Skip to content

Commit 9cf1bf1

Browse files
add unprotected ping
1 parent f29cb6e commit 9cf1bf1

File tree

8 files changed

+127
-4
lines changed

8 files changed

+127
-4
lines changed

lib/semian/activerecord_trilogy_adapter.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,15 @@ def active?
9999
false
100100
end
101101

102+
def unprotected_ping
103+
# Call the underlying active? method without Semian protection
104+
# The super here refers to the original active? from the Trilogy adapter
105+
@raw_connection&.ping
106+
rescue => e
107+
Semian.logger&.debug("[trilogy_adapter] Unprotected ping failed: #{e.message}")
108+
false
109+
end
110+
102111
def with_resource_timeout(temp_timeout)
103112
if @raw_connection.nil?
104113
prev_read_timeout = @config[:read_timeout] || 0

lib/semian/adaptive_circuit_breaker.rb

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,14 +126,28 @@ def start_ping_thread
126126
end
127127

128128
def send_background_ping
129-
return unless @resource&.respond_to?(:ping)
129+
# Use unprotected_ping if available, otherwise fall back to ping
130+
return unless @resource
131+
132+
ping_method = if @resource.respond_to?(:unprotected_ping)
133+
:unprotected_ping
134+
elsif @resource.respond_to?(:ping)
135+
:ping
136+
else
137+
return
138+
end
130139

131140
# Send ungated ping (not affected by rejection)
132141
begin
133-
@resource.ping
134-
@pid_controller.record_ping(:success)
135-
rescue
142+
result = @resource.send(ping_method)
143+
if result
144+
@pid_controller.record_ping(:success)
145+
else
146+
@pid_controller.record_ping(:failure)
147+
end
148+
rescue => e
136149
@pid_controller.record_ping(:failure)
150+
Semian.logger&.debug("[#{@name}] Background ping failed: #{e.message}")
137151
end
138152
end
139153

lib/semian/grpc.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,28 @@ def handle_operation(operation, scope)
122122
@semian.send(:acquire_semian_resource, adapter: :grpc, scope: scope) { execute.call }
123123
end
124124
end
125+
126+
def unprotected_ping
127+
# For gRPC, we'll check if the channel is in a ready state
128+
# This doesn't make an actual RPC call but checks connection state
129+
130+
return false if @ch.nil?
131+
132+
# Get the channel connectivity state
133+
state = @ch.connectivity_state(false)
134+
135+
# GRPC channel states:
136+
# IDLE (0) - channel is idle
137+
# CONNECTING (1) - channel is connecting
138+
# READY (2) - channel is ready for work
139+
# TRANSIENT_FAILURE (3) - channel has seen a failure
140+
# SHUTDOWN (4) - channel has been shut down
141+
142+
state == 2 # GRPC::Core::ConnectivityStates::READY
143+
rescue => e
144+
Semian.logger&.debug("[grpc] Unprotected ping failed: #{e.message}")
145+
false
146+
end
125147
end
126148
end
127149

lib/semian/mysql2.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ def ping
8080
false
8181
end
8282

83+
def unprotected_ping
84+
return false if closed?
85+
86+
raw_ping
87+
rescue => e
88+
Semian.logger&.debug("[mysql2] Unprotected ping failed: #{e.message}")
89+
false
90+
end
91+
8392
def query(*args)
8493
if query_whitelisted?(*args)
8594
raw_query(*args)

lib/semian/net_http.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,33 @@ def with_resource_timeout(timeout)
124124
end
125125
end
126126

127+
def unprotected_ping
128+
# Perform a simple HEAD request to check connectivity
129+
# First ensure we're connected
130+
return false unless started?
131+
132+
begin
133+
# Create a simple HEAD request
134+
req = Net::HTTP::Head.new("/")
135+
136+
# Temporarily save semian state and disable it
137+
saved_semian_enabled = @semian_enabled
138+
@semian_enabled = false
139+
140+
# Make the request directly without going through Semian
141+
# We just need to verify we can reach the server
142+
request(req)
143+
144+
# Any response (even error responses) means the server is reachable
145+
true
146+
rescue => e
147+
Semian.logger&.debug("[net_http] Unprotected ping failed: #{e.message}")
148+
false
149+
ensure
150+
@semian_enabled = saved_semian_enabled
151+
end
152+
end
153+
127154
private
128155

129156
def handle_error_responses(result)

lib/semian/redis.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,20 @@ def with_resource_timeout(temp_timeout)
139139
end
140140
end
141141

142+
def unprotected_ping
143+
# Send a PING command directly without Semian protection
144+
return false unless connected?
145+
146+
begin
147+
# Directly call the Redis PING command bypassing Semian's io wrapper
148+
raw_io { [:ping] }
149+
true
150+
rescue => e
151+
Semian.logger&.debug("[redis] Unprotected ping failed: #{e.message}")
152+
false
153+
end
154+
end
155+
142156
private
143157

144158
def resource_exceptions

lib/semian/redis/v5.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ def semian_resource
3939
def semian_identifier
4040
_client.semian_identifier
4141
end
42+
43+
def unprotected_ping
44+
# Delegate to the underlying RedisClient's unprotected_ping
45+
_client.unprotected_ping
46+
end
4247
end
4348

4449
module RedisV5Client

lib/semian/redis_client.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,17 @@ module RedisClient
9999
include Semian::Adapter
100100
include RedisClientCommon
101101

102+
def unprotected_ping
103+
# Send a PING command directly without Semian protection
104+
105+
# Use call_v to bypass the normal flow
106+
result = call_v(["PING"])
107+
result == "PONG"
108+
rescue => e
109+
Semian.logger&.debug("[redis_client] Unprotected ping failed: #{e.message}")
110+
false
111+
end
112+
102113
private
103114

104115
def resource_exceptions
@@ -129,6 +140,18 @@ module RedisClientPool
129140

130141
define_method(:semian_resource, Semian::Adapter.instance_method(:semian_resource))
131142
define_method(:clear_semian_resource, Semian::Adapter.instance_method(:clear_semian_resource))
143+
144+
def unprotected_ping
145+
# For pooled connections, use with to get a client and ping
146+
147+
with do |client|
148+
result = client.call_v(["PING"])
149+
result == "PONG"
150+
end
151+
rescue => e
152+
Semian.logger&.debug("[redis_client_pool] Unprotected ping failed: #{e.message}")
153+
false
154+
end
132155
end
133156
end
134157

0 commit comments

Comments
 (0)