litestack 0.2.3 → 0.3.0
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/CHANGELOG.md +16 -0
- data/Gemfile +2 -0
- data/README.md +5 -11
- data/assets/event_page.png +0 -0
- data/assets/index_page.png +0 -0
- data/assets/topic_page.png +0 -0
- data/bench/bench_jobs_rails.rb +1 -1
- data/bench/bench_jobs_raw.rb +1 -1
- data/bin/liteboard +81 -0
- data/lib/action_cable/subscription_adapter/litecable.rb +1 -11
- data/lib/generators/litestack/install/USAGE +11 -0
- data/lib/generators/litestack/install/install_generator.rb +35 -0
- data/lib/generators/litestack/install/templates/cable.yml +11 -0
- data/lib/generators/litestack/install/templates/database.yml +34 -0
- data/lib/litestack/liteboard/liteboard.rb +305 -0
- data/lib/litestack/liteboard/views/event.erb +32 -0
- data/lib/litestack/liteboard/views/index.erb +54 -0
- data/lib/litestack/liteboard/views/layout.erb +303 -0
- data/lib/litestack/liteboard/views/litecable.erb +118 -0
- data/lib/litestack/liteboard/views/litecache.erb +144 -0
- data/lib/litestack/liteboard/views/litedb.erb +168 -0
- data/lib/litestack/liteboard/views/litejob.erb +151 -0
- data/lib/litestack/liteboard/views/topic.erb +48 -0
- data/lib/litestack/litecable.rb +25 -35
- data/lib/litestack/litecable.sql.yml +1 -1
- data/lib/litestack/litecache.rb +31 -28
- data/lib/litestack/litedb.rb +124 -1
- data/lib/litestack/litejob.rb +2 -2
- data/lib/litestack/litejobqueue.rb +8 -8
- data/lib/litestack/litemetric.rb +177 -88
- data/lib/litestack/litemetric.sql.yml +312 -42
- data/lib/litestack/litemetric_collector.sql.yml +56 -0
- data/lib/litestack/litequeue.rb +28 -29
- data/lib/litestack/litequeue.sql.yml +11 -0
- data/lib/litestack/litesupport.rb +137 -57
- data/lib/litestack/railtie.rb +10 -0
- data/lib/litestack/version.rb +1 -1
- data/lib/litestack.rb +1 -0
- data/lib/sequel/adapters/litedb.rb +1 -1
- data/template.rb +7 -0
- metadata +81 -5
- data/lib/litestack/metrics_app.rb +0 -5
data/lib/litestack/litedb.rb
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
# all components should require the support module
|
2
2
|
require_relative 'litesupport'
|
3
3
|
|
4
|
+
# all measurable components should require the litemetric class
|
5
|
+
require_relative 'litemetric'
|
6
|
+
|
4
7
|
# Litedb inherits from the SQLite3::Database class and adds a few initialization options
|
5
8
|
class Litedb < ::SQLite3::Database
|
6
9
|
|
10
|
+
# add litemetric support
|
11
|
+
include Litemetric::Measurable
|
12
|
+
|
7
13
|
# overrride the original initilaizer to allow for connection configuration
|
8
14
|
def initialize(file, options = {}, zfs = nil )
|
9
15
|
if block_given?
|
@@ -15,6 +21,13 @@ class Litedb < ::SQLite3::Database
|
|
15
21
|
super(file, options, zfs)
|
16
22
|
init unless options[:noinit] == true
|
17
23
|
end
|
24
|
+
@running = true
|
25
|
+
@collecting_metrics = options[:metrics]
|
26
|
+
collect_metrics if @collecting_metrics
|
27
|
+
end
|
28
|
+
|
29
|
+
def collecting_metrics?
|
30
|
+
@collecting_metrics
|
18
31
|
end
|
19
32
|
|
20
33
|
# enforce immediate mode to avoid deadlocks for a small performance penalty
|
@@ -22,6 +35,67 @@ class Litedb < ::SQLite3::Database
|
|
22
35
|
super(mode)
|
23
36
|
end
|
24
37
|
|
38
|
+
# return the size of the database file
|
39
|
+
def size
|
40
|
+
execute("SELECT s.page_size * c.page_count FROM pragma_page_size() AS s, pragma_page_count() AS c")[0][0]
|
41
|
+
end
|
42
|
+
|
43
|
+
def schema_object_count(type = nil)
|
44
|
+
execute("SELECT count(*) FROM SQLITE_MASTER WHERE iif(?1 IS NOT NULL, type = ?1, TRUE)", type)[0][0]
|
45
|
+
end
|
46
|
+
|
47
|
+
# collect snapshot information
|
48
|
+
def snapshot
|
49
|
+
{
|
50
|
+
summary: {
|
51
|
+
path: filename,
|
52
|
+
journal_mode: journal_mode,
|
53
|
+
synchronous: synchronous,
|
54
|
+
size: size.to_f / (1024 * 1024),
|
55
|
+
tables: schema_object_count('table'),
|
56
|
+
indexes: schema_object_count('index')
|
57
|
+
}
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
# override prepare to return Litedb::Statement (and pass the sql to it)
|
62
|
+
def prepare(sql)
|
63
|
+
stmt = Litedb::Statement.new(self, sql)
|
64
|
+
stmt.sql = sql.strip.upcase
|
65
|
+
return stmt unless block_given?
|
66
|
+
begin
|
67
|
+
yield stmt
|
68
|
+
ensure
|
69
|
+
stmt.close unless stmt.closed?
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# override execute to capture metrics
|
74
|
+
def execute(sql, bind_vars = [], *args, &block)
|
75
|
+
if bind_vars.nil? || !args.empty?
|
76
|
+
if args.empty?
|
77
|
+
bind_vars = []
|
78
|
+
else
|
79
|
+
bind_vars = [bind_vars] + args
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
prepare(sql) do |stmt|
|
84
|
+
measure(stmt.stmt_type, stmt.sql) do
|
85
|
+
stmt.bind_params(bind_vars)
|
86
|
+
stmt = SQLite3::ResultSet.new self, stmt
|
87
|
+
end
|
88
|
+
if block_given?
|
89
|
+
stmt.each do |row|
|
90
|
+
yield row
|
91
|
+
end
|
92
|
+
else
|
93
|
+
stmt.to_a
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
25
99
|
private
|
26
100
|
|
27
101
|
# default connection configuration values
|
@@ -41,7 +115,56 @@ class Litedb < ::SQLite3::Database
|
|
41
115
|
# increase the local connection cache to 2000 pages
|
42
116
|
self.cache_size = 2000
|
43
117
|
end
|
44
|
-
|
118
|
+
|
45
119
|
end
|
46
120
|
|
121
|
+
# the Litedb::Statement also inherits from SQLite3::Statement
|
122
|
+
class Litedb::Statement < SQLite3::Statement
|
123
|
+
|
124
|
+
include Litemetric::Measurable
|
125
|
+
|
126
|
+
attr_accessor :sql
|
127
|
+
|
128
|
+
def initialize(db, sql)
|
129
|
+
super(db, sql)
|
130
|
+
collect_metrics if db.collecting_metrics?
|
131
|
+
end
|
132
|
+
|
133
|
+
def metrics_identifier
|
134
|
+
"Litedb" # overridden to match the parent class
|
135
|
+
end
|
136
|
+
|
137
|
+
# return the type of the statement
|
138
|
+
def stmt_type
|
139
|
+
@stmt_type ||= detect_stmt_type
|
140
|
+
end
|
141
|
+
|
142
|
+
def detect_stmt_type
|
143
|
+
if @sql.start_with?("SEL") || @sql.start_with?("WITH")
|
144
|
+
"Read"
|
145
|
+
elsif @sql.start_with?("CRE") || @sql.start_with?("ALT") || @sql.start_with?("DRO")
|
146
|
+
"Schema change"
|
147
|
+
elsif @sql.start_with?("PRA")
|
148
|
+
"Pragma"
|
149
|
+
else
|
150
|
+
"Write"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# overriding each to measure the query time (plus the processing time as well, sadly)
|
155
|
+
def each
|
156
|
+
measure(stmt_type, @sql) do
|
157
|
+
super
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# overriding execute to measure the query time
|
162
|
+
def execute(*bind_vars)
|
163
|
+
res = nil
|
164
|
+
measure(stmt_type, @sql) do
|
165
|
+
res = super(*bind_vars)
|
166
|
+
end
|
167
|
+
res
|
168
|
+
end
|
47
169
|
|
170
|
+
end
|
data/lib/litestack/litejob.rb
CHANGED
@@ -19,7 +19,7 @@ class Litejobqueue < Litequeue
|
|
19
19
|
# can be overriden by passing new options in a hash
|
20
20
|
# to Litejobqueue.new, it will also be then passed to the underlying Litequeue object
|
21
21
|
# config_path: "./litejob.yml" -> were to find the configuration file (if any)
|
22
|
-
# path: "./queue.db"
|
22
|
+
# path: "./db/queue.db"
|
23
23
|
# mmap_size: 128 * 1024 * 1024 -> 128MB to be held in memory
|
24
24
|
# sync: 1 -> sync only when checkpointing
|
25
25
|
# queues: [["default", 1, "spawn"]] -> an array of queues to process
|
@@ -32,7 +32,7 @@ class Litejobqueue < Litequeue
|
|
32
32
|
# This can be particularly useful for long running, IO bound jobs. It is not recommended though for threaded environments, as it can result in creating many threads that may consudme a lot of memory.
|
33
33
|
DEFAULT_OPTIONS = {
|
34
34
|
config_path: "./litejob.yml",
|
35
|
-
path: "
|
35
|
+
path: Litesupport.root.join("queue.sqlite3"),
|
36
36
|
queues: [["default", 1]],
|
37
37
|
workers: 5,
|
38
38
|
retries: 5,
|
@@ -79,6 +79,11 @@ class Litejobqueue < Litequeue
|
|
79
79
|
pgroups[q[1]] << [q[0], q[2] == "spawn"]
|
80
80
|
end
|
81
81
|
@queues = pgroups.keys.sort.reverse.collect{|p| [p, pgroups[p]]}
|
82
|
+
collect_metrics if @options[:metrics]
|
83
|
+
end
|
84
|
+
|
85
|
+
def metrics_identifier
|
86
|
+
"Litejob" # overrides default identifier
|
82
87
|
end
|
83
88
|
|
84
89
|
# push a job to the queue
|
@@ -164,12 +169,7 @@ class Litejobqueue < Litequeue
|
|
164
169
|
def job_finished
|
165
170
|
Litesupport.synchronize(@mutex){@jobs_in_flight -= 1}
|
166
171
|
end
|
167
|
-
|
168
|
-
# return a hash encapsulating the info about the current jobqueue
|
169
|
-
def snapshot
|
170
|
-
info
|
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
|
data/lib/litestack/litemetric.rb
CHANGED
@@ -13,11 +13,12 @@ class Litemetric
|
|
13
13
|
|
14
14
|
DEFAULT_OPTIONS = {
|
15
15
|
config_path: "./litemetric.yml",
|
16
|
-
path: "
|
16
|
+
path: Litesupport.root.join("metrics.sqlite3"),
|
17
17
|
sync: 1,
|
18
|
-
mmap_size:
|
19
|
-
flush_interval:
|
20
|
-
summarize_interval:
|
18
|
+
mmap_size: 128 * 1024 * 1024, # 16MB of memory to easily process 1 year worth of data
|
19
|
+
flush_interval: 10, # flush data every 10 seconds
|
20
|
+
summarize_interval: 30, # summarize data every 1/2 minute
|
21
|
+
snapshot_interval: 10*60 # snapshot every 10 minutes
|
21
22
|
}
|
22
23
|
|
23
24
|
RESOLUTIONS = {
|
@@ -27,8 +28,20 @@ class Litemetric
|
|
27
28
|
week: 7*24*3600 # 1 week (lowest resolution)
|
28
29
|
}
|
29
30
|
|
31
|
+
# :nodoc:
|
32
|
+
def self.options=(options)
|
33
|
+
# an ugly hack to pass options to a singleton
|
34
|
+
# need to rethink the whole singleton thing
|
35
|
+
@options = options
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.options
|
39
|
+
@options
|
40
|
+
end
|
41
|
+
|
30
42
|
# :nodoc:
|
31
43
|
def initialize(options = {})
|
44
|
+
options = options.merge(Litemetric.options) if Litemetric.options
|
32
45
|
init(options)
|
33
46
|
end
|
34
47
|
|
@@ -41,36 +54,18 @@ class Litemetric
|
|
41
54
|
|
42
55
|
## event capturing
|
43
56
|
##################
|
44
|
-
|
45
|
-
def capture(topic, event, key=event, value=nil)
|
46
|
-
if key.is_a? Array
|
47
|
-
key.each{|k| capture_single_key(topic, event, k, value)}
|
48
|
-
else
|
49
|
-
capture_single_key(topic, event, key, value)
|
50
|
-
end
|
51
|
-
end
|
52
57
|
|
53
|
-
def
|
54
|
-
|
55
|
-
time_slot = current_time_slot # should that be 5 minutes?
|
56
|
-
topic_slot = @metrics[topic]
|
57
|
-
if event_slot = topic_slot[event]
|
58
|
-
if key_slot = event_slot[key]
|
59
|
-
if key_slot[time_slot]
|
60
|
-
key_slot[time_slot][:count] += 1
|
61
|
-
key_slot[time_slot][:value] += value unless value.nil?
|
62
|
-
else # new time slot
|
63
|
-
key_slot[time_slot] = {count: 1, value: value}
|
64
|
-
end
|
65
|
-
else
|
66
|
-
event_slot[key] = {time_slot => {count: 1, value: value}}
|
67
|
-
end
|
68
|
-
else # new event
|
69
|
-
topic_slot[event] = {key => {time_slot => {count: 1, value: value}}}
|
70
|
-
end
|
71
|
-
end
|
58
|
+
def current_time_slot
|
59
|
+
(Time.now.to_i / 300) * 300
|
72
60
|
end
|
73
61
|
|
62
|
+
def capture(topic, event, key=event, value=nil)
|
63
|
+
@collector.capture(topic, event, key, value, current_time_slot)
|
64
|
+
end
|
65
|
+
|
66
|
+
def capture_snapshot(topic, state)
|
67
|
+
run_stmt(:capture_state, topic, Oj.dump(state))
|
68
|
+
end
|
74
69
|
|
75
70
|
## event reporting
|
76
71
|
##################
|
@@ -78,17 +73,48 @@ class Litemetric
|
|
78
73
|
def topics
|
79
74
|
run_stmt(:list_topics).to_a
|
80
75
|
end
|
81
|
-
|
82
|
-
def
|
83
|
-
|
76
|
+
|
77
|
+
def topic_summaries(resolution, count, order, dir, search)
|
78
|
+
search = "%#{search}%" if search
|
79
|
+
if dir.downcase == "desc"
|
80
|
+
run_stmt(:topics_summaries, resolution, count, order, search).to_a
|
81
|
+
else
|
82
|
+
run_stmt(:topics_summaries_asc, resolution, count, order, search).to_a
|
83
|
+
end
|
84
84
|
end
|
85
|
-
|
86
|
-
def
|
87
|
-
|
85
|
+
|
86
|
+
def events_summaries(topic, resolution, order, dir, search, count)
|
87
|
+
search = "%#{search}%" if search
|
88
|
+
if dir.downcase == "desc"
|
89
|
+
run_stmt_hash(:events_summaries, topic, resolution, order, search, count)
|
90
|
+
else
|
91
|
+
run_stmt_hash(:events_summaries_asc, topic, resolution, order, search, count)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def keys_summaries(topic, event, resolution, order, dir, search, count)
|
96
|
+
search = "%#{search}%" if search
|
97
|
+
if dir.downcase == "desc"
|
98
|
+
run_stmt_hash(:keys_summaries, topic, event, resolution, order, search, count).to_a
|
99
|
+
else
|
100
|
+
run_stmt_hash(:keys_summaries_asc, topic, event, resolution, order, search, count).to_a
|
101
|
+
end
|
88
102
|
end
|
89
103
|
|
90
|
-
def
|
91
|
-
run_stmt(:
|
104
|
+
def topic_data_points(step, count, resolution, topic)
|
105
|
+
run_stmt(:topic_data_points, step, count, resolution, topic).to_a
|
106
|
+
end
|
107
|
+
|
108
|
+
def event_data_points(step, count, resolution, topic, event)
|
109
|
+
run_stmt_hash(:event_data_points, step, count, resolution, topic, event).to_a
|
110
|
+
end
|
111
|
+
|
112
|
+
def key_data_points(step, count, resolution, topic, event, key)
|
113
|
+
run_stmt_hash(:key_data_points, step, count, resolution, topic, event, key).to_a
|
114
|
+
end
|
115
|
+
|
116
|
+
def snapshot(topic)
|
117
|
+
run_stmt(:snapshot, topic)[0].to_a
|
92
118
|
end
|
93
119
|
|
94
120
|
## summarize data
|
@@ -107,9 +133,23 @@ class Litemetric
|
|
107
133
|
###################
|
108
134
|
|
109
135
|
private
|
110
|
-
|
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
|
+
|
111
151
|
def exit_callback
|
112
|
-
puts "--- Litemetric detected an exit, flushing metrics"
|
152
|
+
STDERR.puts "--- Litemetric detected an exit, flushing metrics"
|
113
153
|
@running = false
|
114
154
|
flush
|
115
155
|
end
|
@@ -118,9 +158,10 @@ class Litemetric
|
|
118
158
|
super
|
119
159
|
@metrics = {}
|
120
160
|
@registered = {}
|
121
|
-
@flusher = create_flusher
|
122
|
-
@summarizer = create_summarizer
|
123
161
|
@mutex = Litesupport::Mutex.new
|
162
|
+
@collector = Litemetric::Collector.new({dbpath: @options[:path]})
|
163
|
+
@summarizer = create_summarizer
|
164
|
+
@flusher = create_flusher
|
124
165
|
end
|
125
166
|
|
126
167
|
def current_time_slot
|
@@ -128,53 +169,20 @@ class Litemetric
|
|
128
169
|
end
|
129
170
|
|
130
171
|
def flush
|
131
|
-
|
132
|
-
|
133
|
-
conn.transaction(:immediate) do
|
134
|
-
@metrics.each_pair do |topic, event_hash|
|
135
|
-
event_hash.each_pair do |event, key_hash|
|
136
|
-
key_hash.each_pair do |key, time_hash|
|
137
|
-
time_hash.each_pair do |time, data|
|
138
|
-
conn.stmts[:capture_event].execute!(topic, event.to_s, key, time, data[:count], data[:value]) if data
|
139
|
-
time_hash[time] = nil
|
140
|
-
to_delete << [topic, event, key, time]
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
to_delete.each do |r|
|
148
|
-
@metrics[r[0]][r[1]][r[2]].delete(r[3])
|
149
|
-
@metrics[r[0]][r[1]].delete(r[2]) if @metrics[r[0]][r[1]][r[2]].empty?
|
150
|
-
@metrics[r[0]].delete(r[1]) if @metrics[r[0]][r[1]].empty?
|
151
|
-
end
|
152
|
-
end
|
172
|
+
@collector.flush
|
173
|
+
end
|
153
174
|
|
154
175
|
def create_connection
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
version = conn.get_first_value("PRAGMA user_version")
|
159
|
-
sql["schema"].each_pair do |v, obj|
|
160
|
-
if v > version
|
161
|
-
conn.transaction do
|
162
|
-
obj.each{|k, s| conn.execute(s)}
|
163
|
-
conn.user_version = v
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
|
-
sql["stmts"].each { |k, v| conn.stmts[k.to_sym] = conn.prepare(v) }
|
168
|
-
conn
|
176
|
+
super("#{__dir__}/litemetric.sql.yml") do |conn|
|
177
|
+
conn.wal_autocheckpoint = 10000 # checkpoint after 10000 pages are written
|
178
|
+
end
|
169
179
|
end
|
170
180
|
|
171
181
|
def create_flusher
|
172
182
|
Litesupport.spawn do
|
173
183
|
while @running do
|
174
184
|
sleep @options[:flush_interval]
|
175
|
-
|
176
|
-
flush
|
177
|
-
end
|
185
|
+
flush
|
178
186
|
end
|
179
187
|
end
|
180
188
|
end
|
@@ -199,6 +207,16 @@ class Litemetric
|
|
199
207
|
def collect_metrics
|
200
208
|
@litemetric = Litemetric.instance
|
201
209
|
@litemetric.register(metrics_identifier)
|
210
|
+
@snapshotter = create_snapshotter
|
211
|
+
end
|
212
|
+
|
213
|
+
def create_snapshotter
|
214
|
+
Litesupport.spawn do
|
215
|
+
while @running do
|
216
|
+
sleep @litemetric.options[:snapshot_interval]
|
217
|
+
capture_snapshot
|
218
|
+
end
|
219
|
+
end
|
202
220
|
end
|
203
221
|
|
204
222
|
def metrics_identifier
|
@@ -211,18 +229,89 @@ class Litemetric
|
|
211
229
|
end
|
212
230
|
|
213
231
|
def measure(event, key=event)
|
214
|
-
|
232
|
+
unless @litemetric
|
233
|
+
yield
|
234
|
+
return 0
|
235
|
+
end
|
215
236
|
t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
216
|
-
|
237
|
+
yield
|
217
238
|
t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
218
|
-
value =
|
239
|
+
value = t2 - t1
|
219
240
|
capture(event, key, value)
|
220
|
-
|
241
|
+
value # return value so other events can reuse it
|
221
242
|
end
|
222
243
|
|
223
|
-
def
|
224
|
-
|
244
|
+
def capture_snapshot
|
245
|
+
return unless @litemetric
|
246
|
+
state = snapshot if defined? snapshot
|
247
|
+
if state
|
248
|
+
@litemetric.capture_snapshot(metrics_identifier, state)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
class Litemetric
|
256
|
+
|
257
|
+
class Collector
|
258
|
+
|
259
|
+
include Litesupport::Liteconnection
|
260
|
+
|
261
|
+
DEFAULT_OPTIONS = {
|
262
|
+
path: ":memory:",
|
263
|
+
sync: 1,
|
264
|
+
flush_interval: 3, # flush data every 1 minute
|
265
|
+
summarize_interval: 10, # summarize data every 1 minute
|
266
|
+
snapshot_interval: 1 # snapshot every 10 minutes
|
267
|
+
}
|
268
|
+
|
269
|
+
RESOLUTIONS = {
|
270
|
+
minute: 300, # 5 minutes (highest resolution)
|
271
|
+
hour: 3600, # 1 hour
|
272
|
+
day: 24*3600, # 1 day
|
273
|
+
week: 7*24*3600 # 1 week (lowest resolution)
|
274
|
+
}
|
275
|
+
|
276
|
+
def initialize(options = {})
|
277
|
+
init(options)
|
278
|
+
end
|
279
|
+
|
280
|
+
def capture(topic, event, key, value=nil, time=nil)
|
281
|
+
if key.is_a? Array
|
282
|
+
key.each{|k| capture_single_key(topic, event, k, value, time)}
|
283
|
+
else
|
284
|
+
capture_single_key(topic, event, key, value, time)
|
285
|
+
end
|
286
|
+
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 ,1, value)
|
290
|
+
end
|
291
|
+
|
292
|
+
def flush
|
293
|
+
t = Time.now
|
294
|
+
limit = 1000 # migrate 1000 records at a time
|
295
|
+
count = run_stmt(:event_count)[0][0]
|
296
|
+
while count > 0
|
297
|
+
@conn.acquire do |conn|
|
298
|
+
conn.transaction(:immediate) do
|
299
|
+
conn.stmts[:migrate_events].execute!(limit)
|
300
|
+
conn.stmts[:delete_migrated_events].execute!(limit)
|
301
|
+
count = conn.stmts[:event_count].execute![0][0]
|
302
|
+
end
|
303
|
+
end
|
304
|
+
sleep 0.005 #give other threads a chance to run
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
def create_connection
|
309
|
+
super("#{__dir__}/litemetric_collector.sql.yml") do |conn|
|
310
|
+
conn.execute("ATTACH ? as m", @options[:dbpath].to_s)
|
311
|
+
conn.wal_autocheckpoint = 10000
|
312
|
+
end
|
225
313
|
end
|
226
314
|
|
227
315
|
end
|
316
|
+
|
228
317
|
end
|