cronwatch 0.14.1 → 0.14.2

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: d0a9fcfe8eb1b18e8afd7b80e2b41e62569bf606db798252d2305f9f353235ff
4
- data.tar.gz: 12626c9fc256348658a3843ac1704b97ef7cd4fde62f289e0c570c174dd29704
3
+ metadata.gz: c7d7828b625c4a8a6e5d82d3c85b89433a76ca0527fdba4818e8ca4c5e19118d
4
+ data.tar.gz: 1f6f92d282de1d64819055756f661bc51e7d3cc1ef878a56e71ff0bd28f8eb6d
5
5
  SHA512:
6
- metadata.gz: 4d9274543edac3d3400762e5863b08a2b9cf8a818b134996e28a292cfe802552e6ecffbc68b18a7a4a9258499e476e7e7982a546b4b204ebc5f146829e14cebc
7
- data.tar.gz: ab1c1ef624f8e94f56eb73c2be364ebf9b799e1b7bceef3a69e4398604e72c1eb63252f894aec82bdb4dfc61d9b9803048a02564dd60d807a273cfde62b0b364
6
+ metadata.gz: 7286c73679cc2cefaa7eab00171ec0cfd54adabb2a77ee963a1160f73417dafb5494fcc4135ea22e67a5ec0eec0b98c8c6ac77640621c1584c3ce6af73ff8f2c
7
+ data.tar.gz: e73021ad7c4dd4e6c2399281c5d673911613a2b0e334d4621110912c3b2b2acc7219ae6d3fea4e5056341be9608d34a7758de1b6d80fdf77a2e7f9bf6c4e62da
@@ -410,8 +410,10 @@ module Cronwatch
410
410
  stored = @store.get_run(run.id)
411
411
  return record_over(definition, stored, run, evaluate) if stored
412
412
 
413
+ inserted = run.dup
414
+ inserted.status = :running if evaluate
413
415
  begin
414
- @store.insert_run(run)
416
+ @store.insert_run(inserted)
415
417
  rescue StandardError
416
418
  # Another process recorded it first.
417
419
  again = begin
@@ -425,10 +427,7 @@ module Cronwatch
425
427
  end
426
428
  return [] unless evaluate
427
429
 
428
- update_state(run.job) { |before| [Evaluate.hold_run(Evaluate.on_run_start(definition, run, before, now), run.id), nil] }
429
- return [] if run.status == :running
430
-
431
- finish_run(definition, run, now, pending_only: true)
430
+ record_over(definition, inserted, run, true)
432
431
  end
433
432
 
434
433
  # Hands an error to on_error, as a source reports what went wrong. See #report.
@@ -917,7 +916,19 @@ module Cronwatch
917
916
  report(RuntimeError.new("run #{run.id} of #{run.job} belongs to job \"#{stored.job}\"; ignored"), "recording #{run.job}")
918
917
  return []
919
918
  end
920
- return [] if !%i[running timeout].include?(stored.status) || run.status == :running
919
+ return [] unless %i[running timeout].include?(stored.status)
920
+
921
+ if evaluate && stored.status == :running
922
+ update_state(run.job) do |before|
923
+ # Recheck each CAS attempt so a concurrent finish cannot resurrect its marker.
924
+ current = @store.get_run(run.id)
925
+ if current.nil? || current.job != run.job || current.status != :running || (before.pending_runs || []).include?(run.id)
926
+ next [before, nil]
927
+ end
928
+ [Evaluate.hold_run(Evaluate.on_run_start(definition, run, before, now), run.id), nil]
929
+ end
930
+ end
931
+ return [] if run.status == :running
921
932
 
922
933
  tracked = (read_state(run.job).pending_runs || []).include?(run.id)
923
934
  late, ignored = claim_finish(run)
@@ -688,7 +688,7 @@ module Cronwatch
688
688
 
689
689
  # Whether a running run has gone on longer than the job's timeout.
690
690
  def stuck?(definition, run, now)
691
- run.status == :running && now - run.started_at > timeout_ms(definition)
691
+ run.status == :running && run.finished_at.nil? && now - run.started_at > timeout_ms(definition)
692
692
  end
693
693
 
694
694
  # While a job is silenced nothing new is recorded as an incident: conditions
@@ -55,20 +55,33 @@ module Cronwatch
55
55
  # name lookup: it can be interrupted from Ruby 3.3. On Ruby 3.2 a lookup
56
56
  # that hangs is cut off only by the client's channel deadline.
57
57
  http.open_timeout = HTTP.remaining(deadline)
58
- http.start do |conn|
58
+ # Net::HTTP's read and write timeouts restart for each socket operation.
59
+ # Bound the entire exchange too, including a status line or headers
60
+ # arriving one byte at a time. The connection is private to this call.
61
+ answer = nil
62
+ Timeout.timeout(HTTP.remaining(deadline), TimeoutError, "The operation was aborted due to timeout") do
63
+ http.start
59
64
  remaining = HTTP.remaining(deadline)
60
- conn.read_timeout = remaining
61
- conn.write_timeout = remaining
62
- conn.request(request) do |response|
65
+ http.read_timeout = remaining
66
+ http.write_timeout = remaining
67
+ http.request(request) do |response|
63
68
  status = response.code.to_i
69
+ answer = Response.new(status: status, body: "".b)
64
70
  # Returning from inside the block leaves the rest unread; the connection closes after.
65
- return Response.new(status: status, body: "") if status >= 200 && status < 300
71
+ return answer if status >= 200 && status < 300
66
72
 
67
- return Response.new(status: status, body: read_body(conn, response, deadline))
73
+ read_body(http, response, deadline, answer.body)
74
+ return answer
68
75
  end
69
76
  end
70
- rescue Net::OpenTimeout, Net::ReadTimeout, Net::WriteTimeout
77
+ rescue Net::OpenTimeout, Net::ReadTimeout, Net::WriteTimeout, TimeoutError
78
+ return answer if answer
79
+
71
80
  raise TimeoutError
81
+ ensure
82
+ # Timeout has cancelled its timer before this cleanup runs. A timeout
83
+ # during close must not replace an answer or interrupt socket cleanup.
84
+ http.finish if http&.started?
72
85
  end
73
86
 
74
87
  # A connection that gives up after ten seconds, as the SDK's requests do.
@@ -85,8 +98,7 @@ module Cronwatch
85
98
 
86
99
  # The body, read in chunks until it ends or MAX_BODY bytes have come,
87
100
  # whichever is first; what had come when the deadline passes first.
88
- def read_body(conn, response, deadline)
89
- body = "".b
101
+ def read_body(conn, response, deadline, body)
90
102
  conn.read_timeout = HTTP.remaining(deadline)
91
103
  response.read_body do |chunk|
92
104
  raise TimeoutError if HTTP.monotonic >= deadline
@@ -109,6 +109,7 @@ module Cronwatch
109
109
  @pending = {}
110
110
  # Runs read before they started, by runid, with when they were first seen.
111
111
  @held = {}
112
+ @retry = {}
112
113
  # Each job's name and definition as last declared, by jobid.
113
114
  @known = {}
114
115
  # The last definition declared for each name, so an unchanged job is not declared again.
@@ -493,10 +494,12 @@ module Cronwatch
493
494
  def record(host, names, row, evaluate, alerts, now)
494
495
  runid = Integer(row["runid"])
495
496
  jobid = Integer(row["jobid"])
497
+ evaluate = @retry.fetch(runid, evaluate)
496
498
  name = @pending[runid] || names[jobid]
497
499
  if name.nil? || (@declared_now && !names.value?(name) && !@declared_now.include?(name))
498
500
  @pending.delete(runid)
499
501
  @held.delete(runid)
502
+ @retry.delete(runid)
500
503
  @retired.delete(name) if name
501
504
  return
502
505
  end
@@ -516,9 +519,11 @@ module Cronwatch
516
519
  begin
517
520
  alerts.concat(host.record_run(run, evaluate: evaluate))
518
521
  rescue StandardError => e
522
+ @retry[runid] = evaluate
519
523
  host.on_error(e, "source pg_cron: run #{runid}")
520
524
  return
521
525
  end
526
+ @retry.delete(runid)
522
527
  if run.status == :running
523
528
  @pending[runid] = name
524
529
  else
@@ -561,7 +566,7 @@ module Cronwatch
561
566
  id = run_id_of(run.id)
562
567
  @pending[id] = run.job if id && watched.include?(run.job)
563
568
  end
564
- open = (@pending.keys + @held.keys).to_set
569
+ open = (@pending.keys + @held.keys + @retry.keys).to_set
565
570
  complete = false
566
571
  MAX_PAGES.times do
567
572
  jobids = names.keys
@@ -585,6 +590,7 @@ module Cronwatch
585
590
  open.each do |runid|
586
591
  @pending.delete(runid)
587
592
  @held.delete(runid)
593
+ @retry.delete(runid)
588
594
  end
589
595
  end
590
596
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Cronwatch
4
4
  # Kept in step with @cronwatch/sdk.
5
- VERSION = "0.14.1"
5
+ VERSION = "0.14.2"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cronwatch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.1
4
+ version: 0.14.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon C. Phillips