bidi2pdf 0.1.18 → 0.1.19

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: fa0fc469533226ce79d16bb401f4c47093dda42d4a68af67453fab79e086ad65
4
- data.tar.gz: 9e4a1176ad42c03ced1544dcb9022a3dfe7d2663b0d8960f41bea863d3b0e788
3
+ metadata.gz: 60f338197fa95b778df1e96b3cb64c899e49c0a90b22ef9577b51f1a2eb85657
4
+ data.tar.gz: 13cded6812c061d65d0ac42579c29d5614007b08056094557b71902d347d6682
5
5
  SHA512:
6
- metadata.gz: ddd7621c394b454a1df5b6071f474b1bd8699a4de4086e47c7185b01df66476ccad28d7e843b17e79aebf198489053bc237c345a3f638fb4e25e907aa4dc04f1
7
- data.tar.gz: 134e15de59f8d5d7044be15308b1490988bc7eddd0134a9de05feed3accf3d56c52818125da49d36ad91263f6d16d8e58dc6549c85f5897316e9dfab7e60fb73
6
+ metadata.gz: c26da1531bd782b7e858eae98a53d3ec802f82f870b7649bcd40750dce747232ef2cb317a9881241e4ae49a08d70552be7e93923e84aad3837cd4dc1ce218359
7
+ data.tar.gz: 62ee4b751910b2631698797b38d91e2787f37d053d52b5af0e372fe5f76dcb27b06a10bcff43a630208916e7afadd8fee650877dfc2c6acbbfaa07814e0de025
data/CHANGELOG.md CHANGED
@@ -8,10 +8,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
- [unreleased]: https://github.com/dieter-medium/bidi2pdf/compare/v0.1.18..HEAD
11
+ [unreleased]: https://github.com/dieter-medium/bidi2pdf/compare/v0.1.19..HEAD
12
12
 
13
13
  <!-- generated by git-cliff end -->
14
14
 
15
+ ## [0.1.19] - 2026-09-30
16
+
17
+ ### 🐛 Fixed
18
+ - Keep lease times to the fraction of a second
19
+ - Let every lease carry its own TTL
20
+
21
+ ### 🔄 Changed
22
+ - Merge pull request #149 from dieter-medium/feat/lease-expiry
23
+
15
24
  ## [0.1.18] - 2026-09-30
16
25
 
17
26
  ### 🐛 Fixed
@@ -514,7 +523,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
514
523
 
515
524
  ### 🔄 Released
516
525
 
517
- - [unreleased](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.18..HEAD)
526
+ - [unreleased](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.19..HEAD)
527
+ - [0.1.19](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.18..v0.1.19)
518
528
  - [0.1.18](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.17..v0.1.18)
519
529
  - [0.1.17](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.16..v0.1.17)
520
530
  - [0.1.16](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.15..v0.1.16)
data/README.md CHANGED
@@ -572,7 +572,8 @@ Bidi2pdf::SessionWarmer.shutdown
572
572
  | `remote_browser_url` | `nil` | Connect each slot to a remote chromedriver instead of starting a local one. |
573
573
  | `orphan_age` | `:auto` | Remote only: on start, close sessions other warmers left behind older than this (`:auto` = 2 × `max_idle_age`, `nil` = off). |
574
574
  | `registry_dir` | `Dir.tmpdir` | Where the session registry file lives - every process that should clean up after the others must share it. |
575
- | `sweeper` | `nil` | Remote only: settings for a [`ChromeSweeper`](#leaked-chrome-sessions-bidi2pdfchromesweeper) on the same chromedriver, e.g. `{ scope: :all, max_sessions: :auto, pids_limit: 1024, interval: 60 }`. The warmer's own sessions are never touched; `Bidi2pdf::SessionWarmer.sweep!` sweeps on demand. With a sweeper the warmer records its sessions even when `orphan_age` is `nil`. |
575
+ | `sweeper` | `nil` | Remote only: settings for a [`ChromeSweeper`](#leaked-chrome-sessions-bidi2pdfchromesweeper) on the same chromedriver, e.g. `{ scope: :all, max_sessions: :auto, pids_limit: 1024, interval: 60 }`. The warmer's own sessions are never touched; `Bidi2pdf::SessionWarmer.sweep!` sweeps on demand. |
576
+ | `retry_refused_sessions` | `true` | With a `sweeper`: when chromedriver refuses a new session, sweep under pressure and try once more. `false` leaves it to the caller. |
576
577
 
577
578
  #### Leftover sessions on a shared chromedriver
578
579
 
@@ -634,9 +635,11 @@ those, it closes nothing more and reports `limit_exceeded`. A session is closed
634
635
  **Live sessions of other processes (leases).** Several processes often share one chromedriver - Puma
635
636
  workers, a job worker - and each has renders in flight and warm spares the others know nothing
636
637
  about. So every session bidi2pdf opens is recorded in the registry with a lease, and a heartbeat
637
- thread in the owning process renews it every 20 s while the session is open. A session whose lease
638
- is younger than `lease_ttl` belongs to a live process: no sweeper in any process closes it, and it
639
- is not even inspected. When the process dies - killed, crashed, OOM - the lease runs out and the
638
+ thread in the owning process renews it every 20 s (`SessionRegistry::Heartbeat.interval`) while the
639
+ session is open. Every lease carries its own TTL - three heartbeats of the process that wrote it -
640
+ so a sweeper never has to guess how often another process renews. A session whose lease is still
641
+ fresh belongs to a live process: no sweeper in any process closes it, and it is not even inspected.
642
+ The warmer leases its sessions in remote mode too, with or without a sweeper. When the process dies - killed, crashed, OOM - the lease runs out and the
640
643
  session becomes a leftover like any other. Only processes that share the registry directory see
641
644
  each other's leases: give job workers in another container the same `registry_dir` on a shared
642
645
  volume. Sessions other tools opened have no lease; under `scope: :all` only `min_age` protects them.
@@ -664,7 +667,7 @@ session is refused.
664
667
  | `dry_run` | `false` | Report what would be closed, close nothing. |
665
668
  | `registry_dir` | `Dir.tmpdir`| The registry to read and update - same meaning as the warmer's setting. |
666
669
  | `own_sessions` | `-> { [] }` | A callable returning the caller's live session ids; they are never touched. |
667
- | `lease_ttl` | `60` | A recorded session renewed within this many seconds belongs to a live process and is never touched. |
670
+ | `lease_ttl` | `60` | Only for registry entries written by bidi2pdf 0.1.18, which carry no TTL of their own: renewed within this many seconds counts as live. |
668
671
 
669
672
  How it tells a session's age: chromedriver's `GET /sessions` lists every session but no start time,
670
673
  and Chrome keeps none either. So the sweeper uses the registry time when there is one and otherwise
@@ -23,8 +23,9 @@ module Bidi2pdf
23
23
  # @!attribute threads_per_session [Integer] threads (Docker counts them as pids) of one session.
24
24
  # @!attribute interval [Numeric, nil] seconds between sweeps of ChromeSweeper#start's thread.
25
25
  # @!attribute dry_run [Boolean] report what would be closed, close nothing.
26
- # @!attribute lease_ttl [Numeric] a recorded session renewed within this many seconds belongs to
27
- # a live process and is never closed (SessionRegistry#hold).
26
+ # @!attribute lease_ttl [Numeric] for registry entries without a TTL of their own (written by
27
+ # bidi2pdf 0.1.18): renewed within this many seconds means a live process holds it. Entries
28
+ # written since carry the TTL their owner's heartbeat promises (SessionRegistry#hold).
28
29
  Settings = Data.define(:scope, :orphan_age, :min_age, :unresponsive_checks, :max_sessions, :pids_limit,
29
30
  :pids_budget, :threads_per_session, :interval, :dry_run, :lease_ttl) do
30
31
  # rubocop:disable-next Metrics/ParameterLists
@@ -233,7 +233,7 @@ module Bidi2pdf
233
233
  end
234
234
 
235
235
  def live_sessions
236
- @registry.leased(now: now.to_i, ttl: lease_ttl)
236
+ @registry.leased(now: now, ttl: lease_ttl)
237
237
  end
238
238
 
239
239
  def live_info(id, recorded_at)
@@ -20,6 +20,9 @@ module Bidi2pdf
20
20
 
21
21
  def interval = @interval || DEFAULT_INTERVAL
22
22
 
23
+ # The TTL this process writes into its leases: three missed renewals and it is taken for dead.
24
+ def lease_ttl = interval * 3
25
+
23
26
  def hold(registry, session_id)
24
27
  synchronize do
25
28
  (held[registry.path] ||= [registry, Set.new])[1] << session_id.to_s
@@ -43,9 +46,9 @@ module Bidi2pdf
43
46
  end
44
47
 
45
48
  # Renews every held lease now.
46
- def beat!(now: Time.now.to_i)
49
+ def beat!(now: Time.now.to_f)
47
50
  synchronize { held.values.map { |registry, ids| [registry, ids.to_a] } }
48
- .each { |registry, ids| registry.renew(ids, at: now) }
51
+ .each { |registry, ids| registry.renew(ids, at: now, ttl: lease_ttl) }
49
52
  end
50
53
 
51
54
  private
@@ -14,8 +14,10 @@ module Bidi2pdf
14
14
  # accepted but echoed nowhere - checked against chromedriver 153), hence a file.
15
15
  #
16
16
  # Liveness is a lease: a session the process #hold s is renewed by its Heartbeat every
17
- # Heartbeat.interval seconds. A session whose lease is fresh belongs to a live process - a render
18
- # in flight or a warm spare - and no sweeper closes it; one whose lease ran out belongs to a dead
17
+ # Heartbeat.interval seconds, and every entry carries its own TTL - the time the owner promises to
18
+ # renew it within (three heartbeats) - so a sweeper in another process never has to guess how
19
+ # often that owner renews. A session whose lease is fresh belongs to a live process - a render in
20
+ # flight or a warm spare - and no sweeper closes it; one whose lease ran out belongs to a dead
19
21
  # process. #record alone writes an entry nobody renews.
20
22
  #
21
23
  # Everything here is best effort and fail-open: a directory that is not writable, a locked-out or
@@ -25,7 +27,8 @@ module Bidi2pdf
25
27
  # the directory (Puma workers, a job worker, a spec run) can record at the same time.
26
28
  class SessionRegistry
27
29
  FILE_PREFIX = "bidi2pdf-sessions-"
28
- # Seconds a lease stays fresh without a renewal.
30
+ # Seconds a lease stays fresh without a renewal when its entry carries no TTL of its own (written
31
+ # by bidi2pdf 0.1.18) - matches that version's fixed 20 s heartbeat.
29
32
  DEFAULT_LEASE_TTL = 60
30
33
 
31
34
  attr_reader :path
@@ -41,7 +44,7 @@ module Bidi2pdf
41
44
  def hold(session_id)
42
45
  return false if session_id.nil?
43
46
 
44
- recorded = record(session_id)
47
+ recorded = record(session_id, ttl: Heartbeat.lease_ttl)
45
48
  Heartbeat.hold(self, session_id)
46
49
  recorded
47
50
  end
@@ -52,12 +55,15 @@ module Bidi2pdf
52
55
  Heartbeat.release(self, session_id) unless session_id.nil?
53
56
  end
54
57
 
55
- # Writes an entry, leased from +created_at+ on but not renewed (see #hold).
56
- def record(session_id, created_at: Time.now.to_i)
58
+ # Writes an entry, leased from +created_at+ on for +ttl+ seconds (nil: the reader's default) but
59
+ # not renewed (see #hold).
60
+ # Times are stored as fractions of a second: a whole-second timestamp could cost a short lease
61
+ # (0.5 s heartbeats: 1.5 s) up to a second of its life. Entries of whole seconds still read.
62
+ def record(session_id, created_at: Time.now.to_f, ttl: nil)
57
63
  return false if session_id.nil?
58
64
 
59
65
  update do |entries|
60
- entries[session_id.to_s] = { "created_at" => created_at.to_i, "renewed_at" => created_at.to_i, "owner" => self.class.owner }
66
+ entries[session_id.to_s] = { "created_at" => created_at.to_f, "renewed_at" => created_at.to_f, "ttl" => ttl, "owner" => self.class.owner }.compact
61
67
  end
62
68
  end
63
69
 
@@ -69,10 +75,13 @@ module Bidi2pdf
69
75
  update { |entries| entries.delete(session_id.to_s) }
70
76
  end
71
77
 
72
- # Renews the leases of +session_ids+ that are still recorded.
73
- def renew(session_ids, at: Time.now.to_i)
78
+ # Renews the leases of +session_ids+ that are still recorded, for +ttl+ seconds when given.
79
+ def renew(session_ids, at: Time.now.to_f, ttl: nil)
74
80
  update do |entries|
75
- session_ids.each { |id| entries[id]["renewed_at"] = at.to_i if entries.key?(id) }
81
+ session_ids.select { |id| entries.key?(id) }.each do |id|
82
+ entries[id]["renewed_at"] = at.to_f
83
+ entries[id]["ttl"] = ttl if ttl
84
+ end
76
85
  end
77
86
  end
78
87
 
@@ -81,10 +90,10 @@ module Bidi2pdf
81
90
  read.transform_values { |entry| entry["created_at"] }
82
91
  end
83
92
 
84
- # The recorded session ids whose lease was renewed within +ttl+ seconds before +now+ - sessions
85
- # of a live process.
86
- def leased(now: Time.now.to_i, ttl: DEFAULT_LEASE_TTL)
87
- read.select { |_, entry| entry["renewed_at"].to_i >= now - ttl }.keys
93
+ # The recorded session ids whose lease is still fresh at +now+ - sessions of a live process. An
94
+ # entry's own TTL wins; +ttl+ is only for entries without one.
95
+ def leased(now: Time.now.to_f, ttl: DEFAULT_LEASE_TTL)
96
+ read.select { |_, entry| entry["renewed_at"].to_f + (entry["ttl"] || ttl) >= now }.keys
88
97
  end
89
98
 
90
99
  # The recorded session ids opened at or before +cutoff+ (epoch seconds).
@@ -134,9 +143,9 @@ module Bidi2pdf
134
143
  end
135
144
 
136
145
  def normalize(entry)
137
- return { "created_at" => entry } if entry.is_a?(Integer)
146
+ return { "created_at" => entry } if entry.is_a?(Numeric)
138
147
 
139
- entry if entry.is_a?(Hash) && entry["created_at"].is_a?(Integer)
148
+ entry if entry.is_a?(Hash) && entry["created_at"].is_a?(Numeric)
140
149
  end
141
150
 
142
151
  def unavailable!(error)
@@ -25,7 +25,7 @@ module Bidi2pdf
25
25
  # Closes recorded sessions opened more than +older_than+ seconds before +now+.
26
26
  #
27
27
  # @return [Integer] how many sessions were actually closed.
28
- def sweep(older_than:, now: Time.now.to_i)
28
+ def sweep(older_than:, now: Time.now.to_f)
29
29
  leftovers = @registry.recorded_before(now - older_than) - @registry.leased(now: now)
30
30
  closed = leftovers.count { |id| closed_now?(id) }
31
31
  report(closed)
@@ -65,9 +65,13 @@ module Bidi2pdf
65
65
  # +orphan_age+, +min_age+, +unresponsive_checks+, +max_sessions+, +pids_limit+, +interval+,
66
66
  # ...), or +nil+ (default) for none. This warmer's own sessions are never touched by it; with
67
67
  # an +interval+ it sweeps in the background, and a session that fails to start triggers one
68
- # sweep and one retry. Remote mode only.
68
+ # pressure sweep and one retry (see +retry_refused_sessions+). Remote mode only.
69
69
  attr_accessor :sweeper
70
70
 
71
+ # @return [Boolean] With a +sweeper+: when chromedriver refuses a new session, sweep under
72
+ # pressure and try once more. Default true; false leaves a refused session to the caller.
73
+ attr_accessor :retry_refused_sessions
74
+
71
75
  DEFAULT_MAX_IDLE_AGE = 300
72
76
 
73
77
  def initialize
@@ -80,6 +84,7 @@ module Bidi2pdf
80
84
  @orphan_age = :auto
81
85
  @registry_dir = nil
82
86
  @sweeper = nil
87
+ @retry_refused_sessions = true
83
88
  end
84
89
 
85
90
  # The orphan age in seconds, or nil when the sweep is off (also when +:auto+ has no
@@ -306,7 +311,7 @@ module Bidi2pdf
306
311
  def new_slot
307
312
  @slot_factory.call
308
313
  rescue Bidi2pdf::SessionNotStartedError
309
- raise unless @sweeper
314
+ raise unless @sweeper && @config.retry_refused_sessions
310
315
 
311
316
  @sweeper.sweep!(reason: :create_failed, pressure: true)
312
317
  @slot_factory.call
@@ -324,11 +329,12 @@ module Bidi2pdf
324
329
  Bidi2pdf::ChromeSweeper.new(@config.remote_browser_url, own_sessions: -> { @mutex.synchronize { @own_sessions.dup } }, **options)
325
330
  end
326
331
 
327
- # Remote mode with an orphan age or a sweeper: sessions are recorded, so a later start (or the
328
- # sweeper, whose default scope is :recorded) can close the ones this process leaves behind if it
329
- # dies uncleanly. The start-up sweep itself needs the orphan age - see #sweep_leftovers.
332
+ # Remote mode: every session is leased, so no sweeper in any process takes a warm spare or a
333
+ # render in flight, and a later start (or a sweeper) can close the ones this process leaves
334
+ # behind if it dies uncleanly. The start-up sweep itself needs the orphan age - see
335
+ # #sweep_leftovers.
330
336
  def build_registry
331
- return unless @config.remote_browser_url && (@config.effective_orphan_age || @config.sweeper)
337
+ return unless @config.remote_browser_url
332
338
 
333
339
  Bidi2pdf::SessionRegistry.new(@config.remote_browser_url, dir: @config.registry_dir)
334
340
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bidi2pdf
4
- VERSION = "0.1.18"
4
+ VERSION = "0.1.19"
5
5
  end
@@ -27,8 +27,9 @@ module Bidi2pdf
27
27
  # @!attribute threads_per_session [Integer] threads (Docker counts them as pids) of one session.
28
28
  # @!attribute interval [Numeric, nil] seconds between sweeps of ChromeSweeper#start's thread.
29
29
  # @!attribute dry_run [Boolean] report what would be closed, close nothing.
30
- # @!attribute lease_ttl [Numeric] a recorded session renewed within this many seconds belongs to
31
- # a live process and is never closed (SessionRegistry#hold).
30
+ # @!attribute lease_ttl [Numeric] for registry entries without a TTL of their own (written by
31
+ # bidi2pdf 0.1.18): renewed within this many seconds means a live process holds it. Entries
32
+ # written since carry the TTL their owner's heartbeat promises (SessionRegistry#hold).
32
33
  Settings: untyped
33
34
  end
34
35
  end
@@ -23,6 +23,9 @@ module Bidi2pdf
23
23
 
24
24
  def self.interval: () -> untyped
25
25
 
26
+ # The TTL this process writes into its leases: three missed renewals and it is taken for dead.
27
+ def self.lease_ttl: () -> untyped
28
+
26
29
  def self.hold: (untyped registry, untyped session_id) -> untyped
27
30
 
28
31
  def self.release: (untyped registry, untyped session_id) -> untyped
@@ -7,8 +7,10 @@ module Bidi2pdf
7
7
  # accepted but echoed nowhere - checked against chromedriver 153), hence a file.
8
8
  #
9
9
  # Liveness is a lease: a session the process #hold s is renewed by its Heartbeat every
10
- # Heartbeat.interval seconds. A session whose lease is fresh belongs to a live process - a render
11
- # in flight or a warm spare - and no sweeper closes it; one whose lease ran out belongs to a dead
10
+ # Heartbeat.interval seconds, and every entry carries its own TTL - the time the owner promises to
11
+ # renew it within (three heartbeats) - so a sweeper in another process never has to guess how
12
+ # often that owner renews. A session whose lease is fresh belongs to a live process - a render in
13
+ # flight or a warm spare - and no sweeper closes it; one whose lease ran out belongs to a dead
12
14
  # process. #record alone writes an entry nobody renews.
13
15
  #
14
16
  # Everything here is best effort and fail-open: a directory that is not writable, a locked-out or
@@ -23,7 +25,8 @@ module Bidi2pdf
23
25
 
24
26
  FILE_PREFIX: "bidi2pdf-sessions-"
25
27
 
26
- # Seconds a lease stays fresh without a renewal.
28
+ # Seconds a lease stays fresh without a renewal when its entry carries no TTL of its own (written
29
+ # by bidi2pdf 0.1.18) - matches that version's fixed 20 s heartbeat.
27
30
  DEFAULT_LEASE_TTL: 60
28
31
 
29
32
  attr_reader path: untyped
@@ -39,20 +42,23 @@ module Bidi2pdf
39
42
  # a sweeper takes it once the lease ran out.
40
43
  def release: (untyped session_id) -> (untyped | nil)
41
44
 
42
- # Writes an entry, leased from +created_at+ on but not renewed (see #hold).
43
- def record: (untyped session_id, ?created_at: untyped) -> (false | untyped)
45
+ # Writes an entry, leased from +created_at+ on for +ttl+ seconds (nil: the reader's default) but
46
+ # not renewed (see #hold).
47
+ # Times are stored as fractions of a second: a whole-second timestamp could cost a short lease
48
+ # (0.5 s heartbeats: 1.5 s) up to a second of its life. Entries of whole seconds still read.
49
+ def record: (untyped session_id, ?created_at: untyped, ?ttl: untyped?) -> (false | untyped)
44
50
 
45
51
  # Forgets a session the caller closed itself (or found gone).
46
52
  def forget: (untyped session_id) -> (false | untyped)
47
53
 
48
- # Renews the leases of +session_ids+ that are still recorded.
49
- def renew: (untyped session_ids, ?at: untyped) -> untyped
54
+ # Renews the leases of +session_ids+ that are still recorded, for +ttl+ seconds when given.
55
+ def renew: (untyped session_ids, ?at: untyped, ?ttl: untyped?) -> untyped
50
56
 
51
57
  # Every recorded session: { id => opened at (epoch seconds) }.
52
58
  def recorded: () -> untyped
53
59
 
54
- # The recorded session ids whose lease was renewed within +ttl+ seconds before +now+ - sessions
55
- # of a live process.
60
+ # The recorded session ids whose lease is still fresh at +now+ - sessions of a live process. An
61
+ # entry's own TTL wins; +ttl+ is only for entries without one.
56
62
  def leased: (?now: untyped, ?ttl: untyped) -> untyped
57
63
 
58
64
  # The recorded session ids opened at or before +cutoff+ (epoch seconds).
@@ -70,6 +70,8 @@ module Bidi2pdf
70
70
 
71
71
  @sweeper: untyped
72
72
 
73
+ @retry_refused_sessions: untyped
74
+
73
75
  # @return [Integer] Number of Chrome slots to keep pre-warmed.
74
76
  attr_accessor size: untyped
75
77
 
@@ -111,9 +113,13 @@ module Bidi2pdf
111
113
  # +orphan_age+, +min_age+, +unresponsive_checks+, +max_sessions+, +pids_limit+, +interval+,
112
114
  # ...), or +nil+ (default) for none. This warmer's own sessions are never touched by it; with
113
115
  # an +interval+ it sweeps in the background, and a session that fails to start triggers one
114
- # sweep and one retry. Remote mode only.
116
+ # pressure sweep and one retry (see +retry_refused_sessions+). Remote mode only.
115
117
  attr_accessor sweeper: untyped
116
118
 
119
+ # @return [Boolean] With a +sweeper+: when chromedriver refuses a new session, sweep under
120
+ # pressure and try once more. Default true; false leaves a refused session to the caller.
121
+ attr_accessor retry_refused_sessions: untyped
122
+
117
123
  DEFAULT_MAX_IDLE_AGE: 300
118
124
 
119
125
  def initialize: () -> void
@@ -216,9 +222,10 @@ module Bidi2pdf
216
222
 
217
223
  def build_sweeper: () -> (nil | untyped)
218
224
 
219
- # Remote mode with an orphan age or a sweeper: sessions are recorded, so a later start (or the
220
- # sweeper, whose default scope is :recorded) can close the ones this process leaves behind if it
221
- # dies uncleanly. The start-up sweep itself needs the orphan age - see #sweep_leftovers.
225
+ # Remote mode: every session is leased, so no sweeper in any process takes a warm spare or a
226
+ # render in flight, and a later start (or a sweeper) can close the ones this process leaves
227
+ # behind if it dies uncleanly. The start-up sweep itself needs the orphan age - see
228
+ # #sweep_leftovers.
222
229
  def build_registry: () -> (nil | untyped)
223
230
 
224
231
  def sweep_leftovers: () -> (nil | untyped)
@@ -1,3 +1,3 @@
1
1
  module Bidi2pdf
2
- VERSION: "0.1.18"
2
+ VERSION: "0.1.19"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bidi2pdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.18
4
+ version: 0.1.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dieter S.