Class: AsyncStorage::Allocator

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/async_storage/allocator.rb

Constant Summary collapse

CTRL =
{
  enqueued: '0',
  executed: '1',
  missing: nil,
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(repo, *args) ⇒ Allocator

Returns a new instance of Allocator.

Parameters:

  • repo (AsyncStorage::Repo)

    An instance of Repo

  • args (Array)

    An array with arguments to be fowarded to resolver#call



21
22
23
24
25
26
27
28
29
# File 'lib/async_storage/allocator.rb', line 21

def initialize(repo, *args)
  @repo = repo
  @args = args
  @naming = AsyncStorage::Naming.new(repo.resolver_class, *args)
  # It's different than the config.namespace.
  # Thinking about a directory structure.. The global namespace would be the root directory.
  # And the namespace under Repo level would be the subdirectory.
  @naming.prefix = repo.namespace if repo.namespace
end

Instance Attribute Details

#namingObject (readonly)

Returns the value of attribute naming.



17
18
19
# File 'lib/async_storage/allocator.rb', line 17

def naming
  @naming
end

Instance Method Details

#exist?Boolean

Check if a fresh value exist.

Returns:

  • (Boolean)

    True or False according the object existence



121
122
123
124
125
# File 'lib/async_storage/allocator.rb', line 121

def exist?
  breaker.run(fallback: -> { false }) do
    connection { |redis| redis.exists?(naming.head) && redis.exists?(naming.body) }
  end
end

#fresh?Boolean

Check if a fresh object exists into the storage

Returns:

  • (Boolean)

    true/false according to the object existence and freshness



139
140
141
142
143
# File 'lib/async_storage/allocator.rb', line 139

def fresh?
  breaker.run(fallback: -> { false }) do
    connection { |redis| redis.exists?(naming.body) && redis.ttl(naming.head) > 0 }
  end
end

#getObject, NilClass

Async get value with a given key

Returns:

  • (Object, NilClass)

    Return both stale or fresh object. If does not exist async call the retriever and return nil



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/async_storage/allocator.rb', line 34

def get
  breaker.run(fallback: -> { fetch! }) do
    connection do |redis|
      raw_head = redis.get(naming.head)
      case raw_head
      when CTRL[:executed], CTRL[:enqueued]
        read_body(redis) # Try to deliver stale content
      when CTRL[:missing]
        return update!(redis) unless async?

        perform_async(redis) # Enqueue background job to resolve content
        redis.set(naming.head, CTRL[:enqueued])
        read_body(redis) # Try to deliver stale content
      else
        raise AsyncStorage::Error, format('the key %<k>s have an invalid value. Only "1" or "0" values are expected. And we got %<v>p', v: raw_head, k: naming.head)
      end
    end
  end
end

#get!Object

Sync get value with a given value

Returns:

  • (Object)

    Return the result from resolver



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/async_storage/allocator.rb', line 57

def get!
  breaker.run(fallback: -> { fetch! }) do
    connection do |redis|
      raw_head = redis.get(naming.head)
      case raw_head
      when CTRL[:executed]
        read_body(redis) || begin
          update!(redis) unless redis.exists?(naming.body)
        end
      when CTRL[:missing], CTRL[:enqueued]
        update!(redis)
      else
        raise AsyncStorage::Error, format('the key %<k>s have an invalid value. Only "1" or "0" values are expected. And we got %<v>p', v: raw_head, k: naming.head)
      end
    end
  end
end

#invalidateBoolean

Expire object the object with a given key. The stale object will not be removed

Returns:

  • (Boolean)

    True or False according to the object existence



78
79
80
81
82
83
84
# File 'lib/async_storage/allocator.rb', line 78

def invalidate
  breaker.run(fallback: -> { false }) do
    connection do |redis|
      redis.del(naming.head) == 1
    end
  end
end

#invalidate!Boolean

Delete object with a given key.

Returns:

  • (Boolean)

    True or False according to the object existence



89
90
91
92
93
94
95
96
97
98
# File 'lib/async_storage/allocator.rb', line 89

def invalidate!
  breaker.run(fallback: -> { false }) do
    connection do |redis|
      redis.multi do |cli|
        cli.del(naming.body)
        cli.del(naming.head)
      end.include?(1)
    end
  end
end

#refreshObject, NilClass

Invalidate object with the given key and update content according to the strategy

Returns:

  • (Object, NilClass)

    Stale object or nil when it does not exist



103
104
105
106
107
# File 'lib/async_storage/allocator.rb', line 103

def refresh
  breaker.run(fallback: -> { fetch! }) do
    get.tap { invalidate }
  end
end

#refresh!Object

Fetch data from resolver and store it into redis

Returns:

  • (Object)

    Return the result from resolver



112
113
114
115
116
# File 'lib/async_storage/allocator.rb', line 112

def refresh!
  breaker.run(fallback: -> { fetch! }) do
    connection { |redis| update!(redis) }
  end
end

#stale?NilClass, Boolean

Check if object with a given key is stale

Returns:

  • (NilClass, Boolean)

    Return nil if the object does not exist or true/false according to the object freshness state



130
131
132
133
134
# File 'lib/async_storage/allocator.rb', line 130

def stale?
  breaker.run(fallback: -> { false }) do
    connection { |redis| redis.exists?(naming.body) && redis.ttl(naming.head) < 0 }
  end
end