zold 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/bin/zold +9 -6
  4. data/features/gem_package.feature +1 -1
  5. data/features/step_definitions/steps.rb +2 -2
  6. data/fixtures/merge/into-no-wallet/assert.rb +1 -1
  7. data/fixtures/merge/random-expenses/assert.rb +1 -1
  8. data/fixtures/merge/simple-case/assert.rb +1 -1
  9. data/lib/zold/age.rb +16 -8
  10. data/lib/zold/amount.rb +4 -2
  11. data/lib/zold/cached_wallets.rb +5 -6
  12. data/lib/zold/commands/clean.rb +15 -8
  13. data/lib/zold/commands/diff.rb +3 -3
  14. data/lib/zold/commands/fetch.rb +4 -4
  15. data/lib/zold/commands/list.rb +5 -4
  16. data/lib/zold/commands/merge.rb +2 -1
  17. data/lib/zold/commands/node.rb +14 -12
  18. data/lib/zold/commands/propagate.rb +2 -2
  19. data/lib/zold/commands/push.rb +3 -3
  20. data/lib/zold/commands/remove.rb +4 -1
  21. data/lib/zold/commands/routines/reconnect.rb +1 -0
  22. data/lib/zold/copies.rb +13 -14
  23. data/lib/zold/dir_items.rb +44 -0
  24. data/lib/zold/head.rb +1 -1
  25. data/lib/zold/key.rb +2 -2
  26. data/lib/zold/log.rb +2 -0
  27. data/lib/zold/node/async_entrance.rb +38 -28
  28. data/lib/zold/node/entrance.rb +4 -11
  29. data/lib/zold/node/farm.rb +9 -9
  30. data/lib/zold/node/front.rb +40 -25
  31. data/lib/zold/node/nodup_entrance.rb +4 -4
  32. data/lib/zold/node/safe_entrance.rb +2 -2
  33. data/lib/zold/node/spread_entrance.rb +5 -9
  34. data/lib/zold/node/sync_entrance.rb +2 -23
  35. data/lib/zold/patch.rb +2 -2
  36. data/lib/zold/remotes.rb +10 -6
  37. data/lib/zold/sync_wallets.rb +3 -22
  38. data/lib/zold/tree_wallets.rb +11 -6
  39. data/lib/zold/txns.rb +1 -1
  40. data/lib/zold/version.rb +1 -1
  41. data/lib/zold/wallet.rb +14 -5
  42. data/lib/zold/wallets.rb +5 -4
  43. data/test/commands/routines/test_spread.rb +1 -1
  44. data/test/commands/test_alias.rb +4 -4
  45. data/test/commands/test_clean.rb +14 -1
  46. data/test/commands/test_create.rb +2 -2
  47. data/test/commands/test_diff.rb +5 -5
  48. data/test/commands/test_fetch.rb +2 -2
  49. data/test/commands/test_merge.rb +19 -19
  50. data/test/commands/test_node.rb +1 -1
  51. data/test/commands/test_pay.rb +7 -6
  52. data/test/commands/test_propagate.rb +2 -1
  53. data/test/commands/test_pull.rb +1 -1
  54. data/test/commands/test_push.rb +1 -1
  55. data/test/commands/test_remove.rb +57 -0
  56. data/test/commands/test_taxes.rb +1 -1
  57. data/test/fake_home.rb +11 -8
  58. data/test/node/fake_node.rb +2 -2
  59. data/test/node/test_async_entrance.rb +24 -8
  60. data/test/node/test_emission.rb +2 -2
  61. data/test/node/test_entrance.rb +8 -6
  62. data/test/node/test_farm.rb +1 -1
  63. data/test/node/test_front.rb +42 -33
  64. data/test/node/test_nodup_entrance.rb +2 -2
  65. data/test/node/test_safe_entrance.rb +5 -5
  66. data/test/node/test_spread_entrance.rb +3 -3
  67. data/test/node/test_sync_entrance.rb +1 -1
  68. data/test/test__helper.rb +3 -29
  69. data/test/test_cached_wallets.rb +1 -1
  70. data/test/test_copies.rb +4 -2
  71. data/test/test_dir_items.rb +88 -0
  72. data/test/test_key.rb +2 -2
  73. data/test/test_log.rb +38 -0
  74. data/test/test_patch.rb +11 -11
  75. data/test/test_prefixes.rb +1 -1
  76. data/test/test_remotes.rb +12 -6
  77. data/test/test_sync_wallets.rb +6 -6
  78. data/test/test_tax.rb +4 -4
  79. data/test/test_tree_wallets.rb +16 -2
  80. data/test/test_wallet.rb +26 -26
  81. data/test/test_wallets.rb +5 -2
  82. data/test/test_zold.rb +2 -2
  83. data/test/upgrades/test_protocol_up.rb +1 -1
  84. data/upgrades/move_wallets_into_tree.rb +1 -1
  85. data/upgrades/protocol_up.rb +3 -3
  86. data/upgrades/rename_foreign_wallets.rb +1 -1
  87. data/zold.gemspec +27 -25
  88. metadata +91 -56
@@ -47,6 +47,7 @@ module Zold
47
47
  score = @farm.best[0]
48
48
  args << "--ignore-node=#{score.host}:#{score.port}" if score
49
49
  cmd.run(args + ['defaults']) unless @opts['routine-immediately']
50
+ return if @opts['routine-immediately'] && @remotes.all.empty?
50
51
  cmd.run(args + ['trim'])
51
52
  cmd.run(args + ['select'])
52
53
  cmd.run(args + ['update'] + (@opts['never-reboot'] ? [] : ['--reboot']))
data/lib/zold/copies.rb CHANGED
@@ -22,10 +22,12 @@
22
22
 
23
23
  require 'time'
24
24
  require 'csv'
25
+ require 'futex'
25
26
  require 'backtrace'
26
27
  require_relative 'log'
27
28
  require_relative 'size'
28
29
  require_relative 'wallet'
30
+ require_relative 'dir_items'
29
31
 
30
32
  # The list of copies.
31
33
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -38,15 +40,12 @@ module Zold
38
40
  EXT = '.zc'
39
41
 
40
42
  def initialize(dir, log: Log::Quiet.new)
41
- raise 'Dir can\'t be nil' if dir.nil?
42
43
  @dir = dir
43
- raise 'Log can\'t be nil' if log.nil?
44
44
  @log = log
45
- @mutex = Mutex.new
46
45
  end
47
46
 
48
47
  def root
49
- File.join(@dir, '..')
48
+ File.expand_path(File.join(@dir, '..'))
50
49
  end
51
50
 
52
51
  def to_s
@@ -54,7 +53,7 @@ module Zold
54
53
  end
55
54
 
56
55
  def clean
57
- @mutex.synchronize do
56
+ Futex.new(file, log: @log).open do
58
57
  list = load
59
58
  list.reject! { |s| s[:time] < Time.now - 24 * 60 * 60 }
60
59
  save(list)
@@ -74,7 +73,7 @@ module Zold
74
73
  wallet.refurbish
75
74
  raise "Invalid protocol #{wallet.protocol} in #{file}" unless wallet.protocol == Zold::PROTOCOL
76
75
  rescue StandardError => e
77
- File.delete(file)
76
+ FileUtils.rm_rf(file)
78
77
  @log.debug("Copy at #{f} deleted: #{Backtrace.new(e)}")
79
78
  deleted += 1
80
79
  end
@@ -84,7 +83,7 @@ module Zold
84
83
  end
85
84
 
86
85
  def remove(host, port)
87
- @mutex.synchronize do
86
+ Futex.new(file, log: @log).open do
88
87
  save(load.reject { |s| s[:host] == host && s[:port] == port })
89
88
  end
90
89
  end
@@ -98,21 +97,21 @@ module Zold
98
97
  raise "Time must be in the past: #{time}" if time > Time.now
99
98
  raise 'Score must be Integer' unless score.is_a?(Integer)
100
99
  raise "Score can't be negative: #{score}" if score.negative?
101
- @mutex.synchronize do
100
+ Futex.new(file, log: @log).open do
102
101
  FileUtils.mkdir_p(@dir)
103
102
  list = load
104
103
  target = list.find do |s|
105
104
  f = File.join(@dir, "#{s[:name]}#{Copies::EXT}")
106
- File.exist?(f) && File.read(f) == content
105
+ File.exist?(f) && IO.read(f) == content
107
106
  end
108
107
  if target.nil?
109
- max = Dir.new(@dir)
108
+ max = DirItems.new(@dir).fetch
110
109
  .select { |f| File.basename(f, Copies::EXT) =~ /^[0-9]+$/ }
111
110
  .map(&:to_i)
112
111
  .max
113
112
  max = 0 if max.nil?
114
113
  name = (max + 1).to_s
115
- File.write(File.join(@dir, "#{name}#{Copies::EXT}"), content)
114
+ IO.write(File.join(@dir, "#{name}#{Copies::EXT}"), content)
116
115
  else
117
116
  name = target[:name]
118
117
  end
@@ -130,7 +129,7 @@ module Zold
130
129
  end
131
130
 
132
131
  def all
133
- @mutex.synchronize do
132
+ Futex.new(file, log: @log).open do
134
133
  load.group_by { |s| s[:name] }.map do |name, scores|
135
134
  {
136
135
  name: name,
@@ -160,7 +159,7 @@ module Zold
160
159
  private
161
160
 
162
161
  def save(list)
163
- File.write(
162
+ IO.write(
164
163
  file,
165
164
  list.map do |r|
166
165
  [
@@ -173,7 +172,7 @@ module Zold
173
172
  end
174
173
 
175
174
  def files
176
- Dir.new(@dir).select { |f| File.basename(f, Copies::EXT) =~ /^[0-9]+$/ }
175
+ DirItems.new(@dir).fetch.select { |f| File.basename(f, Copies::EXT) =~ /^[0-9]+$/ }
177
176
  end
178
177
 
179
178
  def file
@@ -0,0 +1,44 @@
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
+ # Items in a directory.
24
+ #
25
+ # We need this class because Dir.new() from Ruby is blocking. It doesn't
26
+ # allow to write and read to any files in a directory, while listing it.
27
+ # More: https://stackoverflow.com/questions/52987672/
28
+ #
29
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
30
+ # Copyright:: Copyright (c) 2018 Yegor Bugayenko
31
+ # License:: MIT
32
+ module Zold
33
+ # Items in a dir
34
+ class DirItems
35
+ def initialize(dir)
36
+ @dir = dir
37
+ end
38
+
39
+ def fetch(recursive: true)
40
+ txt = `find #{@dir} #{recursive ? '' : '-maxdepth 1'} -type f -print 2>/dev/null`
41
+ txt.nil? ? [] : txt.strip.split(' ').map { |f| f[(@dir.length + 1)..-1] }
42
+ end
43
+ end
44
+ end
data/lib/zold/head.rb CHANGED
@@ -39,7 +39,7 @@ module Zold
39
39
 
40
40
  def fetch
41
41
  raise "Wallet file '#{@file}' is absent" unless File.exist?(@file)
42
- lines = File.read(@file).split(/\n/)
42
+ lines = IO.read(@file).split(/\n/)
43
43
  raise "Not enough lines in #{@file}, just #{lines.count}" if lines.count < 4
44
44
  lines.take(4)
45
45
  end
data/lib/zold/key.rb CHANGED
@@ -37,7 +37,7 @@ module Zold
37
37
  unless file.nil?
38
38
  path = File.expand_path(file)
39
39
  raise "Can't find RSA key at #{file} (#{path})" unless File.exist?(path)
40
- return File.read(path)
40
+ return IO.read(path)
41
41
  end
42
42
  unless text.nil?
43
43
  return text if text.start_with?('-----')
@@ -77,7 +77,7 @@ module Zold
77
77
  text = @body.call.strip
78
78
  unless text.start_with?('-----BEGIN')
79
79
  Tempfile.open do |f|
80
- File.write(f.path, text)
80
+ IO.write(f.path, text)
81
81
  text = `ssh-keygen -f #{f.path} -e -m pem`
82
82
  end
83
83
  end
data/lib/zold/log.rb CHANGED
@@ -54,6 +54,7 @@ module Zold
54
54
  end
55
55
 
56
56
  def debug(msg)
57
+ return unless debug?
57
58
  @mutex.synchronize do
58
59
  @log.debug(msg)
59
60
  end
@@ -64,6 +65,7 @@ module Zold
64
65
  end
65
66
 
66
67
  def info(msg)
68
+ return unless info?
67
69
  @mutex.synchronize do
68
70
  @log.info(msg)
69
71
  end
@@ -21,11 +21,13 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  require 'concurrent'
24
+ require 'futex'
24
25
  require_relative '../log'
25
26
  require_relative '../age'
26
27
  require_relative '../size'
27
28
  require_relative '../id'
28
29
  require_relative '../verbose_thread'
30
+ require_relative '../dir_items'
29
31
 
30
32
  # The async entrance of the web front.
31
33
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -35,29 +37,25 @@ module Zold
35
37
  # The entrance
36
38
  class AsyncEntrance
37
39
  # How many threads to use for processing
38
- THREADS = Concurrent.processor_count * 8
40
+ THREADS = [Concurrent.processor_count, 4].max
39
41
 
40
42
  # Queue length
41
43
  MAX_QUEUE = Concurrent.processor_count * 64
42
44
 
43
45
  def initialize(entrance, dir, log: Log::Quiet.new)
44
- raise 'Entrance can\'t be nil' if entrance.nil?
45
46
  @entrance = entrance
46
- raise 'Directory can\'t be nil' if dir.nil?
47
- raise 'Directory must be of type String' unless dir.is_a?(String)
48
47
  @dir = dir
49
- raise 'Log can\'t be nil' if log.nil?
50
48
  @log = log
51
49
  @mutex = Mutex.new
52
50
  end
53
51
 
54
52
  def to_json
53
+ opts = queue
55
54
  json = {
56
- 'queue': queue.count,
55
+ 'queue': opts.count,
57
56
  'pool.length': @pool.length,
58
57
  'pool.running': @pool.running?
59
58
  }
60
- opts = queue
61
59
  json['queue_age'] = opts.empty? ? 0 : Time.now - File.mtime(File.join(@dir, opts[0]))
62
60
  @entrance.to_json.merge(json)
63
61
  end
@@ -66,7 +64,9 @@ module Zold
66
64
  @entrance.start do
67
65
  FileUtils.mkdir_p(@dir)
68
66
  @pool = Concurrent::FixedThreadPool.new(
69
- AsyncEntrance::THREADS, max_queue: AsyncEntrance::MAX_QUEUE, fallback_policy: :abort
67
+ AsyncEntrance::THREADS,
68
+ max_queue: AsyncEntrance::MAX_QUEUE,
69
+ fallback_policy: :abort
70
70
  )
71
71
  AsyncEntrance::THREADS.times do |t|
72
72
  @pool.post do
@@ -80,14 +80,21 @@ module Zold
80
80
  end
81
81
  begin
82
82
  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
83
90
  ensure
84
91
  @log.info("Stopping async entrance, pool length is #{@pool.length}, queue length is #{@pool.queue_length}")
85
92
  @pool.shutdown
86
93
  if @pool.wait_for_termination(10)
87
- @log.info('Async entrance terminated peacefully')
94
+ @log.info("Async entrance terminated peacefully with #{queue.count} wallets left in the queue")
88
95
  else
89
96
  @pool.kill
90
- @log.info('Async entrance was killed')
97
+ @log.info("Async entrance was killed, #{queue.count} wallets left in the queue")
91
98
  end
92
99
  end
93
100
  end
@@ -96,36 +103,39 @@ module Zold
96
103
  # Always returns an array with a single ID of the pushed wallet
97
104
  def push(id, body)
98
105
  raise "Queue is too long (#{queue.count} wallets), try again later" if queue.count > AsyncEntrance::MAX_QUEUE
99
- @mutex.synchronize do
100
- File.write(File.join(@dir, id.to_s), body)
106
+ start = Time.now
107
+ Futex.new(file(id), log: @log).open do |f|
108
+ IO.write(f, body)
101
109
  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)}")
102
112
  [id]
103
113
  end
104
114
 
105
115
  private
106
116
 
107
117
  def take
108
- id = ''
109
- body = ''
110
- @mutex.synchronize do
111
- opts = queue
112
- unless opts.empty?
113
- file = File.join(@dir, opts[0])
114
- id = opts[0]
115
- body = File.read(file)
116
- File.delete(file)
117
- end
118
- end
119
- return if id.empty? || body.empty?
120
118
  start = Time.now
119
+ opts = queue
120
+ return if opts.empty?
121
+ id = opts[0]
122
+ body = Futex.new(file(id), log: @log).open do |f|
123
+ b = File.exist?(f) ? IO.read(f) : ''
124
+ FileUtils.rm_f(f)
125
+ b
126
+ end
127
+ return if body.empty?
121
128
  @entrance.push(Id.new(id), body)
122
- @log.debug("Pushed #{id}/#{Size.new(body.length)} to #{@entrance.class.name} in #{Age.new(start)}")
129
+ @log.debug("Pushed #{id}/#{Size.new(body.length)} to #{@entrance.class.name} \
130
+ in #{Age.new(start, limit: 0.1)} (#{queue.count} still in the queue)")
123
131
  end
124
132
 
125
133
  def queue
126
- Dir.new(@dir)
127
- .select { |f| f =~ /^[0-9a-f]{16}$/ }
128
- .sort_by { |f| File.mtime(File.join(@dir, f)) }
134
+ DirItems.new(@dir).fetch.select { |f| f =~ /^[0-9a-f]{16}$/ }
135
+ end
136
+
137
+ def file(id)
138
+ File.join(@dir, id.to_s)
129
139
  end
130
140
  end
131
141
  end
@@ -25,6 +25,7 @@ require_relative '../log'
25
25
  require_relative '../remotes'
26
26
  require_relative '../copies'
27
27
  require_relative '../tax'
28
+ require_relative '../age'
28
29
  require_relative '../commands/clean'
29
30
  require_relative '../commands/merge'
30
31
  require_relative '../commands/fetch'
@@ -38,19 +39,11 @@ module Zold
38
39
  # The entrance
39
40
  class Entrance
40
41
  def initialize(wallets, remotes, copies, address, log: Log::Quiet.new, network: 'test')
41
- raise 'Wallets can\'t be nil' if wallets.nil?
42
- raise 'Wallets must implement the contract of Wallets: method #find is required' unless wallets.respond_to?(:find)
43
42
  @wallets = wallets
44
- raise 'Remotes can\'t be nil' if remotes.nil?
45
- raise "Remotes must be of type Remotes: #{remotes.class.name}" unless remotes.is_a?(Remotes)
46
43
  @remotes = remotes
47
- raise 'Copies can\'t be nil' if copies.nil?
48
44
  @copies = copies
49
- raise 'Address can\'t be nil' if address.nil?
50
45
  @address = address
51
- raise 'Log can\'t be nil' if log.nil?
52
46
  @log = log
53
- raise 'Network can\'t be nil' if network.nil?
54
47
  @network = network
55
48
  @history = []
56
49
  @speed = []
@@ -88,12 +81,12 @@ module Zold
88
81
  ).run(['merge', id.to_s])
89
82
  Clean.new(wallets: @wallets, copies: copies.root, log: @log).run(['clean', id.to_s])
90
83
  copies.remove(localhost, Remotes::PORT)
91
- sec = (Time.now - start).round(2)
92
84
  if modified.empty?
93
- @log.info("Accepted #{id} in #{sec}s and not modified anything")
85
+ @log.info("Accepted #{id} in #{Age.new(start, limit: 1)} and not modified anything")
94
86
  else
95
- @log.info("Accepted #{id} in #{sec}s and modified #{modified.join(', ')}")
87
+ @log.info("Accepted #{id} in #{Age.new(start, limit: 1)} and modified #{modified.join(', ')}")
96
88
  end
89
+ sec = (Time.now - start).round(2)
97
90
  @mutex.synchronize do
98
91
  @history.shift if @history.length >= 16
99
92
  @speed.shift if @speed.length >= 64
@@ -23,6 +23,7 @@
23
23
  require 'time'
24
24
  require 'open3'
25
25
  require 'backtrace'
26
+ require 'futex'
26
27
  require_relative '../log'
27
28
  require_relative '../score'
28
29
  require_relative '../age'
@@ -48,7 +49,6 @@ module Zold
48
49
  @invoice = invoice
49
50
  @pipeline = Queue.new
50
51
  @threads = []
51
- @mutex = Mutex.new
52
52
  end
53
53
 
54
54
  def best
@@ -127,14 +127,14 @@ module Zold
127
127
  def finish(thread)
128
128
  start = Time.now
129
129
  @alive = false
130
- @log.info("Attempting to terminate the thread \"#{thread.name}\"...")
130
+ @log.info("Attempting to terminate the thread \"#{thread.name}\" of the farm...")
131
131
  loop do
132
132
  delay = Time.now - start
133
133
  if thread.join(0.1)
134
134
  @log.info("Thread \"#{thread.name}\" finished in #{Age.new(start)}")
135
135
  break
136
136
  end
137
- if delay > 10
137
+ if delay > 1
138
138
  thread.exit
139
139
  @log.error("Thread \"#{thread.name}\" forcefully terminated after #{Age.new(start)}")
140
140
  end
@@ -220,9 +220,9 @@ module Zold
220
220
  def save(threads, list = [])
221
221
  scores = load + list
222
222
  period = 24 * 60 * 60 / [threads, 1].max
223
- @mutex.synchronize do
224
- File.write(
225
- @cache,
223
+ Futex.new(@cache, log: @log).open do |f|
224
+ IO.write(
225
+ f,
226
226
  scores.select(&:valid?)
227
227
  .reject(&:expired?)
228
228
  .sort_by(&:value)
@@ -237,9 +237,9 @@ module Zold
237
237
  end
238
238
 
239
239
  def load
240
- @mutex.synchronize do
241
- if File.exist?(@cache)
242
- File.read(@cache).split(/\n/)
240
+ Futex.new(@cache, log: @log).open do |f|
241
+ if File.exist?(f)
242
+ IO.read(f).split(/\n/)
243
243
  .map { |t| parse_score_line(t) }
244
244
  .reject(&:zero?)
245
245
  else
@@ -55,7 +55,7 @@ module Zold
55
55
  set :suppress_messages, true
56
56
  set :start, Time.now
57
57
  set :lock, false
58
- set :show_exceptions, false
58
+ set :show_exceptions, true
59
59
  set :server, :puma
60
60
  set :log, nil? # to be injected at node.rb
61
61
  set :trace, nil? # to be injected at node.rb
@@ -68,6 +68,7 @@ module Zold
68
68
  set :nohup_log, false # to be injected at node.rb
69
69
  set :home, nil? # to be injected at node.rb
70
70
  set :logging, true # to be injected at node.rb
71
+ set :logger, nil? # to be injected at node.rb
71
72
  set :address, nil? # to be injected at node.rb
72
73
  set :farm, nil? # to be injected at node.rb
73
74
  set :metronome, nil? # to be injected at node.rb
@@ -121,7 +122,7 @@ while #{settings.address} is in '#{settings.network}'"
121
122
  headers[Http::PROTOCOL_HEADER] = settings.protocol.to_s
122
123
  headers['Access-Control-Allow-Origin'] = '*'
123
124
  headers[Http::SCORE_HEADER] = score.reduced(16).to_s
124
- headers['X-Zold-Thread'] = Thread.current.name
125
+ headers['X-Zold-Thread'] = Thread.current.name.to_s
125
126
  headers['X-Zold-Milliseconds'] = ((Time.now - @start) * 1000).round.to_s
126
127
  end
127
128
 
@@ -155,7 +156,7 @@ while #{settings.address} is in '#{settings.network}'"
155
156
  raise "Log not found at #{settings.nohup_log}" unless File.exist?(settings.nohup_log)
156
157
  response.headers['Content-Type'] = 'text/plain'
157
158
  response.headers['Content-Disposition'] = "attachment; filename='#{File.basename(settings.nohup_log)}'"
158
- File.read(settings.nohup_log)
159
+ IO.read(settings.nohup_log)
159
160
  end
160
161
 
161
162
  get '/favicon.ico' do
@@ -177,12 +178,12 @@ while #{settings.address} is in '#{settings.network}'"
177
178
  protocol: settings.protocol,
178
179
  score: score.to_h,
179
180
  pid: Process.pid,
180
- cpus: Concurrent.processor_count,
181
+ cpus: Cachy.cache(:a_cpus) { Concurrent.processor_count },
181
182
  memory: GetProcessMem.new.bytes.to_i,
182
183
  platform: RUBY_PLATFORM,
183
- load: Usagewatch.uw_load.to_f,
184
+ load: Cachy.cache(:a_load, expires_in: 5 * 60) { Usagewatch.uw_load.to_f },
184
185
  threads: "#{Thread.list.select { |t| t.status == 'run' }.count}/#{Thread.list.count}",
185
- wallets: Cachy.cache(:a_key, expires_in: 5 * 60) { settings.wallets.all.count },
186
+ wallets: Cachy.cache(:a_wallets, expires_in: 5 * 60) { settings.wallets.all.count },
186
187
  remotes: settings.remotes.all.count,
187
188
  nscore: settings.remotes.all.map { |r| r[:score] }.inject(&:+) || 0,
188
189
  farm: settings.farm.to_json,
@@ -196,7 +197,7 @@ while #{settings.address} is in '#{settings.network}'"
196
197
  get %r{/wallet/(?<id>[A-Fa-f0-9]{16})} do
197
198
  error 404 if settings.disable_fetch
198
199
  id = Id.new(params[:id])
199
- settings.wallets.find(id) do |wallet|
200
+ copy_of(id) do |wallet|
200
201
  error 404 unless wallet.exists?
201
202
  content_type 'application/json'
202
203
  JSON.pretty_generate(
@@ -219,7 +220,7 @@ while #{settings.address} is in '#{settings.network}'"
219
220
  get %r{/wallet/(?<id>[A-Fa-f0-9]{16}).json} do
220
221
  error 404 if settings.disable_fetch
221
222
  id = Id.new(params[:id])
222
- settings.wallets.find(id) do |wallet|
223
+ copy_of(id) do |wallet|
223
224
  error 404 unless wallet.exists?
224
225
  content_type 'application/json'
225
226
  JSON.pretty_generate(
@@ -241,7 +242,7 @@ while #{settings.address} is in '#{settings.network}'"
241
242
  get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/balance} do
242
243
  error 404 if settings.disable_fetch
243
244
  id = Id.new(params[:id])
244
- settings.wallets.find(id) do |wallet|
245
+ copy_of(id) do |wallet|
245
246
  error 404 unless wallet.exists?
246
247
  content_type 'text/plain'
247
248
  wallet.balance.to_i.to_s
@@ -251,7 +252,7 @@ while #{settings.address} is in '#{settings.network}'"
251
252
  get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/key} do
252
253
  error 404 if settings.disable_fetch
253
254
  id = Id.new(params[:id])
254
- settings.wallets.find(id) do |wallet|
255
+ copy_of(id) do |wallet|
255
256
  error 404 unless wallet.exists?
256
257
  content_type 'text/plain'
257
258
  wallet.key.to_pub
@@ -261,7 +262,7 @@ while #{settings.address} is in '#{settings.network}'"
261
262
  get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/mtime} do
262
263
  error 404 if settings.disable_fetch
263
264
  id = Id.new(params[:id])
264
- settings.wallets.find(id) do |wallet|
265
+ copy_of(id) do |wallet|
265
266
  error 404 unless wallet.exists?
266
267
  content_type 'text/plain'
267
268
  wallet.mtime.utc.iso8601.to_s
@@ -271,7 +272,7 @@ while #{settings.address} is in '#{settings.network}'"
271
272
  get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/digest} do
272
273
  error 404 if settings.disable_fetch
273
274
  id = Id.new(params[:id])
274
- settings.wallets.find(id) do |wallet|
275
+ copy_of(id) do |wallet|
275
276
  error 404 unless wallet.exists?
276
277
  content_type 'text/plain'
277
278
  wallet.digest
@@ -281,7 +282,7 @@ while #{settings.address} is in '#{settings.network}'"
281
282
  get %r{/wallet/(?<id>[A-Fa-f0-9]{16})\.txt} do
282
283
  error 404 if settings.disable_fetch
283
284
  id = Id.new(params[:id])
284
- settings.wallets.find(id) do |wallet|
285
+ copy_of(id) do |wallet|
285
286
  error 404 unless wallet.exists?
286
287
  content_type 'text/plain'
287
288
  [
@@ -305,17 +306,17 @@ while #{settings.address} is in '#{settings.network}'"
305
306
  get %r{/wallet/(?<id>[A-Fa-f0-9]{16})\.bin} do
306
307
  error 404 if settings.disable_fetch
307
308
  id = Id.new(params[:id])
308
- settings.wallets.find(id) do |wallet|
309
+ copy_of(id) do |wallet|
309
310
  error 404 unless wallet.exists?
310
311
  content_type 'text/plain'
311
- File.read(wallet.path)
312
+ IO.read(wallet.path)
312
313
  end
313
314
  end
314
315
 
315
316
  get %r{/wallet/(?<id>[A-Fa-f0-9]{16})/copies} do
316
317
  error 404 if settings.disable_fetch
317
318
  id = Id.new(params[:id])
318
- settings.wallets.find(id) do |wallet|
319
+ copy_of(id) do |wallet|
319
320
  error 404 unless wallet.exists?
320
321
  content_type 'text/plain'
321
322
  copies = Copies.new(File.join(settings.copies, id))
@@ -335,12 +336,12 @@ while #{settings.address} is in '#{settings.network}'"
335
336
  error 404 if settings.disable_fetch
336
337
  id = Id.new(params[:id])
337
338
  name = params[:name]
338
- settings.wallets.find(id) do |wallet|
339
+ copy_of(id) do |wallet|
339
340
  error 404 unless wallet.exists?
340
341
  copy = Copies.new(File.join(settings.copies, id)).all.find { |c| c[:name] == name }
341
342
  error 404 if copy.nil?
342
343
  content_type 'text/plain'
343
- File.read(copy[:path])
344
+ IO.read(copy[:path])
344
345
  end
345
346
  end
346
347
 
@@ -383,12 +384,15 @@ while #{settings.address} is in '#{settings.network}'"
383
384
 
384
385
  get '/threads' do
385
386
  content_type 'text/plain'
386
- Thread.list.map do |t|
387
- [
388
- "#{t.name}: status=#{t.status}; alive=#{t.alive?}",
389
- t.backtrace.nil? ? 'NO BACKTRACE' : " #{t.backtrace.join("\n ")}"
390
- ].join("\n")
391
- end.join("\n\n")
387
+ [
388
+ "Total threads: #{Thread.list.count}",
389
+ Thread.list.map do |t|
390
+ [
391
+ "#{t.name}: status=#{t.status}; alive=#{t.alive?}",
392
+ t.backtrace.nil? ? 'NO BACKTRACE' : " #{t.backtrace.join("\n ")}"
393
+ ].join("\n")
394
+ end
395
+ ].flatten.join("\n\n")
392
396
  end
393
397
 
394
398
  not_found do
@@ -421,9 +425,20 @@ while #{settings.address} is in '#{settings.network}'"
421
425
  end
422
426
 
423
427
  def score
424
- best = settings.farm.best
428
+ best = Cachy.cache(:a_score, expires_in: 60) { settings.farm.best }
425
429
  raise 'Score is empty, there is something wrong with the Farm!' if best.empty?
426
430
  best[0]
427
431
  end
432
+
433
+ def copy_of(id)
434
+ Tempfile.open([id.to_s, Wallet::EXT]) do |f|
435
+ settings.wallets.find(id) do |wallet|
436
+ IO.write(f, IO.read(wallet.path)) if File.exist?(wallet.path)
437
+ end
438
+ path = f.path
439
+ f.delete if File.size(f.path).zero?
440
+ yield Wallet.new(path)
441
+ end
442
+ end
428
443
  end
429
444
  end