ru.Bee 1.8.0 → 1.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a7e2d8462e9cf796796f117503bbc4d51dfebaa8d9923d5e1e6905d522a5af7
4
- data.tar.gz: d8eb71da20db0400dc96900f73f53481c835449d4b3f1ee20979dc95e7c083f4
3
+ metadata.gz: 95729d36d2f3e6f133212e3d2e4e94bbb741a60f1c68a85a2caf97d060dcf782
4
+ data.tar.gz: 261f8261a80d2b62a79fa92b87c86677f6d1e7dd9ab2689d3e325bb1160914e2
5
5
  SHA512:
6
- metadata.gz: 041f294e20e60f29ab5c03833613815fe7c462b7cc248a79aad228fbfe680e5474f6c38695c884d060a9cebfa0c0f2e918a915c102e2476e98d45df39b7a77ab
7
- data.tar.gz: b0f0acda20a4de0d3c8bfebc32b9229c9be04f1311d7f2bab5e3d6a5fa7819a7414f05d0902988801a7ed624b681dfbd14ca3e7682a85a5daf094014ee907a0c
6
+ metadata.gz: 95f59f4ee8cbce2e45ff8fa6563ed3c565a8a3fe5fe7954fae249aa75af3db98fb65ee0a801f55189ad80650b2dbabb86746c3100a9a53b05eae37c3d33a18b5
7
+ data.tar.gz: 27b6af04508c9438fb838946024ffef66f8b41808de3f503c7dc6e872a7642e3e749028b71eeb77cb97657e6988d0067a1622f1654a775c3007eb4c335aee7d4
@@ -10,6 +10,11 @@ Rubee::Configuration.setup(env = :development) do |config|
10
10
 
11
11
  ## configure logger
12
12
  # config.logger = { logger: MyLogger, env: }
13
+
14
+ ## configure db write retries
15
+ # config.db_max_retries = { env:, value: 3 }
16
+ # config.db_retry_delay = { env:, value: 0.1 }
17
+ # config.db_busy_timeout = { env:, value: 2000 }
13
18
  end
14
19
 
15
20
  Rubee::Configuration.setup(env = :test) do |config|
@@ -24,6 +29,11 @@ Rubee::Configuration.setup(env = :test) do |config|
24
29
 
25
30
  ## configure logger
26
31
  # config.logger = { logger: MyLogger, env: }
32
+
33
+ ## configure db write retries
34
+ # config.db_max_retries = { env:, value: 3 }
35
+ # config.db_retry_delay = { env:, value: 0.1 }
36
+ # config.db_busy_timeout = { env:, value: 2000 }
27
37
  end
28
38
 
29
39
  Rubee::Configuration.setup(env = :production) do |config|
@@ -38,4 +48,9 @@ Rubee::Configuration.setup(env = :production) do |config|
38
48
 
39
49
  ## configure logger
40
50
  # config.logger = { logger: MyLogger, env: }
51
+
52
+ ## configure db write retries
53
+ # config.db_max_retries = { env:, value: 3 }
54
+ # config.db_retry_delay = { env:, value: 0.1 }
55
+ # config.db_busy_timeout = { env:, value: 2000 }
41
56
  end
@@ -76,6 +76,10 @@ module Rubee
76
76
 
77
77
  require_relative File.join(root_directory,
78
78
  'rubee/models/sequel_object')
79
+ return if black_list.include?('db_tools.rb')
80
+
81
+ require_relative File.join(root_directory,
82
+ 'rubee/models/db_tools')
79
83
 
80
84
  Dir[File.join(root_directory, 'rubee/cli/**', '*.rb')].each do |file|
81
85
  require_relative file
@@ -51,6 +51,21 @@ module Rubee
51
51
  @configuraiton[args[:app].to_sym][args[:env].to_sym][:fiber_pool_limit] = args[:value]
52
52
  end
53
53
 
54
+ def db_max_retries=(args)
55
+ args[:app] ||= :app
56
+ @configuraiton[args[:app].to_sym][args[:env].to_sym][:db_max_retries] = args[:value]
57
+ end
58
+
59
+ def db_retry_delay=(args)
60
+ args[:app] ||= :app
61
+ @configuraiton[args[:app].to_sym][args[:env].to_sym][:db_retry_delay] = args[:value]
62
+ end
63
+
64
+ def db_busy_timeout=(args)
65
+ args[:app] ||= :app
66
+ @configuraiton[args[:app].to_sym][args[:env].to_sym][:db_busy_timeout] = args[:value]
67
+ end
68
+
54
69
  def logger=(args)
55
70
  args[:app] ||= :app
56
71
  @configuraiton[args[:app].to_sym][args[:env].to_sym][:logger] = args[:logger]
@@ -0,0 +1,36 @@
1
+ module Rubee
2
+ module DBTools
3
+ MAX_RETRIES = Rubee::Configuration.get_db_max_retries || 3
4
+ DELAY = Rubee::Configuration.get_db_retry_delay || 0.1
5
+ BUSY_TIMEOUT = Rubee::Configuration.get_db_busy_timeout || 2000
6
+
7
+ class << self
8
+ def with_retry
9
+ retries = 0
10
+ begin
11
+ yield
12
+ rescue Sequel::DatabaseError => e
13
+ # Applicable for msqlite only, however it can be extended in the future
14
+ if Rubee::SequelObject::DB.adapter_scheme == :sqlite &&
15
+ e.cause.is_a?(SQLite3::BusyException) && retries < MAX_RETRIES
16
+ retries += 1
17
+ sleep(DELAY)
18
+ retry
19
+ else
20
+ raise e
21
+ end
22
+ end
23
+ end
24
+
25
+ def set_prerequisites!
26
+ # Necessary changes to make sqlite be none blocking
27
+ if Rubee::SequelObject::DB.adapter_scheme == :sqlite
28
+ # WAL mode allows concurrent reads and non-blocking writes.
29
+ Rubee::SequelObject::DB.execute("PRAGMA journal_mode = WAL")
30
+ # Wait 2000ms for a write lock.
31
+ Rubee::SequelObject::DB.execute("PRAGMA busy_timeout = #{BUSY_TIMEOUT}")
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -50,7 +50,7 @@ module Rubee
50
50
  def update(args = {})
51
51
  assign_attributes(args)
52
52
  found_hash = self.class.dataset.where(id:)
53
- return self.class.find(id) if found_hash&.update(**args)
53
+ return self.class.find(id) if Rubee::DBTools.with_retry { found_hash&.update(**args) }
54
54
 
55
55
  false
56
56
  end
@@ -130,6 +130,10 @@ module Rubee
130
130
  return if defined?(DB) && !DB.nil?
131
131
 
132
132
  const_set(:DB, Sequel.connect(Rubee::Configuration.get_database_url))
133
+
134
+ Rubee::DBTools.set_prerequisites!
135
+
136
+ true
133
137
  end
134
138
 
135
139
  def dataset
@@ -169,7 +173,7 @@ module Rubee
169
173
  end
170
174
 
171
175
  def create(attrs)
172
- out_id = dataset.insert(**attrs)
176
+ out_id = Rubee::DBTools.with_retry { dataset.insert(**attrs) }
173
177
  new(**attrs.merge(id: out_id))
174
178
  end
175
179
 
data/lib/rubee.rb CHANGED
@@ -16,7 +16,7 @@ module Rubee
16
16
  JS_DIR = File.join(APP_ROOT, LIB, 'js') unless defined?(JS_DIR)
17
17
  CSS_DIR = File.join(APP_ROOT, LIB, 'css') unless defined?(CSS_DIR)
18
18
  ROOT_PATH = File.expand_path(File.join(__dir__, '..')) unless defined?(ROOT_PATH)
19
- VERSION = '1.8.0'
19
+ VERSION = '1.9.0'
20
20
 
21
21
  require_relative 'rubee/router'
22
22
  require_relative 'rubee/logger'
@@ -1,5 +1,4 @@
1
1
  require_relative '../test_helper'
2
-
3
2
  describe 'User model' do
4
3
  describe '.create' do
5
4
  after do
@@ -68,6 +67,32 @@ describe 'User model' do
68
67
  _(User.all.count).must_equal(initial_count)
69
68
  end
70
69
  end
70
+
71
+ describe 'locking model' do
72
+ before do
73
+ User.destroy_all(cascade: true)
74
+ end
75
+
76
+ it 'triggers save two times' do
77
+ t1 = Thread.new do
78
+ User::DB.transaction do
79
+ user2 = User.new(email: 'holding-lock2@example.com', password: '125')
80
+ sleep(0.5)
81
+ user2.save
82
+ end
83
+ end
84
+ sleep(0.1)
85
+ t2 = Thread.new do
86
+ user1 = User.new(email: 'holding-lock@example.com', password: '123')
87
+ user1.save
88
+ end
89
+
90
+ t1.join
91
+ t2.join
92
+
93
+ _(User.all.count).must_equal(2)
94
+ end
95
+ end
71
96
  end
72
97
 
73
98
  describe '.update' do
data/lib/tests/test.db CHANGED
Binary file
data/readme.md CHANGED
@@ -100,6 +100,11 @@ bundle install
100
100
  4. Run ruBee server. Default port is 7000
101
101
  ```bash
102
102
  rubee start # or rubee start_dev for development
103
+
104
+ # Sarting from veriosn 1.8.0, you can also start you rubee server with yjit compiler and enjoy speed boost.
105
+ rubee start --yjit
106
+ # Option is available for dev environment too
107
+ rubee start_dev --yjit
103
108
  ```
104
109
 
105
110
  5. Open your browser and go to http://localhost:7000
@@ -334,6 +339,25 @@ So it may safe some resources.
334
339
 
335
340
  [Back to content](#content)
336
341
 
342
+ ## Mysqlite production ready
343
+ Starting from verison 1.9.0 main issue for using sqlite - write db lock is resolved!
344
+ If you feel comfortable you can play with retry configuration parameters:
345
+
346
+ ```ruby
347
+ ## configure db write retries
348
+ config.db_max_retries = { env:, value: 3 } # set it to 0 to disable or increase if needed
349
+ config.db_retry_delay = { env:, value: 0.1 }
350
+ config.db_busy_timeout = { env:, value: 1000 } # this is busy timeout in ms, before raising bussy error
351
+ ```
352
+
353
+ For Rubee model class persist methods create and update retry will be added automatically. However, \
354
+ if you want to do it with Sequel dataset you need to do it yourself:
355
+
356
+ ```ruby
357
+ Rubee::DBTools.with_retry { User.dataset.insert(email: "test@ok.com", password: "123") }
358
+ ```
359
+ [Back to content](#content)
360
+
337
361
  ## Routing
338
362
  Rubee uses explicit routes. In the routes.rb yout can define routes for any of the main HTTP methods. \
339
363
  You can also add any matched parameter denoted by a pair of `{ }` in the path of the route. \
@@ -971,4 +995,4 @@ Have an idea or you wnat to discuss something?
971
995
  Please open a [discussion](https://github.com/nucleom42/rubee/discussions)
972
996
 
973
997
  ## License
974
- This project is released under the MIT License.
998
+ This project is released under the [MIT License](https://github.com/nucleom42/rubee/blob/main/LICENSE).
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ru.Bee
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oleg Saltykov
@@ -255,6 +255,7 @@ files:
255
255
  - lib/rubee/generator.rb
256
256
  - lib/rubee/logger.rb
257
257
  - lib/rubee/models/database_objectable.rb
258
+ - lib/rubee/models/db_tools.rb
258
259
  - lib/rubee/models/sequel_object.rb
259
260
  - lib/rubee/router.rb
260
261
  - lib/tests/async/thread_async_test.rb