Class: Collavre::CreativePresenceStore

Inherits:
Object
  • Object
show all
Defined in:
app/models/collavre/creative_presence_store.rb

Constant Summary collapse

KEY_PREFIX =
"creative_presence:"
LOCK_TTL =

seconds

2
PRESENCE_TTL =

minutes

10

Class Method Summary collapse

Class Method Details

.add(root_id, user_id) ⇒ Object



7
8
9
10
11
12
13
14
15
16
# File 'app/models/collavre/creative_presence_store.rb', line 7

def self.add(root_id, user_id)
  with_lock(root_id) do
    ids = list(root_id)
    unless ids.include?(user_id)
      ids << user_id
    end
    Rails.cache.write(key(root_id), ids, expires_in: PRESENCE_TTL.minutes)
    ids
  end
end

.key(root_id) ⇒ Object



32
33
34
# File 'app/models/collavre/creative_presence_store.rb', line 32

def self.key(root_id)
  "#{KEY_PREFIX}#{root_id}"
end

.list(root_id) ⇒ Object



28
29
30
# File 'app/models/collavre/creative_presence_store.rb', line 28

def self.list(root_id)
  Rails.cache.read(key(root_id)) || []
end

.lock_key(root_id) ⇒ Object



36
37
38
# File 'app/models/collavre/creative_presence_store.rb', line 36

def self.lock_key(root_id)
  "#{KEY_PREFIX}lock:#{root_id}"
end

.remove(root_id, user_id) ⇒ Object



18
19
20
21
22
23
24
25
26
# File 'app/models/collavre/creative_presence_store.rb', line 18

def self.remove(root_id, user_id)
  with_lock(root_id) do
    ids = list(root_id)
    if ids.delete(user_id)
      Rails.cache.write(key(root_id), ids, expires_in: PRESENCE_TTL.minutes)
    end
    ids
  end
end

.with_lock(root_id, &block) ⇒ Object

Simple spin-lock using cache. Prevents race conditions in multi-process environments.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'app/models/collavre/creative_presence_store.rb', line 41

def self.with_lock(root_id, &block)
  lk = lock_key(root_id)
  # Try to acquire lock (expires after LOCK_TTL to avoid deadlocks)
  10.times do
    if Rails.cache.write(lk, true, unless_exist: true, expires_in: LOCK_TTL.seconds)
      begin
        return block.call
      ensure
        Rails.cache.delete(lk)
      end
    end
    sleep(0.05)
  end
  # Fallback: proceed without lock (better than blocking forever)
  block.call
end