title: Sequel (Ruby), how to increment and use a DB counter in a safe way?
body: <p>I found 4 "proper" ways to do this:</p>
  
  <ol>
  <li>In <a href="http://sequel.jeremyevans.net/rdoc/files/doc/active_record_rdoc.html" rel="nofollow">the cheat sheet for ActiveRecord users</a> substitutes for ActiveRecord's <code>increment</code> and <code>increment_counter</code> are supposed to be <code>album.values[:column] -= 1 # or += 1 for increment</code> and <code>album.update(:counter_name=&gt;Sequel.+(:counter_name, 1))</code></li>
  <li>In a <a href="https://stackoverflow.com/questions/17967550/sequel-gem-increment">SO solution</a> <code>update_sql</code> is suggested for the same effect <code>s[:query_volume].update_sql(:queries =&gt; Sequel.expr(3) + :queries)</code></li>
  <li>In a <a href="https://osdir.com/ml/lang.ruby.sequel/2007-12/msg00056.html" rel="nofollow">random thread</a> I found this one <code>dataset.update_sql(:exp =&gt; 'exp + 10'.lit)</code></li>
  <li>In <a href="http://sequel.jeremyevans.net/rdoc/classes/Sequel/Dataset.html#method-i-update" rel="nofollow">the Sequels API docs for update</a> I found this solution <code>http://sequel.jeremyevans.net/rdoc/classes/Sequel/Dataset.html#method-i-update</code></li>
  </ol>
  
  <p>yet none of the solutions actually update the value and return the result in a safe, atomic way.</p>
  
  <p>Solutions based on "adding a value and then saving" should, afaik, fail nondeterministically in multiprocessing environments resulting with errors such as:</p>
  
  <ol>
  <li>album's counter is 0</li>
  <li>thread A and thread B both fetch <code>album</code></li>
  <li>thread A and thread B both increment the value in the hash/model/etc</li>
  <li>thread A and thread B both update the counter to same value</li>
  <li>as a result: A and B both set the counter to 1 and work with counter value 1</li>
  </ol>
  
  <p><code>Sequel.expr</code> and <code>Sequel.+</code> on the other hand don't actually return a value, but a <code>Sequel::SQL::NumericExpression</code> and (afaik) you have no way of getting it out short of doing another DB roundtrip, which means this can happen:</p>
  
  <ol>
  <li>album's counter is 0</li>
  <li>thread A and B both increment the value, value is incremented by 2</li>
  <li>thread A and B both fetch the row from the DB</li>
  <li>as a result: A and B both set the counter to 2 and work with counter value 2</li>
  </ol>
  
  <p>So, short of writing custom locking code, what's the solution? If there's none, short of writing custom locking code :) what's the best way to do it?</p>
  
  <h2>Update 1</h2>
  
  <p>I'm generally not happy with answers saying that I want too much of life, as 1 answer suggests :) </p>
  
  <p>The albums are just an example from the docs. </p>
  
  <p>Imagine for example that you have a transaction counter on an e-commerce POS which can accept 2 transactions at the same time on different hosts and to the bank you need to send them with an integer counter unique in 24h (called systan), send 2 trx with same systan and 1 will be declined, or worse, gaps in the counts are alerted (because they hint at "missing transactions") so it's not possible to use the DB's ID value. </p>
  
  <p>A less severe example, but more related to my use case, several file exports get triggered simultaneously in a background worker, every file destination has its own counter. Gaps in the counters are alerted, workers are on different hosts (so mutexes are not useful). And I have a feeling I'll soon be solving the more severe problem anyway.</p>
  
  <p>The DB sequences are no good either because it would mean doing DDL on addition of every terminal, and we're talking 1000s here. Even in my less sever use case DDLing on web portal actions is still a PITA, and might even not work depending on the cacheing scheme below (due to implementation of <code>ActiveRecord</code> and <code>Sequel</code> - and in my case I use both - might require server restart just to register a merchant).</p>
  
  <p>Redis can do this, but it seems insane to add another infrastructure component just for counters when you're sitting on an ACID-compliant database.</p>