crudjt 1.0.0.pre.beta.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3b39e1babc7abf1c23c786365127925435747b9819cad1b8911f2f98629a97e5
4
+ data.tar.gz: 2c092c94d52ac011696d889aa31415c9eb82118ad26d5ac688c46b1686e9e1ea
5
+ SHA512:
6
+ metadata.gz: ee52b8fdec0f35f41b0353e11775e6e97504af1a4ac828f7c88310ac88b35f5f89bfd9e2d86773b63b14cc5317e4d2a8ce46b0ee872f135a0ef254a4df9daa5e
7
+ data.tar.gz: 584c45b5df3b5920c2e40177dd336524196c66047b9e0416e216d66f1d19dbe3c4f5f9467ed951f4ada0fa6d31fd4699e691dd5475a5931957a194e472775061
@@ -0,0 +1,15 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4
+ patreon: crudjt # Replace with a single Patreon username
5
+ open_collective: # Replace with a single Open Collective username
6
+ ko_fi: # Replace with a single Ko-fi username
7
+ tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8
+ community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9
+ liberapay: # Replace with a single Liberapay username
10
+ issuehunt: # Replace with a single IssueHunt username
11
+ lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12
+ polar: # Replace with a single Polar username
13
+ buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14
+ thanks_dev: # Replace with a single thanks.dev username
15
+ custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
@@ -0,0 +1,41 @@
1
+ name: Run autotest on multiple OS and architectures
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ jobs:
7
+ test:
8
+ strategy:
9
+ matrix:
10
+ include:
11
+ - os: macos-latest
12
+ arch: aarch64
13
+ - os: ubuntu-latest
14
+ arch: x86_64
15
+
16
+ runs-on: ${{ matrix.os }}
17
+
18
+ steps:
19
+ - name: Checkout code
20
+ uses: actions/checkout@v3
21
+
22
+ - name: Setup Ruby
23
+ uses: ruby/setup-ruby@v1
24
+ with:
25
+ ruby-version: '3.3'
26
+ bundler-cache: true
27
+ cache-version: 2
28
+
29
+ - name: Install dependencies and build gem
30
+ run: |
31
+ gem install bundler --no-document
32
+ bundle install
33
+ bundle exec rake build
34
+
35
+ - name: Install gem
36
+ run: gem install pkg/crudjt-1.0.0-beta.0.gem --force
37
+
38
+ - name: Run autotest
39
+ env:
40
+ CRUDJT_AUTOTEST_ALLOWED: true
41
+ run: ruby autotest.rb
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /spec/reports/
7
+ /tmp/
8
+ /pkg/
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+ - Docker support
5
+ - Performance metrics
6
+ - Disk footprint
7
+
8
+ ## [1.0.0-beta.0] - 2026-03-18
9
+ - Initial beta release
10
+ - Fast B-tree–backed token store for stateful user sessions
11
+ - Provides authentication and authorization across multiple processes
12
+ - Optimized for vertical scaling on a single server
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+ # Specify your gem's dependencies in crudjt.gemspec
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,102 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ crudjt (1.0.0.pre.beta.0)
5
+ ffi (~> 1.17)
6
+ grpc (~> 1.78.0)
7
+ lru_redux (~> 1.1)
8
+ msgpack (~> 1.8)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ bigdecimal (4.0.1)
14
+ ffi (1.17.3)
15
+ ffi (1.17.3-aarch64-linux-gnu)
16
+ ffi (1.17.3-aarch64-linux-musl)
17
+ ffi (1.17.3-arm-linux-gnu)
18
+ ffi (1.17.3-arm-linux-musl)
19
+ ffi (1.17.3-arm64-darwin)
20
+ ffi (1.17.3-x86-linux-gnu)
21
+ ffi (1.17.3-x86-linux-musl)
22
+ ffi (1.17.3-x86_64-darwin)
23
+ ffi (1.17.3-x86_64-linux-gnu)
24
+ ffi (1.17.3-x86_64-linux-musl)
25
+ google-protobuf (4.34.0)
26
+ bigdecimal
27
+ rake (~> 13.3)
28
+ google-protobuf (4.34.0-aarch64-linux-gnu)
29
+ bigdecimal
30
+ rake (~> 13.3)
31
+ google-protobuf (4.34.0-aarch64-linux-musl)
32
+ bigdecimal
33
+ rake (~> 13.3)
34
+ google-protobuf (4.34.0-arm64-darwin)
35
+ bigdecimal
36
+ rake (~> 13.3)
37
+ google-protobuf (4.34.0-x86-linux-gnu)
38
+ bigdecimal
39
+ rake (~> 13.3)
40
+ google-protobuf (4.34.0-x86-linux-musl)
41
+ bigdecimal
42
+ rake (~> 13.3)
43
+ google-protobuf (4.34.0-x86_64-darwin)
44
+ bigdecimal
45
+ rake (~> 13.3)
46
+ google-protobuf (4.34.0-x86_64-linux-gnu)
47
+ bigdecimal
48
+ rake (~> 13.3)
49
+ google-protobuf (4.34.0-x86_64-linux-musl)
50
+ bigdecimal
51
+ rake (~> 13.3)
52
+ googleapis-common-protos-types (1.22.0)
53
+ google-protobuf (~> 4.26)
54
+ grpc (1.78.1)
55
+ google-protobuf (>= 3.25, < 5.0)
56
+ googleapis-common-protos-types (~> 1.0)
57
+ grpc (1.78.1-aarch64-linux-gnu)
58
+ google-protobuf (>= 3.25, < 5.0)
59
+ googleapis-common-protos-types (~> 1.0)
60
+ grpc (1.78.1-aarch64-linux-musl)
61
+ google-protobuf (>= 3.25, < 5.0)
62
+ googleapis-common-protos-types (~> 1.0)
63
+ grpc (1.78.1-arm64-darwin)
64
+ google-protobuf (>= 3.25, < 5.0)
65
+ googleapis-common-protos-types (~> 1.0)
66
+ grpc (1.78.1-x86-linux-gnu)
67
+ google-protobuf (>= 3.25, < 5.0)
68
+ googleapis-common-protos-types (~> 1.0)
69
+ grpc (1.78.1-x86-linux-musl)
70
+ google-protobuf (>= 3.25, < 5.0)
71
+ googleapis-common-protos-types (~> 1.0)
72
+ grpc (1.78.1-x86_64-darwin)
73
+ google-protobuf (>= 3.25, < 5.0)
74
+ googleapis-common-protos-types (~> 1.0)
75
+ grpc (1.78.1-x86_64-linux-gnu)
76
+ google-protobuf (>= 3.25, < 5.0)
77
+ googleapis-common-protos-types (~> 1.0)
78
+ grpc (1.78.1-x86_64-linux-musl)
79
+ google-protobuf (>= 3.25, < 5.0)
80
+ googleapis-common-protos-types (~> 1.0)
81
+ lru_redux (1.1.0)
82
+ msgpack (1.8.0)
83
+ rake (13.3.1)
84
+
85
+ PLATFORMS
86
+ aarch64-linux-gnu
87
+ aarch64-linux-musl
88
+ arm-linux-gnu
89
+ arm-linux-musl
90
+ arm64-darwin
91
+ ruby
92
+ x86-linux-gnu
93
+ x86-linux-musl
94
+ x86_64-darwin
95
+ x86_64-linux-gnu
96
+ x86_64-linux-musl
97
+
98
+ DEPENDENCIES
99
+ crudjt!
100
+
101
+ BUNDLED WITH
102
+ 2.6.7
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 v_akymov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,212 @@
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="logos/crudjt_logo_white_on_dark.svg">
4
+ <source media="(prefers-color-scheme: light)" srcset="logos/crudjt_logo_dark_on_white.svg">
5
+ <img alt="Shows a dark logo" src="logos/crudjt_logo_dark.png">
6
+ </picture>
7
+ </br>
8
+ Ruby SDK for the fast, file-backed, scalable JSON token engine
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://www.patreon.com/crudjt">
13
+ <img src="logos/buy_me_a_coffee_orange.svg" alt="Buy Me a Coffee"/>
14
+ </a>
15
+ </p>
16
+
17
+ > ⚠️ Version 1.0.0-beta — production testing phase
18
+ > API is stable. Feedback is welcome before the final 1.0.0 release
19
+
20
+ Fast B-tree–backed token store for stateful user sessions
21
+ Provides authentication and authorization across multiple processes
22
+ Optimized for vertical scaling on a single server
23
+
24
+ # Installation
25
+
26
+ With Bundler:
27
+ ```sh
28
+ bundle add crudjt
29
+ ```
30
+ Or via RubyGems:
31
+ ```sh
32
+ gem install crudjt
33
+ ```
34
+
35
+ ## How to use
36
+
37
+ - One process starts the master
38
+ - All other processes connect to it
39
+
40
+ ## Start CRUDJT master (once)
41
+
42
+ Start the CRUDJT master when your application boots
43
+
44
+ Only **one process** can do this for a **single token storage**
45
+
46
+ The master process manages sessions and coordination
47
+ All functions can also be used directly from it
48
+
49
+ ### Generate a new secret key (terminal)
50
+
51
+ ```sh
52
+ export CRUDJT_SECRET_KEY=$(openssl rand -base64 48)
53
+ ```
54
+
55
+ ### Start master (ruby)
56
+ ```ruby
57
+ require 'crudjt'
58
+
59
+ CRUDJT::Config.start_master(
60
+ secret_key: ENV.fetch('CRUDJT_SECRET_KEY'),
61
+ store_jt_path: 'path/to/local/storage', # optional
62
+ grpc_host: '127.0.0.1', # default
63
+ grpc_port: 50051 # default
64
+ )
65
+ ```
66
+
67
+ *Important: Use the same `secret_key` across all sessions. If the key changes, previously stored tokens cannot be decrypted and will return `nil` or `false`*
68
+
69
+ ## Start CRUDJT master in Docker
70
+ > `docker-compose.yml` will be published after 1.0.0-beta Docker image builds
71
+
72
+ ## Connect to an existing CRUDJT master
73
+
74
+ Use this in all other processes
75
+
76
+ Typical examples:
77
+ - multiple local processes
78
+ - background jobs
79
+ - forked processes
80
+
81
+ ```ruby
82
+ require 'crudjt'
83
+
84
+ CRUDJT::Config.connect_to_master(
85
+ grpc_host: '127.0.0.1', # default
86
+ grpc_port: 50051 # default
87
+ )
88
+ ```
89
+
90
+ ### Process layout
91
+
92
+ App boot
93
+ ├─ Process A → start_master
94
+ ├─ Process B → connect_to_master
95
+ └─ Process C → connect_to_master
96
+
97
+ # C
98
+
99
+ ```ruby
100
+ data = { user_id: 42, role: 11 } # required
101
+ ttl = 3600 * 24 * 30 # optional: token lifetime (seconds)
102
+
103
+ # Optional: read limit
104
+ # Each read decrements the counter
105
+ # When it reaches zero — the token is deleted
106
+ silence_read = 10
107
+
108
+ token = CRUDJT.create(data, ttl: ttl, silence_read: silence_read)
109
+ # token == 'HBmKFXoXgJ46mCqer1WXyQ'
110
+ ```
111
+
112
+ ```ruby
113
+ # To disable token expiration or read limits, pass `nil`
114
+ CRUDJT.create({ user_id: 42, role: 11 }, ttl: nil, silence_read: nil)
115
+ ```
116
+
117
+ # R
118
+
119
+ ```ruby
120
+ result = CRUDJT.read('HBmKFXoXgJ46mCqer1WXyQ')
121
+ # result == {'metadata' => {'ttl' => 101001, 'silence_read' => 9}, 'data' => {'user_id' => 42, 'role' => 11}}
122
+ ```
123
+
124
+ ```ruby
125
+ # When expired or not found token
126
+ result = CRUDJT.read('HBmKFXoXgJ46mCqer1WXyQ')
127
+ # result == nil
128
+ ```
129
+
130
+ # U
131
+
132
+ ```ruby
133
+ data = { user_id: 42, role: 8 }
134
+ # `nil` disables limits
135
+ ttl = 600
136
+ silence_read = 100
137
+
138
+ result = CRUDJT.update('HBmKFXoXgJ46mCqer1WXyQ', data, ttl: ttl, silence_read: silence_read)
139
+ # result == true
140
+ ```
141
+
142
+ ```ruby
143
+ # When expired or not found token
144
+ CRUDJT.update('HBmKFXoXgJ46mCqer1WXyQ', { user_id: 42, role: 8 })
145
+ # result == false
146
+ ```
147
+
148
+ # D
149
+ ```ruby
150
+ result = CRUDJT.delete('HBmKFXoXgJ46mCqer1WXyQ')
151
+ # result == true
152
+ ```
153
+
154
+ ```ruby
155
+ # When expired or not found token
156
+ result = CRUDJT.delete('HBmKFXoXgJ46mCqer1WXyQ')
157
+ # result == false
158
+ ```
159
+
160
+ # Performance
161
+ > Metrics will be published after 1.0.0-beta GitHub Actions builds
162
+
163
+ # Storage (File-backed)
164
+
165
+ ## Disk footprint
166
+ > Metrics will be published after 1.0.0-beta GitHub Actions builds
167
+
168
+ ## Path Lookup Order
169
+ Stored tokens are placed in the **file system** according to the following order
170
+
171
+ 1. Explicitly set via `CRUDJT::Config.start_master(store_jt_path: 'custom/path/to/file_system_db')`
172
+ 2. Default system location
173
+ - **Linux**: `/var/lib/store_jt`
174
+ - **macOS**: `/usr/local/var/store_jt`
175
+ - **Windows**: `C:\Program Files\store_jt`
176
+ 3. Project root directory (fallback)
177
+
178
+ ## Storage Characteristics
179
+ * CRUDJT **automatically removing expired tokens** after start and every 24 hours without blocking the main thread
180
+ * **Storage automatically fsyncs every 500ms**, meanwhile tokens ​​are available from cache
181
+
182
+ # Multi-process Coordination
183
+ For multi-process scenarios, CRUDJT uses gRPC over an insecure local port for same-host communication only. It is not intended for inter-machine or internet-facing usage
184
+
185
+ # Limits
186
+ The library has the following limits and requirements
187
+
188
+ - **Ruby version:** tested with 2.7
189
+ - **Supported platforms:** Linux, macOS (x86_64 / arm64). Windows (experimental, x86_64 / arm64)
190
+ - **Maximum json size per token:** 256 bytes
191
+ - **`secret_key` format:** must be Base64
192
+ - **`secret_key` size:** must be 32, 48, or 64 bytes
193
+
194
+ # Contact & Support
195
+ <p align="center">
196
+ <picture>
197
+ <source media="(prefers-color-scheme: dark)" srcset="logos/crudjt_favicon_160x160_white_on_dark.svg" width=160 height=160>
198
+ <source media="(prefers-color-scheme: light)" srcset="logos/crudjt_favicon_160x160_dark_on_white.svg" width=160 height=160>
199
+ <img alt="Shows a dark favicon in light color mode and a white one in dark color mode" src="logos/crudjt_favicon_160x160_white.png" width=160 height=160>
200
+ </picture>
201
+ </p>
202
+
203
+ - **Custom integrations / new features / collaboration**: support@crudjt.com
204
+ - **Library support & bug reports:** [open an issue](https://github.com/crudjt/crudjt-ruby/issues)
205
+
206
+
207
+ # Lincense
208
+ CRUDJT is released under the [MIT License](LICENSE.txt)
209
+
210
+ <p align="center">
211
+ 💘 Shoot your g . ? Love me out via <a href="https://www.patreon.com/crudjt">Patreon Sponsors</a>!
212
+ </p>
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/autotest.rb ADDED
@@ -0,0 +1,192 @@
1
+ require 'benchmark'
2
+ require 'crudjt'
3
+
4
+ if ENV['CRUDJT_AUTOTEST_ALLOWED'] != 'true'
5
+ return STDOUT.puts "Denied run autotest for this environment. Set ENV['CRUDJT_AUTOTEST_ALLOWED'] = 'true'"
6
+ end
7
+
8
+ p "OS: #{RbConfig::CONFIG['host_os']}"
9
+ p "CPU: #{RbConfig::CONFIG['host_cpu']}"
10
+
11
+ p 'Checking secret key validations...'
12
+ # when started without secret key
13
+ begin
14
+ CRUDJT::Config.start_master
15
+ rescue RuntimeError => error
16
+ p error.message == CRUDJT::Validation.error_message(CRUDJT::Validation::ERROR_SECRET_KEY_NOT_SET)
17
+ else
18
+ p false
19
+ end
20
+
21
+ # when started with fake base64 secret key
22
+ begin
23
+ CRUDJT::Config.start_master(secret_key: 'bla-bla-bla')
24
+ rescue ArgumentError => error
25
+ p error.message == "'secret_key' must be a valid Base64 string"
26
+ else
27
+ p false
28
+ end
29
+
30
+ # when started with wrong secret key lenght
31
+ begin
32
+ key_16_bytes = '2v+XIslTkPTfjva0xeCLHQ=='
33
+ CRUDJT::Config.start_master(secret_key: key_16_bytes)
34
+ rescue ArgumentError => error
35
+ p error.message == "'secret_key' must be exactly 32, 48, or 64 bytes. Got #{Base64.strict_decode64(key_16_bytes).bytesize} bytes"
36
+ else
37
+ p false
38
+ end
39
+
40
+ p 'Checking base validations...'
41
+
42
+ # when not started store_jt
43
+ begin
44
+ CRUDJT.original_create({ some_key: 'some value' })
45
+ rescue RuntimeError => error
46
+ p error.message == CRUDJT::Validation.error_message(CRUDJT::Validation::ERROR_NOT_STARTED)
47
+ else
48
+ p false
49
+ end
50
+
51
+ # when not started store_jt
52
+ begin
53
+ CRUDJT.original_create({ some_key: 'some value' })
54
+ rescue RuntimeError => error
55
+ p error.message == CRUDJT::Validation.error_message(CRUDJT::Validation::ERROR_NOT_STARTED)
56
+ else
57
+ p false unless RbConfig::CONFIG['host_os'].include?('w32')
58
+ end
59
+
60
+ CRUDJT::Config.start_master(
61
+ secret_key: 'Cm7B68NWsMNNYjzMDREacmpe5sI1o0g40ZC9w1yQW3WOes7Gm59UsittLOHR2dciYiwmaYq98l3tG8h9yXVCxg=='
62
+ )
63
+
64
+ p "Master: #{CRUDJT::Config.master?}"
65
+
66
+
67
+ # without metadata
68
+ p 'Checking without metadata...'
69
+ data = { user_id: 42, role: 11 }
70
+ expected_data = { data: data.transform_keys(&:to_s) }.transform_keys(&:to_s)
71
+
72
+ updated_data = { user_id: 42, role: 8 }
73
+ expected_updated_data = { data: updated_data.transform_keys(&:to_s) }.transform_keys(&:to_s)
74
+
75
+ token = CRUDJT.create(data)
76
+
77
+ p CRUDJT.read(token) == expected_data
78
+ p CRUDJT.update(token, updated_data) == true
79
+ p CRUDJT.read(token) == expected_updated_data
80
+ p CRUDJT.delete(token) == true
81
+ p CRUDJT.read(token) == nil
82
+
83
+ # with ttl
84
+ p 'Checking ttl...'
85
+
86
+ data = { user_id: 42, role: 11 }
87
+
88
+ ttl = 5
89
+ token_with_ttl = CRUDJT.create(data, ttl: ttl)
90
+
91
+ expected_ttl = ttl
92
+ ttl.times do |i|
93
+ p CRUDJT.read(token_with_ttl) == JSON.parse({ metadata: { ttl: expected_ttl }, data: data }.to_json)
94
+ expected_ttl -= 1
95
+
96
+ sleep 1
97
+ end
98
+ p CRUDJT.read(token_with_ttl) == nil
99
+
100
+ # when expired ttl
101
+ p 'when expired ttl'
102
+ data = { user_id: 42, role: 11 }
103
+ ttl = 1
104
+ token = CRUDJT.create(data, ttl: ttl)
105
+ sleep ttl
106
+ p CRUDJT.read(token) == nil
107
+ p CRUDJT.update(token, data) == false
108
+ p CRUDJT.delete(token) == false
109
+
110
+ p CRUDJT.update(token, data) == false
111
+ p CRUDJT.read(token) == nil
112
+
113
+ # with silence read
114
+ p "Checking silence read..."
115
+
116
+ data = { user_id: 42, role: 11 }
117
+ silence_read = 6
118
+ token_with_silence_read = CRUDJT.create(data, silence_read: silence_read)
119
+
120
+ expected_silence_read = silence_read - 1
121
+ silence_read.times do
122
+ p CRUDJT.read(token_with_silence_read) == JSON.parse({ metadata: { silence_read: expected_silence_read }, data: data }.to_json)
123
+ expected_silence_read -= 1
124
+ end
125
+ p CRUDJT.read(token_with_silence_read) == nil
126
+
127
+ # with ttl and silence read
128
+ p "Checking ttl and silence read..."
129
+
130
+ data = { user_id: 42, role: 11 }
131
+ ttl = 5
132
+ silence_read = ttl
133
+ token_with_ttl_and_silence_read = CRUDJT.create(data, ttl: ttl, silence_read: silence_read)
134
+
135
+ expected_ttl = ttl
136
+ expected_silence_read = silence_read - 1
137
+ silence_read.times do
138
+ p CRUDJT.read(token_with_ttl_and_silence_read) == JSON.parse({ metadata: { ttl: expected_ttl, silence_read: expected_silence_read }, data: data }.to_json)
139
+ expected_ttl -= 1
140
+ expected_silence_read -= 1
141
+
142
+ sleep 1
143
+ end
144
+ p CRUDJT.read(token_with_ttl_and_silence_read) == nil
145
+
146
+ # with scale load
147
+
148
+ REQUESTS = 40_000
149
+
150
+ data = {user_id: 414243, role: 11, devices: {ios_expired_at: Time.now.to_s, android_expired_at: Time.now.to_s, external_api_integration_expired_at: Time.now.to_s}, a: "a" * 100 }
151
+ while MessagePack.pack(data).bytesize > CRUDJT::Validation::MAX_HASH_SIZE
152
+ data[:a].chop!
153
+ end
154
+
155
+ updated_data = { user_id: 42, role: 11 }
156
+
157
+ p "Hash bytesize: #{MessagePack.pack(data).bytesize}"
158
+ values = []
159
+ 10.times do
160
+ tokens = []
161
+
162
+ p 'Checking scale load...'
163
+
164
+ # when create
165
+ p 'when creates 40k tokens'
166
+ puts Benchmark.measure { REQUESTS.times { |i| tokens << CRUDJT.create(data) } }
167
+
168
+ # when read
169
+ p 'when reads 40k tokens'
170
+ index = rand(0..REQUESTS)
171
+ puts Benchmark.measure { REQUESTS.times { |i| CRUDJT.read(tokens[i]) } }
172
+
173
+ # when updates
174
+ p 'when updates 40k tokens'
175
+ puts Benchmark.measure { REQUESTS.times { |i| CRUDJT.update(tokens[i], updated_data) } }
176
+ #
177
+ # when delete
178
+ p 'when deletes 40k tokens'
179
+ puts Benchmark.measure { REQUESTS.times { |i| CRUDJT.delete(tokens[i]) } }
180
+ end
181
+
182
+ # when cache after read from file system
183
+ p 'when caches after read from file system'
184
+
185
+ LIMIT_ON_READY_FOR_CACHE = 2
186
+
187
+ previus_tokens = []
188
+
189
+ REQUESTS.times { previus_tokens << CRUDJT.create(data) }
190
+ REQUESTS.times { CRUDJT.create(data) }
191
+
192
+ LIMIT_ON_READY_FOR_CACHE.times { puts Benchmark.measure { REQUESTS.times { |i| CRUDJT.read(previus_tokens[i]) } } }
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "crudjt"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/crudjt.gemspec ADDED
@@ -0,0 +1,44 @@
1
+ require_relative 'lib/crudjt/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "crudjt"
5
+ spec.version = CRUDJT::VERSION
6
+ spec.authors = ["Vlad Akymov (v_akymov)"]
7
+ spec.email = ["support@crudjt.com"]
8
+
9
+ spec.summary = %q{Fast B-tree–backed token store for stateful sessions}
10
+ spec.description = <<~DESC
11
+ Fast B-tree–backed token store for stateful user sessions
12
+ Provides authentication and authorization across multiple processes
13
+ Optimized for vertical scaling on a single server
14
+ DESC
15
+ spec.homepage = "https://github.com/crudjt"
16
+ spec.license = "MIT"
17
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
18
+
19
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
20
+
21
+ spec.metadata["homepage_uri"] = spec.homepage
22
+ spec.metadata["source_code_uri"] = "https://github.com/crudjt/crudjt-ruby"
23
+ spec.metadata["documentation_uri"] = "https://github.com/crudjt/crudjt-ruby#readme"
24
+ spec.metadata["changelog_uri"] = "https://github.com/crudjt/crudjt-ruby/blob/master/CHANGELOG.md"
25
+ spec.metadata["bug_tracker_uri"] = "https://github.com/crudjt/crudjt-ruby/issues"
26
+ spec.metadata["funding_uri"] = "https://patreon.com/crudjt"
27
+ spec.metadata["rubygems_mfa_required"] = "true"
28
+ spec.metadata["keywords"] = "auth, authentication, token, sessions, crud"
29
+
30
+ spec.add_dependency "ffi", "~> 1.17"
31
+ spec.add_dependency "msgpack", "~> 1.8"
32
+ spec.add_dependency "lru_redux", "~> 1.1"
33
+ spec.add_dependency "grpc", "~> 1.78.0"
34
+
35
+ # Specify which files should be added to the gem when it is released.
36
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
37
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
38
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
39
+ end
40
+
41
+ spec.bindir = "exe"
42
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
43
+ spec.require_paths = ["lib"]
44
+ end