Skip to content

Commit 51869c2

Browse files
Add tags to long tasks. (#4)
1 parent 0df5936 commit 51869c2

5 files changed

Lines changed: 75 additions & 1 deletion

File tree

guides/long-tasks/readme.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,16 @@ end
128128

129129
The block form ensures the long task is properly stopped even if an exception occurs. They can also be nested.
130130

131+
### Tags
132+
133+
You can attach application-specific metadata when starting a long task:
134+
135+
```ruby
136+
Falcon::Limiter::LongTask.current.start(tags: {name: :external_api_call})
137+
```
138+
139+
Tags are stored on the long task while it is started or pending, and cleared when it stops. The limiter does not interpret tags directly; they are intended for instrumentation layers.
140+
131141
## Long Task Lifecycle
132142

133143
### 1. Creation

lib/falcon/limiter/long_task.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ class LongTask
1919
# The priority to use when stopping a long task to re-acquire the connection token.
2020
STOP_PRIORITY = 1000
2121

22+
attr_reader :tags
23+
2224
# @returns [LongTask] The current long task.
2325
def self.current
2426
Fiber.current.falcon_limiter_long_task
@@ -63,6 +65,7 @@ def initialize(request, limiter, connection_token = nil, start_delay: 0.1)
6365

6466
@token = Async::Limiter::Token.new(@limiter)
6567
@delayed_start_task = nil
68+
@tags = nil
6669
end
6770

6871
# Check if the long task has been started, but not necessarily acquired (e.g. if there was a delay).
@@ -84,7 +87,7 @@ def pending?
8487
end
8588

8689
# Start the long task, optionally with a delay to avoid overhead for short operations
87-
def start(delay: @start_delay)
90+
def start(delay: @start_delay, tags: nil)
8891
# If already started, nothing to do:
8992
if started?
9093
if block_given?
@@ -100,6 +103,8 @@ def start(delay: @start_delay)
100103
delay = nil
101104
end
102105

106+
@tags = tags
107+
103108
# Otherwise, start the long task:
104109
if delay&.positive?
105110
# Wait specified delay before starting the long task:
@@ -137,6 +142,8 @@ def stop(force: false, **options)
137142

138143
# Release the long task token:
139144
release(force, **options)
145+
ensure
146+
@tags = nil
140147
end
141148

142149
private

readme.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ Please see the [project documentation](https://socketry.github.io/falcon-limiter
2626

2727
Please see the [project releases](https://socketry.github.io/falcon-limiter/releases/index) for all releases.
2828

29+
### Unreleased
30+
31+
- Add optional `Falcon::Limiter::LongTask#start(tags:)` metadata for instrumentation.
32+
2933
### v0.4.0
3034

3135
- Add `Falcon::Limiter::LongTask#pending?` for detecting delayed long tasks which have not acquired yet.

releases.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Releases
22

3+
## Unreleased
4+
5+
- Add optional `Falcon::Limiter::LongTask#start(tags:)` metadata for instrumentation.
6+
37
## v0.4.0
48

59
- Add `Falcon::Limiter::LongTask#pending?` for detecting delayed long tasks which have not acquired yet.

test/falcon/limiter/long_task.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,28 @@
6161
expect(long_task).not.to be(:started?)
6262
end
6363

64+
it "stores tags while started and clears them when stopped" do
65+
long_task = Falcon::Limiter::LongTask.for(mock_request, long_task_limiter, start_delay: 0)
66+
tags = {name: :rpc, controller: "ProductsController"}
67+
68+
long_task.start(delay: 0, tags: tags)
69+
expect(long_task.tags).to be == tags
70+
71+
long_task.stop
72+
expect(long_task.tags).to be_nil
73+
end
74+
75+
it "stores tags during block form and clears them afterwards" do
76+
long_task = Falcon::Limiter::LongTask.for(mock_request, long_task_limiter, start_delay: 0)
77+
tags = {name: :graphql_proxy}
78+
79+
long_task.start(delay: 0, tags: tags) do |task|
80+
expect(task.tags).to be == tags
81+
end
82+
83+
expect(long_task.tags).to be_nil
84+
end
85+
6486
it "can start with delay" do
6587
long_task = Falcon::Limiter::LongTask.for(mock_request, long_task_limiter, start_delay: 0.01)
6688

@@ -91,6 +113,19 @@
91113
long_task.stop
92114
expect(long_task).not.to be(:pending?)
93115
expect(long_task).not.to be(:acquired?)
116+
expect(long_task.tags).to be_nil
117+
end
118+
119+
it "stores tags while pending" do
120+
long_task = Falcon::Limiter::LongTask.for(mock_request, long_task_limiter, start_delay: 0.1)
121+
tags = {name: :core_polling}
122+
123+
long_task.start(delay: 0.1, tags: tags)
124+
expect(long_task).to be(:pending?)
125+
expect(long_task.tags).to be == tags
126+
127+
long_task.stop
128+
expect(long_task.tags).to be_nil
94129
end
95130

96131
it "is pending while waiting to acquire after delay" do
@@ -276,4 +311,18 @@
276311
# Clean up
277312
long_task.stop(force: true)
278313
end
314+
315+
it "does not replace tags when already started" do
316+
long_task = Falcon::Limiter::LongTask.new(nil, long_task_limiter)
317+
tags = {name: :outer}
318+
319+
long_task.start(delay: 0, tags: tags)
320+
long_task.start(delay: 0, tags: {name: :inner})
321+
322+
expect(long_task.tags).to be == tags
323+
324+
# Clean up
325+
long_task.stop(force: true)
326+
expect(long_task.tags).to be_nil
327+
end
279328
end

0 commit comments

Comments
 (0)