Class: SolidObjects::DatabaseAdapters::Sqlite
Constant Summary
collapse
- LOCK_RETRY_INTERVAL =
0.001
- MAXIMUM_BUSY_RETRY_INTERVAL =
0.25
- LOCK_RETRY_MUTEX =
Thread::Mutex.new
- LOCK_RETRY_CONDITION =
Thread::ConditionVariable.new
SolidObjects::DatabaseAdapter::FAMILIES, SolidObjects::DatabaseAdapter::TRANSACTION_CLOCK, SolidObjects::DatabaseAdapter::TRANSACTION_CLOCK_SCOPE
Instance Attribute Summary
#connection_pool, #fixed_connection
Instance Method Summary
collapse
#additional_server_reasons, #claim_lock, #configure_transaction_deadline, #database_now, family, for, #initialize, #lock_candidates, #read_database_now, #server_version, #supports_skip_locked?, #unsupported_server_reasons, #with_connection, #with_transaction_clock
Instance Method Details
#busy_error?(error) ⇒ Boolean
148
149
150
151
152
153
154
155
156
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 148
def busy_error?(error)
cause = error
while cause
return true if cause.class.name.match?(/BusyException|BusyError/)
cause = cause.cause
end
false
end
|
129
130
131
132
133
134
135
136
137
138
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 129
def configured_busy_handler_timeout(connection)
return nil unless connection.respond_to?(:raw_connection)
return nil unless connection.raw_connection.respond_to?(:busy_handler_timeout=)
pool = connection.respond_to?(:pool) ? connection.pool : nil
return nil unless pool.respond_to?(:db_config)
timeout = pool.db_config.configuration_hash[:timeout]
timeout&.to_i
end
|
#current_time_expression ⇒ String
17
18
19
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 17
def current_time_expression
"STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')"
end
|
#deadline_error?(error) ⇒ Boolean
141
142
143
144
145
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 141
def deadline_error?(error)
return false unless SyncDeadline.active?
busy_error?(error)
end
|
#minimum_server_version ⇒ Gem::Version?
12
13
14
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 12
def minimum_server_version
Gem::Version.new("3.35")
end
|
#restorable_busy_wait(connection) ⇒ Hash[Symbol, untyped]?
107
108
109
110
111
112
113
114
115
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 107
def restorable_busy_wait(connection)
handler_timeout = configured_busy_handler_timeout(connection)
return { handler_timeout: } if handler_timeout
pragma_timeout = connection.select_value("PRAGMA busy_timeout").to_i
return nil unless pragma_timeout.positive?
{ pragma_timeout: }
end
|
#restore_busy_wait(connection, busy_wait) ⇒ void
This method returns an undefined value.
118
119
120
121
122
123
124
125
126
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 118
def restore_busy_wait(connection, busy_wait)
handler_timeout = busy_wait[:handler_timeout]
if handler_timeout
connection.raw_connection.busy_handler_timeout = handler_timeout
return
end
connection.execute("PRAGMA busy_timeout = #{busy_wait.fetch(:pragma_timeout)}")
end
|
#transaction { ... } ⇒ Object
22
23
24
25
26
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 22
def transaction(&block)
return with_lock_retry { super } if SyncDeadline.active?
with_busy_retry { super }
end
|
#wait_before_busy_retry(attempts) ⇒ void
This method returns an undefined value.
#wait_before_retry ⇒ void
This method returns an undefined value.
The deadline can expire between the check above and this wait, which
would otherwise ask for a negative interval.
#with_busy_retry { ... } ⇒ Object
A write outside a synchronous deadline has no Ruby-level budget, so it
depends entirely on SQLite's busy handler. Concurrent writers can
exhaust that, which surfaces as a lock error the caller cannot retry.
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 32
def with_busy_retry
attempts = 0
begin
yield
rescue => error
raise unless busy_error?(error)
attempts += 1
raise if attempts > SolidObjects.configuration.lock_retry_attempts
wait_before_busy_retry(attempts)
retry
end
end
|
#with_lock_probe { ... } ⇒ Object
75
76
77
78
79
80
81
82
83
84
85
86
87
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 75
def with_lock_probe
return yield unless SyncDeadline.active?
with_connection do |connection|
with_transaction_deadline(connection) { yield }
end
rescue => error
raise unless deadline_error?(error)
raise DatabaseDeadlineExceeded,
"database remained locked at the synchronous invocation deadline",
cause: error
end
|
#with_lock_retry { ... } ⇒ Object
#with_transaction_deadline(connection) { ... } ⇒ Object
92
93
94
95
96
97
98
99
100
101
102
103
104
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 92
def with_transaction_deadline(connection)
return yield unless SyncDeadline.active?
busy_wait = restorable_busy_wait(connection)
return yield unless busy_wait
begin
connection.execute("PRAGMA busy_timeout = 0")
yield
ensure
restore_busy_wait(connection, busy_wait)
end
end
|