Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
144 lines (120 sloc) 3.94 KB
require 'rubygems'
require 'sinatra/base'
require 'zlib'
__LIB_DIR__ = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift __LIB_DIR__ unless
$LOAD_PATH.include?(__LIB_DIR__) ||
$LOAD_PATH.include?(File.expand_path(__LIB_DIR__))
require 'bitkeg/pair'
require 'bitkeg/server'
class BitKeg
unless const_defined?(:METADATA_LENGTH)
METADATA_LENGTH = 16
end
unless const_defined?(:TOMBSTONE)
TOMBSTONE = 'bitkeg_tombstone'
end
unless const_defined?(:VERSION)
VERSION = '0.0.0'
end
def self.run
Rack::Handler::WEBrick.run(
BitKeg::Server.new,
:Port => 9292,
:AccessLog => [],
:Logger => WEBrick::Log.new(nil, WEBrick::Log::ERROR)
)
end
def initialize(directory)
Thread.main[:BitKeg] ||= {}
@directory = File.expand_path(directory)
# load existing data from hint files
files = Dir.glob(File.join(@directory, '*.keg.hint'))
for file in files
file_basename = file[0...-9] # file - '.keg.hint'
offset = 0
pair = BitKeg::Pair.new(:file_basename => file_basename)
while data = IO.read(pair.hint_filename, METADATA_LENGTH, offset)
# hint => [32 bit int timestamp, key length, value length, value position, key]
pair.timestamp, pair.key_length, pair.value_length, pair.offset = data.unpack('IIII')
pair.key = IO.read(pair.hint_filename, pair.key_length, pair.offset + METADATA_LENGTH)
pair.write_memory
# prepare for next iteration
offset += pair.length
pair = BitKeg::Pair.new(:file_basename => file_basename)
end
end
@file_basename = File.join(@directory, Time.now.to_f.to_s)
end
def delete(key)
if value = Thread.main[:BitKeg][key]
file_basename, length, offset, timestamp = value
pair = BitKeg::Pair.new(
:file_basename => file_basename,
:key => key,
:value => BitKeg::TOMBSTONE
)
pair.write_data
Thread.main[:BitKeg].delete(key)
true
else
false
end
end
def get(key)
if value = Thread.main[:BitKeg][key]
file_basename, length, offset, timestamp = value
pair = BitKeg::Pair.new(:file_basename => file_basename, :offset => offset)
if data = IO.read(pair.data_filename, length, offset)
# data => [crc, 32 bit int timestamp, key length, value length, key, value]
crc, pair.timestamp, pair.key_length, pair.value_length = data.unpack('IIII')
if crc != Zlib::crc32(data[4..-1])
raise 'crc mismatch'
end
pair.key = data[METADATA_LENGTH, pair.key_length]
pair.value = data[METADATA_LENGTH + pair.key_length, pair.value_length]
end
end
end
def merge
merged_basename = File.join(@directory, Time.now.to_f.to_s)
merged_files = Dir.glob(File.join(@directory, '*.keg.hint'))
merged_files.map! {|file_basename| file_basename[0...-9]}
merged_files -= [@file_basename]
for key, values in Thread.main[:BitKeg]
file_basename, length, offset, timestamp = values
if file_basename != @file_basename
pair = BitKeg::Pair.new(
:file_basename => file_basename,
:key => key,
:offset => offset
)
non_value_length = METADATA_LENGTH + pair.key_length
if data = IO.read(pair.data_filename, length - non_value_length, offset + non_value_length)
pair.value = data
end
pair.file_basename = merged_basename
pair.write_data
pair.write_hint
pair.write_memory
end
end
for file in merged_files
File.delete("#{file}.keg.data")
File.delete("#{file}.keg.hint")
end
true
end
# TODO: rotate files when they become large
def put(key, value)
pair = BitKeg::Pair.new(
:file_basename => @file_basename,
:key => key,
:value => value
)
pair.write_data
pair.write_hint
pair.write_memory
pair.value
end
end