class Redi2casa

Constants

VERSION

Public Class Methods

new(hosts, keyspace) click to toggle source
# File lib/redi2casa.rb, line 7
def initialize(hosts, keyspace)
  @hosts = hosts
  @keyspace = keyspace
  connect
end

Public Instance Methods

clear_bits(key) click to toggle source
# File lib/redi2casa/clear_bits.rb, line 2
def clear_bits key
  del(key, "hash")
end
Also aliased as: clearbits
clearbits(key)
Alias for: clear_bits
connect() click to toggle source
# File lib/redi2casa.rb, line 13
def connect
  @db_conn =  Cql::Client.connect(:hosts => @hosts)
  @db_conn.use(@keyspace)
end
Also aliased as: reconnect
del(key, type) click to toggle source

Type can be sets, hashes, lists, keyvalue

# File lib/redi2casa/del.rb, line 3
def del key, type
  if type == "counter"
    raise RuntimeError.new("deleting counters is not supported")
  elsif type == 'keyvalue'
    execute("delete from keyvalue where key = ?", key.to_s)
  elsif type == 'hash'
    execute("delete from hashes where key = ?", key.to_s)
  elsif type == 'list'
    execute("delete from lists where namespace = ?", key.to_s)
  elsif type == 'set'
    execute("delete from sets where key = ?", key.to_s)
  elsif type == 'sorted_set'
    execute("delete from sorted_sets where key = ?", key.to_s)
  else
    raise RuntimeError.new("Invalid type")
  end
end
execute(base_query, *args) click to toggle source
# File lib/redi2casa.rb, line 24
def execute base_query, *args
  @failed ||= 0
  statement = @db_conn.prepare(base_query)
  statement.execute(*args)
rescue Cql::NotConnectedError, Cql::Io::ConnectionError, Cql::Io::ConnectionTimeoutError
  reconnect
  @failed += 1
  retry if @failed < 3
  raise
rescue Cql::QueryError => e
  if e.message =~  /Operation timed out/i
    @failed += 1
    retry if @failed < 3
    raise
  end
  raise Redi2casaError.new("Cql::QueryError query:#{base_query}, args: #{args.inspect}, exception: #{e.inspect}")
rescue ThreadError => e
  if e.message =~ /Attempt to unlock a mutex/i
    @failed += 1
    retry if @failed < 3
    raise
  end
  raise
ensure
  @failed = 0
end
get(key, type = 'keyvalue') click to toggle source
# File lib/redi2casa/get.rb, line 2
def get key, type = 'keyvalue'
  if type == 'keyvalue'
    resp = execute("select * from keyvalue where key = ?", key.to_s)
    parse_response(resp, "value")
  elsif type == 'counter'
    # @ is a special column in counter used by incrby
    resp = execute("select value from counters where KEY = ? and column1 = '@'", key.to_s)
    parse_response(resp, "value").to_i
  end
end
get_bit(key, offset) click to toggle source
# File lib/redi2casa/get_bit.rb, line 2
def get_bit key, offset
  hget("#{key}", "#{offset}", "hash").to_i
end
Also aliased as: getbit
getbit(key, offset)
Alias for: get_bit
hdel(key, column1, type = "counter") click to toggle source
# File lib/redi2casa/hdel.rb, line 2
def hdel key, column1, type = "counter"
  if type == "counter"
    raise RuntimeError.new("deleting counters is not supported")
  elsif type == "hash"
    execute("delete from hashes where key = ? and column1 = ?", key.to_s, column1.to_s)
  end
end
hget(key, column1, type = "counter") click to toggle source
# File lib/redi2casa/hget.rb, line 2
def hget key, column1, type = "counter"
  if type.to_s == "hash"
    resp = execute("select value from hashes where key = ? and column1 = ?", key.to_s, column1.to_s)
    parse_response(resp, "value")
  elsif type.to_s == "counter"
    resp = execute("select value from counters where key = ? and column1 = ?", key.to_s, column1.to_s)
    parse_response(resp, "value").to_i
  end
end
hgetall(key, type = "counter") click to toggle source
# File lib/redi2casa/hgetall.rb, line 2
def hgetall key, type = "counter"
  if type.to_s == "counter"
    response = execute("select * from counters where KEY = ?", key.to_s)
    response.inject({}) {|hsh, entry| hsh[entry["column1"]] = entry["value"]; hsh}
  elsif type.to_s == "hash"
    response = execute("select * from hashes where KEY = ?", key.to_s)
    response.inject({}) {|hsh, entry| hsh[entry["column1"]] = entry["value"]; hsh}
  end
end
hincrby(key, column1, value = 1) click to toggle source
# File lib/redi2casa/hincrby.rb, line 2
def hincrby key, column1, value = 1
  value = value.to_i
  execute("update counters set value = value + ? where key = ? and column1 = ?", value, key.to_s, column1.to_s)
end
hkeys(key, type = "counter") click to toggle source
# File lib/redi2casa/hkeys.rb, line 2
def hkeys key, type = "counter"
  if type.to_s == "counter"
    response = execute("select column1 from counters where key = ?", key.to_s)
  elsif type.to_s == "hash"
    response = execute("select column1 from hashes where key = ?", key.to_s)
  else
    raise RuntimeError.new("Invalid type")
  end
  response.collect {|entry| entry["column1"]}
end
hlen(key, type = "hash") click to toggle source

Supports only hashes now

# File lib/redi2casa/hlen.rb, line 3
def hlen key, type = "hash"
  if type == "hash"
    response = execute("select count(*) from hashes where key = ?", key.to_s)
  elsif type == "counter"
    response = execute("select count(*) from counters where key = ?", key.to_s)
  else
    raise RuntimeError.new("Invalid type")
  end
  parse_response(response, "count").to_i
end
hmget(key, type, *column1s) click to toggle source
# File lib/redi2casa/hmget.rb, line 2
def hmget key, type, *column1s
  column1s.flatten!
  column1s.collect! { |column1|  column1.to_s }
  if type == "counter"
    table = "counters"
  elsif type == "hash"
    table = "hashes"
  else
    raise RuntimeError.new("invalid table")
  end

  resp = execute("select column1, value from #{table} where key = ? and column1 IN ?", key.to_s, column1s)
  hash = {}
  resp.each do |r|
    hash[r["column1"]] = r["value"]
  end
  result = []
  column1s.each do |column1|
    result << hash[column1.to_s]
  end
  result
end
hset(key, column1, value) click to toggle source
# File lib/redi2casa/hset.rb, line 2
def hset key, column1, value
  execute("UPDATE hashes SET value = ? WHERE column1 = ? and key = ?", value.to_s, column1.to_s, key.to_s)
end
incr(key, value = 1)
Alias for: incrby
incrby(key, value = 1) click to toggle source
# File lib/redi2casa/incrby.rb, line 2
def incrby key, value = 1
  hincrby key, '@', value
end
Also aliased as: incr
keys(pattern, type) click to toggle source
# File lib/redi2casa/keys.rb, line 2
def keys pattern, type

end
lflush(namespace) click to toggle source
# File lib/redi2casa/ltrim.rb, line 19
def lflush namespace
  execute("UPDATE lists SET values = []  WHERE namespace = ?", namespace.to_s)
end
llen(namespace) click to toggle source
# File lib/redi2casa/llen.rb, line 2
def llen namespace
  lrange(namespace, 0, -1).size
end
lpop(namespace) click to toggle source

has a read modify write problem

# File lib/redi2casa/lpop.rb, line 3
def lpop namespace
  resp = execute("select values from lists where namespace = ?", namespace.to_s)
  values = []
  resp.each {|entry| values = entry.to_hash["values"]}
  if values
    resp = values.shift
    lrepush(namespace, values)
  else
    resp = values
  end
  resp
end
lpush(namespace, data) click to toggle source
# File lib/redi2casa/lpush.rb, line 2
def lpush namespace, data
  execute("UPDATE lists SET values = ? + values WHERE namespace = ?", [ data.to_s], namespace.to_s)
end
lrange(namespace, first, last) click to toggle source
# File lib/redi2casa/lrange.rb, line 2
def lrange namespace, first, last
  resp = execute("select values from lists where namespace = ?", namespace.to_s)
  resp.each {|entry| 
    values = entry.to_hash["values"] || []
    return values[first..last]
  }
end
lrem(namespace, count, value) click to toggle source
# File lib/redi2casa/lrem.rb, line 2
def lrem namespace, count, value
  elements = lrange(namespace, 0, -1)
  if count == 0
    delete_positions = []
    elements.delete(value)
  elsif count < 0
    count = count * -1
    delete_positions = []
    (elements.length - 1).downto(0) do |i|
      if elements[i] == value && count > 0
        delete_positions << i
        count -= 1
      end
    end
  elsif count > 0
    delete_positions = []
    elements.each_index do |i|
      if elements[i] == value && count > 0
        delete_positions << i
        count -= 1
      end
    end
  end

  delete_positions.sort!
  delete_positions.reverse!

  puts delete_positions.inspect
  delete_positions.each do |pos|
    elements.delete_at(pos)
  end

  lrepush(namespace, elements)
end
ltrim(namespace, first, last) click to toggle source
# File lib/redi2casa/ltrim.rb, line 2
def ltrim namespace, first, last
  resp = execute("select values from lists where namespace = ?", namespace.to_s)
  values = {}
  resp.each {|entry| values = entry.to_hash["values"]}
  values_count = values.count
  #if first is greater than list length, redis returns empty list
  #if negative value of last is equal or greater than list length a similar behaviour is shown
  if (first > values_count) || (-last >= values_count)
    lflush(namespace)
  elsif last > values_count
    last = values_count
  else
    new_list = values[first..last]
    lrepush(namespace, new_list)
  end
end
mget(keys, type = "keyvalue") click to toggle source
# File lib/redi2casa/mget.rb, line 2
def mget keys, type = "keyvalue"
  values = keys.collect do |key|
    get(key, type)
  end
end
parse_response(resp, key) click to toggle source
# File lib/redi2casa.rb, line 20
def parse_response(resp, key)
  resp.map {|entry| entry[key].to_s }.first
end
reconnect()
Alias for: connect
rpop(namespace) click to toggle source
# File lib/redi2casa/rpop.rb, line 2
def rpop namespace
  resp = execute("select values from lists where namespace = ?", namespace.to_s)
  values = []
  resp.fetch {|entry| values = entry.to_hash["values"]}
  resp = values.pop
  lrepush(namespace, values)
  resp
end
rpush(namespace, data) click to toggle source
# File lib/redi2casa/rpush.rb, line 2
def rpush namespace, data
  execute("UPDATE lists SET values = values + ? WHERE namespace = ?", [ data.to_s], namespace.to_s)
end
sadd(key, *members) click to toggle source
# File lib/redi2casa/sadd.rb, line 2
def sadd key, *members
  members.flatten!
  members.collect! { |member|  member.to_s }
  execute("update sets set members = members + ? where key = ?", members, key.to_s)
end
sadd_with_expire( key, ttl, *members) click to toggle source
# File lib/redi2casa/sadd_with_expire.rb, line 2
def sadd_with_expire( key, ttl, *members)
  members.flatten!
  members.collect! { |member|  member.to_s }
  execute("update sets using ttl ? set members = members + ? where key = ?", ttl, members, key.to_s)
end
scard(key) click to toggle source
# File lib/redi2casa/scard.rb, line 2
def scard key
  smembers(key).size
end
set(key, value) click to toggle source
# File lib/redi2casa/set.rb, line 2
def set key, value
  execute("UPDATE keyvalue set value = ? where key = ?", value.to_s, key.to_s)
end
set_bit(key, offset, bit) click to toggle source

FIXME:This could potentially lead to read write problem.

# File lib/redi2casa/set_bit.rb, line 3
def set_bit key, offset, bit
  if !(bit != 0 || bit != 1)
    raise RuntimeError.new("Invalid bit")
  end
  hset("#{key}","#{offset}", bit.to_s)
end
Also aliased as: setbit
setbit(key, offset, bit)
Alias for: set_bit
setex(key, ttl, value) click to toggle source
# File lib/redi2casa/setex.rb, line 2
def setex key, ttl, value
  execute("UPDATE keyvalue using TTL ? set value = ? where key = ?", ttl, value.to_s, key.to_s)
end
sismember(key, value) click to toggle source
# File lib/redi2casa/sismember.rb, line 2
def sismember key, value
  smembers(key).include?(value)
end
smembers(key) click to toggle source
# File lib/redi2casa/smembers.rb, line 2
def smembers key
  resp = execute("select * from sets where key = ?", key.to_s)
  if resp.first
    resp.first["members"].to_a
  else
    []
  end
end
spop(key) click to toggle source
# File lib/redi2casa/spop.rb, line 2
def spop key
  members = smembers(key)
  if members
    srem key, members.first
    members.first
  end
end
srem(key, member) click to toggle source
# File lib/redi2casa/srem.rb, line 2
def srem key, member
  execute("update sets set members = members - {'#{member}'} where key = ?", key.to_s)
end
zadd(key, score, value) click to toggle source

Random text is an extra column added to make sure we have multiple entries with same key, score and value

# File lib/redi2casa/zadd.rb, line 3
def zadd key, score, value
  execute("insert into sorted_sets (key, score,value, random_text) values (?, ?, ?, ?)", key.to_s, score, value, Time.now.to_i.to_s)
end
zrangebyscore(key, lower_limit, higher_limit = -1) click to toggle source

Ranges are exclusive

# File lib/redi2casa/zrangebyscore.rb, line 3
def zrangebyscore key, lower_limit, higher_limit = -1
  if higher_limit == -1
    resp = execute("select value from sorted_sets where key = ? and score >= ?", key.to_s, lower_limit)
  else
    resp = execute("select value from sorted_sets where key = ? and score >= ? and score <= ?", key.to_s, lower_limit, higher_limit)
  end
  resp.collect do |entry|
    entry['value']
  end
end
zrem(key, member) click to toggle source
# File lib/redi2casa/zrem.rb, line 2
def zrem key, member
  resp = execute("select * from sorted_sets where key = ?", key.to_s)
  resp.each do |entry|
    if entry['value'] == member.to_s
      execute("delete from sorted_sets where key = ? and score = ? and value = ?", key, entry['score'], entry['value'])
    end
  end
end
zscore(key, value) click to toggle source
# File lib/redi2casa/zscore.rb, line 2
def zscore key, value
  resp = execute("select * from sorted_sets where key = ?", key.to_s)
  resp.each do |entry|
    return entry['score'] if entry['value'] == value.to_s
  end
  nil
end

Private Instance Methods

lrepush(namespace, list) click to toggle source
# File lib/redi2casa/ltrim.rb, line 24
def lrepush namespace, list
  execute("UPDATE lists SET values = ?  WHERE namespace = ?", list, namespace.to_s)
end