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::TRANSACTION_CLOCK, SolidObjects::DatabaseAdapter::TRANSACTION_CLOCK_SCOPE
Instance Attribute Summary
#connection_pool, #fixed_connection
Instance Method Summary
collapse
#claim_lock, #configure_transaction_deadline, #database_now, for, #initialize, #lock_candidates, #read_database_now, #supports_skip_locked?, #with_connection, #with_transaction_clock
Instance Method Details
#busy_error?(error) ⇒ Boolean
143
144
145
146
147
148
149
150
151
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 143
def busy_error?(error)
cause = error
while cause
return true if cause.class.name.match?(/BusyException|BusyError/)
cause = cause.cause
end
false
end
|
124
125
126
127
128
129
130
131
132
133
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 124
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
12
13
14
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 12
def current_time_expression
"STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')"
end
|
#deadline_error?(error) ⇒ Boolean
136
137
138
139
140
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 136
def deadline_error?(error)
return false unless SyncDeadline.active?
busy_error?(error)
end
|
#restorable_busy_wait(connection) ⇒ Hash[Symbol, untyped]?
102
103
104
105
106
107
108
109
110
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 102
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.
113
114
115
116
117
118
119
120
121
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 113
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
17
18
19
20
21
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 17
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.
#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.
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 27
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
70
71
72
73
74
75
76
77
78
79
80
81
82
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 70
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
87
88
89
90
91
92
93
94
95
96
97
98
99
|
# File 'lib/solid_objects/database_adapters/sqlite.rb', line 87
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
|