dboard 3.2.0 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2ca84c9c7461e1f0aefa5463e6796774e3fcb41d909430f0447bf9c240609753
4
- data.tar.gz: e0f59d7466411b2dcfff3799f521271950dfab15e1322b5d15aaaca66b621ea7
3
+ metadata.gz: d1b44cea534d5b5317f70d02d39b43cd2d072b3eca3446243f0b98bba7569823
4
+ data.tar.gz: d70dee83fae380054f04250252e9956e29630b7f05a9eb49f4f14e536891ee82
5
5
  SHA512:
6
- metadata.gz: a32643a51fbdd0acb92d4b7e3c7f88db9f2749316918867e700e6582ac0956814a0789a6df1934f71017623716b55b69c8510886d3b5156f2be9183b474ddfc5
7
- data.tar.gz: e222ceed0499f6bac7622f690cc71815d095252dc6158c7db48007e595ba57b8e617bcc7e033030910ae7088071ee555f699ba93ab701753db053c0c01728f13
6
+ metadata.gz: 264dc148bf21c0dbce7173c42f412c5d55d54c27ff26124ac692485c4d7538e66dafd920adc31bb79a595b8069bcd635f07513bc7736ce81205038d5020b0e14
7
+ data.tar.gz: 7640bd50fab1e0a0f43641462657482ba996bff8e0185e6d76f6537a6c2fafdcfe5467201cbd8f45fe5508bc396ddcb5b5bef61ed525c82e80a39236f61237e2
@@ -27,27 +27,3 @@ jobs:
27
27
  bundler-cache: true # runs 'bundle install' and caches installed gems automatically
28
28
  - name: Run tests
29
29
  run: bundle exec rake
30
-
31
- release:
32
- if: github.ref_name == github.event.repository.default_branch
33
- needs: test
34
- environment: Rubygems
35
- runs-on: ubuntu-latest
36
- steps:
37
- - uses: actions/checkout@v7
38
- - name: Set up Ruby
39
- uses: ruby/setup-ruby@v1
40
- with:
41
- ruby-version: "3.4"
42
- bundler-cache: true # runs 'bundle install' and caches installed gems automatically
43
- - name: Publish gem
44
- run: |
45
- mkdir -p $HOME/.gem
46
- touch $HOME/.gem/credentials
47
- chmod 0600 $HOME/.gem/credentials
48
- printf -- '---\n:rubygems_api_key: %s\n' "$RUBYGEMS_API_KEY" > "$HOME/.gem/credentials"
49
- sed -i -E "s/(VERSION = \"[0-9.]+)\"/\1.${GITHUB_RUN_NUMBER}\"/" lib/version.rb # Add build number to version
50
- gem build *.gemspec
51
- gem push *.gem
52
- env:
53
- RUBYGEMS_API_KEY: "${{secrets.RUBYGEMS_API_KEY}}"
data/README.md CHANGED
@@ -23,19 +23,13 @@ Things dboard do for you:
23
23
 
24
24
  Refreshing sources:
25
25
 
26
- Each source defines `update_interval` (seconds), and the collector polls it on that cadence.
26
+ Each source defines `update_interval` (seconds); the collector polls it on that cadence and pushes the result to the dashboard.
27
27
 
28
- You can also trigger an out-of-band refresh (e.g. from an inbound webhook) with `Dboard::Collector.request_update(:key)`, where `:key` is the key the source was registered under. This is throttled: the first trigger refreshes immediately, and further triggers inside the floor window are coalesced into a single trailing refresh, so a flood of triggers never causes more than one refresh per floor. The poll and manual triggers share one clock, so a manual refresh lets the next poll cycle skip its fetch.
28
+ To refresh a source between scheduled polls (e.g. from an inbound webhook), call `Dboard::Collector.request_update(:key)`, where `:key` is the key the source was registered under. On-demand refreshes are throttled: the first fires immediately and rapid repeats collapse into at most one refresh per floor. The floor defaults to 30 seconds; a source can lower it with `min_update_interval` (seconds), capped at its `update_interval`.
29
29
 
30
- The floor defaults to 30 seconds. A source may override it by defining `min_update_interval` (seconds). The effective floor is capped at the source's `update_interval`, so it can never exceed the poll interval.
30
+ For a targeted refresh, pass an argument: `Dboard::Collector.request_update(:key, arg)`. A source receives it by defining `fetch(args = nil)`: `args` is `nil` for a full refresh and an array of the requested arguments for a targeted one, so the source can refresh only what changed (e.g. the single project named in a webhook).
31
31
 
32
- Targeted refreshes:
33
-
34
- `request_update` also takes an optional argument for a targeted refresh: `Dboard::Collector.request_update(:key, arg)`. The argument is opaque; the framework forwards it verbatim to the source and never inspects it. Omitting it (or passing `nil`) means a full refresh of the whole source.
35
-
36
- A source opts in by defining `fetch(args = nil)`. It receives `nil` for a full refresh (a scheduled poll or a no-arg `request_update`) and an array of the accumulated arguments for a targeted refresh. Arguments are batched the same way triggers are coalesced: the leading refresh carries the first argument, and any arguments that arrive during the active window are delivered together in the one trailing refresh. A no-arg (full) request queued during a burst wins over pending arguments, since a full refresh already covers them. The poll shares the same clock and floor as targeted refreshes, so both together still fetch at most once per floor.
37
-
38
- Publishing stays wholesale: the collector always replaces the stored blob for the key. A source doing a targeted refresh must therefore read its current state (e.g. from the cache) and return the full merged blob, not only the refreshed part.
32
+ Publishing is wholesale: the collector always replaces the stored blob for a key, so a targeted refresh must return the full merged blob (reading current state from the cache if needed), not only the part it refreshed.
39
33
 
40
34
  Data flow:
41
35
 
data/lib/collector.rb CHANGED
@@ -50,7 +50,8 @@ module Dboard
50
50
  wait_a_little_bit_to_not_start_all_fetches_at_once
51
51
 
52
52
  loop do
53
- update_in_thread(source, instance)
53
+ request_update(source)
54
+ sleep instance.update_interval
54
55
  end
55
56
  end
56
57
  end
@@ -110,11 +111,15 @@ module Dboard
110
111
  private
111
112
 
112
113
  def fetch_source(instance, batch)
113
- if batch.nil? || batch.any? { |entry| entry.equal?(FULL) }
114
- instance.fetch
115
- else
116
- instance.fetch(batch)
117
- end
114
+ full_batch?(batch) ? instance.fetch : instance.fetch(batch)
115
+ end
116
+
117
+ def full_batch?(batch)
118
+ batch.nil? || batch.any? { |entry| entry.equal?(FULL) }
119
+ end
120
+
121
+ def describe_batch(batch)
122
+ full_batch?(batch) ? "full" : "targeted: #{batch.join(", ")}"
118
123
  end
119
124
 
120
125
  def decide_request(active, last_update_at, floor, now)
@@ -182,25 +187,13 @@ module Dboard
182
187
  }
183
188
  return false if batch.equal?(SKIP)
184
189
 
190
+ started = monotonic_now
185
191
  update_source(source, instance, batch)
192
+ puts "#{source} refreshed (#{describe_batch(batch)}) in #{(monotonic_now - started).round(1)}s"
186
193
  true
187
194
  }
188
195
  end
189
196
 
190
- def update_in_thread(source, instance)
191
- puts "#{source} polling..."
192
- fired = perform_refresh(source, instance, instance.update_interval)
193
- time_until_next_update = @mutex.synchronize {
194
- remaining = instance.update_interval - (monotonic_now - @last_update_at[source])
195
- remaining < 0 ? 0 : remaining
196
- }
197
- puts "#{source} #{fired ? "updated" : "still fresh"}, will poll again in #{time_until_next_update} seconds (interval: #{instance.update_interval})."
198
- sleep time_until_next_update
199
- rescue Exception => ex
200
- puts "Something failed outside the update_source method. #{ex.message}"
201
- puts ex.backtrace
202
- end
203
-
204
197
  def min_interval_for(instance)
205
198
  override = instance.respond_to?(:min_update_interval) ? instance.min_update_interval : DEFAULT_MIN_INTERVAL
206
199
  [ override, instance.update_interval ].min
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Dboard
2
- VERSION = "3.2.0"
2
+ VERSION = "3.2.1"
3
3
  end
@@ -245,19 +245,6 @@ RSpec.describe Dboard::Collector do
245
245
  end
246
246
 
247
247
  describe "the shared clock" do
248
- it "lets a manual refresh make the next poll cycle skip its fetch" do
249
- source = double(:source, update_interval: 100)
250
- allow(source).to receive(:fetch).and_return({ ok: true })
251
- collector.register_source(:src, source)
252
- use_fake_clock
253
- run_workers_inline
254
-
255
- collector.request_update(:src)
256
- collector.send(:update_in_thread, :src, source)
257
-
258
- expect(source).to have_received(:fetch).once
259
- end
260
-
261
248
  it "re-evaluates a scheduled trailing when a poll refreshes during its sleep" do
262
249
  source = double(:source, update_interval: 100, min_update_interval: 10)
263
250
  clock = FakeClock.new
@@ -387,26 +374,6 @@ RSpec.describe Dboard::Collector do
387
374
  expect(Dboard::Publisher).to have_received(:publish).with(:src, { ok: true })
388
375
  end
389
376
 
390
- it "leaves pending args untouched when a poll wins and delivers them on the next on-demand refresh" do
391
- source = double(:source, update_interval: 100)
392
- fetch_args = []
393
- allow(source).to receive(:fetch) { |*args| fetch_args << args.first; { ok: true } }
394
- collector.register_source(:src, source)
395
- use_fake_clock
396
- workers = capture_worker_blocks
397
-
398
- collector.request_update(:src, "a")
399
- collector.request_update(:src, "p")
400
- expect(collector.instance_variable_get(:@pending)[:src]).to eq([ "p" ])
401
-
402
- collector.send(:update_in_thread, :src, source)
403
- expect(collector.instance_variable_get(:@pending)[:src]).to eq([ "p" ])
404
-
405
- workers.each(&:call)
406
-
407
- expect(fetch_args).to eq([ nil, [ "a" ], [ "p" ] ])
408
- end
409
-
410
377
  it "retries the retained leading snapshot when a poll refreshes before it runs" do
411
378
  source = double(:source, update_interval: 100)
412
379
  fetch_args = []
@@ -421,20 +388,6 @@ RSpec.describe Dboard::Collector do
421
388
 
422
389
  expect(fetch_args).to eq([ [ "p" ] ])
423
390
  end
424
-
425
- it "shares one clock between a partial refresh and the poll" do
426
- source = double(:source, update_interval: 100)
427
- allow(source).to receive(:fetch).and_return({ ok: true })
428
- collector.register_source(:src, source)
429
- use_fake_clock
430
- run_workers_inline
431
-
432
- collector.request_update(:src, "p")
433
- collector.send(:update_in_thread, :src, source)
434
-
435
- expect(source).to have_received(:fetch).with([ "p" ]).once
436
- expect(source).to have_received(:fetch).with(no_args).exactly(0).times
437
- end
438
391
  end
439
392
 
440
393
  describe "under real threads" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dboard
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joakim Kolsjö