|
@@ -1,413 +0,0 @@
|
|
|
-<?xml version="1.0" encoding="utf-8"?>
|
|
|
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
|
|
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
|
-<html xmlns="http://www.w3.org/1999/xhtml"
|
|
|
-lang="en" xml:lang="en">
|
|
|
-<head>
|
|
|
-<title>README</title>
|
|
|
-<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
|
|
|
-<meta name="generator" content="Org-mode"/>
|
|
|
-<meta name="generated" content="2010-09-08 23:24:52 PDT"/>
|
|
|
-<meta name="author" content="Seth Falcon"/>
|
|
|
-<meta name="description" content=""/>
|
|
|
-<meta name="keywords" content=""/>
|
|
|
-<style type="text/css">
|
|
|
- <!--/*--><![CDATA[/*><!--*/
|
|
|
- html { font-family: Times, serif; font-size: 12pt; }
|
|
|
- .title { text-align: center; }
|
|
|
- .todo { color: red; }
|
|
|
- .done { color: green; }
|
|
|
- .tag { background-color: #add8e6; font-weight:normal }
|
|
|
- .target { }
|
|
|
- .timestamp { color: #bebebe; }
|
|
|
- .timestamp-kwd { color: #5f9ea0; }
|
|
|
- p.verse { margin-left: 3% }
|
|
|
- pre {
|
|
|
- border: 1pt solid #AEBDCC;
|
|
|
- background-color: #F3F5F7;
|
|
|
- padding: 5pt;
|
|
|
- font-family: courier, monospace;
|
|
|
- font-size: 90%;
|
|
|
- overflow:auto;
|
|
|
- }
|
|
|
- table { border-collapse: collapse; }
|
|
|
- td, th { vertical-align: top; }
|
|
|
- dt { font-weight: bold; }
|
|
|
- div.figure { padding: 0.5em; }
|
|
|
- div.figure p { text-align: center; }
|
|
|
- textarea { overflow-x: auto; }
|
|
|
- .linenr { font-size:smaller }
|
|
|
- .code-highlighted {background-color:#ffff00;}
|
|
|
- .org-info-js_info-navigation { border-style:none; }
|
|
|
- #org-info-js_console-label { font-size:10px; font-weight:bold;
|
|
|
- white-space:nowrap; }
|
|
|
- .org-info-js_search-highlight {background-color:#ffff00; color:#000000;
|
|
|
- font-weight:bold; }
|
|
|
- /*]]>*/-->
|
|
|
-</style>
|
|
|
-<script type="text/javascript">
|
|
|
-<!--/*--><![CDATA[/*><!--*/
|
|
|
- function CodeHighlightOn(elem, id)
|
|
|
- {
|
|
|
- var target = document.getElementById(id);
|
|
|
- if(null != target) {
|
|
|
- elem.cacheClassElem = elem.className;
|
|
|
- elem.cacheClassTarget = target.className;
|
|
|
- target.className = "code-highlighted";
|
|
|
- elem.className = "code-highlighted";
|
|
|
- }
|
|
|
- }
|
|
|
- function CodeHighlightOff(elem, id)
|
|
|
- {
|
|
|
- var target = document.getElementById(id);
|
|
|
- if(elem.cacheClassElem)
|
|
|
- elem.className = elem.cacheClassElem;
|
|
|
- if(elem.cacheClassTarget)
|
|
|
- target.className = elem.cacheClassTarget;
|
|
|
- }
|
|
|
-/*]]>*///-->
|
|
|
-</script>
|
|
|
-
|
|
|
-</head>
|
|
|
-<body>
|
|
|
-<div id="content">
|
|
|
-
|
|
|
-<h1 class="title">README</h1>
|
|
|
-
|
|
|
-
|
|
|
-<div id="table-of-contents">
|
|
|
-<h2>Table of Contents</h2>
|
|
|
-<div id="text-table-of-contents">
|
|
|
-<ul>
|
|
|
-<li><a href="#sec-1">1 pidq - A Process Pool Library for Erlang </a>
|
|
|
-<ul>
|
|
|
-<li><a href="#sec-1_1">1.1 Use pidq to manage pools of processes (pids). </a></li>
|
|
|
-<li><a href="#sec-1_2">1.2 Motivation </a></li>
|
|
|
-<li><a href="#sec-1_3">1.3 Usage and API </a>
|
|
|
-<ul>
|
|
|
-<li><a href="#sec-1_3_1">1.3.1 Startup configuration </a></li>
|
|
|
-<li><a href="#sec-1_3_2">1.3.2 Getting and returning pids </a></li>
|
|
|
-<li><a href="#sec-1_3_3">1.3.3 Other things you can do </a></li>
|
|
|
-</ul>
|
|
|
-</li>
|
|
|
-<li><a href="#sec-1_4">1.4 Details </a>
|
|
|
-<ul>
|
|
|
-<li><a href="#sec-1_4_1">1.4.1 Pool management </a></li>
|
|
|
-</ul>
|
|
|
-</li>
|
|
|
-</ul>
|
|
|
-</li>
|
|
|
-</ul>
|
|
|
-</div>
|
|
|
-</div>
|
|
|
-
|
|
|
-<div id="outline-container-1" class="outline-2">
|
|
|
-<h2 id="sec-1"><span class="section-number-2">1</span> pidq - A Process Pool Library for Erlang </h2>
|
|
|
-<div class="outline-text-2" id="text-1">
|
|
|
-
|
|
|
-
|
|
|
-<p>
|
|
|
-<b>Note:</b> this is all work very much in progress. If you are
|
|
|
-interested, drop me a note. Right now, it is really just a readme
|
|
|
-and no working code.
|
|
|
-</p>
|
|
|
-
|
|
|
-</div>
|
|
|
-
|
|
|
-<div id="outline-container-1_1" class="outline-3">
|
|
|
-<h3 id="sec-1_1"><span class="section-number-3">1.1</span> Use pidq to manage pools of processes (pids). </h3>
|
|
|
-<div class="outline-text-3" id="text-1_1">
|
|
|
-
|
|
|
-
|
|
|
-<ul>
|
|
|
-<li>
|
|
|
-Protect the pids from being used concurrently. The main pidq
|
|
|
-interface is <code>pidq:take_pid/0</code> and <code>pidq:return_pid/2</code>. The pidq
|
|
|
-server will keep track of which pids are <b>in use</b> and which are
|
|
|
-<b>free</b>.
|
|
|
-
|
|
|
-</li>
|
|
|
-<li>
|
|
|
-Maintain the size of the pid pool. Specify a maximum number of pids
|
|
|
-in the pool. Trigger pid creation when the free count drops below a
|
|
|
-minimum level or when a pid is marked as failing.
|
|
|
-
|
|
|
-</li>
|
|
|
-<li>
|
|
|
-Organize pids by type and randomly load-balance pids by type. This
|
|
|
-is useful when the pids represent client processes connected to a
|
|
|
-particular node in a cluster (think database read slaves). Separate
|
|
|
-pools are maintained for each type and a request for a pid will
|
|
|
-randomly select a type.
|
|
|
-
|
|
|
-</li>
|
|
|
-</ul>
|
|
|
-</div>
|
|
|
-
|
|
|
-</div>
|
|
|
-
|
|
|
-<div id="outline-container-1_2" class="outline-3">
|
|
|
-<h3 id="sec-1_2"><span class="section-number-3">1.2</span> Motivation </h3>
|
|
|
-<div class="outline-text-3" id="text-1_2">
|
|
|
-
|
|
|
-
|
|
|
-<p>
|
|
|
-The need for the pidq kit arose while writing an Erlang-based
|
|
|
-application that uses <a href="https://wiki.basho.com/display/RIAK/">Riak</a> for data storage. When using the Erlang
|
|
|
-protocol buffer client for Riak, one should avoid accessing a given
|
|
|
-client concurrently. This is because each client is associated with a
|
|
|
-unique client ID that corresponds to an element in an object's vector
|
|
|
-clock. Concurrent action from the same client ID defeats the vector
|
|
|
-clock. For some further explaination, see <sup><a class="footref" name="fnr.1" href="#fn.1">1</a></sup> and <sup><a class="footref" name="fnr.2" href="#fn.2">2</a></sup>.
|
|
|
-</p>
|
|
|
-<p>
|
|
|
-I wanted to avoid spinning up a new client for each request in the
|
|
|
-application. Riak's protocol buffer client is a <code>gen_server</code> process
|
|
|
-and my intuition is that one doesn't want to pay for the startup time
|
|
|
-for every request you send to an app. This suggested a pool of
|
|
|
-clients with some management to avoid concurrent use of a given
|
|
|
-client. On top of that, it seemed convenient to add the ability to
|
|
|
-load balance between clients connected to different nodes in the Riak
|
|
|
-cluster. The load-balancing is a secondary feature; even if you end
|
|
|
-up setting up <a href="http://haproxy.1wt.eu/">HAProxy</a> for that aspect, you might still want the client
|
|
|
-pooling.
|
|
|
-</p>
|
|
|
-</div>
|
|
|
-
|
|
|
-</div>
|
|
|
-
|
|
|
-<div id="outline-container-1_3" class="outline-3">
|
|
|
-<h3 id="sec-1_3"><span class="section-number-3">1.3</span> Usage and API </h3>
|
|
|
-<div class="outline-text-3" id="text-1_3">
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-</div>
|
|
|
-
|
|
|
-<div id="outline-container-1_3_1" class="outline-4">
|
|
|
-<h4 id="sec-1_3_1"><span class="section-number-4">1.3.1</span> Startup configuration </h4>
|
|
|
-<div class="outline-text-4" id="text-1_3_1">
|
|
|
-
|
|
|
-
|
|
|
-<p>
|
|
|
-The idea is that you would wire up pidq to be a supervised process in
|
|
|
-your application. When you start pidq, you specify a module and
|
|
|
-function to use for creating new pids. You also specify the
|
|
|
-properties for each pool that you want pidq to manage, including the
|
|
|
-arguments to pass to the pid starter function.
|
|
|
-</p>
|
|
|
-<p>
|
|
|
-An example configuration looks like this:
|
|
|
-</p>
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-<pre class="src src-erlang"><span style="color: #FF6400;">Pool1</span> = [{<span style="color: #D8FA3C;">name</span>, <span style="color: #61CE3C;">"node1"</span>},
|
|
|
- {<span style="color: #D8FA3C;">max_pids</span>, 10},
|
|
|
- {<span style="color: #D8FA3C;">min_free</span>, 2},
|
|
|
- {<span style="color: #D8FA3C;">init_size</span>, 5}
|
|
|
- {<span style="color: #D8FA3C;">pid_starter_args</span>, <span style="color: #FF6400;">Args1</span>}],
|
|
|
-
|
|
|
-<span style="color: #FF6400;">Pool2</span> = [{<span style="color: #D8FA3C;">name</span>, <span style="color: #61CE3C;">"node2"</span>},
|
|
|
- {<span style="color: #D8FA3C;">max_pids</span>, 100},
|
|
|
- {<span style="color: #D8FA3C;">min_free</span>, 2},
|
|
|
- {<span style="color: #D8FA3C;">init_size</span>, 50}
|
|
|
- {<span style="color: #D8FA3C;">pid_starter_args</span>, <span style="color: #FF6400;">Args2</span>}],
|
|
|
-
|
|
|
-<span style="color: #FF6400;">Config</span> = [{<span style="color: #D8FA3C;">pid_starter</span>, {<span style="color: #FF6400;">M</span>, <span style="color: #FF6400;">F</span>}},
|
|
|
- {<span style="color: #D8FA3C;">pid_stopper</span>, {<span style="color: #FF6400;">M</span>, <span style="color: #FF6400;">F</span>}},
|
|
|
- {<span style="color: #D8FA3C;">pools</span>, [<span style="color: #FF6400;">Pool1</span>, <span style="color: #FF6400;">Pool2</span>]}]
|
|
|
-
|
|
|
-<span style="color: #AEAEAE; font-style: italic;">% </span><span style="color: #AEAEAE; font-style: italic;">either call this directly, or wire this
|
|
|
-</span><span style="color: #AEAEAE; font-style: italic;">% </span><span style="color: #AEAEAE; font-style: italic;">call into your application's supervisor
|
|
|
-</span><span style="color: #8DA6CE;">pidq</span>:<span style="color: #8DA6CE;">start</span>(<span style="color: #FF6400;">Config</span>)
|
|
|
-
|
|
|
-</pre>
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-<p>
|
|
|
-Each pool has a unique name, a maximum number of pids, an initial
|
|
|
-number of pids, and a minimum free pids count. When pidq starts, it
|
|
|
-will create pids to match the <code>init_size</code> value. If there are <code>min_free</code>
|
|
|
-pids or fewer, pidq will add a pid as long as that doesn't bring the
|
|
|
-total used + free count over <code>max_pids</code>.
|
|
|
-</p>
|
|
|
-<p>
|
|
|
-Specifying a <code>pid_stopper</code> function is optional. If not specified,
|
|
|
-<code>exit(pid, kill)</code> will be used to shutdown pids in the case of error,
|
|
|
-pidq shutdown, or pool removal. The function specified will be passed
|
|
|
-a pid as returned by the <code>pid_starter</code> function.
|
|
|
-</p>
|
|
|
-</div>
|
|
|
-
|
|
|
-</div>
|
|
|
-
|
|
|
-<div id="outline-container-1_3_2" class="outline-4">
|
|
|
-<h4 id="sec-1_3_2"><span class="section-number-4">1.3.2</span> Getting and returning pids </h4>
|
|
|
-<div class="outline-text-4" id="text-1_3_2">
|
|
|
-
|
|
|
-
|
|
|
-<p>
|
|
|
-Once started, the main interaction you will have with pidq is through
|
|
|
-two functions, <code>take_pid/0</code> and <code>return_pid/2</code>.
|
|
|
-</p>
|
|
|
-<p>
|
|
|
-Call <code>pidq:take_pid()</code> to obtain a pid from the pool. When you are done
|
|
|
-with it, return it to the pool using <code>pidq:return_pid(Pid, ok)</code>. If
|
|
|
-you encountered an error using the pid, you can pass <code>fail</code> as the
|
|
|
-second argument. In this case, pidq will permently remove that pid
|
|
|
-from the pool and start a new pid to replace it.
|
|
|
-</p>
|
|
|
-</div>
|
|
|
-
|
|
|
-</div>
|
|
|
-
|
|
|
-<div id="outline-container-1_3_3" class="outline-4">
|
|
|
-<h4 id="sec-1_3_3"><span class="section-number-4">1.3.3</span> Other things you can do </h4>
|
|
|
-<div class="outline-text-4" id="text-1_3_3">
|
|
|
-
|
|
|
-
|
|
|
-<p>
|
|
|
-You can get the status for the system via <code>pidq:status()</code>. This will
|
|
|
-return some informational details about the pools being managed.
|
|
|
-</p>
|
|
|
-<p>
|
|
|
-You can also add or remove new pools while pidq is running using
|
|
|
-<code>pidq:add_pool/1</code> and <code>pidq:remove_pool/1</code>. Each pid
|
|
|
-</p>
|
|
|
-</div>
|
|
|
-</div>
|
|
|
-
|
|
|
-</div>
|
|
|
-
|
|
|
-<div id="outline-container-1_4" class="outline-3">
|
|
|
-<h3 id="sec-1_4"><span class="section-number-3">1.4</span> Details </h3>
|
|
|
-<div class="outline-text-3" id="text-1_4">
|
|
|
-
|
|
|
-
|
|
|
-<p>
|
|
|
-pidq is implemented as a <code>gen_server</code>. Server state consists of:
|
|
|
-</p>
|
|
|
-<ul>
|
|
|
-<li>
|
|
|
-A dict of pools keyed by pool name.
|
|
|
-</li>
|
|
|
-<li>
|
|
|
-A dict mapping in use pids to their pool name.
|
|
|
-</li>
|
|
|
-<li>
|
|
|
-A dict mapping consumer process pids to the pid they are using.
|
|
|
-</li>
|
|
|
-<li>
|
|
|
-A module and function to use for starting new pids.
|
|
|
-
|
|
|
-</li>
|
|
|
-</ul>
|
|
|
-
|
|
|
-<p>Each pool keeps track of its parameters, such as max pids to allow,
|
|
|
-initial pids to start, number of pids in use, and a list of free pids.
|
|
|
-</p>
|
|
|
-<p>
|
|
|
-Since our motivating use-case is Riak's pb client, we opt to reuse a
|
|
|
-given client as much as possible to avoid unnecessary vector clock
|
|
|
-growth; pids are taken from the head of the free list and returned
|
|
|
-to the head of the free list.
|
|
|
-</p>
|
|
|
-<p>
|
|
|
-pidq is a system process and traps exits. Before giving out a pid, it
|
|
|
-links to the requesting consumer process. This way, if the consumer
|
|
|
-process crashes, pidq can recover the pid. When the pid is returned,
|
|
|
-the requesting process will be unlinked. Since the state of the pid
|
|
|
-is unknown in the case of a crashing consumer, we will destroy the pid
|
|
|
-and add a fresh one to the pool.
|
|
|
-</p>
|
|
|
-<p>
|
|
|
-The pid starter MFA should use spawn<sub>link</sub> so that pidq will be linked
|
|
|
-to the pids (is it confusing that we've taken the term "pid" and
|
|
|
-turned it into a noun of this system?). This way, when pids crash,
|
|
|
-pidq will be notified and can refill the pool with new pids.
|
|
|
-</p>
|
|
|
-<p>
|
|
|
-Also note that an alternative to a consumer explicitly returning a pid
|
|
|
-is for the consumer to exit normally. pidq will receive the normal
|
|
|
-exit and can reclaim the pid. In fact, we might want to implement pid
|
|
|
-return as "fake death" by sending pidq exit(PidqPid, normal).
|
|
|
-</p>
|
|
|
-
|
|
|
-</div>
|
|
|
-
|
|
|
-<div id="outline-container-1_4_1" class="outline-4">
|
|
|
-<h4 id="sec-1_4_1"><span class="section-number-4">1.4.1</span> Pool management </h4>
|
|
|
-<div class="outline-text-4" id="text-1_4_1">
|
|
|
-
|
|
|
-
|
|
|
-<p>
|
|
|
-It is an error to add a pool with a name that already exists.
|
|
|
-</p>
|
|
|
-<p>
|
|
|
-Pool removal has two forms:
|
|
|
-</p>
|
|
|
-<ul>
|
|
|
-<li>
|
|
|
-<b>graceful</b> pids in the free list are killed (using exit(pid, kill)
|
|
|
-unless a <code>pid_stopper</code> is specified in the pool parameters. No pids
|
|
|
-will be handed out from this pool's free list. As pids are
|
|
|
-returned, they are shut down. When the pool is empty, it is
|
|
|
-removed.
|
|
|
-
|
|
|
-</li>
|
|
|
-<li>
|
|
|
-<b>immediate</b> all pids in free and in-use lists are shut down; the
|
|
|
-pool is removed.
|
|
|
-
|
|
|
-
|
|
|
-</li>
|
|
|
-</ul>
|
|
|
-
|
|
|
-
|
|
|
-<pre class="src src-erlang"><span style="color: #7fffd4;">-spec</span>(<span style="color: #8DA6CE;">take_pid</span>() -> <span style="color: #94bff3;">pid</span>()).
|
|
|
-
|
|
|
-<span style="color: #7fffd4;">-spec</span>(<span style="color: #8DA6CE;">return_pid</span>(<span style="color: #94bff3;">pid</span>(), <span style="color: #D8FA3C;">ok</span> | <span style="color: #D8FA3C;">fail</span>) -> <span style="color: #D8FA3C;">ignore</span>).
|
|
|
-
|
|
|
-<span style="color: #7fffd4;">-spec</span>(<span style="color: #8DA6CE;">status</span>() -> [<span style="color: #8DA6CE;">term</span>()]).
|
|
|
-
|
|
|
-<span style="color: #7fffd4;">-type</span>(<span style="color: #8DA6CE;">pid_type_opt</span>() ::
|
|
|
- {<span style="color: #D8FA3C;">name</span>, <span style="color: #8DA6CE;">string</span>()} |
|
|
|
- {<span style="color: #D8FA3C;">max_pids</span>, <span style="color: #8DA6CE;">int</span>()} |
|
|
|
- {<span style="color: #D8FA3C;">min_free</span>, <span style="color: #8DA6CE;">int</span>()} |
|
|
|
- {<span style="color: #D8FA3C;">init_size</span>, <span style="color: #8DA6CE;">int</span>()} |
|
|
|
- {<span style="color: #D8FA3C;">pid_starter_args</span>, [<span style="color: #8DA6CE;">term</span>()]}).
|
|
|
-
|
|
|
-<span style="color: #7fffd4;">-type</span>(<span style="color: #8DA6CE;">pid_type_spec</span>() :: [<span style="color: #8DA6CE;">pid_type_opt</span>()]).
|
|
|
-<span style="color: #7fffd4;">-spec</span>(<span style="color: #8DA6CE;">add_type</span>(<span style="color: #8DA6CE;">pid_type_spec</span>()) -> <span style="color: #D8FA3C;">ok</span> | {<span style="color: #D8FA3C;">error</span>, <span style="color: #FF6400;">Why</span>}).
|
|
|
-<span style="color: #7fffd4;">-spec</span>(<span style="color: #8DA6CE;">remove_type</span>(<span style="color: #8DA6CE;">string</span>()) -> <span style="color: #D8FA3C;">ok</span> | {<span style="color: #D8FA3C;">error</span>, <span style="color: #FF6400;">Why</span>}).
|
|
|
-</pre>
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-</div>
|
|
|
-</div>
|
|
|
-</div>
|
|
|
-</div>
|
|
|
-<div id="footnotes">
|
|
|
-<h2 class="footnotes">Footnotes: </h2>
|
|
|
-<div id="text-footnotes">
|
|
|
-<p class="footnote"><sup><a class="footnum" name="fn.1" href="#fnr.1">1</a></sup> <a href="http://lists.basho.com/pipermail/riak-users_lists.basho.com/2010-September/001900.html">http://lists.basho.com/pipermail/riak-users_lists.basho.com/2010-September/001900.html</a>
|
|
|
-</p>
|
|
|
-<p class="footnote"><sup><a class="footnum" name="fn.2" href="#fnr.2">2</a></sup> <a href="http://lists.basho.com/pipermail/riak-users_lists.basho.com/2010-September/001904.html">http://lists.basho.com/pipermail/riak-users_lists.basho.com/2010-September/001904.html</a>
|
|
|
-</p>
|
|
|
-</div>
|
|
|
-</div>
|
|
|
-<div id="postamble">
|
|
|
-<p class="author"> Author: Seth Falcon
|
|
|
-</p>
|
|
|
-<p class="date"> Date: 2010-09-08 23:24:52 PDT</p>
|
|
|
-<p class="creator">HTML generated by org-mode 7.01trans in emacs 23</p>
|
|
|
-</div>
|
|
|
-</div>
|
|
|
-</body>
|
|
|
-</html>
|