{"action":"created","comment":{"url":"https://api.github.com/repos/DockYard/reefpoints/pulls/comments/88704499","pull_request_review_id":9272487,"id":88704499,"diff_hunk":"@@ -0,0 +1,138 @@\n+---\n+layout: post\n+title: \"How GenServer deals with errors in concurrent environment\"\n+summary: \"GenServer can help to deal with race conditions, deadlocks, and many edge cases\"\n+author: \"Daniel Xu\"\n+twitter: \"Daniel_Xu_For\"\n+github: \"Daniel-Xu\"\n+published: true\n+tags: Elixir\n+---\n+\n+## Pattern for stateful server process\n+\n+we can implement our own stateful server with process and tail recursion:\n+\n+```elixir\n+def start do\n+  spawn(fn ->\n+    initial_state = ...\n+    loop(initial_state)\n+  end)\n+end\n+\n+defp loop(state) do\n+  receive do\n+    {:message, caller, msg} ->\n+      new_state = handle(msg, state)\n+      send(caller, {:reply, new_msg})\n+      loop(state)\n+    :stop -> terminate()\n+  end\n+end\n+\n+def call(name_or_pid, message)\n+  send(name_or_pid, {:message, self(), message})\n+  receive do\n+    {:reply, reply} -> reply\n+  end\n+end\n+```\n+\n+* In the pseudo code, we define a start function which can be used to start the server process with the initial state.\n+* Because of `receive`, the process will be blocked to wait for messages. Whenever clients send a message to the server process using `call`,\n+  it will pattern match on message and invoke the corresponding function to get the new state.\n+* After getting the new state, it replies to the client and runs the loop function from the beginning with a new state.\n+\n+It's simple but error-prone because we don't handle any error condition. This becomes more complicated when it comes to a concurrent environment.\n+The good news is that GenServer can help to deal with race conditions, deadlocks, and many edge cases.\n+\n+## GenServer helps with error handling\n+\n+### Ensure source of message using `reference`\n+\n+In stateful server process above, the client sends message to the server with format of {:message, pid_or_name, message}\n+and waits for a message with the format of {:reply, reply}.\n+\n+As shown in the picture, the client might receive a same formatted reply from different servers,\n+how can we make sure that the reply is from the correct server?\n+\n+![msg](http://d.pr/i/m7Xs+)\n+\n+The solution is to use **a unique reference** to tag the message. when the client sends a request to the server,\n+it creates a unique reference first and sends it with its pid together: `{:message, {ref, pid}, message}`,\n+and the server can reply with `{:reply, ref, reply}`. In this way, the client can pattern match based on the unique reference.\n+\n+Now, we have `call` function as following:\n+\n+```elixir\n+def call(name_or_pid, message)\n+  ref = make_ref()\n+  send(name_or_pid, {:message, {ref, self()}, message})\n+  receive do\n+    {:reply, ref, reply} -> reply\n+  end\n+end\n+```\n+\n+Erlang/OTP solves this problem with a unique reference as shown [here](https://github.com/erlang/otp/blob/maint/lib/stdlib/src/gen.erl#L167).\n+\n+### What if the server crashes\n+\n+1. Server crashes before message is sent by client via server Pid\n+\n+  If the server crashes before the message is sent from the client, the message will be lost and the client will be **blocked** in `receive` block.\n+\n+  The solution is to monitor the server process using `Process.monitor(server_pid)`. The reason why we choose `monitor` instead of\n+  `links` is that monitor is unidirectional, so termination of the client will not affect the server.\n+\n+  In case of a server crash, the client will receive a `Down` message, so we can take action in `receive` block.\n+  Noting that `monitor` returns a reference, we can now drop the `make_ref()`.\n+\n+2. Server crashes before message is sent by client via registered name\n+\n+  In this case, the client process will terminate. To avoid crashing, we need a `catch` to trap any exception.\n+  `try...catch` is useful here.\n+\n+3. Server crashes right after replying to client\n+\n+  If server crashes right after it sends its reply to the client, a `Down` message will be sent to the client's\n+  mailbox. The client, however, will never have a chance to patten match this message because it `demonitors` the server.\n+\n+  This might cause memory leak and slow down the server. Ultimately, a single slow process may cause an entire system to crash by consuming all the available memory.\n+\n+  `Process.demonitor(ref, [:flush])` is the solution for this issue. Every time we demonitor a server,\n+  passing in `flush` option can make sure that any `Down` message belongs to that monitor will be cleared.\n+\n+After handling server crash, `call` function looks like:\n+\n+```elixir\n+def call(name_or_pid, message)\n+  ref = Process.monitor(name_or_pid)\n+  try do\n+    send(name_or_pid, {:message, {ref, self()}, message})\n+  catch\n+    _ -> :error\n+  end","path":"source/posts/2016-11-18-how-genserver-helps-to-handle-errors.md","position":119,"original_position":116,"commit_id":"7924f6db89b67ae91ec4d646ceaeb28054e336c7","original_commit_id":"cf6243615cc7d4f28e07f72ec4faa05390236d7a","user":{"login":"bcardarella","id":18524,"avatar_url":"https://avatars.githubusercontent.com/u/18524?v=3","gravatar_id":"","url":"https://api.github.com/users/bcardarella","html_url":"https://github.com/bcardarella","followers_url":"https://api.github.com/users/bcardarella/followers","following_url":"https://api.github.com/users/bcardarella/following{/other_user}","gists_url":"https://api.github.com/users/bcardarella/gists{/gist_id}","starred_url":"https://api.github.com/users/bcardarella/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bcardarella/subscriptions","organizations_url":"https://api.github.com/users/bcardarella/orgs","repos_url":"https://api.github.com/users/bcardarella/repos","events_url":"https://api.github.com/users/bcardarella/events{/privacy}","received_events_url":"https://api.github.com/users/bcardarella/received_events","type":"User","site_admin":false},"body":"@Daniel-Xu Elixr has `rescue` which should get you the same I believe","created_at":"2016-11-18T17:50:10Z","updated_at":"2016-11-18T17:50:10Z","html_url":"https://github.com/DockYard/reefpoints/pull/561#discussion_r88704499","pull_request_url":"https://api.github.com/repos/DockYard/reefpoints/pulls/561","_links":{"self":{"href":"https://api.github.com/repos/DockYard/reefpoints/pulls/comments/88704499"},"html":{"href":"https://github.com/DockYard/reefpoints/pull/561#discussion_r88704499"},"pull_request":{"href":"https://api.github.com/repos/DockYard/reefpoints/pulls/561"}}},"pull_request":{"url":"https://api.github.com/repos/DockYard/reefpoints/pulls/561","id":94306310,"html_url":"https://github.com/DockYard/reefpoints/pull/561","diff_url":"https://github.com/DockYard/reefpoints/pull/561.diff","patch_url":"https://github.com/DockYard/reefpoints/pull/561.patch","issue_url":"https://api.github.com/repos/DockYard/reefpoints/issues/561","number":561,"state":"open","locked":false,"title":"GenServer error handling","user":{"login":"Daniel-Xu","id":548144,"avatar_url":"https://avatars.githubusercontent.com/u/548144?v=3","gravatar_id":"","url":"https://api.github.com/users/Daniel-Xu","html_url":"https://github.com/Daniel-Xu","followers_url":"https://api.github.com/users/Daniel-Xu/followers","following_url":"https://api.github.com/users/Daniel-Xu/following{/other_user}","gists_url":"https://api.github.com/users/Daniel-Xu/gists{/gist_id}","starred_url":"https://api.github.com/users/Daniel-Xu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Daniel-Xu/subscriptions","organizations_url":"https://api.github.com/users/Daniel-Xu/orgs","repos_url":"https://api.github.com/users/Daniel-Xu/repos","events_url":"https://api.github.com/users/Daniel-Xu/events{/privacy}","received_events_url":"https://api.github.com/users/Daniel-Xu/received_events","type":"User","site_admin":false},"body":"[rendered](https://github.com/DockYard/reefpoints/pull/561/files?short_path=bf316d3#diff-bf316d307826309d27c7dd00178c04ff)","created_at":"2016-11-18T05:48:10Z","updated_at":"2016-11-18T17:50:10Z","closed_at":null,"merged_at":null,"merge_commit_sha":"55c99bd723dbc11450facc070ddfd4ced38f0620","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/DockYard/reefpoints/pulls/561/commits","review_comments_url":"https://api.github.com/repos/DockYard/reefpoints/pulls/561/comments","review_comment_url":"https://api.github.com/repos/DockYard/reefpoints/pulls/comments{/number}","comments_url":"https://api.github.com/repos/DockYard/reefpoints/issues/561/comments","statuses_url":"https://api.github.com/repos/DockYard/reefpoints/statuses/7924f6db89b67ae91ec4d646ceaeb28054e336c7","head":{"label":"DockYard:2016-11-18-how-genserver-helps-to-handle-errors","ref":"2016-11-18-how-genserver-helps-to-handle-errors","sha":"7924f6db89b67ae91ec4d646ceaeb28054e336c7","user":{"login":"DockYard","id":873981,"avatar_url":"https://avatars.githubusercontent.com/u/873981?v=3","gravatar_id":"","url":"https://api.github.com/users/DockYard","html_url":"https://github.com/DockYard","followers_url":"https://api.github.com/users/DockYard/followers","following_url":"https://api.github.com/users/DockYard/following{/other_user}","gists_url":"https://api.github.com/users/DockYard/gists{/gist_id}","starred_url":"https://api.github.com/users/DockYard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DockYard/subscriptions","organizations_url":"https://api.github.com/users/DockYard/orgs","repos_url":"https://api.github.com/users/DockYard/repos","events_url":"https://api.github.com/users/DockYard/events{/privacy}","received_events_url":"https://api.github.com/users/DockYard/received_events","type":"Organization","site_admin":false},"repo":{"id":2262874,"name":"reefpoints","full_name":"DockYard/reefpoints","owner":{"login":"DockYard","id":873981,"avatar_url":"https://avatars.githubusercontent.com/u/873981?v=3","gravatar_id":"","url":"https://api.github.com/users/DockYard","html_url":"https://github.com/DockYard","followers_url":"https://api.github.com/users/DockYard/followers","following_url":"https://api.github.com/users/DockYard/following{/other_user}","gists_url":"https://api.github.com/users/DockYard/gists{/gist_id}","starred_url":"https://api.github.com/users/DockYard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DockYard/subscriptions","organizations_url":"https://api.github.com/users/DockYard/orgs","repos_url":"https://api.github.com/users/DockYard/repos","events_url":"https://api.github.com/users/DockYard/events{/privacy}","received_events_url":"https://api.github.com/users/DockYard/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/DockYard/reefpoints","description":"DockYard Blog","fork":false,"url":"https://api.github.com/repos/DockYard/reefpoints","forks_url":"https://api.github.com/repos/DockYard/reefpoints/forks","keys_url":"https://api.github.com/repos/DockYard/reefpoints/keys{/key_id}","collaborators_url":"https://api.github.com/repos/DockYard/reefpoints/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/DockYard/reefpoints/teams","hooks_url":"https://api.github.com/repos/DockYard/reefpoints/hooks","issue_events_url":"https://api.github.com/repos/DockYard/reefpoints/issues/events{/number}","events_url":"https://api.github.com/repos/DockYard/reefpoints/events","assignees_url":"https://api.github.com/repos/DockYard/reefpoints/assignees{/user}","branches_url":"https://api.github.com/repos/DockYard/reefpoints/branches{/branch}","tags_url":"https://api.github.com/repos/DockYard/reefpoints/tags","blobs_url":"https://api.github.com/repos/DockYard/reefpoints/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/DockYard/reefpoints/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/DockYard/reefpoints/git/refs{/sha}","trees_url":"https://api.github.com/repos/DockYard/reefpoints/git/trees{/sha}","statuses_url":"https://api.github.com/repos/DockYard/reefpoints/statuses/{sha}","languages_url":"https://api.github.com/repos/DockYard/reefpoints/languages","stargazers_url":"https://api.github.com/repos/DockYard/reefpoints/stargazers","contributors_url":"https://api.github.com/repos/DockYard/reefpoints/contributors","subscribers_url":"https://api.github.com/repos/DockYard/reefpoints/subscribers","subscription_url":"https://api.github.com/repos/DockYard/reefpoints/subscription","commits_url":"https://api.github.com/repos/DockYard/reefpoints/commits{/sha}","git_commits_url":"https://api.github.com/repos/DockYard/reefpoints/git/commits{/sha}","comments_url":"https://api.github.com/repos/DockYard/reefpoints/comments{/number}","issue_comment_url":"https://api.github.com/repos/DockYard/reefpoints/issues/comments{/number}","contents_url":"https://api.github.com/repos/DockYard/reefpoints/contents/{+path}","compare_url":"https://api.github.com/repos/DockYard/reefpoints/compare/{base}...{head}","merges_url":"https://api.github.com/repos/DockYard/reefpoints/merges","archive_url":"https://api.github.com/repos/DockYard/reefpoints/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/DockYard/reefpoints/downloads","issues_url":"https://api.github.com/repos/DockYard/reefpoints/issues{/number}","pulls_url":"https://api.github.com/repos/DockYard/reefpoints/pulls{/number}","milestones_url":"https://api.github.com/repos/DockYard/reefpoints/milestones{/number}","notifications_url":"https://api.github.com/repos/DockYard/reefpoints/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/DockYard/reefpoints/labels{/name}","releases_url":"https://api.github.com/repos/DockYard/reefpoints/releases{/id}","deployments_url":"https://api.github.com/repos/DockYard/reefpoints/deployments","created_at":"2011-08-24T16:55:40Z","updated_at":"2016-10-06T22:10:26Z","pushed_at":"2016-11-18T17:40:10Z","git_url":"git://github.com/DockYard/reefpoints.git","ssh_url":"git@github.com:DockYard/reefpoints.git","clone_url":"https://github.com/DockYard/reefpoints.git","svn_url":"https://github.com/DockYard/reefpoints","homepage":"https://dockyard.com/blog","size":23674,"stargazers_count":40,"watchers_count":40,"language":"CSS","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":65,"mirror_url":null,"open_issues_count":15,"forks":65,"open_issues":15,"watchers":40,"default_branch":"master"}},"base":{"label":"DockYard:master","ref":"master","sha":"25eb53941284db154a569d5b016c9974ca1bb33f","user":{"login":"DockYard","id":873981,"avatar_url":"https://avatars.githubusercontent.com/u/873981?v=3","gravatar_id":"","url":"https://api.github.com/users/DockYard","html_url":"https://github.com/DockYard","followers_url":"https://api.github.com/users/DockYard/followers","following_url":"https://api.github.com/users/DockYard/following{/other_user}","gists_url":"https://api.github.com/users/DockYard/gists{/gist_id}","starred_url":"https://api.github.com/users/DockYard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DockYard/subscriptions","organizations_url":"https://api.github.com/users/DockYard/orgs","repos_url":"https://api.github.com/users/DockYard/repos","events_url":"https://api.github.com/users/DockYard/events{/privacy}","received_events_url":"https://api.github.com/users/DockYard/received_events","type":"Organization","site_admin":false},"repo":{"id":2262874,"name":"reefpoints","full_name":"DockYard/reefpoints","owner":{"login":"DockYard","id":873981,"avatar_url":"https://avatars.githubusercontent.com/u/873981?v=3","gravatar_id":"","url":"https://api.github.com/users/DockYard","html_url":"https://github.com/DockYard","followers_url":"https://api.github.com/users/DockYard/followers","following_url":"https://api.github.com/users/DockYard/following{/other_user}","gists_url":"https://api.github.com/users/DockYard/gists{/gist_id}","starred_url":"https://api.github.com/users/DockYard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DockYard/subscriptions","organizations_url":"https://api.github.com/users/DockYard/orgs","repos_url":"https://api.github.com/users/DockYard/repos","events_url":"https://api.github.com/users/DockYard/events{/privacy}","received_events_url":"https://api.github.com/users/DockYard/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/DockYard/reefpoints","description":"DockYard Blog","fork":false,"url":"https://api.github.com/repos/DockYard/reefpoints","forks_url":"https://api.github.com/repos/DockYard/reefpoints/forks","keys_url":"https://api.github.com/repos/DockYard/reefpoints/keys{/key_id}","collaborators_url":"https://api.github.com/repos/DockYard/reefpoints/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/DockYard/reefpoints/teams","hooks_url":"https://api.github.com/repos/DockYard/reefpoints/hooks","issue_events_url":"https://api.github.com/repos/DockYard/reefpoints/issues/events{/number}","events_url":"https://api.github.com/repos/DockYard/reefpoints/events","assignees_url":"https://api.github.com/repos/DockYard/reefpoints/assignees{/user}","branches_url":"https://api.github.com/repos/DockYard/reefpoints/branches{/branch}","tags_url":"https://api.github.com/repos/DockYard/reefpoints/tags","blobs_url":"https://api.github.com/repos/DockYard/reefpoints/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/DockYard/reefpoints/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/DockYard/reefpoints/git/refs{/sha}","trees_url":"https://api.github.com/repos/DockYard/reefpoints/git/trees{/sha}","statuses_url":"https://api.github.com/repos/DockYard/reefpoints/statuses/{sha}","languages_url":"https://api.github.com/repos/DockYard/reefpoints/languages","stargazers_url":"https://api.github.com/repos/DockYard/reefpoints/stargazers","contributors_url":"https://api.github.com/repos/DockYard/reefpoints/contributors","subscribers_url":"https://api.github.com/repos/DockYard/reefpoints/subscribers","subscription_url":"https://api.github.com/repos/DockYard/reefpoints/subscription","commits_url":"https://api.github.com/repos/DockYard/reefpoints/commits{/sha}","git_commits_url":"https://api.github.com/repos/DockYard/reefpoints/git/commits{/sha}","comments_url":"https://api.github.com/repos/DockYard/reefpoints/comments{/number}","issue_comment_url":"https://api.github.com/repos/DockYard/reefpoints/issues/comments{/number}","contents_url":"https://api.github.com/repos/DockYard/reefpoints/contents/{+path}","compare_url":"https://api.github.com/repos/DockYard/reefpoints/compare/{base}...{head}","merges_url":"https://api.github.com/repos/DockYard/reefpoints/merges","archive_url":"https://api.github.com/repos/DockYard/reefpoints/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/DockYard/reefpoints/downloads","issues_url":"https://api.github.com/repos/DockYard/reefpoints/issues{/number}","pulls_url":"https://api.github.com/repos/DockYard/reefpoints/pulls{/number}","milestones_url":"https://api.github.com/repos/DockYard/reefpoints/milestones{/number}","notifications_url":"https://api.github.com/repos/DockYard/reefpoints/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/DockYard/reefpoints/labels{/name}","releases_url":"https://api.github.com/repos/DockYard/reefpoints/releases{/id}","deployments_url":"https://api.github.com/repos/DockYard/reefpoints/deployments","created_at":"2011-08-24T16:55:40Z","updated_at":"2016-10-06T22:10:26Z","pushed_at":"2016-11-18T17:40:10Z","git_url":"git://github.com/DockYard/reefpoints.git","ssh_url":"git@github.com:DockYard/reefpoints.git","clone_url":"https://github.com/DockYard/reefpoints.git","svn_url":"https://github.com/DockYard/reefpoints","homepage":"https://dockyard.com/blog","size":23674,"stargazers_count":40,"watchers_count":40,"language":"CSS","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":65,"mirror_url":null,"open_issues_count":15,"forks":65,"open_issues":15,"watchers":40,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/DockYard/reefpoints/pulls/561"},"html":{"href":"https://github.com/DockYard/reefpoints/pull/561"},"issue":{"href":"https://api.github.com/repos/DockYard/reefpoints/issues/561"},"comments":{"href":"https://api.github.com/repos/DockYard/reefpoints/issues/561/comments"},"review_comments":{"href":"https://api.github.com/repos/DockYard/reefpoints/pulls/561/comments"},"review_comment":{"href":"https://api.github.com/repos/DockYard/reefpoints/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/DockYard/reefpoints/pulls/561/commits"},"statuses":{"href":"https://api.github.com/repos/DockYard/reefpoints/statuses/7924f6db89b67ae91ec4d646ceaeb28054e336c7"}}}}
{"actor":{"display_login":"bcardarella"}}