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 CHANGED
@@ -4,8 +4,6 @@ require 'fcntl'
4
4
  require 'zlib'
5
5
 
6
6
  require 'daybreak/version'
7
- require 'daybreak/locking'
8
7
  require 'daybreak/record'
9
8
  require 'daybreak/writer'
10
- require 'daybreak/reader'
11
9
  require 'daybreak/db'
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
- @reader.read do |(key, data, deleted)|
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
@@ -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 read(io)
32
- lock io do
33
- record = []
34
- masked = read32(io)
35
- # Read the record's key bytes
36
- record << io.read(masked & (DELETION_MASK - 1)) <<
37
- # Read the record's value bytes
38
- io.read(read32(io)) <<
39
- # Set the deletion flag
40
- ((masked & DELETION_MASK) != 0)
41
- crc = io.read(4)
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(io)
63
- raw = io.read(4)
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
@@ -1,4 +1,4 @@
1
1
  module Daybreak
2
2
  # Updated using SemVer
3
- VERSION = "0.1.2"
3
+ VERSION = "0.1.3"
4
4
  end
@@ -40,8 +40,7 @@ module Daybreak
40
40
  private
41
41
 
42
42
  def open!
43
- @fd = File.open @file, 'a'
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
- loop do
69
+ buf, finished = '', false
70
+ until finished && buf.empty?
74
71
  record = @queue.pop
75
- unless record
76
- @fd.flush
77
- break
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, File::LOCK_EX) { buf = try_write fd, buf }
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
- begin
91
- if defined?(Fcntl::O_NONBLOCK)
92
- s = fd.write_nonblock(buf)
93
- else
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
- hash: 31
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
- date: 2013-01-07 00:00:00 -05:00
19
- default_executable:
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
- prerelease: false
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
- hash: 3
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
- requirement: &id002 !ruby/object:Gem::Requirement
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
- hash: 3
44
- segments:
45
- - 0
46
- version: "0"
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
47
38
  type: :development
48
- version_requirements: *id002
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
- hash: 3
95
- segments:
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
- hash: 3
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.6.2
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 in ruby.
114
- test_files:
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:
@@ -1,14 +0,0 @@
1
- module Daybreak
2
- # File locking mixin
3
- module Locking
4
- # Lock a file with the type <tt>lock</tt>
5
- def lock(fd, lock=File::LOCK_SH)
6
- fd.flock lock
7
- begin
8
- yield
9
- ensure
10
- fd.flock File::LOCK_UN
11
- end
12
- end
13
- end
14
- end
@@ -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