Browse Source

Update docs.

Roberto Ostinelli 3 years ago
parent
commit
455f728ade
2 changed files with 39 additions and 24 deletions
  1. 10 17
      OPTIONS.md
  2. 29 7
      src/syn.erl

+ 10 - 17
OPTIONS.md

@@ -46,30 +46,23 @@ config :syn,
 ## strict_mode
 ## strict_mode
 By default, Syn doesn't enforce which processes perform the Registry and Process Groups operations:
 By default, Syn doesn't enforce which processes perform the Registry and Process Groups operations:
 for instance, a process can be registered by a call running in another process.
 for instance, a process can be registered by a call running in another process.
-While this is fine in standard operations, those that end up updating a process' metadata could potentially
+While this is fine in most operations, those that end up updating a process' metadata can potentially
 lead to race conditions if caution is not taken.
 lead to race conditions if caution is not taken.
 
 
-Operations are serialized by the [authority node](internals.html#node-authority) responsible for a process, however
-simultaneous requests coming from different processes to update a specific process' metadata can result
-in unexpected consequences, even if this doesn't compromise Syn's strict eventual consistency
-(i.e. the data across the cluster will still eventually converge).
+Just as for simple Key Value stores, proper application design needs to be implemented.
+Otherwise, two different processes updating simultaneously the value of a key will end up overwriting each other,
+with the last successful write being the data ultimately kept.
 
 
-Imagine for instance that a process A has a metadata of attributes such as `[{color, "blue"}]`. A different process
-might request to update process A's metadata to include size, by setting its metadata to `[{color, "blue"}, {size, 100}]`.
-At almost the same time another process might also request to update process A's metadata to include weight, by setting
-its metadata to `[{color, "blue"}, {weight, 1.0}]`.
-
-These requests will be delivered to the authority node which will treat them sequentially.
-Therefore, the first incoming request (for eg. the one that sets the size) will be overwritten shortly after
-by the second incoming request (the one that sets the weight), thus resulting in the loss of the `{size, 100}` tuple
-in the process' metadata.  The end result will be that process A' metadata will be propagated to the whole cluster as
-`[{color, "blue"}, {weight, 1.0}]`.
+Similarly, simultaneous requests coming from different processes to update a specific process' metadata will
+result in the last write received by the  [authority node](internals.html#node-authority) being propagated
+to the whole cluster.
 
 
 This can be circumvented by proper application design, for instance by ensuring that a single process
 This can be circumvented by proper application design, for instance by ensuring that a single process
 is always responsible for updating a specific process' metadata.
 is always responsible for updating a specific process' metadata.
 
 
-When enabled, Syn's `strict_mode` is a helper to enforce that a process can only update its own metadata.
-This basically means that the `Pid` parameter of most methods must be `self()`.
+When enabled, Syn's `strict_mode` is a _helper_ which enforces that a process can only update its own metadata.
+This basically means that the `Pid` parameter of most methods must be `self()`. Therefore, concurrent requests from
+different processes to update another process' metadata will be rejected, which can help to control the flow.
 
 
 `strict_mode` is a global setting that cannot be specified on a per-scope basis. The same setting SHOULD be set
 `strict_mode` is a global setting that cannot be specified on a per-scope basis. The same setting SHOULD be set
 on every Erlang cluster node running Syn.
 on every Erlang cluster node running Syn.

+ 29 - 7
src/syn.erl

@@ -288,9 +288,12 @@ register(Scope, Name, Pid) ->
 %%
 %%
 %% You may register the same process with different names.
 %% You may register the same process with different names.
 %% You may also re-register a process multiple times, for example if you need to update its metadata, however it is
 %% You may also re-register a process multiple times, for example if you need to update its metadata, however it is
-%% RECOMMENDED to be aware of the implications of updating metadata, see the <a href="options.html#strict_mode">`strict_mode'</a>
+%% recommended to be aware of the implications of updating metadata, see the <a href="options.html#strict_mode">`strict_mode'</a>
 %% option for more information.
 %% option for more information.
 %%
 %%
+%% If you want to update a process' metadata by modifying its existing one, you may consider using
+%% {@link update_registry/3} instead.
+%%
 %% When a process gets registered, Syn will automatically monitor it.
 %% When a process gets registered, Syn will automatically monitor it.
 %%
 %%
 %% Possible error reasons:
 %% Possible error reasons:
@@ -346,21 +349,29 @@ register(Scope, Name, Pid, Meta) ->
 
 
 %% @doc Updates the registered Name metadata in the specified Scope.
 %% @doc Updates the registered Name metadata in the specified Scope.
 %%
 %%
-%% Atomically calls Fun with the current metadata, and stores the return value as new metadata.
+%% Atomically calls Fun with the current metadata, and stores the return value as new metadata. It is
+%% recommended to be aware of the implications of updating metadata, see the <a href="options.html#strict_mode">`strict_mode'</a>
+%% option for more information.
+%%
+%% Possible error reasons:
+%% <ul>
+%% <li>`undefined': The Name cannot be found.</li>
+%% <li>`{update_fun, {Reason, Stacktrace}}': An error has occurred in applying the supplied Fun.</li>
+%% </ul>
 %%
 %%
 %% <h2>Examples</h2>
 %% <h2>Examples</h2>
 %% <h3>Elixir</h3>
 %% <h3>Elixir</h3>
 %% ```
 %% ```
 %% iex> :syn.register(:devices, "SN-123-456789", self(), 10)
 %% iex> :syn.register(:devices, "SN-123-456789", self(), 10)
 %% :ok
 %% :ok
-%% iex> :syn.update_registry(:devices, "area-1", fn existing_meta -> existing_meta * 2 end)
+%% iex> :syn.update_registry(:devices, "area-1", fn _pid, existing_meta -> existing_meta * 2 end)
 %% {:ok, {#PID<0.105.0>, 20}}
 %% {:ok, {#PID<0.105.0>, 20}}
 %% '''
 %% '''
 %% <h3>Erlang</h3>
 %% <h3>Erlang</h3>
 %% ```
 %% ```
 %% 1> syn:register(devices, "SN-123-456789", self(), 10).
 %% 1> syn:register(devices, "SN-123-456789", self(), 10).
 %% ok
 %% ok
-%% 2> syn:update_registry(devices, "SN-123-456789", fun(ExistingMeta) -> ExistingMeta * 2 end).
+%% 2> syn:update_registry(devices, "SN-123-456789", fun(_Pid, ExistingMeta) -> ExistingMeta * 2 end).
 %% {ok, {<0.69.0>, 20}}
 %% {ok, {<0.69.0>, 20}}
 %% '''
 %% '''
 -spec update_registry(Scope :: atom(), Name :: term(), Fun :: function()) ->
 -spec update_registry(Scope :: atom(), Name :: term(), Fun :: function()) ->
@@ -486,7 +497,7 @@ members(Scope, GroupName) ->
 %% 2> syn:member(devices, "area-1", self()).
 %% 2> syn:member(devices, "area-1", self()).
 %% [{<0.69.0>, meta}]
 %% [{<0.69.0>, meta}]
 %% '''
 %% '''
--spec member(Scope :: atom(), GroupName :: term(), pid()) -> {pid(), Meta :: term()} | undefined.
+-spec member(Scope :: atom(), GroupName :: term(), Pid :: pid()) -> {Pid :: pid(), Meta :: term()} | undefined.
 member(Scope, GroupName, Pid) ->
 member(Scope, GroupName, Pid) ->
     syn_pg:member(Scope, GroupName, Pid).
     syn_pg:member(Scope, GroupName, Pid).
 
 
@@ -497,7 +508,15 @@ is_member(Scope, GroupName, Pid) ->
 
 
 %% @doc Updates the GroupName member metadata in the specified Scope.
 %% @doc Updates the GroupName member metadata in the specified Scope.
 %%
 %%
-%% Atomically calls Fun with the current metadata, and stores the return value as new metadata.
+%% Atomically calls Fun with the current metadata, and stores the return value as new metadata. It is
+%% recommended to be aware of the implications of updating metadata, see the <a href="options.html#strict_mode">`strict_mode'</a>
+%% option for more information.
+%%
+%% Possible error reasons:
+%% <ul>
+%% <li>`undefined': The `pid()' cannot be found in GroupName.</li>
+%% <li>`{update_fun, {Reason, Stacktrace}}': An error has occurred in applying the supplied Fun.</li>
+%% </ul>
 %%
 %%
 %% <h2>Examples</h2>
 %% <h2>Examples</h2>
 %% <h3>Elixir</h3>
 %% <h3>Elixir</h3>
@@ -539,9 +558,12 @@ join(Scope, GroupName, Pid) ->
 %%
 %%
 %% A process can join multiple groups.
 %% A process can join multiple groups.
 %% A process may also join the same group multiple times, for example if you need to update its metadata, however it is
 %% A process may also join the same group multiple times, for example if you need to update its metadata, however it is
-%% RECOMMENDED to be aware of the implications of updating metadata, see the <a href="options.html#strict_mode">`strict_mode'</a>
+%% recommended to be aware of the implications of updating metadata, see the <a href="options.html#strict_mode">`strict_mode'</a>
 %% option for more information.
 %% option for more information.
 %%
 %%
+%% If you want to update a process' metadata by modifying its existing one, you may consider using
+%% {@link update_member/4} instead.
+%%
 %% When a process joins a group, Syn will automatically monitor it.
 %% When a process joins a group, Syn will automatically monitor it.
 %%
 %%
 %% Possible error reasons:
 %% Possible error reasons: