solid_cache_dashboard 0.0.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.
- checksums.yaml +7 -0
- data/README.md +121 -0
- data/Rakefile +10 -0
- data/app/assets/javascripts/solid_cache_dashboard/alpine.js +5 -0
- data/app/assets/javascripts/solid_cache_dashboard/application.js +60 -0
- data/app/assets/stylesheets/solid_cache_dashboard/application.css +1468 -0
- data/app/assets/stylesheets/solid_cache_dashboard/tailwind.css +625 -0
- data/app/controllers/solid_cache_dashboard/appearance_controller.rb +9 -0
- data/app/controllers/solid_cache_dashboard/application_controller.rb +12 -0
- data/app/controllers/solid_cache_dashboard/cache_entries_controller.rb +37 -0
- data/app/controllers/solid_cache_dashboard/cache_events_controller.rb +9 -0
- data/app/controllers/solid_cache_dashboard/dashboard_controller.rb +69 -0
- data/app/controllers/solid_cache_dashboard/stats_controller.rb +19 -0
- data/app/helpers/solid_cache_dashboard/application_helper.rb +20 -0
- data/app/views/layouts/solid_cache_dashboard/application.html.erb +28 -0
- data/app/views/solid_cache_dashboard/application/_flash_messages.html.erb +10 -0
- data/app/views/solid_cache_dashboard/application/_footer.html.erb +12 -0
- data/app/views/solid_cache_dashboard/application/_navbar.html.erb +32 -0
- data/app/views/solid_cache_dashboard/cache_entries/index.html.erb +136 -0
- data/app/views/solid_cache_dashboard/cache_entries/show.html.erb +118 -0
- data/app/views/solid_cache_dashboard/cache_events/index.html.erb +152 -0
- data/app/views/solid_cache_dashboard/dashboard/index.html.erb +163 -0
- data/app/views/solid_cache_dashboard/stats/index.html.erb +302 -0
- data/config/routes.rb +17 -0
- data/lib/generators/solid_cache_dashboard/install/install_generator.rb +17 -0
- data/lib/generators/solid_cache_dashboard/install/templates/create_solid_cache_dashboard_events.rb +16 -0
- data/lib/solid_cache_dashboard/cache_entry.rb +18 -0
- data/lib/solid_cache_dashboard/cache_event.rb +22 -0
- data/lib/solid_cache_dashboard/configuration.rb +17 -0
- data/lib/solid_cache_dashboard/decorators/cache_entries_decorator.rb +27 -0
- data/lib/solid_cache_dashboard/decorators/cache_entry_decorator.rb +59 -0
- data/lib/solid_cache_dashboard/decorators/cache_event_decorator.rb +72 -0
- data/lib/solid_cache_dashboard/decorators/cache_events_decorator.rb +44 -0
- data/lib/solid_cache_dashboard/engine.rb +19 -0
- data/lib/solid_cache_dashboard/instrumentation.rb +58 -0
- data/lib/solid_cache_dashboard/models/cache_event.rb +51 -0
- data/lib/solid_cache_dashboard/version.rb +5 -0
- data/lib/solid_cache_dashboard.rb +39 -0
- data/package-lock.json +1040 -0
- data/package.json +16 -0
- metadata +125 -0
@@ -0,0 +1,163 @@
|
|
1
|
+
<% content_for :page_title, "Dashboard" %>
|
2
|
+
|
3
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
|
4
|
+
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm rounded-lg">
|
5
|
+
<div class="px-4 py-5 sm:p-6">
|
6
|
+
<div class="flex items-center">
|
7
|
+
<div class="shrink-0 bg-green-100 dark:bg-green-700 rounded-md p-3">
|
8
|
+
<svg class="h-6 w-6 text-green-600 dark:text-green-100" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
9
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
10
|
+
</svg>
|
11
|
+
</div>
|
12
|
+
<div class="ml-5 w-0 flex-1">
|
13
|
+
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">
|
14
|
+
Cache Hits
|
15
|
+
</dt>
|
16
|
+
<dd class="flex items-baseline">
|
17
|
+
<div class="text-2xl font-semibold text-gray-900 dark:text-white">
|
18
|
+
<%= @cache_events.hit_count %>
|
19
|
+
</div>
|
20
|
+
</dd>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
</div>
|
25
|
+
|
26
|
+
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm rounded-lg">
|
27
|
+
<div class="px-4 py-5 sm:p-6">
|
28
|
+
<div class="flex items-center">
|
29
|
+
<div class="shrink-0 bg-amber-100 dark:bg-amber-700 rounded-md p-3">
|
30
|
+
<svg class="h-6 w-6 text-amber-600 dark:text-amber-100" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
31
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
32
|
+
</svg>
|
33
|
+
</div>
|
34
|
+
<div class="ml-5 w-0 flex-1">
|
35
|
+
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">
|
36
|
+
Cache Misses
|
37
|
+
</dt>
|
38
|
+
<dd class="flex items-baseline">
|
39
|
+
<div class="text-2xl font-semibold text-gray-900 dark:text-white">
|
40
|
+
<%= @cache_events.miss_count %>
|
41
|
+
</div>
|
42
|
+
</dd>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
|
48
|
+
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm rounded-lg">
|
49
|
+
<div class="px-4 py-5 sm:p-6">
|
50
|
+
<div class="flex items-center">
|
51
|
+
<div class="shrink-0 bg-sky-100 dark:bg-sky-700 rounded-md p-3">
|
52
|
+
<svg class="h-6 w-6 text-sky-600 dark:text-sky-100" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
53
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
|
54
|
+
</svg>
|
55
|
+
</div>
|
56
|
+
<div class="ml-5 w-0 flex-1">
|
57
|
+
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">
|
58
|
+
Hit Ratio
|
59
|
+
</dt>
|
60
|
+
<dd class="flex items-baseline">
|
61
|
+
<div class="text-2xl font-semibold text-gray-900 dark:text-white">
|
62
|
+
<%= @cache_events.hit_percentage %>
|
63
|
+
</div>
|
64
|
+
</dd>
|
65
|
+
</div>
|
66
|
+
</div>
|
67
|
+
</div>
|
68
|
+
</div>
|
69
|
+
</div>
|
70
|
+
|
71
|
+
<div class="grid grid-cols-1 gap-6 mb-6">
|
72
|
+
<div class="bg-white dark:bg-gray-800 shadow-sm overflow-hidden rounded-lg">
|
73
|
+
<div class="px-4 py-5 sm:px-6 flex justify-between items-center">
|
74
|
+
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">Cache Activity</h3>
|
75
|
+
<div>
|
76
|
+
<div class="inline-flex rounded-md shadow-xs">
|
77
|
+
<% ["30m", "1h", "3h", "6h", "12h", "1d"].each do |period| %>
|
78
|
+
<%= link_to period.upcase, root_path(chart_period: period), class: "relative inline-flex items-center px-3 py-2 text-sm font-medium #{params[:chart_period] == period ? 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-white' : 'bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700'} border border-gray-300 dark:border-gray-600" %>
|
79
|
+
<% end %>
|
80
|
+
</div>
|
81
|
+
</div>
|
82
|
+
</div>
|
83
|
+
<div class="px-4 py-5 sm:p-6 bg-white dark:bg-gray-800">
|
84
|
+
<%= line_chart @charts, height: "300px", colors: @charts.map { |c| c[:color] }, library: { scales: { y: { beginAtZero: true }, x: { ticks: { autoSkip: true, maxTicksLimit: 10 } } } } %>
|
85
|
+
</div>
|
86
|
+
</div>
|
87
|
+
</div>
|
88
|
+
|
89
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
90
|
+
<div class="bg-white dark:bg-gray-800 shadow-sm overflow-hidden rounded-lg">
|
91
|
+
<div class="px-4 py-5 sm:px-6">
|
92
|
+
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">Recent Cache Entries</h3>
|
93
|
+
</div>
|
94
|
+
<div class="border-t border-gray-200 dark:border-gray-700">
|
95
|
+
<div class="overflow-x-auto">
|
96
|
+
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
97
|
+
<thead class="bg-gray-50 dark:bg-gray-900">
|
98
|
+
<tr>
|
99
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Key</th>
|
100
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Size</th>
|
101
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Created</th>
|
102
|
+
</tr>
|
103
|
+
</thead>
|
104
|
+
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
105
|
+
<% @cache_entries.take(5).each do |entry| %>
|
106
|
+
<tr>
|
107
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white truncate max-w-[200px]">
|
108
|
+
<%= link_to entry.key, cache_entry_path(entry.key_hash), class: "hover:underline" %>
|
109
|
+
</td>
|
110
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
111
|
+
<%= entry.human_byte_size %>
|
112
|
+
</td>
|
113
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
114
|
+
<%= entry.created_at_ago %>
|
115
|
+
</td>
|
116
|
+
</tr>
|
117
|
+
<% end %>
|
118
|
+
</tbody>
|
119
|
+
</table>
|
120
|
+
</div>
|
121
|
+
<div class="bg-white dark:bg-gray-800 px-4 py-3 border-t border-gray-200 dark:border-gray-700 text-right sm:px-6">
|
122
|
+
<%= link_to "View all entries", cache_entries_path, class: "inline-flex items-center px-4 py-2 border border-gray-300 dark:border-gray-600 shadow-xs text-sm font-medium rounded-md text-gray-700 dark:text-white bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700" %>
|
123
|
+
</div>
|
124
|
+
</div>
|
125
|
+
</div>
|
126
|
+
|
127
|
+
<div class="bg-white dark:bg-gray-800 shadow-sm overflow-hidden rounded-lg">
|
128
|
+
<div class="px-4 py-5 sm:px-6">
|
129
|
+
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">Recent Cache Events</h3>
|
130
|
+
</div>
|
131
|
+
<div class="border-t border-gray-200 dark:border-gray-700">
|
132
|
+
<div class="overflow-x-auto">
|
133
|
+
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
134
|
+
<thead class="bg-gray-50 dark:bg-gray-900">
|
135
|
+
<tr>
|
136
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Type</th>
|
137
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Key</th>
|
138
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Time</th>
|
139
|
+
</tr>
|
140
|
+
</thead>
|
141
|
+
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
142
|
+
<% @cache_events.take(5).each do |event| %>
|
143
|
+
<tr>
|
144
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
145
|
+
<%= badge event.event_type.to_s.capitalize, event.color %>
|
146
|
+
</td>
|
147
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white truncate max-w-[200px]">
|
148
|
+
<%= event.key_string %>
|
149
|
+
</td>
|
150
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
151
|
+
<%= event.created_at_ago %>
|
152
|
+
</td>
|
153
|
+
</tr>
|
154
|
+
<% end %>
|
155
|
+
</tbody>
|
156
|
+
</table>
|
157
|
+
</div>
|
158
|
+
<div class="bg-white dark:bg-gray-800 px-4 py-3 border-t border-gray-200 dark:border-gray-700 text-right sm:px-6">
|
159
|
+
<%= link_to "View all events", cache_events_path, class: "inline-flex items-center px-4 py-2 border border-gray-300 dark:border-gray-600 shadow-xs text-sm font-medium rounded-md text-gray-700 dark:text-white bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700" %>
|
160
|
+
</div>
|
161
|
+
</div>
|
162
|
+
</div>
|
163
|
+
</div>
|
@@ -0,0 +1,302 @@
|
|
1
|
+
<% content_for :page_title, "Cache Statistics" %>
|
2
|
+
|
3
|
+
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-6">
|
4
|
+
<!-- Cache Entries Count -->
|
5
|
+
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm rounded-lg">
|
6
|
+
<div class="px-4 py-5 sm:p-6">
|
7
|
+
<div class="flex items-center">
|
8
|
+
<div class="shrink-0 bg-blue-100 dark:bg-blue-700 rounded-md p-3">
|
9
|
+
<svg class="h-6 w-6 text-blue-600 dark:text-blue-100" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
10
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"></path>
|
11
|
+
</svg>
|
12
|
+
</div>
|
13
|
+
<div class="ml-5 w-0 flex-1">
|
14
|
+
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">
|
15
|
+
Cache Entries
|
16
|
+
</dt>
|
17
|
+
<dd class="flex items-baseline">
|
18
|
+
<div class="text-2xl font-semibold text-gray-900 dark:text-white">
|
19
|
+
<%= @cache_entries_count %>
|
20
|
+
</div>
|
21
|
+
</dd>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<!-- Cache Size -->
|
28
|
+
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm rounded-lg">
|
29
|
+
<div class="px-4 py-5 sm:p-6">
|
30
|
+
<div class="flex items-center">
|
31
|
+
<div class="shrink-0 bg-purple-100 dark:bg-purple-700 rounded-md p-3">
|
32
|
+
<svg class="h-6 w-6 text-purple-600 dark:text-purple-100" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
33
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4"></path>
|
34
|
+
</svg>
|
35
|
+
</div>
|
36
|
+
<div class="ml-5 w-0 flex-1">
|
37
|
+
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">
|
38
|
+
Cache Size
|
39
|
+
</dt>
|
40
|
+
<dd class="flex items-baseline">
|
41
|
+
<div class="text-2xl font-semibold text-gray-900 dark:text-white">
|
42
|
+
<%= @cache_entries_human_size %>
|
43
|
+
</div>
|
44
|
+
</dd>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
</div>
|
48
|
+
</div>
|
49
|
+
|
50
|
+
<!-- Hit Ratio -->
|
51
|
+
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm rounded-lg">
|
52
|
+
<div class="px-4 py-5 sm:p-6">
|
53
|
+
<div class="flex items-center">
|
54
|
+
<div class="shrink-0 bg-sky-100 dark:bg-sky-700 rounded-md p-3">
|
55
|
+
<svg class="h-6 w-6 text-sky-600 dark:text-sky-100" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
56
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
|
57
|
+
</svg>
|
58
|
+
</div>
|
59
|
+
<div class="ml-5 w-0 flex-1">
|
60
|
+
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">
|
61
|
+
Hit Ratio
|
62
|
+
</dt>
|
63
|
+
<dd class="flex items-baseline">
|
64
|
+
<div class="text-2xl font-semibold text-gray-900 dark:text-white">
|
65
|
+
<%= @hit_percentage %>
|
66
|
+
</div>
|
67
|
+
</dd>
|
68
|
+
</div>
|
69
|
+
</div>
|
70
|
+
</div>
|
71
|
+
</div>
|
72
|
+
|
73
|
+
<!-- Total Operations -->
|
74
|
+
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm rounded-lg">
|
75
|
+
<div class="px-4 py-5 sm:p-6">
|
76
|
+
<div class="flex items-center">
|
77
|
+
<div class="shrink-0 bg-indigo-100 dark:bg-indigo-700 rounded-md p-3">
|
78
|
+
<svg class="h-6 w-6 text-indigo-600 dark:text-indigo-100" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
79
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
|
80
|
+
</svg>
|
81
|
+
</div>
|
82
|
+
<div class="ml-5 w-0 flex-1">
|
83
|
+
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">
|
84
|
+
Total Operations
|
85
|
+
</dt>
|
86
|
+
<dd class="flex items-baseline">
|
87
|
+
<div class="text-2xl font-semibold text-gray-900 dark:text-white">
|
88
|
+
<%= @hit_count + @miss_count + @write_count + @delete_count %>
|
89
|
+
</div>
|
90
|
+
</dd>
|
91
|
+
</div>
|
92
|
+
</div>
|
93
|
+
</div>
|
94
|
+
</div>
|
95
|
+
</div>
|
96
|
+
|
97
|
+
<!-- Event Type Breakdown -->
|
98
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
|
99
|
+
<!-- Event Types -->
|
100
|
+
<div class="bg-white dark:bg-gray-800 shadow-sm overflow-hidden rounded-lg">
|
101
|
+
<div class="px-4 py-5 sm:px-6">
|
102
|
+
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">Cache Operation Metrics</h3>
|
103
|
+
</div>
|
104
|
+
<div class="border-t border-gray-200 dark:border-gray-700">
|
105
|
+
<div class="overflow-x-auto">
|
106
|
+
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
107
|
+
<thead class="bg-gray-50 dark:bg-gray-900">
|
108
|
+
<tr>
|
109
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Operation</th>
|
110
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Count</th>
|
111
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Percentage</th>
|
112
|
+
</tr>
|
113
|
+
</thead>
|
114
|
+
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
115
|
+
<% total_events = @hit_count + @miss_count + @write_count + @delete_count %>
|
116
|
+
<% total_events = 1 if total_events.zero? %>
|
117
|
+
<tr>
|
118
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">
|
119
|
+
<%= badge "Hit", "green" %>
|
120
|
+
</td>
|
121
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
122
|
+
<%= @hit_count %>
|
123
|
+
</td>
|
124
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
125
|
+
<%= "#{((@hit_count.to_f / total_events) * 100).round(2)}%" %>
|
126
|
+
</td>
|
127
|
+
</tr>
|
128
|
+
<tr>
|
129
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">
|
130
|
+
<%= badge "Miss", "amber" %>
|
131
|
+
</td>
|
132
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
133
|
+
<%= @miss_count %>
|
134
|
+
</td>
|
135
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
136
|
+
<%= "#{((@miss_count.to_f / total_events) * 100).round(2)}%" %>
|
137
|
+
</td>
|
138
|
+
</tr>
|
139
|
+
<tr>
|
140
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">
|
141
|
+
<%= badge "Write", "blue" %>
|
142
|
+
</td>
|
143
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
144
|
+
<%= @write_count %>
|
145
|
+
</td>
|
146
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
147
|
+
<%= "#{((@write_count.to_f / total_events) * 100).round(2)}%" %>
|
148
|
+
</td>
|
149
|
+
</tr>
|
150
|
+
<tr>
|
151
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">
|
152
|
+
<%= badge "Delete", "red" %>
|
153
|
+
</td>
|
154
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
155
|
+
<%= @delete_count %>
|
156
|
+
</td>
|
157
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
158
|
+
<%= "#{((@delete_count.to_f / total_events) * 100).round(2)}%" %>
|
159
|
+
</td>
|
160
|
+
</tr>
|
161
|
+
</tbody>
|
162
|
+
</table>
|
163
|
+
</div>
|
164
|
+
</div>
|
165
|
+
</div>
|
166
|
+
|
167
|
+
<!-- Read Performance -->
|
168
|
+
<div class="bg-white dark:bg-gray-800 shadow-sm overflow-hidden rounded-lg">
|
169
|
+
<div class="px-4 py-5 sm:px-6">
|
170
|
+
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">Read Performance</h3>
|
171
|
+
</div>
|
172
|
+
<div class="border-t border-gray-200 dark:border-gray-700">
|
173
|
+
<div class="px-4 py-5 sm:p-6">
|
174
|
+
<div class="space-y-4">
|
175
|
+
<div>
|
176
|
+
<h4 class="text-sm font-medium text-gray-500 dark:text-gray-400">Hit Rate</h4>
|
177
|
+
<div class="mt-2 relative pt-1">
|
178
|
+
<div class="overflow-hidden h-6 text-xs flex rounded-full bg-gray-200 dark:bg-gray-700">
|
179
|
+
<div style="width:<%= @hit_percentage %>" class="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-green-500 dark:bg-green-600 rounded-l-full">
|
180
|
+
<span class="px-2 font-semibold"><%= @hit_percentage %></span>
|
181
|
+
</div>
|
182
|
+
<div style="width:<%= 100 - (@hit_ratio * 100).round(2) %>%" class="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-amber-500 dark:bg-amber-600 rounded-r-full">
|
183
|
+
<span class="px-2 font-semibold"><%= "#{(100 - (@hit_ratio * 100).round(2))}%" %></span>
|
184
|
+
</div>
|
185
|
+
</div>
|
186
|
+
<div class="mt-1 flex justify-between text-xs text-gray-500 dark:text-gray-400">
|
187
|
+
<span>Hits: <%= @hit_count %></span>
|
188
|
+
<span>Misses: <%= @miss_count %></span>
|
189
|
+
</div>
|
190
|
+
</div>
|
191
|
+
</div>
|
192
|
+
|
193
|
+
<div class="pt-4">
|
194
|
+
<h4 class="text-sm font-medium text-gray-500 dark:text-gray-400">Operation Distribution</h4>
|
195
|
+
<div class="mt-2 flex flex-col space-y-2">
|
196
|
+
<div class="relative pt-1">
|
197
|
+
<div class="flex mb-2 items-center justify-between">
|
198
|
+
<div>
|
199
|
+
<span class="text-xs font-semibold inline-block py-1 px-2 uppercase rounded-full text-green-600 bg-green-200 dark:text-green-100 dark:bg-green-700">
|
200
|
+
Reads (Hits + Misses)
|
201
|
+
</span>
|
202
|
+
</div>
|
203
|
+
<div class="text-right">
|
204
|
+
<span class="text-xs font-semibold inline-block text-green-600 dark:text-green-100">
|
205
|
+
<%= "#{(((@hit_count + @miss_count).to_f / total_events) * 100).round(2)}%" %>
|
206
|
+
</span>
|
207
|
+
</div>
|
208
|
+
</div>
|
209
|
+
<div class="overflow-hidden h-2 mb-4 text-xs flex rounded bg-gray-200 dark:bg-gray-700">
|
210
|
+
<div style="width:<%= (((@hit_count + @miss_count).to_f / total_events) * 100).round(2) %>%" class="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-green-500 dark:bg-green-600"></div>
|
211
|
+
</div>
|
212
|
+
</div>
|
213
|
+
<div class="relative pt-1">
|
214
|
+
<div class="flex mb-2 items-center justify-between">
|
215
|
+
<div>
|
216
|
+
<span class="text-xs font-semibold inline-block py-1 px-2 uppercase rounded-full text-blue-600 bg-blue-200 dark:text-blue-100 dark:bg-blue-700">
|
217
|
+
Writes
|
218
|
+
</span>
|
219
|
+
</div>
|
220
|
+
<div class="text-right">
|
221
|
+
<span class="text-xs font-semibold inline-block text-blue-600 dark:text-blue-100">
|
222
|
+
<%= "#{((@write_count.to_f / total_events) * 100).round(2)}%" %>
|
223
|
+
</span>
|
224
|
+
</div>
|
225
|
+
</div>
|
226
|
+
<div class="overflow-hidden h-2 mb-4 text-xs flex rounded bg-gray-200 dark:bg-gray-700">
|
227
|
+
<div style="width:<%= ((@write_count.to_f / total_events) * 100).round(2) %>%" class="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-blue-500 dark:bg-blue-600"></div>
|
228
|
+
</div>
|
229
|
+
</div>
|
230
|
+
<div class="relative pt-1">
|
231
|
+
<div class="flex mb-2 items-center justify-between">
|
232
|
+
<div>
|
233
|
+
<span class="text-xs font-semibold inline-block py-1 px-2 uppercase rounded-full text-red-600 bg-red-200 dark:text-red-100 dark:bg-red-700">
|
234
|
+
Deletes
|
235
|
+
</span>
|
236
|
+
</div>
|
237
|
+
<div class="text-right">
|
238
|
+
<span class="text-xs font-semibold inline-block text-red-600 dark:text-red-100">
|
239
|
+
<%= "#{((@delete_count.to_f / total_events) * 100).round(2)}%" %>
|
240
|
+
</span>
|
241
|
+
</div>
|
242
|
+
</div>
|
243
|
+
<div class="overflow-hidden h-2 mb-4 text-xs flex rounded bg-gray-200 dark:bg-gray-700">
|
244
|
+
<div style="width:<%= ((@delete_count.to_f / total_events) * 100).round(2) %>%" class="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-red-500 dark:bg-red-600"></div>
|
245
|
+
</div>
|
246
|
+
</div>
|
247
|
+
</div>
|
248
|
+
</div>
|
249
|
+
</div>
|
250
|
+
</div>
|
251
|
+
</div>
|
252
|
+
</div>
|
253
|
+
</div>
|
254
|
+
|
255
|
+
<!-- Cache Storage Stats -->
|
256
|
+
<div class="grid grid-cols-1 gap-6">
|
257
|
+
<div class="bg-white dark:bg-gray-800 shadow-sm overflow-hidden rounded-lg">
|
258
|
+
<div class="px-4 py-5 sm:px-6">
|
259
|
+
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">Cache Storage Details</h3>
|
260
|
+
</div>
|
261
|
+
<div class="border-t border-gray-200 dark:border-gray-700">
|
262
|
+
<div class="divide-y divide-gray-200 dark:divide-gray-700">
|
263
|
+
<div class="py-4 sm:py-5 sm:grid sm:grid-cols-2 sm:gap-4 sm:px-6">
|
264
|
+
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">
|
265
|
+
Total Entries
|
266
|
+
</dt>
|
267
|
+
<dd class="mt-1 text-sm text-gray-900 dark:text-white sm:mt-0">
|
268
|
+
<%= @cache_entries_count %>
|
269
|
+
</dd>
|
270
|
+
</div>
|
271
|
+
<div class="py-4 sm:py-5 sm:grid sm:grid-cols-2 sm:gap-4 sm:px-6">
|
272
|
+
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">
|
273
|
+
Total Size
|
274
|
+
</dt>
|
275
|
+
<dd class="mt-1 text-sm text-gray-900 dark:text-white sm:mt-0">
|
276
|
+
<%= @cache_entries_human_size %> (<%= @cache_entries_size %> bytes)
|
277
|
+
</dd>
|
278
|
+
</div>
|
279
|
+
<div class="py-4 sm:py-5 sm:grid sm:grid-cols-2 sm:gap-4 sm:px-6">
|
280
|
+
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">
|
281
|
+
Average Entry Size
|
282
|
+
</dt>
|
283
|
+
<dd class="mt-1 text-sm text-gray-900 dark:text-white sm:mt-0">
|
284
|
+
<% if @cache_entries_count > 0 %>
|
285
|
+
<%= ActiveSupport::NumberHelper.number_to_human_size(@cache_entries_size / @cache_entries_count) %>
|
286
|
+
<% else %>
|
287
|
+
0 B
|
288
|
+
<% end %>
|
289
|
+
</dd>
|
290
|
+
</div>
|
291
|
+
<div class="py-4 sm:py-5 sm:grid sm:grid-cols-2 sm:gap-4 sm:px-6">
|
292
|
+
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">
|
293
|
+
Hit Ratio
|
294
|
+
</dt>
|
295
|
+
<dd class="mt-1 text-sm text-gray-900 dark:text-white sm:mt-0">
|
296
|
+
<%= @hit_percentage %> (<%= @hit_count %> hits / <%= @hit_count + @miss_count %> reads)
|
297
|
+
</dd>
|
298
|
+
</div>
|
299
|
+
</div>
|
300
|
+
</div>
|
301
|
+
</div>
|
302
|
+
</div>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
SolidCacheDashboard::Engine.routes.draw do
|
2
|
+
resources :cache_entries, only: [:index, :show] do
|
3
|
+
collection do
|
4
|
+
delete :clear_all
|
5
|
+
end
|
6
|
+
member do
|
7
|
+
delete :delete
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
resources :cache_events, only: [:index]
|
12
|
+
|
13
|
+
get "stats", to: "stats#index", as: :stats
|
14
|
+
post "appearance/toggle", to: "appearance#toggle", as: :toggle_appearance
|
15
|
+
|
16
|
+
root "dashboard#index"
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module SolidCacheDashboard
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path("templates", __dir__)
|
5
|
+
|
6
|
+
def create_migrations
|
7
|
+
template "create_solid_cache_dashboard_events.rb", "db/migrate/#{timestamp}_create_solid_cache_dashboard_events.rb"
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def timestamp
|
13
|
+
Time.current.strftime("%Y%m%d%H%M%S")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/generators/solid_cache_dashboard/install/templates/create_solid_cache_dashboard_events.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreateSolidCacheDashboardEvents < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
create_table :solid_cache_dashboard_events do |t|
|
4
|
+
t.string :event_type, null: false
|
5
|
+
t.integer :key_hash, limit: 8, null: false
|
6
|
+
t.string :key_string
|
7
|
+
t.integer :byte_size, limit: 4
|
8
|
+
t.float :duration
|
9
|
+
t.datetime :created_at, null: false
|
10
|
+
|
11
|
+
t.index [:event_type]
|
12
|
+
t.index [:key_hash]
|
13
|
+
t.index [:created_at]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module SolidCacheDashboard
|
2
|
+
module CacheEntry
|
3
|
+
# Constants
|
4
|
+
ACTIVE = :active
|
5
|
+
EXPIRED = :expired
|
6
|
+
|
7
|
+
STATUSES = [ACTIVE, EXPIRED]
|
8
|
+
|
9
|
+
STATUS_COLORS = {
|
10
|
+
ACTIVE => "green",
|
11
|
+
EXPIRED => "amber"
|
12
|
+
}
|
13
|
+
|
14
|
+
def self.status_color(status)
|
15
|
+
STATUS_COLORS[status] || "zinc"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module SolidCacheDashboard
|
2
|
+
module CacheEvent
|
3
|
+
# Types of cache events
|
4
|
+
HIT = :hit
|
5
|
+
MISS = :miss
|
6
|
+
WRITE = :write
|
7
|
+
DELETE = :delete
|
8
|
+
|
9
|
+
EVENT_TYPES = [HIT, MISS, WRITE, DELETE]
|
10
|
+
|
11
|
+
EVENT_COLORS = {
|
12
|
+
HIT => "green",
|
13
|
+
MISS => "amber",
|
14
|
+
WRITE => "sky",
|
15
|
+
DELETE => "red"
|
16
|
+
}
|
17
|
+
|
18
|
+
def self.event_color(event_type)
|
19
|
+
EVENT_COLORS[event_type] || "zinc"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module SolidCacheDashboard
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :title
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@title = "Solid Cache Dashboard"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.configuration
|
11
|
+
@configuration ||= Configuration.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.configure
|
15
|
+
yield(configuration)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module SolidCacheDashboard
|
2
|
+
module Decorators
|
3
|
+
class CacheEntriesDecorator
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
delegate :current_page, :total_pages, :limit_value, :total_count, :offset_value, to: :@entries
|
7
|
+
|
8
|
+
def initialize(entries)
|
9
|
+
@entries = entries
|
10
|
+
end
|
11
|
+
|
12
|
+
def each(&block)
|
13
|
+
@entries.each do |entry|
|
14
|
+
block.call(SolidCacheDashboard.decorate(entry))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def total_size
|
19
|
+
@total_size ||= @entries.sum(:byte_size)
|
20
|
+
end
|
21
|
+
|
22
|
+
def human_total_size
|
23
|
+
ActiveSupport::NumberHelper.number_to_human_size(total_size)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module SolidCacheDashboard
|
2
|
+
module Decorators
|
3
|
+
class CacheEntryDecorator
|
4
|
+
def initialize(entry)
|
5
|
+
@entry = entry
|
6
|
+
end
|
7
|
+
|
8
|
+
def key_hash
|
9
|
+
@entry.key_hash
|
10
|
+
end
|
11
|
+
|
12
|
+
def key
|
13
|
+
readable_key = @entry.key.force_encoding("UTF-8")
|
14
|
+
readable_key.match?(/^[-+\/=A-Za-z0-9]+$/) ? readable_key : key_hash
|
15
|
+
rescue
|
16
|
+
key_hash
|
17
|
+
end
|
18
|
+
|
19
|
+
def value
|
20
|
+
@value ||= Marshal.load(@entry.value)
|
21
|
+
rescue
|
22
|
+
"Binary data"
|
23
|
+
end
|
24
|
+
|
25
|
+
def byte_size
|
26
|
+
@entry.byte_size
|
27
|
+
end
|
28
|
+
|
29
|
+
def human_byte_size
|
30
|
+
ActiveSupport::NumberHelper.number_to_human_size(byte_size)
|
31
|
+
end
|
32
|
+
|
33
|
+
def created_at
|
34
|
+
@entry.created_at
|
35
|
+
end
|
36
|
+
|
37
|
+
def created_at_ago
|
38
|
+
time_ago_in_words(created_at)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def time_ago_in_words(time)
|
44
|
+
distance_in_seconds = (Time.now - time).round
|
45
|
+
|
46
|
+
case distance_in_seconds
|
47
|
+
when 0..59
|
48
|
+
"#{distance_in_seconds} seconds ago"
|
49
|
+
when 60..3599
|
50
|
+
"#{(distance_in_seconds / 60).round} minutes ago"
|
51
|
+
when 3600..86399
|
52
|
+
"#{(distance_in_seconds / 3600).round} hours ago"
|
53
|
+
else
|
54
|
+
"#{(distance_in_seconds / 86400).round} days ago"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|