Class: Ask::State::Memory

Inherits:
Adapter
  • Object
show all
Defined in:
lib/ask/state.rb

Overview

In-process state backend backed by a Hash. All operations are thread-safe via a Mutex. Data is not persisted — lost on process exit.

Instance Method Summary collapse

Constructor Details

#initializeMemory

Returns a new instance of Memory.



156
157
158
159
160
161
162
# File 'lib/ask/state.rb', line 156

def initialize
  @data = {}
  @locks = {}
  @queues = {}
  @lists = {}
  @mutex = Mutex.new
end

Instance Method Details

#acquire_lock(key, ttl: 10) ⇒ Object

-- locking --



208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/ask/state.rb', line 208

def acquire_lock(key, ttl: 10)
  @mutex.synchronize do
    existing = @locks[key]
    if existing.nil? || existing.expired?
      lock = Lock.new(
        id: key,
        token: SecureRandom.hex(16),
        expires_at: Time.now + ttl
      )
      @locks[key] = lock
      lock
    end
  end
end

#closeObject

-- lifecycle --



300
301
302
303
304
305
306
307
# File 'lib/ask/state.rb', line 300

def close
  @mutex.synchronize do
    @data.clear
    @locks.clear
    @queues.clear
    @lists.clear
  end
end

#delete(key) ⇒ Object



184
185
186
# File 'lib/ask/state.rb', line 184

def delete(key)
  @mutex.synchronize { @data.delete(key) }
end

#dequeue(queue) ⇒ Object



250
251
252
253
254
255
256
257
# File 'lib/ask/state.rb', line 250

def dequeue(queue)
  @mutex.synchronize do
    q = @queues[queue]
    return nil unless q&.any?

    q.shift
  end
end

#enqueue(queue, value) ⇒ Object

-- queues --



237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/ask/state.rb', line 237

def enqueue(queue, value)
  @mutex.synchronize do
    @queues[queue] ||= []
    entry = QueueEntry.new(
      id: SecureRandom.uuid,
      value: value,
      enqueued_at: Time.now
    )
    @queues[queue] << entry
    entry
  end
end

#get(key) ⇒ Object

-- key-value --



166
167
168
169
170
171
172
173
# File 'lib/ask/state.rb', line 166

def get(key)
  @mutex.synchronize do
    expiry = @data[key]&.dig(:expires_at)
    return nil if expiry && Time.now >= expiry

    @data[key]&.dig(:value)
  end
end

#list_append(key, value, max_length: nil) ⇒ Object

-- lists --



267
268
269
270
271
272
273
# File 'lib/ask/state.rb', line 267

def list_append(key, value, max_length: nil)
  @mutex.synchronize do
    @lists[key] ||= []
    @lists[key] << value
    @lists[key].shift if max_length && @lists[key].length > max_length
  end
end

#list_range(key, start = 0, stop = -1)) ⇒ Object



275
276
277
278
279
280
281
282
283
284
285
# File 'lib/ask/state.rb', line 275

def list_range(key, start = 0, stop = -1)
  @mutex.synchronize do
    list = @lists[key] || []
    return list if start == 0 && stop == -1

    stop = list.length - 1 if stop == -1 || stop >= list.length
    return [] if start > stop

    list[start..stop] || []
  end
end

#list_remove(key, value) ⇒ Object



287
288
289
290
291
292
293
294
295
296
# File 'lib/ask/state.rb', line 287

def list_remove(key, value)
  @mutex.synchronize do
    list = @lists[key]
    return 0 unless list

    before = list.length
    list.delete(value)
    before - list.length
  end
end

#queue_depth(queue) ⇒ Object



259
260
261
262
263
# File 'lib/ask/state.rb', line 259

def queue_depth(queue)
  @mutex.synchronize do
    (@queues[queue] || []).length
  end
end

#release_lock(key, lock) ⇒ Object



223
224
225
226
227
228
229
230
231
232
233
# File 'lib/ask/state.rb', line 223

def release_lock(key, lock)
  @mutex.synchronize do
    current = @locks[key]
    if current && current.token == lock.token && !current.expired?
      @locks.delete(key)
      true
    else
      false
    end
  end
end

#set(key, value, ttl: nil) ⇒ Object



175
176
177
178
179
180
181
182
# File 'lib/ask/state.rb', line 175

def set(key, value, ttl: nil)
  @mutex.synchronize do
    @data[key] = {
      value: value,
      expires_at: ttl ? Time.now + ttl : nil
    }
  end
end

#set_if_not_exists(key, value, ttl: nil) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/ask/state.rb', line 188

def set_if_not_exists(key, value, ttl: nil)
  @mutex.synchronize do
    if @data.key?(key)
      expiry = @data[key][:expires_at]
      return false if expiry.nil? || Time.now < expiry

      # Key expired — treat as nonexistent
      @data.delete(key)
    end

    @data[key] = {
      value: value,
      expires_at: ttl ? Time.now + ttl : nil
    }
    true
  end
end