daybreak 0.1.2 → 0.1.3
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/lib/daybreak.rb +0 -2
- data/lib/daybreak/db.rb +7 -2
- data/lib/daybreak/record.rb +13 -18
- data/lib/daybreak/version.rb +1 -1
- data/lib/daybreak/writer.rb +30 -24
- metadata +51 -66
- data/lib/daybreak/locking.rb +0 -14
- data/lib/daybreak/reader.rb +0 -26
data/lib/daybreak.rb
CHANGED
data/lib/daybreak/db.rb
CHANGED
@@ -16,7 +16,6 @@ module Daybreak
|
|
16
16
|
def initialize(file, default=nil, &blk)
|
17
17
|
@table = {}
|
18
18
|
@file_name = file
|
19
|
-
@reader = Reader.new(@file_name)
|
20
19
|
@writer = Writer.new(@file_name)
|
21
20
|
@default = block_given? ? blk : default
|
22
21
|
read!
|
@@ -162,7 +161,13 @@ module Daybreak
|
|
162
161
|
# Read all values from the log file. If you want to check for changed data
|
163
162
|
# call this again.
|
164
163
|
def read!
|
165
|
-
|
164
|
+
buf = nil
|
165
|
+
File.open(@file_name, 'rb') do |fd|
|
166
|
+
fd.flock(File::LOCK_SH)
|
167
|
+
buf = fd.read
|
168
|
+
end
|
169
|
+
until buf.empty?
|
170
|
+
key, data, deleted = Record.deserialize(buf)
|
166
171
|
if deleted
|
167
172
|
@table.delete key
|
168
173
|
else
|
data/lib/daybreak/record.rb
CHANGED
@@ -9,7 +9,6 @@ module Daybreak
|
|
9
9
|
class CorruptDataError < Exception; end
|
10
10
|
|
11
11
|
extend self
|
12
|
-
extend Locking
|
13
12
|
|
14
13
|
# The mask a record uses to check for deletion.
|
15
14
|
DELETION_MASK = 1 << 31
|
@@ -28,20 +27,17 @@ module Daybreak
|
|
28
27
|
|
29
28
|
# Create a new record to read from IO.
|
30
29
|
# @param [#read] io an IO instance to read from
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
raise CorruptDataError, 'CRC mismatch' unless crc == crc_string(key_data_string(record))
|
43
|
-
record
|
44
|
-
end
|
30
|
+
def deserialize(buf)
|
31
|
+
record = []
|
32
|
+
masked = read32(buf)
|
33
|
+
# Read the record's key bytes
|
34
|
+
record << buf.slice!(0, masked & (DELETION_MASK - 1)) <<
|
35
|
+
# Read the record's value bytes
|
36
|
+
buf.slice!(0, read32(buf)) <<
|
37
|
+
# Set the deletion flag
|
38
|
+
((masked & DELETION_MASK) != 0)
|
39
|
+
raise CorruptDataError, 'CRC mismatch' unless buf.slice!(0, 4) == crc_string(key_data_string(record))
|
40
|
+
record
|
45
41
|
end
|
46
42
|
|
47
43
|
private
|
@@ -59,9 +55,8 @@ module Daybreak
|
|
59
55
|
[length].pack('N') << data
|
60
56
|
end
|
61
57
|
|
62
|
-
def read32(
|
63
|
-
|
64
|
-
raw.unpack('N')[0]
|
58
|
+
def read32(buf)
|
59
|
+
buf.slice!(0, 4).unpack('N')[0]
|
65
60
|
end
|
66
61
|
end
|
67
62
|
end
|
data/lib/daybreak/version.rb
CHANGED
data/lib/daybreak/writer.rb
CHANGED
@@ -40,8 +40,7 @@ module Daybreak
|
|
40
40
|
private
|
41
41
|
|
42
42
|
def open!
|
43
|
-
@fd = File.open @file, '
|
44
|
-
@fd.binmode
|
43
|
+
@fd = File.open @file, 'ab'
|
45
44
|
|
46
45
|
if defined?(Fcntl::O_NONBLOCK)
|
47
46
|
f = @fd.fcntl(Fcntl::F_GETFL, 0)
|
@@ -52,8 +51,6 @@ module Daybreak
|
|
52
51
|
# Workers handle the actual fiddly bits of asynchronous io and
|
53
52
|
# and handle background writes.
|
54
53
|
class Worker
|
55
|
-
include Locking
|
56
|
-
|
57
54
|
def initialize(fd)
|
58
55
|
@queue = Queue.new
|
59
56
|
@fd = fd
|
@@ -69,41 +66,50 @@ module Daybreak
|
|
69
66
|
# Loop and block if we don't have work to do or if
|
70
67
|
# the file isn't ready for another write just yet.
|
71
68
|
def work
|
72
|
-
buf = ''
|
73
|
-
|
69
|
+
buf, finished = '', false
|
70
|
+
until finished && buf.empty?
|
74
71
|
record = @queue.pop
|
75
|
-
|
76
|
-
|
77
|
-
|
72
|
+
if record
|
73
|
+
buf << Record.serialize(record)
|
74
|
+
else
|
75
|
+
finished = true
|
78
76
|
end
|
79
|
-
buf << Record.serialize(record)
|
80
77
|
read, write = IO.select [], [@fd]
|
81
78
|
if write and fd = write.first
|
82
|
-
lock(fd
|
79
|
+
lock(fd) { buf = try_write fd, buf }
|
83
80
|
end
|
84
81
|
end
|
82
|
+
@fd.flush
|
85
83
|
end
|
86
84
|
|
87
85
|
# Try and write the buffer to the file via non blocking file writes.
|
88
86
|
# If the write fails try again.
|
89
87
|
def try_write(fd, buf)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
s = fd.write(buf)
|
95
|
-
end
|
96
|
-
if s < buf.length
|
97
|
-
buf = buf[s..-1] # didn't finish
|
98
|
-
else
|
99
|
-
buf = ''
|
100
|
-
end
|
101
|
-
rescue Errno::EAGAIN
|
102
|
-
buf = buf # try this again
|
88
|
+
if defined?(Fcntl::O_NONBLOCK)
|
89
|
+
s = fd.write_nonblock(buf)
|
90
|
+
else
|
91
|
+
s = fd.write(buf)
|
103
92
|
end
|
93
|
+
if s < buf.length
|
94
|
+
buf = buf[s..-1] # didn't finish
|
95
|
+
else
|
96
|
+
buf = ""
|
97
|
+
end
|
98
|
+
buf
|
99
|
+
rescue Errno::EAGAIN
|
104
100
|
buf
|
105
101
|
end
|
106
102
|
|
103
|
+
# Lock a file with the type <tt>lock</tt>
|
104
|
+
def lock(fd)
|
105
|
+
fd.flock File::LOCK_EX
|
106
|
+
begin
|
107
|
+
yield
|
108
|
+
ensure
|
109
|
+
fd.flock File::LOCK_UN
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
107
113
|
# finish! and start up another worker thread.
|
108
114
|
def flush!
|
109
115
|
finish!
|
metadata
CHANGED
@@ -1,61 +1,55 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: daybreak
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.3
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 1
|
9
|
-
- 2
|
10
|
-
version: 0.1.2
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Jeff Larson
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2013-01-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
22
15
|
name: rake
|
23
|
-
|
24
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
25
17
|
none: false
|
26
|
-
requirements:
|
27
|
-
- -
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
segments:
|
31
|
-
- 0
|
32
|
-
version: "0"
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
33
22
|
type: :development
|
34
|
-
version_requirements: *id001
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
|
-
name: minitest
|
37
23
|
prerelease: false
|
38
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: minitest
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
39
33
|
none: false
|
40
|
-
requirements:
|
41
|
-
- -
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
|
44
|
-
segments:
|
45
|
-
- 0
|
46
|
-
version: "0"
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
47
38
|
type: :development
|
48
|
-
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
49
46
|
description: A simple dimple key-value store for ruby.
|
50
|
-
email:
|
47
|
+
email:
|
51
48
|
- thejefflarson@gmail.com
|
52
49
|
executables: []
|
53
|
-
|
54
50
|
extensions: []
|
55
|
-
|
56
51
|
extra_rdoc_files: []
|
57
|
-
|
58
|
-
files:
|
52
|
+
files:
|
59
53
|
- .gitignore
|
60
54
|
- .travis.yml
|
61
55
|
- .yardopts
|
@@ -67,8 +61,6 @@ files:
|
|
67
61
|
- daybreak.gemspec
|
68
62
|
- lib/daybreak.rb
|
69
63
|
- lib/daybreak/db.rb
|
70
|
-
- lib/daybreak/locking.rb
|
71
|
-
- lib/daybreak/reader.rb
|
72
64
|
- lib/daybreak/record.rb
|
73
65
|
- lib/daybreak/version.rb
|
74
66
|
- lib/daybreak/writer.rb
|
@@ -77,43 +69,36 @@ files:
|
|
77
69
|
- test/prof.rb
|
78
70
|
- test/test.rb
|
79
71
|
- test/test_helper.rb
|
80
|
-
has_rdoc: true
|
81
72
|
homepage: http://propublica.github.com/daybreak/
|
82
|
-
licenses:
|
73
|
+
licenses:
|
83
74
|
- MIT
|
84
75
|
post_install_message:
|
85
76
|
rdoc_options: []
|
86
|
-
|
87
|
-
require_paths:
|
77
|
+
require_paths:
|
88
78
|
- lib
|
89
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
80
|
none: false
|
91
|
-
requirements:
|
92
|
-
- -
|
93
|
-
- !ruby/object:Gem::Version
|
94
|
-
|
95
|
-
|
96
|
-
- 0
|
97
|
-
version: "0"
|
98
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
86
|
none: false
|
100
|
-
requirements:
|
101
|
-
- -
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
|
104
|
-
segments:
|
105
|
-
- 0
|
106
|
-
version: "0"
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
107
91
|
requirements: []
|
108
|
-
|
109
92
|
rubyforge_project:
|
110
|
-
rubygems_version: 1.
|
93
|
+
rubygems_version: 1.8.24
|
111
94
|
signing_key:
|
112
95
|
specification_version: 3
|
113
|
-
summary: Daybreak provides an in memory key-value store that is easily enumerable
|
114
|
-
|
96
|
+
summary: Daybreak provides an in memory key-value store that is easily enumerable
|
97
|
+
in ruby.
|
98
|
+
test_files:
|
115
99
|
- test/bench.rb
|
116
100
|
- test/compare.rb
|
117
101
|
- test/prof.rb
|
118
102
|
- test/test.rb
|
119
103
|
- test/test_helper.rb
|
104
|
+
has_rdoc:
|
data/lib/daybreak/locking.rb
DELETED
data/lib/daybreak/reader.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
module Daybreak
|
2
|
-
# Class for building out the table, you shouldn't need to access this
|
3
|
-
# class directly. Readers are responsible for reading each record in
|
4
|
-
# the file and yeilding the parsed records.
|
5
|
-
class Reader
|
6
|
-
# @param [String] file the file to read from
|
7
|
-
def initialize(file)
|
8
|
-
@file_name = file
|
9
|
-
end
|
10
|
-
|
11
|
-
# Read all values from the aof file.
|
12
|
-
#
|
13
|
-
# Right now this is really expensive, every call to read will
|
14
|
-
# close and reread the whole db file, but since cross process
|
15
|
-
# consistency is handled by the user, this should be fair warning.
|
16
|
-
def read
|
17
|
-
File.open(@file_name, 'r') do |fd|
|
18
|
-
fd.binmode
|
19
|
-
fd.advise(:sequential) if fd.respond_to? :advise
|
20
|
-
while !fd.eof?
|
21
|
-
yield Record.read(fd)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|