Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Worker agents #14

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ ebin/test
ebin/*.beam
logs
test/*.beam
cover
test/ct.cover.spec
4 changes: 2 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Copyright (c) 2020, Jan Uhlig <[email protected]>
Copyright (c) 2020, Maria Scott <maria-12648430@gmx.net>
Copyright (c) 2020-2021, Jan Uhlig <[email protected]>
Copyright (c) 2020-2021, Maria Scott <maria-12648430@hnc-agency.org>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

PROJECT = hnc
PROJECT_DESCRIPTION = hnc - Erlang Worker Pool
PROJECT_VERSION = 0.3.0
PROJECT_VERSION = 0.4.0

CT_OPTS += -pa ebin -pa test -ct_hooks hnc_ct_hook []

Expand Down
44 changes: 40 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# hnc (Agency) - Erlang worker pool

`hnc` is an Erlang worker pool application,
focussing on robustness and performance.

## Usage Example

```erlang
%% Start hnc application.
application:ensure_all_started(hnc).
Expand All @@ -8,17 +13,48 @@ application:ensure_all_started(hnc).
{ok, _}=hnc:start_pool(my_pool, #{}, my_worker, []).

%% Check out a worker.
WorkerRef=hnc:checkout(my_pool).
Ref=hnc:checkout(my_pool).

%% Get the worker pid from the identifier.
Worker=hnc:get_worker(WorkerRef).
Worker=hnc:get_worker(Ref).

%% Do some stuff with the worker.
my_worker:do_stuff(Worker).
Result=my_worker:do_stuff(Worker).

%% Check worker back in.
ok=hnc:checkin(my_pool, WorkerRef).
ok=hnc:checkin(my_pool, Ref).

%% Or use a transaction to combine checking out,
%% doing stuff, and checking back in into one
%% convenient function.
Result=hnc:transaction(
my_pool,
fun (Worker) -> my_worker:do_stuff(Worker) end
).

%% Stop the pool
ok=hnc:stop_pool(my_pool).
```

## Usage as a dependency

### `Erlang.mk`

```
DEPS = hnc
dep_hnc = git https://github.com/hnc-agency/hnc 0.4.0
```

### `rebar3`

```
{deps, [
{hnc, ".*", {git, "https://github.com/hnc-agency/hnc",
{tag, "0.4.0"}}}
]}.
```

## Authors

* Jan Uhlig (`juhlig`)
* Maria Scott (`Maria-12648430`)
5 changes: 0 additions & 5 deletions doc/src/guide/advanced.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,3 @@ second element specifies the interval (in milliseconds) between such checks.
given time.
* It will always keep (ie, not stop) the minimum amount of workers specified in the `size`
configuration option.
* When stopping workers, the cleanup will take the setting of the `strategy` configuration
option into consideration, ie it will preferrably stop those workers that are the least
likely to be checked out again. Specifically, with the `fifo` strategy it will stop those
workers that returned last, while with the `lifo` strategy it will stop those workers
that returned first.
17 changes: 17 additions & 0 deletions doc/src/guide/changelog.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,20 @@ Mostly refactoring and cleanup, and some new features.

* The pool does not wait for the initial workers to complete their
startup before becoming operationali any more.

=== 0.4.0

Large refactoring and bugfixes.

* Workers are paired with agents that watch and take care of them,
removing complexity from and reducing resource consumption of the
pool process.

* The strategy is no longer considered to select workers for pool
shrinking.

* The functions `checkin`, `give_away`, and `get_worker` raise an
error exception if the calling process is not the current owner
of the worker.

* Augmented tests.
4 changes: 2 additions & 2 deletions doc/src/guide/embedded.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ in the `Usage` chapter.
----
{ok, _} = my_pool_sup:start_link().

WorkerRef = hnc:checkout(my_pool).
ok = hnc:checkin(WorkerRef).
Ref = hnc:checkout(my_pool).
ok = hnc:checkin(Ref).

Result = hnc:transaction(my_other_pool, fun do_stuff/1).
----
Expand Down
5 changes: 0 additions & 5 deletions doc/src/guide/misc.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,3 @@ their start phase before it can be started.
Still, you may want to keep the startup phases of your workers
as short as possible, since a user will still have to wait for
that amount of time before it receives a newly started worker.

To achieve short startup phases, you may consider returning
the worker pid from the respective `start_link/1` function
immediately, and do any initialization in a separate step,
maybe even when the worker is being used for the first time.
39 changes: 19 additions & 20 deletions doc/src/guide/usage.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ The pool identifier, as given in `start_pool/4`.

[source,erlang]
----
WorkerRef = hnc:checkout(PoolName, Timeout).
Ref = hnc:checkout(PoolName, Timeout).
----

`PoolName`::
Expand All @@ -84,31 +84,31 @@ The pool identifier, as given in `start_pool/4`.
The maximum time allowed for the checkout. This
argument is optional and defaults to `infinity`.

`WorkerRef`::
`Ref`::
The identifier of a worker from the pool specified by `PoolName`.

The return value is a worker _identifier_ (to be used when checking
it back in, giving it away, or querying it's status). To get the actual
worker _pid_ to perform a task with, `hnc:get_worker(WorkerRef)` must be
worker _pid_ to perform a task with, `hnc:get_worker(Ref)` must be
used.

==== Checking in a worker

[source,erlang]
----
Result = hnc:checkin(WorkerRef, Timeout).
ok = hnc:checkin(Ref, Timeout).
----

`WorkerRef`::
`Ref`::
The identifier of the worker to be returned, as returned by `checkout/1,2`.

`Timeout`::
The maximum time allowed for the checkin. This argument
is optional and defaults to `infinity`.

`Result`::
The result of the checkin operation, either `ok` on success, or `{error, not_owner}`
if the process doing the checkin is not the current owner of the worker.
This function returns `ok` on success, or raises an error exception with
reason `not_owner` if the process doing the checkin is not the current
owner of the worker.

==== Transactions

Expand Down Expand Up @@ -150,15 +150,15 @@ and the worker will not be checked in when the original process dies.

The process calling this function must be the current owner of the worker.

The process receiving the worker is sent a message `{'HNC-WORKER-TRANSFER', WorkerRef, FromPid, GiftData}`.
The process receiving the worker is sent a message `{'HNC-WORKER-TRANSFER', Ref, FromPid, GiftData}`.

[source,erlang]
----
Result = hnc:give_away(WorkerRef, OtherProcess, GiftData).
Result = hnc:give_away(WorkerRef, OtherProcess, GiftData, Timeout).
ok = hnc:give_away(Ref, OtherProcess, GiftData).
ok = hnc:give_away(Ref, OtherProcess, GiftData, Timeout).
----

`WorkerRef`::
`Ref`::
The identifier of the worker to be given away, as returned by `checkout/1,2`.

`OtherProcess`::
Expand All @@ -170,9 +170,9 @@ Arbitrary term to send along with the transfer message.
`Timeout`::
The maximum time allowed for the worker transfer operation.

`Result`::
The result of the operation, either `ok` on success, or `{error, not_owner}`
if the process calling this function is not the current owner of the worker.
This function returns `ok` on success, or raises and error exception
with reason `not_owner` if the process calling this function is not
the current owner of the worker.

=== Runtime configuration

Expand Down Expand Up @@ -291,20 +291,19 @@ remains in the pool.

[source,erlang]
----
WorkerStatus = hnc:worker_status(WorkerRef, Timeout).
WorkerStatus = hnc:worker_status(Ref, Timeout).
----

`WorkerRef`::
`Ref`::
The identifier of the worker whose status to retrieve, as returned by `checkout/1,2`.

`Timeout`::
The maximum time allowed to fetch the status. This
argument is optional and defaults to `5000`.

`WorkerStatus`::
The current status of the worker, either `idle`,
`out`, or `returning`. If the given worker is not
known to the pool, `undefined` is returned.
The current status of the worker, either `starting`,
`idle`, `out`, or `returning`.

==== Pool status

Expand Down
42 changes: 18 additions & 24 deletions doc/src/manual/hnc.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ interacting with pools.

[source,erlang]
----
worker_ref() = term()
ref() = term()

pool() = atom()

Expand All @@ -60,13 +60,12 @@ on_return() = undefined | {fun((worker()) -> any()), timeout()}

shutdown() = timeout() | brutal_kill

worker_status() = idle | out | returning
worker_status() = starting | idle | out | returning

pool_status() = #{
idle := non_neg_integer(),
out := non_neg_integer(),
starting := non_neg_integer(),
returning := non_neg_integer()
available := non_neg_integer(),
unavailable := non_neg_integer(),
starting := non_neg_integer()
}

opts() = #{
Expand Down Expand Up @@ -230,7 +229,7 @@ WorkerRef = hnc:checkout(PoolName, Timeout).

PoolName = pool():: Pool identifier as given in `start_pool/4`.
Timeout = timeout():: Maximum time to wait for the checkout to succeed.
WorkerRef = worker_ref():: The identifier of the worker that was checked out from the pool.
WorkerRef = ref():: The identifier of the worker that was checked out from the pool.

Checks out a worker from the pool. If the pool has `idle` workers available,
it will return one of them. Which of the available workers is picked depends
Expand All @@ -250,14 +249,12 @@ worker.

[source,erlang]
----
Result = hnc:checkin(WorkerRef).
Result = hnc:checkin(WorkerRef, Timeout).
ok = hnc:checkin(WorkerRef).
ok = hnc:checkin(WorkerRef, Timeout).
----

WorkerRef = worker_ref():: The identifier of the worker to be checkedi back in, as returned by `checkout/1,2`.
Timeout = timeout():: Maximum time to wait for the checkin to succeed.
Result = ok | {error, Reason}:: The result of the `checkin` operation.
Reason = not_owner | not_found:: If checking in failed, the reason for the failure.

Returns the worker identified by the given `WorkerRef` to the pool.

Expand All @@ -269,7 +266,7 @@ it in the `on_return` option, the worker is killed and removed from the pool, as
it is then assumed to be in an undefined state.

Returns `ok` on success. If the process doing the checkin is not the current owner
of the worker, `{error, not_owner}` is returned.
of the worker identifier, an error exception with reason `not_owner` is raised.

=== Performing a transaction

Expand Down Expand Up @@ -298,22 +295,20 @@ If you want the worker to remain checked out, you may give it away to another pr

[source,erlang]
----
Result = hnc:give_away(WorkerRef, NewUser, GiftData).
Result = hnc:give_away(WorkerRef, NewUser, GiftData, Timeout).
ok = hnc:give_away(WorkerRef, NewUser, GiftData).
ok = hnc:give_away(WorkerRef, NewUser, GiftData, Timeout).
----

WorkerRef = worker_ref():: The identifier of the worker to be transferred to `NewUser`, as returned by `checkout/1,2`.
WorkerRef = ref():: The identifier of the worker to be transferred to `NewUser`, as returned by `checkout/1,2`.
NewUser = pid():: The pid of the process to give the worker to.
GiftData = term():: Additional data to send to the new user.
Timeout = timeout():: Maximum time to wait before a worker becomes available.
Result = ok | {error, Reason}:: The result of the `give_away` operation.
Reason = not_owner | not_found:: If giving away failed, the reason for the failure.

On success, `ok` is returned. Additionally, the new owner process is sent a message
`{'HNC-WORKER-TRANSFER', WorkerRef, FromPid, GiftData}`.

If the process calling this function is not the owner of the worker, `{error, not_owner}`
is returned.
If the process calling this function is not the owner of the worker identifier, an error exception
with reason `not_owner` is raised.

=== Getting and setting the checkout strategy

Expand Down Expand Up @@ -384,9 +379,9 @@ Status = hnc:worker_status(WorkerRef).
Status = hnc:worker_status(WorkerRef, Timeout).
----

WorkerRef = worker_ref():: The identifier of the worker whose status to query.
WorkerRef = ref():: The identifier of the worker whose status to query.
Timeout = timeout():: Maximum time to wait.
Status = worker_status() | undefined:: The status of the given worker of the given pool.
Status = worker_status():: The status of the given worker of the given pool.

Retrieve the status of the given worker.

Expand All @@ -408,7 +403,6 @@ Status = pool_status():: The status of the given pool.

Retrieve the pool status in a map.

* `idle`: number of checked in (available) workers.
* `out`: number of checked out (not available) workers.
* `available`: number of available workers.
* `unavailable`: number of unavailable workers.
* `starting`: number of workers that are in the process of being started (not yet available).
* `returning`: number of workers that are in the process of returning (not yet available).
4 changes: 2 additions & 2 deletions ebin/hnc.app
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{application, 'hnc', [
{description, "hnc - Erlang Worker Pool"},
{vsn, "0.3.0"},
{modules, ['hnc','hnc_app','hnc_pool','hnc_pool_sup','hnc_sup','hnc_worker','hnc_worker_sup','hnc_worker_sup_sup']},
{vsn, "0.4.0"},
{modules, ['hnc','hnc_agent','hnc_app','hnc_pool','hnc_pool_sup','hnc_sup','hnc_worker','hnc_worker_sup','hnc_worker_sup_sup']},
{registered, [hnc_sup]},
{applications, [kernel,stdlib]},
{mod, {hnc_app, []}},
Expand Down
Loading