zold 0.16.12 → 0.16.13

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: b3c8beba4ea2ef38460a7107b2d1bf9d751f4dac69924ad31a8b87a771585349
4
- data.tar.gz: a9cb982cbb48c63d6755f91251e95a8a72dc5a3dd68bf810001b8ded5994cdf8
3
+ metadata.gz: ec1ca28cf2d44bd430e43965a3a2ebdae2c10c149ed70e2edc38fb43295feb93
4
+ data.tar.gz: ef06fa34ba837fb99be1a935c907cbf1ad5a0a732f733b80e7e710c5d9b9f852
5
5
  SHA512:
6
- metadata.gz: b9d5e6df44283a6d75d49b75c5ecb5abb829059114699b4ddbd2e8629e1f76e306be5a64ce29c2f1fb2bf8f6508e7c21efa158325ee7186833d1689b8613d3c5
7
- data.tar.gz: a6cc7024026e66a280af7e6410d9e10a1ea6bda8e7d1663e85630ad5077fa03b42991672cc69e3d7717f2e5244b56d571f56bcb2bd1c3b1e1da86f0161da137f
6
+ metadata.gz: a6b6aade154501ffab625707cbafcce32790b9c878a7d889a844277c7b8bc291922b8c39924d7772fd10572e5f9a1354ad65bf7cd15ee5c75b384a07f52f2dfc
7
+ data.tar.gz: c927263e4882236b28f58a7b49971b6194c0e852d57b836b0438ff22a3fcac541caea38d815d78b5aa54c5a07021fab8e4b83d5416fc4c946d6efce68d9c4f75
@@ -49,6 +49,7 @@ module Zold
49
49
  private
50
50
 
51
51
  def text(sec)
52
+ return "#{(sec * 1_000_000).round}μs" if sec < 0.001
52
53
  return "#{(sec * 1000).round}ms" if sec < 1
53
54
  return "#{sec.round(2)}s" if sec < 60
54
55
  return "#{(sec / 60).round}m" if sec < 60 * 60
@@ -68,7 +68,7 @@ Available options:"
68
68
  in #{Age.new(start, limit: 0.01)}, #{cps.all.count} left:\n" +
69
69
  cps.all.map do |c|
70
70
  wallet = Wallet.new(c[:path])
71
- " #{c[:name]}: #{c[:score]} #{wallet.memo} \
71
+ " #{c[:name]}: #{c[:score]} #{wallet.mnemo} \
72
72
  #{Size.new(File.size(c[:path]))}/#{Age.new(File.mtime(c[:path]))}"
73
73
  end.join("\n")
74
74
  )
@@ -93,7 +93,7 @@ Available options:"
93
93
  @log.debug("#{cps.all.count} local copies:")
94
94
  cps.all.each do |c|
95
95
  wallet = Wallet.new(c[:path])
96
- @log.debug(" #{c[:name]}: #{c[:score]} #{wallet.memo} \
96
+ @log.debug(" #{c[:name]}: #{c[:score]} #{wallet.mnemo} \
97
97
  #{Size.new(File.size(c[:path]))}/#{Age.new(File.mtime(c[:path]))}")
98
98
  end
99
99
  end
@@ -128,7 +128,7 @@ Available options:"
128
128
  raise "The balance of #{id} is #{wallet.balance} and it's not a root wallet"
129
129
  end
130
130
  copy = cps.add(IO.read(f), score.host, score.port, score.value)
131
- @log.info("#{r} returned #{Size.new(body.length)} #{wallet.memo} \
131
+ @log.info("#{r} returned #{Size.new(body.length)} #{wallet.mnemo} \
132
132
  #{digest(json)}/#{Age.new(json['mtime'])}/#{json['copies']}c \
133
133
  as copy #{copy} of #{id} in #{Age.new(start, limit: 4)}: #{Rainbow(score.value).green} (#{json['version']})")
134
134
  end
@@ -38,7 +38,7 @@ module Zold
38
38
  def run(_ = [])
39
39
  @wallets.all.each do |id|
40
40
  @wallets.find(id) do |wallet|
41
- msg = "#{id}: #{wallet.memo}"
41
+ msg = "#{id}: #{wallet.mnemo}"
42
42
  msg += " (net:#{wallet.network})" if wallet.network != Wallet::MAIN_NETWORK
43
43
  @log.info(msg)
44
44
  end
@@ -88,7 +88,7 @@ Available options:"
88
88
  wallet.flush
89
89
  if modified
90
90
  @log.info("#{cps.count} copies with the total score of #{score} successfully merged \
91
- into #{wallet.memo} in #{Age.new(start, limit: 0.1 + cps.count * 0.01)}")
91
+ into #{wallet.mnemo} in #{Age.new(start, limit: 0.1 + cps.count * 0.01)}")
92
92
  else
93
93
  @log.info("Nothing changed in #{wallet.id} after merge of #{cps.count} copies")
94
94
  end
@@ -79,8 +79,8 @@ module Zold
79
79
  "The strength of the score (default: #{Score::STRENGTH})",
80
80
  default: Score::STRENGTH
81
81
  o.integer '--threads',
82
- "How many threads to use for scores finding (default: #{[Concurrent.processor_count, 4].max})",
83
- default: [Concurrent.processor_count, 4].max
82
+ "How many threads to use for scores finding (default: #{[Concurrent.processor_count / 2, 2].max})",
83
+ default: [Concurrent.processor_count / 2, 2].max
84
84
  o.bool '--dump-errors',
85
85
  'Make HTTP front-end errors visible in the log (false by default)',
86
86
  default: false
@@ -115,7 +115,7 @@ Available options:"
115
115
  @log.debug("Don't forget to do 'zold push #{from}'")
116
116
  @log.info(txn.id)
117
117
  tax = Tax.new(from)
118
- @log.info("The tax debt of #{from.memo} is #{tax.debt} \
118
+ @log.info("The tax debt of #{from.mnemo} is #{tax.debt} \
119
119
  (#{tax.in_debt? ? 'too high' : 'still acceptable'})")
120
120
  txn
121
121
  end
@@ -95,7 +95,7 @@ total score for #{id} is #{total}")
95
95
  response = r.http(uri).put(content)
96
96
  @wallets.find(id) do |wallet|
97
97
  if response.code == '304'
98
- @log.info("#{r}: same version of #{wallet.memo} there, in #{Age.new(start, limit: 0.5)}")
98
+ @log.info("#{r}: same version of #{wallet.mnemo} there, in #{Age.new(start, limit: 0.5)}")
99
99
  return 0
100
100
  end
101
101
  r.assert_code(200, response)
@@ -104,7 +104,7 @@ total score for #{id} is #{total}")
104
104
  r.assert_valid_score(score)
105
105
  r.assert_score_ownership(score)
106
106
  r.assert_score_strength(score) unless opts['ignore-score-weakness']
107
- @log.info("#{r} accepted #{wallet.memo} in #{Age.new(start, limit: 4)}: \
107
+ @log.info("#{r} accepted #{wallet.mnemo} in #{Age.new(start, limit: 4)}: \
108
108
  #{Rainbow(score.value).green} (#{json['version']})")
109
109
  score.value
110
110
  end
@@ -115,7 +115,7 @@ Available options:"
115
115
  raise 'The wallet is absent' unless wallet.exists?
116
116
  tax = Tax.new(wallet)
117
117
  debt = total = tax.debt
118
- @log.info("The current debt of #{wallet.memo} is #{debt} (#{debt.to_i} zents), \
118
+ @log.info("The current debt of #{wallet.mnemo} is #{debt} (#{debt.to_i} zents), \
119
119
  the balance is #{wallet.balance}: #{tax.to_text}")
120
120
  unless tax.in_debt?
121
121
  @log.debug("No need to pay taxes yet, while the debt is less than #{Tax::TRIAL} (#{Tax::TRIAL.to_i} zents)")
@@ -0,0 +1,54 @@
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
+ require_relative 'log'
24
+ require_relative 'verbose_thread'
25
+ require_relative 'age'
26
+
27
+ # Endless loop.
28
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
29
+ # Copyright:: Copyright (c) 2018 Yegor Bugayenko
30
+ # License:: MIT
31
+ module Zold
32
+ # Endless loop
33
+ class Endless
34
+ def initialize(title, log: Log::Quiet.new)
35
+ @title = title
36
+ @log = log
37
+ end
38
+
39
+ def run
40
+ start = Time.now
41
+ Thread.current.name = @title
42
+ Thread.current.abort_on_exception = true
43
+ begin
44
+ loop do
45
+ VerboseThread.new(@log).run(true) do
46
+ yield
47
+ end
48
+ end
49
+ ensure
50
+ @log.debug("Endless loop \"#{@title}\" quit in #{Age.new(start)}")
51
+ end
52
+ end
53
+ end
54
+ end
@@ -22,11 +22,11 @@
22
22
 
23
23
  require 'concurrent'
24
24
  require 'futex'
25
- require_relative '../log'
25
+ require 'securerandom'
26
26
  require_relative '../age'
27
27
  require_relative '../size'
28
28
  require_relative '../id'
29
- require_relative '../verbose_thread'
29
+ require_relative '../endless'
30
30
  require_relative '../dir_items'
31
31
 
32
32
  # The async entrance of the web front.
@@ -36,79 +36,54 @@ require_relative '../dir_items'
36
36
  module Zold
37
37
  # The entrance
38
38
  class AsyncEntrance
39
- # How many threads to use for processing
40
- THREADS = [Concurrent.processor_count, 4].max
41
-
42
- # Queue length
43
- MAX_QUEUE = Concurrent.processor_count * 64
44
-
45
- def initialize(entrance, dir, log: Log::Quiet.new)
39
+ def initialize(entrance, dir, log: Log::Quiet.new, threads: [Concurrent.processor_count, 4].max)
46
40
  @entrance = entrance
47
41
  @dir = dir
48
42
  @log = log
43
+ @total = threads
49
44
  @mutex = Mutex.new
50
45
  end
51
46
 
52
47
  def to_json
53
- opts = queue
54
- json = {
55
- 'queue': opts.count,
56
- 'pool.length': @pool.length,
57
- 'pool.running': @pool.running?
58
- }
59
- json['queue_age'] = opts.empty? ? 0 : Time.now - File.mtime(File.join(@dir, opts[0]))
60
- @entrance.to_json.merge(json)
48
+ @entrance.to_json.merge(
49
+ 'queue': queue.count,
50
+ 'threads': @threads.count
51
+ )
61
52
  end
62
53
 
63
54
  def start
55
+ raise 'Block must be given to start()' unless block_given?
64
56
  @entrance.start do
65
57
  FileUtils.mkdir_p(@dir)
66
- @pool = Concurrent::FixedThreadPool.new(
67
- AsyncEntrance::THREADS,
68
- max_queue: AsyncEntrance::MAX_QUEUE,
69
- fallback_policy: :abort
70
- )
71
- AsyncEntrance::THREADS.times do |t|
72
- @pool.post do
73
- Thread.current.name = "async-e##{t}"
74
- loop do
75
- VerboseThread.new(@log).run(true) { take }
76
- break if @pool.shuttingdown?
77
- sleep(1 + Random.rand(100) / 100)
58
+ @threads = (0..@total - 1).map do |i|
59
+ Thread.start do
60
+ Endless.new("async-e##{i}", log: @log).run do
61
+ take
62
+ sleep(1)
78
63
  end
79
64
  end
80
65
  end
81
66
  begin
82
67
  yield(self)
83
- cycle = 0
84
- until queue.empty?
85
- @log.info("Stopping async entrance, #{queue.count} still in the queue (cycle=#{cycle})...")
86
- cycle += 1
87
- raise "Can't wait for async entrance to stop for so long" if cycle > 10
88
- sleep 1
89
- end
90
68
  ensure
91
- @log.info("Stopping async entrance, pool length is #{@pool.length}, queue length is #{@pool.queue_length}")
92
- @pool.shutdown
93
- if @pool.wait_for_termination(10)
94
- @log.info("Async entrance terminated peacefully with #{queue.count} wallets left in the queue")
95
- else
96
- @pool.kill
97
- @log.info("Async entrance was killed, #{queue.count} wallets left in the queue")
98
- end
69
+ @threads.each(&:kill)
99
70
  end
100
71
  end
101
72
  end
102
73
 
103
74
  # Always returns an array with a single ID of the pushed wallet
104
75
  def push(id, body)
105
- raise "Queue is too long (#{queue.count} wallets), try again later" if queue.count > AsyncEntrance::MAX_QUEUE
76
+ raise "Queue is too long (#{queue.count} wallets), try again later" if queue.count > 256
106
77
  start = Time.now
107
- Futex.new(file(id), log: @log).open do |f|
108
- IO.write(f, body)
78
+ loop do
79
+ uuid = SecureRandom.uuid
80
+ file = File.join(@dir, "#{id}-#{uuid}")
81
+ next if File.exist?(file)
82
+ IO.write(file, body)
83
+ @log.debug("Added #{id}/#{Size.new(body.length)} to the queue at pos.#{queue.count} \
84
+ in #{Age.new(start, limit: 0.05)}: #{uuid}")
85
+ break
109
86
  end
110
- @log.debug("Added #{id}/#{Size.new(body.length)} to the queue at pos.#{queue.count} \
111
- in #{Age.new(start, limit: 0.05)}")
112
87
  [id]
113
88
  end
114
89
 
@@ -116,27 +91,23 @@ in #{Age.new(start, limit: 0.05)}")
116
91
 
117
92
  def take
118
93
  start = Time.now
119
- opts = queue
120
- return if opts.empty?
121
- id = opts[0]
122
- Thread.current.thread_variable_set(:wallet, id.to_s)
123
- body = Futex.new(file(id), log: @log).open do |f|
124
- b = File.exist?(f) ? IO.read(f) : ''
125
- FileUtils.rm_f(f)
126
- b
94
+ id, body = @mutex.synchronize do
95
+ opts = queue
96
+ return if opts.empty?
97
+ file = File.join(@dir, opts[0])
98
+ id = opts[0].split('-')[0]
99
+ Thread.current.thread_variable_set(:wallet, id)
100
+ body = IO.read(file)
101
+ FileUtils.rm_f(file)
102
+ [id, body]
127
103
  end
128
- return if body.empty?
129
104
  @entrance.push(Id.new(id), body)
130
105
  @log.debug("Pushed #{id}/#{Size.new(body.length)} to #{@entrance.class.name} \
131
106
  in #{Age.new(start, limit: 0.1)} (#{queue.count} still in the queue)")
132
107
  end
133
108
 
134
109
  def queue
135
- DirItems.new(@dir).fetch.select { |f| f =~ /^[0-9a-f]{16}$/ }
136
- end
137
-
138
- def file(id)
139
- File.join(@dir, id.to_s)
110
+ DirItems.new(@dir).fetch.select { |f| f =~ /^[0-9a-f]{16}-/ }
140
111
  end
141
112
  end
142
113
  end
@@ -51,6 +51,7 @@ module Zold
51
51
  end
52
52
 
53
53
  def start
54
+ raise 'Block must be given to start()' unless block_given?
54
55
  yield(self)
55
56
  end
56
57
 
@@ -91,7 +92,7 @@ module Zold
91
92
  @history.shift if @history.length >= 16
92
93
  @speed.shift if @speed.length >= 64
93
94
  @wallets.find(id) do |wallet|
94
- @history << "#{id}/#{sec}/#{modified.count}/#{wallet.memo}"
95
+ @history << "#{id}/#{sec}/#{modified.count}/#{wallet.mnemo}"
95
96
  end
96
97
  @speed << sec
97
98
  end
@@ -24,11 +24,12 @@ require 'time'
24
24
  require 'open3'
25
25
  require 'backtrace'
26
26
  require 'futex'
27
+ require 'concurrent'
27
28
  require 'json'
28
29
  require 'zold/score'
29
30
  require_relative '../log'
30
31
  require_relative '../age'
31
- require_relative '../verbose_thread'
32
+ require_relative '../endless'
32
33
  require_relative 'farmers'
33
34
 
34
35
  # The farm of scores.
@@ -96,7 +97,6 @@ module Zold
96
97
  threads: @threads.map do |t|
97
98
  "#{t.name}/#{t.status}/#{t.alive? ? 'alive' : 'dead'}"
98
99
  end.join(', '),
99
- cleanup: @cleanup.status,
100
100
  pipeline: @pipeline.size,
101
101
  best: best.map(&:to_mnemo).join(', ')
102
102
  }
@@ -112,41 +112,36 @@ module Zold
112
112
  #
113
113
  # The farm will stop all its threads and close all resources safely
114
114
  # right after the block provided exists.
115
- def start(host, port, strength: 8, threads: 8)
115
+ def start(host, port, strength: Score::STRENGTH, threads: Concurrent.processor_count)
116
116
  raise 'Block is required for the farm to start' unless block_given?
117
117
  @log.info('Zero-threads farm won\'t score anything!') if threads.zero?
118
118
  if best.empty?
119
- @log.info("No scores found in cache at #{@cache}")
119
+ @log.info("No scores found in the cache at #{@cache}")
120
120
  else
121
121
  @log.info("#{best.size} scores pre-loaded from #{@cache}, the best is: #{best[0]}")
122
122
  end
123
- cleanup(host, port, strength, threads)
124
123
  @threads = (1..threads).map do |t|
124
+ Thread.current.thread_variable_set(:tid, t.to_s)
125
125
  Thread.new do
126
- Thread.current.abort_on_exception = true
127
- Thread.current.name = "f#{t}"
128
- loop do
129
- VerboseThread.new(@log).run do
130
- cycle(host, port, strength, threads)
131
- end
126
+ Endless.new("f#{t}", log: @log).run do
127
+ cycle(host, port, strength, threads)
132
128
  end
133
129
  end
134
130
  end
135
- @cleanup = Thread.new do
136
- Thread.current.abort_on_exception = true
137
- Thread.current.name = 'cleanup'
138
- loop do
139
- VerboseThread.new(@log).run(true) do
140
- cleanup(host, port, strength, threads)
141
- end
131
+ ready = false
132
+ @threads << Thread.new do
133
+ Endless.new('cleanup', log: @log).run do
134
+ cleanup(host, port, strength, threads)
135
+ ready = true
136
+ sleep(1)
142
137
  end
143
138
  end
144
- @log.info("Farm started with #{@threads.count} threads at #{host}:#{port}, strength is #{strength}")
145
- return unless block_given?
139
+ @log.info("Farm started with #{@threads.count} threads (one for cleanup) \
140
+ at #{host}:#{port}, strength is #{strength}")
141
+ loop { break if ready }
146
142
  begin
147
143
  yield(self)
148
144
  ensure
149
- @cleanup.kill
150
145
  @threads.each(&:kill)
151
146
  @log.info("Farm stopped (threads=#{threads}, strength=#{strength})")
152
147
  end
@@ -172,7 +167,7 @@ module Zold
172
167
  begin
173
168
  s << @pipeline.pop(true)
174
169
  rescue ThreadError => _
175
- sleep 0.25
170
+ sleep(0.25)
176
171
  end
177
172
  s.compact!
178
173
  break unless s.empty?
@@ -193,7 +188,7 @@ module Zold
193
188
  def save(threads, list = [])
194
189
  scores = load + list
195
190
  period = @lifetime / [threads, 1].max
196
- Futex.new(@cache, log: @log).open do |f|
191
+ Futex.new(@cache).open do |f|
197
192
  IO.write(
198
193
  f,
199
194
  scores.select(&:valid?)
@@ -210,19 +205,16 @@ module Zold
210
205
  end
211
206
 
212
207
  def load
213
- Futex.new(@cache, log: @log).open do |f|
214
- if File.exist?(f)
215
- IO.read(f).split(/\n/).map do |t|
216
- begin
217
- Score.parse(t)
218
- rescue StandardError => e
219
- @log.error(Backtrace.new(e).to_s)
220
- nil
221
- end
222
- end.compact
223
- else
224
- []
225
- end
208
+ return [] unless File.exist?(@cache)
209
+ Futex.new(@cache).open do |f|
210
+ IO.read(f).split(/\n/).map do |t|
211
+ begin
212
+ Score.parse(t)
213
+ rescue StandardError => e
214
+ @log.error(Backtrace.new(e).to_s)
215
+ nil
216
+ end
217
+ end.compact
226
218
  end
227
219
  end
228
220
  end
@@ -55,6 +55,7 @@ module Zold
55
55
  'ruby',
56
56
  Shellwords.escape(bin),
57
57
  '--skip-upgrades',
58
+ "--info-tid=#{Thread.current.thread_variable_get(:tid)}",
58
59
  "--info-thread=#{Shellwords.escape(Thread.current.name)}",
59
60
  "--info-start=#{Time.now.utc.iso8601}",
60
61
  '--low-priority',
@@ -84,7 +85,7 @@ for #{score.value}/#{score.strength} at #{score.host}:#{score.port}")
84
85
  raise "Failed to calculate the score (##{thr.value}): #{buffer}" unless thr.value.to_i.zero?
85
86
  break
86
87
  end
87
- sleep 0.25
88
+ sleep(10)
88
89
  Thread.current.thread_variable_set(:buffer, buffer.length.to_s)
89
90
  end
90
91
  after = Score.parse(buffer.strip)
@@ -334,7 +334,7 @@ in #{Age.new(@start, limit: 1)}")
334
334
  "\n\n" +
335
335
  copies.all.map do |c|
336
336
  w = Wallet.new(c[:path])
337
- "#{c[:name]}: #{c[:score]} #{w.memo} \
337
+ "#{c[:name]}: #{c[:score]} #{w.mnemo} \
338
338
  #{Size.new(File.size(c[:path]))}/#{Age.new(File.mtime(c[:path]))}"
339
339
  end.join("\n")
340
340
  end
@@ -42,6 +42,7 @@ module Zold
42
42
  end
43
43
 
44
44
  def start
45
+ raise 'Block must be given to start()' unless block_given?
45
46
  @entrance.start { yield(self) }
46
47
  end
47
48
 
@@ -63,14 +64,11 @@ module Zold
63
64
  w.exists? ? IO.read(w.path).to_s : ''
64
65
  end
65
66
  if before == after
66
- @log.info(
67
- "Duplicate of #{Size.new(after.length)} #{wallet.memo} ignored"
68
- )
67
+ @log.info("Duplicate of #{Size.new(after.length)} #{wallet.mnemo} ignored")
69
68
  return []
70
69
  end
71
- @log.info(
72
- "New content for #{id} arrived, #{Size.new(before.length)} before and #{Size.new(after.length)} after"
73
- )
70
+ @log.info("New content for #{wallet.mnemo} arrived, \
71
+ #{Size.new(before.length)} before and #{Size.new(after.length)} after")
74
72
  @entrance.push(id, body)
75
73
  end
76
74
  end
@@ -46,6 +46,7 @@ module Zold
46
46
  end
47
47
 
48
48
  def start
49
+ raise 'Block must be given to start()' unless block_given?
49
50
  @entrance.start { yield(self) }
50
51
  end
51
52
 
@@ -75,7 +76,7 @@ module Zold
75
76
  Emission.new(wallet).check
76
77
  tax = Tax.new(wallet)
77
78
  if tax.in_debt?
78
- raise "Taxes are not paid, can't accept the wallet #{wallet.memo}; the debt is #{tax.debt} \
79
+ raise "Taxes are not paid, can't accept the wallet #{wallet.mnemo}; the debt is #{tax.debt} \
79
80
  (#{tax.debt.to_i} zents); formula ingredients are #{tax.to_text}"
80
81
  end
81
82
  @entrance.push(id, body)
@@ -26,6 +26,7 @@ require_relative 'emission'
26
26
  require_relative '../log'
27
27
  require_relative '../remotes'
28
28
  require_relative '../copies'
29
+ require_relative '../endless'
29
30
  require_relative '../tax'
30
31
  require_relative '../commands/merge'
31
32
  require_relative '../commands/fetch'
@@ -57,26 +58,23 @@ module Zold
57
58
  end
58
59
 
59
60
  def start
61
+ raise 'Block must be given to start()' unless block_given?
60
62
  @entrance.start do
61
63
  @seen = Set.new
62
64
  @modified = Queue.new
63
65
  @push = Thread.start do
64
- Thread.current.abort_on_exception = true
65
- Thread.current.name = 'push'
66
- VerboseThread.new(@log).run(true) do
67
- loop do
68
- id = @modified.pop
69
- if @remotes.all.empty?
70
- @log.info("There are no remotes, won\'t spread #{id}")
71
- else
72
- Thread.current.thread_variable_set(:wallet, id.to_s)
73
- Push.new(wallets: @wallets, remotes: @remotes, log: @log).run(
74
- ['push', "--ignore-node=#{@address}", id.to_s] +
75
- (@ignore_score_weakeness ? ['--ignore-score-weakness'] : [])
76
- )
77
- end
78
- @mutex.synchronize { @seen.delete(id) }
66
+ Endless.new('push', log: @log).run do
67
+ id = @modified.pop
68
+ if @remotes.all.empty?
69
+ @log.info("There are no remotes, won\'t spread #{id}")
70
+ else
71
+ Thread.current.thread_variable_set(:wallet, id.to_s)
72
+ Push.new(wallets: @wallets, remotes: @remotes, log: @log).run(
73
+ ['push', "--ignore-node=#{@address}", id.to_s] +
74
+ (@ignore_score_weakeness ? ['--ignore-score-weakness'] : [])
75
+ )
79
76
  end
77
+ @mutex.synchronize { @seen.delete(id) }
80
78
  end
81
79
  end
82
80
  begin
@@ -45,6 +45,7 @@ module Zold
45
45
  end
46
46
 
47
47
  def start
48
+ raise 'Block must be given to start()' unless block_given?
48
49
  @entrance.start do
49
50
  yield(self)
50
51
  end
@@ -25,6 +25,6 @@
25
25
  # Copyright:: Copyright (c) 2018 Yegor Bugayenko
26
26
  # License:: MIT
27
27
  module Zold
28
- VERSION = '0.16.12'
28
+ VERSION = '0.16.13'
29
29
  PROTOCOL = 2
30
30
  end
@@ -71,7 +71,7 @@ module Zold
71
71
  id.to_s
72
72
  end
73
73
 
74
- def memo
74
+ def mnemo
75
75
  "#{id}/#{balance.to_zld(4)}/#{txns.count}t/#{digest[0, 6]}/#{Size.new(File.size(@file))}"
76
76
  end
77
77
 
@@ -36,7 +36,7 @@ class TestAsyncEntrance < Minitest::Test
36
36
  def test_renders_json
37
37
  FakeHome.new(log: test_log).run do |home|
38
38
  Zold::AsyncEntrance.new(FakeEntrance.new, home.dir, log: test_log).start do |e|
39
- assert_equal(true, e.to_json[:'pool.running'])
39
+ assert_equal(0, e.to_json[:queue])
40
40
  end
41
41
  end
42
42
  end
@@ -66,8 +66,8 @@ class TestAsyncEntrance < Minitest::Test
66
66
  wallet.sub(amount, "NOPREFIX@#{Zold::Id.new}", key)
67
67
  5.times { e.push(wallet.id, IO.read(wallet.path)) }
68
68
  end
69
+ assert_equal_wait(true) { basic.count >= 20 }
69
70
  end
70
- assert_equal_wait(true) { basic.count >= 20 }
71
71
  end
72
72
  end
73
73
 
@@ -59,6 +59,28 @@ class FarmTest < Minitest::Test
59
59
  end
60
60
  end
61
61
 
62
+ # @todo #527:30min This test takes too long. The speed should be less than
63
+ # a few milliseconds, however, if you run it a few times, you will see
64
+ # that it is over 100ms sometimes. This is way too slow. I can't understand
65
+ # what's going on. It seems that IO.read() is taking too long sometimes.
66
+ # Try to measure its time of execution in Farm.load() and you will see
67
+ # that it's usually a few microseconds, but sometimes over 200ms.
68
+ def test_reads_scores_at_high_speed
69
+ Dir.mktmpdir do |dir|
70
+ farm = Zold::Farm.new('NOPREFIX6@ffffffffffffffff', File.join(dir, 'f'), log: test_log)
71
+ farm.start('localhost', 80, threads: 4, strength: 4) do
72
+ assert_wait { !farm.best.empty? && !farm.best[0].value.zero? }
73
+ cycles = 100
74
+ speed = (0..cycles - 1).map do
75
+ start = Time.now
76
+ farm.best
77
+ Time.now - start
78
+ end.inject(&:+) / cycles
79
+ test_log.debug("Average speed is #{(speed * 1000).round(2)}ms in #{cycles} cycles")
80
+ end
81
+ end
82
+ end
83
+
62
84
  def test_makes_best_score_in_background
63
85
  Dir.mktmpdir do |dir|
64
86
  farm = Zold::Farm.new('NOPREFIX1@ffffffffffffffff', File.join(dir, 'f'), log: test_log)
@@ -25,6 +25,7 @@ require 'tmpdir'
25
25
  require 'threads'
26
26
  require_relative 'test__helper'
27
27
  require_relative '../lib/zold/age'
28
+ require_relative '../lib/zold/endless'
28
29
  require_relative '../lib/zold/dir_items'
29
30
 
30
31
  # DirItems test.
@@ -36,7 +37,7 @@ class TestDirItems < Minitest::Test
36
37
  Dir.mktmpdir do |dir|
37
38
  file = File.join(dir, 'hey.txt')
38
39
  back = Thread.start do
39
- loop do
40
+ Endless.new('test-diritems', log: test_log).run do
40
41
  Zold::DirItems.new(dir).fetch
41
42
  end
42
43
  end
@@ -46,7 +46,7 @@ class TestWallet < Minitest::Test
46
46
  def test_generates_memo
47
47
  FakeHome.new(log: test_log).run do |home|
48
48
  wallet = home.create_wallet
49
- assert(!wallet.memo.nil?)
49
+ assert(!wallet.mnemo.nil?)
50
50
  end
51
51
  end
52
52
 
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.16.12
4
+ version: 0.16.13
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-11-04 00:00:00.000000000 Z
11
+ date: 2018-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backtrace
@@ -526,6 +526,7 @@ files:
526
526
  - lib/zold/commands/taxes.rb
527
527
  - lib/zold/copies.rb
528
528
  - lib/zold/dir_items.rb
529
+ - lib/zold/endless.rb
529
530
  - lib/zold/gem.rb
530
531
  - lib/zold/head.rb
531
532
  - lib/zold/hexnum.rb