<div dir="ltr">I think the key is to keep the transactions small. If you notice, a lot of the volume allocations pass *db to the functions so they can use the db when needed.  Also, none of the calls in a Tx context call out to glusterfs because it would be time consuming and single threaded.  Instead state is saved on the db, the tx is finished, and the work can the be started on gluster.  If there is an error or the db needs to be update, it will do it after the completion of the work, which could have (and most likely was) done in parallel.<div><br></div><div>I think for the PR proposed, the SetState() call could take *db instead of *tx.  If it is the simple ones (enable/disable) then the change can be done, and the function returns.</div><div><br></div><div>If it is &quot;remove&quot;, then state of the device in the db can be changed, then db can be released and work can be started on that device.  Then SetState() can return to the asynchttp routine with a result.  The key thing here is that all bricks used to replace the bricks on the removed device where replaced in parallel.</div><div><br></div><div>- Luis</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Mar 9, 2017 at 5:03 PM, Raghavendra Talur <span dir="ltr">&lt;<a href="mailto:rtalur@redhat.com" target="_blank">rtalur@redhat.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On Fri, Mar 10, 2017 at 3:21 AM, Michael Adam &lt;<a href="mailto:obnox@samba.org">obnox@samba.org</a>&gt; wrote:<br>
&gt; Hi Talur,<br>
&gt;<br>
&gt; Thanks for this write-up!<br>
&gt;<br>
&gt; It sounds all very consistent.<br>
&gt;<br>
&gt; But I will want to thoroughly look at the code.<br>
&gt; The Mutex thing sounds a bit like complete<br>
&gt; serialization (for write processes).<br>
<br>
</span>Yes, for small functions, this would completely serialize the<br>
goroutines and essentially we lose all the benefits. I could not come<br>
up with any better solution though.<br>
<div class="HOEnZb"><div class="h5"><br>
<br>
&gt;<br>
&gt; Cheers - Michael<br>
&gt;<br>
&gt; On 2017-03-10 at 02:03 +0530, Raghavendra Talur wrote:<br>
&gt;&gt; Here is what Bolt documentation says about transactions:<br>
&gt;&gt;<br>
&gt;&gt; ```<br>
&gt;&gt; Transactions<br>
&gt;&gt;<br>
&gt;&gt; Bolt allows only one read-write transaction at a time but allows as<br>
&gt;&gt; many read-only transactions as you want at a time. Each transaction<br>
&gt;&gt; has a consistent view of the data as it existed when the transaction<br>
&gt;&gt; started.<br>
&gt;&gt;<br>
&gt;&gt; Individual transactions and all objects created from them (e.g.<br>
&gt;&gt; buckets, keys) are not thread safe. To work with data in multiple<br>
&gt;&gt; goroutines you must start a transaction for each one or use locking to<br>
&gt;&gt; ensure only one goroutine accesses a transaction at a time. Creating<br>
&gt;&gt; transaction from the DB is thread safe.<br>
&gt;&gt; ```<br>
&gt;&gt;<br>
&gt;&gt; Now, in heketi, there some operations at app.* level that might have<br>
&gt;&gt; to view/update many buckets(node,brick,volumes,<wbr>device). The methods of<br>
&gt;&gt; these objects take either a *bolt.Tx or a *bolt.DB.<br>
&gt;&gt;<br>
&gt;&gt; In my understanding, there are some app.* operations where it is a<br>
&gt;&gt; must that all the methods that are called within it, should get the<br>
&gt;&gt; same view of db; in that case, it makes sense that app.* function<br>
&gt;&gt; starts a tx and hands it down to the lower methods thereby ensuring a<br>
&gt;&gt; consistent view.<br>
&gt;&gt;<br>
&gt;&gt; Some methods however, like Create and Destroy methods of brick have<br>
&gt;&gt; been written to take a *bolt.DB as a argument. This seems to be done<br>
&gt;&gt; to enable use of goroutines. Now, we have functions in the higher<br>
&gt;&gt; layer calling these very methods which would want the operation to be<br>
&gt;&gt; done in the transaction created above.<br>
&gt;&gt;<br>
&gt;&gt; In PR #710 I have made two changes to tackle the above problem:<br>
&gt;&gt; 1. Changed these methods to take *bolt.Tx as arg instead of *bolt.DB<br>
&gt;&gt; 2. Introduce a sync.Mutex  as another member of StatusGroup, any<br>
&gt;&gt; goroutines that want to operate on the db using a shared tx must call<br>
&gt;&gt; the new Lock() method of StatusGroup and call Unlock() after the db<br>
&gt;&gt; operation is done.<br>
&gt;&gt;<br>
&gt;&gt; In summary, as bolt documentation suggests, to work with data in<br>
&gt;&gt; multiple go routines:<br>
&gt;&gt; we used to: start a transaction for each one<br>
&gt;&gt; now we do: mutex locking to ensure only one goroutine accesses a<br>
&gt;&gt; transaction at a time<br>
&gt;&gt;<br>
&gt;&gt; If there are better ideas, they are most welcome.<br>
&gt;&gt;<br>
&gt;&gt; Thanks,<br>
&gt;&gt; Raghavendra Talur<br>
&gt;&gt; ______________________________<wbr>_________________<br>
&gt;&gt; heketi-devel mailing list<br>
&gt;&gt; <a href="mailto:heketi-devel@gluster.org">heketi-devel@gluster.org</a><br>
&gt;&gt; <a href="http://lists.gluster.org/mailman/listinfo/heketi-devel" rel="noreferrer" target="_blank">http://lists.gluster.org/<wbr>mailman/listinfo/heketi-devel</a><br>
______________________________<wbr>_________________<br>
heketi-devel mailing list<br>
<a href="mailto:heketi-devel@gluster.org">heketi-devel@gluster.org</a><br>
<a href="http://lists.gluster.org/mailman/listinfo/heketi-devel" rel="noreferrer" target="_blank">http://lists.gluster.org/<wbr>mailman/listinfo/heketi-devel</a><br>
</div></div></blockquote></div><br></div>