<div dir="ltr">Dear List<div><br></div><div>I need some Mnesia help, please. I will try to be precise in my question and give appropriate context: I am really grateful to all of you who help the rest of us cultivate some skill with Erlang.</div><div><div><br></div><div>I developed a special-purpose, in-memory distributed cache for our company's Java application. It has been very successful thus far. My need was for a cache consisting of about 10,000 rows that needed to be distributed amongst 2 - 4 nodes and be capable of handling 100s of transactions per second at most. All operations (put, evict, etc.) can happen at any node.</div><div><br></div><div>My current version has been in production, doing fine. According to basho_bench (admittedly synthetic), the cache is good to about 20,000 ops per second with mean latency for puts at 0.75 ms at the 95th percentile.</div><div><br></div><div>And, up until now, all transactions have been in a sync-dirty context.<br></div><div><div><br></div><div>As is typical in these types of environments we have a race-condition that we are trying to solve. Java client, A, does a put:(K, V0) to the cache while, at about the same time, another Java client, B, puts an updated version of the same key - put:(K,V1). And, of course, it can happen that the second write gets presented to the cache first putting the "newest" value, V1, in the cache and then the first write gets presented to the cache putting the "stale" V0 in the cache.</div><div><br></div><div>Our strategy for addressing this is that the Java application will include a strictly monotonic sequence number with each Put. The cache will maintain a table that persists the highest sequence number that it has seen and if a Put with a smaller sequence number is presented to the cache, the cache rejects the Put; otherwise it writes the new sequence to the sequence table and does the Put.</div><div><br></div><div>My implementation of the above involves having a replicated table which persists the highest sequence number seen, and I wrap the following steps in an async_transaction </div><div>1. Compare the sequence number in the Put with the "high-water-mark" sequence number</div><div>2. If the Put's sequence number is higher, do the put and update the high-water-mark;</div><div>3. else, abandon the put</div><div><br></div><div>I knew that there would be a cost to a non-dirty transaction, but I was really surprised at the magnitude of the cost. Puts went from </div><div>20,000 ops per second with mean latency at 0.75 ms to 1,500 ops per second with mean latency of 12ms.<br></div><div><br></div><div>So, now to my questions:</div><div>1. If I am trying to transactionally protect this sequence table, do I need to use sync_transaction or will async_transaction work. I don't understand what an ACID transaction means when there are more than 1 node and a transaction context which is async. If two Puts are happening on two different nodes, will one block until the other completes EVEN when in async mode?</div><div>2. is the magnitude of the performance hit seem "normal"?</div><div>3. Is there a better way to achieve what I am looking for then the above?</div><div><br></div><div><br></div><div>Thanks very much... and some code if it helps...</div><div><br></div><div><br></div><div><div>put(Key, Value, SeqNo) -></div><div>    F = fun() -></div><div><span class="Apple-tab-span" style="white-space:pre">              </span>case mnesia:wread(seq, global) of</div><div><span class="Apple-tab-span" style="white-space:pre">            </span>    [] -></div><div><span class="Apple-tab-span" style="white-space:pre">                       </span>simple_put(Key, Value),</div><div><span class="Apple-tab-span" style="white-space:pre">                      </span>mnesia:write(#seq{key=global, value = SeqNo});</div><div><span class="Apple-tab-span" style="white-space:pre">               </span>    [#seq{value = Old}=S] when SeqNo > Old -></div><div><span class="Apple-tab-span" style="white-space:pre">                        </span>simple_put(Key, Value),</div><div><span class="Apple-tab-span" style="white-space:pre">                      </span>mnesia:write(S#seq{value = SeqNo});</div><div><span class="Apple-tab-span" style="white-space:pre">          </span>    [#seq{value = Old}] -></div><div><span class="Apple-tab-span" style="white-space:pre">                      </span>lager:warning("~p: out of order put. Map: ~p, Old: ~p, New: ~p.", </div><div><span class="Apple-tab-span" style="white-space:pre">                                </span>      [?MODULE, Old, SeqNo]),</div><div><span class="Apple-tab-span" style="white-space:pre">                     </span>{out_of_seq, {put, Key, Old, SeqNo}}</div><div><span class="Apple-tab-span" style="white-space:pre">         </span>end</div><div><span class="Apple-tab-span" style="white-space:pre">  </span>end,</div><div>    case mnesia:sync_transaction(F) of</div><div><span class="Apple-tab-span" style="white-space:pre">  </span>{atomic, {out_of_seq, _}} = R -></div><div><span class="Apple-tab-span" style="white-space:pre">  </span>    R;</div><div><span class="Apple-tab-span" style="white-space:pre">     </span>{atomic, Result} -></div><div><span class="Apple-tab-span" style="white-space:pre">       </span>    Result;</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>{aborted, Reason} -></div><div><span class="Apple-tab-span" style="white-space:pre">      </span>    {error, Reason}</div></div><div>    end.</div><div><br></div><div><br></div><div><br></div><div><br></div><div>e</div></div></div></div>