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 +4 -4
- data/CHANGELOG.md +24 -0
- data/README.md +6 -2
- data/bench/bench_cache_rails.rb +36 -2
- data/bench/bench_cache_raw.rb +32 -11
- data/bin/liteboard +2 -1
- data/lib/active_record/connection_adapters/litedb_adapter.rb +7 -9
- data/lib/active_support/cache/litecache.rb +27 -4
- data/lib/generators/litestack/install/templates/cable.yml +0 -3
- data/lib/litestack/litecache.rb +51 -17
- data/lib/litestack/litecache.sql.yml +7 -5
- data/lib/litestack/litemetric.sql.yml +1 -1
- data/lib/litestack/litemetric_collector.sql.yml +1 -1
- data/lib/litestack/litescheduler.rb +10 -4
- data/lib/litestack/litesupport.rb +3 -2
- data/lib/litestack/version.rb +1 -1
- data/lib/railties/rails/commands/dbconsole.rb +5 -5
- metadata +17 -4
- data/Gemfile.lock +0 -92
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2ac02213caf3525c40b676550a6daa24380f3012a5d4c8f8042cf39aa477593
|
4
|
+
data.tar.gz: 00d0f60e20bcba2cc22704d4e7159fe9b819920d313e7dc7d4e4e799420e95ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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,
|
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
|
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
|
```
|
data/bench/bench_cache_rails.rb
CHANGED
@@ -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
|
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
|
-
|
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 = []
|
data/bench/bench_cache_raw.rb
CHANGED
@@ -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", "--
|
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
|
-
|
45
|
-
|
46
|
-
args = []
|
44
|
+
def self.dbconsole(config, options = {})
|
45
|
+
args = []
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
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
|
-
#
|
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
|
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
|
data/lib/litestack/litecache.rb
CHANGED
@@ -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 ->
|
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:
|
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
|
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
|
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
|
193
|
-
return cache.transaction(mode) { yield } unless acquire
|
228
|
+
def transaction(mode=:immediate)
|
194
229
|
@conn.acquire do |cache|
|
195
|
-
cache.
|
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.
|
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
|
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 <=
|
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
|
-
)
|
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 = '___';
|
@@ -29,7 +29,7 @@ module Litescheduler
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def self.storage
|
32
|
-
if
|
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
|
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
|
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
|
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
|
-
|
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
|
data/lib/litestack/version.rb
CHANGED
@@ -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.
|
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:
|
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.
|
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
|