diff --git a/README.md b/README.md index 17fc8ad..9b3297c 100644 --- a/README.md +++ b/README.md @@ -62,13 +62,18 @@ production: The options are: - `connects_to` - set the Active Record database configuration for the Solid Cable models. All options available in Active Record can be used here. -- `polling_interval` - sets the frequency of the polling interval. -- `message_retention` - sets the retention time for messages kept in the database. Used as the cut-off when trimming is performed. +- `polling_interval` - sets the frequency of the polling interval. (Defaults to + 0.1.seconds) +- `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) +- `autotrim` - sets wether you want Solid Cable to handle autotrimming messages. + (Defaults to true) ## Trimming -Currently, messages are kept around forever, and have to be manually pruned via the `SolidCable::PruneJob.perform_later` job. -This job uses the `message_retention` setting to determine how long messages are to be kept around. +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. + +Autotrimming can negatively impact performance depending on your workload because it is doing a delete on ubsubscribe. If +you would prefer, you can disable autotrimming by setting `autotrim: false` and you can manually enqueue the job later, `SolidCable::PruneJob.perform_later`, or run it on a recurring interval out of band. ## License diff --git a/app/jobs/solid_cable/prune_job.rb b/app/jobs/solid_cable/trim_job.rb similarity index 50% rename from app/jobs/solid_cable/prune_job.rb rename to app/jobs/solid_cable/trim_job.rb index 87d3ece..57dc8a3 100644 --- a/app/jobs/solid_cable/prune_job.rb +++ b/app/jobs/solid_cable/trim_job.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true module SolidCable - class PruneJob < ActiveJob::Base + class TrimJob < ActiveJob::Base def perform - ::SolidCable::Message.prunable.delete_all + ::SolidCable::Message.trimmable.delete_all end end end diff --git a/app/models/solid_cable/message.rb b/app/models/solid_cable/message.rb index 4441599..c210fd4 100644 --- a/app/models/solid_cable/message.rb +++ b/app/models/solid_cable/message.rb @@ -2,7 +2,7 @@ module SolidCable class Message < SolidCable::Record - scope :prunable, lambda { + scope :trimmable, lambda { where(created_at: ..::SolidCable.message_retention.ago) } scope :broadcastable, lambda { |channels, last_id| diff --git a/lib/action_cable/subscription_adapter/solid_cable.rb b/lib/action_cable/subscription_adapter/solid_cable.rb index fbd7dce..37521f0 100644 --- a/lib/action_cable/subscription_adapter/solid_cable.rb +++ b/lib/action_cable/subscription_adapter/solid_cable.rb @@ -64,6 +64,8 @@ def add_channel(channel, on_success) def remove_channel(channel) channels.delete(channel) + + ::SolidCable::TrimJob.perform_now if ::SolidCable.autotrim? end def invoke_callback(*) diff --git a/lib/solid_cable.rb b/lib/solid_cable.rb index 3200398..4ee95b3 100644 --- a/lib/solid_cable.rb +++ b/lib/solid_cable.rb @@ -22,6 +22,10 @@ def message_retention parse_duration(cable_config.message_retention, default: 1.day) end + def autotrim? + cable_config.autotrim != false + end + private def cable_config diff --git a/test/config_helpers.rb b/test/config_helpers.rb new file mode 100644 index 0000000..ed672c1 --- /dev/null +++ b/test/config_helpers.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module ConfigHelpers + extend ActiveSupport::Concern + + class ConfigStub + def initialize(**) + @config = ActiveSupport::OrderedOptions.new. + update({ adapter: :test }.merge(**)) + end + + def config_for(_file) + @config + end + end + + def with_cable_config(**) + Rails.stub(:application, ConfigStub.new(**)) { yield } + end +end diff --git a/test/lib/action_cable/subscription_adapter/solid_cable_test.rb b/test/lib/action_cable/subscription_adapter/solid_cable_test.rb index e2ed971..9488a9c 100644 --- a/test/lib/action_cable/subscription_adapter/solid_cable_test.rb +++ b/test/lib/action_cable/subscription_adapter/solid_cable_test.rb @@ -5,8 +5,10 @@ require "active_support/core_ext/hash/indifferent_access" require "pathname" +require "config_helpers" class ActionCable::SubscriptionAdapter::SolidCableTest < ActionCable::TestCase + include ConfigHelpers WAIT_WHEN_EXPECTING_EVENT = 1 WAIT_WHEN_NOT_EXPECTING_EVENT = 0.2 @@ -28,7 +30,8 @@ def setup end def cable_config - { adapter: "solid_cable", polling_interval: "0.01.seconds" } + { adapter: "solid_cable", message_retention: "1.second", + polling_interval: "0.01.seconds" } end def teardown @@ -82,6 +85,24 @@ def test_broadcast_after_unsubscribe assert_empty keep_queue end + def test_trims_after_unsubscribe + with_cable_config message_retention: "1.second" do + keep_queue = nil + subscribe_as_queue("channel") do |queue| + keep_queue = queue + + @tx_adapter.broadcast("channel", "hello world") + + assert_equal 1, SolidCable::Message.where(channel: "channel").count + assert_equal "hello world", queue.pop + sleep 2 + end + sleep 3 + + assert_equal 0, SolidCable::Message.where(channel: "channel").count + end + end + def test_multiple_broadcast subscribe_as_queue("channel") do |queue| @tx_adapter.broadcast("channel", "bananas") diff --git a/test/solid_cable_test.rb b/test/solid_cable_test.rb index 2af4b58..0228197 100644 --- a/test/solid_cable_test.rb +++ b/test/solid_cable_test.rb @@ -1,9 +1,29 @@ # frozen_string_literal: true require "test_helper" +require "config_helpers" class SolidCableTest < ActiveSupport::TestCase + include ConfigHelpers + test "it has a version number" do assert SolidCable::VERSION end + + test "autotrimming when nothing is set" do + assert_not Rails.application.config_for("cable").key?(:autotrim) + assert SolidCable.autotrim? + end + + test "autotrimming when set to false" do + with_cable_config autotrim: false do + assert_not SolidCable.autotrim? + end + end + + test "autotrimming when set to true" do + with_cable_config autotrim: true do + assert SolidCable.autotrim? + end + end end diff --git a/test/test_helper.rb b/test/test_helper.rb index de9fbec..1bca1a9 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -8,6 +8,7 @@ "../test/dummy/db/migrate", __dir__ )] require "rails/test_help" +require "minitest/autorun" # Load fixtures from the engine if ActiveSupport::TestCase.respond_to?(:fixture_paths=)