Files
ubicloud/lib/sem_snap.rb
Jeremy Evans 863af1b8a4 Do not create a Semaphore if there is no related Strand
This is designed to handle race conditions, where the strand is
deleted between when the object is retrieved and when incr_* is
called.

One caller of Semaphore.incr, SemSnap.incr, relies on the
Semaphore being returned.  Update this method handle the
case where nil is returned by Semaphore.incr.

An alternative approach would be raising an exception for this
case.  That can potentially alert us to cases that pass invalid
strand ids, but it would require more code to handle race
conditions.  We couple have separate methods, one that raised by
default, and another than can be used in cases where we know the
strand id is correct, but want to avoid a race condition.  However,
I'm not sure the complexity is worth it.

While here, omit unnecessary hash values, switch from
create_with_id to create, and explicit dataset call, and unnecessary
explicit receiver.
2025-03-20 13:13:49 -07:00

58 lines
1.1 KiB
Ruby

# frozen_string_literal: true
class SemSnap
def initialize(strand_id, deferred: false)
@deferred = deferred
@strand_id = strand_id
@extant = Hash.new { |h, k| h[k.intern] = [] }
@defer_delete = []
Semaphore.where(strand_id: @strand_id).each do |sem|
add_semaphore_instance_to_snapshot(sem)
end
end
def self.use(strand_id)
new(strand_id, deferred: true).use do |snap|
yield snap
end
end
def use
yield self
ensure
apply
end
def set?(name)
name = name.intern
@extant.include?(name)
end
def decr(name)
name = name.intern
ids = @extant.delete(name)
return unless ids && ids.length > 0
@defer_delete.concat(ids)
apply unless @deferred
end
def incr(name)
if (semaphore = Semaphore.incr(@strand_id, name))
add_semaphore_instance_to_snapshot(semaphore)
end
end
private
def apply
return if @defer_delete.empty?
Semaphore.where(strand_id: @strand_id, id: @defer_delete).destroy
@defer_delete.clear
end
def add_semaphore_instance_to_snapshot(sem)
@extant[sem.name.intern] << sem.id
end
end