From 7a49aafac3a701a9a7408c09275606c3072106ca Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Mon, 25 Apr 2022 10:23:44 -0500 Subject: [PATCH] ExpiringLock instead of require expired We *want* to try billing a little before expiry, to prevent the account lapsing. So use an ExpiringLock to prevent double-billing races. --- lib/bill_plan_command.rb | 45 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/lib/bill_plan_command.rb b/lib/bill_plan_command.rb index e46bb50..25a06e1 100644 --- a/lib/bill_plan_command.rb +++ b/lib/bill_plan_command.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true +require_relative "expiring_lock" + class BillPlanCommand def self.for(customer) return ForUnregistered.new(customer) unless customer.registered? - return ForNotExpired.new(customer) unless customer.expires_at <= Time.now unless customer.balance > customer.monthly_price return ForLowBalance.new(customer) @@ -16,19 +17,30 @@ class BillPlanCommand @customer = customer end + def reload_customer(db) + @customer = Command.execution.customer_repo.with(db: db) + .find(customer_id).sync + end + def call - billed = @customer.bill_plan(note: "Renew account plan") { |db| - @customer = Command.execution.customer_repo.with(db: db) - .find(@customer.customer_id).sync - @customer.balance > @customer.monthly_price && - @customer.expires_at <= Time.now - } - Command.reply do |reply| - reply.note_type = billed ? :info : :error - reply.note_text = "#{@customer.customer_id}#{billed ? '' : ' not'} billed" + ExpiringLock.new("jmp_customer_bill_plan-#{customer_id}").with { + @customer.bill_plan(note: "Renew account plan") { |db| + reload_customer(db).balance > @customer.monthly_price + } + }.then do |billed| + Command.reply do |reply| + reply.note_type = billed ? :info : :error + reply.note_text = "#{customer_id}#{billed ? '' : ' not'} billed" + end end end +protected + + def customer_id + @customer.customer_id + end + class ForLowBalance def initialize(customer) @customer = customer @@ -76,17 +88,4 @@ class BillPlanCommand end end end - - class ForNotExpired - def initialize(customer) - @customer = customer - end - - def call - Command.reply do |reply| - reply.note_type = :error - reply.note_text = "#{@customer.customer_id} is not expired" - end - end - end end -- 2.38.5