solid_cable 2.0.2 → 3.0.1

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: c7179cc12b0e26e67a4cf8c7a3422143d6405d33806972e77e546f2643c47d4a
4
- data.tar.gz: bb2bf9cbeca60b7eebca95c3dc13951e7769b616b18663c65bdf4dd915e23ca0
3
+ metadata.gz: 1aef7361e9ee2d03dbcd1d8bc89009cd4f3af500b1834e7fab52663850fde410
4
+ data.tar.gz: 63185c05065aa3ad657189fa7c2bbd8bf2a049eee2596e4e5d441c7c881ad258
5
5
  SHA512:
6
- metadata.gz: ed13873bf8f5783d34946df8437687f2642fea10e74edd82cac2c266108311dcf52d709556a6916fe17c26428306bac931d031de6cacdb3f8dd470477339de56
7
- data.tar.gz: d1ffc9ad2042e5910298ac4afe3562bda426423938a2aa62472ee46694b5dbe48a340a55c7b2649b4f024c1f75f113fb9b551366f6a7768c692e58af10e114a9
6
+ metadata.gz: 7b8da2e04fd45d8dea8098ad3d453d06d3df53881369480157f03fff63f1d1fa615fdfcc6a162aa2b85976c3767afc25ecae725663b375edc89568470176bda3
7
+ data.tar.gz: ba1fb17d9ac2c916e815db54d4495f6cd0f53f72d63f276932fcb935958b3e7853e7bd2685c5e677b5972f98a306910b2598405bf7ead4dfabb25aa2ba74b59e
data/README.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  Solid Cable is a database-backed Action Cable adapter that keeps messages in a table and continously polls for updates. This makes it possible to drop the common dependency on Redis, if it isn't needed for any other purpose. Despite polling, the performance of Solid Cable is comparable to Redis in most situations. And in all circumstances, it makes it easier to deploy Rails when Redis is no longer a required dependency for Action Cable functionality.
4
4
 
5
+ > [!NOTE]
6
+ > Solid Cable is tested to work with MySQL, SQLite, and PostgreSQL.
7
+ >
8
+ > Action Cable already has a [dedicated PostgreSQL adapter](https://guides.rubyonrails.org/action_cable_overview.html#postgresql-adapter),
9
+ > which utilizes the builtin `NOTIFY` command for better performance. However, that
10
+ > adapter has an 8kb limit on its payload. Solid Cable is a great alternative if you find yourself
11
+ > broadcasting large payloads, or prefer not to use the `NOTIFY` command.
5
12
 
6
13
  ## Installation
7
14
 
@@ -45,6 +52,16 @@ production:
45
52
 
46
53
  Then run `db:prepare` in production to ensure the database is created and the schema is loaded.
47
54
 
55
+ ### Single database configuration
56
+
57
+ Running Solid Cable in a separate database is recommended, but it's also possible to use a single database for both the app and Action Cable.
58
+
59
+ 1. Copy the contents of `db/cable_schema.rb` into a normal migration and delete `db/cable_schema.rb`
60
+ 2. Remove `connects_to` from `config/cable.yml`
61
+ 3. `bin/rails db:migrate`
62
+
63
+ You won't have multiple databases, so `database.yml` doesn't need to have primary and cable database.
64
+
48
65
  ## Configuration
49
66
 
50
67
  All configuration is managed via the `config/cable.yml` file. By default, it'll be configured like this:
@@ -67,14 +84,176 @@ The options are:
67
84
  - `message_retention` - sets the retention time for messages kept in the database. Used as the cut-off when trimming is performed. (Defaults to 1.day)
68
85
  - `autotrim` - sets wether you want Solid Cable to handle autotrimming messages. (Defaults to true)
69
86
  - `silence_polling` - whether to silence Active Record logs emitted when polling (Defaults to true)
87
+ - `use_skip_locked` - whether to use `FOR UPDATE SKIP LOCKED` when performing trimming. This will be automatically detected in the future, and for now, you'd only need to set this to `false` if your database doesn't support it. For MySQL, that'd be versions < 8, and for PostgreSQL, versions < 9.5. If you use SQLite, this has no effect, as writes are sequential. (Defaults to true)
88
+ - `trim_batch_size` - the batch size to use when deleting old records (default: `100`)
89
+
70
90
 
71
91
  ## Trimming
72
92
 
73
- Messages are autotrimmed based upon the `message_retention` setting to determine how long messages are to be kept around. If no `message_retention` is given or parsing fails, it defaults to `1.day`. Messages are trimmed when a subscriber unsubscribes.
93
+ Messages are autotrimmed based upon the `message_retention` setting to determine how long messages are to be kept around. If no `message_retention` is given or parsing fails, it defaults to `1.day`. Messages are trimmed when a messsage is broadcast.
74
94
 
75
- Autotrimming can negatively impact performance depending on your workload because it is doing a delete on ubsubscribe. If
95
+ Autotrimming can negatively impact performance slightly depending on your workload because it is potentially doing a delete on broadcast. If
76
96
  you would prefer, you can disable autotrimming by setting `autotrim: false` and you can manually enqueue the job later, `SolidCable::TrimJob.perform_later`, or run it on a recurring interval out of band.
77
97
 
98
+
99
+ ## Upgrading
100
+
101
+ If you have already installed Solid Cable < 3 and are upgrading to version 3,
102
+ run `solid_cable:update` to install a new migration.
103
+
104
+
105
+ ## Benchmarks
106
+
107
+ Inside the `bench` directory there is a minimal Rails app that is used to benchmark.
108
+ You are welcome to update the config/deploy.yml file to point to your own server
109
+ if you want to deploy the app to your own server and run benchmarks.
110
+
111
+ To benchmark we use [k6](https://k6.io). Most of the setup was gotten from this
112
+ [article](https://evilmartians.com/chronicles/real-time-stress-anycable-k6-websockets-and-yabeda).
113
+ 1. Install k6
114
+ 1. Install xk6-cable by running `xk6 build --with
115
+ github.com/anycable/xk6-cable`. This will output a custom k6 binary.
116
+ 1. Run the load test with `./k6 run loadtest.js`
117
+ - This script takes a variety of ENV variables:
118
+ - WS_URL: The url to send websocket connections
119
+ - MAX: The number of virtual users to hit the server with
120
+ - TIME: The duration of the load test
121
+ - MESSAGES_NUM: The number of messages each VU will send to the server
122
+
123
+
124
+ #### Results
125
+
126
+ Our loadtest is run on a Hetzner CCX13, with a MESSAGES_NUM of 5, and a TIME of 90.
127
+
128
+ ##### SQLite
129
+
130
+ With a polling interval of 0.1 seconds and autotrimming enabled.
131
+
132
+ 100 VUs
133
+ ```
134
+ rtt..................: avg=135.82ms min=50ms med=138ms max=357ms p(90)=174ms p(95)=195ms
135
+ ws_connecting........: avg=205.81ms min=149.35ms med=199.01ms max=509.48ms p(90)=254.04ms p(95)=261.77ms
136
+ ```
137
+ 250 VUs
138
+ ```
139
+ rtt..................: avg=146.24ms min=50ms med=144ms max=435ms p(90)=209ms p(95)=234.04ms
140
+ ws_connecting........: avg=222.15ms min=146.47ms med=208.57ms max=1.3s p(90)=263.6ms p(95)=284.18ms
141
+ ```
142
+ 500 VUs
143
+ ```
144
+ rtt..................: avg=271.79ms min=48ms med=205ms max=1.15s p(90)=558ms p(95)=660ms
145
+ ws_connecting........: avg=248.81ms min=145.89ms med=221.89ms max=1.38s p(90)=290.41ms p(95)=322.2ms
146
+ ```
147
+ 750 VUs
148
+ ```
149
+ rtt..................: avg=548.27ms min=51ms med=438ms max=5.19s p(90)=1.18s p(95)=1.29s
150
+ ws_connecting........: avg=266.37ms min=144.06ms med=224.93ms max=2.33s p(90)=298ms p(95)=342.87ms
151
+ ```
152
+
153
+ With trimming disabled
154
+
155
+ 250 VUs
156
+ ```
157
+ rtt..................: avg=139.47ms min=48ms med=142ms max=807ms p(90)=189ms p(95)=214ms
158
+ ws_connecting........: avg=212.58ms min=146.19ms med=196.25ms max=1.25s p(90)=255.74ms p(95)=272.44ms
159
+ ```
160
+
161
+ With a polling interval of 0.01 seconds it becomes comparable to Redis
162
+
163
+ 250 VUs
164
+ ```
165
+ rtt..................: avg=84.22ms min=43ms med=69ms max=416ms p(90)=137ms p(95)=150ms
166
+ ws_connecting........: avg=219.37ms min=144.71ms med=200.77ms max=2.17s p(90)=265.23ms p(95)=290.83ms
167
+ ```
168
+
169
+ ##### Redis
170
+
171
+ This instance was hosted on the same machine.
172
+
173
+ 100 VUs
174
+ ```
175
+ rtt..................: avg=68.95ms min=41ms med=56ms max=6.23s p(90)=114ms p(95)=129ms
176
+ ws_connecting........: avg=211.09ms min=153.23ms med=195.69ms max=1.44s p(90)=258.1ms p(95)=272.23ms
177
+ ```
178
+ 250 VUs
179
+ ```
180
+ rtt..................: avg=69.32ms min=40ms med=56ms max=645ms p(90)=119ms p(95)=135ms
181
+ ws_connecting........: avg=212.95ms min=142.92ms med=196.31ms max=1.25s p(90)=260.25ms p(95)=273.49ms
182
+ ```
183
+ 500 VUs
184
+ ```
185
+ rtt..................: avg=87.5ms min=40ms med=67ms max=839ms p(90)=149ms p(95)=176ms
186
+ ws_connecting........: avg=242.62ms min=142.03ms med=213.76ms max=2.34s p(90)=291.25ms p(95)=324.04ms
187
+ ```
188
+ 750 VUs
189
+ ```
190
+ rtt..................: avg=162.54ms min=39ms med=123ms max=2.26s p(90)=343.1ms p(95)=438ms
191
+ ws_connecting........: avg=353.08ms min=143ms med=264.15ms max=2.73s p(90)=541.36ms p(95)=1.15s
192
+ ```
193
+
194
+
195
+ ##### MySQL
196
+
197
+ With a polling interval of 0.1 seconds and autotrimming enabled. This instance
198
+ was also hosted on the same machine.
199
+
200
+ 100 VUs
201
+ ```
202
+ rtt..................: avg=136.02ms min=51ms med=137ms max=877ms p(90)=168.1ms p(95)=198ms
203
+ ws_connecting........: avg=207.76ms min=151.93ms med=196.74ms max=1.21s p(90)=249.91ms p(95)=260.37ms
204
+ ```
205
+ 250 VUs
206
+ ```
207
+ rtt..................: avg=159.33ms min=51ms med=149ms max=559ms p(90)=236ms p(95)=263ms
208
+ ws_connecting........: avg=232.38ms min=151.6ms med=218.09ms max=1.38s p(90)=287.99ms p(95)=324.6ms
209
+ ```
210
+ 500 VUs
211
+ ```
212
+ rtt..................: avg=441.07ms min=51ms med=312ms max=2.29s p(90)=931ms p(95)=1.07s
213
+ ws_connecting........: avg=256.73ms min=152.23ms med=231.02ms max=2.31s p(90)=305.69ms p(95)=340.83ms
214
+ ```
215
+ 750 VUs
216
+ ```
217
+ rtt..................: avg=822.08ms min=51ms med=732ms max=5.05s p(90)=1.76s p(95)=1.97s
218
+ ws_connecting........: avg=278.08ms min=146.66ms med=236.35ms max=2.37s p(90)=318.17ms p(95)=374.98ms
219
+ ```
220
+
221
+
222
+ ##### PostgreSQL with Solid Cable
223
+
224
+ With a polling interval of 0.1 seconds and autotrimming enabled. This instance
225
+ was also hosted on the same machine.
226
+
227
+ 100 VUs
228
+ ```
229
+ rtt..................: avg=137.45ms min=48ms med=139ms max=439ms p(90)=179.1ms p(95)=204ms
230
+ ws_connecting........: avg=207.13ms min=150.29ms med=197.76ms max=443.67ms p(90)=254.44ms p(95)=263.29ms
231
+ ```
232
+ 250 VUs
233
+ ```
234
+ rtt..................: avg=151.63ms min=49ms med=146ms max=538ms p(90)=222ms p(95)=248.04ms
235
+ ws_connecting........: avg=245.89ms min=147.18ms med=205.57ms max=30s p(90)=265.08ms p(95)=281.15ms
236
+ ```
237
+ 500 VUs
238
+ ```
239
+ rtt..................: avg=362.79ms min=50ms med=249ms max=1.21s p(90)=757ms p(95)=844ms
240
+ ws_connecting........: avg=257.02ms min=146.13ms med=227.65ms max=2.39s p(90)=303.22ms p(95)=344.39ms
241
+ ```
242
+
243
+
244
+ ##### PostgreSQL with dedicated adapter
245
+
246
+ 100 VUs
247
+ ```
248
+ rtt..................: avg=69.76ms min=41ms med=57ms max=622ms p(90)=116ms p(95)=133ms
249
+ ws_connecting........: avg=210.97ms min=149.68ms med=196.06ms max=1.27s p(90)=259.67ms p(95)=273.17ms
250
+ ```
251
+ 250 VUs
252
+ ```
253
+ rtt..................: avg=73.43ms min=40ms med=58ms max=698ms p(90)=126ms p(95)=141ms
254
+ ws_connecting........: avg=210.83ms min=143.01ms med=195.22ms max=1.27s p(90)=259.27ms p(95)=272.6ms
255
+ ```
256
+
78
257
  ## License
79
258
 
80
259
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -3,7 +3,24 @@
3
3
  module SolidCable
4
4
  class TrimJob < ActiveJob::Base
5
5
  def perform
6
- ::SolidCable::Message.trimmable.delete_all
6
+ return unless trim?
7
+
8
+ ::SolidCable::Message.transaction do
9
+ ids = ::SolidCable::Message.trimmable.non_blocking_lock.
10
+ limit(trim_batch_size).pluck(:id)
11
+ ::SolidCable::Message.where(id: ids).delete_all
12
+ end
7
13
  end
14
+
15
+ private
16
+ def trim_batch_size
17
+ ::SolidCable.trim_batch_size
18
+ end
19
+
20
+ def trim?
21
+ expires_per_write = (1 / trim_batch_size.to_f) * ::SolidCable.trim_chance
22
+
23
+ rand < (expires_per_write - expires_per_write.floor)
24
+ end
8
25
  end
9
26
  end
@@ -3,10 +3,28 @@
3
3
  module SolidCable
4
4
  class Message < SolidCable::Record
5
5
  scope :trimmable, lambda {
6
- where(created_at: ..::SolidCable.message_retention.ago)
6
+ where(created_at: ...::SolidCable.message_retention.ago)
7
7
  }
8
8
  scope :broadcastable, lambda { |channels, last_id|
9
- where(channel: channels).where(id: (last_id + 1)..).order(:id)
9
+ where(channel_hash: channel_hashes_for(channels)).
10
+ where(id: (last_id + 1)..).order(:id)
10
11
  }
12
+
13
+ class << self
14
+ def broadcast(channel, payload)
15
+ insert({ created_at: Time.current, channel:, payload:,
16
+ channel_hash: channel_hash_for(channel) })
17
+ end
18
+
19
+ def channel_hashes_for(channels)
20
+ channels.map { |channel| channel_hash_for(channel) }
21
+ end
22
+
23
+ # Need to unpack this as a signed integer since Postgresql and SQLite
24
+ # don't support unsigned integers
25
+ def channel_hash_for(channel)
26
+ Digest::SHA256.digest(channel.to_s).unpack1("q>")
27
+ end
28
+ end
11
29
  end
12
30
  end
@@ -5,5 +5,13 @@ module SolidCable
5
5
  self.abstract_class = true
6
6
 
7
7
  connects_to(**SolidCable.connects_to) if SolidCable.connects_to.present?
8
+
9
+ def self.non_blocking_lock
10
+ if SolidCable.use_skip_locked
11
+ lock(Arel.sql("FOR UPDATE SKIP LOCKED"))
12
+ else
13
+ lock
14
+ end
15
+ end
8
16
  end
9
17
  end
@@ -15,7 +15,9 @@ module ActionCable
15
15
  end
16
16
 
17
17
  def broadcast(channel, payload)
18
- ::SolidCable::Message.insert({ channel:, payload: })
18
+ ::SolidCable::Message.broadcast(channel, payload)
19
+
20
+ ::SolidCable::TrimJob.perform_now if ::SolidCable.autotrim?
19
21
  end
20
22
 
21
23
  def subscribe(channel, callback, success_callback = nil)
@@ -29,90 +31,86 @@ module ActionCable
29
31
  delegate :shutdown, to: :listener
30
32
 
31
33
  private
32
-
33
- def listener
34
- @listener || @server.mutex.synchronize do
35
- @listener ||= Listener.new(@server.event_loop)
34
+ def listener
35
+ @listener || @server.mutex.synchronize do
36
+ @listener ||= Listener.new(@server.event_loop)
37
+ end
36
38
  end
37
- end
38
39
 
39
- class Listener < ::ActionCable::SubscriptionAdapter::SubscriberMap
40
- def initialize(event_loop)
41
- super()
40
+ class Listener < ::ActionCable::SubscriptionAdapter::SubscriberMap
41
+ def initialize(event_loop)
42
+ super()
42
43
 
43
- @event_loop = event_loop
44
+ @event_loop = event_loop
44
45
 
45
- @thread = Thread.new do
46
- Thread.current.abort_on_exception = true
47
- listen
46
+ @thread = Thread.new do
47
+ Thread.current.abort_on_exception = true
48
+ listen
49
+ end
48
50
  end
49
- end
50
51
 
51
- def listen
52
- while running?
53
- with_polling_volume { broadcast_messages }
52
+ def listen
53
+ while running?
54
+ with_polling_volume { broadcast_messages }
54
55
 
55
- sleep ::SolidCable.polling_interval
56
+ sleep ::SolidCable.polling_interval
57
+ end
56
58
  end
57
- end
58
59
 
59
- def shutdown
60
- self.running = false
61
- Thread.pass while thread.alive?
62
- end
63
-
64
- def add_channel(channel, on_success)
65
- channels.add(channel)
66
- event_loop.post(&on_success) if on_success
67
- end
68
-
69
- def remove_channel(channel)
70
- channels.delete(channel)
60
+ def shutdown
61
+ self.running = false
62
+ Thread.pass while thread.alive?
63
+ end
71
64
 
72
- ::SolidCable::TrimJob.perform_now if ::SolidCable.autotrim?
73
- end
65
+ def add_channel(channel, on_success)
66
+ channels.add(channel)
67
+ event_loop.post(&on_success) if on_success
68
+ end
74
69
 
75
- def invoke_callback(*)
76
- event_loop.post { super }
77
- end
70
+ def remove_channel(channel)
71
+ channels.delete(channel)
72
+ end
78
73
 
79
- private
74
+ def invoke_callback(*)
75
+ event_loop.post { super }
76
+ end
80
77
 
81
- attr_reader :event_loop, :thread
82
- attr_writer :running, :last_id
78
+ private
79
+ attr_reader :event_loop, :thread
80
+ attr_writer :running, :last_id
83
81
 
84
- def running?
85
- if defined?(@running)
86
- @running
87
- else
88
- self.running = true
89
- end
90
- end
82
+ def running?
83
+ if defined?(@running)
84
+ @running
85
+ else
86
+ self.running = true
87
+ end
88
+ end
91
89
 
92
- def last_id
93
- @last_id ||= ::SolidCable::Message.maximum(:id) || 0
94
- end
90
+ def last_id
91
+ @last_id ||= ::SolidCable::Message.maximum(:id) || 0
92
+ end
95
93
 
96
- def channels
97
- @channels ||= Set.new
98
- end
94
+ def channels
95
+ @channels ||= Set.new
96
+ end
99
97
 
100
- def broadcast_messages
101
- ::SolidCable::Message.broadcastable(channels, last_id).
102
- each do |message|
103
- broadcast(message.channel, message.payload)
104
- self.last_id = message.id
98
+ def broadcast_messages
99
+ ::SolidCable::Message.broadcastable(channels, last_id).
100
+ each do |message|
101
+ broadcast(message.channel, message.payload)
102
+ self.last_id = message.id
103
+ end
105
104
  end
106
- end
107
105
 
108
- def with_polling_volume
109
- if ::SolidCable.silence_polling?
110
- ActiveRecord::Base.logger.silence { yield }
111
- else
112
- yield
113
- end
106
+ def with_polling_volume
107
+ if ::SolidCable.silence_polling?
108
+ ActiveRecord::Base.logger.silence { yield }
109
+ else
110
+ yield
111
+ end
112
+ end
114
113
  end
115
- end
116
114
  end
117
115
  end
118
116
  end
@@ -1,3 +1,7 @@
1
+ # Async adapter only works within the same process, so for manually triggering cable updates from a console,
2
+ # and seeing results in the browser, you must do so from the web console (running inside the dev process),
3
+ # not a terminal started via bin/rails console! Add "console" to any action or any ERB template view
4
+ # to make the web console appear.
1
5
  development:
2
6
  adapter: async
3
7
 
@@ -1,13 +1,11 @@
1
- # frozen_string_literal: true
2
-
3
1
  ActiveRecord::Schema[7.1].define(version: 1) do
4
2
  create_table "solid_cable_messages", force: :cascade do |t|
5
- t.text "channel"
6
- t.text "payload"
3
+ t.binary "channel", limit: 1024, null: false
4
+ t.binary "payload", limit: 536870912, null: false
7
5
  t.datetime "created_at", null: false
8
- t.datetime "updated_at", null: false
9
- t.index ["channel"], name: "index_solid_cable_messages_on_channel",
10
- length: 500
6
+ t.integer "channel_hash", limit: 8, null: false
7
+ t.index ["channel"], name: "index_solid_cable_messages_on_channel"
8
+ t.index ["channel_hash"], name: "index_solid_cable_messages_on_channel_hash"
11
9
  t.index ["created_at"], name: "index_solid_cable_messages_on_created_at"
12
10
  end
13
11
  end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Updates Solid Cable migrations
3
+
4
+ Example:
5
+ bin/rails generate solid_cable:update
6
+
7
+ This will perform the following:
8
+ Installs new Solid Cable migrations
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateCompactChannel < ActiveRecord::Migration[7.2]
4
+ def change
5
+ change_column :solid_cable_messages, :channel, :binary, limit: 1024, null: false
6
+ add_column :solid_cable_messages, :channel_hash, :integer, limit: 8, if_not_exists: true
7
+ add_index :solid_cable_messages, :channel_hash, if_not_exists: true
8
+ change_column :solid_cable_messages, :payload, :binary, limit: 536_870_912, null: false
9
+
10
+ SolidCable::Message.find_each do |msg|
11
+ msg.update(channel_hash: SolidCable::Message.channel_hash_for(msg.channel))
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/active_record"
5
+
6
+ class SolidCable::UpdateGenerator < Rails::Generators::Base
7
+ include ActiveRecord::Generators::Migration
8
+
9
+ source_root File.expand_path("templates", __dir__)
10
+
11
+ def copy_files
12
+ migration_template "db/migrate/create_compact_channel.rb",
13
+ "db/cable_migrate/create_compact_channel.rb"
14
+ end
15
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidCable
4
- VERSION = "2.0.2"
4
+ VERSION = "3.0.1"
5
5
  end
data/lib/solid_cable.rb CHANGED
@@ -11,7 +11,7 @@ module SolidCable
11
11
  end
12
12
 
13
13
  def silence_polling?
14
- !!cable_config.silence_polling
14
+ cable_config.silence_polling != false
15
15
  end
16
16
 
17
17
  def polling_interval
@@ -26,19 +26,40 @@ module SolidCable
26
26
  cable_config.autotrim != false
27
27
  end
28
28
 
29
- private
29
+ def trim_batch_size
30
+ if (size = cable_config.trim_batch_size.to_i) < 2
31
+ 100
32
+ else
33
+ size
34
+ end
35
+ end
30
36
 
31
- def cable_config
32
- Rails.application.config_for("cable")
37
+ def use_skip_locked
38
+ cable_config.use_skip_locked != false
33
39
  end
34
40
 
35
- def parse_duration(duration, default:)
36
- if duration.present?
37
- *amount, units = duration.to_s.split(".")
38
- amount.join(".").to_f.public_send(units)
39
- else
40
- default
41
- end
41
+ # For every write that we do, we attempt to delete trim_chance times as
42
+ # many records. This ensures there is downward pressure on the cache size
43
+ # while there is valid data to delete. Read this as 'every time the trim job
44
+ # runs theres a trim_multiplier chance this trims'. Adjust number to make it
45
+ # more or less likely to trim. Only works like this if trim_batch_size is
46
+ # 100
47
+ def trim_chance
48
+ 2
42
49
  end
50
+
51
+ private
52
+ def cable_config
53
+ Rails.application.config_for("cable")
54
+ end
55
+
56
+ def parse_duration(duration, default:)
57
+ if duration.present?
58
+ *amount, units = duration.to_s.split(".")
59
+ amount.join(".").to_f.public_send(units)
60
+ else
61
+ default
62
+ end
63
+ end
43
64
  end
44
65
  end
@@ -3,6 +3,10 @@
3
3
  desc "Copy over the schema and set cable adapter for Solid Cable"
4
4
  namespace :solid_cable do
5
5
  task :install do
6
- Rails::Command.invoke :generate, ["solid_cable:install"]
6
+ Rails::Command.invoke :generate, [ "solid_cable:install" ]
7
+ end
8
+
9
+ task :update do
10
+ Rails::Command.invoke :generate, [ "solid_cable:update" ]
7
11
  end
8
12
  end
metadata CHANGED
@@ -1,17 +1,59 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid_cable
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Pezza
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-12 00:00:00.000000000 Z
11
+ date: 2024-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rails
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '7.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '7.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activejob
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '7.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '7.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: actioncable
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '7.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '7.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: railties
15
57
  requirement: !ruby/object:Gem::Requirement
16
58
  requirements:
17
59
  - - ">="
@@ -42,6 +84,9 @@ files:
42
84
  - lib/generators/solid_cable/install/install_generator.rb
43
85
  - lib/generators/solid_cable/install/templates/config/cable.yml
44
86
  - lib/generators/solid_cable/install/templates/db/cable_schema.rb
87
+ - lib/generators/solid_cable/update/USAGE
88
+ - lib/generators/solid_cable/update/templates/db/migrate/create_compact_channel.rb
89
+ - lib/generators/solid_cable/update/update_generator.rb
45
90
  - lib/solid_cable.rb
46
91
  - lib/solid_cable/engine.rb
47
92
  - lib/solid_cable/railtie.rb
@@ -52,7 +97,7 @@ licenses:
52
97
  - MIT
53
98
  metadata:
54
99
  homepage_uri: http://github.com/npezza93/solid_cable
55
- source_code_uri: http://github.com/npezza93/solid_cable
100
+ source_code_uri: http://github.com/rails/solid_cable
56
101
  rubygems_mfa_required: 'true'
57
102
  post_install_message:
58
103
  rdoc_options: []