lonbaker (owner)

Fork Of

Revisions

gist: 110231 Download_button fork
public
Public Clone URL: git://gist.github.com/110231.git
Embed All Files: show embed
Text only #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
require 'redis'
 
module Nanite
  class State
    include Enumerable
    
    # this class encapsulates the state of a nanite system using redis as the
    # data store. here is the schema, for each agent we store a number of items,
    # for a nanite with the identity: nanite-foobar we store the following things:
    #
    # nanite-foobar: 0.72 # load average or 'status'
    # s-nanite-foobar: { /foo/bar, /foo/nik } # a SET of the provided services
    # t-nanite-foobar: 123456789 # unix timestamp of the last state update
    #
    # also we do an inverted index for quick lookup of agents providing a certain
    # service, so for each service the agent provides, we add the nanite to a SET
    # of all the nanites that provide said service:
    #
    # /gems/list: { nanite-foobar, nanite-nickelbag, nanite-another } # redis SET
    #
    # This way we can do a lookup of what nanites provide a set of services based
    # on redis SET intersection:
    #
    # nanites_for('/gems/list', '/customer/1')
    # => returns a nested array of nanites and their state that provide the intersection
    # of these two service tags
    
    def initialize
      @redis = Redis.new
    end
    
    def catch_redis_error(meth,&blk)
      blk.call
    rescue RedisError => e
      Nanite::Log.info("redis error in method: #{meth}")
      raise e
    end
    
    def [](nanite)
      catch_redis_error("[]") do
        status = @redis[nanite]
        timestamp = @redis["t-#{nanite}"]
        services = @redis.set_members("s-#{nanite}")
        return nil unless status && timestamp && services
        {:services => services, :status => status, :timestamp => timestamp.to_i}
      end
    end
    
    def []=(nanite, hsh)
      catch_redis_error("[]=") do
        update_state(nanite, hsh[:status], hsh[:services])
      end
    end
    
    def delete(nanite)
      catch_redis_error("delete") do
        redis.set_members("s-#{nanite}").each do |srv|
          @redis.set_delete(s, nanite)
        end
        @redis.delete nanite
        @redis.delete "s-#{nanite}"
        @redis.delete "t-#{nanite}"
      end
    end
    
    def all_services
      catch_redis_error("all_services") do
        @redis.set_members("nanite-services")
      end
    end
    
    def update_state(name, status, services)
      old_services = @redis.set_members("srv-#{name}")
      if old_services
        (old_services - services).each do |s|
          @redis.set_delete(s, name)
        end
      end
      @redis.delete("s-#{name}")
      services.each do |srv|
        @redis.set_add(srv, name)
        @redis.set_add("s-#{name}", srv)
      end
      @redis[name] = status
      @redis["t-#{name}"] = Time.now.to_i
    end
    
    def list_nanites
      catch_redis_error("list_nanites") do
        @redis.keys("nanite-*")
      end
    end
    
    def clear_state
      catch_redis_error("clear_state") do
        @redis.keys("*").each {|k| @redis.delete k}
      end
    end
    
    def each
      list_nanites.each do |nan|
        yield({:services => @redis["s-#{nan}"], :status => @redis[nan], :timestamp => @redis["t-#{nan}"]})
      end
    end
    
    def nanites_for(*srvs)
      catch_redis_error("nanites_for") do
        res = []
        @redis.set_intersect(srvs).each do |nan|
          res << [nan, self[nan]]
        end
        res
      end
    end
  end
end