{"id":13302,"date":"2025-08-21T06:19:00","date_gmt":"2025-08-21T06:19:00","guid":{"rendered":"https:\/\/www.bacancytechnology.com\/qanda\/?p=13302"},"modified":"2025-08-21T06:19:00","modified_gmt":"2025-08-21T06:19:00","slug":"make-count-thread-safe","status":"publish","type":"post","link":"https:\/\/www.bacancytechnology.com\/qanda\/ruby-on-rails\/make-count-thread-safe","title":{"rendered":"How to Make Count thread-safe?"},"content":{"rendered":"<h3>Option 1: Use update_counters<\/h3>\n<p>Rails provides a built-in method:<br \/>\n<code>Foobar.increment_counter(:foo_count, foobar.id)<\/code><br \/>\nThis translates into a SQL query like:<br \/>\n<code>UPDATE foobars SET foo_count = foo_count + 1 WHERE id = X<\/code><br \/>\nBecause this happens directly in the database in one atomic operation, it&#8217;s thread-safe and web-instance-safe.<\/p>\n<h3>Option 2: Raw SQL<\/h3>\n<p>If you prefer, or need something custom:<br \/>\n<code>Foobar.where(id: foobar.id).update_all(\"foo_count = foo_count + 1\")<\/code><\/p>\n<h3>Option 3: Database Locking (Pessimistic Lock)<\/h3>\n<p>If you absolutely need to perform other operations with the updated object:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">\r\nFoobar.transaction do\r\n  foobar = Foobar.lock(true).find(foobar.id)\r\n  foobar.foo_count += 1\r\n  foobar.save!\r\nend\r\n<\/pre>\n<p>This uses a SELECT &#8230; FOR UPDATE query, locking the row until the transaction completes. But be cautious: this can reduce performance under high concurrency.<\/p>\n<h3>Option 4: PostgreSQL Triggers (Database-level)<\/h3>\n<p>Move the increment logic into the database using a trigger:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">\r\nCREATE FUNCTION increment_foo_count()\r\nRETURNS TRIGGER AS $$\r\nBEGIN\r\n  UPDATE foobars SET foo_count = foo_count + 1 WHERE id = NEW.foobar_id;\r\n  RETURN NEW;\r\nEND;\r\n\r\nCREATE TRIGGER trigger_increment_foo_count\r\nAFTER INSERT ON foo_count_events\r\nFOR EACH ROW\r\nEXECUTE FUNCTION increment_foo_count();\r\n<\/pre>\n<p>Then in Rails, you insert a row into foo_count_events, and the database handles the rest.<\/p>\n<div class=\"qanda-read-box\"><div class=\"bg-light read-more-icon\"><img decoding=\"async\" src=\"https:\/\/assets.bacancytechnology.com\/qanda\/wp-content\/uploads\/2025\/04\/24061434\/read-txt.png\" alt=\"Also Read\"><p><\/p><h3>Also Read:<\/h3><a href=\"https:\/\/www.bacancytechnology.com\/blog\/ruby-on-rails-gems\" target=\"_blank\">Best Ruby On Rails Gems<\/a><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Option 1: Use update_counters Rails provides a built-in method: Foobar.increment_counter(:foo_count, foobar.id) This translates into a SQL query like: UPDATE foobars SET foo_count = foo_count + 1 WHERE id = X Because this happens directly in the database in one atomic operation, it&#8217;s thread-safe and web-instance-safe. Option 2: Raw SQL If you prefer, or need something [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":13304,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[11],"tags":[],"class_list":["post-13302","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ruby-on-rails"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/13302"}],"collection":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/comments?post=13302"}],"version-history":[{"count":2,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/13302\/revisions"}],"predecessor-version":[{"id":13305,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/13302\/revisions\/13305"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/media\/13304"}],"wp:attachment":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/media?parent=13302"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/categories?post=13302"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/tags?post=13302"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}