litestack 0.2.6 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/BENCHMARKS.md +11 -0
- data/CHANGELOG.md +19 -0
- data/Gemfile +2 -0
- data/README.md +1 -1
- 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/bench/uljob.rb +1 -1
- data/lib/action_cable/subscription_adapter/litecable.rb +1 -11
- data/lib/active_support/cache/litecache.rb +1 -1
- data/lib/generators/litestack/install/templates/database.yml +5 -1
- data/lib/litestack/liteboard/liteboard.rb +172 -35
- data/lib/litestack/liteboard/views/index.erb +52 -20
- data/lib/litestack/liteboard/views/layout.erb +189 -38
- 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/litecable.rb +27 -37
- data/lib/litestack/litecable.sql.yml +1 -1
- data/lib/litestack/litecache.rb +7 -18
- data/lib/litestack/litedb.rb +17 -2
- data/lib/litestack/litejob.rb +2 -3
- data/lib/litestack/litejobqueue.rb +51 -48
- data/lib/litestack/litemetric.rb +46 -69
- data/lib/litestack/litemetric.sql.yml +14 -12
- data/lib/litestack/litemetric_collector.sql.yml +4 -4
- data/lib/litestack/litequeue.rb +9 -20
- data/lib/litestack/litescheduler.rb +84 -0
- data/lib/litestack/litesearch/index.rb +230 -0
- data/lib/litestack/litesearch/model.rb +178 -0
- data/lib/litestack/litesearch/schema.rb +193 -0
- data/lib/litestack/litesearch/schema_adapters/backed_adapter.rb +147 -0
- data/lib/litestack/litesearch/schema_adapters/basic_adapter.rb +128 -0
- data/lib/litestack/litesearch/schema_adapters/contentless_adapter.rb +17 -0
- data/lib/litestack/litesearch/schema_adapters/standalone_adapter.rb +33 -0
- data/lib/litestack/litesearch/schema_adapters.rb +9 -0
- data/lib/litestack/litesearch.rb +37 -0
- data/lib/litestack/litesupport.rb +55 -125
- data/lib/litestack/version.rb +1 -1
- data/lib/litestack.rb +2 -1
- data/lib/sequel/adapters/litedb.rb +3 -2
- metadata +20 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 11ef03890b2883bd21fb443d959774f9932f56e927d2ee2a40a028713526ce9c
|
4
|
+
data.tar.gz: c531925eeffa84973475c14d4d230d8919d5e5ec095e5055f9bab6b0f94519e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82c0878fb57fa89290c6550ddbf5b393e436883252285dd78083ba7e96f78947aa3fdf3e92d96f3baec6b9b2b0d2edfdc470f4e1c544427f3da8db704179de27
|
7
|
+
data.tar.gz: 358bc249c3f1371714e12f4df0a0d82883e42eec14528f4faaef8a5e611e62af1039d8c00ebdc9d89be1a514a3bfd698ce3f988fa1b4f29eb1cf72fd1ca03b25
|
data/BENCHMARKS.md
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
This is a set of initial (simple) benchmars, designed to understand the baseline performance for different litestack components against their counterparts.
|
4
4
|
These are not real life scenarios and I hope I will be able to produce some interesting ones soon.
|
5
5
|
|
6
|
+
All these benchmarks were run on an 8 core, 16 thread, AMD 5700U based laptop, in a Virtual Box VM
|
7
|
+
|
6
8
|
> ![litedb](https://github.com/oldmoe/litestack/blob/master/assets/litedb_logo_teal.png?raw=true)
|
7
9
|
|
8
10
|
### Point Read
|
@@ -109,5 +111,14 @@ Two scenarios were benchmarked, an empty job and one with a 100ms sleep to simul
|
|
109
111
|
|
110
112
|
Running Litejob with fibers is producing much faster results than any threaded solution. Still though, threaded Litejob remains ahead of Sidekiq in all scenarios.
|
111
113
|
|
114
|
+
> ![litecable](https://github.com/oldmoe/litestack/blob/master/assets/litecable_logo_teal.png?raw=true)
|
115
|
+
|
116
|
+
A client written using the Iodine web server was used to generate the WS load in an event driven fashion. The Rails application, the Iodine based load generator and the Redis server were all run on the same machine to exclude network overheads (Redis still pays for the TCP stack overhead though)
|
112
117
|
|
118
|
+
|Requests|Redis Req/Sec|Litestack Req/sec|Redis p90 Latency (ms)|Litestack p90 Latency (ms)|Redis p99 Latency (ms)|Litestack p99 Latancy (ms)|
|
119
|
+
|-:|-:|-:|-:|-:|-:|-:|
|
120
|
+
|1,000|2611|3058|34|27|153|78|
|
121
|
+
|10,000|3110|5328|81|40|138|122
|
122
|
+
|100,000|3403|5385|41|36|153|235
|
113
123
|
|
124
|
+
On average, Litecable is quite faster than the Redis based version and offers better latenices for over 90% of the requests, though Redis usually delivers better p99 latencies,
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.4.1] - 2023-10-11
|
4
|
+
|
5
|
+
- Add missing Litesearch::Model dependency
|
6
|
+
|
7
|
+
## [0.4.0] - 2023-10-11
|
8
|
+
|
9
|
+
- Introduced Litesearch, dynamic & fast full text search capability for Litedb
|
10
|
+
- ActiveRecord and Sequel integration for Litesearch
|
11
|
+
- Slight improvement to the Sequel Litedb adapter for better Litesearch integration
|
12
|
+
|
13
|
+
## [0.3.0] - 2023-08-13
|
14
|
+
|
15
|
+
- Reworked the Litecable thread safety model
|
16
|
+
- Fixed multiple litejob bugs (thanks Stephen Margheim)
|
17
|
+
- Fixed Railtie dependency (thanks Marco Roth)
|
18
|
+
- Litesupport fixes (thanks Stephen Margheim)
|
19
|
+
- Much improved metrics reporting for Litedb, Litecache, Litejob & Litecable
|
20
|
+
- Removed (for now, will come again later) litemetric reporting support for ad-hoc modules
|
21
|
+
|
3
22
|
## [0.2.6] - 2023-07-16
|
4
23
|
|
5
24
|
- Much improved database location setting (thanks Brad Gessler)
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -183,7 +183,7 @@ production:
|
|
183
183
|
|
184
184
|
## Contributing
|
185
185
|
|
186
|
-
Bug reports are welcome on GitHub at https://github.com/oldmoe/litestack.
|
186
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/oldmoe/litestack.
|
187
187
|
|
188
188
|
## License
|
189
189
|
|
Binary file
|
Binary file
|
Binary file
|
data/bench/bench_jobs_rails.rb
CHANGED
data/bench/bench_jobs_raw.rb
CHANGED
@@ -28,7 +28,7 @@ end
|
|
28
28
|
|
29
29
|
require './uljob.rb'
|
30
30
|
|
31
|
-
STDERR.puts "litejob started in #{
|
31
|
+
STDERR.puts "litejob started in #{Litescheduler.backend} environmnet"
|
32
32
|
|
33
33
|
t = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
34
34
|
bench("enqueuing litejobs", count) do |i|
|
data/bench/uljob.rb
CHANGED
@@ -10,20 +10,10 @@ module ActionCable
|
|
10
10
|
|
11
11
|
prepend ChannelPrefix
|
12
12
|
|
13
|
-
DEFAULT_OPTIONS = {
|
14
|
-
config_path: "./config/litecable.yml",
|
15
|
-
path: "./db/cable.db",
|
16
|
-
sync: 0, # no need to sync at all
|
17
|
-
mmap_size: 16 * 1024 * 1024, # 16MB of memory hold hot messages
|
18
|
-
expire_after: 10, # remove messages older than 10 seconds
|
19
|
-
listen_interval: 0.005, # check new messages every 5 milliseconds
|
20
|
-
metrics: false
|
21
|
-
}
|
22
|
-
|
23
13
|
def initialize(server, logger=nil)
|
24
14
|
@server = server
|
25
15
|
@logger = server.logger
|
26
|
-
super(
|
16
|
+
super({config_path: "./config/litecable.yml"})
|
27
17
|
end
|
28
18
|
|
29
19
|
def shutdown
|
@@ -6,10 +6,14 @@
|
|
6
6
|
#
|
7
7
|
# `Litesupport.root.join("data.sqlite3")` stores
|
8
8
|
# application data in the path `./db/#{Rails.env}/data.sqlite3`
|
9
|
+
#
|
10
|
+
# idle_timeout should be set to zero, to avoid recycling sqlite connections
|
11
|
+
# and losing the page cache
|
12
|
+
#
|
9
13
|
default: &default
|
10
14
|
adapter: litedb
|
11
15
|
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
12
|
-
|
16
|
+
idle_timeout: 0
|
13
17
|
database: <%= Litesupport.root.join("data.sqlite3") %>
|
14
18
|
|
15
19
|
development:
|
@@ -16,13 +16,21 @@ class Liteboard
|
|
16
16
|
get "/", to: ->(env) do
|
17
17
|
Liteboard.new(env).call(:index)
|
18
18
|
end
|
19
|
-
|
20
|
-
get "/topics
|
21
|
-
Liteboard.new(env).call(:
|
19
|
+
|
20
|
+
get "/topics/Litejob", to: ->(env) do
|
21
|
+
Liteboard.new(env).call(:litejob)
|
22
|
+
end
|
23
|
+
|
24
|
+
get "/topics/Litecache", to: ->(env) do
|
25
|
+
Liteboard.new(env).call(:litecache)
|
22
26
|
end
|
23
27
|
|
24
|
-
get "/topics
|
25
|
-
Liteboard.new(env).call(:
|
28
|
+
get "/topics/Litedb", to: ->(env) do
|
29
|
+
Liteboard.new(env).call(:litedb)
|
30
|
+
end
|
31
|
+
|
32
|
+
get "/topics/Litecable", to: ->(env) do
|
33
|
+
Liteboard.new(env).call(:litecable)
|
26
34
|
end
|
27
35
|
|
28
36
|
end
|
@@ -45,12 +53,6 @@ class Liteboard
|
|
45
53
|
after(res)
|
46
54
|
end
|
47
55
|
|
48
|
-
def render(tpl_name)
|
49
|
-
layout = Tilt.new("#{__dir__}/views/layout.erb")
|
50
|
-
tpl = Tilt.new("#{__dir__}/views/#{tpl_name.to_s}.erb")
|
51
|
-
res = layout.render(self){tpl.render(self)}
|
52
|
-
end
|
53
|
-
|
54
56
|
def after(body=nil)
|
55
57
|
[200, {'Cache-Control' => 'no-cache'}, [body]]
|
56
58
|
end
|
@@ -76,48 +78,150 @@ class Liteboard
|
|
76
78
|
end
|
77
79
|
@search = params(:search)
|
78
80
|
@search = nil if @search == ''
|
81
|
+
@topics = @lm.topic_summaries(@resolution, @step * @count, @order, @dir, @search)
|
79
82
|
end
|
80
83
|
|
81
84
|
def index
|
82
85
|
@order = 'topic' unless @order
|
83
|
-
topics = @lm.topics
|
84
|
-
@topics = @lm.topic_summaries(@resolution, @step * @count, @order, @dir, @search)
|
85
86
|
@topics.each do |topic|
|
86
87
|
data_points = @lm.topic_data_points(@step, @count, @resolution, topic[0])
|
87
|
-
topic << data_points.collect{|r| [r[0],r[2]]}
|
88
|
+
topic << data_points.collect{|r| [r[0],r[2] || 0]}
|
88
89
|
end
|
89
90
|
render :index
|
90
91
|
end
|
91
92
|
|
92
|
-
def
|
93
|
+
def litecache
|
93
94
|
@order = 'rcount' unless @order
|
94
|
-
@topic =
|
95
|
+
@topic = 'Litecache'
|
95
96
|
@events = @lm.events_summaries(@topic, @resolution, @order, @dir, @search, @step * @count)
|
96
97
|
@events.each do |event|
|
97
|
-
data_points = @lm.event_data_points(@step, @count, @resolution, @topic, event[
|
98
|
-
event
|
99
|
-
event
|
98
|
+
data_points = @lm.event_data_points(@step, @count, @resolution, @topic, event['name'])
|
99
|
+
event['counts'] = data_points.collect{|r| [r['rtime'],r['rcount']]}
|
100
|
+
event['values'] = data_points.collect{|r| [r['rtime'],r['ravg']]}
|
100
101
|
end
|
101
|
-
@snapshot =
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
102
|
+
@snapshot = read_snapshot(@topic)
|
103
|
+
@size = @snapshot[0][:summary][:size] rescue 0
|
104
|
+
@max_size = @snapshot[0][:summary][:max_size] rescue 0
|
105
|
+
@full = (@size / @max_size)*100 rescue 0
|
106
|
+
@entries = @snapshot[0][:summary][:entries] rescue 0
|
107
|
+
@gets = @events.find{|t| t['name'] == 'get'}
|
108
|
+
@sets = @events.find{|t| t['name'] == 'set'}
|
109
|
+
@reads = @gets['rcount'] rescue 0
|
110
|
+
@writes = @sets['rcount'] rescue 0
|
111
|
+
@hitrate = @gets['ravg'] rescue 0
|
112
|
+
@hits = @reads * @hitrate
|
113
|
+
@misses = @reads - @hits
|
114
|
+
@reads_vs_writes = @gets['counts'].collect.with_index{|obj, i| obj.clone << @sets['counts'][i][1] } rescue []
|
115
|
+
@hits_vs_misses = @gets['values'].collect.with_index{|obj, i| [obj[0], obj[1].to_f * @gets['counts'][i][1].to_f, (1 - obj[1].to_f) * @gets['counts'][i][1].to_f] } rescue []
|
116
|
+
@top_reads = @lm.keys_summaries(@topic, 'get', @resolution, @order, @dir, nil, @step * @count).first(8)
|
117
|
+
@top_writes = @lm.keys_summaries(@topic, 'set', @resolution, @order, @dir, nil, @step * @count).first(8)
|
118
|
+
render :litecache
|
119
|
+
end
|
120
|
+
|
121
|
+
def litedb
|
122
|
+
@order = 'rcount' unless @order
|
123
|
+
@topic = 'Litedb'
|
124
|
+
@events = @lm.events_summaries(@topic, @resolution, @order, @dir, @search, @step * @count)
|
125
|
+
@events.each do |event|
|
126
|
+
data_points = @lm.event_data_points(@step, @count, @resolution, @topic, event['name'])
|
127
|
+
event['counts'] = data_points.collect{|r| [r['rtime'],r['rcount'] || 0]}
|
128
|
+
event['values'] = data_points.collect{|r| [r['rtime'],r['rtotal'] || 0]}
|
106
129
|
end
|
107
|
-
|
130
|
+
@snapshot = read_snapshot(@topic)
|
131
|
+
@size = @snapshot[0][:summary][:size] rescue 0
|
132
|
+
@tables = @snapshot[0][:summary][:tables] rescue 0
|
133
|
+
@indexes = @snapshot[0][:summary][:indexes] rescue 0
|
134
|
+
@gets = @events.find{|t| t['name'] == 'Read'}
|
135
|
+
@sets = @events.find{|t| t['name'] == 'Write'}
|
136
|
+
@reads = @gets['rcount'] rescue 0
|
137
|
+
@writes = @sets['rcount'] rescue 0
|
138
|
+
@time = @gets['ravg'] rescue 0
|
139
|
+
@reads_vs_writes = @gets['counts'].collect.with_index{|obj, i| obj.clone << @sets['counts'][i][1] } rescue []
|
140
|
+
@reads_vs_writes_times = @gets['values'].collect.with_index{|obj, i| [obj[0], obj[1], @sets['values'][i][1].to_f] } rescue []
|
141
|
+
@read_times = @gets['rtotal'] rescue 0
|
142
|
+
@write_times = @sets['rtotal'] rescue 0
|
143
|
+
@slowest = @lm.keys_summaries(@topic, 'Read', @resolution, 'ravg', 'desc', nil, @step * @count).first(8)
|
144
|
+
@slowest += @lm.keys_summaries(@topic, 'Write', @resolution, 'ravg', 'desc', nil, @step * @count).first(8)
|
145
|
+
@slowest = @slowest.sort{|a, b| a['ravg'] <=> b['ravg']}.reverse.first(8)
|
146
|
+
@popular = @lm.keys_summaries(@topic, 'Read', @resolution, 'rtotal', 'desc', nil, @step * @count).first(8)
|
147
|
+
@popular += @lm.keys_summaries(@topic, 'Write', @resolution, 'rtotal', 'desc', nil, @step * @count).first(8)
|
148
|
+
@popular = @popular.sort{|a, b| a['rtotal'] <=> b['rtotal']}.reverse.first(8)
|
149
|
+
render :litedb
|
108
150
|
end
|
109
151
|
|
110
|
-
def
|
152
|
+
def litejob
|
111
153
|
@order = 'rcount' unless @order
|
112
|
-
@topic =
|
113
|
-
@
|
114
|
-
@
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
154
|
+
@topic = 'Litejob'
|
155
|
+
@events = @lm.events_summaries(@topic, @resolution, @order, @dir, @search, @step * @count)
|
156
|
+
@events.each do |event|
|
157
|
+
data_points = @lm.event_data_points(@step, @count, @resolution, @topic, event['name'])
|
158
|
+
event['counts'] = data_points.collect{|r| [r['rtime'],r['rcount'] || 0]}
|
159
|
+
event['values'] = data_points.collect{|r| [r['rtime'],r['rtotal'] || 0]}
|
160
|
+
end
|
161
|
+
@snapshot = read_snapshot(@topic)
|
162
|
+
@size = @snapshot[0][:summary][:size] rescue 0
|
163
|
+
@jobs = @snapshot[0][:summary][:jobs] rescue 0
|
164
|
+
@queues = @snapshot[0][:queues] rescue {}
|
165
|
+
@processed_jobs = @events.find{|e|e['name'] == 'perform'}
|
166
|
+
@processed_count = @processed_jobs['rcount'] rescue 0
|
167
|
+
@processing_time = @processed_jobs['rtotal'] rescue 0
|
168
|
+
keys_summaries = @lm.keys_summaries(@topic, 'perform', @resolution, 'rcount', 'desc', nil, @step * @count)
|
169
|
+
@processed_count_by_queue = keys_summaries.collect{|r|[r['key'], r['rcount']]}
|
170
|
+
@processing_time_by_queue = keys_summaries.collect{|r|[r['key'], r['rtotal']]} #.sort{|r1, r2| r1['rtotal'] > r2['rtotal'] }
|
171
|
+
@processed_count_over_time = @events.find{|e| e['name'] == 'perform'}['counts'] rescue []
|
172
|
+
@processing_time_over_time = @events.find{|e| e['name'] == 'perform'}['values'] rescue []
|
173
|
+
@processed_count_over_time_by_queues = []
|
174
|
+
@processing_time_over_time_by_queues = []
|
175
|
+
keys = ['Time']
|
176
|
+
keys_summaries.each_with_index do |summary,i|
|
177
|
+
key = summary['key']
|
178
|
+
keys << key
|
179
|
+
data_points = @lm.key_data_points(@step, @count, @resolution, @topic, 'perform', key)
|
180
|
+
if i == 0
|
181
|
+
data_points.each do |dp|
|
182
|
+
@processed_count_over_time_by_queues << [dp['rtime']]
|
183
|
+
@processing_time_over_time_by_queues << [dp['rtime']]
|
184
|
+
end
|
185
|
+
end
|
186
|
+
data_points.each_with_index do |dp, j|
|
187
|
+
@processed_count_over_time_by_queues[j] << (dp['rcount'] || 0)
|
188
|
+
@processing_time_over_time_by_queues[j] << (dp['rtotal'] || 0)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
@processed_count_over_time_by_queues.unshift(keys)
|
192
|
+
@processing_time_over_time_by_queues.unshift(keys)
|
193
|
+
render :litejob
|
194
|
+
end
|
195
|
+
|
196
|
+
def litecable
|
197
|
+
@order = 'rcount' unless @order
|
198
|
+
@topic = 'Litecable'
|
199
|
+
@events = @lm.events_summaries(@topic, @resolution, @order, @dir, @search, @step * @count)
|
200
|
+
@events.each do |event|
|
201
|
+
data_points = @lm.event_data_points(@step, @count, @resolution, @topic, event['name'])
|
202
|
+
event['counts'] = data_points.collect{|r| [r['rtime'],r['rcount'] || 0]}
|
203
|
+
end
|
204
|
+
|
205
|
+
@subscription_count = @events.find{|t| t['name'] == 'subscribe'}['rcount'] rescue 0
|
206
|
+
@broadcast_count = @events.find{|t| t['name'] == 'broadcast'}['rcount'] rescue 0
|
207
|
+
@message_count = @events.find{|t| t['name'] == 'message'}['rcount'] rescue 0
|
208
|
+
|
209
|
+
@subscriptions_over_time = @events.find{|t| t['name'] == 'subscribe'}['counts'] rescue []
|
210
|
+
@broadcasts_over_time = @events.find{|t| t['name'] == 'broadcast'}['counts'] rescue []
|
211
|
+
@messages_over_time = @events.find{|t| t['name'] == 'message'}['counts'] rescue []
|
212
|
+
@messages_over_time = @messages_over_time.collect.with_index{|msg, i| [msg[0], @broadcasts_over_time[i][1], msg[1]]}
|
213
|
+
|
214
|
+
@top_subscribed_channels = @lm.keys_summaries(@topic, 'subscribe', @resolution, @order, @dir, @search, @step * @count).first(8)
|
215
|
+
@top_messaged_channels = @lm.keys_summaries(@topic, 'message', @resolution, @order, @dir, @search, @step * @count).first(8)
|
216
|
+
render :litecable
|
217
|
+
end
|
218
|
+
|
219
|
+
def index_url
|
220
|
+
"/?res=#{@res}&order=#{@order}&dir=#{@dir}&search=#{@search}"
|
221
|
+
end
|
222
|
+
|
223
|
+
def topic_url(topic)
|
224
|
+
"/topics/#{encode(topic)}?res=#{@res}&order=#{@order}&dir=#{@dir}&search=#{@search}"
|
121
225
|
end
|
122
226
|
|
123
227
|
def index_sort_url(field)
|
@@ -156,10 +260,43 @@ class Liteboard
|
|
156
260
|
URI.encode_uri_component(text)
|
157
261
|
end
|
158
262
|
|
263
|
+
def round(float)
|
264
|
+
return 0 unless float.is_a? Numeric
|
265
|
+
((float * 100).round).to_f / 100
|
266
|
+
end
|
267
|
+
|
268
|
+
def format(float)
|
269
|
+
string = float.to_s
|
270
|
+
whole, decimal = string.split('.')
|
271
|
+
whole = whole.split('').reverse.each_slice(3).map(&:join).join(',').reverse
|
272
|
+
whole = [whole, decimal].join('.') if decimal
|
273
|
+
whole
|
274
|
+
end
|
275
|
+
|
159
276
|
def self.app
|
160
277
|
@@app
|
161
278
|
end
|
162
279
|
|
280
|
+
private
|
281
|
+
|
282
|
+
def read_snapshot(topic)
|
283
|
+
snapshot = @lm.snapshot(topic)
|
284
|
+
if snapshot.empty?
|
285
|
+
snapshot = []
|
286
|
+
else
|
287
|
+
snapshot[0] = Oj.load(snapshot[0]) unless snapshot[0].nil?
|
288
|
+
end
|
289
|
+
snapshot
|
290
|
+
end
|
291
|
+
|
292
|
+
def render(tpl_name)
|
293
|
+
layout = Tilt.new("#{__dir__}/views/layout.erb")
|
294
|
+
tpl_path = "#{__dir__}/views/#{tpl_name.to_s}.erb"
|
295
|
+
tpl = Tilt.new(tpl_path)
|
296
|
+
res = layout.render(self){tpl.render(self)}
|
297
|
+
end
|
298
|
+
|
299
|
+
|
163
300
|
end
|
164
301
|
|
165
302
|
|
@@ -1,22 +1,54 @@
|
|
1
1
|
|
2
|
-
<h5>All topics</h5>
|
3
|
-
<div id="search"><form><input id="search-field" type="text" placeholder="Search topics" onkeydown="search_kd(this)" onkeyup="search_ku(this)" value="<%=@search%>"/></form></div>
|
4
|
-
<table class="sortable table">
|
5
|
-
<tr>
|
6
|
-
<th width="20%" class="<%='sorted' if @order == 'topic'%>"><a href="<%=index_sort_url('topic')%>">Topics</a> <%=dir('topic')%></th>
|
7
|
-
<th width="20%" class="<%='sorted' if @order == 'rcount'%>"><a href="<%=index_sort_url('rcount')%>">Total events</a> <%=dir('rcount')%></th>
|
8
|
-
<th width="40%">Events over time</th>
|
9
|
-
</tr>
|
10
|
-
<% @topics.each do |topic|%>
|
11
|
-
<tr>
|
12
|
-
<td title="<%=topic[0]%>"><div class="label"><a href="./topics/<%=encode(topic[0])%>?res=<%=@res%>"><%=topic[0]%></a></div></td>
|
13
|
-
<td><%=topic[3]%></td>
|
14
|
-
<td class="chart"><span class="inlinecolumn hidden" data-label="Count"><%=Oj.dump(topic[4]) if topic[4]%></span></td>
|
15
|
-
</tr>
|
16
|
-
<%end%>
|
17
|
-
<% if @topics.empty? %>
|
18
|
-
<tr>
|
19
|
-
<td class="empty" colspan="5">No data to display</td>
|
20
|
-
</tr>
|
21
|
-
<% end %>
|
22
2
|
|
3
|
+
|
4
|
+
|
5
|
+
<div class="container">
|
6
|
+
<% @topics.each do |topic|%>
|
7
|
+
|
8
|
+
<div class = "row justify-content-center">
|
9
|
+
|
10
|
+
<div class = "col-6">
|
11
|
+
<div class="card">
|
12
|
+
<div class="card-header">
|
13
|
+
<a href="./topics/<%=encode(topic[0])%>?res=<%=@res%>"><%=topic[0]%></a>
|
14
|
+
</div>
|
15
|
+
<div class="card-body">
|
16
|
+
<div class="container">
|
17
|
+
<div class= "row">
|
18
|
+
<div class= "col">
|
19
|
+
<h1><%=topic[3]%> <span class="fs-4">events</span></h1>
|
20
|
+
</div>
|
21
|
+
<div class= "col">
|
22
|
+
<span class="inlineminicolumn hidden" data-label="Count"><%=Oj.dump(topic[4].unshift(['Time', 'Count'])) if topic[4]%></span>
|
23
|
+
</div>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
</div>
|
31
|
+
|
32
|
+
<div class = "row">
|
33
|
+
<br/>
|
34
|
+
</div>
|
35
|
+
|
36
|
+
|
37
|
+
<%end%>
|
38
|
+
<% if @topics.empty? %>
|
39
|
+
<div class = "row justify-content-center">
|
40
|
+
|
41
|
+
<div class = "col-6">
|
42
|
+
<div class="card">
|
43
|
+
<div class="card-header">
|
44
|
+
Topics
|
45
|
+
</div>
|
46
|
+
<div class="card-body justify-content-center">
|
47
|
+
No data to display
|
48
|
+
</div>
|
49
|
+
</div>
|
50
|
+
</div>
|
51
|
+
|
52
|
+
</div>
|
53
|
+
<%end%>
|
54
|
+
</div>
|