litestack 0.2.6 → 0.4.1

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.
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
@@ -15,17 +15,17 @@
15
15
  h1 { font-family: antonio }
16
16
  #content{ padding-right: 12px; padding-left: 12px; padding-bottom: 60px;}
17
17
  table.head { margin-top: 12px; margin-bottom:12px}
18
- table.head select { color: #078; background-color: #fff; font-weight: normal }
18
+ select { color: #078; background-color: #fff; font-weight: normal }
19
19
  .table th { color: #078; font-weight: normal; }
20
20
  .table th.sorted { font-weight: bold }
21
21
  .table td { color: #444; vertical-align:middle; font-size: 18px}
22
22
  .table td:first-child { color: #444; vertical-align:middle; font-size: 15px; font-weight:normal}
23
23
  .table td.empty { text-align:center}
24
- a { color: #078; }
24
+ a, a.nav-link { color: #078; }
25
+ .nav-pills .nav-link.active { color: #fff; background-color: #078}
25
26
  a .logo { color: #000;}
26
27
  a:visited { color: #078; }
27
- //table.summary { width: 50% }
28
- span.hidden { display: none}
28
+ .hidden { display: none}
29
29
  div#search {margin-bottom: 8px}
30
30
  div#footer {position:fixed; left:0px; height: 40px; width:100%; background-color:#0891; border-top: #0893 1px solid; padding: 8px; bottom: 0; text-align: right}
31
31
  .logo{font-family: antonio}
@@ -41,23 +41,46 @@
41
41
  <div id="header">
42
42
  <h1><span class="logo"><span class="logo-half">lite</span>board | </span> <span class="logo smaller">the <span class="logo-half">lite</span>metric dashboard</span></span></h1>
43
43
  </div>
44
- <table class="head">
45
- <tr>
46
- <td>
47
- Showing data for the last <select onchange="window.location = locationWithParam('res', this.value)">
44
+ <div class="container">
45
+ <div class = "row">
46
+ &nbsp;<br/>
47
+ </div>
48
+ <div class="row">
49
+ <div class="col">
50
+ <nav class="navbar bg-body-tertiary">
51
+ <div>&nbsp;&nbsp;Showing data for the last <select onchange="window.location = locationWithParam('res', this.value)">
48
52
  <%= mapping = {'hour' => '60 minutes', 'day' => '24 hours', 'week' => '7 days', 'year' => '52 weeks'}%>
49
53
  <% ['hour', 'day', 'week', 'year'].each do |res| %>
50
54
  <option value=<%=res%> <%='selected' if res == @res%>><%=mapping[res]%></option>
51
55
  <% end %>
52
- </select>
53
- </td>
54
- <td></td>
55
- </tr>
56
- <tr>
57
- <td></td><td></td>
58
- </tr>
59
- </table>
56
+ </select></div>
57
+ </nav>
58
+ </div>
59
+ </div>
60
+ <div class = "row">
61
+ &nbsp;<br/>
62
+ </div>
60
63
  <%= yield %>
64
+
65
+ </div>
66
+
67
+ <div class="container" style="position: fixed; left:0px; top: 145px">
68
+ <div class="row justify-content-center">
69
+ <div class="col">
70
+ <div class="card" style="width: 15rem;">
71
+ <div class="card-body">
72
+ <ul class="nav nav-pills nav-fill flex-column list-group list-group-flush">
73
+ <li class="list-group-item"><a class="nav-link <%='active' unless @topic%>" href="<%=index_url%>">Home</a></li>
74
+ <%@topics.each do |topic|%>
75
+ <li class="list-group-item"><a class="nav-link <%='active' if @topic == topic[0]%>" href="<%=topic_url(topic[0])%>"><%=topic[0]%></a></li>
76
+ <%end%>
77
+ </ul>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </div>
83
+
61
84
  </div>
62
85
  <div id="footer">
63
86
  Powered by <a href="https://www.github.com/oldmoe/litestack" target="_blank"><span class="logo"><span class="logo-half">lite</span>stack</span></a>
@@ -65,44 +88,170 @@
65
88
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
66
89
  </body>
67
90
  <script>
68
- google.charts.load('current', {'packages':['corechart']});
91
+ google.charts.load('current', {'packages':['corechart', 'bar']});
69
92
 
70
- google.charts.setOnLoadCallback(drawChart);
93
+ google.charts.setOnLoadCallback(drawMiniColumnChart);
94
+ google.charts.setOnLoadCallback(drawColumnChart);
95
+ google.charts.setOnLoadCallback(drawPieChart);
96
+ google.charts.setOnLoadCallback(drawStackedColumnChart);
71
97
 
72
- function drawChart() {
98
+
99
+ function drawMiniColumnChart() {
100
+ elements = document.querySelectorAll(".inlineminicolumn")
101
+ elements.forEach(element => {
102
+ var label = element.dataset.label;
103
+ var mydata = eval(element.innerText)
104
+ element.innerText = ''
105
+ element.classList.remove("hidden")
106
+ if(mydata.length > 1) {
107
+ mydata.forEach(row => {
108
+ if(mydata[0].length == 5){ // we are doing custom tooltips
109
+ if(row[0] != "Time"){
110
+ row[1] = Number(row[1].toPrecision(4))
111
+ row[3] = Number(row[2].toPrecision(4))
112
+ row[2] = row[0]+': '+mydata[0][1]+' '+row[1]
113
+ row[4] = row[0]+': '+mydata[0][3]+' '+row[3]
114
+ }
115
+ }
116
+ })
117
+ var data = google.visualization.arrayToDataTable(
118
+ mydata
119
+ )
120
+ var options = {
121
+ animation: {'startup': true, 'duration': 300},
122
+ width: 300,
123
+ height: 70,
124
+ chartArea: {width:'100%', height: '100%'},
125
+ backgroundColor: 'none',
126
+ bar: {groupWidth: "61.8%"},
127
+ colors : ['#089', 'silver' ],
128
+ vAxis: {'gridlines': {'count' : 0}, 'textPosition' : 'none', 'baselineColor' : 'none'},
129
+ hAxis: { 'count' : 0, 'textPosition' : 'none', 'baselineColor' : 'none'},
130
+ legend: {'position': 'none'},
131
+ tooltip: {showColorCode: true, isHtml: true},
132
+ isStacked: true
133
+ }
134
+ var chart = new google.visualization.ColumnChart(element);
135
+ chart.draw(data, options);
136
+ }
137
+ })
138
+ }
139
+
140
+ function drawColumnChart() {
73
141
  elements = document.querySelectorAll(".inlinecolumn")
74
142
  elements.forEach(element => {
75
- //console.log(element)
76
143
  var label = element.dataset.label;
77
144
  var mydata = eval(element.innerText)
78
- //console.log(mydata)
79
- if(mydata.length > 0) {
80
- var data = new google.visualization.DataTable();
81
-
82
- data.addColumn('string', 'Time');
83
- data.addColumn('number', label);
145
+ element.innerText = ''
146
+ element.classList.remove("hidden")
147
+ if(mydata.length > 1) {
84
148
  mydata.forEach(row => {
85
- row[1] = Number(row[1])
86
- data.addRows([row])
149
+ if(mydata[0].length == 5){ // we are doing custom tooltips
150
+ if(row[0] != "Time"){
151
+ row[1] = Number(row[1].toPrecision(4))
152
+ row[3] = Number(row[2].toPrecision(4))
153
+ row[2] = row[0]+': '+mydata[0][1]+' '+row[1]
154
+ row[4] = row[0]+': '+mydata[0][3]+' '+row[3]
155
+ }
156
+ }
87
157
  })
88
- //data.addRows(mydata)
158
+ var data = google.visualization.arrayToDataTable(
159
+ mydata
160
+ )
89
161
  var options = {
90
162
  animation: {'startup': true, 'duration': 300},
91
- width: 400,
92
- height: 40,
163
+ width: 550,
164
+ height: 350,
165
+ chartArea: {width:'100%', height: '80%'},
93
166
  backgroundColor: 'none',
94
- curveType: 'function',
95
- colors : ['#089'],
96
- vAxis: {'gridlines': {'count' : 0}, 'textPosition' : 'none', 'baselineColor' : 'none', 'minValue' : 0 , 'maxValue' : 50},
167
+ bar: {groupWidth: "61.8%"},
168
+ colors : ['#089', 'silver' ],
169
+ vAxis: {'gridlines': {'count' : 0}, 'textPosition' : 'none', 'baselineColor' : 'none'},
97
170
  hAxis: { 'count' : 0, 'textPosition' : 'none', 'baselineColor' : 'none'},
98
- legend: {'position': 'none'}
171
+ legend: {'position': 'bottom'},
172
+ tooltip: {showColorCode: true, isHtml: true},
173
+ isStacked: true
174
+ }
175
+ var chart = new google.visualization.ColumnChart(element);
176
+ chart.draw(data, options);
177
+ }
178
+ })
179
+ }
180
+
181
+ function drawPieChart() {
182
+ elements = document.querySelectorAll(".inlinepie")
183
+ elements.forEach(element => {
184
+ var label = element.dataset.label;
185
+ var mydata = eval(element.innerText)
186
+ element.innerText = ''
187
+ element.classList.remove("hidden")
188
+ if(mydata.length >= 2) {
189
+ var data = google.visualization.arrayToDataTable(
190
+ mydata
191
+ )
192
+ var options = {
193
+ animation: {'startup': true, 'duration': 300},
194
+ annotations: {textStyle: {bold: true}, alwaysOutside: false },
195
+ width: 600,
196
+ height: 350,
197
+ backgroundColor: 'none',
198
+ bar: {groupWidth: "80%"},
199
+ colors : ['#089', 'silver', '#545B77' ],
200
+ axisTitlesPosition: 'none',
201
+ chartArea: {width:'90%', height: '85%'},
202
+ tooltip : {showColorCode: true},
203
+ vAxis: {gridlines: {count : 0}, textPosition : 'in', baselineColor : 'none', textStyle: {color: '#089', bold: true} },
204
+ hAxis: {gridlines: {count : 0}, textPosition : 'none', baselineColor : 'none'},
205
+ legend: {'position': 'bottom'},
206
+ isStacked: true,
207
+ bars: 'horizontal'
208
+ }
209
+ var chart = new google.visualization.PieChart(element);
210
+ if(mydata[1][1] == 0 && mydata[2][1] == 0){
211
+ return 0
212
+ }
213
+ chart.draw(data, options);
214
+ }
215
+ })
216
+ }
217
+
218
+
219
+ function drawStackedColumnChart() {
220
+ elements = document.querySelectorAll(".inlinestackedcolumn")
221
+ elements.forEach(element => {
222
+ var label = element.dataset.label;
223
+ var mydata = eval(element.innerText)
224
+ element.innerText = ''
225
+ element.classList.remove("hidden")
226
+ if(mydata.length > 1) {
227
+ console.log(mydata)
228
+ var data = google.visualization.arrayToDataTable(
229
+ mydata
230
+ )
231
+ var options = {
232
+ animation: {'startup': true, 'duration': 300},
233
+ annotations: {textStyle: {bold: true}, alwaysOutside: false },
234
+ width: 600,
235
+ height: 350,
236
+ backgroundColor: 'none',
237
+ bar: {groupWidth: "80%"},
238
+ colors : [ '#089', 'silver', '#545B77', 'silver'],
239
+ axisTitlesPosition: 'none',
240
+ chartArea: {width:'100%', height: '85%'},
241
+ tooltip : {showColorCode: true},
242
+ vAxis: {gridlines: {count : 0}, textPosition : 'none', baselineColor : 'none', textStyle: {color: '#089', bold: true} },
243
+ hAxis: {gridlines: {count : 0}, textPosition : 'none', baselineColor : 'none'},
244
+ legend: {'position': 'bottom'},
245
+ isStacked: true,
246
+ bars: 'horizontal'
99
247
  }
100
248
  var chart = new google.visualization.AreaChart(element);
101
249
  chart.draw(data, options);
102
- element.classList.remove("hidden")
103
250
  }
104
251
  })
105
252
  }
253
+
254
+
106
255
  function search_kd(el){
107
256
  //store the current value
108
257
  el.oldvalue = el.value
@@ -122,10 +271,11 @@
122
271
  el.timeout = window.setTimeout(function(){
123
272
  el.timeout = null
124
273
  window.location = locationWithParam('search', el.value)
125
- }, 300)
274
+ }, 500)
126
275
  }
127
276
 
128
277
  $(document).ready(function(){
278
+ /*
129
279
  el = $('#search-field')[0]
130
280
  el.focus()
131
281
  if(el.value && el.value.length > 0){
@@ -137,7 +287,8 @@
137
287
  var re = new RegExp("("+el.value+")", "giu")
138
288
  link.innerHTML = link.innerHTML.replaceAll(re, "<span class='token'>$1</span>") ;
139
289
  }
140
- }
290
+ }
291
+ */
141
292
  })
142
293
 
143
294
  function locationWithParam(param, value){
@@ -0,0 +1,118 @@
1
+ <div class = "row">
2
+
3
+ <div class = "col">
4
+ <div class="card">
5
+ <div class="card-header">
6
+ Subscriptions
7
+ </div>
8
+ <div class="card-body">
9
+ <h1><%=format(@subscription_count)%></h1>
10
+ </div>
11
+ </div>
12
+ </div>
13
+
14
+ <div class = "col">
15
+ <div class="card">
16
+ <div class="card-header">
17
+ Messages Received
18
+ </div>
19
+ <div class="card-body">
20
+ <h1><%=format(@broadcast_count)%></h1>
21
+ </div>
22
+ </div>
23
+ </div>
24
+
25
+ <div class = "col">
26
+ <div class="card">
27
+ <div class="card-header">
28
+ Messages delivered
29
+ </div>
30
+ <div class="card-body">
31
+ <h1><%=format(@message_count)%></h1>
32
+ </div>
33
+ </div>
34
+ </div>
35
+
36
+ </div>
37
+
38
+ <div class = "row">
39
+ &nbsp;<br/>
40
+ </div>
41
+
42
+ <div class="row">
43
+
44
+ <div class = "col">
45
+ <div class="card">
46
+ <div class="card-header">
47
+ Subscriptions over time
48
+ </div>
49
+ <div class="card-body">
50
+ <span class="hidden inlinecolumn">
51
+ <%=[["Time", "Count"]] + @subscriptions_over_time.to_a%>
52
+ </span>
53
+ </div>
54
+ </div>
55
+ </div>
56
+
57
+ <div class = "col">
58
+ <div class="card">
59
+ <div class="card-header">
60
+ Messages received/delivered over time
61
+ </div>
62
+ <div class="card-body">
63
+ <span class="hidden inlinestackedcolumn">
64
+ <%=[["Time", "Recieved Count", "Delivered Count"]] + @messages_over_time.to_a%>
65
+ </span>
66
+ </div>
67
+ </div>
68
+ </div>
69
+
70
+ </div>
71
+
72
+ <div class = "row">
73
+ &nbsp;<br/>
74
+ </div>
75
+
76
+ <div class = "row">
77
+
78
+ <div class = "col-6">
79
+ <div class="card">
80
+ <div class="card-header">
81
+ Channels with most subscriptions
82
+ </div>
83
+ <div class="card-body">
84
+ <table class="table">
85
+ <%@top_subscribed_channels.each do |r| %>
86
+ <tr>
87
+ <td><%=r['key']%></td>
88
+ <td align="right"><h6><%=format(r['rcount'])%>&nbsp;subs</h6></td>
89
+ </tr>
90
+ <% end %>
91
+ </table>
92
+ </div>
93
+ </div>
94
+ </div>
95
+
96
+ <div class = "col-6">
97
+ <div class="card">
98
+ <div class="card-header">
99
+ Channels with most messages delivered
100
+ </div>
101
+ <div class="card-body">
102
+ <table class="table">
103
+ <%@top_messaged_channels.each do |r| %>
104
+ <tr>
105
+ <td><%=r['key']%></td>
106
+ <td align="right"><h6><%=format(r['rcount'])%>&nbsp;messages</h6></td>
107
+ </tr>
108
+ <% end %>
109
+ </table>
110
+ </div>
111
+ </div>
112
+ </div>
113
+
114
+ </div>
115
+
116
+ <div class = "row">
117
+ &nbsp;<br/>
118
+ </div>
@@ -0,0 +1,144 @@
1
+
2
+ <div class = "row">
3
+
4
+ <div class = "col">
5
+ <div class="card">
6
+ <div class="card-header">
7
+ Current size / Max size
8
+ </div>
9
+ <div class="card-body">
10
+ <h1><%=format(round(@size))%>MB / <%=format(round(@max_size))%>MB <span class="fs-4"><%=round(@full)%>% full</span></h1>
11
+ </div>
12
+ </div>
13
+ </div>
14
+
15
+ <div class = "col">
16
+ <div class="card">
17
+ <div class="card-header">
18
+ Number of entries
19
+ </div>
20
+ <div class="card-body">
21
+ <h1><%=format(@entries)%></h1>
22
+ </div>
23
+ </div>
24
+ </div>
25
+
26
+ </div>
27
+
28
+ <div class = "row">
29
+ &nbsp;<br/>
30
+ </div>
31
+
32
+ <div class = "row">
33
+
34
+ <div class = "col">
35
+ <div class="card">
36
+ <div class="card-header">
37
+ Reads / Writes
38
+ </div>
39
+ <div class="card-body">
40
+ <h1><%=format(@reads)%> / <%=format(@writes)%> <span class="fs-4"><%=round(@reads.to_f/@writes) rescue 0%> reads/write</span></h1>
41
+ <hr/>
42
+ <span class="hidden inlinepie"><%=[["name", "value"],["reads", @reads],["writes", @writes]]%></span>
43
+ </div>
44
+ </div>
45
+ </div>
46
+
47
+ <div class = "col">
48
+ <div class="card">
49
+ <div class="card-header">
50
+ Read hits / Misses
51
+ </div>
52
+ <div class="card-body">
53
+ <h1><%=format(@hits)%> / <%=format(@misses)%> <span class="fs-4"><%=round(@hitrate*100)%>% hit rate</span></h1>
54
+ <hr/>
55
+ <span class="hidden inlinepie"><%=[["name", "value"],["hits", @hits],["misses", @misses]]%></span>
56
+ </div>
57
+ </div>
58
+ </div>
59
+
60
+ </div>
61
+
62
+ <div class = "row">
63
+ &nbsp;<br/>
64
+ </div>
65
+
66
+ <div class = "row">
67
+
68
+ <div class = "col">
69
+ <div class="card">
70
+ <div class="card-header">
71
+ Reads, writes over time
72
+ </div>
73
+ <div class="card-body">
74
+ <div class="hidden inlinecolumn">
75
+ <%=Oj.dump([["Time", "Reads", "Writes"]] + @reads_vs_writes)%>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </div>
80
+
81
+ <div class = "col">
82
+ <div class="card">
83
+ <div class="card-header">
84
+ Hits vs misses over time
85
+ </div>
86
+ <div class="card-body">
87
+ <span class="hidden inlinecolumn">
88
+ <%=Oj.dump([["Time", "Hits", "Misses"]] + @hits_vs_misses)%>
89
+ </span>
90
+ </div>
91
+ </div>
92
+ </div>
93
+
94
+ </div>
95
+
96
+ <div class = "row">
97
+ &nbsp;<br/>
98
+ </div>
99
+
100
+ <div class = "row">
101
+
102
+ <div class = "col">
103
+ <div class="card">
104
+ <div class="card-header">
105
+ Most read entries
106
+ </div>
107
+ <div class="card-body">
108
+ <table class="table">
109
+ <%@top_reads.each do |r| %>
110
+ <tr>
111
+ <td><%=r['key']%></td>
112
+ <td align="right"><h5><%=format(r['rcount'])%></h5></td>
113
+ </tr>
114
+ <% end %>
115
+ </table>
116
+ </div>
117
+ </div>
118
+ </div>
119
+
120
+ <div class = "col">
121
+ <div class="card">
122
+ <div class="card-header">
123
+ Most written entries
124
+ </div>
125
+ <div class="card-body">
126
+ <table class="table">
127
+ <%@top_writes.each do |r| %>
128
+ <tr>
129
+ <td><%=r['key']%></td>
130
+ <td align="right"><h5><%=format(r['rcount'])%></h5></td>
131
+ </tr>
132
+ <% end %>
133
+ </table>
134
+ </div>
135
+ </div>
136
+ </div>
137
+
138
+ </div>
139
+
140
+ <div class = "row">
141
+ &nbsp;<br/>
142
+ </div>
143
+
144
+