litestack 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98a82dcbe7e4fe0b215e254fac5791127142fcbfd4a156b2509434f97b58aea9
4
- data.tar.gz: a914b64d6e031559b1bdca83a8198426a49005f56ca1d0b4da2a7c6cb2384ab7
3
+ metadata.gz: e2ac02213caf3525c40b676550a6daa24380f3012a5d4c8f8042cf39aa477593
4
+ data.tar.gz: 00d0f60e20bcba2cc22704d4e7159fe9b819920d313e7dc7d4e4e799420e95ca
5
5
  SHA512:
6
- metadata.gz: 72935d15fcced99bcc0da9cfb6c1ffcd43478a5456c073a3d8ab468a3e49f7c681bbdec861cb7c1e2c16659e0d11429e16d855be38fd30735448078977205dc9
7
- data.tar.gz: ac181213e66533926ce5d075e8256dcf2c5dfa727ff7becaa244bbf053f354c564f29a294f4b2b8c1436320d41e9dc4e4697ba9e3169324bc798c60d8bfc762a
6
+ metadata.gz: 7c9c726aecbfd99ad73d82c50c285178becefb4792ece0672b55dfede10a47f0563700a691398373f372ca829491c13e0332a81b9b075f9a0435ae55ac138a1c
7
+ data.tar.gz: b4a28527632a3065d0deb8eadb5c806d260edfd89aa9b3f00ebbc20a2aa63ebfb7df907e7bd0a500f9e368ecd91b8967789b1f056632ef5bc73502dbbe1524c9
data/CHANGELOG.md CHANGED
@@ -1,7 +1,20 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.3] - 2024-02-15
4
+
5
+ - [View Diff](https://github.com/oldmoe/litestack/compare/v0.4.1...master)
6
+ - Add "sequel" as a development dependency
7
+ - Diff links in CHANGELOG (thanks Weston Ganger)
8
+ - Fix deamonize type in liteboard (thanks Julian Rubisch)
9
+ - Better Litecache schema (streamlined numeric value support)
10
+ - Support for set_multi and get_multi in Litecache (read_multi and write_multi support for Rails Cache store)
11
+ - More tests written for Litecache and Rails Litecache store
12
+ - Experimenting with removing the Rails LocalCache as it doesn't show enough improvement in perfromance to compensate for the memory overhead
13
+ - Switch Litecache to a FIFO eviction model vs LRU (thanks Julian Rubisch and Stephen Margheim)
14
+
3
15
  ## [0.4.2] - 2023-11-11
4
16
 
17
+ - [View Diff](https://github.com/oldmoe/litestack/compare/v0.4.1...v0.4.2)
5
18
  - Add similarity search support for Litesearch (works best for non-trigram indexes)
6
19
  - Enable similarity search for ActiveRecord and Sequel models
7
20
  - Fix Litesearch tests
@@ -13,16 +26,19 @@
13
26
 
14
27
  ## [0.4.1] - 2023-10-11
15
28
 
29
+ - [View Diff](https://github.com/oldmoe/litestack/compare/v0.4.0...v0.4.1)
16
30
  - Add missing Litesearch::Model dependency
17
31
 
18
32
  ## [0.4.0] - 2023-10-11
19
33
 
34
+ - [View Diff](https://github.com/oldmoe/litestack/compare/v0.3.0...v0.4.0)
20
35
  - Introduced Litesearch, dynamic & fast full text search capability for Litedb
21
36
  - ActiveRecord and Sequel integration for Litesearch
22
37
  - Slight improvement to the Sequel Litedb adapter for better Litesearch integration
23
38
 
24
39
  ## [0.3.0] - 2023-08-13
25
40
 
41
+ - [View Diff](https://github.com/oldmoe/litestack/compare/v0.2.6...v0.3.0)
26
42
  - Reworked the Litecable thread safety model
27
43
  - Fixed multiple litejob bugs (thanks Stephen Margheim)
28
44
  - Fixed Railtie dependency (thanks Marco Roth)
@@ -32,6 +48,7 @@
32
48
 
33
49
  ## [0.2.6] - 2023-07-16
34
50
 
51
+ - [View Diff](https://github.com/oldmoe/litestack/compare/v0.2.3...v0.2.6)
35
52
  - Much improved database location setting (thanks Brad Gessler)
36
53
  - A Rails generator for better Rails Litestack defaults (thanks Brad Gessler)
37
54
  - Revamped Litemetric, now much faster and more accurate (still experimental)
@@ -39,19 +56,23 @@
39
56
 
40
57
  ## [0.2.3] - 2023-05-20
41
58
 
59
+ - [View Diff](https://github.com/oldmoe/litestack/compare/v0.2.2...v0.2.3)
42
60
  - Cut back on options defined in the Litejob Rails adapter
43
61
 
44
62
  ## [0.2.2] - 2023-05-18
45
63
 
64
+ - [View Diff](https://github.com/oldmoe/litestack/compare/v0.2.1...v0.2.2)
46
65
  - Fix default queue location in Litejob
47
66
 
48
67
 
49
68
  ## [0.2.1] - 2023-05-08
50
69
 
70
+ - [View Diff](https://github.com/oldmoe/litestack/compare/v0.2.0...v0.2.1)
51
71
  - Fix a race condition in Litecable
52
72
 
53
73
  ## [0.2.0] - 2023-05-08
54
74
 
75
+ - [View Diff](https://github.com/oldmoe/litestack/compare/v0.1.8...v0.2.0)
55
76
  - Litecable, a SQLite driver for ActionCable
56
77
  - Litemetric for metrics collection support (experimental, disabled by default)
57
78
  - New schema for Litejob, old jobs are auto-migrated
@@ -61,6 +82,7 @@
61
82
 
62
83
  ## [0.1.8] - 2023-03-08
63
84
 
85
+ - [View Diff](https://github.com/oldmoe/litestack/compare/v0.1.7...v0.1.8)
64
86
  - More code cleanups, more test coverage
65
87
  - Retry support for jobs in Litejob
66
88
  - Job storage and garbage collection for failed jobs
@@ -69,6 +91,7 @@
69
91
 
70
92
  ## [0.1.7] - 2023-03-05
71
93
 
94
+ - [View Diff](https://github.com/oldmoe/litestack/compare/v0.1.6...v0.1.7)
72
95
  - Code cleanup, removal of references to older name
73
96
  - Fix for the litedb rake tasks (thanks: netmute)
74
97
  - More fixes for the new concurrency model
@@ -76,6 +99,7 @@
76
99
 
77
100
  ## [0.1.6] - 2023-03-03
78
101
 
102
+ - [View Diff](https://github.com/oldmoe/litestack/compare/v0.1.0...v0.1.6)
79
103
  - Revamped the locking model, more robust, minimal performance hit
80
104
  - Introduced a new resource pooling class
81
105
  - Litecache and Litejob now use the resource pool
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ![litestack](https://github.com/oldmoe/litestack/blob/master/assets/litestack_logo_teal_large.png?raw=true)
2
2
 
3
+ <a href="https://badge.fury.io/rb/litestack" target="_blank"><img height="21" style='border:0px;height:21px;' border='0' src="https://badge.fury.io/rb/litestack.svg" alt="Gem Version"></a>
4
+ <a href='https://github.com/oldmoe/litestack/actions' target='_blank'><img src="https://github.com/oldmoe/litestack/actions/workflows/ruby.yml/badge.svg?branch=master" style="max-width:100%;" height='21' style='border:0px;height:21px;' border='0' alt="CI Status"></a>
5
+ <a href='https://rubygems.org/gems/litestack' target='_blank'><img height='21' style='border:0px;height:21px;' src='https://img.shields.io/gem/dt/litestack?color=brightgreen&label=Rubygems%20Downloads' border='0' alt='RubyGems Downloads' /></a>
6
+
3
7
  All your data infrastructure, in a gem!
4
8
 
5
9
  Litestack is a Ruby gem that provides both Ruby and Ruby on Rails applications an all-in-one solution for web application data infrastructure. It exploits the power and embeddedness of SQLite to deliver a full-fledged SQL database, a fast cache , a robust job queue, a reliable message broker, a full text search engine and a metrics platform all in a single package.
@@ -24,7 +28,7 @@ With litestack you only need to add a single gem to your app which would replace
24
28
  - Cache Server (e.g. Redis, Memcached)
25
29
  - Job Processor (e.g. Sidekiq, Goodjob)
26
30
  - Pubsub Server (e.g. Redis, PostgreSQL)
27
- - Fulltext Search Server (e.g. Elasticsearch, Mielisearch)
31
+ - Fulltext Search Server (e.g. Elasticsearch, Meilisearch)
28
32
 
29
33
  To make it even more efficient, litestack will detect the presence of Fiber based IO frameworks like Async (e.g. when you use the Falcon web server) or Polyphony. It will then switch its background workers for caches and queues to fibers (using the semantics of the existing framework). This is done transparently and will generally lead to lower CPU and memory utilization.
30
34
 
@@ -258,7 +262,7 @@ Book.search('author: writer').limit(1).all
258
262
  > ![litemetric](https://github.com/oldmoe/litestack/blob/master/assets/litemetric_logo_teal.png?raw=true)
259
263
 
260
264
  ### Litemetric
261
- Litestack comes with a mdoule that can collect useful metrics for its different components, in each component, you need to add the following to the respective .yml file (database.yml in case of Litedb)
265
+ Litestack comes with a module that can collect useful metrics for its different components, in each component, you need to add the following to the respective .yml file (database.yml in case of Litedb)
262
266
  ```yml
263
267
  metrics: true # default is false
264
268
  ```
@@ -2,7 +2,7 @@ require "active_support"
2
2
  require_relative "../lib/litestack"
3
3
  require_relative "./bench"
4
4
 
5
- cache = ActiveSupport::Cache::Litecache.new({path: "../db/rails_cache.db"})
5
+ cache = ActiveSupport::Cache::Litecache.new
6
6
 
7
7
  # can only use the lookup method when the gem is installed
8
8
  # cache = ActiveSupport::Cache.lookup_store(:litecache, {path: '../db/rails_cache.db'})
@@ -12,8 +12,10 @@ redis = ActiveSupport::Cache.lookup_store(:redis_cache_store, {})
12
12
  values = []
13
13
  keys = []
14
14
  count = 1000
15
+ payload_sizes = [10, 100, 1000, 10000]
16
+ #payload_sizes = [100]
15
17
 
16
- [10, 100, 1000, 10000].each do |size|
18
+ payload_sizes.each do |size|
17
19
  count.times do
18
20
  keys << random_str(10)
19
21
  values << random_str(size)
@@ -31,6 +33,21 @@ count = 1000
31
33
  redis.write(keys[i], values[i])
32
34
  end
33
35
 
36
+ puts "== Multi Writes =="
37
+ bench("litecache multi-writes", count/5) do |i|
38
+ idx = i * 5
39
+ payload = {}
40
+ 5.times {|j| payload[keys[idx + j]] = values[idx + j] }
41
+ cache.write_multi(payload)
42
+ end
43
+
44
+ bench("Redis multi-writes", count/5) do |i|
45
+ idx = i * 5
46
+ payload = {}
47
+ 5.times {|j| payload[keys[idx + j]] = values[idx + j] }
48
+ redis.write_multi(payload)
49
+ end
50
+
34
51
  puts "== Reads =="
35
52
  bench("litecache reads", count) do |i|
36
53
  cache.read(random_keys[i])
@@ -39,6 +56,23 @@ count = 1000
39
56
  bench("Redis reads", count) do |i|
40
57
  redis.read(random_keys[i])
41
58
  end
59
+
60
+ puts "== Multi Reads =="
61
+ bench("litecache multi-reads", count/5) do |i|
62
+ idx = i * 5
63
+ payload = []
64
+ 5.times {|j| payload << random_keys[idx+j]}
65
+ cache.read_multi(*payload)
66
+ end
67
+
68
+ bench("Redis multi-reads", count/5) do |i|
69
+ idx = i * 5
70
+ payload = []
71
+ 5.times {|j| payload << random_keys[idx+j]}
72
+ redis.read_multi(*payload)
73
+ end
74
+
75
+
42
76
  puts "=========================================================="
43
77
 
44
78
  keys = []
@@ -11,7 +11,7 @@ Fiber.scheduler.run
11
11
  require_relative "../lib/litestack/litecache"
12
12
  # require 'litestack'
13
13
 
14
- cache = Litecache.new({path: "../db/cache.db"}) # default settings
14
+ cache = Litecache.new #({path: "../db/cache.db"}) # default settings
15
15
  redis = Redis.new # default settings
16
16
 
17
17
  values = []
@@ -35,28 +35,49 @@ count.times { keys << random_str(10) }
35
35
  cache.set(keys[i], values[i])
36
36
  end
37
37
 
38
- # bench("file writes", count) do |i|
39
- # f = File.open("../files/#{keys[i]}.data", 'w+')
40
- # f.write(values[i])
41
- # f.close
42
- # end
43
-
44
38
  bench("Redis writes", count) do |i|
45
39
  redis.set(keys[i], values[i])
46
40
  end
47
41
 
42
+ puts "== Multi Writes =="
43
+ bench("litecache multi-writes", count/5) do |i|
44
+ idx = i * 5
45
+ payload = {}
46
+ 5.times {|j| payload[keys[idx + j]] = values[idx + j] }
47
+ cache.set_multi(payload)
48
+ end
49
+
50
+ bench("Redis multi-writes", count/5) do |i|
51
+ idx = i * 5
52
+ payload = []
53
+ 5.times {|j| payload << keys[idx + j]; payload << values[idx + j]}
54
+ redis.mset(*payload)
55
+ end
56
+
48
57
  puts "== Reads =="
49
58
  bench("litecache reads", count) do |i|
50
59
  cache.get(random_keys[i])
51
60
  end
52
61
 
53
- # bench("file reads", count) do |i|
54
- # data = File.read("../files/#{keys[i]}.data")
55
- # end
56
-
57
62
  bench("Redis reads", count) do |i|
58
63
  redis.get(random_keys[i])
59
64
  end
65
+
66
+ puts "== Multi Reads =="
67
+ bench("litecache multi-reads", count/5) do |i|
68
+ idx = i * 5
69
+ payload = []
70
+ 5.times {|j| payload << random_keys[idx+j]}
71
+ cache.get_multi(*payload)
72
+ end
73
+
74
+ bench("Redis multi-reads", count/5) do |i|
75
+ idx = i * 5
76
+ payload = []
77
+ 5.times {|j| payload << random_keys[idx+j]}
78
+ redis.mget(*payload)
79
+ end
80
+
60
81
  puts "=========================================================="
61
82
 
62
83
  values = []
data/bin/liteboard CHANGED
@@ -15,6 +15,7 @@ options = {
15
15
  Port: 9292,
16
16
  Host: 'localhost',
17
17
  environment: 'production',
18
+ pid: 'tmp/pids/liteboard.pid',
18
19
  quiet: false
19
20
  }
20
21
 
@@ -25,7 +26,7 @@ OptionParser.new do |parser|
25
26
  parser.on("-s", "--server SERVER", "use SERVER (e.g. puma/falcon/iodine)") { |v| options[:port] = v }
26
27
  parser.on("-H", "--host HOST", "listen on HOST (default: #{options[:Host]})") { |v| options[:Host] = v }
27
28
  parser.on("-p", "--port PORT", "use PORT (default: #{options[:Port]})") { |v| options[:Port] = v.to_i rescue options[:Port] }
28
- parser.on("-D", "--deamonize", "run in the background") { |v| options[:deamonize] = true }
29
+ parser.on("-D", "--daemonize", "run in the background") { |v| options[:daemonize] = true }
29
30
  parser.on("-E", "--env ENVIRONMENT", "which environment to use (default: #{options[:environment]})") { |v| options[:environment] = v }
30
31
  parser.on("-q", "--quiet", "turn off logging") { |v| options[:quiet] = true }
31
32
  parser.on("-h", "--help", "print this message") do
@@ -41,18 +41,16 @@ module ActiveRecord
41
41
  class LitedbAdapter < SQLite3Adapter
42
42
  ADAPTER_NAME = "litedb"
43
43
 
44
- class << self
45
- def dbconsole(config, options = {})
46
- args = []
44
+ def self.dbconsole(config, options = {})
45
+ args = []
47
46
 
48
- args << "-#{options[:mode]}" if options[:mode]
49
- args << "-header" if options[:header]
50
- args << File.expand_path(config.database, Rails.respond_to?(:root) ? Rails.root : nil)
47
+ args << "-#{options[:mode]}" if options[:mode]
48
+ args << "-header" if options[:header]
49
+ args << File.expand_path(config.database, Rails.respond_to?(:root) ? Rails.root : nil)
51
50
 
52
- find_cmd_and_exec("sqlite3", *args)
53
- end
51
+ self.find_cmd_and_exec("sqlite3", *args)
54
52
  end
55
-
53
+
56
54
  NATIVE_DATABASE_TYPES = {
57
55
  primary_key: "integer PRIMARY KEY NOT NULL",
58
56
  string: {name: "text"},
@@ -8,7 +8,7 @@ require_relative "../../litestack/litecache"
8
8
  module ActiveSupport
9
9
  module Cache
10
10
  class Litecache < Store
11
- prepend Strategy::LocalCache
11
+ #prepend Strategy::LocalCache
12
12
 
13
13
  def self.supports_cache_versioning?
14
14
  true
@@ -25,17 +25,22 @@ module ActiveSupport
25
25
  options = merged_options(options)
26
26
  # todo: fix me
27
27
  # this is currently a hack to avoid dealing with Rails cache encoding and decoding
28
- # @cache.transaction(:immediate) do
28
+ # and it can result in a race condition as it stands
29
+ # @cache.transaction(:immediate) do
30
+ # currently transactions are not compatible with acquiring connections
31
+ # this needs fixing by storing the connection to the context once acquired
29
32
  if (value = read(key, options))
30
33
  value = value.to_i + amount
31
34
  write(key, value, options)
35
+ else
36
+ write(key, amount, options)
32
37
  end
33
38
  # end
34
39
  end
35
40
 
36
41
  def decrement(key, amount = 1, options = nil)
37
42
  options = merged_options(options)
38
- increment(key, -1 * amount, options[:expires_in])
43
+ increment(key, -1 * amount, options)
39
44
  end
40
45
 
41
46
  def prune(limit = nil, time = nil)
@@ -46,7 +51,7 @@ module ActiveSupport
46
51
  @cache.prune(limit)
47
52
  end
48
53
 
49
- def clear
54
+ def clear(options=nil)
50
55
  @cache.clear
51
56
  end
52
57
 
@@ -73,10 +78,28 @@ module ActiveSupport
73
78
  deserialize_entry(@cache.get(key))
74
79
  end
75
80
 
81
+ def read_multi_entries(names, **options)
82
+ results = {}
83
+ return results if names == []
84
+ rs = @cache.get_multi(*names.flatten)
85
+ rs.each_pair{|k, v| results[k] = deserialize_entry(v).value }
86
+ results
87
+ end
88
+
76
89
  # Write an entry to the cache.
77
90
  def write_entry(key, entry, **options)
78
91
  write_serialized_entry(key, serialize_entry(entry, **options), **options)
79
92
  end
93
+
94
+ def write_multi_entries(entries, **options)
95
+ return if entries.empty?
96
+ entries.each_pair {|k,v| entries[k] = serialize_entry(v, **options)}
97
+ expires_in = options[:expires_in].to_i
98
+ if options[:race_condition_ttl] && expires_in > 0 && !options[:raw]
99
+ expires_in += 5.minutes
100
+ end
101
+ @cache.set_multi(entries, expires_in)
102
+ end
80
103
 
81
104
  def write_serialized_entry(key, payload, **options)
82
105
  expires_in = options[:expires_in].to_i
@@ -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
@@ -26,7 +26,7 @@ class Litecache
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)
@@ -82,11 +82,30 @@ class Litecache
82
82
  end
83
83
  true
84
84
  end
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
+ begin
92
+ key = k.to_s
93
+ conn.stmts[:setter].execute!(key, v, expires_in)
94
+ capture(:set, key)
95
+ rescue SQLite3::FullException
96
+ conn.stmts[:extra_pruner].execute!(0.2)
97
+ conn.execute("vacuum")
98
+ retry
99
+ end
100
+ end
101
+ end
102
+ true
103
+ end
85
104
 
86
105
  # 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
106
  def set_unless_exists(key, value, expires_in = nil)
88
107
  key = key.to_s
89
- expires_in = @options[:expires_in] if expires_in.nil? || expires_in.zero?
108
+ expires_in ||= @expires_in
90
109
  changes = 0
91
110
  @conn.acquire do |cache|
92
111
  cache.transaction(:immediate) do
@@ -107,13 +126,30 @@ class Litecache
107
126
  def get(key)
108
127
  key = key.to_s
109
128
  if (record = @conn.acquire { |cache| cache.stmts[:getter].execute!(key)[0] })
110
- @last_visited[key] = true
111
129
  capture(:get, key, 1)
112
130
  return record[1]
113
131
  end
114
132
  capture(:get, key, 0)
115
133
  nil
116
134
  end
135
+
136
+ # get multiple values by their keys, a hash with values corresponding to the keys
137
+ # is returned,
138
+ def get_multi(*keys)
139
+ results = {}
140
+ transaction(:deferred) do |conn|
141
+ keys.length.times do |i|
142
+ key = keys[i].to_s
143
+ if (record = conn.stmts[:getter].execute!(key)[0])
144
+ results[keys[i]] = record[1] # use the original key format
145
+ capture(:get, key, 1)
146
+ else
147
+ capture(:get, key, 0)
148
+ end
149
+ end
150
+ end
151
+ results
152
+ end
117
153
 
118
154
  # delete a key, value pair from the cache
119
155
  def delete(key)
@@ -127,7 +163,7 @@ class Litecache
127
163
 
128
164
  # increment an integer value by amount, optionally add an expiry value (in seconds)
129
165
  def increment(key, amount, expires_in = nil)
130
- expires_in ||= @expires_in
166
+ expires_in ||= @expires_in
131
167
  @conn.acquire { |cache| cache.stmts[:incrementer].execute!(key.to_s, amount, expires_in) }
132
168
  end
133
169
 
@@ -189,11 +225,14 @@ class Litecache
189
225
  end
190
226
 
191
227
  # low level access to SQLite transactions, use with caution
192
- def transaction(mode, acquire = true)
193
- return cache.transaction(mode) { yield } unless acquire
228
+ def transaction(mode=:immediate)
194
229
  @conn.acquire do |cache|
195
- cache.transaction(mode) do
230
+ if cache.transaction_active?
196
231
  yield
232
+ else
233
+ cache.transaction(mode) do
234
+ yield cache
235
+ end
197
236
  end
198
237
  end
199
238
  end
@@ -209,17 +248,12 @@ class Litecache
209
248
  Litescheduler.spawn do
210
249
  while @running
211
250
  @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
251
+ cache.stmts[:pruner].execute!
218
252
  rescue SQLite3::BusyException
219
253
  retry
220
254
  rescue SQLite3::FullException
221
255
  cache.stmts[:extra_pruner].execute!(0.2)
222
- rescue Exception # standard:disable Lint/RescueException
256
+ rescue Exception => e # standard:disable Lint/RescueException
223
257
  # database is closed
224
258
  end
225
259
  sleep @options[:sleep_interval]
@@ -3,10 +3,10 @@ schema:
3
3
  create_table_data: >
4
4
  CREATE TABLE IF NOT EXISTS data(
5
5
  id TEXT PRIMARY KEY,
6
- value TEXT,
6
+ value ANY,
7
7
  expires_in INTEGER,
8
8
  last_used INTEGER
9
- );
9
+ ) STRICT;
10
10
  create_expiry_index: >
11
11
  CREATE INDEX IF NOT EXISTS expiry_index ON data (expires_in);
12
12
  create_last_used_index: >
@@ -14,7 +14,7 @@ schema:
14
14
 
15
15
  stmts:
16
16
  pruner: >
17
- DELETE FROM data WHERE expires_in <= $1;
17
+ DELETE FROM data WHERE expires_in <= unixepoch('now');
18
18
 
19
19
  extra_pruner: >
20
20
  DELETE FROM data WHERE id IN (
@@ -49,7 +49,7 @@ stmts:
49
49
  value = EXCLUDED.value,
50
50
  last_used = EXCLUDED.last_used,
51
51
  expires_in = EXCLUDED.expires_in;
52
-
52
+
53
53
  inserter: >
54
54
  INSERT INTO data (id, value, expires_in, last_used)
55
55
  VALUES ($1, $2, unixepoch('now') + $3, unixepoch('now'))
@@ -65,7 +65,7 @@ stmts:
65
65
  SELECT id FROM data WHERE id = $1;
66
66
 
67
67
  getter: >
68
- SELECT id, value, expires_in FROM data WHERE id = $1;
68
+ SELECT id, value, expires_in FROM data WHERE id = $1 AND expires_in >= unixepoch('now');
69
69
 
70
70
  deleter: >
71
71
  delete FROM data WHERE id = $1 RETURNING value;
@@ -79,6 +79,8 @@ stmts:
79
79
  last_used = EXCLUDED.last_used,
80
80
  expires_in = EXCLUDED.expires_in;
81
81
 
82
+
83
+
82
84
  counter: >
83
85
  SELECT count(*) FROM data;
84
86
 
@@ -19,7 +19,7 @@ schema:
19
19
  created_at INTEGER DEFAULT((unixepoch()/300*300)) NOT NULL,
20
20
  resolution TEXT DEFAULT('minute') NOT NULL,
21
21
  PRIMARY KEY(resolution, created_at, topic, name, key)
22
- ) STRICT;
22
+ );
23
23
 
24
24
  create_topic_index_on_events: >
25
25
  CREATE INDEX events_topic_index ON events (resolution, created_at, topic) WHERE name = '___';
@@ -12,7 +12,7 @@ schema:
12
12
  created_at INTEGER DEFAULT((unixepoch()/300*300)) NOT NULL ON CONFLICT REPLACE,
13
13
  resolution TEXT DEFAULT('minute') NOT NULL,
14
14
  PRIMARY KEY(resolution, created_at, topic, name, key)
15
- ) STRICT;
15
+ );
16
16
 
17
17
  stmts:
18
18
  capture_event: >
@@ -29,7 +29,7 @@ module Litescheduler
29
29
  end
30
30
 
31
31
  def self.storage
32
- if backend == :fiber || backend == :poylphony
32
+ if fiber_backed?
33
33
  Fiber.current.storage
34
34
  else
35
35
  Thread.current
@@ -37,7 +37,7 @@ module Litescheduler
37
37
  end
38
38
 
39
39
  def self.current
40
- if backend == :fiber || backend == :poylphony
40
+ if fiber_backed?
41
41
  Fiber.current
42
42
  else
43
43
  Thread.current
@@ -64,7 +64,7 @@ module Litescheduler
64
64
  # they must send (true) as a parameter to this method
65
65
  # else it is a no-op for fibers
66
66
  def self.synchronize(fiber_sync = false, &block)
67
- if (backend == :fiber) || (backend == :polyphony)
67
+ if fiber_backed?
68
68
  yield # do nothing, just run the block as is
69
69
  else
70
70
  mutex.synchronize(&block)
@@ -72,7 +72,7 @@ module Litescheduler
72
72
  end
73
73
 
74
74
  def self.max_contexts
75
- return 50 if backend == :fiber || backend == :polyphony
75
+ return 50 if fiber_backed?
76
76
  5
77
77
  end
78
78
 
@@ -81,4 +81,10 @@ module Litescheduler
81
81
  # a single mutex per process (is that ok?)
82
82
  @@mutex ||= Mutex.new
83
83
  end
84
+
85
+ private
86
+
87
+ def self.fiber_backed?
88
+ backend == :fiber || backend == :polyphony
89
+ end
84
90
  end
@@ -196,7 +196,7 @@ module Litesupport
196
196
  end
197
197
 
198
198
  def setup
199
- @conn = create_pooled_connection
199
+ @conn = create_pooled_connection(@options[:connection_count])
200
200
  @logger = create_logger
201
201
  @running = true
202
202
  end
@@ -231,7 +231,8 @@ module Litesupport
231
231
  end
232
232
 
233
233
  def create_pooled_connection(count = 1)
234
- Litesupport::Pool.new(1) { create_connection }
234
+ count = 1 unless count and count.is_a? Integer
235
+ Litesupport::Pool.new(count) { create_connection }
235
236
  end
236
237
 
237
238
  # common db object options
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Litestack
4
- VERSION = "0.4.2"
4
+ VERSION = "0.4.3"
5
5
  end
@@ -27,7 +27,7 @@ module Rails
27
27
 
28
28
  args << db_config.database
29
29
 
30
- find_cmd_and_exec(["mysql", "mysql5"], *args)
30
+ ActiveRecord::Base.connection.class.find_cmd_and_exec(["mysql", "mysql5"], *args)
31
31
 
32
32
  when /^postgres|^postgis/
33
33
  ENV["PGUSER"] = config[:username] if config[:username]
@@ -38,7 +38,7 @@ module Rails
38
38
  ENV["PGSSLCERT"] = config[:sslcert].to_s if config[:sslcert]
39
39
  ENV["PGSSLKEY"] = config[:sslkey].to_s if config[:sslkey]
40
40
  ENV["PGSSLROOTCERT"] = config[:sslrootcert].to_s if config[:sslrootcert]
41
- find_cmd_and_exec("psql", db_config.database)
41
+ ActiveRecord::Base.connection.class.find_cmd_and_exec("psql", db_config.database)
42
42
 
43
43
  when "sqlite3", "litedb"
44
44
  args = []
@@ -47,7 +47,7 @@ module Rails
47
47
  args << "-header" if @options[:header]
48
48
  args << File.expand_path(db_config.database, Rails.respond_to?(:root) ? Rails.root : nil)
49
49
 
50
- find_cmd_and_exec("sqlite3", *args)
50
+ ActiveRecord::Base.connection.class.find_cmd_and_exec("sqlite3", *args)
51
51
 
52
52
  when "oracle", "oracle_enhanced"
53
53
  logon = ""
@@ -58,7 +58,7 @@ module Rails
58
58
  logon << "@#{db_config.database}" if db_config.database
59
59
  end
60
60
 
61
- find_cmd_and_exec("sqlplus", logon)
61
+ ActiveRecord::Base.connection.class.find_cmd_and_exec("sqlplus", logon)
62
62
 
63
63
  when "sqlserver"
64
64
  args = []
@@ -73,7 +73,7 @@ module Rails
73
73
  args += ["-S", host_arg]
74
74
  end
75
75
 
76
- find_cmd_and_exec("sqlcmd", *args)
76
+ ActiveRecord::Base.connection.class.find_cmd_and_exec("sqlcmd", *args)
77
77
 
78
78
  else
79
79
  abort "Unknown command-line client for #{db_config.database}."
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: litestack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mohamed Hassan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-11 00:00:00.000000000 Z
11
+ date: 2024-02-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sqlite3
@@ -178,6 +178,20 @@ dependencies:
178
178
  - - ">="
179
179
  - !ruby/object:Gem::Version
180
180
  version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: sequel
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
181
195
  description:
182
196
  email:
183
197
  - oldmoe@gmail.com
@@ -190,7 +204,6 @@ files:
190
204
  - BENCHMARKS.md
191
205
  - CHANGELOG.md
192
206
  - Gemfile
193
- - Gemfile.lock
194
207
  - LICENSE.txt
195
208
  - README.md
196
209
  - ROADMAP.md
@@ -296,7 +309,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
296
309
  - !ruby/object:Gem::Version
297
310
  version: '0'
298
311
  requirements: []
299
- rubygems_version: 3.4.20
312
+ rubygems_version: 3.5.3
300
313
  signing_key:
301
314
  specification_version: 4
302
315
  summary: A SQLite based, lightning fast, super efficient and dead simple to setup
data/Gemfile.lock DELETED
@@ -1,92 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- litestack (0.4.1)
5
- erubi
6
- hanami-router
7
- oj
8
- rack
9
- sqlite3
10
- tilt
11
-
12
- GEM
13
- remote: https://rubygems.org/
14
- specs:
15
- ast (2.4.2)
16
- docile (1.4.0)
17
- erubi (1.12.0)
18
- hanami-router (0.6.2)
19
- hanami-utils (~> 0.7)
20
- http_router (~> 0.11)
21
- hanami-utils (0.9.2)
22
- http_router (0.11.2)
23
- rack (>= 1.0.0)
24
- url_mount (~> 0.2.1)
25
- json (2.6.3)
26
- language_server-protocol (3.17.0.3)
27
- lint_roller (1.1.0)
28
- minitest (5.19.0)
29
- oj (3.15.1)
30
- parallel (1.23.0)
31
- parser (3.2.2.3)
32
- ast (~> 2.4.1)
33
- racc
34
- racc (1.7.1)
35
- rack (3.0.8)
36
- rainbow (3.1.1)
37
- rake (13.0.6)
38
- regexp_parser (2.8.1)
39
- rexml (3.2.6)
40
- rubocop (1.52.1)
41
- json (~> 2.3)
42
- parallel (~> 1.10)
43
- parser (>= 3.2.2.3)
44
- rainbow (>= 2.2.2, < 4.0)
45
- regexp_parser (>= 1.8, < 3.0)
46
- rexml (>= 3.2.5, < 4.0)
47
- rubocop-ast (>= 1.28.0, < 2.0)
48
- ruby-progressbar (~> 1.7)
49
- unicode-display_width (>= 2.4.0, < 3.0)
50
- rubocop-ast (1.29.0)
51
- parser (>= 3.2.1.0)
52
- rubocop-performance (1.18.0)
53
- rubocop (>= 1.7.0, < 2.0)
54
- rubocop-ast (>= 0.4.0)
55
- ruby-progressbar (1.13.0)
56
- simplecov (0.22.0)
57
- docile (~> 1.1)
58
- simplecov-html (~> 0.11)
59
- simplecov_json_formatter (~> 0.1)
60
- simplecov-html (0.12.3)
61
- simplecov_json_formatter (0.1.4)
62
- sqlite3 (1.6.3-arm64-darwin)
63
- standard (1.30.1)
64
- language_server-protocol (~> 3.17.0.2)
65
- lint_roller (~> 1.0)
66
- rubocop (~> 1.52.0)
67
- standard-custom (~> 1.0.0)
68
- standard-performance (~> 1.1.0)
69
- standard-custom (1.0.2)
70
- lint_roller (~> 1.0)
71
- rubocop (~> 1.50)
72
- standard-performance (1.1.2)
73
- lint_roller (~> 1.1)
74
- rubocop-performance (~> 1.18.0)
75
- tilt (2.2.0)
76
- unicode-display_width (2.4.2)
77
- url_mount (0.2.1)
78
- rack
79
-
80
- PLATFORMS
81
- arm64-darwin-21
82
-
83
- DEPENDENCIES
84
- litestack!
85
- minitest (~> 5.0)
86
- rack (~> 3.0)
87
- rake (~> 13.0)
88
- simplecov
89
- standard (~> 1.3)
90
-
91
- BUNDLED WITH
92
- 2.4.8