litestack 0.4.2 → 0.4.4

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 (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