rubcask 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.standard.yml +3 -0
  3. data/Gemfile +20 -0
  4. data/Gemfile.lock +74 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +111 -0
  7. data/Rakefile +14 -0
  8. data/benchmark/benchmark_io.rb +49 -0
  9. data/benchmark/benchmark_server.rb +10 -0
  10. data/benchmark/benchmark_server_pipeline.rb +24 -0
  11. data/benchmark/benchmark_worker.rb +46 -0
  12. data/benchmark/op_times.rb +32 -0
  13. data/benchmark/profile.rb +15 -0
  14. data/benchmark/server_benchmark_helper.rb +138 -0
  15. data/example/server_runner.rb +15 -0
  16. data/lib/rubcask/bytes.rb +11 -0
  17. data/lib/rubcask/concurrency/fake_atomic_fixnum.rb +34 -0
  18. data/lib/rubcask/concurrency/fake_lock.rb +41 -0
  19. data/lib/rubcask/concurrency/fake_monitor_mixin.rb +21 -0
  20. data/lib/rubcask/config.rb +55 -0
  21. data/lib/rubcask/data_entry.rb +9 -0
  22. data/lib/rubcask/data_file.rb +91 -0
  23. data/lib/rubcask/directory.rb +437 -0
  24. data/lib/rubcask/expirable_entry.rb +9 -0
  25. data/lib/rubcask/hint_entry.rb +9 -0
  26. data/lib/rubcask/hint_file.rb +56 -0
  27. data/lib/rubcask/hinted_file.rb +148 -0
  28. data/lib/rubcask/keydir_entry.rb +9 -0
  29. data/lib/rubcask/merge_directory.rb +75 -0
  30. data/lib/rubcask/protocol.rb +74 -0
  31. data/lib/rubcask/server/abstract_server.rb +113 -0
  32. data/lib/rubcask/server/async.rb +78 -0
  33. data/lib/rubcask/server/client.rb +131 -0
  34. data/lib/rubcask/server/config.rb +31 -0
  35. data/lib/rubcask/server/pipeline.rb +49 -0
  36. data/lib/rubcask/server/runner/config.rb +43 -0
  37. data/lib/rubcask/server/runner.rb +107 -0
  38. data/lib/rubcask/server/threaded.rb +171 -0
  39. data/lib/rubcask/task/clean_directory.rb +19 -0
  40. data/lib/rubcask/tombstone.rb +40 -0
  41. data/lib/rubcask/version.rb +5 -0
  42. data/lib/rubcask/worker/direct_worker.rb +23 -0
  43. data/lib/rubcask/worker/factory.rb +42 -0
  44. data/lib/rubcask/worker/ractor_worker.rb +40 -0
  45. data/lib/rubcask/worker/thread_worker.rb +40 -0
  46. data/lib/rubcask.rb +19 -0
  47. metadata +102 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c7d54e3884c99955ecbab6624997f84c8469d1f60ad73a34631e206efffebbeb
4
+ data.tar.gz: d2c62d450a8ebd9abfc5e12ac26c2747365afbe98ff72fe8117fc8659c02b1d0
5
+ SHA512:
6
+ metadata.gz: 6be6603b4238edd7a93548e6440c4dcee3263afb254b40999b774e7b8e60dc39d021457bd88a4f3cc0017a2812685c48d574aacbc1f35a0ae0eda35c46fad468
7
+ data.tar.gz: 57e451dd32e7e555e6692bbaf441370a90df623cffef4aa52169db0869ad1b99650372e20abed61ca0eefe3f1247ca4e8ae39f06d3a142d2b932713387c787dc
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ ruby_version: 3.2.0
2
+ ignore:
3
+ - 'benchmark/**/*'
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in rubcask.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "minitest", "~> 5.0"
11
+
12
+ gem "standard", "~> 1.20"
13
+
14
+ gem "benchmark-ips", "~> 2.10"
15
+
16
+ gem "kalibera", "~> 0.1.2"
17
+
18
+ gem "timecop", "~> 0.9.6"
19
+
20
+ gem "simplecov", "~> 0.22.0"
data/Gemfile.lock ADDED
@@ -0,0 +1,74 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rubcask (0.1.0)
5
+ concurrent-ruby (~> 1.1)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.2)
11
+ benchmark-ips (2.10.0)
12
+ concurrent-ruby (1.1.10)
13
+ docile (1.4.0)
14
+ json (2.6.3)
15
+ json (2.6.3-java)
16
+ kalibera (0.1.2)
17
+ memoist (~> 0.16)
18
+ rbzip2 (~> 0.3)
19
+ language_server-protocol (3.17.0.2)
20
+ memoist (0.16.2)
21
+ minitest (5.16.3)
22
+ parallel (1.22.1)
23
+ parser (3.1.3.0)
24
+ ast (~> 2.4.1)
25
+ rainbow (3.1.1)
26
+ rake (13.0.6)
27
+ rbzip2 (0.3.0)
28
+ regexp_parser (2.6.1)
29
+ rexml (3.2.5)
30
+ rubocop (1.40.0)
31
+ json (~> 2.3)
32
+ parallel (~> 1.10)
33
+ parser (>= 3.1.2.1)
34
+ rainbow (>= 2.2.2, < 4.0)
35
+ regexp_parser (>= 1.8, < 3.0)
36
+ rexml (>= 3.2.5, < 4.0)
37
+ rubocop-ast (>= 1.23.0, < 2.0)
38
+ 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)
46
+ simplecov (0.22.0)
47
+ docile (~> 1.1)
48
+ simplecov-html (~> 0.11)
49
+ simplecov_json_formatter (~> 0.1)
50
+ simplecov-html (0.12.3)
51
+ simplecov_json_formatter (0.1.4)
52
+ standard (1.20.0)
53
+ language_server-protocol (~> 3.17.0.2)
54
+ rubocop (= 1.40.0)
55
+ rubocop-performance (= 1.15.1)
56
+ timecop (0.9.6)
57
+ unicode-display_width (2.3.0)
58
+
59
+ PLATFORMS
60
+ ruby
61
+ universal-java-17
62
+
63
+ DEPENDENCIES
64
+ benchmark-ips (~> 2.10)
65
+ kalibera (~> 0.1.2)
66
+ minitest (~> 5.0)
67
+ rake (~> 13.0)
68
+ rubcask!
69
+ simplecov (~> 0.22.0)
70
+ standard (~> 1.20)
71
+ timecop (~> 0.9.6)
72
+
73
+ BUNDLED WITH
74
+ 2.3.6
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Marcin Henryk Bartkowiak
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # Rubcask
2
+ Rubcask is a Bitcask-like log-structured Key/Value storage library.
3
+
4
+ It ships with a TCP server and client implementing a custom protocol.
5
+
6
+ It has design very similar to bitcask including merge operation, moving to a next file after reaching a configurable limit; timestamp however is used for expiration only.
7
+
8
+ ## Documentation
9
+ https://rubydoc.info/github/mhib/rubcask/master
10
+
11
+ ## Disclaimer
12
+ This library is not production-ready.
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem 'rubcask'
20
+ ```
21
+
22
+ And then execute:
23
+
24
+ $ bundle install
25
+
26
+ Or install it yourself as:
27
+
28
+ $ gem install rubcask
29
+
30
+ ## Usage
31
+ Rubcask's main methods are very similar to ruby's hash.
32
+
33
+ ```ruby
34
+ dir = Rubcask::Directory.new("path_to_directory")
35
+ dir["key"] = "value"
36
+ dir["key"] # => "value"
37
+ dir.delete("key") # => true
38
+ dir.close
39
+ ```
40
+
41
+ You can also set value with a ttl.
42
+
43
+ ```ruby
44
+ dir = Rubcask::Directory.new("path_to_directory")
45
+ dir.set_with_ttl("key", "value", 10)
46
+ dir["key"] # => "value"
47
+ sleep(11)
48
+ dir["key"] # => nil
49
+ dir.close
50
+ ```
51
+
52
+ Rubcask does not store encoding information and stores keys as bytes, so using utf-8 strings is the same as using ASCII strings.
53
+ The same goes with values.
54
+
55
+ ```ruby
56
+ dir = Rubcask::Directory.new("path_to_directory")
57
+ dir["jeż"] = "3"
58
+ dir["jeż".b] # => 3
59
+ dir.close
60
+ ```
61
+
62
+ Rubcask can be used both as a library and as a server.
63
+
64
+ See `examples/server_runner.rb` for example configuration with a TCP server and a merge worker.
65
+
66
+ See `lib/rubcask/server/client.rb` for server client
67
+
68
+ ## Thread safety
69
+ By default `Rubcask::Directory` is thread safe.
70
+
71
+ Consider installing `concurrent-ruby-ext` for some performance gains.
72
+
73
+ If you do not want to pay performance penalty for synchronization, it is possible to disable thread synchronization in config.
74
+
75
+ ```ruby
76
+ config = Rubcask::Config.configure { |c| c.threadsafe = false }
77
+ dir = Rubcask::Directory.new("path_to_directory", config: config)
78
+ ```
79
+
80
+ It is only safe to do that when using Rubcask as a library; server implementations assumes that Directory is run with `threadsafe = true`.
81
+
82
+ ## Server implementations
83
+ Projects is shipped with threaded server that does not require any dependencies, and with async server that requires `async-io`.
84
+
85
+ They implement the same custom protocol.
86
+
87
+ Generally async server is faster especially for pipelines as it buffers reads.
88
+
89
+ Note that async server might not work on JRuby.
90
+
91
+ ## Supported Ruby implementations
92
+ Tested against supported versions of CRuby and JRuby. TruffleRuby currently does not work due to some `IO` incompatibilities.
93
+
94
+ ## Todo
95
+ * A script in `bin/` for running server runner easily.
96
+ * (maybe) Nice drb support
97
+ * (maybe) Using trie instead of key-dir with ordered iteration support
98
+
99
+ ## Development
100
+
101
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
102
+
103
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
104
+
105
+ ## Contributing
106
+
107
+ Bug reports and pull requests are welcome on GitHub at https://github.com/mhib/rubcask.
108
+
109
+ ## License
110
+
111
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/test_*.rb"]
10
+ end
11
+
12
+ require "standard/rake"
13
+
14
+ task default: %i[test standard]
@@ -0,0 +1,49 @@
1
+ require "benchmark/ips"
2
+ require "securerandom"
3
+
4
+ require_relative "../lib/rubcask"
5
+
6
+ @values = Array.new(10_000) { SecureRandom.hex(128) }
7
+
8
+ Dir.mktmpdir do |path|
9
+ # This does not reflect raw performance well as there is dir creation overhead but works good enough for comparison
10
+ Benchmark.ips do |x|
11
+ x.warmup = 15
12
+ x.time = 30
13
+
14
+ x.stats = :bootstrap
15
+ x.confidence = 95
16
+
17
+ x.report("put_get_os") do
18
+ Dir.mktmpdir do |path|
19
+ dir = Rubcask::Directory.new(path, config: Rubcask::Config.configure { |x| x.io_strategy = :os })
20
+
21
+ (0...10_000).each do |idx|
22
+ dir[idx.to_s] = @values[idx]
23
+ end
24
+ end
25
+ end
26
+
27
+ x.report("put_get_ruby") do
28
+ Dir.mktmpdir do |path|
29
+ dir = Rubcask::Directory.new(path, config: Rubcask::Config.new)
30
+
31
+ (0...10_000).each do |idx|
32
+ dir[idx.to_s] = @values[idx]
33
+ end
34
+ end
35
+ end
36
+
37
+ x.report("put_get_os_sync") do
38
+ Dir.mktmpdir do |path|
39
+ dir = Rubcask::Directory.new(path, config: Rubcask::Config.configure { |x| x.io_strategy = :os_sync })
40
+
41
+ (0...10_000).each do |idx|
42
+ dir[idx.to_s] = @values[idx]
43
+ end
44
+ end
45
+ end
46
+
47
+ x.compare!
48
+ end
49
+ end
@@ -0,0 +1,10 @@
1
+ require_relative "server_benchmark_helper"
2
+ include ServerBenchmarkHelper
3
+
4
+ run_benchmark_with_threaded_server("Threaded") do |client|
5
+ client.get("key")
6
+ end
7
+
8
+ run_benchmark_with_async_server("Async") do |client|
9
+ client.get("key")
10
+ end
@@ -0,0 +1,24 @@
1
+ require_relative "./server_benchmark_helper"
2
+
3
+ include ServerBenchmarkHelper
4
+
5
+ pipeline_block = proc do |client|
6
+ client.pipelined do |pipe|
7
+ pipe.get("key")
8
+ pipe.get("unknown_key")
9
+ pipe.ping
10
+ pipe.get("key")
11
+ end
12
+ end
13
+
14
+ without_pipeline_block = proc do |client|
15
+ client.get("key")
16
+ client.get("unknown_key")
17
+ client.ping
18
+ client.get("key")
19
+ end
20
+
21
+ run_benchmark_with_async_server("Asyc pipelined", &pipeline_block)
22
+ run_benchmark_with_async_server("Async without pipeline", &without_pipeline_block)
23
+ run_benchmark_with_threaded_server("Threaded pipelined", &pipeline_block)
24
+ run_benchmark_with_threaded_server("Threaded without pipeline", &without_pipeline_block)
@@ -0,0 +1,46 @@
1
+ require "benchmark/ips"
2
+
3
+ require "fileutils"
4
+ require "securerandom"
5
+ require "tmpdir"
6
+
7
+ require_relative "../lib/rubcask/worker/factory"
8
+ require_relative "../lib/rubcask/task/clean_directory"
9
+
10
+ class SimulateIOTask
11
+ def call
12
+ 10_000.times { IO.read("/dev/null") }
13
+ sleep(0.01)
14
+ end
15
+ end
16
+
17
+ @benchmark_procedure = lambda do |type, times|
18
+ times.times do
19
+ worker = Rubcask::Worker::Factory.new_worker(type)
20
+ 10.times { worker.push(SimulateIOTask.new) }
21
+ 100_000.times { IO.read("/dev/null") } # Simulate some IO
22
+ worker.close
23
+ end
24
+ end
25
+
26
+ Benchmark.ips do |x|
27
+ x.warmup = 15
28
+ x.time = 30
29
+
30
+ x.stats = :bootstrap
31
+ x.confidence = 95
32
+
33
+ x.report("thread") do |times|
34
+ @benchmark_procedure[:thread, times]
35
+ end
36
+
37
+ x.report("ractor") do |times|
38
+ @benchmark_procedure[:ractor, times]
39
+ end
40
+
41
+ x.report("direct") do |times|
42
+ @benchmark_procedure[:direct, times]
43
+ end
44
+
45
+ x.compare!
46
+ end
@@ -0,0 +1,32 @@
1
+ require "benchmark/ips"
2
+ require "securerandom"
3
+
4
+ require_relative "../lib/rubcask"
5
+
6
+ Benchmark.ips do |x|
7
+ x.time = 30
8
+
9
+ x.report("1 milion writes") do
10
+ Dir.mktmpdir do |path|
11
+ dir = Rubcask::Directory.new(path, config: Rubcask::Config.new)
12
+
13
+ 1_000_000.times do |idx|
14
+ dir[idx.to_s] = SecureRandom.hex(128)
15
+ end
16
+ end
17
+ end
18
+
19
+ x.report("1 milion writes gets") do
20
+ Dir.mktmpdir do |path|
21
+ dir = Rubcask::Directory.new(path, config: Rubcask::Config.new)
22
+
23
+ 1_000_000.times do |idx|
24
+ dir[idx.to_s] = SecureRandom.hex(128)
25
+ end
26
+
27
+ 1_000_000.times do |idx|
28
+ dir[idx.to_s]
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ require "tempfile"
2
+ require "securerandom"
3
+ require "stackprof"
4
+
5
+ require_relative "../lib/rubcask"
6
+
7
+ StackProf.run(out: "tmp/stackprof-cpu-myapp.dump", raw: true) do
8
+ Dir.mktmpdir do |path|
9
+ dir = Rubcask::Directory.new(path, config: Rubcask::Config.new)
10
+
11
+ 1_000_000.times do |idx|
12
+ dir[idx.to_s] = SecureRandom.hex(128)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,138 @@
1
+ require "concurrent"
2
+ require "descriptive_statistics/safe"
3
+
4
+ require "tempfile"
5
+
6
+ require_relative "../lib/rubcask"
7
+ require_relative "../lib/rubcask/server/client"
8
+
9
+ require_relative "../lib/rubcask/server/threaded"
10
+ require_relative "../lib/rubcask/server/async"
11
+
12
+ module ServerBenchmarkHelper
13
+ NUMBER_OF_THREADS = 128
14
+ VALUE = ("8 bytes" * Rubcask::Bytes::KILOBYTE).freeze
15
+
16
+ def run_benchmark(seconds, threads, hostname, port)
17
+ res = Concurrent::Array.new
18
+
19
+ threads.times.map do
20
+ Thread.new do
21
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
22
+ client = Rubcask::Server::Client.new(hostname, port)
23
+ array = []
24
+ while (task_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)) - start < seconds
25
+ task_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
26
+ yield(client)
27
+ array << Process.clock_gettime(Process::CLOCK_MONOTONIC) - task_start
28
+ end
29
+ res.concat(array)
30
+ client.close
31
+ end
32
+ end.map(&:join)
33
+
34
+ res.extend(DescriptiveStatistics)
35
+ {
36
+ "count" => res.size,
37
+ "median" => res.median,
38
+ "mean" => res.mean,
39
+ "standard deviation" => res.standard_deviation,
40
+ "95 percentile" => res.percentile(95),
41
+ "99 percentile" => res.percentile(99),
42
+ "99.99 percentile" => res.percentile(99.99),
43
+ "max" => res.max
44
+ }
45
+ end
46
+
47
+ def run_benchmark_with_async_server(label, &block)
48
+ Dir.mktmpdir do |path|
49
+ dir = Rubcask::Directory.new(path, config: Rubcask::Config::DEFAULT_SERVER_CONFIG)
50
+ dir["key"] = VALUE
51
+
52
+ config = Rubcask::Server::Config.new { |c| c.port = get_free_port }
53
+ server = Rubcask::Server::Async.new(dir, config: config)
54
+ from_thread_q = Thread::Queue.new
55
+ to_thread_q = Thread::Queue.new
56
+ server_thread = Thread.new(server) do |s|
57
+ Sync do
58
+ condition = Async::Condition.new
59
+ Async do
60
+ condition.wait
61
+ from_thread_q << nil
62
+ end
63
+
64
+ s.start(condition)
65
+
66
+ to_thread_q.pop
67
+ server.shutdown
68
+ end
69
+ end
70
+ from_thread_q.pop
71
+ begin
72
+ run_benchmark(10, 10, config.hostname, config.port, &block) # warmup
73
+
74
+ rd, wr = IO.pipe
75
+
76
+ Process.fork do
77
+ val = run_benchmark(10, NUMBER_OF_THREADS, config.hostname, config.port, &block)
78
+ rd.close
79
+ wr.write(val.to_s)
80
+ wr.close
81
+ end
82
+
83
+ wr.close
84
+ puts "#{label}: #{rd.read}"
85
+ rd.close
86
+ ensure
87
+ to_thread_q << nil
88
+ server_thread.join
89
+ dir.close
90
+ end
91
+ end
92
+ end
93
+
94
+ def run_benchmark_with_threaded_server(label, &block)
95
+ Dir.mktmpdir do |path|
96
+ dir = Rubcask::Directory.new(path, config: Rubcask::Config::DEFAULT_SERVER_CONFIG)
97
+ dir["key"] = VALUE
98
+
99
+ config = Rubcask::Server::Config.new { |c| c.port = get_free_port }
100
+ server = Rubcask::Server::Threaded.new(dir, config: config)
101
+ server.setup_shutdown_pipe
102
+ begin
103
+ server.connect
104
+ rescue Errno::EADDRINUSE
105
+ retry
106
+ end
107
+ server_thread = Thread.new(server, &:start)
108
+ begin
109
+ run_benchmark(10, 10, config.hostname, config.port, &block) # warmup
110
+ rd, wr = IO.pipe
111
+
112
+ Process.fork do
113
+ rd.close
114
+ val = run_benchmark(10, NUMBER_OF_THREADS, config.hostname, config.port, &block)
115
+ wr.write(val.to_s)
116
+ wr.close
117
+ end
118
+
119
+ wr.close
120
+ puts "#{label}: #{rd.read}"
121
+ rd.close
122
+ ensure
123
+ server.shutdown
124
+ server_thread.join
125
+ dir.close
126
+ end
127
+ end
128
+ end
129
+
130
+ def get_free_port
131
+ server = TCPServer.new("127.0.0.1", 0)
132
+ begin
133
+ server.addr[1]
134
+ ensure
135
+ server.close
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,15 @@
1
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
2
+
3
+ require "tempfile"
4
+
5
+ require "rubcask"
6
+ require "rubcask/server/runner"
7
+
8
+ Dir.mktmpdir do |tmpdir|
9
+ runner_config = Rubcask::Server::Runner::Config.configure do |c|
10
+ c.directory_path = tmpdir
11
+ end
12
+
13
+ runner = Rubcask::Server::Runner.new(runner_config: runner_config)
14
+ runner.start
15
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubcask
4
+ # Containssize of units of data in bytes
5
+ module Bytes
6
+ BYTE = 1
7
+ KILOBYTE = 1 << 10
8
+ MEGABYTE = 1 << 20
9
+ GIGABYTE = 1 << 30
10
+ end
11
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubcask
4
+ module Concurrency
5
+ # A fake class that implements interface of Concurrent::AtomicFixnum
6
+ # without actually doing any synchronization
7
+ class FakeAtomicFixnum
8
+ attr_accessor :value
9
+ def initialize(initial = 0)
10
+ @value = initial
11
+ end
12
+
13
+ def compare_and_set(expected, update)
14
+ @value = update if @value == expected
15
+ @value
16
+ end
17
+
18
+ def decrement(delta = 1)
19
+ @value -= delta
20
+ @value
21
+ end
22
+
23
+ def increment(delta = 1)
24
+ @value += delta
25
+ @value
26
+ end
27
+
28
+ def update
29
+ @value = yield(@value)
30
+ @value
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubcask
4
+ module Concurrency
5
+ # Fake of Concurrent::ReentrantReadWriteLock
6
+ # It does not do any synchronization
7
+ class FakeLock
8
+ def acquire_read_lock
9
+ true
10
+ end
11
+
12
+ def acquire_write_lock
13
+ true
14
+ end
15
+
16
+ def release_read_lock
17
+ false
18
+ end
19
+
20
+ def release_write_lock
21
+ false
22
+ end
23
+
24
+ def has_waiters?
25
+ false
26
+ end
27
+
28
+ def with_write_lock
29
+ yield
30
+ end
31
+
32
+ def with_read_lock
33
+ yield
34
+ end
35
+
36
+ def write_locked?
37
+ false
38
+ end
39
+ end
40
+ end
41
+ end