Class: Trebuchet::Backend::RedisHammerspaced

Inherits:
Redis
  • Object
show all
Defined in:
lib/trebuchet/backend/redis_hammerspaced.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Redis

#get_all_history, #get_archived_feature_names, #get_history, #get_sentinel, #remove_feature, #remove_strategy, #set_strategy, #store_history, #unpack_strategy, #update_sentinel

Constructor Details

#initialize(*args) ⇒ RedisHammerspaced

Returns a new instance of RedisHammerspaced.



13
14
15
16
17
# File 'lib/trebuchet/backend/redis_hammerspaced.rb', line 13

def initialize(*args)
  # args.first must be a hash
  super(*args)
  @hammerspace = args.first[:hammerspace]
end

Instance Attribute Details

#namespaceObject

This class will rely on a cron job to sync all trebuchet features to local hammerspace thus this class never directly updates hammerspace We also cache in memory the features and rely on before_filter to lazily invalidate local cache



11
12
13
# File 'lib/trebuchet/backend/redis_hammerspaced.rb', line 11

def namespace
  @namespace
end

Instance Method Details

#append_strategy(feature_name, strategy, options = nil) ⇒ Object



65
66
67
68
69
70
# File 'lib/trebuchet/backend/redis_hammerspaced.rb', line 65

def append_strategy(feature_name, strategy, options = nil)
  # though we can't clear the strategy for all active instances
  # this will clear the cache in the console environment to show current settings
  clear_cached_strategies
  super(feature_name, strategy, options)
end

#cache_strategy(feature_name, strategy) ⇒ Object



72
73
74
# File 'lib/trebuchet/backend/redis_hammerspaced.rb', line 72

def cache_strategy(feature_name, strategy)
  cached_strategies[feature_name] = strategy
end

#cached_strategiesObject



76
77
78
# File 'lib/trebuchet/backend/redis_hammerspaced.rb', line 76

def cached_strategies
  @cached_strategies ||= Hash.new
end

#clear_cached_strategiesObject



80
81
82
# File 'lib/trebuchet/backend/redis_hammerspaced.rb', line 80

def clear_cached_strategies
  @cached_strategies = nil
end

#generate_hammerspace_hash(feature_names, features, last_updated) ⇒ Object

feature_names is an array of strings features is an array of strategies Each strategy is of form [“<key1>”, “<value1>”, “<key2>”, “<value2>”…] We need to decode the values because they are in string form (not actual hash)



117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/trebuchet/backend/redis_hammerspaced.rb', line 117

def generate_hammerspace_hash(feature_names, features, last_updated)
  hash = {
    sentinel_key => last_updated.to_s,
    feature_names_key => feature_names.to_json,
  }

  feature_names.zip(features) do |feature_name, feature|
    h = {}
    feature.each_slice(2) {|k,v| h[k]=JSON.load(v)}
    hash[feature_key(feature_name)] = h.to_json
  end
  hash
end

#get_feature_namesObject



59
60
61
62
63
# File 'lib/trebuchet/backend/redis_hammerspaced.rb', line 59

def get_feature_names
  # Read from hammerspace
  return [] unless @hammerspace.has_key?(feature_names_key)
  JSON.load(@hammerspace[feature_names_key])
end

#get_strategy(feature_name) ⇒ Object



19
20
21
22
23
24
25
26
27
# File 'lib/trebuchet/backend/redis_hammerspaced.rb', line 19

def get_strategy(feature_name)
  if cached_strategies.has_key?(feature_name)
    # use cached if available (even if value is nil)
    cached_strategies[feature_name]
  else
    # call to hammerspace
    cache_strategy feature_name, get_strategy_hammerspace(feature_name)
  end
end

#get_strategy_hammerspace(feature_name) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/trebuchet/backend/redis_hammerspaced.rb', line 29

def get_strategy_hammerspace(feature_name)
  # Read from hammerspace
  h = @hammerspace[feature_key(feature_name)]
  return nil unless h
  # h will be a string, we need to convert it back to Hash
  begin
    h = JSON.load(h)
  rescue
    return nil
  end
  unpack_strategy_hammerspace(h)
end

#refreshObject



84
85
86
87
88
89
90
91
# File 'lib/trebuchet/backend/redis_hammerspaced.rb', line 84

def refresh
  # We close and reopen hammerspace to see if we need to invalidate local cache
  uid = @hammerspace.uid
  @hammerspace.close
  if @hammerspace.uid != uid
    clear_cached_strategies
  end
end

#unpack_strategy_hammerspace(options) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/trebuchet/backend/redis_hammerspaced.rb', line 42

def unpack_strategy_hammerspace(options)
  # We don't need to further convert values
  # because it's already taken care of
  # by the refresh cron job
  # assumption here is that v will be an array and we
  # are using the first element for now
  # This makes the format compatible with redis backend
  return nil unless options.is_a?(Hash)
  [].tap do |a|
    options.each do |k, v|
      key = k.to_sym
      a << key
      a << v.first
    end
  end
end

#update_hammerspace(forced = false) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/trebuchet/backend/redis_hammerspaced.rb', line 93

def update_hammerspace(forced = false)
  last_updated = get_sentinel

  return if !forced && last_updated == @hammerspace[sentinel_key]

  feature_names = @redis.smembers(feature_names_key)

  features = @redis.pipelined do
    feature_names.each do |feature_name|
      @redis.hgetall(feature_key(feature_name))
    end
  end

  hash = generate_hammerspace_hash(feature_names, features, last_updated)

  @hammerspace.replace(hash)
  @hammerspace.close
  clear_cached_strategies
end