diff --git a/lib/falcon/limiter/long_task.rb b/lib/falcon/limiter/long_task.rb index 687710c..b2814b1 100644 --- a/lib/falcon/limiter/long_task.rb +++ b/lib/falcon/limiter/long_task.rb @@ -77,6 +77,12 @@ def acquired? @token.acquired? end + # Check if the long task is waiting to acquire the long task token. + # @returns [Boolean] If the long task has been scheduled but not yet acquired. + def pending? + @delayed_start_task != nil + end + # Start the long task, optionally with a delay to avoid overhead for short operations def start(delay: @start_delay) # If already started, nothing to do: diff --git a/releases.md b/releases.md index 0b14eae..23a0a7b 100644 --- a/releases.md +++ b/releases.md @@ -1,5 +1,9 @@ # Releases +## Unreleased + + - Add `Falcon::Limiter::LongTask#pending?` for detecting delayed long tasks which have not acquired yet. + ## v0.3.0 - Use `Async::Limiter::Token#close` when closing sockets so cached tokens cannot re-acquire after socket close. diff --git a/test/falcon/limiter/long_task.rb b/test/falcon/limiter/long_task.rb index e631a59..0c8c061 100644 --- a/test/falcon/limiter/long_task.rb +++ b/test/falcon/limiter/long_task.rb @@ -19,7 +19,7 @@ io = Object.new io.define_singleton_method(:token){token} - stream = Object.new + stream = Object.new stream.define_singleton_method(:io){io} connection = Object.new @@ -66,10 +66,12 @@ # Start with delay long_task.start(delay: 0.01) + expect(long_task).to be(:pending?) expect(long_task).not.to be(:acquired?) # Not started yet due to delay # Wait for delay to complete sleep(0.02) + expect(long_task).not.to be(:pending?) expect(long_task).to be(:acquired?) # Stop the long task @@ -82,13 +84,35 @@ # Start with delay long_task.start(delay: 0.1) + expect(long_task).to be(:pending?) expect(long_task).not.to be(:acquired?) # Stop before delay completes long_task.stop + expect(long_task).not.to be(:pending?) expect(long_task).not.to be(:acquired?) end + it "is pending while waiting to acquire after delay" do + long_task_limiter = Falcon::Limiter::Semaphore.new(1) + token = Async::Limiter::Token.acquire(long_task_limiter) + long_task = Falcon::Limiter::LongTask.for(mock_request, long_task_limiter, start_delay: 0.01) + + long_task.start(delay: 0.01) + sleep(0.02) + + expect(long_task).to be(:pending?) + expect(long_task).not.to be(:acquired?) + + token.release + sleep(0.01) + + expect(long_task).not.to be(:pending?) + expect(long_task).to be(:acquired?) + + long_task.stop + end + it "can force stop" do long_task = Falcon::Limiter::LongTask.for(mock_request, long_task_limiter, start_delay: 0) @@ -108,7 +132,7 @@ # Should handle missing connection gracefully (no exception raised) expect do - long_task.start(delay: 0) + long_task.start(delay: 0) end.not.to raise_exception # Long task should still work even without connection token @@ -122,7 +146,7 @@ io = Object.new io.define_singleton_method(:token){token} - stream = Object.new + stream = Object.new stream.define_singleton_method(:io){io} connection = Object.new @@ -147,7 +171,7 @@ io = Object.new io.define_singleton_method(:token){token} - stream = Object.new + stream = Object.new stream.define_singleton_method(:io){io} persistent_connection = Object.new