Stop using dbcancel as the nogvl unblock function - #607
emailrhoads wants to merge 2 commits into
Conversation
MRI calls the UBF on any pending interrupt (including SIGCHLD on main), which aborted in-flight batches and made Result#each return []. Client :timeout still uses dbsetinterrupt. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@andyundso any thoughts on this idea? |
|
@emailrhoads I am trying to grasp the consequence of this change. If you have some time and maybe dev environment running, can you add the following test to client = new_connection
assert_client_works(client)
thread = Thread.new do
client.do("waitfor delay '00:00:05'")
end
sleep 0.1
time = Benchmark.measure do
thread.kill
end
assert time.real < 5Otherwise I might have some time on the weekend to see myself. Nulling out the unblock function would mean the call just continue to run indefinitely, but maybe I am missing something. |
Andy asked whether kill still aborts WAITFOR after removing dbcancel as UBF. Kill returns immediately; join waits for the batch (~5s). Co-authored-by: Cursor <cursoragent@cursor.com>
@andyundso added the test and a bit more which I think was the spirit of your request at https://github.com/rails-sqlserver/tiny_tds/pull/607/changes#diff-fe76b289030e9af8010639512c4d9fab1f5cfd43207ae9137850ae1949dafae1R82 In short, we lose the ability to do Thread#kill and Timeout.timeout with this change and have it behave the way we would like. Here are the actual test results though
The caller would need to use something like client.cancel to break the running connection instead. |
|
I am sure I miss something in this discussion.
I am not sure if we agree here what the correct behaviour is. Sure, that other threads have influence of the current thread is not ideal, but not being able to kill a running program is also not good.
Your test table clearly shows that with the current master build, we won't have to wait long at I checked some other DB libraries and the require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'benchmark'
gem 'pg'
end
time = Benchmark.measure do
thread = Thread.new do
conn = PG.connect(
host: "127.0.0.1",
port: 5432,
dbname: "postgres",
user: "postgres",
password: "mysecretpassword"
)
conn.exec("SELECT pg_sleep(5)")
conn.close
end
sleep(0.5)
thread.kill
thread.join
end
puts time.inspectyields: so in short, there might be a way to have both, no thread cancels and still fast runtime. |
Summary
rb_thread_call_without_gvlcurrently usesdbcancelas the unblock function. MRI invokes that UBF whenever the waiting thread has a pending interrupt, including process-directed signals such asSIGCHLDdelivered to main after another thread spawns a child.dbcancelthen aborts the in-flight batch.Result#eachtreatsFAILas an empty success, so long queries can return[]even though SQL Server produced rows.NULL, NULL) so other Ruby threads can run, but no longer cancels SQL on those interrupts. Client:timeoutis unchanged (dbsetinterrupt).Timeout.timeout/Thread#killwait until the batch finishes or:timeoutfires.Test plan
WAITFOR DELAY; the followingSELECT 42must still return a row.:timeoutviadbsetinterrupt).