litestack 0.4.2 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.standard.yml +3 -1
  3. data/BENCHMARKS.md +3 -3
  4. data/CAVEATS.md +20 -0
  5. data/CHANGELOG.md +40 -1
  6. data/FILESYSTEMS.md +55 -0
  7. data/Gemfile +2 -0
  8. data/README.md +8 -4
  9. data/ROADMAP.md +6 -6
  10. data/assets/litestack_advantage.png +0 -0
  11. data/bench/bench.rb +2 -0
  12. data/bench/bench_cache_rails.rb +33 -2
  13. data/bench/bench_cache_raw.rb +36 -12
  14. data/bench/bench_jobs_rails.rb +3 -3
  15. data/bench/bench_jobs_raw.rb +3 -3
  16. data/bin/liteboard +16 -13
  17. data/gemfiles/rails70.gemfile +5 -0
  18. data/gemfiles/rails71.gemfile +5 -0
  19. data/gemfiles/rails71.gemfile.lock +264 -0
  20. data/lib/active_job/queue_adapters/litejob_adapter.rb +11 -3
  21. data/lib/active_record/connection_adapters/litedb_adapter.rb +8 -0
  22. data/lib/active_support/cache/litecache.rb +40 -7
  23. data/lib/generators/litestack/install/install_generator.rb +2 -2
  24. data/lib/generators/litestack/install/templates/cable.yml +0 -3
  25. data/lib/litestack/liteboard/liteboard.rb +15 -19
  26. data/lib/litestack/liteboard/views/litecable.erb +1 -1
  27. data/lib/litestack/litecable.rb +1 -1
  28. data/lib/litestack/litecache.rb +51 -19
  29. data/lib/litestack/litecache.sql.yml +7 -5
  30. data/lib/litestack/litedb.rb +5 -1
  31. data/lib/litestack/litejob.rb +1 -1
  32. data/lib/litestack/litejobqueue.rb +24 -14
  33. data/lib/litestack/litemetric.rb +7 -6
  34. data/lib/litestack/litemetric.sql.yml +1 -1
  35. data/lib/litestack/litemetric_collector.sql.yml +1 -1
  36. data/lib/litestack/litequeue.rb +17 -2
  37. data/lib/litestack/litequeue.sql.yml +38 -5
  38. data/lib/litestack/litescheduler.rb +9 -4
  39. data/lib/litestack/litesearch/index.rb +11 -10
  40. data/lib/litestack/litesearch/model.rb +61 -3
  41. data/lib/litestack/litesearch/schema.rb +7 -2
  42. data/lib/litestack/litesearch/schema_adapters/backed_adapter.rb +69 -25
  43. data/lib/litestack/litesearch/schema_adapters.rb +4 -4
  44. data/lib/litestack/litesearch.rb +2 -2
  45. data/lib/litestack/litesupport.rb +9 -7
  46. data/lib/litestack/railtie.rb +4 -2
  47. data/lib/litestack/version.rb +1 -1
  48. data/lib/litestack.rb +15 -15
  49. data/lib/railties/rails/commands/dbconsole.rb +5 -5
  50. data/lib/sequel/adapters/litedb.rb +9 -1
  51. data/lib/sequel/adapters/shared/litedb.rb +2 -2
  52. data/scripts/build_metrics.rb +2 -2
  53. data/scripts/test_cable.rb +1 -1
  54. metadata +105 -56
  55. data/Gemfile.lock +0 -92
@@ -0,0 +1,264 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ litestack (0.4.4)
5
+ erubi
6
+ oj
7
+ rack
8
+ rackup
9
+ sqlite3
10
+ tilt
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ actioncable (7.1.3.2)
16
+ actionpack (= 7.1.3.2)
17
+ activesupport (= 7.1.3.2)
18
+ nio4r (~> 2.0)
19
+ websocket-driver (>= 0.6.1)
20
+ zeitwerk (~> 2.6)
21
+ actionmailbox (7.1.3.2)
22
+ actionpack (= 7.1.3.2)
23
+ activejob (= 7.1.3.2)
24
+ activerecord (= 7.1.3.2)
25
+ activestorage (= 7.1.3.2)
26
+ activesupport (= 7.1.3.2)
27
+ mail (>= 2.7.1)
28
+ net-imap
29
+ net-pop
30
+ net-smtp
31
+ actionmailer (7.1.3.2)
32
+ actionpack (= 7.1.3.2)
33
+ actionview (= 7.1.3.2)
34
+ activejob (= 7.1.3.2)
35
+ activesupport (= 7.1.3.2)
36
+ mail (~> 2.5, >= 2.5.4)
37
+ net-imap
38
+ net-pop
39
+ net-smtp
40
+ rails-dom-testing (~> 2.2)
41
+ actionpack (7.1.3.2)
42
+ actionview (= 7.1.3.2)
43
+ activesupport (= 7.1.3.2)
44
+ nokogiri (>= 1.8.5)
45
+ racc
46
+ rack (>= 2.2.4)
47
+ rack-session (>= 1.0.1)
48
+ rack-test (>= 0.6.3)
49
+ rails-dom-testing (~> 2.2)
50
+ rails-html-sanitizer (~> 1.6)
51
+ actiontext (7.1.3.2)
52
+ actionpack (= 7.1.3.2)
53
+ activerecord (= 7.1.3.2)
54
+ activestorage (= 7.1.3.2)
55
+ activesupport (= 7.1.3.2)
56
+ globalid (>= 0.6.0)
57
+ nokogiri (>= 1.8.5)
58
+ actionview (7.1.3.2)
59
+ activesupport (= 7.1.3.2)
60
+ builder (~> 3.1)
61
+ erubi (~> 1.11)
62
+ rails-dom-testing (~> 2.2)
63
+ rails-html-sanitizer (~> 1.6)
64
+ activejob (7.1.3.2)
65
+ activesupport (= 7.1.3.2)
66
+ globalid (>= 0.3.6)
67
+ activemodel (7.1.3.2)
68
+ activesupport (= 7.1.3.2)
69
+ activerecord (7.1.3.2)
70
+ activemodel (= 7.1.3.2)
71
+ activesupport (= 7.1.3.2)
72
+ timeout (>= 0.4.0)
73
+ activestorage (7.1.3.2)
74
+ actionpack (= 7.1.3.2)
75
+ activejob (= 7.1.3.2)
76
+ activerecord (= 7.1.3.2)
77
+ activesupport (= 7.1.3.2)
78
+ marcel (~> 1.0)
79
+ activesupport (7.1.3.2)
80
+ base64
81
+ bigdecimal
82
+ concurrent-ruby (~> 1.0, >= 1.0.2)
83
+ connection_pool (>= 2.2.5)
84
+ drb
85
+ i18n (>= 1.6, < 2)
86
+ minitest (>= 5.1)
87
+ mutex_m
88
+ tzinfo (~> 2.0)
89
+ ast (2.4.2)
90
+ base64 (0.2.0)
91
+ bigdecimal (3.1.6)
92
+ builder (3.2.4)
93
+ concurrent-ruby (1.2.3)
94
+ connection_pool (2.4.1)
95
+ crass (1.0.6)
96
+ date (3.3.4)
97
+ docile (1.4.0)
98
+ drb (2.2.1)
99
+ erubi (1.12.0)
100
+ globalid (1.2.1)
101
+ activesupport (>= 6.1)
102
+ hanami-router (0.6.2)
103
+ hanami-utils (~> 0.7)
104
+ http_router (~> 0.11)
105
+ hanami-utils (0.9.2)
106
+ http_router (0.11.2)
107
+ rack (>= 1.0.0)
108
+ url_mount (~> 0.2.1)
109
+ i18n (1.14.1)
110
+ concurrent-ruby (~> 1.0)
111
+ io-console (0.7.2)
112
+ irb (1.11.2)
113
+ rdoc
114
+ reline (>= 0.4.2)
115
+ json (2.7.1)
116
+ language_server-protocol (3.17.0.3)
117
+ lint_roller (1.1.0)
118
+ loofah (2.22.0)
119
+ crass (~> 1.0.2)
120
+ nokogiri (>= 1.12.0)
121
+ mail (2.8.1)
122
+ mini_mime (>= 0.1.1)
123
+ net-imap
124
+ net-pop
125
+ net-smtp
126
+ marcel (1.0.4)
127
+ mini_mime (1.1.5)
128
+ minitest (5.22.2)
129
+ mutex_m (0.2.0)
130
+ net-imap (0.4.10)
131
+ date
132
+ net-protocol
133
+ net-pop (0.1.2)
134
+ net-protocol
135
+ net-protocol (0.2.2)
136
+ timeout
137
+ net-smtp (0.4.0.1)
138
+ net-protocol
139
+ nio4r (2.7.0)
140
+ nokogiri (1.16.2-arm64-darwin)
141
+ racc (~> 1.4)
142
+ oj (3.16.3)
143
+ bigdecimal (>= 3.0)
144
+ parallel (1.24.0)
145
+ parser (3.3.0.5)
146
+ ast (~> 2.4.1)
147
+ racc
148
+ psych (5.1.2)
149
+ stringio
150
+ racc (1.7.3)
151
+ rack (3.0.9.1)
152
+ rack-session (2.0.0)
153
+ rack (>= 3.0.0)
154
+ rack-test (2.1.0)
155
+ rack (>= 1.3)
156
+ rackup (2.1.0)
157
+ rack (>= 3)
158
+ webrick (~> 1.8)
159
+ rails (7.1.3.2)
160
+ actioncable (= 7.1.3.2)
161
+ actionmailbox (= 7.1.3.2)
162
+ actionmailer (= 7.1.3.2)
163
+ actionpack (= 7.1.3.2)
164
+ actiontext (= 7.1.3.2)
165
+ actionview (= 7.1.3.2)
166
+ activejob (= 7.1.3.2)
167
+ activemodel (= 7.1.3.2)
168
+ activerecord (= 7.1.3.2)
169
+ activestorage (= 7.1.3.2)
170
+ activesupport (= 7.1.3.2)
171
+ bundler (>= 1.15.0)
172
+ railties (= 7.1.3.2)
173
+ rails-dom-testing (2.2.0)
174
+ activesupport (>= 5.0.0)
175
+ minitest
176
+ nokogiri (>= 1.6)
177
+ rails-html-sanitizer (1.6.0)
178
+ loofah (~> 2.21)
179
+ nokogiri (~> 1.14)
180
+ railties (7.1.3.2)
181
+ actionpack (= 7.1.3.2)
182
+ activesupport (= 7.1.3.2)
183
+ irb
184
+ rackup (>= 1.0.0)
185
+ rake (>= 12.2)
186
+ thor (~> 1.0, >= 1.2.2)
187
+ zeitwerk (~> 2.6)
188
+ rainbow (3.1.1)
189
+ rake (13.1.0)
190
+ rdoc (6.6.2)
191
+ psych (>= 4.0.0)
192
+ regexp_parser (2.9.0)
193
+ reline (0.4.3)
194
+ io-console (~> 0.5)
195
+ rexml (3.2.6)
196
+ rubocop (1.61.0)
197
+ json (~> 2.3)
198
+ language_server-protocol (>= 3.17.0)
199
+ parallel (~> 1.10)
200
+ parser (>= 3.3.0.2)
201
+ rainbow (>= 2.2.2, < 4.0)
202
+ regexp_parser (>= 1.8, < 3.0)
203
+ rexml (>= 3.2.5, < 4.0)
204
+ rubocop-ast (>= 1.30.0, < 2.0)
205
+ ruby-progressbar (~> 1.7)
206
+ unicode-display_width (>= 2.4.0, < 3.0)
207
+ rubocop-ast (1.31.1)
208
+ parser (>= 3.3.0.4)
209
+ rubocop-performance (1.20.2)
210
+ rubocop (>= 1.48.1, < 2.0)
211
+ rubocop-ast (>= 1.30.0, < 2.0)
212
+ ruby-progressbar (1.13.0)
213
+ sequel (5.78.0)
214
+ bigdecimal
215
+ simplecov (0.22.0)
216
+ docile (~> 1.1)
217
+ simplecov-html (~> 0.11)
218
+ simplecov_json_formatter (~> 0.1)
219
+ simplecov-html (0.12.3)
220
+ simplecov_json_formatter (0.1.4)
221
+ sqlite3 (1.7.2-arm64-darwin)
222
+ standard (1.34.0)
223
+ language_server-protocol (~> 3.17.0.2)
224
+ lint_roller (~> 1.0)
225
+ rubocop (~> 1.60)
226
+ standard-custom (~> 1.0.0)
227
+ standard-performance (~> 1.3)
228
+ standard-custom (1.0.2)
229
+ lint_roller (~> 1.0)
230
+ rubocop (~> 1.50)
231
+ standard-performance (1.3.1)
232
+ lint_roller (~> 1.1)
233
+ rubocop-performance (~> 1.20.2)
234
+ stringio (3.1.0)
235
+ thor (1.3.1)
236
+ tilt (2.3.0)
237
+ timeout (0.4.1)
238
+ tzinfo (2.0.6)
239
+ concurrent-ruby (~> 1.0)
240
+ unicode-display_width (2.5.0)
241
+ url_mount (0.2.1)
242
+ rack
243
+ webrick (1.8.1)
244
+ websocket-driver (0.7.6)
245
+ websocket-extensions (>= 0.1.0)
246
+ websocket-extensions (0.1.5)
247
+ zeitwerk (2.6.13)
248
+
249
+ PLATFORMS
250
+ arm64-darwin
251
+
252
+ DEPENDENCIES
253
+ litestack!
254
+ minitest
255
+ rack (~> 3.0)
256
+ rails (~> 7.1)
257
+ railties
258
+ rake
259
+ sequel
260
+ simplecov
261
+ standard
262
+
263
+ BUNDLED WITH
264
+ 2.5.3
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../../litestack/litejob"
4
+ require "active_support"
4
5
  require "active_support/core_ext/enumerable"
6
+ require "active_support/core_ext/numeric/time"
5
7
  require "active_support/core_ext/array/access"
6
8
  require "active_job"
7
9
 
@@ -18,20 +20,26 @@ module ActiveJob
18
20
  # Job.options = DEFAULT_OPTIONS.merge(options)
19
21
  end
20
22
 
23
+ def enqueue_after_transaction_commit?
24
+ Job.options[:enqueue_after_transaction_commit]
25
+ end
26
+
21
27
  def enqueue(job) # :nodoc:
22
28
  Job.queue = job.queue_name
23
29
  Job.perform_async(job.serialize)
24
30
  end
25
31
 
26
- def enqueue_at(job, timestamp) # :nodoc:
32
+ def enqueue_at(job, time) # :nodoc:
33
+ time = time.from_now if time.respond_to?(:from_now) #is_a?(ActiveSupport::Duration)
27
34
  Job.queue = job.queue_name
28
- Job.perform_at(timestamp, job.serialize)
35
+ Job.perform_at(time, job.serialize)
29
36
  end
30
37
 
31
38
  class Job # :nodoc:
32
39
  DEFAULT_OPTIONS = {
33
40
  config_path: "./config/litejob.yml",
34
- logger: nil # Rails performs its logging already
41
+ logger: nil, # Rails performs its logging already
42
+ enqueue_after_transaction_commit: true
35
43
  }
36
44
 
37
45
  include ::Litejob
@@ -1,4 +1,5 @@
1
1
  require_relative "../../litestack/litedb"
2
+
2
3
  require "active_record"
3
4
  require "active_record/connection_adapters/sqlite3_adapter"
4
5
  require "active_record/tasks/sqlite_database_tasks"
@@ -90,3 +91,10 @@ module ActiveRecord
90
91
  end
91
92
  end
92
93
  end
94
+
95
+ ActiveRecord::ConnectionAdapters.register(
96
+ "litedb", "ActiveRecord::ConnectionAdapters::LitedbAdapter",
97
+ "active_record/connection_adapters/litedb_adapter"
98
+ ) if ActiveRecord::ConnectionAdapters.respond_to?(:register)
99
+
100
+
@@ -1,14 +1,16 @@
1
- require "delegate"
2
- require "active_support/core_ext/enumerable"
3
- require "active_support/core_ext/array/extract_options"
1
+ require "active_support"
4
2
  require "active_support/core_ext/numeric/time"
5
3
  require "active_support/cache"
4
+
6
5
  require_relative "../../litestack/litecache"
7
6
 
8
7
  module ActiveSupport
9
8
  module Cache
9
+
10
+ self.format_version = 7.0
11
+
10
12
  class Litecache < Store
11
- prepend Strategy::LocalCache
13
+ # prepend Strategy::LocalCache
12
14
 
13
15
  def self.supports_cache_versioning?
14
16
  true
@@ -25,17 +27,22 @@ module ActiveSupport
25
27
  options = merged_options(options)
26
28
  # todo: fix me
27
29
  # this is currently a hack to avoid dealing with Rails cache encoding and decoding
30
+ # and it can result in a race condition as it stands
28
31
  # @cache.transaction(:immediate) do
32
+ # currently transactions are not compatible with acquiring connections
33
+ # this needs fixing by storing the connection to the context once acquired
29
34
  if (value = read(key, options))
30
35
  value = value.to_i + amount
31
36
  write(key, value, options)
37
+ else
38
+ write(key, amount, options)
32
39
  end
33
40
  # end
34
41
  end
35
42
 
36
43
  def decrement(key, amount = 1, options = nil)
37
44
  options = merged_options(options)
38
- increment(key, -1 * amount, options[:expires_in])
45
+ increment(key, -1 * amount, options)
39
46
  end
40
47
 
41
48
  def prune(limit = nil, time = nil)
@@ -46,7 +53,7 @@ module ActiveSupport
46
53
  @cache.prune(limit)
47
54
  end
48
55
 
49
- def clear
56
+ def clear(options = nil)
50
57
  @cache.clear
51
58
  end
52
59
 
@@ -68,18 +75,44 @@ module ActiveSupport
68
75
 
69
76
  private
70
77
 
78
+ def serialize_entries(entry, **options)
79
+ Marshal.dump(entry)
80
+ end
81
+
82
+ def deserialize_entries(entry)
83
+ Marshal.load(entry.to_s)
84
+ end
85
+
71
86
  # Read an entry from the cache.
72
87
  def read_entry(key, **options)
73
88
  deserialize_entry(@cache.get(key))
74
89
  end
75
90
 
91
+ def read_multi_entries(names, **options)
92
+ results = {}
93
+ return results if names == []
94
+ rs = @cache.get_multi(*names.flatten)
95
+ rs.each_pair { |k, v| results[k] = deserialize_entry(v).value }
96
+ results
97
+ end
98
+
76
99
  # Write an entry to the cache.
77
100
  def write_entry(key, entry, **options)
78
101
  write_serialized_entry(key, serialize_entry(entry, **options), **options)
79
102
  end
80
103
 
104
+ def write_multi_entries(entries, **options)
105
+ return if entries.empty?
106
+ entries.each_pair { |k, v| entries[k] = serialize_entry(v, **options) }
107
+ expires_in = options[:expires_in].to_i if options[:expires_in]
108
+ if options[:race_condition_ttl] && expires_in > 0 && !options[:raw]
109
+ expires_in += 5.minutes
110
+ end
111
+ @cache.set_multi(entries, expires_in)
112
+ end
113
+
81
114
  def write_serialized_entry(key, payload, **options)
82
- expires_in = options[:expires_in].to_i
115
+ expires_in = options[:expires_in].to_i if options[:expires_in]
83
116
  if options[:race_condition_ttl] && expires_in > 0 && !options[:raw]
84
117
  expires_in += 5.minutes
85
118
  end
@@ -1,10 +1,10 @@
1
1
  class Litestack::InstallGenerator < Rails::Generators::Base
2
2
  source_root File.expand_path("templates", __dir__)
3
3
 
4
- # Force copy configuratioon files so Rails installs don't ask questions
4
+ # Force copy configuration files so Rails installs don't ask questions
5
5
  # that less experienced people might not understand. The more Sr folks.
6
6
  # will know to check git to look at what changed.
7
- def modify_database_adapater
7
+ def modify_database_adapter
8
8
  copy_file "database.yml", "config/database.yml", force: true
9
9
  end
10
10
 
@@ -4,8 +4,5 @@ development:
4
4
  test:
5
5
  adapter: test
6
6
 
7
- staging:
8
- adapter: litecable
9
-
10
7
  production:
11
8
  adapter: litecable
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "hanami/router"
3
+ require "rack"
4
4
  require "tilt"
5
5
  require "erubi"
6
6
 
@@ -11,37 +11,32 @@ class Liteboard
11
11
  @@resolutions = {"minute" => [300, 12], "hour" => [3600, 24], "day" => [3600 * 24, 7], "week" => [3600 * 24 * 7, 53], "year" => [3600 * 24 * 365, 100]}
12
12
  @@res_mapping = {"hour" => "minute", "day" => "hour", "week" => "day", "year" => "week"}
13
13
  @@templates = {}
14
- @@app = Hanami::Router.new do
15
- get "/", to: ->(env) do
14
+ @@app = proc do |env|
15
+ case path = env["PATH_INFO"]
16
+ when "/"
16
17
  Liteboard.new(env).call(:index)
17
- end
18
-
19
- get "/topics/Litejob", to: ->(env) do
18
+ when "/topics/Litejob"
20
19
  Liteboard.new(env).call(:litejob)
21
- end
22
-
23
- get "/topics/Litecache", to: ->(env) do
20
+ when "/topics/Litecache"
24
21
  Liteboard.new(env).call(:litecache)
25
- end
26
-
27
- get "/topics/Litedb", to: ->(env) do
22
+ when "/topics/Litedb"
28
23
  Liteboard.new(env).call(:litedb)
29
- end
30
-
31
- get "/topics/Litecable", to: ->(env) do
24
+ when "/topics/Litecable"
32
25
  Liteboard.new(env).call(:litecable)
33
26
  end
27
+
34
28
  end
35
29
 
36
30
  def initialize(env)
37
31
  @env = env
38
- @params = @env["router.params"]
32
+ @req = Rack::Request.new(@env)
33
+ @params = @req.params
39
34
  @running = true
40
35
  @lm = Litemetric.instance
41
36
  end
42
37
 
43
38
  def params(key)
44
- URI.decode_uri_component(@params[key].to_s)
39
+ URI.decode_uri_component(@params[key.to_s].to_s)
45
40
  end
46
41
 
47
42
  def call(method)
@@ -227,9 +222,9 @@ class Liteboard
227
222
  @topic = "Litejob"
228
223
  @events = @lm.events_summaries(@topic, @resolution, @order, @dir, @search, @step * @count)
229
224
  @events.each do |event|
230
- data_points = @lm.event_data_points(@step, @count, @resolution, @topic, event["name"])
225
+ data_points = @lm.event_data_points(@step, @count, @resolution, @topic, event[:name])
231
226
  event["counts"] = data_points.collect { |r| [r["rtime"], r["rcount"] || 0] }
232
- event["values"] = data_points.collect { |r| [r["rtime"], r["rtotal"] || 0] }
227
+ event["values"] = data_points.collect { |r| [r["rtime"], (r["rtotal"] || 0.0)] }
233
228
  end
234
229
  @snapshot = read_snapshot(@topic)
235
230
  @size = begin
@@ -391,6 +386,7 @@ class Liteboard
391
386
  end
392
387
 
393
388
  def format(float)
389
+ float = float.round(3)
394
390
  string = float.to_s
395
391
  whole, decimal = string.split(".")
396
392
  whole = whole.chars.reverse.each_slice(3).map(&:join).join(",").reverse
@@ -61,7 +61,7 @@
61
61
  </div>
62
62
  <div class="card-body">
63
63
  <span class="hidden inlinestackedcolumn">
64
- <%=[["Time", "Recieved Count", "Delivered Count"]] + @messages_over_time.to_a%>
64
+ <%=[["Time", "Received Count", "Delivered Count"]] + @messages_over_time.to_a%>
65
65
  </span>
66
66
  </div>
67
67
  </div>
@@ -113,7 +113,7 @@ class Litecable
113
113
  def create_listener
114
114
  Litescheduler.spawn do
115
115
  while @running
116
- @last_fetched_id ||= (run_stmt(:last_id)[0][0] || 0)
116
+ @last_fetched_id ||= run_stmt(:last_id)[0][0] || 0
117
117
  run_stmt(:fetch, @last_fetched_id, @pid).to_a.each do |msg|
118
118
  @logger.info "RECEIVED #{msg}"
119
119
  @last_fetched_id = msg[0]
@@ -20,13 +20,13 @@ class Litecache
20
20
  include Litemetric::Measurable
21
21
 
22
22
  # the default options for the cache
23
- # can be overriden by passing new options in a hash
23
+ # can be overridden by passing new options in a hash
24
24
  # to Litecache.new
25
25
  # path: "./cache.db"
26
26
  # expiry: 60 * 60 * 24 * 30 -> one month default expiry if none is provided
27
27
  # size: 128 * 1024 * 1024 -> 128MB
28
28
  # mmap_size: 128 * 1024 * 1024 -> 128MB to be held in memory
29
- # min_size: 32 * 1024 -> 32MB
29
+ # min_size: 32 * 1024 -> 32KB
30
30
  # return_full_record: false -> only return the payload
31
31
  # sleep_interval: 1 -> 1 second of sleep between cleanup runs
32
32
 
@@ -39,7 +39,7 @@ class Litecache
39
39
  mmap_size: 128 * 1024 * 1024, # 128MB
40
40
  min_size: 8 * 1024 * 1024, # 16MB
41
41
  return_full_record: false, # only return the payload
42
- sleep_interval: 1, # 1 second
42
+ sleep_interval: 30, # 30 seconds
43
43
  metrics: false
44
44
  }
45
45
 
@@ -63,15 +63,15 @@ class Litecache
63
63
 
64
64
  def initialize(options = {})
65
65
  options[:size] = DEFAULT_OPTIONS[:min_size] if options[:size] && options[:size] < DEFAULT_OPTIONS[:min_size]
66
- @last_visited = {}
67
66
  init(options)
67
+ @expires_in = @options[:expiry] || 60 * 60 * 24 * 30
68
68
  collect_metrics if @options[:metrics]
69
69
  end
70
70
 
71
71
  # add a key, value pair to the cache, with an optional expiry value (number of seconds)
72
72
  def set(key, value, expires_in = nil)
73
73
  key = key.to_s
74
- expires_in = @options[:expires_in] if expires_in.nil? || expires_in.zero?
74
+ expires_in ||= @expires_in
75
75
  @conn.acquire do |cache|
76
76
  cache.stmts[:setter].execute!(key, value, expires_in)
77
77
  capture(:set, key)
@@ -83,10 +83,27 @@ class Litecache
83
83
  true
84
84
  end
85
85
 
86
+ # set multiple keys and values in one shot set_multi({k1: v1, k2: v2, ... })
87
+ def set_multi(keys_and_values, expires_in = nil)
88
+ expires_in ||= @expires_in
89
+ transaction do |conn|
90
+ keys_and_values.each_pair do |k, v|
91
+ key = k.to_s
92
+ conn.stmts[:setter].execute!(key, v, expires_in)
93
+ capture(:set, key)
94
+ rescue SQLite3::FullException
95
+ conn.stmts[:extra_pruner].execute!(0.2)
96
+ conn.execute("vacuum")
97
+ retry
98
+ end
99
+ end
100
+ true
101
+ end
102
+
86
103
  # add a key, value pair to the cache, but only if the key doesn't exist, with an optional expiry value (number of seconds)
87
104
  def set_unless_exists(key, value, expires_in = nil)
88
105
  key = key.to_s
89
- expires_in = @options[:expires_in] if expires_in.nil? || expires_in.zero?
106
+ expires_in ||= @expires_in
90
107
  changes = 0
91
108
  @conn.acquire do |cache|
92
109
  cache.transaction(:immediate) do
@@ -107,7 +124,6 @@ class Litecache
107
124
  def get(key)
108
125
  key = key.to_s
109
126
  if (record = @conn.acquire { |cache| cache.stmts[:getter].execute!(key)[0] })
110
- @last_visited[key] = true
111
127
  capture(:get, key, 1)
112
128
  return record[1]
113
129
  end
@@ -115,6 +131,24 @@ class Litecache
115
131
  nil
116
132
  end
117
133
 
134
+ # get multiple values by their keys, a hash with values corresponding to the keys
135
+ # is returned,
136
+ def get_multi(*keys)
137
+ results = {}
138
+ transaction(:deferred) do |conn|
139
+ keys.length.times do |i|
140
+ key = keys[i].to_s
141
+ if (record = conn.stmts[:getter].execute!(key)[0])
142
+ results[keys[i]] = record[1] # use the original key format
143
+ capture(:get, key, 1)
144
+ else
145
+ capture(:get, key, 0)
146
+ end
147
+ end
148
+ end
149
+ results
150
+ end
151
+
118
152
  # delete a key, value pair from the cache
119
153
  def delete(key)
120
154
  changes = 0
@@ -126,13 +160,13 @@ class Litecache
126
160
  end
127
161
 
128
162
  # increment an integer value by amount, optionally add an expiry value (in seconds)
129
- def increment(key, amount, expires_in = nil)
163
+ def increment(key, amount = 1, expires_in = nil)
130
164
  expires_in ||= @expires_in
131
165
  @conn.acquire { |cache| cache.stmts[:incrementer].execute!(key.to_s, amount, expires_in) }
132
166
  end
133
167
 
134
168
  # decrement an integer value by amount, optionally add an expiry value (in seconds)
135
- def decrement(key, amount, expires_in = nil)
169
+ def decrement(key, amount = 1, expires_in = nil)
136
170
  increment(key, -amount, expires_in)
137
171
  end
138
172
 
@@ -189,11 +223,14 @@ class Litecache
189
223
  end
190
224
 
191
225
  # low level access to SQLite transactions, use with caution
192
- def transaction(mode, acquire = true)
193
- return cache.transaction(mode) { yield } unless acquire
226
+ def transaction(mode = :immediate)
194
227
  @conn.acquire do |cache|
195
- cache.transaction(mode) do
228
+ if cache.transaction_active?
196
229
  yield
230
+ else
231
+ cache.transaction(mode) do
232
+ yield cache
233
+ end
197
234
  end
198
235
  end
199
236
  end
@@ -202,19 +239,14 @@ class Litecache
202
239
 
203
240
  def setup
204
241
  super # create connection
205
- @bgthread = spawn_worker # create backgroud pruner thread
242
+ @bgthread = spawn_worker # create background pruner thread
206
243
  end
207
244
 
208
245
  def spawn_worker
209
246
  Litescheduler.spawn do
210
247
  while @running
211
248
  @conn.acquire do |cache|
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
215
- end
216
- cache.stmts[:pruner].execute!
217
- end
249
+ cache.stmts[:pruner].execute!
218
250
  rescue SQLite3::BusyException
219
251
  retry
220
252
  rescue SQLite3::FullException