zold 0.11.7 → 0.11.8

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
  SHA1:
3
- metadata.gz: 12b394868e7a954a6a72411f2326339ca57cd6c2
4
- data.tar.gz: e39cfab10878262a6431c4bd2bd5a06a3939a973
3
+ metadata.gz: 73c4792176a13df605a885ea46e4cb92b0df76c6
4
+ data.tar.gz: 6ca3f32b2975db9dcf533aeb746e80e775224427
5
5
  SHA512:
6
- metadata.gz: b414866acdc3a4a099e293c5ec6358d9ad0ffbd223306469e38477736430818d84b5bc7eda44e120f69af5c9aa202e5ebae1989a8b5257f7e8a7ff8e5e701977
7
- data.tar.gz: 6edc6beb6b837bf7f9151cb3f9e652bd737c2c8362d558d0c039a6edc6806f2344bec791bda91fb3544966bd78a8508ebcd0fe7e31b85feccfaabcd2a4633097
6
+ metadata.gz: 6982d8437738d658d3d530c69525a501cc409237d2ae42c8affe8edc046636e83e06ae1cb51d0fbe8cefa0da9ae30e1a2da43a72c19037b721c631cb765fee73
7
+ data.tar.gz: 14a0e10962910847fea075ea48b26063e2baac9ed91e1b46453cee53764ebbc641b773bb0de5d8aeb854881852941bad46a15e208e379b5f266017da98d8ce33
@@ -26,6 +26,7 @@ require_relative 'args'
26
26
  require_relative '../log'
27
27
  require_relative '../patch'
28
28
  require_relative '../wallet'
29
+ require_relative '../atomic_file'
29
30
 
30
31
  # DIFF command.
31
32
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -69,7 +70,7 @@ Available options:"
69
70
  cps[1..-1].each do |c|
70
71
  patch.join(Wallet.new(c[:path]))
71
72
  end
72
- before = File.read(wallet.path)
73
+ before = AtomicFile.new(wallet.path).read
73
74
  after = ''
74
75
  Tempfile.open do |f|
75
76
  patch.save(f, overwrite: true)
@@ -98,9 +98,8 @@ module Zold
98
98
  Front.set(:copies, copies)
99
99
  address = "#{opts[:host]}:#{opts[:port]}".downcase
100
100
  Front.set(:address, address)
101
- Front.set(
102
- :entrance, Entrance.new(wallets, remotes, copies, address, log: @log)
103
- )
101
+ entrance = Entrance.new(wallets, remotes, copies, address, log: @log)
102
+ Front.set(:entrance, entrance)
104
103
  Front.set(:root, Dir.pwd)
105
104
  Front.set(:port, opts['bind-port'])
106
105
  Front.set(:reboot, !opts['never-reboot'])
@@ -26,6 +26,7 @@ require_relative 'args'
26
26
  require_relative '../log'
27
27
  require_relative '../id'
28
28
  require_relative '../http'
29
+ require_relative '../atomic_file'
29
30
 
30
31
  # PUSH command.
31
32
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -77,7 +78,7 @@ Available options:"
77
78
  return 0
78
79
  end
79
80
  start = Time.now
80
- content = File.read(wallet.path)
81
+ content = AtomicFile.new(wallet.path).read
81
82
  response = r.http("/wallet/#{wallet.id}#{opts['sync'] ? '?sync=true' : ''}").put(content)
82
83
  if response.code == '304'
83
84
  @log.info("#{r}: same version of #{wallet.id} there")
@@ -141,7 +141,7 @@ Available options:"
141
141
 
142
142
  def trim(opts)
143
143
  @remotes.all.each do |r|
144
- remove(r[:host], r[:port], opts) if r[:errors] > 20
144
+ remove(r[:host], r[:port], opts) if r[:errors] > Remotes::TOLERANCE
145
145
  end
146
146
  @log.info("The list of remotes trimmed, #{@remotes.all.count} nodes left there")
147
147
  end
data/lib/zold/copies.rb CHANGED
@@ -66,7 +66,7 @@ module Zold
66
66
  list = load
67
67
  target = list.find do |s|
68
68
  f = File.join(@dir, s[:name])
69
- File.exist?(f) && File.read(f) == content
69
+ File.exist?(f) && AtomicFile.new(f).read == content
70
70
  end
71
71
  if target.nil?
72
72
  max = Dir.new(@dir)
data/lib/zold/key.rb CHANGED
@@ -22,6 +22,7 @@ gem 'openssl'
22
22
  require 'openssl'
23
23
  require 'base64'
24
24
  require 'tempfile'
25
+ require_relative 'atomic_file'
25
26
 
26
27
  # The RSA key (either private or public).
27
28
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -35,7 +36,7 @@ module Zold
35
36
  unless file.nil?
36
37
  path = File.expand_path(file)
37
38
  raise "Can't find RSA key at #{file} (#{path})" unless File.exist?(path)
38
- return File.read(path)
39
+ return AtomicFile.new(path).read
39
40
  end
40
41
  unless text.nil?
41
42
  return text if text.start_with?('-----')
@@ -44,7 +44,29 @@ module Zold
44
44
  @address = address
45
45
  @log = log
46
46
  @semaphores = Concurrent::Map.new
47
+ @push_mutex = Mutex.new
48
+ @modified = Set.new
47
49
  @pool = Concurrent::FixedThreadPool.new(16, max_queue: 64, fallback_policy: :abort)
50
+ @pushes = Concurrent::FixedThreadPool.new(1, max_queue: 64, fallback_policy: :abort)
51
+ end
52
+
53
+ def to_json
54
+ {
55
+ 'semaphores': @semaphores.size,
56
+ 'modified': @modified.length,
57
+ 'pool': {
58
+ 'completed_task_count': @pool.completed_task_count,
59
+ 'largest_length': @pool.largest_length,
60
+ 'length': @pool.length,
61
+ 'queue_length': @pool.queue_length
62
+ },
63
+ 'pushes': {
64
+ 'completed_task_count': @pushes.completed_task_count,
65
+ 'largest_length': @pushes.largest_length,
66
+ 'length': @pushes.length,
67
+ 'queue_length': @pushes.queue_length
68
+ }
69
+ }
48
70
  end
49
71
 
50
72
  def push(id, body, sync: true)
@@ -75,6 +97,9 @@ module Zold
75
97
  end
76
98
  end
77
99
 
100
+ private
101
+
102
+ # Returns a list of modifed wallets (as Zold::Id)
78
103
  def push_sync(id, body)
79
104
  @semaphores.put_if_absent(id, Mutex.new)
80
105
  @semaphores.get(id).synchronize do
@@ -85,6 +110,7 @@ module Zold
85
110
  end
86
111
  end
87
112
 
113
+ # Returns a list of modifed wallets (as Zold::Id)
88
114
  def push_unsafe(id, body)
89
115
  copies = Copies.new(File.join(@copies, id.to_s))
90
116
  copies.add(body, '0.0.0.0', Remotes::PORT, 0)
@@ -96,10 +122,19 @@ module Zold
96
122
  ).run(['merge', id.to_s])
97
123
  Clean.new(wallets: @wallets, copies: copies.root, log: @log).run(['clean', id.to_s])
98
124
  copies.remove('remote', Remotes::PORT)
99
- Push.new(
100
- wallets: @wallets, remotes: @remotes, log: @log
101
- ).run(['push', "--ignore-node=#{@address}"] + modified.map(&:to_s))
125
+ @push_mutex.synchronize { @modified += modified }
126
+ @pushes.post { push_one } if @pushes.length < 2
102
127
  modified
103
128
  end
129
+
130
+ def push_one
131
+ @push_mutex.synchronize do
132
+ id = @modified.to_a[0]
133
+ return if id.nil?
134
+ Push.new(
135
+ wallets: @wallets, remotes: @remotes, log: @log
136
+ ).run(['push', "--ignore-node=#{@address}"] + [id.to_s])
137
+ end
138
+ end
104
139
  end
105
140
  end
@@ -111,7 +111,8 @@ module Zold
111
111
 
112
112
  def history(max = 16)
113
113
  if File.exist?(@cache)
114
- File.readlines(@cache)
114
+ AtomicFile.new(@cache).read
115
+ .split(/\n/)
115
116
  .map { |t| Score.parse(t) }
116
117
  .reject(&:expired?)
117
118
  .sort_by(&:value)
@@ -29,6 +29,7 @@ require_relative '../wallet'
29
29
  require_relative '../log'
30
30
  require_relative '../id'
31
31
  require_relative '../http'
32
+ require_relative '../atomic_file'
32
33
 
33
34
  # The web front of the node.
34
35
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -40,7 +41,7 @@ module Zold
40
41
  configure do
41
42
  set :bind, '0.0.0.0'
42
43
  set :suppress_messages, true
43
- set :dump_errors, true
44
+ set :dump_errors, false
44
45
  set :start, Time.now
45
46
  set :lock, false
46
47
  set :show_exceptions, false
@@ -123,6 +124,7 @@ module Zold
123
124
  wallets: settings.wallets.all.count,
124
125
  remotes: settings.remotes.all.count,
125
126
  farm: settings.farm.to_json,
127
+ entrance: settings.entrance.to_json,
126
128
  date: `date --iso-8601=seconds -u`.strip,
127
129
  hours_alive: ((Time.now - settings.start) / (60 * 60)).round(2),
128
130
  home: 'https://www.zold.io'
@@ -137,7 +139,7 @@ module Zold
137
139
  {
138
140
  version: VERSION,
139
141
  score: score.to_h,
140
- body: File.read(wallet.path)
142
+ body: AtomicFile.new(wallet.path).read
141
143
  }.to_json
142
144
  end
143
145
 
@@ -146,7 +148,7 @@ module Zold
146
148
  wallet = settings.wallets.find(id)
147
149
  request.body.rewind
148
150
  body = request.body.read
149
- if wallet.exists? && File.read(wallet.path) == body
151
+ if wallet.exists? && AtomicFile.new(wallet.path).read == body
150
152
  status 304
151
153
  return
152
154
  end
data/lib/zold/patch.rb CHANGED
@@ -20,6 +20,7 @@
20
20
 
21
21
  require_relative 'wallet'
22
22
  require_relative 'signature'
23
+ require_relative 'atomic_file'
23
24
 
24
25
  # Patch.
25
26
  #
@@ -54,11 +55,11 @@ module Zold
54
55
  # Returns TRUE if the file was actually modified
55
56
  def save(file, overwrite: false)
56
57
  before = ''
57
- before = File.read(file) if File.exist?(file)
58
+ before = AtomicFile.new(file).read if File.exist?(file)
58
59
  wallet = Wallet.new(file)
59
60
  wallet.init(@id, @key, overwrite: overwrite, network: @network)
60
61
  @txns.each { |t| wallet.add(t) }
61
- after = File.read(file)
62
+ after = AtomicFile.new(file).read
62
63
  before != after
63
64
  end
64
65
  end
data/lib/zold/remotes.rb CHANGED
@@ -34,6 +34,9 @@ module Zold
34
34
  # The default TCP port all nodes are supposed to use.
35
35
  PORT = 4096
36
36
 
37
+ # At what amount of errors we delete the remote automatically
38
+ TOLERANCE = 50
39
+
37
40
  # Empty, for standalone mode
38
41
  class Empty
39
42
  def all
@@ -138,12 +141,21 @@ module Zold
138
141
  yield Remotes::Remote.new(r[:host], r[:port], score, log: log)
139
142
  rescue StandardError => e
140
143
  error(r[:host], r[:port])
141
- log.info("#{Rainbow("#{r[:host]}:#{r[:port]}").red}: #{e.message}")
144
+ errors = errors(r[:host], r[:port])
145
+ log.info("#{Rainbow("#{r[:host]}:#{r[:port]}").red}: #{e.message}; errors=#{errors}")
142
146
  log.debug(e.backtrace[0..5].join("\n\t"))
147
+ remove(r[:host], r[:port]) if errors > Remotes::TOLERANCE
143
148
  end
144
149
  end
145
150
  end
146
151
 
152
+ def errors(host, port = Remotes::PORT)
153
+ raise 'Port has to be of type Integer' unless port.is_a?(Integer)
154
+ list = load
155
+ raise "#{host}:#{port} is absent among #{list.count} remotes" unless exists?(host, port)
156
+ list.find { |r| r[:host] == host.downcase && r[:port] == port }[:errors]
157
+ end
158
+
147
159
  def error(host, port = Remotes::PORT)
148
160
  raise 'Port has to be of type Integer' unless port.is_a?(Integer)
149
161
  list = load
data/lib/zold/version.rb CHANGED
@@ -23,5 +23,5 @@
23
23
  # Copyright:: Copyright (c) 2018 Yegor Bugayenko
24
24
  # License:: MIT
25
25
  module Zold
26
- VERSION = '0.11.7'.freeze
26
+ VERSION = '0.11.8'.freeze
27
27
  end
data/lib/zold/wallet.rb CHANGED
@@ -161,7 +161,7 @@ module Zold
161
161
 
162
162
  def lines
163
163
  raise "File '#{@file}' is absent" unless File.exist?(@file)
164
- File.readlines(@file)
164
+ AtomicFile.new(@file).read.split(/\n/)
165
165
  end
166
166
  end
167
167
  end
@@ -34,6 +34,14 @@ require_relative '../../lib/zold/commands/pay'
34
34
  # Copyright:: Copyright (c) 2018 Yegor Bugayenko
35
35
  # License:: MIT
36
36
  class TestEntrance < Minitest::Test
37
+ def test_renders_json
38
+ FakeHome.new.run do |home|
39
+ wallet = home.create_wallet(Zold::Id.new)
40
+ entrance = Zold::Entrance.new(home.wallets, home.remotes, home.copies(wallet).root, 'x', log: test_log)
41
+ assert_equal(0, entrance.to_json[:modified])
42
+ end
43
+ end
44
+
37
45
  def test_pushes_wallet
38
46
  sid = Zold::Id.new
39
47
  tid = Zold::Id.new
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zold
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.7
4
+ version: 0.11.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko