mirror of
https://github.com/discourse/discourse.git
synced 2025-02-21 03:19:10 +00:00
DEV: new lock
step for services (#30872)
That allows services to wrap steps within a Distributed lock (mutex).
This commit is contained in:
parent
049b6b8f54
commit
a16b2f2248
@ -112,6 +112,10 @@ module Service
|
||||
steps << TransactionStep.new(&block)
|
||||
end
|
||||
|
||||
def lock(*keys, &block)
|
||||
steps << LockStep.new(*keys, &block)
|
||||
end
|
||||
|
||||
def options(&block)
|
||||
klass = Class.new(Service::OptionsBase).tap { _1.class_eval(&block) }
|
||||
const_set("Options", klass)
|
||||
@ -257,6 +261,43 @@ module Service
|
||||
end
|
||||
end
|
||||
|
||||
# @!visibility private
|
||||
class LockStep < Step
|
||||
include StepsHelpers
|
||||
|
||||
attr_reader :steps
|
||||
|
||||
def initialize(*keys, &block)
|
||||
@keys = keys
|
||||
@steps = []
|
||||
instance_exec(&block)
|
||||
end
|
||||
|
||||
def run_step
|
||||
success =
|
||||
begin
|
||||
DistributedMutex.synchronize(lock_name) do
|
||||
steps.each { |step| step.call(instance, context) }
|
||||
:success
|
||||
end
|
||||
rescue Discourse::ReadOnly
|
||||
:read_only
|
||||
end
|
||||
|
||||
if success != :success
|
||||
context[result_key].fail(lock_not_aquired: true)
|
||||
context.fail!
|
||||
end
|
||||
end
|
||||
|
||||
def lock_name
|
||||
[
|
||||
context.__service_class__.to_s.underscore,
|
||||
*@keys.flat_map { |key| [key, context[:params].send(key)] },
|
||||
].join(":")
|
||||
end
|
||||
end
|
||||
|
||||
# @!visibility private
|
||||
class TryStep < Step
|
||||
include StepsHelpers
|
||||
|
@ -167,6 +167,20 @@ RSpec.describe Service::Runner do
|
||||
end
|
||||
end
|
||||
|
||||
class LockService
|
||||
include Service::Base
|
||||
|
||||
params do
|
||||
attribute :post_id, :integer
|
||||
attribute :user_id, :integer
|
||||
end
|
||||
|
||||
lock(:post_id, :user_id) { step :locked_step }
|
||||
|
||||
def locked_step
|
||||
end
|
||||
end
|
||||
|
||||
describe ".call" do
|
||||
subject(:runner) { described_class.call(service, dependencies, &actions_block) }
|
||||
|
||||
@ -501,5 +515,36 @@ RSpec.describe Service::Runner do
|
||||
expect(runner).to eq :success
|
||||
end
|
||||
end
|
||||
|
||||
context "when aquiring a lock" do
|
||||
let(:service) { LockService }
|
||||
let(:dependencies) { { params: { post_id: 123, user_id: 456 } } }
|
||||
let(:actions) { <<-BLOCK }
|
||||
proc do
|
||||
on_success { :success }
|
||||
on_failure { :failure }
|
||||
end
|
||||
BLOCK
|
||||
|
||||
it "runs successfully" do
|
||||
expect(runner).to eq :success
|
||||
end
|
||||
end
|
||||
|
||||
context "when failing to acquire a lock" do
|
||||
let(:service) { LockService }
|
||||
let(:dependencies) { { params: { post_id: 123, user_id: 456 } } }
|
||||
let(:actions) { <<-BLOCK }
|
||||
proc do
|
||||
on_success { :success }
|
||||
on_failure { :failure }
|
||||
end
|
||||
BLOCK
|
||||
|
||||
it "fails the service" do
|
||||
DistributedMutex.stubs(:synchronize).returns
|
||||
expect(runner).to eq :failure
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user