rubcask 0.2.0 → 0.3.0

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.
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: []