litestack 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.standard.yml +3 -0
  3. data/BENCHMARKS.md +23 -7
  4. data/CHANGELOG.md +11 -0
  5. data/Gemfile +1 -7
  6. data/Gemfile.lock +92 -0
  7. data/README.md +120 -6
  8. data/ROADMAP.md +45 -0
  9. data/Rakefile +3 -1
  10. data/WHYLITESTACK.md +1 -1
  11. data/assets/litecache_metrics.png +0 -0
  12. data/assets/litedb_metrics.png +0 -0
  13. data/assets/litemetric_logo_teal.png +0 -0
  14. data/assets/litesearch_logo_teal.png +0 -0
  15. data/bench/bench.rb +17 -10
  16. data/bench/bench_cache_rails.rb +10 -13
  17. data/bench/bench_cache_raw.rb +17 -22
  18. data/bench/bench_jobs_rails.rb +18 -12
  19. data/bench/bench_jobs_raw.rb +17 -10
  20. data/bench/bench_queue.rb +4 -6
  21. data/bench/rails_job.rb +5 -7
  22. data/bench/skjob.rb +4 -4
  23. data/bench/uljob.rb +6 -6
  24. data/lib/action_cable/subscription_adapter/litecable.rb +5 -8
  25. data/lib/active_job/queue_adapters/litejob_adapter.rb +6 -8
  26. data/lib/active_record/connection_adapters/litedb_adapter.rb +65 -75
  27. data/lib/active_support/cache/litecache.rb +38 -41
  28. data/lib/generators/litestack/install/install_generator.rb +3 -3
  29. data/lib/generators/litestack/install/templates/database.yml +7 -1
  30. data/lib/litestack/liteboard/liteboard.rb +269 -149
  31. data/lib/litestack/litecable.rb +41 -37
  32. data/lib/litestack/litecable.sql.yml +22 -11
  33. data/lib/litestack/litecache.rb +79 -88
  34. data/lib/litestack/litecache.sql.yml +81 -22
  35. data/lib/litestack/litecache.yml +1 -1
  36. data/lib/litestack/litedb.rb +35 -40
  37. data/lib/litestack/litejob.rb +30 -29
  38. data/lib/litestack/litejobqueue.rb +63 -65
  39. data/lib/litestack/litemetric.rb +80 -92
  40. data/lib/litestack/litemetric.sql.yml +244 -234
  41. data/lib/litestack/litemetric_collector.sql.yml +38 -41
  42. data/lib/litestack/litequeue.rb +39 -41
  43. data/lib/litestack/litequeue.sql.yml +39 -31
  44. data/lib/litestack/litescheduler.rb +15 -15
  45. data/lib/litestack/litesearch/index.rb +93 -63
  46. data/lib/litestack/litesearch/model.rb +66 -65
  47. data/lib/litestack/litesearch/schema.rb +53 -56
  48. data/lib/litestack/litesearch/schema_adapters/backed_adapter.rb +46 -50
  49. data/lib/litestack/litesearch/schema_adapters/basic_adapter.rb +44 -35
  50. data/lib/litestack/litesearch/schema_adapters/contentless_adapter.rb +3 -6
  51. data/lib/litestack/litesearch/schema_adapters/standalone_adapter.rb +7 -9
  52. data/lib/litestack/litesearch/schema_adapters.rb +4 -9
  53. data/lib/litestack/litesearch.rb +6 -9
  54. data/lib/litestack/litesupport.rb +76 -86
  55. data/lib/litestack/railtie.rb +1 -1
  56. data/lib/litestack/version.rb +2 -2
  57. data/lib/litestack.rb +6 -4
  58. data/lib/railties/rails/commands/dbconsole.rb +11 -15
  59. data/lib/sequel/adapters/litedb.rb +16 -21
  60. data/lib/sequel/adapters/shared/litedb.rb +168 -168
  61. data/scripts/build_metrics.rb +91 -0
  62. data/scripts/test_cable.rb +30 -0
  63. data/scripts/test_job_retry.rb +33 -0
  64. data/scripts/test_metrics.rb +60 -0
  65. data/template.rb +2 -2
  66. metadata +101 -6
@@ -1,18 +1,16 @@
1
1
  # frozen_stringe_literal: true
2
2
 
3
3
  # all components should require the support module
4
- require_relative 'litesupport'
5
- require_relative 'litemetric'
4
+ require_relative "litesupport"
5
+ require_relative "litemetric"
6
6
 
7
- require 'base64'
8
- require 'oj'
7
+ require "base64"
8
+ require "oj"
9
9
 
10
10
  class Litecable
11
-
12
11
  include Litesupport::Liteconnection
13
12
  include Litemetric::Measurable
14
13
 
15
-
16
14
  DEFAULT_OPTIONS = {
17
15
  config_path: "./litecable.yml",
18
16
  path: Litesupport.root.join("cable.sqlite3"),
@@ -22,56 +20,63 @@ class Litecable
22
20
  listen_interval: 0.05, # check new messages every 50 milliseconds
23
21
  metrics: false
24
22
  }
25
-
26
- def initialize(options = {})
27
- @messages = Litesupport::Pool.new(1){[]}
23
+
24
+ def initialize(options = {})
25
+ @messages = Litesupport::Pool.new(1) { [] }
28
26
  init(options)
29
27
  collect_metrics if @options[:metrics]
30
28
  end
31
-
29
+
32
30
  # broadcast a message to a specific channel
33
- def broadcast(channel, payload=nil)
31
+ def broadcast(channel, payload = nil)
34
32
  # group meesages and only do broadcast every 10 ms
35
33
  # but broadcast locally normally
36
- @messages.acquire{|msgs| msgs << [channel.to_s, Oj.dump(payload)]}
34
+ @messages.acquire { |msgs| msgs << [channel.to_s, Oj.dump(payload)] }
37
35
  capture(:broadcast, channel)
38
- local_broadcast(channel, payload)
36
+ local_broadcast(channel, payload)
39
37
  end
40
-
38
+
41
39
  # subscribe to a channel, optionally providing a success callback proc
42
40
  def subscribe(channel, subscriber, success_callback = nil)
43
41
  @subscribers.acquire do |subs|
44
42
  subs[channel] = {} unless subs[channel]
45
43
  subs[channel][subscriber] = true
46
44
  end
45
+ success_callback&.call
47
46
  capture(:subscribe, channel)
48
47
  end
49
-
48
+
50
49
  # unsubscribe from a channel
51
50
  def unsubscribe(channel, subscriber)
52
- @subscribers.acquire{|subs| subs[channel].delete(subscriber) rescue nil }
51
+ @subscribers.acquire { |subs|
52
+ begin
53
+ subs[channel].delete(subscriber)
54
+ rescue
55
+ nil
56
+ end
57
+ }
53
58
  capture(:unsubscribe, channel)
54
59
  end
55
60
 
56
- private
57
-
58
- # broadcast the message to local subscribers
59
- def local_broadcast(channel, payload=nil)
61
+ private
62
+
63
+ # broadcast the message to local subscribers
64
+ def local_broadcast(channel, payload = nil)
60
65
  subscribers = []
61
- @subscribers.acquire do |subs|
62
- return unless subs[channel]
66
+ @subscribers.acquire do |subs|
67
+ break unless subs[channel]
63
68
  subscribers = subs[channel].keys
64
69
  end
65
70
  subscribers.each do |subscriber|
66
- subscriber.call(payload)
71
+ subscriber.call(payload)
67
72
  capture(:message, channel)
68
73
  end
69
- end
70
-
74
+ end
75
+
71
76
  def setup
72
77
  super # create connection
73
78
  @pid = Process.pid
74
- @subscribers = Litesupport::Pool.new(1){{}}
79
+ @subscribers = Litesupport::Pool.new(1) { {} }
75
80
  @running = true
76
81
  @listener = create_listener
77
82
  @pruner = create_pruner
@@ -81,48 +86,47 @@ class Litecable
81
86
 
82
87
  def create_broadcaster
83
88
  Litescheduler.spawn do
84
- while @running do
89
+ while @running
85
90
  @messages.acquire do |msgs|
86
91
  if msgs.length > 0
87
92
  run_sql("BEGIN IMMEDIATE")
88
- while msg = msgs.shift
93
+ while (msg = msgs.shift)
89
94
  run_stmt(:publish, msg[0], msg[1], @pid)
90
95
  end
91
96
  run_sql("END")
92
- end
97
+ end
93
98
  end
94
99
  sleep 0.02
95
- end
100
+ end
96
101
  end
97
102
  end
98
103
 
99
104
  def create_pruner
100
105
  Litescheduler.spawn do
101
- while @running do
106
+ while @running
102
107
  run_stmt(:prune, @options[:expire_after])
103
108
  sleep @options[:expire_after]
104
- end
109
+ end
105
110
  end
106
111
  end
107
112
 
108
113
  def create_listener
109
114
  Litescheduler.spawn do
110
- while @running do
115
+ while @running
111
116
  @last_fetched_id ||= (run_stmt(:last_id)[0][0] || 0)
112
117
  run_stmt(:fetch, @last_fetched_id, @pid).to_a.each do |msg|
113
118
  @logger.info "RECEIVED #{msg}"
114
119
  @last_fetched_id = msg[0]
115
- local_broadcast(msg[1], Oj.load(msg[2]))
120
+ local_broadcast(msg[1], Oj.load(msg[2]))
116
121
  end
117
122
  sleep @options[:listen_interval]
118
- end
123
+ end
119
124
  end
120
125
  end
121
126
 
122
127
  def create_connection
123
128
  super("#{__dir__}/litecable.sql.yml") do |conn|
124
- conn.wal_autocheckpoint = 10000
129
+ conn.wal_autocheckpoint = 10000
125
130
  end
126
131
  end
127
-
128
132
  end
@@ -2,23 +2,34 @@ schema:
2
2
  1:
3
3
  create_table_messages: >
4
4
  CREATE TABLE IF NOT EXISTS messages(
5
- id INTEGER PRIMARY KEY autoincrement,
6
- channel TEXT NOT NULL,
7
- value TEXT NOT NULL,
8
- pid INTEGER,
5
+ id INTEGER PRIMARY KEY autoincrement,
6
+ channel TEXT NOT NULL,
7
+ value TEXT NOT NULL,
8
+ pid INTEGER,
9
9
  created_at INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT(unixepoch())
10
10
  );
11
11
  create_index_messages_by_date: >
12
- CREATE INDEX IF NOT EXISTS messages_by_date ON messages(created_at);
12
+ CREATE INDEX IF NOT EXISTS messages_by_date ON messages(created_at);
13
13
 
14
14
  stmts:
15
+ publish: >
16
+ INSERT INTO messages(channel, value, pid)
17
+ VALUES ($1, $2, $3);
15
18
 
16
- publish: INSERT INTO messages(channel, value, pid) VALUES ($1, $2, $3)
19
+ last_id: >
20
+ SELECT max(id) FROM messages;
17
21
 
18
- last_id: SELECT max(id) FROM messages
19
-
20
- fetch: SELECT id, channel, value, created_at FROM messages WHERE id > $1 and pid != $2
22
+ fetch: >
23
+ SELECT id, channel, value, created_at
24
+ FROM messages
25
+ WHERE id > $1
26
+ AND pid != $2;
21
27
 
22
- prune: DELETE FROM messages WHERE created_at < (unixepoch() - $1)
28
+ prune: >
29
+ DELETE FROM messages
30
+ WHERE created_at < (unixepoch() - $1);
23
31
 
24
- check_prune: SELECT count(*) FROM messages WHERE created_at < (unixepoch() - $1)
32
+ check_prune: >
33
+ SELECT count(*)
34
+ FROM messages
35
+ WHERE created_at < (unixepoch() - $1);
@@ -1,31 +1,30 @@
1
1
  # frozen_stringe_literal: true
2
2
 
3
3
  # all components should require the support module
4
- require_relative 'litesupport'
5
- require_relative 'litemetric'
4
+ require_relative "litesupport"
5
+ require_relative "litemetric"
6
6
 
7
7
  ##
8
- #Litecache is a caching library for Ruby applications that is built on top of SQLite. It is designed to be simple to use, very fast, and feature-rich, providing developers with a reliable and efficient way to cache data.
8
+ # Litecache is a caching library for Ruby applications that is built on top of SQLite. It is designed to be simple to use, very fast, and feature-rich, providing developers with a reliable and efficient way to cache data.
9
9
  #
10
- #One of the main features of Litecache is automatic key expiry, which allows developers to set an expiration time for each cached item. This ensures that cached data is automatically removed from the cache after a certain amount of time has passed, reducing the risk of stale data being served to users.
10
+ # One of the main features of Litecache is automatic key expiry, which allows developers to set an expiration time for each cached item. This ensures that cached data is automatically removed from the cache after a certain amount of time has passed, reducing the risk of stale data being served to users.
11
11
  #
12
- #In addition, Litecache supports LRU (Least Recently Used) removal, which means that if the cache reaches its capacity limit, the least recently used items will be removed first to make room for new items. This ensures that the most frequently accessed data is always available in the cache.
12
+ # In addition, Litecache supports LRU (Least Recently Used) removal, which means that if the cache reaches its capacity limit, the least recently used items will be removed first to make room for new items. This ensures that the most frequently accessed data is always available in the cache.
13
13
  #
14
- #Litecache also supports integer value increment/decrement, which allows developers to increment or decrement the value of a cached item in a thread-safe manner. This is useful for implementing counters or other types of numerical data that need to be updated frequently.
14
+ # Litecache also supports integer value increment/decrement, which allows developers to increment or decrement the value of a cached item in a thread-safe manner. This is useful for implementing counters or other types of numerical data that need to be updated frequently.
15
15
  #
16
- #Overall, Litecache is a powerful and flexible caching library that provides automatic key expiry, LRU removal, and integer value increment/decrement capabilities. Its fast performance and simple API make it an excellent choice for Ruby applications that need a reliable and efficient way to cache data.
16
+ # Overall, Litecache is a powerful and flexible caching library that provides automatic key expiry, LRU removal, and integer value increment/decrement capabilities. Its fast performance and simple API make it an excellent choice for Ruby applications that need a reliable and efficient way to cache data.
17
17
 
18
18
  class Litecache
19
-
20
19
  include Litesupport::Liteconnection
21
20
  include Litemetric::Measurable
22
21
 
23
22
  # the default options for the cache
24
- # can be overriden by passing new options in a hash
23
+ # can be overriden by passing new options in a hash
25
24
  # to Litecache.new
26
25
  # path: "./cache.db"
27
26
  # expiry: 60 * 60 * 24 * 30 -> one month default expiry if none is provided
28
- # size: 128 * 1024 * 1024 -> 128MB
27
+ # size: 128 * 1024 * 1024 -> 128MB
29
28
  # mmap_size: 128 * 1024 * 1024 -> 128MB to be held in memory
30
29
  # min_size: 32 * 1024 -> 32MB
31
30
  # return_full_record: false -> only return the payload
@@ -35,15 +34,15 @@ class Litecache
35
34
  path: Litesupport.root.join("cache.sqlite3"),
36
35
  config_path: "./litecache.yml",
37
36
  sync: 0,
38
- expiry: 60 * 60 * 24 * 30, # one month
39
- size: 128 * 1024 * 1024, #128MB
40
- mmap_size: 128 * 1024 * 1024, #128MB
41
- min_size: 8 * 1024 * 1024, #16MB
42
- return_full_record: false, #only return the payload
37
+ expiry: 60 * 60 * 24 * 30, # one month
38
+ size: 128 * 1024 * 1024, # 128MB
39
+ mmap_size: 128 * 1024 * 1024, # 128MB
40
+ min_size: 8 * 1024 * 1024, # 16MB
41
+ return_full_record: false, # only return the payload
43
42
  sleep_interval: 1, # 1 second
44
43
  metrics: false
45
44
  }
46
-
45
+
47
46
  # creates a new instance of Litecache
48
47
  # can optionally receive an options hash which will be merged
49
48
  # with the DEFAULT_OPTIONS (the new hash overrides any matching keys in the default one).
@@ -61,57 +60,53 @@ class Litecache
61
60
  #
62
61
  # litecache.clear # nothing remains in the cache
63
62
  # litecache.close # optional, you can safely kill the process
64
-
63
+
65
64
  def initialize(options = {})
66
- options[:size] = DEFAULT_OPTIONS[:min_size] if options[:size] && options[:size] < DEFAULT_OPTIONS[:min_size]
65
+ options[:size] = DEFAULT_OPTIONS[:min_size] if options[:size] && options[:size] < DEFAULT_OPTIONS[:min_size]
67
66
  @last_visited = {}
68
- init(options)
67
+ init(options)
69
68
  collect_metrics if @options[:metrics]
70
69
  end
71
-
72
- # add a key, value pair to the cache, with an optional expiry value (number of seconds)
70
+
71
+ # add a key, value pair to the cache, with an optional expiry value (number of seconds)
73
72
  def set(key, value, expires_in = nil)
74
73
  key = key.to_s
75
- expires_in = @options[:expires_in] if expires_in.nil? or expires_in.zero?
74
+ expires_in = @options[:expires_in] if expires_in.nil? || expires_in.zero?
76
75
  @conn.acquire do |cache|
77
- begin
78
- cache.stmts[:setter].execute!(key, value, expires_in)
79
- capture(:set, key)
80
- rescue SQLite3::FullException
81
- cache.stmts[:extra_pruner].execute!(0.2)
82
- cache.execute("vacuum")
83
- retry
84
- end
76
+ cache.stmts[:setter].execute!(key, value, expires_in)
77
+ capture(:set, key)
78
+ rescue SQLite3::FullException
79
+ cache.stmts[:extra_pruner].execute!(0.2)
80
+ cache.execute("vacuum")
81
+ retry
85
82
  end
86
- return true
83
+ true
87
84
  end
88
-
89
- # add a key, value pair to the cache, but only if the key doesn't exist, with an optional expiry value (number of seconds)
85
+
86
+ # add a key, value pair to the cache, but only if the key doesn't exist, with an optional expiry value (number of seconds)
90
87
  def set_unless_exists(key, value, expires_in = nil)
91
88
  key = key.to_s
92
- expires_in = @options[:expires_in] if expires_in.nil? or expires_in.zero?
89
+ expires_in = @options[:expires_in] if expires_in.nil? || expires_in.zero?
93
90
  changes = 0
94
91
  @conn.acquire do |cache|
95
- begin
96
- cache.transaction(:immediate) do
97
- cache.stmts[:inserter].execute!(key, value, expires_in)
98
- changes = cache.changes
99
- end
100
- capture(:set, key)
101
- rescue SQLite3::FullException
102
- cache.stmts[:extra_pruner].execute!(0.2)
103
- cache.execute("vacuum")
104
- retry
92
+ cache.transaction(:immediate) do
93
+ cache.stmts[:inserter].execute!(key, value, expires_in)
94
+ changes = cache.changes
105
95
  end
96
+ capture(:set, key)
97
+ rescue SQLite3::FullException
98
+ cache.stmts[:extra_pruner].execute!(0.2)
99
+ cache.execute("vacuum")
100
+ retry
106
101
  end
107
- return changes > 0
102
+ changes > 0
108
103
  end
109
-
104
+
110
105
  # get a value by its key
111
106
  # if the key doesn't exist or it is expired then null will be returned
112
107
  def get(key)
113
108
  key = key.to_s
114
- if record = @conn.acquire{|cache| cache.stmts[:getter].execute!(key)[0] }
109
+ if (record = @conn.acquire { |cache| cache.stmts[:getter].execute!(key)[0] })
115
110
  @last_visited[key] = true
116
111
  capture(:get, key, 1)
117
112
  return record[1]
@@ -119,7 +114,7 @@ class Litecache
119
114
  capture(:get, key, 0)
120
115
  nil
121
116
  end
122
-
117
+
123
118
  # delete a key, value pair from the cache
124
119
  def delete(key)
125
120
  changes = 0
@@ -127,59 +122,59 @@ class Litecache
127
122
  cache.stmts[:deleter].execute!(key)
128
123
  changes = cache.changes
129
124
  end
130
- return changes > 0
125
+ changes > 0
131
126
  end
132
-
127
+
133
128
  # increment an integer value by amount, optionally add an expiry value (in seconds)
134
129
  def increment(key, amount, expires_in = nil)
135
- expires_in = @expires_in unless expires_in
136
- @conn.acquire{|cache| cache.stmts[:incrementer].execute!(key.to_s, amount, expires_in) }
130
+ expires_in ||= @expires_in
131
+ @conn.acquire { |cache| cache.stmts[:incrementer].execute!(key.to_s, amount, expires_in) }
137
132
  end
138
-
133
+
139
134
  # decrement an integer value by amount, optionally add an expiry value (in seconds)
140
135
  def decrement(key, amount, expires_in = nil)
141
136
  increment(key, -amount, expires_in)
142
137
  end
143
-
138
+
144
139
  # delete all entries in the cache up limit (ordered by LRU), if no limit is provided approximately 20% of the entries will be deleted
145
- def prune(limit=nil)
140
+ def prune(limit = nil)
146
141
  @conn.acquire do |cache|
147
- if limit and limit.is_a? Integer
142
+ if limit&.is_a? Integer
148
143
  cache.stmts[:limited_pruner].execute!(limit)
149
- elsif limit and limit.is_a? Float
144
+ elsif limit&.is_a? Float
150
145
  cache.stmts[:extra_pruner].execute!(limit)
151
146
  else
152
- cache.stmts[:pruner].execute!
147
+ cache.stmts[:pruner].execute!
153
148
  end
154
149
  end
155
150
  end
156
-
151
+
157
152
  # return the number of key, value pairs in the cache
158
153
  def count
159
154
  run_stmt(:counter)[0][0]
160
155
  end
161
-
156
+
162
157
  # return the actual size of the cache file
163
- #def size
158
+ # def size
164
159
  # run_stmt(:sizer)[0][0]
165
- #end
166
-
160
+ # end
161
+
167
162
  # delete all key, value pairs in the cache
168
163
  def clear
169
164
  run_sql("delete FROM data")
170
165
  end
171
-
166
+
172
167
  # close the connection to the cache file
173
168
  def close
174
169
  @running = false
175
170
  super
176
171
  end
177
-
172
+
178
173
  # return the maximum size of the cache
179
174
  def max_size
180
175
  run_sql("SELECT s.page_size * c.max_page_count FROM pragma_page_size() as s, pragma_max_page_count() as c")[0][0].to_f / (1024 * 1024)
181
176
  end
182
-
177
+
183
178
  def snapshot
184
179
  {
185
180
  summary: {
@@ -193,10 +188,9 @@ class Litecache
193
188
  }
194
189
  end
195
190
 
196
-
197
191
  # low level access to SQLite transactions, use with caution
198
- def transaction(mode, acquire=true)
199
- return cache.transaction(mode){yield} unless acquire
192
+ def transaction(mode, acquire = true)
193
+ return cache.transaction(mode) { yield } unless acquire
200
194
  @conn.acquire do |cache|
201
195
  cache.transaction(mode) do
202
196
  yield
@@ -204,44 +198,41 @@ class Litecache
204
198
  end
205
199
  end
206
200
 
207
- private
208
-
201
+ private
202
+
209
203
  def setup
210
204
  super # create connection
211
205
  @bgthread = spawn_worker # create backgroud pruner thread
212
206
  end
213
-
207
+
214
208
  def spawn_worker
215
209
  Litescheduler.spawn do
216
210
  while @running
217
211
  @conn.acquire do |cache|
218
- begin
219
- cache.transaction(:immediate) do
220
- @last_visited.delete_if do |k| # there is a race condition here, but not a serious one
221
- cache.stmts[:toucher].execute!(k) || true
222
- end
223
- cache.stmts[:pruner].execute!
212
+ cache.transaction(:immediate) do
213
+ @last_visited.delete_if do |k| # there is a race condition here, but not a serious one
214
+ cache.stmts[:toucher].execute!(k) || true
224
215
  end
225
- rescue SQLite3::BusyException
226
- retry
227
- rescue SQLite3::FullException
228
- cache.stmts[:extra_pruner].execute!(0.2)
229
- rescue Exception
230
- # database is closed
216
+ cache.stmts[:pruner].execute!
231
217
  end
218
+ rescue SQLite3::BusyException
219
+ retry
220
+ rescue SQLite3::FullException
221
+ cache.stmts[:extra_pruner].execute!(0.2)
222
+ rescue Exception # standard:disable Lint/RescueException
223
+ # database is closed
232
224
  end
233
225
  sleep @options[:sleep_interval]
234
226
  end
235
227
  end
236
228
  end
237
-
229
+
238
230
  def create_connection
239
231
  super("#{__dir__}/litecache.sql.yml") do |conn|
240
232
  conn.cache_size = 2000
241
- conn.journal_size_limit = [(@options[:size]/2).to_i, @options[:min_size]].min
233
+ conn.journal_size_limit = [(@options[:size] / 2).to_i, @options[:min_size]].min
242
234
  conn.max_page_count = (@options[:size] / conn.page_size).to_i
243
235
  conn.case_sensitive_like = true
244
236
  end
245
- end
246
-
237
+ end
247
238
  end
@@ -1,28 +1,87 @@
1
1
  schema:
2
- 1:
3
- create_table_data: >
4
- CREATE table if not exists data(id text primary key, value text, expires_in integer, last_used integer)
2
+ 1:
3
+ create_table_data: >
4
+ CREATE TABLE IF NOT EXISTS data(
5
+ id TEXT PRIMARY KEY,
6
+ value TEXT,
7
+ expires_in INTEGER,
8
+ last_used INTEGER
9
+ );
5
10
  create_expiry_index: >
6
- CREATE index if not exists expiry_index on data (expires_in)
11
+ CREATE INDEX IF NOT EXISTS expiry_index ON data (expires_in);
7
12
  create_last_used_index: >
8
- CREATE index if not exists last_used_index on data (last_used)
13
+ CREATE INDEX IF NOT EXISTS last_used_index ON data (last_used);
9
14
 
10
15
  stmts:
11
- pruner: DELETE FROM data WHERE expires_in <= $1
12
- extra_pruner: DELETE FROM data WHERE id IN (SELECT id FROM data ORDER BY last_used ASC LIMIT (SELECT CAST((count(*) * $1) AS int) FROM data))
13
- limited_pruner: DELETE FROM data WHERE id IN (SELECT id FROM data ORDER BY last_used asc limit $1)
14
- toucher: UPDATE data SET last_used = unixepoch('now') WHERE id = $1
16
+ pruner: >
17
+ DELETE FROM data WHERE expires_in <= $1;
18
+
19
+ extra_pruner: >
20
+ DELETE FROM data WHERE id IN (
21
+ SELECT id
22
+ FROM data
23
+ ORDER BY last_used ASC
24
+ LIMIT (
25
+ SELECT CAST((count(*) * $1) AS int)
26
+ FROM data
27
+ )
28
+ );
29
+
30
+ limited_pruner: >
31
+ DELETE FROM data
32
+ WHERE id IN (
33
+ SELECT id
34
+ FROM data
35
+ ORDER BY last_used ASC
36
+ LIMIT $1
37
+ );
38
+
39
+ toucher: >
40
+ UPDATE data
41
+ SET last_used = unixepoch('now')
42
+ WHERE id = $1;
43
+
15
44
  setter: >
16
- INSERT into data (id, value, expires_in, last_used) VALUES ($1, $2, unixepoch('now') + $3, unixepoch('now')) on conflict(id) do
17
- UPDATE SET value = excluded.value, last_used = excluded.last_used, expires_in = excluded.expires_in
18
- inserter: >
19
- INSERT into data (id, value, expires_in, last_used) VALUES ($1, $2, unixepoch('now') + $3, unixepoch('now')) on conflict(id) do
20
- UPDATE SET value = excluded.value, last_used = excluded.last_used, expires_in = excluded.expires_in WHERE id = $1 and expires_in <= unixepoch('now')
21
- finder: SELECT id FROM data WHERE id = $1
22
- getter: SELECT id, value, expires_in FROM data WHERE id = $1
23
- deleter: delete FROM data WHERE id = $1 returning value
24
- incrementer: >
25
- INSERT into data (id, value, expires_in, last_used) VALUES ($1, $2, unixepoch('now') + $3, unixepoch('now')) on conflict(id) do
26
- UPDATE SET value = cast(value AS int) + cast(excluded.value as int), last_used = excluded.last_used, expires_in = excluded.expires_in
27
- counter: SELECT count(*) FROM data
28
- sizer: SELECT size.page_size * count.page_count FROM pragma_page_size() AS size, pragma_page_count() AS count
45
+ INSERT INTO data (id, value, expires_in, last_used)
46
+ VALUES ($1, $2, unixepoch('now') + $3, unixepoch('now'))
47
+ ON CONFLICT(id) DO UPDATE
48
+ SET
49
+ value = EXCLUDED.value,
50
+ last_used = EXCLUDED.last_used,
51
+ expires_in = EXCLUDED.expires_in;
52
+
53
+ inserter: >
54
+ INSERT INTO data (id, value, expires_in, last_used)
55
+ VALUES ($1, $2, unixepoch('now') + $3, unixepoch('now'))
56
+ ON CONFLICT(id) DO UPDATE
57
+ SET
58
+ value = EXCLUDED.value,
59
+ last_used = EXCLUDED.last_used,
60
+ expires_in = EXCLUDED.expires_in
61
+ WHERE id = $1
62
+ AND expires_in <= unixepoch('now');
63
+
64
+ finder: >
65
+ SELECT id FROM data WHERE id = $1;
66
+
67
+ getter: >
68
+ SELECT id, value, expires_in FROM data WHERE id = $1;
69
+
70
+ deleter: >
71
+ delete FROM data WHERE id = $1 RETURNING value;
72
+
73
+ incrementer: >
74
+ INSERT INTO data (id, value, expires_in, last_used)
75
+ VALUES ($1, $2, unixepoch('now') + $3, unixepoch('now'))
76
+ ON CONFLICT(id) DO UPDATE
77
+ SET
78
+ value = CAST(value AS int) + CAST(EXCLUDED.value AS int),
79
+ last_used = EXCLUDED.last_used,
80
+ expires_in = EXCLUDED.expires_in;
81
+
82
+ counter: >
83
+ SELECT count(*) FROM data;
84
+
85
+ sizer: >
86
+ SELECT size.page_size * count.page_count
87
+ FROM pragma_page_size() AS size, pragma_page_count() AS count;
@@ -1,6 +1,6 @@
1
1
  data: 123
2
2
 
3
- production:
3
+ production:
4
4
  data: abc
5
5
 
6
6
  developments: