From bebcd662872d9409d7de33d76adef1453f3e5dfa Mon Sep 17 00:00:00 2001 From: Eliot Sykes Date: Thu, 8 Jan 2026 12:12:08 +0000 Subject: [PATCH] Use monotonic clock so postgres timeouts are unaffected by system clock changes --- lib/with_advisory_lock/core_advisory.rb | 4 ++-- test/with_advisory_lock/lock_test.rb | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/with_advisory_lock/core_advisory.rb b/lib/with_advisory_lock/core_advisory.rb index d0991e0..4c576b5 100644 --- a/lib/with_advisory_lock/core_advisory.rb +++ b/lib/with_advisory_lock/core_advisory.rb @@ -70,8 +70,8 @@ def advisory_lock_and_yield(lock_name, lock_str, lock_stack_item, options, &) def yield_with_lock_and_timeout(lock_keys, lock_name, lock_str, lock_stack_item, shared, transaction, timeout_seconds, &) - give_up_at = timeout_seconds ? Time.now + timeout_seconds : nil - while give_up_at.nil? || Time.now < give_up_at + give_up_at = timeout_seconds ? Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout_seconds : nil + while give_up_at.nil? || Process.clock_gettime(Process::CLOCK_MONOTONIC) < give_up_at r = yield_with_lock(lock_keys, lock_name, lock_str, lock_stack_item, shared, transaction, 0, &) return r if r.lock_was_acquired? diff --git a/test/with_advisory_lock/lock_test.rb b/test/with_advisory_lock/lock_test.rb index 1b2c528..de39246 100644 --- a/test/with_advisory_lock/lock_test.rb +++ b/test/with_advisory_lock/lock_test.rb @@ -200,13 +200,14 @@ def setup begin # Attempt to acquire with a short timeout - should fail quickly - start_time = Time.now - result = model_class.with_advisory_lock(lock_name, timeout_seconds: 1) { 'success' } - elapsed = Time.now - start_time + elapsed = Benchmark.realtime do + result = model_class.with_advisory_lock(lock_name, timeout_seconds: 1) { 'success' } + + # Should return false and complete within reasonable time (< 3 seconds) + # If it were using Ruby polling, it would take longer + assert_not result + end - # Should return false and complete within reasonable time (< 3 seconds) - # If it were using Ruby polling, it would take longer - assert_not result assert elapsed < 3.0, "Expected quick timeout, but took #{elapsed} seconds" ensure other_conn.query_value("SELECT RELEASE_LOCK(#{other_conn.quote(lock_keys.first)})")