Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/falcon/limiter/long_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions releases.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
32 changes: 28 additions & 4 deletions test/falcon/limiter/long_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
Loading