lex-health 0.1.7 → 0.2.0

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: 58aebf740f7b4a79bf0c97cc5bae4cec886fe731bda53c681e9526f3c5d6ebc7
4
- data.tar.gz: 71b495ec970cd3230614a9f6a812fdf9987bcb00c0a16784fe83cedfb23d9438
3
+ metadata.gz: 51bae536ed975da049ebfa332b71974b49d85886afed5f7c631de9c9d7c63265
4
+ data.tar.gz: 9e24916e2b2d9229121c89884c7487453dd569b24ae2fb4c277dce33a615d4d5
5
5
  SHA512:
6
- metadata.gz: 0ebad0bf1c817a9bccba265b7c5b127c045b725ad8a25a966d141547f8ce535f0d5073155c41e0346b44f1d6b4409b15c0b8a72fc6d98a1357300889224356b9
7
- data.tar.gz: de77a45ce02799fe3f9720054585ef6d332f7d852732035c0c8511f7c92f4fd7ec64444c2bd7b920ec6ddb219139b65ed3e37544733b77213d9bbfe14b907865
6
+ metadata.gz: a38f069ff4658cc951d1b4615e319daaf7e1576a285c37d4b8f3e40649caca8ce1e94e2d447a89be955d49e51238b26c3cab0b516d61caa43701eb61243c4a98
7
+ data.tar.gz: e31fd840329ca93fef39026b29dd786a3e12ebd980b8aad64332d6ad30c4bde8d9be66ead9f530416f39719a9de9150646bbc49922efce43d38b6a217a3e1ab2
data/.gitignore CHANGED
@@ -7,5 +7,7 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
 
10
+ Gemfile.lock
11
+
10
12
  # rspec failure tracking
11
13
  .rspec_status
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.0] - 2026-03-18
4
+
5
+ ### Fixed
6
+ - `active` column now uses boolean `true` instead of integer `1` (PostgreSQL compatibility)
7
+ - Watchdog message routing key changed from `'health'` to `'node.health'` to match queue binding
8
+ - Added `require 'time'` for `Time.parse`
9
+ - Nil guard on `updated` timestamp in back-in-time comparison
10
+ - TOCTOU race condition on concurrent heartbeat inserts (rescue UniqueConstraintViolation)
11
+ - `delete` method nil guard for nonexistent nodes
12
+ - `mark_workers_offline` now clears `health_node` on expired workers
13
+
14
+ ### Changed
15
+ - Entry point `data_required?` is now `self.` (class method) matching framework expectation
16
+
17
+ ## [0.1.8] - 2026-03-17
18
+
19
+ ### Fixed
20
+ - Watchdog `expire` guards against missing `Legion::Data::Model::Node` constant before use, returning an error hash when the model is unavailable
21
+
3
22
  ## [0.1.7] - 2026-03-16
4
23
 
5
24
  ### Fixed
data/CLAUDE.md CHANGED
@@ -10,7 +10,7 @@ Legion Extension that reads heartbeat messages from cluster nodes and updates th
10
10
 
11
11
  **GitHub**: https://github.com/LegionIO/lex-health
12
12
  **License**: MIT
13
- **Version**: 0.1.7
13
+ **Version**: 0.2.0
14
14
 
15
15
  ## Architecture
16
16
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'time'
4
+
3
5
  module Legion
4
6
  module Extensions
5
7
  module Health
@@ -16,7 +18,7 @@ module Legion
16
18
  return { success: result, hostname: hostname, **opts }
17
19
  end
18
20
 
19
- if opts.key?(:timestamp) && !item.values[:updated].nil? && item.values[:updated] > Time.parse(opts[:timestamp])
21
+ if opts.key?(:timestamp) && item.values[:updated] && item.values[:updated] > Time.parse(opts[:timestamp])
20
22
  return { success: false,
21
23
  reason: 'entry already updated',
22
24
  hostname: hostname,
@@ -24,7 +26,7 @@ module Legion
24
26
  **opts }
25
27
  end
26
28
 
27
- update_hash = { active: 1, status: opts[:status], name: hostname, updated: Sequel::CURRENT_TIMESTAMP }
29
+ update_hash = { active: true, status: opts[:status], name: hostname, updated: Sequel::CURRENT_TIMESTAMP }
28
30
  update_hash[:metrics] = Legion::JSON.dump(opts[:metrics]) if opts[:metrics]
29
31
  update_hash[:hosted_worker_ids] = Legion::JSON.dump(opts[:hosted_worker_ids]) if opts[:hosted_worker_ids]
30
32
  update_hash[:version] = opts[:version] if opts[:version]
@@ -36,19 +38,27 @@ module Legion
36
38
  end
37
39
 
38
40
  def insert(hostname:, status: 'unknown', **opts)
39
- insert = { active: 1, status: status, name: hostname }
41
+ insert = { active: true, status: status, name: hostname }
40
42
  insert[:datacenter_id] = opts[:datacenter_id] if opts.key? :datacenter_id
41
43
  insert[:environment_id] = opts[:environment_id] if opts.key? :environment_id
42
- insert[:active] = opts[:active] if opts.key? :active
43
44
  insert[:metrics] = Legion::JSON.dump(opts[:metrics]) if opts[:metrics]
44
45
  insert[:hosted_worker_ids] = Legion::JSON.dump(opts[:hosted_worker_ids]) if opts[:hosted_worker_ids]
45
46
  insert[:version] = opts[:version] if opts[:version]
46
47
 
47
- { success: true, hostname: hostname, node_id: Legion::Data::Model::Node.insert(insert), **insert }
48
+ node_id = begin
49
+ Legion::Data::Model::Node.insert(insert)
50
+ rescue Sequel::UniqueConstraintViolation
51
+ item = Legion::Data::Model::Node[name: hostname]
52
+ item&.id
53
+ end
54
+ { success: true, hostname: hostname, node_id: node_id, **insert }
48
55
  end
49
56
 
50
57
  def delete(node_id:, **_opts)
51
- Legion::Data::Model::Node[node_id].delete
58
+ node = Legion::Data::Model::Node[node_id]
59
+ return { success: false, error: 'node not found', node_id: node_id } if node.nil?
60
+
61
+ node.delete
52
62
  { success: true, node_id: node_id }
53
63
  end
54
64
 
@@ -8,6 +8,8 @@ module Legion
8
8
  include Legion::Extensions::Helpers::Lex
9
9
 
10
10
  def expire(expire_time: 60, **_opts)
11
+ return { success: false, reason: 'Legion::Data::Model::Node not available' } unless defined?(Legion::Data::Model::Node)
12
+
11
13
  cutoff = Time.now - expire_time
12
14
  nodes = []
13
15
  Legion::Data::Model::Node
@@ -36,7 +38,7 @@ module Legion
36
38
  Legion::Data::Model::DigitalWorker
37
39
  .where(health_node: node_name, health_status: 'online')
38
40
  .each do |worker|
39
- worker.update(health_status: 'offline')
41
+ worker.update(health_status: 'offline', health_node: nil)
40
42
  end
41
43
  rescue StandardError => e
42
44
  log.warn "worker offline marking failed: #{e.message}" if respond_to?(:log)
@@ -7,7 +7,7 @@ module Legion
7
7
  module Messages
8
8
  class Watchdog < Legion::Transport::Message
9
9
  def routing_key
10
- 'health'
10
+ 'node.health'
11
11
  end
12
12
 
13
13
  def expiration
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Health
6
- VERSION = '0.1.7'
6
+ VERSION = '0.2.0'
7
7
  end
8
8
  end
9
9
  end
@@ -10,10 +10,6 @@ module Legion
10
10
  def self.data_required?
11
11
  true
12
12
  end
13
-
14
- def data_required?
15
- true
16
- end
17
13
  end
18
14
  end
19
15
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-health
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -100,7 +100,6 @@ executables: []
100
100
  extensions: []
101
101
  extra_rdoc_files: []
102
102
  files:
103
- - ".circleci/config.yml"
104
103
  - ".github/workflows/ci.yml"
105
104
  - ".gitignore"
106
105
  - ".rspec"
@@ -109,7 +108,6 @@ files:
109
108
  - CLAUDE.md
110
109
  - Dockerfile
111
110
  - Gemfile
112
- - Gemfile.lock
113
111
  - LICENSE.txt
114
112
  - README.md
115
113
  - Rakefile
data/.circleci/config.yml DELETED
@@ -1,61 +0,0 @@
1
- version: 2.1
2
- orbs:
3
- ruby: circleci/ruby@0.2.1
4
-
5
- jobs:
6
- "rubocop":
7
- docker:
8
- - image: circleci/ruby:2.5-node
9
- steps:
10
- - checkout
11
- - ruby/load-cache
12
- - ruby/install-deps
13
- - run:
14
- name: Run Rubocop
15
- command: bundle exec rubocop
16
- - ruby/save-cache
17
- "ruby-two-five":
18
- docker:
19
- - image: circleci/ruby:2.5
20
- - image: memcached:1.5-alpine
21
- steps:
22
- - checkout
23
- - ruby/load-cache
24
- - ruby/install-deps
25
- - ruby/run-tests
26
- - ruby/save-cache
27
- "ruby-two-six":
28
- docker:
29
- - image: circleci/ruby:2.6
30
- - image: memcached:1.5-alpine
31
- steps:
32
- - checkout
33
- - ruby/load-cache
34
- - ruby/install-deps
35
- - ruby/run-tests
36
- - ruby/save-cache
37
- "ruby-two-seven":
38
- docker:
39
- - image: circleci/ruby:2.7
40
- - image: memcached:1.5-alpine
41
- steps:
42
- - checkout
43
- - ruby/load-cache
44
- - ruby/install-deps
45
- - ruby/run-tests
46
- - ruby/save-cache
47
-
48
- workflows:
49
- version: 2
50
- rubocop-rspec:
51
- jobs:
52
- - rubocop
53
- - ruby-two-five:
54
- requires:
55
- - rubocop
56
- - ruby-two-six:
57
- requires:
58
- - ruby-two-five
59
- - ruby-two-seven:
60
- requires:
61
- - ruby-two-five
data/Gemfile.lock DELETED
@@ -1,86 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- lex-health (0.1.7)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- addressable (2.8.9)
10
- public_suffix (>= 2.0.2, < 8.0)
11
- ast (2.4.3)
12
- bigdecimal (4.0.1)
13
- diff-lcs (1.6.2)
14
- json (2.19.1)
15
- json-schema (6.2.0)
16
- addressable (~> 2.8)
17
- bigdecimal (>= 3.1, < 5)
18
- language_server-protocol (3.17.0.5)
19
- lint_roller (1.1.0)
20
- mcp (0.8.0)
21
- json-schema (>= 4.1)
22
- parallel (1.27.0)
23
- parser (3.3.10.2)
24
- ast (~> 2.4.1)
25
- racc
26
- prism (1.9.0)
27
- public_suffix (7.0.5)
28
- racc (1.8.1)
29
- rainbow (3.1.1)
30
- rake (13.3.1)
31
- regexp_parser (2.11.3)
32
- rspec (3.13.2)
33
- rspec-core (~> 3.13.0)
34
- rspec-expectations (~> 3.13.0)
35
- rspec-mocks (~> 3.13.0)
36
- rspec-core (3.13.6)
37
- rspec-support (~> 3.13.0)
38
- rspec-expectations (3.13.5)
39
- diff-lcs (>= 1.2.0, < 2.0)
40
- rspec-support (~> 3.13.0)
41
- rspec-mocks (3.13.8)
42
- diff-lcs (>= 1.2.0, < 2.0)
43
- rspec-support (~> 3.13.0)
44
- rspec-support (3.13.7)
45
- rubocop (1.85.1)
46
- json (~> 2.3)
47
- language_server-protocol (~> 3.17.0.2)
48
- lint_roller (~> 1.1.0)
49
- mcp (~> 0.6)
50
- parallel (~> 1.10)
51
- parser (>= 3.3.0.2)
52
- rainbow (>= 2.2.2, < 4.0)
53
- regexp_parser (>= 2.9.3, < 3.0)
54
- rubocop-ast (>= 1.49.0, < 2.0)
55
- ruby-progressbar (~> 1.7)
56
- unicode-display_width (>= 2.4.0, < 4.0)
57
- rubocop-ast (1.49.1)
58
- parser (>= 3.3.7.2)
59
- prism (~> 1.7)
60
- rubocop-rspec (3.9.0)
61
- lint_roller (~> 1.1)
62
- rubocop (~> 1.81)
63
- ruby-progressbar (1.13.0)
64
- sequel (5.102.0)
65
- bigdecimal
66
- sqlite3 (2.9.2-arm64-darwin)
67
- sqlite3 (2.9.2-x86_64-linux-gnu)
68
- unicode-display_width (3.2.0)
69
- unicode-emoji (~> 4.1)
70
- unicode-emoji (4.2.0)
71
-
72
- PLATFORMS
73
- arm64-darwin-25
74
- x86_64-linux
75
-
76
- DEPENDENCIES
77
- lex-health!
78
- rake
79
- rspec
80
- rubocop
81
- rubocop-rspec
82
- sequel
83
- sqlite3
84
-
85
- BUNDLED WITH
86
- 2.6.9