litestack 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.standard.yml +3 -0
- data/BENCHMARKS.md +23 -7
- data/CHANGELOG.md +11 -0
- data/Gemfile +1 -7
- data/Gemfile.lock +92 -0
- data/README.md +120 -6
- data/ROADMAP.md +45 -0
- data/Rakefile +3 -1
- data/WHYLITESTACK.md +1 -1
- data/assets/litecache_metrics.png +0 -0
- data/assets/litedb_metrics.png +0 -0
- data/assets/litemetric_logo_teal.png +0 -0
- data/assets/litesearch_logo_teal.png +0 -0
- data/bench/bench.rb +17 -10
- data/bench/bench_cache_rails.rb +10 -13
- data/bench/bench_cache_raw.rb +17 -22
- data/bench/bench_jobs_rails.rb +18 -12
- data/bench/bench_jobs_raw.rb +17 -10
- data/bench/bench_queue.rb +4 -6
- data/bench/rails_job.rb +5 -7
- data/bench/skjob.rb +4 -4
- data/bench/uljob.rb +6 -6
- data/lib/action_cable/subscription_adapter/litecable.rb +5 -8
- data/lib/active_job/queue_adapters/litejob_adapter.rb +6 -8
- data/lib/active_record/connection_adapters/litedb_adapter.rb +65 -75
- data/lib/active_support/cache/litecache.rb +38 -41
- data/lib/generators/litestack/install/install_generator.rb +3 -3
- data/lib/generators/litestack/install/templates/database.yml +7 -1
- data/lib/litestack/liteboard/liteboard.rb +269 -149
- data/lib/litestack/litecable.rb +41 -37
- data/lib/litestack/litecable.sql.yml +22 -11
- data/lib/litestack/litecache.rb +79 -88
- data/lib/litestack/litecache.sql.yml +81 -22
- data/lib/litestack/litecache.yml +1 -1
- data/lib/litestack/litedb.rb +35 -40
- data/lib/litestack/litejob.rb +30 -29
- data/lib/litestack/litejobqueue.rb +63 -65
- data/lib/litestack/litemetric.rb +80 -92
- data/lib/litestack/litemetric.sql.yml +244 -234
- data/lib/litestack/litemetric_collector.sql.yml +38 -41
- data/lib/litestack/litequeue.rb +39 -41
- data/lib/litestack/litequeue.sql.yml +39 -31
- data/lib/litestack/litescheduler.rb +15 -15
- data/lib/litestack/litesearch/index.rb +93 -63
- data/lib/litestack/litesearch/model.rb +66 -65
- data/lib/litestack/litesearch/schema.rb +53 -56
- data/lib/litestack/litesearch/schema_adapters/backed_adapter.rb +46 -50
- data/lib/litestack/litesearch/schema_adapters/basic_adapter.rb +44 -35
- data/lib/litestack/litesearch/schema_adapters/contentless_adapter.rb +3 -6
- data/lib/litestack/litesearch/schema_adapters/standalone_adapter.rb +7 -9
- data/lib/litestack/litesearch/schema_adapters.rb +4 -9
- data/lib/litestack/litesearch.rb +6 -9
- data/lib/litestack/litesupport.rb +76 -86
- data/lib/litestack/railtie.rb +1 -1
- data/lib/litestack/version.rb +2 -2
- data/lib/litestack.rb +6 -4
- data/lib/railties/rails/commands/dbconsole.rb +11 -15
- data/lib/sequel/adapters/litedb.rb +16 -21
- data/lib/sequel/adapters/shared/litedb.rb +168 -168
- data/scripts/build_metrics.rb +91 -0
- data/scripts/test_cable.rb +30 -0
- data/scripts/test_job_retry.rb +33 -0
- data/scripts/test_metrics.rb +60 -0
- data/template.rb +2 -2
- metadata +101 -6
data/lib/litestack/litemetric.rb
CHANGED
@@ -1,31 +1,30 @@
|
|
1
1
|
# frozen_stringe_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "singleton"
|
4
4
|
|
5
|
-
require_relative
|
5
|
+
require_relative "./litesupport"
|
6
6
|
|
7
7
|
# this class is a singleton
|
8
8
|
# and should remain so
|
9
9
|
class Litemetric
|
10
|
-
|
11
10
|
include Singleton
|
12
11
|
include Litesupport::Liteconnection
|
13
|
-
|
12
|
+
|
14
13
|
DEFAULT_OPTIONS = {
|
15
14
|
config_path: "./litemetric.yml",
|
16
15
|
path: Litesupport.root.join("metrics.sqlite3"),
|
17
16
|
sync: 1,
|
18
17
|
mmap_size: 128 * 1024 * 1024, # 16MB of memory to easily process 1 year worth of data
|
19
|
-
flush_interval:
|
18
|
+
flush_interval: 10, # flush data every 10 seconds
|
20
19
|
summarize_interval: 30, # summarize data every 1/2 minute
|
21
|
-
snapshot_interval: 10*60 # snapshot every 10 minutes
|
20
|
+
snapshot_interval: 10 * 60 # snapshot every 10 minutes
|
22
21
|
}
|
23
22
|
|
24
23
|
RESOLUTIONS = {
|
25
24
|
minute: 300, # 5 minutes (highest resolution)
|
26
25
|
hour: 3600, # 1 hour
|
27
|
-
day: 24*3600, # 1 day
|
28
|
-
week: 7*24*3600 # 1 week (lowest resolution)
|
26
|
+
day: 24 * 3600, # 1 day
|
27
|
+
week: 7 * 24 * 3600 # 1 week (lowest resolution)
|
29
28
|
}
|
30
29
|
|
31
30
|
# :nodoc:
|
@@ -34,111 +33,111 @@ class Litemetric
|
|
34
33
|
# need to rethink the whole singleton thing
|
35
34
|
@options = options
|
36
35
|
end
|
37
|
-
|
36
|
+
|
38
37
|
def self.options
|
39
38
|
@options
|
40
39
|
end
|
41
40
|
|
42
|
-
# :nodoc:
|
41
|
+
# :nodoc:
|
43
42
|
def initialize(options = {})
|
44
43
|
options = options.merge(Litemetric.options) if Litemetric.options
|
45
44
|
init(options)
|
46
45
|
end
|
47
|
-
|
46
|
+
|
48
47
|
# registers a class for metrics to be collected
|
49
48
|
def register(identifier)
|
50
|
-
@registered[identifier] = true
|
49
|
+
@registered[identifier] = true
|
51
50
|
@metrics[identifier] = {} unless @metrics[identifier]
|
52
51
|
run_stmt(:register_topic, identifier) # it is safe to call register topic multiple times with the same identifier
|
53
52
|
end
|
54
|
-
|
53
|
+
|
55
54
|
## event capturing
|
56
55
|
##################
|
57
|
-
|
56
|
+
|
58
57
|
def current_time_slot
|
59
58
|
(Time.now.to_i / 300) * 300
|
60
59
|
end
|
61
|
-
|
62
|
-
def capture(topic, event, key=event, value=nil)
|
60
|
+
|
61
|
+
def capture(topic, event, key = event, value = nil)
|
63
62
|
@collector.capture(topic, event, key, value, current_time_slot)
|
64
63
|
end
|
65
|
-
|
64
|
+
|
66
65
|
def capture_snapshot(topic, state)
|
67
66
|
run_stmt(:capture_state, topic, Oj.dump(state))
|
68
67
|
end
|
69
68
|
|
70
69
|
## event reporting
|
71
70
|
##################
|
72
|
-
|
71
|
+
|
73
72
|
def topics
|
74
73
|
run_stmt(:list_topics).to_a
|
75
74
|
end
|
76
|
-
|
75
|
+
|
77
76
|
def topic_summaries(resolution, count, order, dir, search)
|
78
77
|
search = "%#{search}%" if search
|
79
|
-
if dir.downcase == "desc"
|
78
|
+
if dir.downcase == "desc"
|
80
79
|
run_stmt(:topics_summaries, resolution, count, order, search).to_a
|
81
80
|
else
|
82
81
|
run_stmt(:topics_summaries_asc, resolution, count, order, search).to_a
|
83
82
|
end
|
84
83
|
end
|
85
|
-
|
84
|
+
|
86
85
|
def events_summaries(topic, resolution, order, dir, search, count)
|
87
86
|
search = "%#{search}%" if search
|
88
|
-
if dir.downcase == "desc"
|
87
|
+
if dir.downcase == "desc"
|
89
88
|
run_stmt_hash(:events_summaries, topic, resolution, order, search, count)
|
90
89
|
else
|
91
90
|
run_stmt_hash(:events_summaries_asc, topic, resolution, order, search, count)
|
92
91
|
end
|
93
92
|
end
|
94
|
-
|
93
|
+
|
95
94
|
def keys_summaries(topic, event, resolution, order, dir, search, count)
|
96
95
|
search = "%#{search}%" if search
|
97
|
-
if dir.downcase == "desc"
|
96
|
+
if dir.downcase == "desc"
|
98
97
|
run_stmt_hash(:keys_summaries, topic, event, resolution, order, search, count).to_a
|
99
98
|
else
|
100
99
|
run_stmt_hash(:keys_summaries_asc, topic, event, resolution, order, search, count).to_a
|
101
|
-
end
|
100
|
+
end
|
102
101
|
end
|
103
102
|
|
104
103
|
def topic_data_points(step, count, resolution, topic)
|
105
104
|
run_stmt(:topic_data_points, step, count, resolution, topic).to_a
|
106
|
-
end
|
105
|
+
end
|
107
106
|
|
108
107
|
def event_data_points(step, count, resolution, topic, event)
|
109
108
|
run_stmt_hash(:event_data_points, step, count, resolution, topic, event).to_a
|
110
|
-
end
|
109
|
+
end
|
111
110
|
|
112
111
|
def key_data_points(step, count, resolution, topic, event, key)
|
113
112
|
run_stmt_hash(:key_data_points, step, count, resolution, topic, event, key).to_a
|
114
|
-
end
|
113
|
+
end
|
115
114
|
|
116
115
|
def snapshot(topic)
|
117
116
|
run_stmt(:snapshot, topic)[0].to_a
|
118
117
|
end
|
119
|
-
|
118
|
+
|
120
119
|
## summarize data
|
121
|
-
#################
|
120
|
+
#################
|
122
121
|
|
123
122
|
def summarize
|
124
|
-
run_stmt(:summarize_events, RESOLUTIONS[:hour], "hour", "minute")
|
125
|
-
run_stmt(:summarize_events, RESOLUTIONS[:day], "day", "hour")
|
123
|
+
run_stmt(:summarize_events, RESOLUTIONS[:hour], "hour", "minute")
|
124
|
+
run_stmt(:summarize_events, RESOLUTIONS[:day], "day", "hour")
|
126
125
|
run_stmt(:summarize_events, RESOLUTIONS[:week], "week", "day")
|
127
|
-
run_stmt(:delete_events, "minute", RESOLUTIONS[:hour]*1)
|
128
|
-
run_stmt(:delete_events, "hour", RESOLUTIONS[:day]*1)
|
129
|
-
run_stmt(:delete_events, "day", RESOLUTIONS[:week]*1)
|
126
|
+
run_stmt(:delete_events, "minute", RESOLUTIONS[:hour] * 1)
|
127
|
+
run_stmt(:delete_events, "hour", RESOLUTIONS[:day] * 1)
|
128
|
+
run_stmt(:delete_events, "day", RESOLUTIONS[:week] * 1)
|
130
129
|
end
|
131
130
|
|
132
131
|
## background stuff
|
133
132
|
###################
|
134
133
|
|
135
134
|
private
|
136
|
-
|
135
|
+
|
137
136
|
def run_stmt_hash(stmt, *args)
|
138
137
|
res = run_stmt(stmt, *args)
|
139
138
|
cols = run_stmt_method(stmt, :columns)
|
140
139
|
hashes = []
|
141
|
-
res.each do |
|
140
|
+
res.each do |row|
|
142
141
|
hash = {}
|
143
142
|
row.each_with_index do |field, i|
|
144
143
|
hash[cols[i]] = field
|
@@ -147,11 +146,12 @@ class Litemetric
|
|
147
146
|
end
|
148
147
|
hashes
|
149
148
|
end
|
150
|
-
|
149
|
+
|
151
150
|
def exit_callback
|
152
|
-
|
151
|
+
return unless @collector.count > 0
|
152
|
+
warn "--- Litemetric detected an exit, flushing metrics"
|
153
153
|
@running = false
|
154
|
-
flush
|
154
|
+
@collector.flush
|
155
155
|
end
|
156
156
|
|
157
157
|
def setup
|
@@ -164,38 +164,29 @@ class Litemetric
|
|
164
164
|
@flusher = create_flusher
|
165
165
|
end
|
166
166
|
|
167
|
-
def current_time_slot
|
168
|
-
(Time.now.to_i / 300) * 300 # every 5 minutes
|
169
|
-
end
|
170
|
-
|
171
|
-
def flush
|
172
|
-
@collector.flush
|
173
|
-
end
|
174
|
-
|
175
167
|
def create_connection
|
176
168
|
super("#{__dir__}/litemetric.sql.yml") do |conn|
|
177
169
|
conn.wal_autocheckpoint = 10000 # checkpoint after 10000 pages are written
|
178
170
|
end
|
179
171
|
end
|
180
|
-
|
172
|
+
|
181
173
|
def create_flusher
|
182
174
|
Litescheduler.spawn do
|
183
|
-
while @running
|
175
|
+
while @running
|
184
176
|
sleep @options[:flush_interval]
|
185
|
-
flush
|
186
|
-
end
|
187
|
-
end
|
177
|
+
@collector.flush
|
178
|
+
end
|
179
|
+
end
|
188
180
|
end
|
189
|
-
|
181
|
+
|
190
182
|
def create_summarizer
|
191
183
|
Litescheduler.spawn do
|
192
|
-
while @running
|
184
|
+
while @running
|
193
185
|
sleep @options[:summarize_interval]
|
194
186
|
summarize
|
195
|
-
end
|
196
|
-
end
|
187
|
+
end
|
188
|
+
end
|
197
189
|
end
|
198
|
-
|
199
190
|
end
|
200
191
|
|
201
192
|
## Measurable Module
|
@@ -203,44 +194,43 @@ end
|
|
203
194
|
|
204
195
|
class Litemetric
|
205
196
|
module Measurable
|
206
|
-
|
207
197
|
def collect_metrics
|
208
198
|
@litemetric = Litemetric.instance
|
209
199
|
@litemetric.register(metrics_identifier)
|
210
|
-
@snapshotter = create_snapshotter
|
200
|
+
@snapshotter = create_snapshotter
|
211
201
|
end
|
212
202
|
|
213
203
|
def create_snapshotter
|
214
204
|
Litescheduler.spawn do
|
215
|
-
while @running
|
205
|
+
while @running
|
216
206
|
sleep @litemetric.options[:snapshot_interval]
|
217
207
|
capture_snapshot
|
218
|
-
end
|
219
|
-
end
|
208
|
+
end
|
209
|
+
end
|
220
210
|
end
|
221
211
|
|
222
212
|
def metrics_identifier
|
223
213
|
self.class.name # override in included classes
|
224
214
|
end
|
225
215
|
|
226
|
-
def capture(event, key=event, value=nil)
|
216
|
+
def capture(event, key = event, value = nil)
|
227
217
|
return unless @litemetric
|
228
218
|
@litemetric.capture(metrics_identifier, event, key, value)
|
229
219
|
end
|
230
|
-
|
231
|
-
def measure(event, key=event)
|
220
|
+
|
221
|
+
def measure(event, key = event)
|
232
222
|
unless @litemetric
|
233
223
|
yield
|
234
|
-
return 0
|
224
|
+
return 0
|
235
225
|
end
|
236
|
-
t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
226
|
+
t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
237
227
|
yield
|
238
228
|
t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
239
|
-
value =
|
229
|
+
value = t2 - t1
|
240
230
|
capture(event, key, value)
|
241
|
-
value # return value so other events can reuse it
|
242
|
-
end
|
243
|
-
|
231
|
+
value # return value so other events can reuse it
|
232
|
+
end
|
233
|
+
|
244
234
|
def capture_snapshot
|
245
235
|
return unless @litemetric
|
246
236
|
state = snapshot if defined? snapshot
|
@@ -248,20 +238,17 @@ class Litemetric
|
|
248
238
|
@litemetric.capture_snapshot(metrics_identifier, state)
|
249
239
|
end
|
250
240
|
end
|
251
|
-
|
252
241
|
end
|
253
242
|
end
|
254
243
|
|
255
244
|
class Litemetric
|
256
|
-
|
257
245
|
class Collector
|
258
|
-
|
259
246
|
include Litesupport::Liteconnection
|
260
|
-
|
247
|
+
|
261
248
|
DEFAULT_OPTIONS = {
|
262
249
|
path: ":memory:",
|
263
250
|
sync: 1,
|
264
|
-
flush_interval:
|
251
|
+
flush_interval: 3, # flush data every 1 minute
|
265
252
|
summarize_interval: 10, # summarize data every 1 minute
|
266
253
|
snapshot_interval: 1 # snapshot every 10 minutes
|
267
254
|
}
|
@@ -269,28 +256,31 @@ class Litemetric
|
|
269
256
|
RESOLUTIONS = {
|
270
257
|
minute: 300, # 5 minutes (highest resolution)
|
271
258
|
hour: 3600, # 1 hour
|
272
|
-
day: 24*3600, # 1 day
|
273
|
-
week: 7*24*3600 # 1 week (lowest resolution)
|
259
|
+
day: 24 * 3600, # 1 day
|
260
|
+
week: 7 * 24 * 3600 # 1 week (lowest resolution)
|
274
261
|
}
|
275
|
-
|
262
|
+
|
276
263
|
def initialize(options = {})
|
277
264
|
init(options)
|
278
265
|
end
|
279
|
-
|
280
|
-
def capture(topic, event, key, value=nil, time=nil)
|
266
|
+
|
267
|
+
def capture(topic, event, key, value = nil, time = nil)
|
281
268
|
if key.is_a? Array
|
282
|
-
key.each{|k| capture_single_key(topic, event, k, value, time)}
|
269
|
+
key.each { |k| capture_single_key(topic, event, k, value, time) }
|
283
270
|
else
|
284
271
|
capture_single_key(topic, event, key, value, time)
|
285
272
|
end
|
286
273
|
end
|
287
|
-
|
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
|
274
|
+
|
275
|
+
def capture_single_key(topic, event, key, value, time = nil)
|
276
|
+
run_stmt(:capture_event, topic.to_s, event.to_s, key.to_s, time, 1, value)
|
290
277
|
end
|
291
278
|
|
279
|
+
def count
|
280
|
+
run_stmt(:event_count)[0][0]
|
281
|
+
end
|
282
|
+
|
292
283
|
def flush
|
293
|
-
t = Time.now
|
294
284
|
limit = 1000 # migrate 1000 records at a time
|
295
285
|
count = run_stmt(:event_count)[0][0]
|
296
286
|
while count > 0
|
@@ -299,19 +289,17 @@ class Litemetric
|
|
299
289
|
conn.stmts[:migrate_events].execute!(limit)
|
300
290
|
conn.stmts[:delete_migrated_events].execute!(limit)
|
301
291
|
count = conn.stmts[:event_count].execute![0][0]
|
302
|
-
end
|
292
|
+
end
|
303
293
|
end
|
304
|
-
sleep 0.005 #give other threads a chance to run
|
294
|
+
sleep 0.005 # give other threads a chance to run
|
305
295
|
end
|
306
296
|
end
|
307
297
|
|
308
298
|
def create_connection
|
309
299
|
super("#{__dir__}/litemetric_collector.sql.yml") do |conn|
|
310
|
-
conn.execute("ATTACH ? as m", @options[:dbpath].to_s)
|
300
|
+
conn.execute("ATTACH ? as m", @options[:dbpath].to_s)
|
311
301
|
conn.wal_autocheckpoint = 10000
|
312
302
|
end
|
313
303
|
end
|
314
|
-
|
315
304
|
end
|
316
|
-
|
317
305
|
end
|