zold 0.17.5 → 0.17.6

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: c06af13af0d59a21c9855dc36c17c13cfe838286f7aadcb1ac3b130ed934d65d
4
- data.tar.gz: 4349ebaee96c42a6224b92a4fc9a398f28d0f7c07352a34fd965d63bbf00b706
3
+ metadata.gz: c7a2934cd243231fd712085b3f87a94d6bb4bf614cc1f5a1e9cada9f7268939d
4
+ data.tar.gz: f92eb91031505dee84d3bdbbc072e3d7384494e4954a5420b7656b8089d425e7
5
5
  SHA512:
6
- metadata.gz: 69b146cdf8bcc399e5213e1c8af4faa2aba937fd93a96ee845a0c9552ffbfe24fcb34d37c724298319aeddfde91ab2b30546f9afcb517232959000893ef40b4b
7
- data.tar.gz: 82943553b346434e975041142b6ac21ffe301bae24695dc509613ef9b8fd8c0a625003f1b060360550b3eab1076f3cd42ad02a7f5de7b7d405f09534f36a3c10
6
+ metadata.gz: 7d974ce11c5bf502b56ce917c19ac1d388d3a28399ddcf84e0fc3860dd2935576965451f26750bcf52744c9b33f411a7e885d6c84cdcdcfbecc1699e008d7e5f
7
+ data.tar.gz: 7bb56302734385c3a20a824b0c9d312ed5d88b02adca2c6c0d9c68554e91d598f26ed29f2019e0435d4622308c5b0e889cade6737773ace52c62701b4fc2570c
@@ -159,7 +159,7 @@ module Zold
159
159
  default: false
160
160
  o.string '--alias',
161
161
  'The alias of the node (default: host:port)'
162
- o.string '--no-spawn',
162
+ o.boolean '--no-spawn',
163
163
  'Don\'t use child processes for the score farm',
164
164
  default: false
165
165
  o.bool '--help', 'Print instructions'
@@ -259,8 +259,7 @@ module Zold
259
259
  network: opts['network']
260
260
  ).start do |entrance|
261
261
  Front.set(:entrance, entrance)
262
- farmer = opts['no-spawn'] ? Farmers::Plain.new : Farmers::Fork.new(log: @log)
263
- Farm.new(invoice, File.join(home, 'farm'), log: @log, farmer: farmer, strength: opts[:strength])
262
+ Farm.new(invoice, File.join(home, 'farm'), log: @log, farmer: farmer(opts), strength: opts[:strength])
264
263
  .start(host, opts[:port], threads: opts[:threads]) do |farm|
265
264
  Front.set(:farm, farm)
266
265
  metronome(farm, opts).start do |metronome|
@@ -276,6 +275,14 @@ module Zold
276
275
 
277
276
  private
278
277
 
278
+ def farmer(opts)
279
+ if opts['no-spawn']
280
+ @log.debug('Plain farmer is used, only one CPU will be utilized')
281
+ return Farmers::Plain.new
282
+ end
283
+ Farmers::Fork.new(log: @log)
284
+ end
285
+
279
286
  # Returns exit code
280
287
  def exec(cmd, nohup_log)
281
288
  start = Time.now
@@ -28,6 +28,7 @@ require_relative '../size'
28
28
  require_relative '../id'
29
29
  require_relative '../endless'
30
30
  require_relative '../dir_items'
31
+ require_relative 'soft_error'
31
32
 
32
33
  # The async entrance of the web front.
33
34
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -49,7 +50,8 @@ module Zold
49
50
  def to_json
50
51
  @entrance.to_json.merge(
51
52
  'queue': @queue.size,
52
- 'threads': @threads.count
53
+ 'threads': @threads.count,
54
+ 'queue_limit': @queue_limit
53
55
  )
54
56
  end
55
57
 
@@ -80,7 +82,12 @@ module Zold
80
82
 
81
83
  # Always returns an array with a single ID of the pushed wallet
82
84
  def push(id, body)
83
- raise "Queue is too long (#{@queue.size} wallets), try again later" if @queue.size > @queue_limit
85
+ if @queue.size > @queue_limit
86
+ raise(
87
+ SoftError,
88
+ "Queue is too long (#{@queue.size} wallets), can't add #{id}/#{Size.new(body.length)}, try again later"
89
+ )
90
+ end
84
91
  start = Time.now
85
92
  loop do
86
93
  uuid = SecureRandom.uuid
@@ -196,33 +196,28 @@ at #{host}:#{port}, strength is #{@strength}")
196
196
  def save(threads, list = [])
197
197
  scores = load + list
198
198
  period = @lifetime / [threads, 1].max
199
- Futex.new(@cache).open do |f|
200
- IO.write(
201
- f,
202
- scores.select(&:valid?)
203
- .reject(&:expired?)
204
- .reject { |s| s.strength < @strength }
205
- .sort_by(&:value)
206
- .reverse
207
- .uniq(&:time)
208
- .uniq { |s| (s.age / period).round }
209
- .map(&:to_s)
210
- .uniq
211
- .join("\n")
212
- )
213
- end
199
+ body = scores.select(&:valid?)
200
+ .reject(&:expired?)
201
+ .reject { |s| s.strength < @strength }
202
+ .sort_by(&:value)
203
+ .reverse
204
+ .uniq(&:time)
205
+ .uniq { |s| (s.age / period).round }
206
+ .map(&:to_s)
207
+ .uniq
208
+ .join("\n")
209
+ Futex.new(@cache).open { |f| IO.write(f, body) }
214
210
  end
215
211
 
216
212
  def load
217
213
  return [] unless File.exist?(@cache)
218
- Futex.new(@cache).open(false) do |f|
219
- IO.read(f).split(/\n/).map do |t|
220
- Score.parse(t)
221
- rescue StandardError => e
222
- @log.error(Backtrace.new(e).to_s)
223
- nil
224
- end.compact
225
- end
214
+ body = Futex.new(@cache).open(false) { |f| IO.read(f) }
215
+ body.split(/\n/).reject(&:empty?).map do |t|
216
+ Score.parse(t)
217
+ rescue StandardError => e
218
+ @log.error(Backtrace.new(e).to_s)
219
+ nil
220
+ end.compact
226
221
  end
227
222
  end
228
223
  end
@@ -61,15 +61,17 @@ module Zold
61
61
 
62
62
  def up(score)
63
63
  start = Time.now
64
- stdin, stdout = IO.pipe
64
+ stdout, stdin = IO.pipe
65
65
  pid = Process.fork do
66
- stdout.puts(score.next)
66
+ stdin.puts(score.next)
67
67
  end
68
68
  at_exit { Farmers.kill(@log, pid, start) }
69
69
  Process.wait
70
- stdout.close
71
- after = Score.parse(stdin.read.strip)
72
70
  stdin.close
71
+ text = stdout.read.strip
72
+ stdout.close
73
+ raise "No score was calculated in the process ##{pid} in #{Age.new(start)}" if text.empty?
74
+ after = Score.parse(text)
73
75
  @log.debug("Next score #{after.value}/#{after.strength} found in proc ##{pid} \
74
76
  for #{after.host}:#{after.port} in #{Age.new(start)}: #{after.suffixes}")
75
77
  after
@@ -38,6 +38,7 @@ require_relative '../log'
38
38
  require_relative '../tax'
39
39
  require_relative '../id'
40
40
  require_relative '../http'
41
+ require_relative 'soft_error'
41
42
 
42
43
  # The web front of the node.
43
44
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -46,6 +47,10 @@ require_relative '../http'
46
47
  module Zold
47
48
  # Web front
48
49
  class Front < Sinatra::Base
50
+ # The minimum score required in order to recongnize a requestor
51
+ # as a valuable node and add it to the list of remotes.
52
+ MIN_SCORE = 16
53
+
49
54
  configure do
50
55
  Thread.current.name = 'sinatra'
51
56
  set :bind, '0.0.0.0'
@@ -76,6 +81,7 @@ module Zold
76
81
  use Rack::Deflater
77
82
 
78
83
  before do
84
+ Thread.current.name = "#{request.request_method}:#{request.url}"
79
85
  Thread.current.thread_variable_set(:uri, request.url)
80
86
  Thread.current.thread_variable_set(:ip, request.ip)
81
87
  @start = Time.now
@@ -101,6 +107,7 @@ while #{settings.address} is in '#{settings.opts['network']}'")
101
107
  s = Score.parse_text(header)
102
108
  error(400, 'The score is invalid') unless s.valid?
103
109
  error(400, 'The score is weak') if s.strength < Score::STRENGTH && !settings.opts['ignore-score-weakness']
110
+ return if s.value < Front::MIN_SCORE && !settings.opts['ignore-score-weakness']
104
111
  if settings.address == "#{s.host}:#{s.port}" && !settings.opts['ignore-score-weakness']
105
112
  error(400, 'Self-requests are prohibited')
106
113
  end
@@ -120,10 +127,11 @@ while #{settings.address} is in '#{settings.opts['network']}'")
120
127
  # Currently there are no tests at all that would verify the headers.
121
128
  after do
122
129
  headers['Cache-Control'] = 'no-cache'
130
+ headers['X-Zold-Path'] = request.url
123
131
  headers['X-Zold-Version'] = settings.opts['expose-version']
124
132
  headers[Http::PROTOCOL_HEADER] = settings.protocol.to_s
125
133
  headers['Access-Control-Allow-Origin'] = '*'
126
- headers[Http::SCORE_HEADER] = score.reduced(16).to_s
134
+ headers[Http::SCORE_HEADER] = score.reduced(Front::MIN_SCORE).to_s
127
135
  headers['X-Zold-Thread'] = Thread.current.object_id.to_s
128
136
  unless @start.nil?
129
137
  if Time.now - @start > 1
@@ -199,7 +207,7 @@ in #{Age.new(@start, limit: 1)}")
199
207
  require 'get_process_mem'
200
208
  mem = GetProcessMem.new.bytes.to_i
201
209
  if mem > 256 * 1024 * 1024 && !settings.opts['skip-oom']
202
- settings.log.error('We are too big in memory, quitting; use --skip-oom to never quit')
210
+ settings.log.error("We are too big in memory (#{Size.new(mem)}), quitting; use --skip-oom to never quit")
203
211
  Front.stop!
204
212
  end
205
213
  mem
@@ -420,8 +428,7 @@ in #{Age.new(@start, limit: 1)}")
420
428
  e = env['sinatra.error']
421
429
  content_type 'text/plain'
422
430
  headers['X-Zold-Error'] = e.message
423
- headers['X-Zold-Path'] = request.url
424
- settings.log.error(Backtrace.new(e).to_s)
431
+ settings.log.error(Backtrace.new(e).to_s) unless e.is_a?(SoftError)
425
432
  Backtrace.new(e).to_s
426
433
  end
427
434
 
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018 Yegor Bugayenko
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 all
13
+ # 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 NONINFINGEMENT. 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 THE
21
+ # SOFTWARE.
22
+
23
+ # Soft error.
24
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
25
+ # Copyright:: Copyright (c) 2018 Yegor Bugayenko
26
+ # License:: MIT
27
+ module Zold
28
+ # Soft error
29
+ module SoftError
30
+ # Nothing special
31
+ end
32
+ end
@@ -104,20 +104,21 @@ module Zold
104
104
 
105
105
  def assert_valid_score(score)
106
106
  raise "Invalid score #{score}" unless score.valid?
107
- raise "Expired score (#{Age.new(score.time)}) #{score}" if score.expired?
107
+ raise "Expired score (#{Age.new(score.time)}) #{score.reduce(4)}" if score.expired?
108
108
  end
109
109
 
110
110
  def assert_score_ownership(score)
111
- raise "Masqueraded host #{@host} as #{score.host}: #{score}" if @host != score.host
112
- raise "Masqueraded port #{@port} as #{score.port}: #{score}" if @port != score.port
111
+ raise "Masqueraded host #{@host} as #{score.host}: #{score.reduce(4)}" if @host != score.host
112
+ raise "Masqueraded port #{@port} as #{score.port}: #{score.reduce(4)}" if @port != score.port
113
113
  end
114
114
 
115
115
  def assert_score_strength(score)
116
- raise "Score #{score.strength} is too weak (<#{Score::STRENGTH}): #{score}" if score.strength < Score::STRENGTH
116
+ return if score.strength >= Score::STRENGTH
117
+ raise "Score #{score.strength} is too weak (<#{Score::STRENGTH}): #{score.reduce(4)}"
117
118
  end
118
119
 
119
120
  def assert_score_value(score, min)
120
- raise "Score #{score.value} is too small (<#{min}): #{score}" if score.value < min
121
+ raise "Score #{score.value} is too small (<#{min}): #{score.reduce(4)}" if score.value < min
121
122
  end
122
123
  end
123
124
 
@@ -25,6 +25,6 @@
25
25
  # Copyright:: Copyright (c) 2018 Yegor Bugayenko
26
26
  # License:: MIT
27
27
  module Zold
28
- VERSION = '0.17.5'
28
+ VERSION = '0.17.6'
29
29
  PROTOCOL = 2
30
30
  end
@@ -266,7 +266,7 @@ class FrontTest < Zold::Test
266
266
 
267
267
  def test_performance
268
268
  times = Queue.new
269
- FakeNode.new(log: test_log).run(['--threads=4', '--strength=6', '--no-metronome', '--farmer=ruby-proc']) do |port|
269
+ FakeNode.new(log: test_log).run(['--threads=4', '--strength=6', '--no-metronome']) do |port|
270
270
  Threads.new(10).assert(100) do
271
271
  start = Time.now
272
272
  Zold::Http.new(uri: URI("http://localhost:#{port}/")).get
@@ -278,6 +278,21 @@ class FrontTest < Zold::Test
278
278
  test_log.info("Average response time is #{all.inject(&:+) / all.count}")
279
279
  end
280
280
 
281
+ # The score exposed via the HTTP header must be reduced to the value of 16.
282
+ # We need this in order to optimize the amount of data we transfer in each
283
+ # HTTP request. This value is enough to identify a valueable node, and filter
284
+ # out those that are too weak.
285
+ def test_score_is_reduced
286
+ FakeNode.new(log: test_log).run(['--threads=1', '--strength=1', '--no-metronome', '--no-spawn']) do |port|
287
+ res = Zold::Http.new(uri: URI("http://localhost:#{port}/")).get
288
+ assert_wait { Zold::Score.parse(res.headers[Zold::Http::SCORE_HEADER]).value > Zold::Front::MIN_SCORE - 1 }
289
+ sleep(1)
290
+ assert_equal_wait(Zold::Front::MIN_SCORE) do
291
+ Zold::Score.parse(res.headers[Zold::Http::SCORE_HEADER]).value
292
+ end
293
+ end
294
+ end
295
+
281
296
  def test_headers_are_being_set_correctly
282
297
  Time.stub :now, Time.at(0) do
283
298
  FakeNode.new(log: test_log).run(['--expose-version=9.9.9', '--no-metronome', '--threads=0']) do |port|
@@ -65,7 +65,7 @@ and suggests a different architecture for digital wallet maintenance.'
65
65
  s.add_runtime_dependency 'concurrent-ruby', '1.1.3'
66
66
  s.add_runtime_dependency 'cucumber', '3.1.2' # has to stay here for Heroku
67
67
  s.add_runtime_dependency 'diffy', '3.2.1'
68
- s.add_runtime_dependency 'futex', '0.4.3'
68
+ s.add_runtime_dependency 'futex', '0.6.1'
69
69
  s.add_runtime_dependency 'get_process_mem', '0.2.3'
70
70
  s.add_runtime_dependency 'json', '2.1.0'
71
71
  s.add_runtime_dependency 'memory_profiler', '0.9.12'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zold
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.5
4
+ version: 0.17.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-12-01 00:00:00.000000000 Z
11
+ date: 2018-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backtrace
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - '='
74
74
  - !ruby/object:Gem::Version
75
- version: 0.4.3
75
+ version: 0.6.1
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - '='
81
81
  - !ruby/object:Gem::Version
82
- version: 0.4.3
82
+ version: 0.6.1
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: get_process_mem
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -603,6 +603,7 @@ files:
603
603
  - lib/zold/node/nodup_entrance.rb
604
604
  - lib/zold/node/nospam_entrance.rb
605
605
  - lib/zold/node/safe_entrance.rb
606
+ - lib/zold/node/soft_error.rb
606
607
  - lib/zold/node/spread_entrance.rb
607
608
  - lib/zold/node/sync_entrance.rb
608
609
  - lib/zold/node/trace.rb
@@ -699,7 +700,7 @@ licenses:
699
700
  - MIT
700
701
  metadata: {}
701
702
  post_install_message: |-
702
- Thanks for installing Zold 0.17.5!
703
+ Thanks for installing Zold 0.17.6!
703
704
  Study our White Paper: https://papers.zold.io/wp.pdf
704
705
  Read our blog posts: https://blog.zold.io
705
706
  Try online wallet at: https://wts.zold.io