zold 0.16.12 → 0.16.13

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: 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