rubcask 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0568bcb47da50a78d9679c88b735cb5b2c259efb00297bb87f749463520cb371'
4
- data.tar.gz: 210aaa3e1dd5eb5b0dd26a105f3151717661ff92f24ff54ded3077306b07b838
3
+ metadata.gz: e54816bf08930641478dc6a0438debb94e82f774b2fcaa857fd0f6786f7adc6b
4
+ data.tar.gz: be69c187580c84f89572a445296728f5b987a48252c6470cf43853700aa6b1a0
5
5
  SHA512:
6
- metadata.gz: f52adc24589f583eb1e777c5fe42c1c672e9b2c39e6cd2da5ca75f9be61d7c208a90d146a43adee28978c6217e3557e3b2c89a4290051f10fc2bf92290ab3d6c
7
- data.tar.gz: 626f339669b988a5f844b90285e92985122a08d6144d22bd1a311411ca298876656ef375b13e4230da1c49edb00120191d53d046358fef107e13b34ab1634812
6
+ metadata.gz: 026aaaffc7a9ae96447a21cdd9690fe9bde5b0af59be11e3a04cafea0cce7630916e789baa9fd9f68b6c2082275ca5c096e95441efe35b12df70101a37ae69b8
7
+ data.tar.gz: 870854934247751b5b0597f09c73f5ae256ea02540b5a93b383071c6c0324f515cb0c56e77eed4d586f6d0ed937fadb0fcb2aa3fc17c0a9b5d189d142637f304
data/.standard.yml CHANGED
@@ -1,3 +1,3 @@
1
- ruby_version: 3.2.0
1
+ ruby_version: 2.7.0
2
2
  ignore:
3
3
  - 'benchmark/**/*'
data/Gemfile CHANGED
@@ -7,9 +7,9 @@ gemspec
7
7
 
8
8
  gem "rake", "~> 13.0"
9
9
 
10
- gem "minitest", "~> 5.0"
10
+ gem "minitest", "~> 5.25"
11
11
 
12
- gem "standard", "~> 1.20"
12
+ gem "standard", "~> 1.33"
13
13
 
14
14
  gem "benchmark-ips", "~> 2.10"
15
15
 
data/Gemfile.lock CHANGED
@@ -3,6 +3,7 @@ PATH
3
3
  specs:
4
4
  rubcask (0.2.0)
5
5
  concurrent-ruby (~> 1.1)
6
+ stringio (~> 3.1)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
@@ -11,50 +12,65 @@ GEM
11
12
  benchmark-ips (2.10.0)
12
13
  concurrent-ruby (1.2.0)
13
14
  docile (1.4.0)
14
- json (2.6.3)
15
- json (2.6.3-java)
15
+ json (2.7.1)
16
+ json (2.7.1-java)
16
17
  kalibera (0.1.2)
17
18
  memoist (~> 0.16)
18
19
  rbzip2 (~> 0.3)
19
- language_server-protocol (3.17.0.2)
20
+ language_server-protocol (3.17.0.3)
21
+ lint_roller (1.1.0)
20
22
  memoist (0.16.2)
21
- minitest (5.16.3)
22
- parallel (1.22.1)
23
- parser (3.1.3.0)
23
+ minitest (5.25.4)
24
+ parallel (1.24.0)
25
+ parser (3.2.2.4)
24
26
  ast (~> 2.4.1)
27
+ racc
28
+ racc (1.7.3)
29
+ racc (1.7.3-java)
25
30
  rainbow (3.1.1)
26
31
  rake (13.0.6)
27
32
  rbzip2 (0.3.0)
28
- regexp_parser (2.6.1)
29
- rexml (3.2.5)
30
- rubocop (1.40.0)
33
+ regexp_parser (2.8.3)
34
+ rexml (3.2.6)
35
+ rubocop (1.59.0)
31
36
  json (~> 2.3)
37
+ language_server-protocol (>= 3.17.0)
32
38
  parallel (~> 1.10)
33
- parser (>= 3.1.2.1)
39
+ parser (>= 3.2.2.4)
34
40
  rainbow (>= 2.2.2, < 4.0)
35
41
  regexp_parser (>= 1.8, < 3.0)
36
42
  rexml (>= 3.2.5, < 4.0)
37
- rubocop-ast (>= 1.23.0, < 2.0)
43
+ rubocop-ast (>= 1.30.0, < 2.0)
38
44
  ruby-progressbar (~> 1.7)
39
- unicode-display_width (>= 1.4.0, < 3.0)
40
- rubocop-ast (1.24.1)
41
- parser (>= 3.1.1.0)
42
- rubocop-performance (1.15.1)
43
- rubocop (>= 1.7.0, < 2.0)
44
- rubocop-ast (>= 0.4.0)
45
- ruby-progressbar (1.11.0)
45
+ unicode-display_width (>= 2.4.0, < 3.0)
46
+ rubocop-ast (1.30.0)
47
+ parser (>= 3.2.1.0)
48
+ rubocop-performance (1.20.1)
49
+ rubocop (>= 1.48.1, < 2.0)
50
+ rubocop-ast (>= 1.30.0, < 2.0)
51
+ ruby-progressbar (1.13.0)
46
52
  simplecov (0.22.0)
47
53
  docile (~> 1.1)
48
54
  simplecov-html (~> 0.11)
49
55
  simplecov_json_formatter (~> 0.1)
50
56
  simplecov-html (0.12.3)
51
57
  simplecov_json_formatter (0.1.4)
52
- standard (1.20.0)
58
+ standard (1.33.0)
53
59
  language_server-protocol (~> 3.17.0.2)
54
- rubocop (= 1.40.0)
55
- rubocop-performance (= 1.15.1)
60
+ lint_roller (~> 1.0)
61
+ rubocop (~> 1.59.0)
62
+ standard-custom (~> 1.0.0)
63
+ standard-performance (~> 1.3)
64
+ standard-custom (1.0.2)
65
+ lint_roller (~> 1.0)
66
+ rubocop (~> 1.50)
67
+ standard-performance (1.3.0)
68
+ lint_roller (~> 1.1)
69
+ rubocop-performance (~> 1.20.1)
70
+ stringio (3.1.0)
71
+ stringio (3.1.0-java)
56
72
  timecop (0.9.6)
57
- unicode-display_width (2.3.0)
73
+ unicode-display_width (2.5.0)
58
74
 
59
75
  PLATFORMS
60
76
  ruby
@@ -63,11 +79,11 @@ PLATFORMS
63
79
  DEPENDENCIES
64
80
  benchmark-ips (~> 2.10)
65
81
  kalibera (~> 0.1.2)
66
- minitest (~> 5.0)
82
+ minitest (~> 5.25)
67
83
  rake (~> 13.0)
68
84
  rubcask!
69
85
  simplecov (~> 0.22.0)
70
- standard (~> 1.20)
86
+ standard (~> 1.33)
71
87
  timecop (~> 0.9.6)
72
88
 
73
89
  BUNDLED WITH
@@ -3,7 +3,7 @@
3
3
  require_relative "expirable_entry"
4
4
 
5
5
  module Rubcask
6
- DataEntry = Struct.new(:expire_timestamp, :key, :value) do
6
+ DataEntry = Struct.new(:expire_timestamp, :key, :value, :deleted?) do
7
7
  include ExpirableEntry
8
8
  end
9
9
  end
@@ -12,8 +12,14 @@ module Rubcask
12
12
 
13
13
  attr_reader :write_pos
14
14
 
15
- HEADER_FORMAT = "NQ>nN"
15
+ HEADER_SIZE = 4 + 8 + 2 + 4
16
+
16
17
  HEADER_WITHOUT_CRC_FORMAT = "Q>nN"
18
+ HEADER_FORMAT = "N#{HEADER_WITHOUT_CRC_FORMAT}"
19
+
20
+ EXPIRE_MASK = ~(1 << 63)
21
+ DELETED_MASK = (1 << 63)
22
+ MAX_EXPIRE_VALUE = DELETED_MASK - 1
17
23
 
18
24
  # @param [File] file File with the data
19
25
  # @param [Integer] file_size Current size of `file` in bytes
@@ -92,14 +98,15 @@ module Rubcask
92
98
 
93
99
  key_size = entry.key.bytesize
94
100
  value_size = entry.value.bytesize
95
-
101
+ timestamp_with_deleted = entry.expire_timestamp
102
+ timestamp_with_deleted |= DELETED_MASK if entry.deleted?
96
103
  crc = Zlib.crc32([
97
- entry.expire_timestamp,
104
+ timestamp_with_deleted,
98
105
  key_size,
99
106
  value_size
100
107
  ].pack(HEADER_WITHOUT_CRC_FORMAT) + entry.key + entry.value)
101
108
  @write_pos += @file.write(
102
- [crc, entry.expire_timestamp, key_size, value_size].pack(HEADER_FORMAT),
109
+ [crc, timestamp_with_deleted, key_size, value_size].pack(HEADER_FORMAT),
103
110
  entry.key,
104
111
  entry.value
105
112
  )
@@ -110,16 +117,18 @@ module Rubcask
110
117
  private
111
118
 
112
119
  def read_from_io(io)
113
- header = io.read(18)
120
+ header = io.read(HEADER_SIZE)
114
121
 
115
122
  return nil unless header
116
123
 
117
- crc, expire_timestamp, key_size, value_size = header.unpack(HEADER_FORMAT)
124
+ crc, expire_timestamp_with_deleted, key_size, value_size = header.unpack(HEADER_FORMAT)
118
125
  key = io.read(key_size)
119
126
  value = io.read(value_size)
127
+ expire_timestamp = (expire_timestamp_with_deleted & EXPIRE_MASK)
128
+ deleted = (expire_timestamp_with_deleted & DELETED_MASK) != 0
120
129
 
121
130
  raise ChecksumError, "Checksums do not match" if crc != Zlib.crc32(header[4..] + key + value)
122
- DataEntry.new(expire_timestamp, key, value)
131
+ DataEntry.new(expire_timestamp, key, value, deleted)
123
132
  end
124
133
  end
125
134
  end
@@ -61,6 +61,7 @@ module Rubcask
61
61
  def initialize(dir, config: Config.new)
62
62
  @dir = dir
63
63
  @config = check_config(config)
64
+ @active = nil
64
65
 
65
66
  max_id = 0
66
67
  files = dir_data_files
@@ -107,7 +108,6 @@ module Rubcask
107
108
  # @param [String] value
108
109
  # @param [Integer] ttl Time to live
109
110
  # @return [String] the value provided by the user
110
- # @return [String] the value provided by the user
111
111
  # @raise [ArgumentError] if ttl is negative
112
112
  def set_with_ttl(key, value, ttl)
113
113
  raise ArgumentError, "Negative ttl" if ttl.negative?
@@ -122,8 +122,6 @@ module Rubcask
122
122
  # @return [nil] If no value associated with the key
123
123
  def [](key)
124
124
  key = normalize_key(key)
125
- entry = nil
126
- data_file = nil
127
125
  @lock.with_read_lock do
128
126
  entry = @keydir[key]
129
127
  return nil unless entry
@@ -135,9 +133,9 @@ module Rubcask
135
133
  data_file = @files[entry.file_id]
136
134
 
137
135
  # We are using pread so there's no need to synchronize the read
138
- value = data_file.pread(entry.value_pos, entry.value_size).value
139
- return nil if Tombstone.is_tombstone?(value)
140
- return value
136
+ entry = data_file.pread(entry.value_pos, entry.value_size)
137
+ return nil if entry.deleted?
138
+ return entry.value
141
139
  end
142
140
  end
143
141
 
@@ -199,9 +197,9 @@ module Rubcask
199
197
  @lock.with_read_lock do
200
198
  @keydir.each do |key, entry|
201
199
  file = @files[entry.file_id]
202
- value = file[entry.value_pos, entry.value_size].value
203
- next if Tombstone.is_tombstone?(value)
204
- yield [key, value]
200
+ entry = file[entry.value_pos, entry.value_size]
201
+ next if entry.deleted?
202
+ yield [key, entry.value]
205
203
  end
206
204
  end
207
205
  end
@@ -271,10 +269,11 @@ module Rubcask
271
269
  attr_reader :config, :active, :worker, :logger
272
270
 
273
271
  def put(key, value, expire_timestamp)
272
+ expire_timestamp = expire_timestamp.clamp(NO_EXPIRE_TIMESTAMP, DataFile::MAX_EXPIRE_VALUE)
274
273
  key = normalize_key(key)
275
274
  @lock.with_write_lock do
276
275
  @keydir[key] = active.append(
277
- DataEntry.new(expire_timestamp, key, value)
276
+ DataEntry.new(expire_timestamp, key, value, false)
278
277
  )
279
278
  if active.write_pos >= @config.max_file_size
280
279
  create_new_file!
@@ -286,7 +285,7 @@ module Rubcask
286
285
  # @note This method assumes write lock and normalized key
287
286
  def do_delete(key, prev_file_id)
288
287
  active.append(
289
- DataEntry.new(NO_EXPIRE_TIMESTAMP, key, Tombstone.new_tombstone(active.id, prev_file_id))
288
+ DataEntry.new(NO_EXPIRE_TIMESTAMP, key, Tombstone.new_tombstone(active.id, prev_file_id), true)
290
289
  )
291
290
  @keydir.delete(key)
292
291
  if active.write_pos >= @config.max_file_size
@@ -329,9 +328,10 @@ module Rubcask
329
328
  end
330
329
  end
331
330
 
331
+ merging_paths.each { |_id, path| FileUtils.chmod("+x", path) }
332
+
332
333
  @lock.with_write_lock do
333
334
  close_not_active
334
- merging_paths.each { |_id, path| FileUtils.chmod("+x", path) }
335
335
  reload!
336
336
  end
337
337
  clear_files
@@ -345,8 +345,7 @@ module Rubcask
345
345
  start_pos = pos
346
346
  pos = file.pos
347
347
 
348
- next if entry.expired?
349
- next if Tombstone.is_tombstone?(entry.value)
348
+ next if entry.expired? || entry.deleted?
350
349
 
351
350
  @lock.acquire_read_lock
352
351
  begin
@@ -0,0 +1,58 @@
1
+ require "forwardable"
2
+ require_relative "directory"
3
+
4
+ module Rubcask
5
+ class MarshaledDirectory
6
+ def initialize(directory)
7
+ @directory = directory
8
+ end
9
+
10
+ # Set value associated with given key.
11
+ # @param [Object] key
12
+ # @param [Object] value
13
+ # @return [Object] the value provided by the user
14
+ def []=(key, value)
15
+ @directory[Marshal.dump(key)] = Marshal.dump(value)
16
+ value # rubocop:disable Lint/Void
17
+ end
18
+
19
+ # Set value associated with given key with given ttl
20
+ # @param [Object] key
21
+ # @param [Object] value
22
+ # @param [Integer] ttl Time to live
23
+ # @return [Object] the value provided by the user
24
+ # @raise [ArgumentError] if ttl is negative
25
+ def set_with_ttl(key, value, ttl)
26
+ @directory.set_with_ttl(
27
+ Marshal.dump(key),
28
+ Marshal.dump(value),
29
+ ttl
30
+ )
31
+ value
32
+ end
33
+
34
+ # Gets value associated with the key
35
+ # @param [Object] key
36
+ # @return [Object] value associatiod with the key
37
+ # @return [nil] If no value associated with the key
38
+ def [](key)
39
+ value = @directory[Marshal.dump(key)]
40
+ if value.nil?
41
+ value
42
+ else
43
+ Marshal.load(value)
44
+ end
45
+ end
46
+
47
+ # Remove entry associated with the key.
48
+ # @param [Object] key
49
+ # @return false if the existing value does not exist
50
+ # @return true if the delete was succesfull
51
+ def delete(key)
52
+ @directory.delete(Marshal.dump(key))
53
+ end
54
+
55
+ extend Forwardable
56
+ def_delegators :@directory, *(Directory.public_instance_methods(false) - MarshaledDirectory.public_instance_methods(false))
57
+ end
58
+ end
@@ -59,7 +59,7 @@ module Rubcask
59
59
  # @return [String] Encoded "$1" messege.
60
60
  def generate_cached_message(method)
61
61
  value = encode_message(const_get(method.upcase)).freeze
62
- define_method "#{method}_message" do
62
+ define_method :"#{method}_message" do
63
63
  value
64
64
  end
65
65
  end
@@ -18,25 +18,29 @@ module Rubcask
18
18
 
19
19
  private
20
20
 
21
- def client_loop(conn)
22
- loop do
23
- length = conn.gets(Protocol::SEPARATOR)
21
+ def read_command_args(conn)
22
+ length = conn.gets(SEPARATOR, chomp: true)
24
23
 
25
- break unless length
26
- length = length.to_i
24
+ return nil unless length
25
+ length = length.to_i
27
26
 
28
- command_body = read_command_body(conn, length)
27
+ command_body = read_command_body(conn, length)
29
28
 
30
- break unless command_body
31
- break if command_body.bytesize != length
29
+ return nil unless command_body
30
+ return nil if command_body.bytesize != length
32
31
 
33
- reader = StringIO.new(command_body)
32
+ reader = StringIO.new(command_body)
34
33
 
35
- command = reader.gets(SEPARATOR)
36
- command&.chomp!(SEPARATOR)
34
+ command = reader.gets(SEPARATOR, chomp: true)
37
35
 
38
- args = parse_args(reader)
36
+ args = parse_args(reader)
37
+ [command, args]
38
+ end
39
39
 
40
+ def client_loop(conn)
41
+ while running?
42
+ command_args = read_command_args(conn)
43
+ break unless command_args
40
44
  conn.write(execute_command!(command, args))
41
45
  end
42
46
  end
@@ -82,23 +86,13 @@ module Rubcask
82
86
  end
83
87
 
84
88
  def parse_word(reader)
85
- length = reader.gets(SEPARATOR).to_i
89
+ length = reader.gets(SEPARATOR, chomp: true).to_i
86
90
  return nil if length.zero?
87
91
  reader.read(length)
88
92
  end
89
93
 
90
94
  def read_command_body(conn, length)
91
- command_body = (+"").b
92
- size = 0
93
-
94
- while size < length
95
- val = conn.read([MAX_READ_SIZE, length - size].min)
96
- return nil if val.nil?
97
- size += val.bytesize
98
- command_body << val
99
- end
100
-
101
- command_body
95
+ conn.read(length)
102
96
  end
103
97
 
104
98
  def parse_args(reader)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "async/io"
4
- require "async/io/trap"
4
+ require "async/queue"
5
5
  require "async/io/stream"
6
6
 
7
7
  require_relative "abstract_server"
@@ -18,6 +18,7 @@ module Rubcask
18
18
  @port = config.port
19
19
  @logger = Logger.new($stdout)
20
20
  @endpoint = ::Async::IO::Endpoint.tcp(@hostname, @port)
21
+ @running = false
21
22
  end
22
23
 
23
24
  # Shuts down the server
@@ -25,6 +26,7 @@ module Rubcask
25
26
  def shutdown
26
27
  return unless @task
27
28
  Sync do
29
+ @running = false
28
30
  @shutdown_condition.signal
29
31
  @task.wait
30
32
  end
@@ -33,7 +35,7 @@ module Rubcask
33
35
  # Starts the server
34
36
  # @param [::Async::Condition, nil] on_start_condition The condition will be signalled after a successful bind
35
37
  def start(on_start_condition = nil)
36
- Async do
38
+ Sync do
37
39
  @shutdown_condition = ::Async::Condition.new
38
40
 
39
41
  _, @task = @endpoint.bind do |server, task|
@@ -47,10 +49,13 @@ module Rubcask
47
49
 
48
50
  server.listen(Socket::SOMAXCONN)
49
51
  on_start_condition&.signal
52
+ @running = true
50
53
 
51
54
  server.accept_each do |conn|
52
55
  conn.binmode
53
56
  client_loop(::Async::IO::Stream.new(conn))
57
+ ensure
58
+ ::Async::Task.current.stop
54
59
  end
55
60
  end
56
61
  end
@@ -58,6 +63,27 @@ module Rubcask
58
63
 
59
64
  private
60
65
 
66
+ def running?
67
+ @running
68
+ end
69
+
70
+ def client_loop(conn)
71
+ q = ::Async::Queue.new
72
+ Async do
73
+ @shutdown_condition.wait
74
+ q << nil
75
+ end
76
+ while running?
77
+ Async { q << read_command_args(conn) }
78
+ command_args = q.dequeue
79
+ return unless command_args
80
+
81
+ conn.write(execute_command!(*command_args))
82
+ end
83
+ rescue => error
84
+ Console::Event::Failure.for(error).emit(server, "Error while handling connection")
85
+ end
86
+
61
87
  def define_close_routine(server, task)
62
88
  task.async do |subtask|
63
89
  @shutdown_condition.wait
@@ -66,12 +92,18 @@ module Rubcask
66
92
 
67
93
  server.close
68
94
 
95
+ subtask.with_timeout(30) do
96
+ task.children.each { |t| t.wait unless t == subtask }
97
+ rescue ::Async::TimeoutError
98
+ Console.logger.warn("Could not terminate child connections...")
99
+ end
100
+
69
101
  task.stop
70
102
  end
71
103
  end
72
104
 
73
105
  def read_command_body(conn, length)
74
- conn.read(length) # Async does the looping for us
106
+ conn.read(length)
75
107
  end
76
108
  end
77
109
  end
@@ -113,7 +113,7 @@ module Rubcask
113
113
  end
114
114
 
115
115
  def get_response
116
- length = @socket.gets(Protocol::SEPARATOR)
116
+ length = @socket.gets(SEPARATOR, chomp: true)
117
117
 
118
118
  if length.nil?
119
119
  raise InvalidResponseError, "no response"
@@ -2,8 +2,8 @@
2
2
 
3
3
  require "concurrent/timer_task"
4
4
 
5
- require_relative "./config"
6
- require_relative "./runner/config"
5
+ require_relative "config"
6
+ require_relative "runner/config"
7
7
  require_relative "../config"
8
8
 
9
9
  module Rubcask
@@ -65,6 +65,10 @@ module Rubcask
65
65
  @status = :shutdown
66
66
  cleanup_listeners
67
67
  @threads.list.each(&:kill)
68
+ @threads.list.each do |t|
69
+ t.join
70
+ rescue
71
+ end
68
72
  @status = :stopped
69
73
  @connected = false
70
74
  logger.info "Closed server"
@@ -77,8 +81,12 @@ module Rubcask
77
81
  if @status == :running
78
82
  @status = :shutdown
79
83
  end
80
- @shutdown_pipe[1].write_nonblock("\0")
81
- @shutdown_pipe[1].close
84
+ begin
85
+ @shutdown_pipe[1].write_nonblock("\0")
86
+ @shutdown_pipe[1].close
87
+ rescue
88
+ # We might have race with cleanup shutdown pipe
89
+ end
82
90
  end
83
91
 
84
92
  # Prepares an IO pipe that is used in shutdown process
@@ -93,6 +101,10 @@ module Rubcask
93
101
 
94
102
  attr_reader :logger
95
103
 
104
+ def running?
105
+ @status == :running
106
+ end
107
+
96
108
  def cleanup_listeners
97
109
  @listeners.each do |listener|
98
110
  listener.shutdown
@@ -146,6 +158,16 @@ module Rubcask
146
158
  end
147
159
  end
148
160
 
161
+ def client_loop(conn)
162
+ while running?
163
+ command_args = read_command_args(conn)
164
+ return nil unless command_args
165
+ Thread.handle_interrupt(Exception => :never) do
166
+ conn.write(execute_command!(*command_args))
167
+ end
168
+ end
169
+ end
170
+
149
171
  def client_block(conn)
150
172
  conn.binmode
151
173
  with_interrupt_handle(conn) do |io|
@@ -8,25 +8,16 @@ module Rubcask
8
8
  module Tombstone
9
9
  extend self
10
10
 
11
- PREFIX = "TOMBSTONE".b
12
- PREFIX_SIZE = PREFIX.bytesize
13
- FILE_ID_FORMAT = "N"
14
- FULL_BYTE_SIZE = PREFIX_SIZE + 4
15
-
16
- # @param [String] value value to check
17
- # @return true if value is a tombstone
18
- # @return false otherwise
19
- def is_tombstone?(value)
20
- value.bytesize <= FULL_BYTE_SIZE && value.start_with?(PREFIX)
21
- end
11
+ FILE_ID_FORMAT = "Q>"
12
+ BYTE_SIZE = 8
22
13
 
23
14
  # Creates a new tombstone value
24
15
  # @param [Integer] current_file_id Id of the active file
25
16
  # @param [Integer] prev_file_id Id of the file where the record is currently located
26
17
  # @return [String]
27
18
  def new_tombstone(current_file_id, prev_file_id)
28
- return PREFIX if prev_file_id == current_file_id
29
- PREFIX.b << [prev_file_id].pack(FILE_ID_FORMAT)
19
+ return "" if prev_file_id == current_file_id
20
+ [prev_file_id].pack(FILE_ID_FORMAT)
30
21
  end
31
22
 
32
23
  # Gets file id from tombstone value
@@ -34,7 +25,7 @@ module Rubcask
34
25
  # @return [Integer, nil]
35
26
  def tombstone_file_id(value)
36
27
  return nil if value.bytesize < FULL_BYTE_SIZE
37
- value.byteslice(PREFIX_SIZE, 4).unpack1(FILE_ID_FORMAT)
28
+ value.unpack1(FILE_ID_FORMAT)
38
29
  end
39
30
  end
40
31
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubcask
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/rubcask.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "rubcask/version"
4
4
  require_relative "rubcask/directory"
5
+ require_relative "rubcask/marshaled_directory"
5
6
  require_relative "rubcask/bytes"
6
7
 
7
8
  module Rubcask
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubcask
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcin Henryk Bartkowiak
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2023-02-20 00:00:00.000000000 Z
10
+ date: 2025-01-06 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: concurrent-ruby
@@ -24,6 +23,20 @@ dependencies:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
25
  version: '1.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: stringio
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.1'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.1'
27
40
  description: Bitcask-like Key/Value storege library with a TCP server included
28
41
  email:
29
42
  - mhbartkowiak@gmail.com
@@ -59,6 +72,7 @@ files:
59
72
  - lib/rubcask/hint_file.rb
60
73
  - lib/rubcask/hinted_file.rb
61
74
  - lib/rubcask/keydir_entry.rb
75
+ - lib/rubcask/marshaled_directory.rb
62
76
  - lib/rubcask/merge_directory.rb
63
77
  - lib/rubcask/protocol.rb
64
78
  - lib/rubcask/server/abstract_server.rb
@@ -80,7 +94,6 @@ homepage: https://github.com/mhib/rubcask
80
94
  licenses:
81
95
  - MIT
82
96
  metadata: {}
83
- post_install_message:
84
97
  rdoc_options: []
85
98
  require_paths:
86
99
  - lib
@@ -95,8 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
108
  - !ruby/object:Gem::Version
96
109
  version: '0'
97
110
  requirements: []
98
- rubygems_version: 3.4.1
99
- signing_key:
111
+ rubygems_version: 3.6.2
100
112
  specification_version: 4
101
113
  summary: Key/Value storage library
102
114
  test_files: []