litestack 0.2.6 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/BENCHMARKS.md +11 -0
  3. data/CHANGELOG.md +19 -0
  4. data/Gemfile +2 -0
  5. data/README.md +1 -1
  6. data/assets/event_page.png +0 -0
  7. data/assets/index_page.png +0 -0
  8. data/assets/topic_page.png +0 -0
  9. data/bench/bench_jobs_rails.rb +1 -1
  10. data/bench/bench_jobs_raw.rb +1 -1
  11. data/bench/uljob.rb +1 -1
  12. data/lib/action_cable/subscription_adapter/litecable.rb +1 -11
  13. data/lib/active_support/cache/litecache.rb +1 -1
  14. data/lib/generators/litestack/install/templates/database.yml +5 -1
  15. data/lib/litestack/liteboard/liteboard.rb +172 -35
  16. data/lib/litestack/liteboard/views/index.erb +52 -20
  17. data/lib/litestack/liteboard/views/layout.erb +189 -38
  18. data/lib/litestack/liteboard/views/litecable.erb +118 -0
  19. data/lib/litestack/liteboard/views/litecache.erb +144 -0
  20. data/lib/litestack/liteboard/views/litedb.erb +168 -0
  21. data/lib/litestack/liteboard/views/litejob.erb +151 -0
  22. data/lib/litestack/litecable.rb +27 -37
  23. data/lib/litestack/litecable.sql.yml +1 -1
  24. data/lib/litestack/litecache.rb +7 -18
  25. data/lib/litestack/litedb.rb +17 -2
  26. data/lib/litestack/litejob.rb +2 -3
  27. data/lib/litestack/litejobqueue.rb +51 -48
  28. data/lib/litestack/litemetric.rb +46 -69
  29. data/lib/litestack/litemetric.sql.yml +14 -12
  30. data/lib/litestack/litemetric_collector.sql.yml +4 -4
  31. data/lib/litestack/litequeue.rb +9 -20
  32. data/lib/litestack/litescheduler.rb +84 -0
  33. data/lib/litestack/litesearch/index.rb +230 -0
  34. data/lib/litestack/litesearch/model.rb +178 -0
  35. data/lib/litestack/litesearch/schema.rb +193 -0
  36. data/lib/litestack/litesearch/schema_adapters/backed_adapter.rb +147 -0
  37. data/lib/litestack/litesearch/schema_adapters/basic_adapter.rb +128 -0
  38. data/lib/litestack/litesearch/schema_adapters/contentless_adapter.rb +17 -0
  39. data/lib/litestack/litesearch/schema_adapters/standalone_adapter.rb +33 -0
  40. data/lib/litestack/litesearch/schema_adapters.rb +9 -0
  41. data/lib/litestack/litesearch.rb +37 -0
  42. data/lib/litestack/litesupport.rb +55 -125
  43. data/lib/litestack/version.rb +1 -1
  44. data/lib/litestack.rb +2 -1
  45. data/lib/sequel/adapters/litedb.rb +3 -2
  46. metadata +20 -3
@@ -54,7 +54,7 @@ class Litejobqueue < Litequeue
54
54
  # a method that returns a single instance of the job queue
55
55
  # for use by Litejob
56
56
  def self.jobqueue(options = {})
57
- @@queue ||= Litesupport.synchronize{self.new(options)}
57
+ @@queue ||= Litescheduler.synchronize{self.new(options)}
58
58
  end
59
59
 
60
60
  def self.new(options = {})
@@ -95,7 +95,7 @@ class Litejobqueue < Litequeue
95
95
  # jobqueue = Litejobqueue.new
96
96
  # jobqueue.push(EasyJob, params) # the job will be performed asynchronously
97
97
  def push(jobclass, params, delay=0, queue=nil)
98
- payload = Oj.dump({klass: jobclass, params: params, retries: @options[:retries], queue: queue})
98
+ payload = Oj.dump({klass: jobclass, params: params, retries: @options[:retries], queue: queue}, mode: :strict)
99
99
  res = super(payload, delay, queue)
100
100
  capture(:enqueue, queue)
101
101
  @logger.info("[litejob]:[ENQ] queue:#{res[1]} class:#{jobclass} job:#{res[0]}")
@@ -103,7 +103,7 @@ class Litejobqueue < Litequeue
103
103
  end
104
104
 
105
105
  def repush(id, job, delay=0, queue=nil)
106
- res = super(id, Oj.dump(job), delay, queue)
106
+ res = super(id, Oj.dump(job, mode: :strict), delay, queue)
107
107
  capture(:enqueue, queue)
108
108
  @logger.info("[litejob]:[ENQ] queue:#{res[0]} class:#{job[:klass]} job:#{id}")
109
109
  res
@@ -121,7 +121,7 @@ class Litejobqueue < Litequeue
121
121
  def delete(id)
122
122
  job = super(id)
123
123
  @logger.info("[litejob]:[DEL] job: #{job}")
124
- job = Oj.load(job[0]) if job
124
+ job = Oj.load(job[0], symbol_keys: true) if job
125
125
  job
126
126
  end
127
127
 
@@ -163,17 +163,17 @@ class Litejobqueue < Litequeue
163
163
  end
164
164
 
165
165
  def job_started
166
- Litesupport.synchronize(@mutex){@jobs_in_flight += 1}
166
+ Litescheduler.synchronize(@mutex){@jobs_in_flight += 1}
167
167
  end
168
168
 
169
169
  def job_finished
170
- Litesupport.synchronize(@mutex){@jobs_in_flight -= 1}
170
+ Litescheduler.synchronize(@mutex){@jobs_in_flight -= 1}
171
171
  end
172
172
 
173
173
  # optionally run a job in its own context
174
174
  def schedule(spawn = false, &block)
175
175
  if spawn
176
- Litesupport.spawn &block
176
+ Litescheduler.spawn &block
177
177
  else
178
178
  yield
179
179
  end
@@ -181,50 +181,23 @@ class Litejobqueue < Litequeue
181
181
 
182
182
  # create a worker according to environment
183
183
  def create_worker
184
- Litesupport.spawn do
184
+ Litescheduler.spawn do
185
185
  worker_sleep_index = 0
186
186
  while @running do
187
187
  processed = 0
188
- @queues.each do |level| # iterate through the levels
189
- level[1].each do |q| # iterate through the queues in the level
190
- index = 0
191
- max = level[0]
192
- while index < max && payload = pop(q[0], 1) # fearlessly use the same queue object
193
- capture(:dequeue, q[0])
188
+ @queues.each do |priority, queues| # iterate through the levels
189
+ queues.each do |queue, spawns| # iterate through the queues in the level
190
+ batched = 0
191
+
192
+ while (batched < priority) && (payload = pop(queue, 1)) # fearlessly use the same queue object
193
+ capture(:dequeue, queue)
194
194
  processed += 1
195
- index += 1
196
- begin
197
- id, job = payload[0], payload[1]
198
- job = Oj.load(job)
199
- @logger.info "[litejob]:[DEQ] queue:#{q[0]} class:#{job[:klass]} job:#{id}"
200
- klass = eval(job[:klass])
201
- schedule(q[1]) do # run the job in a new context
202
- job_started #(Litesupport.current_context)
203
- begin
204
- measure(:perform, q[0]){ klass.new.perform(*job[:params]) }
205
- @logger.info "[litejob]:[END] queue:#{q[0]} class:#{job[:klass]} job:#{id}"
206
- rescue Exception => e
207
- # we can retry the failed job now
208
- capture(:fail, q[0])
209
- if job[:retries] == 0
210
- @logger.error "[litejob]:[ERR] queue:#{q[0]} class:#{job[:klass]} job:#{id} failed with #{e}:#{e.message}, retries exhausted, moved to _dead queue"
211
- repush(id, job, @options[:dead_job_retention], '_dead')
212
- else
213
- capture(:retry, q[0])
214
- retry_delay = @options[:retry_delay_multiplier].pow(@options[:retries] - job[:retries]) * @options[:retry_delay]
215
- job[:retries] -= 1
216
- @logger.error "[litejob]:[ERR] queue:#{q[0]} class:#{job[:klass]} job:#{id} failed with #{e}:#{e.message}, retrying in #{retry_delay} seconds"
217
- repush(id, job, retry_delay, q[0])
218
- end
219
- end
220
- job_finished #(Litesupport.current_context)
221
- end
222
- rescue Exception => e
223
- # this is an error in the extraction of job info, retrying here will not be useful
224
- @logger.error "[litejob]:[ERR] failed to extract job info for: #{payload} with #{e}:#{e.message}"
225
- job_finished #(Litesupport.current_context)
226
- end
227
- Litesupport.switch #give other contexts a chance to run here
195
+ batched += 1
196
+
197
+ id, serialized_job = payload
198
+ process_job(queue, id, serialized_job, spawns)
199
+
200
+ Litescheduler.switch # give other contexts a chance to run here
228
201
  end
229
202
  end
230
203
  end
@@ -240,7 +213,7 @@ class Litejobqueue < Litequeue
240
213
 
241
214
  # create a gc for dead jobs
242
215
  def create_garbage_collector
243
- Litesupport.spawn do
216
+ Litescheduler.spawn do
244
217
  while @running do
245
218
  while jobs = pop('_dead', 100)
246
219
  if jobs[0].is_a? Array
@@ -254,4 +227,34 @@ class Litejobqueue < Litequeue
254
227
  end
255
228
  end
256
229
 
230
+ def process_job(queue, id, serialized_job, spawns)
231
+ job = Oj.load(serialized_job)
232
+ @logger.info "[litejob]:[DEQ] queue:#{queue} class:#{job["klass"]} job:#{id}"
233
+ klass = Object.const_get(job["klass"])
234
+ schedule(spawns) do # run the job in a new context
235
+ job_started # (Litesupport.current_context)
236
+ begin
237
+ measure(:perform, queue) { klass.new.perform(*job["params"]) }
238
+ @logger.info "[litejob]:[END] queue:#{queue} class:#{job["klass"]} job:#{id}"
239
+ rescue Exception => e # standard:disable Lint/RescueException
240
+ # we can retry the failed job now
241
+ capture(:fail, queue)
242
+ if job["retries"] == 0
243
+ @logger.error "[litejob]:[ERR] queue:#{queue} class:#{job["klass"]} job:#{id} failed with #{e}:#{e.message}, retries exhausted, moved to _dead queue"
244
+ repush(id, job, @options[:dead_job_retention], "_dead")
245
+ else
246
+ capture(:retry, queue)
247
+ retry_delay = @options[:retry_delay_multiplier].pow(@options[:retries] - job["retries"]) * @options[:retry_delay]
248
+ job["retries"] -= 1
249
+ @logger.error "[litejob]:[ERR] queue:#{queue} class:#{job["klass"]} job:#{id} failed with #{e}:#{e.message}, retrying in #{retry_delay} seconds"
250
+ repush(id, job, retry_delay, queue)
251
+ end
252
+ end
253
+ job_finished # (Litesupport.current_context)
254
+ end
255
+ rescue Exception => e # standard:disable Lint/RescueException
256
+ # this is an error in the extraction of job info, retrying here will not be useful
257
+ @logger.error "[litejob]:[ERR] failed to extract job info for: #{serialized_job} with #{e}:#{e.message}"
258
+ job_finished # (Litesupport.current_context)
259
+ end
257
260
  end
@@ -55,9 +55,12 @@ class Litemetric
55
55
  ## event capturing
56
56
  ##################
57
57
 
58
+ def current_time_slot
59
+ (Time.now.to_i / 300) * 300
60
+ end
58
61
 
59
62
  def capture(topic, event, key=event, value=nil)
60
- @collector.capture(topic, event, key, value)
63
+ @collector.capture(topic, event, key, value, current_time_slot)
61
64
  end
62
65
 
63
66
  def capture_snapshot(topic, state)
@@ -83,18 +86,18 @@ class Litemetric
83
86
  def events_summaries(topic, resolution, order, dir, search, count)
84
87
  search = "%#{search}%" if search
85
88
  if dir.downcase == "desc"
86
- run_stmt(:events_summaries, topic, resolution, order, search, count).to_a
89
+ run_stmt_hash(:events_summaries, topic, resolution, order, search, count)
87
90
  else
88
- run_stmt(:events_summaries_asc, topic, resolution, order, search, count).to_a
91
+ run_stmt_hash(:events_summaries_asc, topic, resolution, order, search, count)
89
92
  end
90
93
  end
91
94
 
92
95
  def keys_summaries(topic, event, resolution, order, dir, search, count)
93
96
  search = "%#{search}%" if search
94
97
  if dir.downcase == "desc"
95
- run_stmt(:keys_summaries, topic, event, resolution, order, search, count).to_a
98
+ run_stmt_hash(:keys_summaries, topic, event, resolution, order, search, count).to_a
96
99
  else
97
- run_stmt(:keys_summaries_asc, topic, event, resolution, order, search, count).to_a
100
+ run_stmt_hash(:keys_summaries_asc, topic, event, resolution, order, search, count).to_a
98
101
  end
99
102
  end
100
103
 
@@ -103,11 +106,11 @@ class Litemetric
103
106
  end
104
107
 
105
108
  def event_data_points(step, count, resolution, topic, event)
106
- run_stmt(:event_data_points, step, count, resolution, topic, event).to_a
109
+ run_stmt_hash(:event_data_points, step, count, resolution, topic, event).to_a
107
110
  end
108
111
 
109
112
  def key_data_points(step, count, resolution, topic, event, key)
110
- run_stmt(:key_data_points, step, count, resolution, topic, event, key).to_a
113
+ run_stmt_hash(:key_data_points, step, count, resolution, topic, event, key).to_a
111
114
  end
112
115
 
113
116
  def snapshot(topic)
@@ -130,7 +133,21 @@ class Litemetric
130
133
  ###################
131
134
 
132
135
  private
133
-
136
+
137
+ def run_stmt_hash(stmt, *args)
138
+ res = run_stmt(stmt, *args)
139
+ cols = run_stmt_method(stmt, :columns)
140
+ hashes = []
141
+ res.each do | row |
142
+ hash = {}
143
+ row.each_with_index do |field, i|
144
+ hash[cols[i]] = field
145
+ end
146
+ hashes << hash
147
+ end
148
+ hashes
149
+ end
150
+
134
151
  def exit_callback
135
152
  STDERR.puts "--- Litemetric detected an exit, flushing metrics"
136
153
  @running = false
@@ -156,29 +173,13 @@ class Litemetric
156
173
  end
157
174
 
158
175
  def create_connection
159
- conn = super
160
- conn.wal_autocheckpoint = 10000 # checkpoint after 10000 pages are written
161
- sql = YAML.load_file("#{__dir__}/litemetric.sql.yml")
162
- version = conn.get_first_value("PRAGMA user_version")
163
- sql["schema"].each_pair do |v, obj|
164
- if v > version
165
- begin
166
- conn.transaction do
167
- obj.each{|k, s| conn.execute(s)}
168
- conn.user_version = v
169
- end
170
- rescue Exception => e
171
- STDERR.puts e.message
172
- raise e
173
- end
174
- end
175
- end
176
- sql["stmts"].each { |k, v| conn.stmts[k.to_sym] = conn.prepare(v) }
177
- conn
176
+ super("#{__dir__}/litemetric.sql.yml") do |conn|
177
+ conn.wal_autocheckpoint = 10000 # checkpoint after 10000 pages are written
178
+ end
178
179
  end
179
180
 
180
181
  def create_flusher
181
- Litesupport.spawn do
182
+ Litescheduler.spawn do
182
183
  while @running do
183
184
  sleep @options[:flush_interval]
184
185
  flush
@@ -187,7 +188,7 @@ class Litemetric
187
188
  end
188
189
 
189
190
  def create_summarizer
190
- Litesupport.spawn do
191
+ Litescheduler.spawn do
191
192
  while @running do
192
193
  sleep @options[:summarize_interval]
193
194
  summarize
@@ -210,7 +211,7 @@ class Litemetric
210
211
  end
211
212
 
212
213
  def create_snapshotter
213
- Litesupport.spawn do
214
+ Litescheduler.spawn do
214
215
  while @running do
215
216
  sleep @litemetric.options[:snapshot_interval]
216
217
  capture_snapshot
@@ -228,13 +229,16 @@ class Litemetric
228
229
  end
229
230
 
230
231
  def measure(event, key=event)
231
- return yield unless @litemetric
232
+ unless @litemetric
233
+ yield
234
+ return 0
235
+ end
232
236
  t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
233
- res = yield
237
+ yield
234
238
  t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
235
- value = (( t2 - t1 ) * 1000).round # capture time in milliseconds
239
+ value = t2 - t1
236
240
  capture(event, key, value)
237
- res
241
+ value # return value so other events can reuse it
238
242
  end
239
243
 
240
244
  def capture_snapshot
@@ -273,16 +277,16 @@ class Litemetric
273
277
  init(options)
274
278
  end
275
279
 
276
- def capture(topic, event, key, value=nil)
280
+ def capture(topic, event, key, value=nil, time=nil)
277
281
  if key.is_a? Array
278
- key.each{|k| capture_single_key(topic, event, k, value)}
282
+ key.each{|k| capture_single_key(topic, event, k, value, time)}
279
283
  else
280
- capture_single_key(topic, event, key, value)
284
+ capture_single_key(topic, event, key, value, time)
281
285
  end
282
286
  end
283
287
 
284
- def capture_single_key(topic, event, key, value)
285
- run_stmt(:capture_event, topic.to_s, event.to_s, key.to_s, nil ,1, value)
288
+ def capture_single_key(topic, event, key, value, time=nil)
289
+ run_stmt(:capture_event, topic.to_s, event.to_s, key.to_s, time ,1, value)
286
290
  end
287
291
 
288
292
  def flush
@@ -302,37 +306,10 @@ class Litemetric
302
306
  end
303
307
 
304
308
  def create_connection
305
- conn = super
306
- conn.execute("ATTACH ? as m", @options[:dbpath].to_s)
307
- conn.wal_autocheckpoint = 10000
308
- sql = YAML.load_file("#{__dir__}/litemetric_collector.sql.yml")
309
- version = conn.get_first_value("PRAGMA user_version")
310
- sql["schema"].each_pair do |v, obj|
311
- if v > version
312
- conn.transaction do
313
- obj.each do |k, s|
314
- begin
315
- conn.execute(s)
316
- rescue Exception => e
317
- STDERR.puts "Error parsing #{k}"
318
- STDERR.puts s
319
- raise e
320
- end
321
- end
322
- conn.user_version = v
323
- end
324
- end
325
- end
326
- sql["stmts"].each do |k, v|
327
- begin
328
- conn.stmts[k.to_sym] = conn.prepare(v)
329
- rescue Exception => e
330
- STDERR.puts "Error parsing #{k}"
331
- STDERR.puts v
332
- raise e
333
- end
309
+ super("#{__dir__}/litemetric_collector.sql.yml") do |conn|
310
+ conn.execute("ATTACH ? as m", @options[:dbpath].to_s)
311
+ conn.wal_autocheckpoint = 10000
334
312
  end
335
- conn
336
313
  end
337
314
 
338
315
  end
@@ -13,13 +13,13 @@ schema:
13
13
  name TEXT DEFAULT('___') NOT NULL ON CONFLICT REPLACE,
14
14
  key TEXT DEFAULT('___') NOT NULL ON CONFLICT REPLACE,
15
15
  count INTEGER DEFAULT(0) NOT NULL ON CONFLICT REPLACE,
16
- value INTEGER,
17
- minimum INTEGER,
18
- maximum INTEGER,
16
+ value REAL,
17
+ minimum REAL,
18
+ maximum REAL,
19
19
  created_at INTEGER DEFAULT((unixepoch()/300*300)) NOT NULL,
20
20
  resolution TEXT DEFAULT('minute') NOT NULL,
21
21
  PRIMARY KEY(resolution, created_at, topic, name, key)
22
- );
22
+ ) STRICT;
23
23
 
24
24
  create_topic_index_on_events: CREATE INDEX events_topic_index ON events (resolution, created_at, topic) WHERE name = '___';
25
25
  create_event_index_on_events: CREATE INDEX events_event_index ON events (resolution, created_at , topic, name) WHERE key = '___';
@@ -226,7 +226,7 @@ stmts:
226
226
  FROM events
227
227
  WHERE topic = ?1 AND name = ?2 AND resolution = ?3
228
228
  AND created_at <= unixepoch()
229
- AND created_at >= unixepoch() -?6
229
+ AND created_at >= unixepoch() - ?6
230
230
  AND key != '___'
231
231
  AND iif(length(?5) > 0, rowid IN (SELECT rowid FROM events_search WHERE key LIKE ?5), TRUE)
232
232
  GROUP BY key
@@ -251,7 +251,7 @@ stmts:
251
251
  FROM events
252
252
  WHERE topic = ?1 AND name = ?2 AND resolution = ?3
253
253
  AND created_at <= unixepoch()
254
- AND created_at >= unixepoch() -?6
254
+ AND created_at >= unixepoch() - ?6
255
255
  AND key != '___'
256
256
  AND iif(length(?5) > 0, rowid IN (SELECT rowid FROM events_search WHERE key LIKE ?5), TRUE)
257
257
  GROUP BY key
@@ -272,8 +272,8 @@ stmts:
272
272
  SELECT
273
273
  datetime(ts.slot, 'unixepoch') AS rtime,
274
274
  ts.slot AS unixtime,
275
- format('%.2f', sum(events.count)) AS rcount,
276
- format('%.2f', avg(events.value/events.count)) AS ravg
275
+ sum(events.count) AS rcount,
276
+ avg(events.value/events.count) AS ravg
277
277
  FROM (
278
278
  WITH RECURSIVE timeslot(x) AS (
279
279
  SELECT (unixepoch()/(?1))*(?1)
@@ -294,8 +294,9 @@ stmts:
294
294
  SELECT
295
295
  datetime(ts.slot, 'unixepoch') AS rtime,
296
296
  ts.slot AS unixtime,
297
- format('%.2f', sum(events.count)) AS rcount,
298
- format('%.2f', avg(events.value/events.count)) AS ravg
297
+ events.count AS rcount,
298
+ events.value / events.count AS ravg,
299
+ events.value AS rtotal
299
300
  FROM (
300
301
  WITH RECURSIVE timeslot(x) AS (
301
302
  SELECT (unixepoch()/(?1))*(?1)
@@ -317,8 +318,9 @@ stmts:
317
318
  SELECT
318
319
  datetime(ts.slot, 'unixepoch') AS rtime,
319
320
  ts.slot AS unixtime,
320
- format('%.2f', sum(events.count)) AS rcount,
321
- format('%.2f', avg(events.value/events.count)) AS ravg
321
+ events.count AS rcount,
322
+ events.value / events.count AS ravg,
323
+ events.value AS rtotal
322
324
  FROM (
323
325
  WITH RECURSIVE timeslot(x) AS (
324
326
  SELECT (unixepoch()/(?1))*(?1)
@@ -7,13 +7,13 @@ schema:
7
7
  name TEXT DEFAULT('___') NOT NULL ON CONFLICT REPLACE,
8
8
  key TEXT DEFAULT('___') NOT NULL ON CONFLICT REPLACE,
9
9
  count INTEGER DEFAULT(0) NOT NULL ON CONFLICT REPLACE,
10
- value INTEGER,
11
- minimum INTEGER,
12
- maximum INTEGER,
10
+ value REAL,
11
+ minimum REAL,
12
+ maximum REAL,
13
13
  created_at INTEGER DEFAULT((unixepoch()/300*300)) NOT NULL ON CONFLICT REPLACE,
14
14
  resolution TEXT DEFAULT('minute') NOT NULL,
15
15
  PRIMARY KEY(resolution, created_at, topic, name, key)
16
- );
16
+ ) STRICT;
17
17
 
18
18
  stmts:
19
19
 
@@ -78,12 +78,12 @@ class Litequeue
78
78
 
79
79
  # deletes all the entries in all queues, or if a queue name is given, deletes all entries in that specific queue
80
80
  def clear(queue=nil)
81
- run_sql("DELETE FROM queue WHERE iif(?, name = ?, 1)", queue)
81
+ run_sql("DELETE FROM queue WHERE iif(?1 IS NOT NULL, name = ?1, TRUE)", queue)
82
82
  end
83
83
 
84
84
  # returns a count of entries in all queues, or if a queue name is given, reutrns the count of entries in that queue
85
85
  def count(queue=nil)
86
- run_sql("SELECT count(*) FROM queue WHERE iif(?, name = ?, 1)", queue)[0][0]
86
+ run_sql("SELECT count(*) FROM queue WHERE iif(?1 IS NOT NULL, name = ?1, TRUE)", queue)[0][0]
87
87
  end
88
88
 
89
89
  # return the size of the queue file on disk
@@ -116,27 +116,16 @@ class Litequeue
116
116
  private
117
117
 
118
118
  def create_connection
119
- conn = super
120
- conn.wal_autocheckpoint = 10000
121
- sql = YAML.load_file("#{__dir__}/litequeue.sql.yml")
122
- version = conn.get_first_value("PRAGMA user_version")
123
- sql["schema"].each_pair do |v, obj|
124
- if v > version
125
- conn.transaction(:immediate) do
126
- obj.each{|k, s| conn.execute(s)}
127
- conn.user_version = v
119
+ super("#{__dir__}/litequeue.sql.yml") do |conn|
120
+ conn.wal_autocheckpoint = 10000
121
+ # check if there is an old database and convert entries to the new format
122
+ if conn.get_first_value("select count(*) from sqlite_master where name = '_ul_queue_'") == 1
123
+ conn.transaction(:immediate) do
124
+ conn.execute("INSERT INTO queue(fire_at, name, value, created_at) SELECT fire_at, queue, value, created_at FROM _ul_queue_")
125
+ conn.execute("DROP TABLE _ul_queue_")
128
126
  end
129
127
  end
130
- end
131
- sql["stmts"].each { |k, v| conn.stmts[k.to_sym] = conn.prepare(v) }
132
- # check if there is an old database and convert entries to the new format
133
- if conn.get_first_value("select count(*) from sqlite_master where name = '_ul_queue_'") == 1
134
- conn.transaction(:immediate) do
135
- conn.execute("INSERT INTO queue(fire_at, name, value, created_at) SELECT fire_at, queue, value, created_at FROM _ul_queue_")
136
- conn.execute("DROP TABLE _ul_queue_")
137
- end
138
128
  end
139
- conn
140
129
  end
141
130
 
142
131
  end
@@ -0,0 +1,84 @@
1
+ # frozen_stringe_literal: true
2
+
3
+ module Litescheduler
4
+ # cache the scheduler we are running in
5
+ # it is an error to change the scheduler for a process
6
+ # or for a child forked from that process
7
+ def self.backend
8
+ @backend ||= if Fiber.scheduler
9
+ :fiber
10
+ elsif defined? Polyphony
11
+ :polyphony
12
+ elsif defined? Iodine
13
+ :iodine
14
+ else
15
+ :threaded
16
+ end
17
+ end
18
+
19
+ # spawn a new execution context
20
+ def self.spawn(&block)
21
+ if backend == :fiber
22
+ Fiber.schedule(&block)
23
+ elsif backend == :polyphony
24
+ spin(&block)
25
+ elsif backend == :threaded or backend == :iodine
26
+ Thread.new(&block)
27
+ end
28
+ # we should never reach here
29
+ end
30
+
31
+ def self.storage
32
+ if backend == :fiber || backend == :poylphony
33
+ Fiber.current.storage
34
+ else
35
+ Thread.current
36
+ end
37
+ end
38
+
39
+ def self.current
40
+ if backend == :fiber || backend == :poylphony
41
+ Fiber.current
42
+ else
43
+ Thread.current
44
+ end
45
+ end
46
+
47
+ # switch the execution context to allow others to run
48
+ def self.switch
49
+ if backend == :fiber
50
+ Fiber.scheduler.yield
51
+ true
52
+ elsif backend == :polyphony
53
+ Fiber.current.schedule
54
+ Thread.current.switch_fiber
55
+ true
56
+ else
57
+ #Thread.pass
58
+ false
59
+ end
60
+ end
61
+
62
+ # bold assumption, we will only synchronize threaded code!
63
+ # If some code explicitly wants to synchronize a fiber
64
+ # they must send (true) as a parameter to this method
65
+ # else it is a no-op for fibers
66
+ def self.synchronize(fiber_sync = false, &block)
67
+ if backend == :fiber or backend == :polyphony
68
+ yield # do nothing, just run the block as is
69
+ else
70
+ self.mutex.synchronize(&block)
71
+ end
72
+ end
73
+
74
+ def self.max_contexts
75
+ return 50 if backend == :fiber || backend == :polyphony
76
+ 5
77
+ end
78
+
79
+ # mutex initialization
80
+ def self.mutex
81
+ # a single mutex per process (is that ok?)
82
+ @@mutex ||= Mutex.new
83
+ end
84
+ end