joffice_redis 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
 - data/LICENSE-2.0.txt +202 -0
 - data/README.markdown +11 -0
 - data/Rakefile +29 -0
 - data/lib/joffice_redis.rb +35 -0
 - data/lib/joffice_redis/cache_model.rb +205 -0
 - data/lib/joffice_redis/hash_table.rb +73 -0
 - data/lib/joffice_redis/marshal.rb +65 -0
 - data/lib/joffice_redis/model_factory.rb +417 -0
 - data/lib/joffice_redis/redis_client_base.rb +121 -0
 - data/lib/joffice_redis/redis_manager.rb +213 -0
 - data/lib/joffice_redis/redis_method_factory.rb +445 -0
 - data/lib/joffice_redis/storages/presence_redis.rb +80 -0
 - metadata +132 -0
 
| 
         @@ -0,0 +1,121 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #encoding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
            =begin
         
     | 
| 
      
 3 
     | 
    
         
            +
            Copyright 2010 Denis Kokorin <virkdi@mail.ru>
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
               Licensed under the Apache License, Version 2.0 (the "License");
         
     | 
| 
      
 6 
     | 
    
         
            +
               you may not use this file except in compliance with the License.
         
     | 
| 
      
 7 
     | 
    
         
            +
               You may obtain a copy of the License at
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                   http://www.apache.org/licenses/LICENSE-2.0
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
               Unless required by applicable law or agreed to in writing, software
         
     | 
| 
      
 12 
     | 
    
         
            +
               distributed under the License is distributed on an "AS IS" BASIS,
         
     | 
| 
      
 13 
     | 
    
         
            +
               WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         
     | 
| 
      
 14 
     | 
    
         
            +
               See the License for the specific language governing permissions and
         
     | 
| 
      
 15 
     | 
    
         
            +
               limitations under the License.
         
     | 
| 
      
 16 
     | 
    
         
            +
            =end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            require 'joffice/kernel/array'
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            module JOffice
         
     | 
| 
      
 22 
     | 
    
         
            +
              class RedisClientBase
         
     | 
| 
      
 23 
     | 
    
         
            +
                
         
     | 
| 
      
 24 
     | 
    
         
            +
                DEFAULT_KEYS_TO_SAVE= [:id].freeze
         
     | 
| 
      
 25 
     | 
    
         
            +
                
         
     | 
| 
      
 26 
     | 
    
         
            +
                def [](k)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  db.get(key(k))
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
                
         
     | 
| 
      
 30 
     | 
    
         
            +
                def []=(k, value)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  db.set!(key(k), value)
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
                
         
     | 
| 
      
 34 
     | 
    
         
            +
                def exists?(k)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  db.exists?(key(k))   
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
                
         
     | 
| 
      
 38 
     | 
    
         
            +
                def delete_all
         
     | 
| 
      
 39 
     | 
    
         
            +
                  db.del!(db.keys("#{prefix}*"))
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
                
         
     | 
| 
      
 42 
     | 
    
         
            +
                def db_name
         
     | 
| 
      
 43 
     | 
    
         
            +
                  raise "Missed db name in class #{self}"
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
                
         
     | 
| 
      
 46 
     | 
    
         
            +
                def db
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @db||=JOffice::RedisManager.instance.open_db_em(db_name)
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
                
         
     | 
| 
      
 50 
     | 
    
         
            +
                def prefix
         
     | 
| 
      
 51 
     | 
    
         
            +
                  ''
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
                
         
     | 
| 
      
 54 
     | 
    
         
            +
                def key(hash_key)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  "#{prefix}#{hash_key.to_s.gsub(/ /,'_')}"      
         
     | 
| 
      
 56 
     | 
    
         
            +
                end     
         
     | 
| 
      
 57 
     | 
    
         
            +
                
         
     | 
| 
      
 58 
     | 
    
         
            +
                def keys_to_save
         
     | 
| 
      
 59 
     | 
    
         
            +
                  DEFAULT_KEYS_TO_SAVE
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
                
         
     | 
| 
      
 62 
     | 
    
         
            +
                def flush_db
         
     | 
| 
      
 63 
     | 
    
         
            +
                  db.flush_db!
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
                
         
     | 
| 
      
 66 
     | 
    
         
            +
                def update_attribute(id, key, value)			
         
     | 
| 
      
 67 
     | 
    
         
            +
                  db.lset!("#{prefix}#{id}", keys_to_save.index(key), Marshal.dump(value))
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
                
         
     | 
| 
      
 70 
     | 
    
         
            +
                def set(id, value, raw=true)      
         
     | 
| 
      
 71 
     | 
    
         
            +
                  db.set!(key(id), raw ? value : Marshal.dump(value))
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
                
         
     | 
| 
      
 74 
     | 
    
         
            +
                def get(id, raw=true)      
         
     | 
| 
      
 75 
     | 
    
         
            +
                  value=db.get(key(id))
         
     | 
| 
      
 76 
     | 
    
         
            +
                   ((raw || !value) ? value : Marshal.load(value))
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
                
         
     | 
| 
      
 79 
     | 
    
         
            +
                def build_key(model)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  key(model_id(model))
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
                
         
     | 
| 
      
 83 
     | 
    
         
            +
                def model_id(model)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  model.id  
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
                
         
     | 
| 
      
 87 
     | 
    
         
            +
                def update_attributes(model, attributes)
         
     | 
| 
      
 88 
     | 
    
         
            +
                  id=model_id(model)
         
     | 
| 
      
 89 
     | 
    
         
            +
                  attributes.each do |name|
         
     | 
| 
      
 90 
     | 
    
         
            +
                    update_attribute(id, name, model.send(name))
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
                
         
     | 
| 
      
 94 
     | 
    
         
            +
                def add_or_update(model)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  id=build_key(model)
         
     | 
| 
      
 96 
     | 
    
         
            +
                  keys_to_save.each do |key|
         
     | 
| 
      
 97 
     | 
    
         
            +
                    db.tail_push!(id, Marshal.dump(model.send(key)))
         
     | 
| 
      
 98 
     | 
    
         
            +
                  end
         
     | 
| 
      
 99 
     | 
    
         
            +
                  db.ltrim!(id, 0, keys_to_save.length)
         
     | 
| 
      
 100 
     | 
    
         
            +
                end  
         
     | 
| 
      
 101 
     | 
    
         
            +
                
         
     | 
| 
      
 102 
     | 
    
         
            +
                def load_by_range(key, ranges)
         
     | 
| 
      
 103 
     | 
    
         
            +
                  result=[]
         
     | 
| 
      
 104 
     | 
    
         
            +
                  ranges.each do |range|        
         
     | 
| 
      
 105 
     | 
    
         
            +
                    db.lrange(key,range.first,range.last).each {|v| result << Marshal.load(v);   }        
         
     | 
| 
      
 106 
     | 
    
         
            +
                  end
         
     | 
| 
      
 107 
     | 
    
         
            +
                  result
         
     | 
| 
      
 108 
     | 
    
         
            +
                end
         
     | 
| 
      
 109 
     | 
    
         
            +
                
         
     | 
| 
      
 110 
     | 
    
         
            +
                def load_values(id, *keys)
         
     | 
| 
      
 111 
     | 
    
         
            +
                  load_by_range(key(id), to_index_range(keys))      
         
     | 
| 
      
 112 
     | 
    
         
            +
                end
         
     | 
| 
      
 113 
     | 
    
         
            +
                
         
     | 
| 
      
 114 
     | 
    
         
            +
                def to_index_range(keys)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  keys.flatten.map {|key| keys_to_save.index(key.to_sym) }.to_ranges
         
     | 
| 
      
 116 
     | 
    
         
            +
                end
         
     | 
| 
      
 117 
     | 
    
         
            +
                private :to_index_range
         
     | 
| 
      
 118 
     | 
    
         
            +
                
         
     | 
| 
      
 119 
     | 
    
         
            +
              end
         
     | 
| 
      
 120 
     | 
    
         
            +
              
         
     | 
| 
      
 121 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,213 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #encoding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
            =begin
         
     | 
| 
      
 3 
     | 
    
         
            +
            Copyright 2010 Denis Kokorin <virkdi@mail.ru>
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
               Licensed under the Apache License, Version 2.0 (the "License");
         
     | 
| 
      
 6 
     | 
    
         
            +
               you may not use this file except in compliance with the License.
         
     | 
| 
      
 7 
     | 
    
         
            +
               You may obtain a copy of the License at
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                   http://www.apache.org/licenses/LICENSE-2.0
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
               Unless required by applicable law or agreed to in writing, software
         
     | 
| 
      
 12 
     | 
    
         
            +
               distributed under the License is distributed on an "AS IS" BASIS,
         
     | 
| 
      
 13 
     | 
    
         
            +
               WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         
     | 
| 
      
 14 
     | 
    
         
            +
               See the License for the specific language governing permissions and
         
     | 
| 
      
 15 
     | 
    
         
            +
               limitations under the License.
         
     | 
| 
      
 16 
     | 
    
         
            +
            =end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            require 'thread'
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            module JOffice
         
     | 
| 
      
 22 
     | 
    
         
            +
              class RedisManager
         
     | 
| 
      
 23 
     | 
    
         
            +
                include Singleton
         
     | 
| 
      
 24 
     | 
    
         
            +
                attr_reader :db_prefix
         
     | 
| 
      
 25 
     | 
    
         
            +
                include JOffice::FiberEvents
         
     | 
| 
      
 26 
     | 
    
         
            +
                def error_callback 
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @error_callback||= lambda {|code| 
         
     | 
| 
      
 28 
     | 
    
         
            +
                    #$log.error("Redis"){"[RedisEM]Error code: #{code}"}
         
     | 
| 
      
 29 
     | 
    
         
            +
                    puts "[RedisEM]Error code: #{code}";          
         
     | 
| 
      
 30 
     | 
    
         
            +
                    #JOffice::LogOutputter.flush; 
         
     | 
| 
      
 31 
     | 
    
         
            +
                    #EM.next_tick{EM.stop} 
         
     | 
| 
      
 32 
     | 
    
         
            +
                  }
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
                
         
     | 
| 
      
 35 
     | 
    
         
            +
                def flush_all
         
     | 
| 
      
 36 
     | 
    
         
            +
                  open_redis.flush_all
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
                
         
     | 
| 
      
 39 
     | 
    
         
            +
                def increment_key
         
     | 
| 
      
 40 
     | 
    
         
            +
                  :"db.increment"
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
                
         
     | 
| 
      
 43 
     | 
    
         
            +
                private :increment_key
         
     | 
| 
      
 44 
     | 
    
         
            +
                
         
     | 
| 
      
 45 
     | 
    
         
            +
                def init_ping_timer
         
     | 
| 
      
 46 
     | 
    
         
            +
                  @@ping_fiber||=Fiber.new{
         
     | 
| 
      
 47 
     | 
    
         
            +
                    while Fiber.yield         
         
     | 
| 
      
 48 
     | 
    
         
            +
                      p "[Redis] ping"
         
     | 
| 
      
 49 
     | 
    
         
            +
                       (@@redis_em||={}).values.each do |r|            
         
     | 
| 
      
 50 
     | 
    
         
            +
                        r.exists!('0')
         
     | 
| 
      
 51 
     | 
    
         
            +
                      end          
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
                  }
         
     | 
| 
      
 54 
     | 
    
         
            +
                  EventMachine::PeriodicTimer.new(10.minutes) do
         
     | 
| 
      
 55 
     | 
    
         
            +
                    @@ping_fiber.resume true
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
                private :init_ping_timer
         
     | 
| 
      
 59 
     | 
    
         
            +
                
         
     | 
| 
      
 60 
     | 
    
         
            +
                def start_ping_timer
         
     | 
| 
      
 61 
     | 
    
         
            +
                  @@ping_timer_thread||=init_ping_timer
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
                private :start_ping_timer
         
     | 
| 
      
 64 
     | 
    
         
            +
                
         
     | 
| 
      
 65 
     | 
    
         
            +
                def config=(v)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  v.symbolize_keys!
         
     | 
| 
      
 67 
     | 
    
         
            +
                  @config=v
         
     | 
| 
      
 68 
     | 
    
         
            +
                  @db_prefix=(config[:prefix] || 'test')
         
     | 
| 
      
 69 
     | 
    
         
            +
                  p "DB_PREFIX: #{db_prefix}"      
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
                
         
     | 
| 
      
 72 
     | 
    
         
            +
                def load_all_names(redis)
         
     | 
| 
      
 73 
     | 
    
         
            +
                  @db_names={}
         
     | 
| 
      
 74 
     | 
    
         
            +
                  a=redis.keys('db:*')
         
     | 
| 
      
 75 
     | 
    
         
            +
                  a.each do |name|
         
     | 
| 
      
 76 
     | 
    
         
            +
                    @db_names[name]=redis.get(name)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end if a
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
                private :load_all_names
         
     | 
| 
      
 80 
     | 
    
         
            +
                
         
     | 
| 
      
 81 
     | 
    
         
            +
                def config
         
     | 
| 
      
 82 
     | 
    
         
            +
                  @config||={}
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
                
         
     | 
| 
      
 85 
     | 
    
         
            +
                def logger=(v)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  @logger=v
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
                
         
     | 
| 
      
 89 
     | 
    
         
            +
                def logger
         
     | 
| 
      
 90 
     | 
    
         
            +
                  @logger||=nil
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
                
         
     | 
| 
      
 93 
     | 
    
         
            +
                def db_names
         
     | 
| 
      
 94 
     | 
    
         
            +
                  @db_names||={}
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
                
         
     | 
| 
      
 97 
     | 
    
         
            +
                private :db_names
         
     | 
| 
      
 98 
     | 
    
         
            +
                
         
     | 
| 
      
 99 
     | 
    
         
            +
                def last_access
         
     | 
| 
      
 100 
     | 
    
         
            +
                  @last_access||={}
         
     | 
| 
      
 101 
     | 
    
         
            +
                end
         
     | 
| 
      
 102 
     | 
    
         
            +
                private :last_access
         
     | 
| 
      
 103 
     | 
    
         
            +
                
         
     | 
| 
      
 104 
     | 
    
         
            +
                #def new_redis(db=1)
         
     | 
| 
      
 105 
     | 
    
         
            +
                #  ::Redis.new({:logger=>logger,:db=>db}.merge(config))
         
     | 
| 
      
 106 
     | 
    
         
            +
                #end
         
     | 
| 
      
 107 
     | 
    
         
            +
                
         
     | 
| 
      
 108 
     | 
    
         
            +
                #def get_or_open_redis_instance(db=1, forse=false)
         
     | 
| 
      
 109 
     | 
    
         
            +
                #  key=:"redisdb_#{db_prefix}_#{db}"   
         
     | 
| 
      
 110 
     | 
    
         
            +
                #  
         
     | 
| 
      
 111 
     | 
    
         
            +
                #  if forse
         
     | 
| 
      
 112 
     | 
    
         
            +
                #    p "Reopen db #{db}"
         
     | 
| 
      
 113 
     | 
    
         
            +
                #    Thread.current[key]=new_redis(db)        
         
     | 
| 
      
 114 
     | 
    
         
            +
                #  else
         
     | 
| 
      
 115 
     | 
    
         
            +
                #    Thread.current[key]||=new_redis(db)
         
     | 
| 
      
 116 
     | 
    
         
            +
                #  end
         
     | 
| 
      
 117 
     | 
    
         
            +
                #  
         
     | 
| 
      
 118 
     | 
    
         
            +
                #end
         
     | 
| 
      
 119 
     | 
    
         
            +
                #private :get_or_open_redis_instance
         
     | 
| 
      
 120 
     | 
    
         
            +
                
         
     | 
| 
      
 121 
     | 
    
         
            +
                def force_open_em_redis(db)      
         
     | 
| 
      
 122 
     | 
    
         
            +
                  redis = EM::Protocols::Redis.connect((config[:host] || '127.0.0.1'), (config[:port] || 6379).to_i) 
         
     | 
| 
      
 123 
     | 
    
         
            +
                  redis.on_error(&error_callback)      
         
     | 
| 
      
 124 
     | 
    
         
            +
                  redis.select(db)      
         
     | 
| 
      
 125 
     | 
    
         
            +
                  redis
         
     | 
| 
      
 126 
     | 
    
         
            +
                end
         
     | 
| 
      
 127 
     | 
    
         
            +
                private :force_open_em_redis
         
     | 
| 
      
 128 
     | 
    
         
            +
                
         
     | 
| 
      
 129 
     | 
    
         
            +
                def redis_em
         
     | 
| 
      
 130 
     | 
    
         
            +
                  @@redis_em||=Hash.new { |hash, id| hash[id] = force_open_em_redis(id); };
         
     | 
| 
      
 131 
     | 
    
         
            +
                end
         
     | 
| 
      
 132 
     | 
    
         
            +
                
         
     | 
| 
      
 133 
     | 
    
         
            +
                def open_em_redis(db=1)     
         
     | 
| 
      
 134 
     | 
    
         
            +
                  redis_em[db]
         
     | 
| 
      
 135 
     | 
    
         
            +
                end
         
     | 
| 
      
 136 
     | 
    
         
            +
                
         
     | 
| 
      
 137 
     | 
    
         
            +
                #private :open_em_redis
         
     | 
| 
      
 138 
     | 
    
         
            +
                def semaphore 
         
     | 
| 
      
 139 
     | 
    
         
            +
                  @semaphore||=Mutex.new
         
     | 
| 
      
 140 
     | 
    
         
            +
                end
         
     | 
| 
      
 141 
     | 
    
         
            +
                
         
     | 
| 
      
 142 
     | 
    
         
            +
                def register_database(key)      
         
     | 
| 
      
 143 
     | 
    
         
            +
                  register_fiber_and_singleton_task("register_database(#{key}) ") do 
         
     | 
| 
      
 144 
     | 
    
         
            +
                    redis=open_em_redis      
         
     | 
| 
      
 145 
     | 
    
         
            +
                    id=redis.get(key)
         
     | 
| 
      
 146 
     | 
    
         
            +
                    if (new_db=id.nil?)
         
     | 
| 
      
 147 
     | 
    
         
            +
                      id=redis.incr(increment_key)
         
     | 
| 
      
 148 
     | 
    
         
            +
                      while id<2
         
     | 
| 
      
 149 
     | 
    
         
            +
                        id=redis.incr(increment_key)
         
     | 
| 
      
 150 
     | 
    
         
            +
                      end          
         
     | 
| 
      
 151 
     | 
    
         
            +
                      redis.set(key,id)
         
     | 
| 
      
 152 
     | 
    
         
            +
                    end          
         
     | 
| 
      
 153 
     | 
    
         
            +
                    p "Open db #{key} id=#{id} #{new_db}"
         
     | 
| 
      
 154 
     | 
    
         
            +
                    db_names[key]=id      
         
     | 
| 
      
 155 
     | 
    
         
            +
                  end
         
     | 
| 
      
 156 
     | 
    
         
            +
                end
         
     | 
| 
      
 157 
     | 
    
         
            +
                
         
     | 
| 
      
 158 
     | 
    
         
            +
                # Lock the object so no other instances can modify it.
         
     | 
| 
      
 159 
     | 
    
         
            +
                # This method implements the design pattern for locks
         
     | 
| 
      
 160 
     | 
    
         
            +
                # described at: http://code.google.com/p/redis/wiki/SetnxCommand
         
     | 
| 
      
 161 
     | 
    
         
            +
                #
         
     | 
| 
      
 162 
     | 
    
         
            +
                # @see Model#mutex
         
     | 
| 
      
 163 
     | 
    
         
            +
                def lock!
         
     | 
| 
      
 164 
     | 
    
         
            +
                  db=open_em_redis 
         
     | 
| 
      
 165 
     | 
    
         
            +
                  until db.setnx('_lock', lock_timeout)
         
     | 
| 
      
 166 
     | 
    
         
            +
                    next unless lock = db.get('_lock')
         
     | 
| 
      
 167 
     | 
    
         
            +
                    sleep(2) and next unless lock_expired?(lock)
         
     | 
| 
      
 168 
     | 
    
         
            +
                    break unless lock = db.getset('_lock', lock_timeout)
         
     | 
| 
      
 169 
     | 
    
         
            +
                    break if lock_expired?(lock)
         
     | 
| 
      
 170 
     | 
    
         
            +
                  end
         
     | 
| 
      
 171 
     | 
    
         
            +
                end
         
     | 
| 
      
 172 
     | 
    
         
            +
                
         
     | 
| 
      
 173 
     | 
    
         
            +
                # Release the lock.
         
     | 
| 
      
 174 
     | 
    
         
            +
                # @see Model#mutex
         
     | 
| 
      
 175 
     | 
    
         
            +
                def unlock!
         
     | 
| 
      
 176 
     | 
    
         
            +
                  open_em_redis.del('_lock')
         
     | 
| 
      
 177 
     | 
    
         
            +
                end    
         
     | 
| 
      
 178 
     | 
    
         
            +
                
         
     | 
| 
      
 179 
     | 
    
         
            +
                def lock_timeout
         
     | 
| 
      
 180 
     | 
    
         
            +
                  Time.now.to_f + 5
         
     | 
| 
      
 181 
     | 
    
         
            +
                end
         
     | 
| 
      
 182 
     | 
    
         
            +
                
         
     | 
| 
      
 183 
     | 
    
         
            +
                def lock_expired? lock
         
     | 
| 
      
 184 
     | 
    
         
            +
                  lock.to_f < Time.now.to_f
         
     | 
| 
      
 185 
     | 
    
         
            +
                end
         
     | 
| 
      
 186 
     | 
    
         
            +
                
         
     | 
| 
      
 187 
     | 
    
         
            +
                def open_db_em(name, force=false)
         
     | 
| 
      
 188 
     | 
    
         
            +
                  key= :"db:#{db_prefix}_#{name}"
         
     | 
| 
      
 189 
     | 
    
         
            +
                  open_em_redis(db_names[key] || register_database(key))              
         
     | 
| 
      
 190 
     | 
    
         
            +
                end
         
     | 
| 
      
 191 
     | 
    
         
            +
                
         
     | 
| 
      
 192 
     | 
    
         
            +
                #    def opend_db(name, forse=false)
         
     | 
| 
      
 193 
     | 
    
         
            +
                #      redis=nil	  
         
     | 
| 
      
 194 
     | 
    
         
            +
                #      key= :"db:#{db_prefix}_#{name}"
         
     | 
| 
      
 195 
     | 
    
         
            +
                #      new_db=false
         
     | 
| 
      
 196 
     | 
    
         
            +
                #      id=(db_names[key] || (new_db=register_database(key)))
         
     | 
| 
      
 197 
     | 
    
         
            +
                #      
         
     | 
| 
      
 198 
     | 
    
         
            +
                #      redis=get_or_open_redis_instance(id, forse)
         
     | 
| 
      
 199 
     | 
    
         
            +
                #      redis.flush_db if new_db
         
     | 
| 
      
 200 
     | 
    
         
            +
                #      
         
     | 
| 
      
 201 
     | 
    
         
            +
                #      if block_given?
         
     | 
| 
      
 202 
     | 
    
         
            +
                #        begin			
         
     | 
| 
      
 203 
     | 
    
         
            +
                #          yield redis
         
     | 
| 
      
 204 
     | 
    
         
            +
                #        rescue
         
     | 
| 
      
 205 
     | 
    
         
            +
                #          p "Error #{$!}"
         
     | 
| 
      
 206 
     | 
    
         
            +
                #          yield get_or_open_redis_instance(id, true)
         
     | 
| 
      
 207 
     | 
    
         
            +
                #        end			
         
     | 
| 
      
 208 
     | 
    
         
            +
                #      end
         
     | 
| 
      
 209 
     | 
    
         
            +
                #      #last_access[id]=Time.new.to_i
         
     | 
| 
      
 210 
     | 
    
         
            +
                #      redis
         
     | 
| 
      
 211 
     | 
    
         
            +
                #    end
         
     | 
| 
      
 212 
     | 
    
         
            +
              end
         
     | 
| 
      
 213 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,445 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #encoding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
            =begin
         
     | 
| 
      
 3 
     | 
    
         
            +
            Copyright 2010 Denis Kokorin <virkdi@mail.ru>
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
               Licensed under the Apache License, Version 2.0 (the "License");
         
     | 
| 
      
 6 
     | 
    
         
            +
               you may not use this file except in compliance with the License.
         
     | 
| 
      
 7 
     | 
    
         
            +
               You may obtain a copy of the License at
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                   http://www.apache.org/licenses/LICENSE-2.0
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
               Unless required by applicable law or agreed to in writing, software
         
     | 
| 
      
 12 
     | 
    
         
            +
               distributed under the License is distributed on an "AS IS" BASIS,
         
     | 
| 
      
 13 
     | 
    
         
            +
               WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         
     | 
| 
      
 14 
     | 
    
         
            +
               See the License for the specific language governing permissions and
         
     | 
| 
      
 15 
     | 
    
         
            +
               limitations under the License.
         
     | 
| 
      
 16 
     | 
    
         
            +
            =end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            require 'fileutils'
         
     | 
| 
      
 20 
     | 
    
         
            +
            require 'em-redis'
         
     | 
| 
      
 21 
     | 
    
         
            +
            require 'active_support'
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            module EventMachine
         
     | 
| 
      
 24 
     | 
    
         
            +
              module Protocols
         
     | 
| 
      
 25 
     | 
    
         
            +
                class RedisMethodFactory
         
     | 
| 
      
 26 
     | 
    
         
            +
                  def initialize(parent)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    @class_name='syncronized_redis_protocol'
         
     | 
| 
      
 28 
     | 
    
         
            +
                    #@buffer=['', "require 'em-redis'","require 'active_support'",'module EventMachine', 'module Protocols', 'module Redis']
         
     | 
| 
      
 29 
     | 
    
         
            +
                    @buffer=['']
         
     | 
| 
      
 30 
     | 
    
         
            +
                    yield self if block_given?
         
     | 
| 
      
 31 
     | 
    
         
            +
                    method 'ping' do |m|
         
     | 
| 
      
 32 
     | 
    
         
            +
                      m << 'exists?(0)'
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
                    method('blank?', 'args') do |m|
         
     | 
| 
      
 35 
     | 
    
         
            +
                      m << "args.blank? || args.flatten!.blank?"
         
     | 
| 
      
 36 
     | 
    
         
            +
                    end
         
     | 
| 
      
 37 
     | 
    
         
            +
                    #@buffer << 'end'
         
     | 
| 
      
 38 
     | 
    
         
            +
                    #@buffer << 'end'
         
     | 
| 
      
 39 
     | 
    
         
            +
                    #@buffer << 'end'
         
     | 
| 
      
 40 
     | 
    
         
            +
                    #generate
         
     | 
| 
      
 41 
     | 
    
         
            +
                    parent.class_eval @buffer.join("\n")
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
                  
         
     | 
| 
      
 44 
     | 
    
         
            +
                  def method(name, args=nil)      
         
     | 
| 
      
 45 
     | 
    
         
            +
                    buf=[]
         
     | 
| 
      
 46 
     | 
    
         
            +
                    yield buf
         
     | 
| 
      
 47 
     | 
    
         
            +
                    buf=buf.map{|v| "    #{v}"}.join("\n")
         
     | 
| 
      
 48 
     | 
    
         
            +
                    signature=(args ? "#{name}(#{args})" : name)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    @buffer << ''
         
     | 
| 
      
 50 
     | 
    
         
            +
                    @buffer << "def #{signature}\n#{buf}\nend"
         
     | 
| 
      
 51 
     | 
    
         
            +
                    @buffer << "public :#{name}"
         
     | 
| 
      
 52 
     | 
    
         
            +
                    @buffer << ''
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
                  
         
     | 
| 
      
 55 
     | 
    
         
            +
                  def method_call_args
         
     | 
| 
      
 56 
     | 
    
         
            +
                    @method_call_args||=['', 'key', 'key, value']
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
                  
         
     | 
| 
      
 59 
     | 
    
         
            +
                  def method_call(name, options={})
         
     | 
| 
      
 60 
     | 
    
         
            +
                    name_sync="#{name}_sync"
         
     | 
| 
      
 61 
     | 
    
         
            +
                    name_async="#{name}_async"        
         
     | 
| 
      
 62 
     | 
    
         
            +
                    
         
     | 
| 
      
 63 
     | 
    
         
            +
                    args = (method_call_args[options[:args_count] || 1] || '*args')       
         
     | 
| 
      
 64 
     | 
    
         
            +
                    
         
     | 
| 
      
 65 
     | 
    
         
            +
                    call_args = case (options[:args_count] || 1)
         
     | 
| 
      
 66 
     | 
    
         
            +
                      when 2 then "[:#{name}, key, value]"
         
     | 
| 
      
 67 
     | 
    
         
            +
                      when 1 then "[:#{name}, key]"
         
     | 
| 
      
 68 
     | 
    
         
            +
                      when 0 then "[:#{name}]"
         
     | 
| 
      
 69 
     | 
    
         
            +
                    else "args.unshift(:#{name})"
         
     | 
| 
      
 70 
     | 
    
         
            +
                    end
         
     | 
| 
      
 71 
     | 
    
         
            +
                    
         
     | 
| 
      
 72 
     | 
    
         
            +
                    method(name_sync, args) do |m|
         
     | 
| 
      
 73 
     | 
    
         
            +
                      m << "return [] if blank?(args)" if (options[:args_count] || 1)>3
         
     | 
| 
      
 74 
     | 
    
         
            +
                      m << "c=Fiber.current"
         
     | 
| 
      
 75 
     | 
    
         
            +
                      m << "call_command(#{call_args}) { |a|  c.resume(a); }"
         
     | 
| 
      
 76 
     | 
    
         
            +
                      m << "Fiber.yield"
         
     | 
| 
      
 77 
     | 
    
         
            +
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
                    
         
     | 
| 
      
 79 
     | 
    
         
            +
                    method(name_async, args) do |m|
         
     | 
| 
      
 80 
     | 
    
         
            +
                      m << "return if blank?(args)" if (options[:args_count] || 1)>3
         
     | 
| 
      
 81 
     | 
    
         
            +
                      m << "call_command(#{call_args}) { |a| }"
         
     | 
| 
      
 82 
     | 
    
         
            +
                    end
         
     | 
| 
      
 83 
     | 
    
         
            +
                    
         
     | 
| 
      
 84 
     | 
    
         
            +
                    @buffer << "alias :#{name}! :#{name_async}"
         
     | 
| 
      
 85 
     | 
    
         
            +
                    @buffer << "alias :#{name} :#{options[:sync] ? name_sync : name_async}"
         
     | 
| 
      
 86 
     | 
    
         
            +
                    
         
     | 
| 
      
 87 
     | 
    
         
            +
                    if options[:alias]
         
     | 
| 
      
 88 
     | 
    
         
            +
                      options[:alias].each do |alias_name|
         
     | 
| 
      
 89 
     | 
    
         
            +
                        @buffer << "alias :#{alias_name} :#{name}"
         
     | 
| 
      
 90 
     | 
    
         
            +
                      end
         
     | 
| 
      
 91 
     | 
    
         
            +
                    end
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
      
 93 
     | 
    
         
            +
                  
         
     | 
| 
      
 94 
     | 
    
         
            +
                  def method_alias(name, options={})      
         
     | 
| 
      
 95 
     | 
    
         
            +
                    em_name="#{name}_real"
         
     | 
| 
      
 96 
     | 
    
         
            +
                    name_sync="#{name}_sync"
         
     | 
| 
      
 97 
     | 
    
         
            +
                    name_async="#{name}_async"        
         
     | 
| 
      
 98 
     | 
    
         
            +
                    @buffer << "alias :#{em_name} :#{name}"
         
     | 
| 
      
 99 
     | 
    
         
            +
                    args = (method_call_args[options[:args_count].to_i || 1] || '*args')
         
     | 
| 
      
 100 
     | 
    
         
            +
                    
         
     | 
| 
      
 101 
     | 
    
         
            +
                    method(name_sync, args) do |m|
         
     | 
| 
      
 102 
     | 
    
         
            +
                      m << "return [] if blank?(args)" if (options[:args_count] || 1)>3
         
     | 
| 
      
 103 
     | 
    
         
            +
                      m << "c=Fiber.current"
         
     | 
| 
      
 104 
     | 
    
         
            +
                      m << "#{em_name}(#{args}) { |a|  c.resume(a); }"
         
     | 
| 
      
 105 
     | 
    
         
            +
                      m << "Fiber.yield"
         
     | 
| 
      
 106 
     | 
    
         
            +
                    end
         
     | 
| 
      
 107 
     | 
    
         
            +
                    
         
     | 
| 
      
 108 
     | 
    
         
            +
                    if options[:cache]
         
     | 
| 
      
 109 
     | 
    
         
            +
                      @buffer << "alias :#{name_sync}_no_cache :#{name_async}"
         
     | 
| 
      
 110 
     | 
    
         
            +
                      method(name_sync, args) do |m|
         
     | 
| 
      
 111 
     | 
    
         
            +
                        m << "@cache_#{name}||=#{name_sync}_no_cache(#{args})"
         
     | 
| 
      
 112 
     | 
    
         
            +
                      end
         
     | 
| 
      
 113 
     | 
    
         
            +
                    end
         
     | 
| 
      
 114 
     | 
    
         
            +
                    
         
     | 
| 
      
 115 
     | 
    
         
            +
                    method(name_async, args) do |m|
         
     | 
| 
      
 116 
     | 
    
         
            +
                      m << "return if blank?(args)" if (options[:args_count] || 1)>3
         
     | 
| 
      
 117 
     | 
    
         
            +
                      m << "#{em_name}(#{args}) { |a| }"
         
     | 
| 
      
 118 
     | 
    
         
            +
                    end
         
     | 
| 
      
 119 
     | 
    
         
            +
                    
         
     | 
| 
      
 120 
     | 
    
         
            +
                    @buffer << "alias :#{name}! :#{name_async}"
         
     | 
| 
      
 121 
     | 
    
         
            +
                    @buffer << "alias :#{name} :#{options[:sync] ? name_sync : name_async}"
         
     | 
| 
      
 122 
     | 
    
         
            +
                    
         
     | 
| 
      
 123 
     | 
    
         
            +
                    if options[:alias]
         
     | 
| 
      
 124 
     | 
    
         
            +
                      options[:alias].each do |alias_name|
         
     | 
| 
      
 125 
     | 
    
         
            +
                        @buffer << "alias :#{alias_name} :#{name}"
         
     | 
| 
      
 126 
     | 
    
         
            +
                      end
         
     | 
| 
      
 127 
     | 
    
         
            +
                    end
         
     | 
| 
      
 128 
     | 
    
         
            +
                  end
         
     | 
| 
      
 129 
     | 
    
         
            +
                  
         
     | 
| 
      
 130 
     | 
    
         
            +
                  def generate
         
     | 
| 
      
 131 
     | 
    
         
            +
                    dir=File.join(File.absolute_path(File.dirname(__FILE__)),'metadata')
         
     | 
| 
      
 132 
     | 
    
         
            +
                    FileUtils.mkdir_p(dir)
         
     | 
| 
      
 133 
     | 
    
         
            +
                    
         
     | 
| 
      
 134 
     | 
    
         
            +
                    path=File.join(dir, "#{@class_name}.rb")
         
     | 
| 
      
 135 
     | 
    
         
            +
                    f=File.open(path,'w')
         
     | 
| 
      
 136 
     | 
    
         
            +
                    f.write(@buffer.join("\n"))
         
     | 
| 
      
 137 
     | 
    
         
            +
                    f.close
         
     | 
| 
      
 138 
     | 
    
         
            +
                    #require path   
         
     | 
| 
      
 139 
     | 
    
         
            +
                  end
         
     | 
| 
      
 140 
     | 
    
         
            +
                end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
              end
         
     | 
| 
      
 143 
     | 
    
         
            +
            end
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
            module EventMachine
         
     | 
| 
      
 146 
     | 
    
         
            +
              module Protocols
         
     | 
| 
      
 147 
     | 
    
         
            +
                module Redis
         
     | 
| 
      
 148 
     | 
    
         
            +
                RedisMethodFactory.new(self) do |m|
         
     | 
| 
      
 149 
     | 
    
         
            +
                  #m.method_alias :set         , {:sync=>false, :args_count=>2}
         
     | 
| 
      
 150 
     | 
    
         
            +
                  #m.method_alias :sort        , {:sync=>true}
         
     | 
| 
      
 151 
     | 
    
         
            +
                  m.method_alias :incr        , {:sync=>true, :alias=>[:increment], :args_count=>1}
         
     | 
| 
      
 152 
     | 
    
         
            +
                  m.method_alias :decr        , {:sync=>false, :alias=>[:decrement], :args_count=>1}
         
     | 
| 
      
 153 
     | 
    
         
            +
                  m.method_alias :mapped_mget , {:sync=>true, :args_count=>999999}	  
         
     | 
| 
      
 154 
     | 
    
         
            +
                  m.method_alias :quit        , {:sync=>false}
         
     | 
| 
      
 155 
     | 
    
         
            +
                  m.method_alias :type        , {:sync=>true, :alias=>[:type?], :args_count=>1}
         
     | 
| 
      
 156 
     | 
    
         
            +
                  
         
     | 
| 
      
 157 
     | 
    
         
            +
                  m.method_call :keys         , {:sync=>true, :args_count=>1}
         
     | 
| 
      
 158 
     | 
    
         
            +
                  m.method_call :get          , {:sync=>true}
         
     | 
| 
      
 159 
     | 
    
         
            +
                  m.method_call :getset       , {:sync=>true, :args_count=>2}
         
     | 
| 
      
 160 
     | 
    
         
            +
                  #m.method_call :incrby       , {:sync=>true, :args_count=>2}
         
     | 
| 
      
 161 
     | 
    
         
            +
                  m.method_call :select       , {:sync=>false, :args_count=>1}
         
     | 
| 
      
 162 
     | 
    
         
            +
                  m.method_call :scard        , {:sync=>true, :alias=>[:set_count], :args_count=>1}      
         
     | 
| 
      
 163 
     | 
    
         
            +
                  m.method_call :smembers     , {:sync=>true, :alias=>[:set_member?], :args_count=>1}      
         
     | 
| 
      
 164 
     | 
    
         
            +
                  m.method_call :rename       , {:sync=>false, :args_count=>2}
         
     | 
| 
      
 165 
     | 
    
         
            +
                  m.method_call :flushall     , {:sync=>false, :alias=>[:flush_all], :args_count=>0}
         
     | 
| 
      
 166 
     | 
    
         
            +
                  m.method_call :flushdb      , {:sync=>false, :alias=>[:flush_db, :flush_db!], :args_count=>0}
         
     | 
| 
      
 167 
     | 
    
         
            +
                  m.method_call :srem         , {:sync=>false, :alias=>[:set_delete, :set_remove, :set_del], :args_count=>2}
         
     | 
| 
      
 168 
     | 
    
         
            +
                  m.method_call :sadd         , {:sync=>false, :alias=>[:set_add], :args_count=>2}
         
     | 
| 
      
 169 
     | 
    
         
            +
                  
         
     | 
| 
      
 170 
     | 
    
         
            +
                  m.method_call :smembers     , {:sync=>true, :alias=>[:members, :set_members], :args_count=>1}
         
     | 
| 
      
 171 
     | 
    
         
            +
                  m.method_call :srandmember  , {:sync=>true, :args_count=>1}      
         
     | 
| 
      
 172 
     | 
    
         
            +
                  
         
     | 
| 
      
 173 
     | 
    
         
            +
                  m.method_call :sismember    , {:sync=>true, :alias=>[:set_member?], :args_count=>2}
         
     | 
| 
      
 174 
     | 
    
         
            +
                  m.method_call :exists       , {:sync=>true, :alias=>[:exists?], :args_count=>1}
         
     | 
| 
      
 175 
     | 
    
         
            +
                  m.method_call :version      , {:sync=>true, :args_count=>0, :cache=>true}
         
     | 
| 
      
 176 
     | 
    
         
            +
                  
         
     | 
| 
      
 177 
     | 
    
         
            +
                  m.method_call :hset      , {:sync=>true, :args_count=>3}    
         
     | 
| 
      
 178 
     | 
    
         
            +
                  
         
     | 
| 
      
 179 
     | 
    
         
            +
                  [:multi, :exec, :discard].each do |name|
         
     | 
| 
      
 180 
     | 
    
         
            +
                    m.method_call name, {:sync=>false, :args_count=>0}   
         
     | 
| 
      
 181 
     | 
    
         
            +
                  end      
         
     | 
| 
      
 182 
     | 
    
         
            +
                  
         
     | 
| 
      
 183 
     | 
    
         
            +
                end  
         
     | 
| 
      
 184 
     | 
    
         
            +
                	
         
     | 
| 
      
 185 
     | 
    
         
            +
                	
         
     | 
| 
      
 186 
     | 
    
         
            +
                  MULTI_BULK_COMMANDS['hset']=true
         
     | 
| 
      
 187 
     | 
    
         
            +
                  MULTI_BULK_COMMANDS['hdel']=true
         
     | 
| 
      
 188 
     | 
    
         
            +
                  
         
     | 
| 
      
 189 
     | 
    
         
            +
                  def bulk_array
         
     | 
| 
      
 190 
     | 
    
         
            +
                    @bulk_array||=[]
         
     | 
| 
      
 191 
     | 
    
         
            +
                  end
         
     | 
| 
      
 192 
     | 
    
         
            +
                  
         
     | 
| 
      
 193 
     | 
    
         
            +
                  def delete_array
         
     | 
| 
      
 194 
     | 
    
         
            +
                    @delete_array||=[]
         
     | 
| 
      
 195 
     | 
    
         
            +
                  end
         
     | 
| 
      
 196 
     | 
    
         
            +
                  
         
     | 
| 
      
 197 
     | 
    
         
            +
                  def bulk_expire_array
         
     | 
| 
      
 198 
     | 
    
         
            +
                    @bulk_expire_array||=[]
         
     | 
| 
      
 199 
     | 
    
         
            +
                  end
         
     | 
| 
      
 200 
     | 
    
         
            +
                  
         
     | 
| 
      
 201 
     | 
    
         
            +
                  def bulk_insert?
         
     | 
| 
      
 202 
     | 
    
         
            +
                    !@bulk_insert.nil?
         
     | 
| 
      
 203 
     | 
    
         
            +
                  end
         
     | 
| 
      
 204 
     | 
    
         
            +
                  
         
     | 
| 
      
 205 
     | 
    
         
            +
            =begin      
         
     | 
| 
      
 206 
     | 
    
         
            +
                  Добавление всех данных в одной транзакции
         
     | 
| 
      
 207 
     | 
    
         
            +
                  Redis send:
         
     | 
| 
      
 208 
     | 
    
         
            +
                    MULTI
         
     | 
| 
      
 209 
     | 
    
         
            +
                    COMMAND_1 ...
         
     | 
| 
      
 210 
     | 
    
         
            +
                    COMMAND_2 ...
         
     | 
| 
      
 211 
     | 
    
         
            +
                    COMMAND_N ...
         
     | 
| 
      
 212 
     | 
    
         
            +
                    EXEC or DISCARD
         
     | 
| 
      
 213 
     | 
    
         
            +
            =end      
         
     | 
| 
      
 214 
     | 
    
         
            +
                  def transaction
         
     | 
| 
      
 215 
     | 
    
         
            +
                    return unless block_given?
         
     | 
| 
      
 216 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 217 
     | 
    
         
            +
                      multi
         
     | 
| 
      
 218 
     | 
    
         
            +
                      yield
         
     | 
| 
      
 219 
     | 
    
         
            +
                      exec
         
     | 
| 
      
 220 
     | 
    
         
            +
                    rescue Exception => e
         
     | 
| 
      
 221 
     | 
    
         
            +
                      discard 
         
     | 
| 
      
 222 
     | 
    
         
            +
                      raise e          
         
     | 
| 
      
 223 
     | 
    
         
            +
                    end
         
     | 
| 
      
 224 
     | 
    
         
            +
                  end
         
     | 
| 
      
 225 
     | 
    
         
            +
                  
         
     | 
| 
      
 226 
     | 
    
         
            +
                  def bulk_set
         
     | 
| 
      
 227 
     | 
    
         
            +
                    bulk_insert=@bulk_insert.nil?
         
     | 
| 
      
 228 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 229 
     | 
    
         
            +
                      @bulk_insert=true
         
     | 
| 
      
 230 
     | 
    
         
            +
                      yield if block_given?
         
     | 
| 
      
 231 
     | 
    
         
            +
                      if bulk_insert
         
     | 
| 
      
 232 
     | 
    
         
            +
                        @bulk_insert=nil
         
     | 
| 
      
 233 
     | 
    
         
            +
                        del_real(delete_array)
         
     | 
| 
      
 234 
     | 
    
         
            +
                        mset_real(bulk_array)          
         
     | 
| 
      
 235 
     | 
    
         
            +
                        bulk_expire_array.each do |value|                
         
     | 
| 
      
 236 
     | 
    
         
            +
                          call_command(value) { |a| }
         
     | 
| 
      
 237 
     | 
    
         
            +
                        end
         
     | 
| 
      
 238 
     | 
    
         
            +
                      end
         
     | 
| 
      
 239 
     | 
    
         
            +
                    ensure
         
     | 
| 
      
 240 
     | 
    
         
            +
                      if bulk_insert             
         
     | 
| 
      
 241 
     | 
    
         
            +
                        delete_array.clear
         
     | 
| 
      
 242 
     | 
    
         
            +
                        bulk_array.clear
         
     | 
| 
      
 243 
     | 
    
         
            +
                        bulk_expire_array.clear
         
     | 
| 
      
 244 
     | 
    
         
            +
                      end
         
     | 
| 
      
 245 
     | 
    
         
            +
                    end
         
     | 
| 
      
 246 
     | 
    
         
            +
                  end
         
     | 
| 
      
 247 
     | 
    
         
            +
                  
         
     | 
| 
      
 248 
     | 
    
         
            +
                  #set
         
     | 
| 
      
 249 
     | 
    
         
            +
                  
         
     | 
| 
      
 250 
     | 
    
         
            +
                  def set_async(key, value)
         
     | 
| 
      
 251 
     | 
    
         
            +
                    if bulk_insert?
         
     | 
| 
      
 252 
     | 
    
         
            +
                      bulk_array << key
         
     | 
| 
      
 253 
     | 
    
         
            +
                      bulk_array << value
         
     | 
| 
      
 254 
     | 
    
         
            +
                    else
         
     | 
| 
      
 255 
     | 
    
         
            +
                      call_command(['set', key, value]) { |a| }
         
     | 
| 
      
 256 
     | 
    
         
            +
                    end
         
     | 
| 
      
 257 
     | 
    
         
            +
                  end            
         
     | 
| 
      
 258 
     | 
    
         
            +
                  
         
     | 
| 
      
 259 
     | 
    
         
            +
                  alias :set! :set_async
         
     | 
| 
      
 260 
     | 
    
         
            +
                  alias :set :set_async
         
     | 
| 
      
 261 
     | 
    
         
            +
                  
         
     | 
| 
      
 262 
     | 
    
         
            +
                  def mset_real(args)      
         
     | 
| 
      
 263 
     | 
    
         
            +
                    argv=args.flatten
         
     | 
| 
      
 264 
     | 
    
         
            +
                    call_command(argv.unshift('mset')) { |a| } unless argv.empty?   
         
     | 
| 
      
 265 
     | 
    
         
            +
                  end
         
     | 
| 
      
 266 
     | 
    
         
            +
                  
         
     | 
| 
      
 267 
     | 
    
         
            +
                  def mset_async(*args)
         
     | 
| 
      
 268 
     | 
    
         
            +
                    if bulk_insert?
         
     | 
| 
      
 269 
     | 
    
         
            +
                      bulk_array+=args          
         
     | 
| 
      
 270 
     | 
    
         
            +
                    else
         
     | 
| 
      
 271 
     | 
    
         
            +
                      mset_real(args)          
         
     | 
| 
      
 272 
     | 
    
         
            +
                    end
         
     | 
| 
      
 273 
     | 
    
         
            +
                  end            
         
     | 
| 
      
 274 
     | 
    
         
            +
                  
         
     | 
| 
      
 275 
     | 
    
         
            +
                  alias :mset! :mset_async
         
     | 
| 
      
 276 
     | 
    
         
            +
                  alias :mset :mset_async
         
     | 
| 
      
 277 
     | 
    
         
            +
                  
         
     | 
| 
      
 278 
     | 
    
         
            +
                  def expire_async(key, value)
         
     | 
| 
      
 279 
     | 
    
         
            +
                    if bulk_insert?
         
     | 
| 
      
 280 
     | 
    
         
            +
                      bulk_expire_array << ['expire', key, value]
         
     | 
| 
      
 281 
     | 
    
         
            +
                    else          
         
     | 
| 
      
 282 
     | 
    
         
            +
                      call_command(['expire', key, value]) { |a| }
         
     | 
| 
      
 283 
     | 
    
         
            +
                    end
         
     | 
| 
      
 284 
     | 
    
         
            +
                  end            
         
     | 
| 
      
 285 
     | 
    
         
            +
                  
         
     | 
| 
      
 286 
     | 
    
         
            +
                  alias :expire! :expire_async
         
     | 
| 
      
 287 
     | 
    
         
            +
                  alias :expire :expire_async
         
     | 
| 
      
 288 
     | 
    
         
            +
                  #end set      
         
     | 
| 
      
 289 
     | 
    
         
            +
                  
         
     | 
| 
      
 290 
     | 
    
         
            +
                  def del_real(keys)         
         
     | 
| 
      
 291 
     | 
    
         
            +
                    call_command(keys.unshift('del')) { |a| } unless keys.empty?        
         
     | 
| 
      
 292 
     | 
    
         
            +
                  end
         
     | 
| 
      
 293 
     | 
    
         
            +
                  
         
     | 
| 
      
 294 
     | 
    
         
            +
                  def del_async(*agrs)
         
     | 
| 
      
 295 
     | 
    
         
            +
                    values=agrs.flatten
         
     | 
| 
      
 296 
     | 
    
         
            +
                    if bulk_insert?
         
     | 
| 
      
 297 
     | 
    
         
            +
                      delete_array += values
         
     | 
| 
      
 298 
     | 
    
         
            +
                    else         
         
     | 
| 
      
 299 
     | 
    
         
            +
                      del_real(values)
         
     | 
| 
      
 300 
     | 
    
         
            +
                    end
         
     | 
| 
      
 301 
     | 
    
         
            +
                  end            
         
     | 
| 
      
 302 
     | 
    
         
            +
                  
         
     | 
| 
      
 303 
     | 
    
         
            +
                  alias :del! :del_async
         
     | 
| 
      
 304 
     | 
    
         
            +
                  alias :del :del_async
         
     | 
| 
      
 305 
     | 
    
         
            +
                  alias :delete :del_async
         
     | 
| 
      
 306 
     | 
    
         
            +
                  
         
     | 
| 
      
 307 
     | 
    
         
            +
                  def mget(keys)
         
     | 
| 
      
 308 
     | 
    
         
            +
                    return [] if keys.blank?
         
     | 
| 
      
 309 
     | 
    
         
            +
                    #keys.flatten!
         
     | 
| 
      
 310 
     | 
    
         
            +
                    #return [] if keys.empty?
         
     | 
| 
      
 311 
     | 
    
         
            +
                    #$log.debug("Redis"){"call mget (#{args.inspect})"} if $debug
         
     | 
| 
      
 312 
     | 
    
         
            +
                    
         
     | 
| 
      
 313 
     | 
    
         
            +
                    c=Fiber.current
         
     | 
| 
      
 314 
     | 
    
         
            +
                    call_command(keys.flatten.unshift('mget')) { |a|  c.resume(a); }
         
     | 
| 
      
 315 
     | 
    
         
            +
                    Fiber.yield
         
     | 
| 
      
 316 
     | 
    
         
            +
                  end
         
     | 
| 
      
 317 
     | 
    
         
            +
                  
         
     | 
| 
      
 318 
     | 
    
         
            +
                  def set_remove_if_empty(key_name, value)
         
     | 
| 
      
 319 
     | 
    
         
            +
                    set_remove(key_name, value)
         
     | 
| 
      
 320 
     | 
    
         
            +
                    del(key_name) if scard(key_name)==0
         
     | 
| 
      
 321 
     | 
    
         
            +
                  end
         
     | 
| 
      
 322 
     | 
    
         
            +
                  
         
     | 
| 
      
 323 
     | 
    
         
            +
                  def method_missing(*argv)
         
     | 
| 
      
 324 
     | 
    
         
            +
                    name=argv[0].to_s
         
     | 
| 
      
 325 
     | 
    
         
            +
                    p "call #{name} (#{argv[1..argv.length]})"
         
     | 
| 
      
 326 
     | 
    
         
            +
                    if $debug  && false
         
     | 
| 
      
 327 
     | 
    
         
            +
                      p "call #{name} (#{argv[1..argv.length]})" 
         
     | 
| 
      
 328 
     | 
    
         
            +
                      #pp Kernel.caller
         
     | 
| 
      
 329 
     | 
    
         
            +
                      #$log.debug("Redis"){"call #{name} (#{argv[1..argv.length]})"}
         
     | 
| 
      
 330 
     | 
    
         
            +
                      argv[0]=(name=name[0,name.length-1]) if name.end_with?('!')
         
     | 
| 
      
 331 
     | 
    
         
            +
                    end
         
     | 
| 
      
 332 
     | 
    
         
            +
                    if name.end_with?('!')
         
     | 
| 
      
 333 
     | 
    
         
            +
                      argv[0]=name[0,name.length-1]         
         
     | 
| 
      
 334 
     | 
    
         
            +
                      call_command(argv) {|a| }
         
     | 
| 
      
 335 
     | 
    
         
            +
                    else
         
     | 
| 
      
 336 
     | 
    
         
            +
                      c=Fiber.current
         
     | 
| 
      
 337 
     | 
    
         
            +
                      call_command(argv) { |a|  c.resume(a); }
         
     | 
| 
      
 338 
     | 
    
         
            +
                      Fiber.yield
         
     | 
| 
      
 339 
     | 
    
         
            +
                    end      
         
     | 
| 
      
 340 
     | 
    
         
            +
                  end
         
     | 
| 
      
 341 
     | 
    
         
            +
                  
         
     | 
| 
      
 342 
     | 
    
         
            +
                  def support_mset?    
         
     | 
| 
      
 343 
     | 
    
         
            +
                    @support_mset||=version >= "1.1" 
         
     | 
| 
      
 344 
     | 
    
         
            +
                  end
         
     | 
| 
      
 345 
     | 
    
         
            +
                  
         
     | 
| 
      
 346 
     | 
    
         
            +
                  def raw_call_command(args, &blk)
         
     | 
| 
      
 347 
     | 
    
         
            +
                    argv = args.flatten.map{|v| v.to_s}
         
     | 
| 
      
 348 
     | 
    
         
            +
                    
         
     | 
| 
      
 349 
     | 
    
         
            +
                    if MULTI_BULK_COMMANDS[argv.first]
         
     | 
| 
      
 350 
     | 
    
         
            +
                      command = "*#{argv.size}\r\n"
         
     | 
| 
      
 351 
     | 
    
         
            +
                      argv.each do |v|
         
     | 
| 
      
 352 
     | 
    
         
            +
                        command << "$#{get_size(v)}\r\n"
         
     | 
| 
      
 353 
     | 
    
         
            +
                        command << "#{v}\r\n"
         
     | 
| 
      
 354 
     | 
    
         
            +
                      end          
         
     | 
| 
      
 355 
     | 
    
         
            +
                    else
         
     | 
| 
      
 356 
     | 
    
         
            +
                      name = argv[0].downcase
         
     | 
| 
      
 357 
     | 
    
         
            +
                      argv[0] = (ALIASES[name] || name)
         
     | 
| 
      
 358 
     | 
    
         
            +
                      raise "#{name} command is disabled" if DISABLED_COMMANDS[name]
         
     | 
| 
      
 359 
     | 
    
         
            +
                      if argv.length > 2 and BULK_COMMANDS[name]
         
     | 
| 
      
 360 
     | 
    
         
            +
                        bulk=argv[-1]
         
     | 
| 
      
 361 
     | 
    
         
            +
                        argv[-1]=get_size(bulk)
         
     | 
| 
      
 362 
     | 
    
         
            +
                      end
         
     | 
| 
      
 363 
     | 
    
         
            +
                      command = "#{argv.join(' ')}\r\n"
         
     | 
| 
      
 364 
     | 
    
         
            +
                      command << "#{bulk}\r\n" if bulk
         
     | 
| 
      
 365 
     | 
    
         
            +
                    end
         
     | 
| 
      
 366 
     | 
    
         
            +
                    
         
     | 
| 
      
 367 
     | 
    
         
            +
                    puts "*** sending: #{command}" if $debug
         
     | 
| 
      
 368 
     | 
    
         
            +
                    @redis_callbacks << [REPLY_PROCESSOR[argv[0]], blk]
         
     | 
| 
      
 369 
     | 
    
         
            +
                    send_data command
         
     | 
| 
      
 370 
     | 
    
         
            +
                  end
         
     | 
| 
      
 371 
     | 
    
         
            +
                  
         
     | 
| 
      
 372 
     | 
    
         
            +
                  
         
     | 
| 
      
 373 
     | 
    
         
            +
                  def process_cmd(line)
         
     | 
| 
      
 374 
     | 
    
         
            +
                    puts "*** processing #{line}" if $debug
         
     | 
| 
      
 375 
     | 
    
         
            +
                    # first character of buffer will always be the response type
         
     | 
| 
      
 376 
     | 
    
         
            +
                    reply_type = line[0, 1]
         
     | 
| 
      
 377 
     | 
    
         
            +
                    reply_args = line.slice(1..-3) # remove type character and \r\n
         
     | 
| 
      
 378 
     | 
    
         
            +
                    case reply_type
         
     | 
| 
      
 379 
     | 
    
         
            +
                      
         
     | 
| 
      
 380 
     | 
    
         
            +
                      #e.g. -MISSING
         
     | 
| 
      
 381 
     | 
    
         
            +
                      when MINUS
         
     | 
| 
      
 382 
     | 
    
         
            +
                      @redis_callbacks.shift # throw away the cb?
         
     | 
| 
      
 383 
     | 
    
         
            +
                      if @err_cb
         
     | 
| 
      
 384 
     | 
    
         
            +
                        @err_cb.call(reply_args)
         
     | 
| 
      
 385 
     | 
    
         
            +
                      else
         
     | 
| 
      
 386 
     | 
    
         
            +
                        err = RedisError.new
         
     | 
| 
      
 387 
     | 
    
         
            +
                        err.code = reply_args
         
     | 
| 
      
 388 
     | 
    
         
            +
                        raise err, "Redis server returned error code: #{err.code}"
         
     | 
| 
      
 389 
     | 
    
         
            +
                      end
         
     | 
| 
      
 390 
     | 
    
         
            +
                      
         
     | 
| 
      
 391 
     | 
    
         
            +
                      # e.g. +OK
         
     | 
| 
      
 392 
     | 
    
         
            +
                      when PLUS
         
     | 
| 
      
 393 
     | 
    
         
            +
                      dispatch_response(reply_args)
         
     | 
| 
      
 394 
     | 
    
         
            +
                      
         
     | 
| 
      
 395 
     | 
    
         
            +
                      # e.g. $3\r\nabc\r\n
         
     | 
| 
      
 396 
     | 
    
         
            +
                      # 'bulk' is more complex because it could be part of multi-bulk
         
     | 
| 
      
 397 
     | 
    
         
            +
                      when DOLLAR
         
     | 
| 
      
 398 
     | 
    
         
            +
                      data_len = Integer(reply_args)
         
     | 
| 
      
 399 
     | 
    
         
            +
                      if data_len == -1 # expect no data; return nil
         
     | 
| 
      
 400 
     | 
    
         
            +
                        if @multibulk_n > 0 # we're in the middle of a multibulk reply
         
     | 
| 
      
 401 
     | 
    
         
            +
                          @values << nil
         
     | 
| 
      
 402 
     | 
    
         
            +
                          if @values.size == @multibulk_n # DING, we're done
         
     | 
| 
      
 403 
     | 
    
         
            +
                            dispatch_response(@values)
         
     | 
| 
      
 404 
     | 
    
         
            +
                            @values = []
         
     | 
| 
      
 405 
     | 
    
         
            +
                            @multibulk_n = 0
         
     | 
| 
      
 406 
     | 
    
         
            +
                          end
         
     | 
| 
      
 407 
     | 
    
         
            +
                        else
         
     | 
| 
      
 408 
     | 
    
         
            +
                          dispatch_response(nil)
         
     | 
| 
      
 409 
     | 
    
         
            +
                        end
         
     | 
| 
      
 410 
     | 
    
         
            +
                      elsif @buffer.size >= data_len + 2 # buffer is full of expected data
         
     | 
| 
      
 411 
     | 
    
         
            +
                        if @multibulk_n > 0 # we're in the middle of a multibulk reply
         
     | 
| 
      
 412 
     | 
    
         
            +
                          @values << @buffer.slice!(0, data_len)
         
     | 
| 
      
 413 
     | 
    
         
            +
                          if @values.size == @multibulk_n # DING, we're done
         
     | 
| 
      
 414 
     | 
    
         
            +
                            dispatch_response(@values)
         
     | 
| 
      
 415 
     | 
    
         
            +
                            @values = []
         
     | 
| 
      
 416 
     | 
    
         
            +
                            @multibulk_n = 0
         
     | 
| 
      
 417 
     | 
    
         
            +
                          end
         
     | 
| 
      
 418 
     | 
    
         
            +
                        else # not multibulk
         
     | 
| 
      
 419 
     | 
    
         
            +
                          value = @buffer.slice!(0, data_len)
         
     | 
| 
      
 420 
     | 
    
         
            +
                          dispatch_response(value)
         
     | 
| 
      
 421 
     | 
    
         
            +
                        end
         
     | 
| 
      
 422 
     | 
    
         
            +
                        @buffer.slice!(0,2) # tossing \r\n
         
     | 
| 
      
 423 
     | 
    
         
            +
                      else # buffer isn't full or nil
         
     | 
| 
      
 424 
     | 
    
         
            +
                        # FYI, ParseError puts command back on head of buffer, waits for
         
     | 
| 
      
 425 
     | 
    
         
            +
                        # more data complete buffer
         
     | 
| 
      
 426 
     | 
    
         
            +
                        raise ParserError 
         
     | 
| 
      
 427 
     | 
    
         
            +
                      end
         
     | 
| 
      
 428 
     | 
    
         
            +
                      
         
     | 
| 
      
 429 
     | 
    
         
            +
                      #e.g. :8
         
     | 
| 
      
 430 
     | 
    
         
            +
                      when COLON
         
     | 
| 
      
 431 
     | 
    
         
            +
                      dispatch_response(Integer(reply_args))
         
     | 
| 
      
 432 
     | 
    
         
            +
                      
         
     | 
| 
      
 433 
     | 
    
         
            +
                      #e.g. *2\r\n$1\r\na\r\n$1\r\nb\r\n 
         
     | 
| 
      
 434 
     | 
    
         
            +
                      when ASTERISK
         
     | 
| 
      
 435 
     | 
    
         
            +
                      @multibulk_n = Integer(reply_args)
         
     | 
| 
      
 436 
     | 
    
         
            +
                      dispatch_response(nil) if @multibulk_n == -1 || @multibulk_n == 0
         
     | 
| 
      
 437 
     | 
    
         
            +
                      
         
     | 
| 
      
 438 
     | 
    
         
            +
                      # Whu?
         
     | 
| 
      
 439 
     | 
    
         
            +
                    else
         
     | 
| 
      
 440 
     | 
    
         
            +
                      raise ProtocolError, "reply type not recognized: #{line.strip}"
         
     | 
| 
      
 441 
     | 
    
         
            +
                    end
         
     | 
| 
      
 442 
     | 
    
         
            +
                  end
         
     | 
| 
      
 443 
     | 
    
         
            +
                end
         
     | 
| 
      
 444 
     | 
    
         
            +
              end
         
     | 
| 
      
 445 
     | 
    
         
            +
            end
         
     |