Skip to content

Commit 49f920b

Browse files
committed
fix: observe the server version once
The reasons list read the version for its comparison and again for its message, and the doctor read it a third time for the passing text. A transient failure on a later read would have replaced an already determined result with a generic verification error, and every healthy check paid extra round trips. One observed version now decides both status and message.
1 parent c20a283 commit 49f920b

4 files changed

Lines changed: 32 additions & 10 deletions

File tree

lib/solid_objects/database_adapter.rb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,15 @@ def server_version
4747
end
4848
end
4949

50-
# @rbs () -> Array[String]
51-
def unsupported_server_reasons
50+
# One observed version decides both the status and the message. Reading it
51+
# again could let a transient failure replace an already determined result.
52+
# @rbs (?Gem::Version?) -> Array[String]
53+
def unsupported_server_reasons(observed = nil)
54+
observed ||= server_version
5255
reasons = []
5356
minimum = minimum_server_version
54-
if minimum && server_version < minimum
55-
reasons << "#{self.class.name.demodulize} #{server_version} is older than " \
57+
if minimum && observed < minimum
58+
reasons << "#{self.class.name.demodulize} #{observed} is older than " \
5659
"Solid Objects requires, which is #{minimum}"
5760
end
5861
reasons.concat(additional_server_reasons)

lib/solid_objects/doctor.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,12 +195,13 @@ def check_authorization
195195
# @rbs () -> Check
196196
def check_database_server
197197
adapter = SolidObjects.database_adapter
198-
reasons = adapter.unsupported_server_reasons
198+
observed = adapter.server_version
199+
reasons = adapter.unsupported_server_reasons(observed)
199200
return warn_check(:database_server, reasons.join("; ")) unless reasons.empty?
200201

201202
pass(
202203
:database_server,
203-
"#{adapter.class.name.demodulize} #{adapter.server_version} meets the tested minimum"
204+
"#{adapter.class.name.demodulize} #{observed} meets the tested minimum"
204205
)
205206
rescue => error
206207
warn_check(:database_server, "#{error.class}: #{error.message}")

sig/generated/lib/solid_objects/database_adapter.rbs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ module SolidObjects
2525
# @rbs () -> Gem::Version
2626
def server_version: () -> Gem::Version
2727

28-
# @rbs () -> Array[String]
29-
def unsupported_server_reasons: () -> Array[String]
28+
# One observed version decides both the status and the message. Reading it
29+
# again could let a transient failure replace an already determined result.
30+
# @rbs (?Gem::Version?) -> Array[String]
31+
def unsupported_server_reasons: (?Gem::Version?) -> Array[String]
3032

3133
# @rbs () -> Array[String]
3234
def additional_server_reasons: () -> Array[String]

test/integration/database_version_test.rb

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,22 @@ class DatabaseVersionTest < ActiveSupport::TestCase
2525
restore(adapter, :with_connection)
2626
end
2727

28+
test "the observed version is read once for status and message" do
29+
adapter = SolidObjects.database_adapter
30+
reads = 0
31+
real = adapter.method(:server_version)
32+
adapter.define_singleton_method(:server_version) {
33+
reads += 1
34+
real.call
35+
}
36+
37+
SolidObjects::Doctor.new.call.check(:database_server)
38+
39+
assert_equal 1, reads, "the doctor should observe the server version once"
40+
ensure
41+
restore(adapter, :server_version)
42+
end
43+
2844
test "a supported server passes verification" do
2945
assert_empty SolidObjects.database_adapter.unsupported_server_reasons
3046
end
@@ -60,7 +76,7 @@ class DatabaseVersionTest < ActiveSupport::TestCase
6076

6177
test "the doctor warns about an unsupported server" do
6278
adapter = SolidObjects.database_adapter
63-
adapter.define_singleton_method(:unsupported_server_reasons) { [ "too old" ] }
79+
adapter.define_singleton_method(:unsupported_server_reasons) { |_observed = nil| [ "too old" ] }
6480

6581
check = SolidObjects::Doctor.new.call.check(:database_server)
6682

@@ -72,7 +88,7 @@ class DatabaseVersionTest < ActiveSupport::TestCase
7288

7389
test "an unsupported server does not fail the report" do
7490
adapter = SolidObjects.database_adapter
75-
adapter.define_singleton_method(:unsupported_server_reasons) { [ "too old" ] }
91+
adapter.define_singleton_method(:unsupported_server_reasons) { |_observed = nil| [ "too old" ] }
7692

7793
assert SolidObjects::Doctor.new.call.healthy?
7894
ensure

0 commit comments

Comments
 (0)