meeseeker 1.0.0 → 2.0.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +116 -98
- data/Rakefile +121 -44
- data/lib/meeseeker.rb +178 -6
- data/lib/meeseeker/block_follower_job.rb +33 -17
- data/lib/meeseeker/hive_engine.rb +20 -0
- data/lib/meeseeker/steem_engine/agent.rb +33 -19
- data/lib/meeseeker/steem_engine/follower_job.rb +31 -14
- data/lib/meeseeker/version.rb +1 -1
- data/lib/meeseeker/witness_schedule_job.rb +6 -2
- data/meeseeker.gemspec +1 -0
- data/test/meeseeker/meeseeker_test.rb +247 -20
- data/test/test_helper.rb +10 -0
- metadata +29 -9
data/lib/meeseeker.rb
CHANGED
@@ -1,28 +1,200 @@
|
|
1
1
|
require 'redis'
|
2
2
|
require 'steem'
|
3
|
+
require 'hive'
|
3
4
|
|
4
5
|
require 'meeseeker/version'
|
5
6
|
require 'meeseeker/block_follower_job'
|
6
7
|
require 'meeseeker/witness_schedule_job'
|
7
8
|
require 'meeseeker/steem_engine/agent'
|
8
9
|
require 'meeseeker/steem_engine/follower_job'
|
10
|
+
require 'meeseeker/hive_engine'
|
9
11
|
|
10
12
|
module Meeseeker
|
11
|
-
|
12
|
-
|
13
|
+
STEEM_CHAIN_ID = '0000000000000000000000000000000000000000000000000000000000000000'
|
14
|
+
HIVE_LEGACY_CHAIN_ID = '0000000000000000000000000000000000000000000000000000000000000000'
|
15
|
+
HIVE_CHAIN_ID = 'beeab0de00000000000000000000000000000000000000000000000000000000'
|
16
|
+
STEEM_CHAIN_KEY_PREFIX = 'steem'
|
17
|
+
HIVE_CHAIN_KEY_PREFIX = 'hive'
|
18
|
+
STEEM_ENGINE_CHAIN_KEY_PREFIX = 'steem_engine'
|
19
|
+
HIVE_ENGINE_CHAIN_KEY_PREFIX = 'hive_engine'
|
20
|
+
LAST_BLOCK_NUM_KEY_SUFFIX = ':meeseeker:last_block_num'
|
21
|
+
LAST_STEEM_ENGINE_BLOCK_NUM_KEY_SUFFIX = ':meeseeker:last_block_num'
|
13
22
|
BLOCKS_PER_DAY = 28800
|
14
23
|
VIRTUAL_TRX_ID = '0000000000000000000000000000000000000000'
|
24
|
+
BLOCK_INTERVAL = 3
|
25
|
+
SHUFFLE_URL = 'shuffle'
|
26
|
+
DEFAULT_STEEM_URL = 'https://api.steemit.com'
|
27
|
+
DEFAULT_STEEM_FAILOVER_URLS = [
|
28
|
+
DEFAULT_STEEM_URL,
|
29
|
+
# 'https://steemd.minnowsupportproject.org',
|
30
|
+
# 'https://anyx.io',
|
31
|
+
# 'http://anyx.io',
|
32
|
+
# 'https://steemd.privex.io',
|
33
|
+
# 'https://api.steem.house'
|
34
|
+
]
|
35
|
+
DEFAULT_HIVE_URL = 'https://api.openhive.network'
|
36
|
+
DEFAULT_HIVE_FAILOVER_URLS = [
|
37
|
+
DEFAULT_HIVE_URL,
|
38
|
+
'https://api.hivekings.com',
|
39
|
+
'https://anyx.io',
|
40
|
+
'http://anyx.io',
|
41
|
+
'https://techcoderx.com',
|
42
|
+
'https://rpc.esteem.app',
|
43
|
+
'https://hived.privex.io',
|
44
|
+
'https://api.pharesim.me',
|
45
|
+
'https://api.hive.blog',
|
46
|
+
'https://rpc.ausbit.dev'
|
47
|
+
]
|
48
|
+
|
49
|
+
def default_chain_key_prefix
|
50
|
+
ENV.fetch('MEESEEKER_CHAIN_KEY_PREFIX', chain_key_prefix)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.chain_key_prefix
|
54
|
+
@chain_key_prefix ||= {}
|
55
|
+
url = default_url(HIVE_CHAIN_KEY_PREFIX)
|
56
|
+
|
57
|
+
return @chain_key_prefix[url] if !!@chain_key_prefix[url]
|
58
|
+
|
59
|
+
# Just use the Hive API for either chain, until we know which one we're
|
60
|
+
# using.
|
61
|
+
api = Hive::DatabaseApi.new(url: url)
|
62
|
+
|
63
|
+
api.get_config do |config|
|
64
|
+
@chain_key_prefix[node_url] = if !!config.HIVE_CHAIN_ID && config.HIVE_CHAIN_ID == HIVE_CHAIN_ID
|
65
|
+
HIVE_CHAIN_KEY_PREFIX
|
66
|
+
elsif !!config.HIVE_CHAIN_ID && config.HIVE_CHAIN_ID == HIVE_LEGACY_CHAIN_ID
|
67
|
+
HIVE_CHAIN_KEY_PREFIX
|
68
|
+
elsif !!config.STEEM_CHAIN_ID && config.STEEM_CHAIN_ID == STEEM_CHAIN_ID
|
69
|
+
STEEM_CHAIN_KEY_PREFIX
|
70
|
+
else
|
71
|
+
config.keys.find{|k| k.end_with? '_CHAIN_ID'}.split('_').first.downcase.tap do |guess|
|
72
|
+
warn "Guessing chain_key_prefix = '#{guess}' for unknown chain on: #{node_url}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.default_url(chain = default_chain_key_prefix)
|
79
|
+
ENV.fetch('MEESEEKER_NODE_URL') do
|
80
|
+
case chain.to_s
|
81
|
+
when STEEM_CHAIN_KEY_PREFIX then DEFAULT_STEEM_URL
|
82
|
+
when HIVE_CHAIN_KEY_PREFIX then DEFAULT_HIVE_URL
|
83
|
+
else
|
84
|
+
raise "Unknown chain: #{chain}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
@problem_node_urls = []
|
90
|
+
|
15
91
|
@redis = Redis.new(url: ENV.fetch('MEESEEKER_REDIS_URL', 'redis://127.0.0.1:6379/0'))
|
16
|
-
@node_url = ENV.fetch('
|
92
|
+
@node_url = default_url(ENV.fetch('MEESEEKER_CHAIN_KEY_PREFIX', HIVE_CHAIN_KEY_PREFIX))
|
17
93
|
@steem_engine_node_url = ENV.fetch('MEESEEKER_STEEM_ENGINE_NODE_URL', 'https://api.steem-engine.com/rpc')
|
94
|
+
@hive_engine_node_url = ENV.fetch('MEESEEKER_HIVE_ENGINE_NODE_URL', 'https://api.hive-engine.com/rpc')
|
18
95
|
@stream_mode = ENV.fetch('MEESEEKER_STREAM_MODE', 'head').downcase.to_sym
|
19
96
|
@include_virtual = ENV.fetch('MEESEEKER_INCLUDE_VIRTUAL', 'true').downcase == 'true'
|
20
97
|
@include_block_header = ENV.fetch('MEESEEKER_INCLUDE_BLOCK_HEADER', 'true').downcase == 'true'
|
21
98
|
@publish_op_custom_id = ENV.fetch('MEESEEKER_PUBLISH_OP_CUSTOM_ID', 'false').downcase == 'true'
|
22
|
-
@expire_keys = ENV.fetch('MEESEEKER_EXPIRE_KEYS', BLOCKS_PER_DAY *
|
99
|
+
@expire_keys = ENV.fetch('MEESEEKER_EXPIRE_KEYS', BLOCKS_PER_DAY * BLOCK_INTERVAL).to_i
|
100
|
+
@max_keys = ENV.fetch('MEESEEKER_MAX_KEYS', '-1').to_i
|
23
101
|
|
24
102
|
extend self
|
25
103
|
|
26
|
-
attr_accessor :redis, :node_url, :steem_engine_node_url,
|
27
|
-
:
|
104
|
+
attr_accessor :redis, :node_url, :steem_engine_node_url,
|
105
|
+
:hive_engine_node_url, :expire_keys, :max_keys, :stream_mode,
|
106
|
+
:include_virtual, :include_block_header, :publish_op_custom_id
|
107
|
+
|
108
|
+
def self.shuffle_node_url(chain = ENV.fetch('MEESEEKER_CHAIN_KEY_PREFIX', HIVE_CHAIN_KEY_PREFIX))
|
109
|
+
chain = chain.to_s
|
110
|
+
node_url = ENV.fetch('MEESEEKER_NODE_URL', default_url(ENV.fetch('MEESEEKER_CHAIN_KEY_PREFIX', chain)))
|
111
|
+
return node_url unless node_url == SHUFFLE_URL
|
112
|
+
|
113
|
+
@problem_node_urls = [] if rand(1..1000) == 13
|
114
|
+
shuffle_node_url!(chain)
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.api_class(chain = default_chain_key_prefix)
|
118
|
+
case chain.to_s
|
119
|
+
when STEEM_CHAIN_KEY_PREFIX then Steem::Api
|
120
|
+
when HIVE_CHAIN_KEY_PREFIX then Hive::Api
|
121
|
+
else
|
122
|
+
raise "Unknown chain: #{chain}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.condenser_api_class(chain = default_chain_key_prefix)
|
127
|
+
case chain.to_s
|
128
|
+
when STEEM_CHAIN_KEY_PREFIX then Steem::CondenserApi
|
129
|
+
when HIVE_CHAIN_KEY_PREFIX then Hive::CondenserApi
|
130
|
+
else
|
131
|
+
raise "Unknown chain: #{chain}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.block_api_class(chain = default_chain_key_prefix)
|
136
|
+
case chain.to_s
|
137
|
+
when STEEM_CHAIN_KEY_PREFIX then Steem::BlockApi
|
138
|
+
when HIVE_CHAIN_KEY_PREFIX then Hive::BlockApi
|
139
|
+
else
|
140
|
+
raise "Unknown chain: #{chain}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.database_api_class(chain = default_chain_key_prefix)
|
145
|
+
case chain.to_s
|
146
|
+
when STEEM_CHAIN_KEY_PREFIX then Steem::DatabaseApi
|
147
|
+
when HIVE_CHAIN_KEY_PREFIX then Hive::DatabaseApi
|
148
|
+
else
|
149
|
+
raise "Unknown chain: #{chain}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.stream_class(chain = default_chain_key_prefix)
|
154
|
+
case chain.to_s
|
155
|
+
when STEEM_CHAIN_KEY_PREFIX then Steem::Stream
|
156
|
+
when HIVE_CHAIN_KEY_PREFIX then Hive::Stream
|
157
|
+
else
|
158
|
+
raise "Unknown chain: #{chain}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.shuffle_node_url!(chain = ENV.fetch('MEESEEKER_CHAIN_KEY_PREFIX', HIVE_CHAIN_KEY_PREFIX))
|
163
|
+
chain = chain.to_s
|
164
|
+
failover_urls = case chain
|
165
|
+
when STEEM_CHAIN_KEY_PREFIX then DEFAULT_STEEM_FAILOVER_URLS - @problem_node_urls
|
166
|
+
when HIVE_CHAIN_KEY_PREFIX then DEFAULT_HIVE_FAILOVER_URLS - @problem_node_urls
|
167
|
+
else; []
|
168
|
+
end
|
169
|
+
url = failover_urls.sample
|
170
|
+
api = api_class(chain).new(url: url)
|
171
|
+
|
172
|
+
api.get_accounts(['fullnodeupdate']) do |accounts|
|
173
|
+
fullnodeupdate = accounts.first
|
174
|
+
metadata = (JSON[fullnodeupdate.json_metadata] rescue nil) || {}
|
175
|
+
|
176
|
+
nodes = metadata.fetch('report', []).map do |report|
|
177
|
+
next if chain == HIVE_CHAIN_KEY_PREFIX && !report[HIVE_CHAIN_KEY_PREFIX]
|
178
|
+
next if chain != HIVE_CHAIN_KEY_PREFIX && !!report[HIVE_CHAIN_KEY_PREFIX]
|
179
|
+
|
180
|
+
report['node']
|
181
|
+
end.compact.uniq
|
182
|
+
|
183
|
+
nodes -= @problem_node_urls
|
184
|
+
|
185
|
+
if nodes.any?
|
186
|
+
nodes.sample
|
187
|
+
else
|
188
|
+
@node_url = failover_urls.sample
|
189
|
+
end
|
190
|
+
end
|
191
|
+
rescue => e
|
192
|
+
puts "#{url}: #{e}"
|
193
|
+
|
194
|
+
@problem_node_urls << url
|
195
|
+
failover_urls -= @problem_node_urls
|
196
|
+
failover_urls.sample
|
197
|
+
end
|
198
|
+
|
199
|
+
shuffle_node_url! if @node_url == SHUFFLE_URL
|
28
200
|
end
|
@@ -3,16 +3,20 @@ module Meeseeker
|
|
3
3
|
MAX_VOP_RETRY = 3
|
4
4
|
|
5
5
|
def perform(options = {})
|
6
|
-
|
6
|
+
chain = (options[:chain] || 'hive').to_sym
|
7
|
+
url = Meeseeker.default_url(chain)
|
8
|
+
block_api = Meeseeker.block_api_class(chain).new(url: url)
|
7
9
|
redis = Meeseeker.redis
|
8
10
|
last_key_prefix = nil
|
9
11
|
trx_index = 0
|
10
12
|
current_block_num = nil
|
11
13
|
block_transactions = []
|
14
|
+
chain_key_prefix = chain.to_s if !!options[:chain]
|
15
|
+
chain_key_prefix ||= Meeseeker.default_chain_key_prefix
|
12
16
|
|
13
17
|
stream_operations(options) do |op, trx_id, block_num|
|
14
18
|
begin
|
15
|
-
current_key_prefix = "
|
19
|
+
current_key_prefix = "#{chain_key_prefix}:#{block_num}:#{trx_id}"
|
16
20
|
|
17
21
|
if current_key_prefix == last_key_prefix
|
18
22
|
trx_index += 1
|
@@ -26,9 +30,9 @@ module Meeseeker
|
|
26
30
|
}
|
27
31
|
|
28
32
|
block_transactions << trx_id unless trx_id == VIRTUAL_TRX_ID
|
29
|
-
redis.publish(
|
33
|
+
redis.publish("#{chain_key_prefix}:transaction", transaction_payload.to_json)
|
30
34
|
end
|
31
|
-
last_key_prefix = "
|
35
|
+
last_key_prefix = "#{chain_key_prefix}:#{block_num}:#{trx_id}"
|
32
36
|
trx_index = 0
|
33
37
|
end
|
34
38
|
|
@@ -42,6 +46,12 @@ module Meeseeker
|
|
42
46
|
puts key
|
43
47
|
end
|
44
48
|
|
49
|
+
unless Meeseeker.max_keys == -1
|
50
|
+
while redis.keys("#{chain_key_prefix}:*").size > Meeseeker.max_keys
|
51
|
+
sleep Meeseeker::BLOCK_INTERVAL
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
45
55
|
redis.set(key, op.to_json)
|
46
56
|
redis.expire(key, Meeseeker.expire_keys) unless Meeseeker.expire_keys == -1
|
47
57
|
|
@@ -56,7 +66,7 @@ module Meeseeker
|
|
56
66
|
block_api.get_block_header(block_num: block_num) do |result|
|
57
67
|
if result.nil? || result.header.nil?
|
58
68
|
puts "Node returned empty result for block_header on block_num: #{block_num} (rate limiting?). Retrying ..."
|
59
|
-
sleep
|
69
|
+
sleep Meeseeker::BLOCK_INTERVAL
|
60
70
|
throw :block_header
|
61
71
|
end
|
62
72
|
|
@@ -65,19 +75,19 @@ module Meeseeker
|
|
65
75
|
end
|
66
76
|
end
|
67
77
|
|
68
|
-
redis.set(
|
69
|
-
redis.publish(
|
78
|
+
redis.set(chain_key_prefix + LAST_BLOCK_NUM_KEY_SUFFIX, block_num)
|
79
|
+
redis.publish("#{chain_key_prefix}:block", block_payload.to_json)
|
70
80
|
current_block_num = block_num
|
71
81
|
end
|
72
82
|
|
73
|
-
redis.publish("
|
83
|
+
redis.publish("#{chain_key_prefix}:op:#{op_type}", {key: key}.to_json)
|
74
84
|
|
75
85
|
if Meeseeker.publish_op_custom_id
|
76
86
|
if %w(custom custom_binary custom_json).include? op_type
|
77
87
|
id = (op["value"]["id"] rescue nil).to_s
|
78
88
|
|
79
89
|
if id.size > 0
|
80
|
-
redis.publish("
|
90
|
+
redis.publish("#{chain_key_prefix}:op:#{op_type}:#{id}", {key: key}.to_json)
|
81
91
|
end
|
82
92
|
end
|
83
93
|
end
|
@@ -85,7 +95,10 @@ module Meeseeker
|
|
85
95
|
end
|
86
96
|
private
|
87
97
|
def stream_operations(options = {}, &block)
|
98
|
+
chain = (options[:chain] || 'hive').to_sym
|
88
99
|
redis = Meeseeker.redis
|
100
|
+
chain_key_prefix = chain.to_s if !!options[:chain]
|
101
|
+
chain_key_prefix ||= Meeseeker.chain_key_prefix
|
89
102
|
last_block_num = nil
|
90
103
|
mode = options.delete(:mode) || Meeseeker.stream_mode
|
91
104
|
options[:include_virtual] ||= Meeseeker.include_virtual
|
@@ -93,8 +106,9 @@ module Meeseeker
|
|
93
106
|
if !!options[:at_block_num]
|
94
107
|
last_block_num = options[:at_block_num].to_i
|
95
108
|
else
|
96
|
-
|
97
|
-
|
109
|
+
url = Meeseeker.default_url(chain)
|
110
|
+
database_api = Meeseeker.database_api_class(chain).new(url: url)
|
111
|
+
last_block_num = redis.get(chain_key_prefix + LAST_BLOCK_NUM_KEY_SUFFIX).to_i + 1
|
98
112
|
|
99
113
|
block_num = catch :dynamic_global_properties do
|
100
114
|
database_api.get_dynamic_global_properties do |dgpo|
|
@@ -125,11 +139,12 @@ module Meeseeker
|
|
125
139
|
end
|
126
140
|
|
127
141
|
begin
|
128
|
-
|
142
|
+
url = Meeseeker.default_url(chain)
|
143
|
+
stream_options = {url: url, mode: mode}
|
129
144
|
options = options.merge(at_block_num: last_block_num)
|
130
145
|
condenser_api = nil
|
131
146
|
|
132
|
-
|
147
|
+
Meeseeker.stream_class.new(stream_options).tap do |stream|
|
133
148
|
puts "Stream begin: #{stream_options.to_json}; #{options.to_json}"
|
134
149
|
|
135
150
|
# Prior to v0.0.4, we only streamed operations with stream.operations.
|
@@ -165,7 +180,8 @@ module Meeseeker
|
|
165
180
|
|
166
181
|
loop do
|
167
182
|
# TODO (HF23) Switch to account_history_api.enum_virtual_ops if supported.
|
168
|
-
|
183
|
+
url = Meeseeker.default_url(chain)
|
184
|
+
condenser_api ||= Meeseeker.condenser_api_class(chain).new(url: url)
|
169
185
|
condenser_api.get_ops_in_block(n, true) do |vops|
|
170
186
|
if vops.nil?
|
171
187
|
puts "Node returned empty result for get_ops_in_block on block_num: #{n} (rate limiting?). Retrying ..."
|
@@ -181,7 +197,7 @@ module Meeseeker
|
|
181
197
|
if retries < MAX_VOP_RETRY
|
182
198
|
retries = retries + 1
|
183
199
|
condenser_api = nil
|
184
|
-
sleep
|
200
|
+
sleep Meeseeker::BLOCK_INTERVAL * retries
|
185
201
|
|
186
202
|
redo
|
187
203
|
end
|
@@ -217,8 +233,8 @@ module Meeseeker
|
|
217
233
|
# We need to tell steem-ruby to avoid json-rpc-batch on this
|
218
234
|
# node.
|
219
235
|
|
220
|
-
|
221
|
-
sleep
|
236
|
+
Meeseeker.block_api_class(chain).const_set 'MAX_RANGE_SIZE', 1
|
237
|
+
sleep Meeseeker::BLOCK_INTERVAL
|
222
238
|
redo
|
223
239
|
end
|
224
240
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Meeseeker::HiveEngine
|
2
|
+
|
3
|
+
class Agent < Meeseeker::SteemEngine::Agent
|
4
|
+
def initialize(options = {})
|
5
|
+
super
|
6
|
+
|
7
|
+
self.user_agent = Meeseeker::AGENT_ID
|
8
|
+
self.max_history = 0
|
9
|
+
self.default_encoding = 'UTF-8'
|
10
|
+
|
11
|
+
@node_url = options[:url] || Meeseeker::hive_engine_node_url
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class FollowerJob < Meeseeker::SteemEngine::FollowerJob
|
16
|
+
def initialize(options = {})
|
17
|
+
@chain_key_prefix = options[:chain_key_prefix] || Meeseeker::HIVE_ENGINE_CHAIN_KEY_PREFIX
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -26,30 +26,44 @@ module Meeseeker::SteemEngine
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def latest_block_info
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
5.times do
|
30
|
+
request_body = {
|
31
|
+
jsonrpc: "2.0",
|
32
|
+
method: :getLatestBlockInfo,
|
33
|
+
id: rpc_id
|
34
|
+
}.to_json
|
35
|
+
|
36
|
+
response = request_with_entity :post, blockchain_uri, request_body, POST_HEADERS
|
37
|
+
latest_block_info = JSON[response.body]["result"]
|
38
|
+
|
39
|
+
return latest_block_info if !!latest_block_info
|
40
|
+
|
41
|
+
sleep 3
|
42
|
+
end
|
34
43
|
|
35
|
-
|
36
|
-
|
37
|
-
JSON[response.body]["result"]
|
44
|
+
return nil
|
38
45
|
end
|
39
46
|
|
40
47
|
def block(block_num)
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
5.times do
|
49
|
+
request_body = {
|
50
|
+
jsonrpc: "2.0",
|
51
|
+
method: :getBlockInfo,
|
52
|
+
params: {
|
53
|
+
blockNumber: block_num.to_i
|
54
|
+
},
|
55
|
+
id: rpc_id
|
56
|
+
}.to_json
|
57
|
+
|
58
|
+
response = request_with_entity :post, blockchain_uri, request_body, POST_HEADERS
|
59
|
+
block = JSON[response.body]["result"]
|
60
|
+
|
61
|
+
return block if !!block
|
62
|
+
|
63
|
+
sleep 3
|
64
|
+
end
|
51
65
|
|
52
|
-
|
66
|
+
return nil
|
53
67
|
end
|
54
68
|
private
|
55
69
|
def rpc_id
|
@@ -2,6 +2,14 @@ module Meeseeker::SteemEngine
|
|
2
2
|
MAX_RETRY_INTERVAL = 18.0
|
3
3
|
|
4
4
|
class FollowerJob
|
5
|
+
def initialize(options = {})
|
6
|
+
@chain_key_prefix = options[:chain_key_prefix] || Meeseeker::STEEM_ENGINE_CHAIN_KEY_PREFIX
|
7
|
+
end
|
8
|
+
|
9
|
+
def chain_name
|
10
|
+
@chain_key_prefix.split('_').map(&:capitalize).join(' ')
|
11
|
+
end
|
12
|
+
|
5
13
|
def perform(options = {})
|
6
14
|
redis = Meeseeker.redis
|
7
15
|
last_key_prefix = nil
|
@@ -16,7 +24,7 @@ module Meeseeker::SteemEngine
|
|
16
24
|
begin
|
17
25
|
trx_id = transaction['transactionId'].to_s.split('-').first
|
18
26
|
block_num = block['blockNumber']
|
19
|
-
current_key_prefix = "
|
27
|
+
current_key_prefix = "#{@chain_key_prefix}:#{block_num}:#{trx_id}"
|
20
28
|
contract = transaction['contract']
|
21
29
|
action = transaction['action']
|
22
30
|
|
@@ -34,15 +42,15 @@ module Meeseeker::SteemEngine
|
|
34
42
|
block_transactions << trx_id
|
35
43
|
|
36
44
|
trx_pub_key = if !!virtual
|
37
|
-
|
45
|
+
"#{@chain_key_prefix}:virtual_transaction"
|
38
46
|
else
|
39
|
-
|
47
|
+
"#{@chain_key_prefix}:transaction"
|
40
48
|
end
|
41
49
|
|
42
50
|
redis.publish(trx_pub_key, transaction_payload.to_json)
|
43
51
|
end
|
44
52
|
|
45
|
-
last_key_prefix = "
|
53
|
+
last_key_prefix = "#{@chain_key_prefix}:#{block_num}:#{trx_id}"
|
46
54
|
trx_index = 0
|
47
55
|
end
|
48
56
|
|
@@ -50,6 +58,12 @@ module Meeseeker::SteemEngine
|
|
50
58
|
puts key
|
51
59
|
end
|
52
60
|
|
61
|
+
unless Meeseeker.max_keys == -1
|
62
|
+
while redis.keys("#{@chain_key_prefix}:*").size > Meeseeker.max_keys
|
63
|
+
sleep Meeseeker::BLOCK_INTERVAL
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
53
67
|
redis.set(key, transaction.to_json)
|
54
68
|
redis.expire(key, Meeseeker.expire_keys) unless Meeseeker.expire_keys == -1
|
55
69
|
|
@@ -59,18 +73,21 @@ module Meeseeker::SteemEngine
|
|
59
73
|
block_num: block_num
|
60
74
|
}
|
61
75
|
|
62
|
-
redis.set(Meeseeker::
|
63
|
-
redis.publish(
|
76
|
+
redis.set(@chain_key_prefix + Meeseeker::LAST_STEEM_ENGINE_BLOCK_NUM_KEY_SUFFIX, block_num)
|
77
|
+
redis.publish("#{@chain_key_prefix}:block", block_payload.to_json)
|
64
78
|
current_block_num = block_num
|
65
79
|
end
|
66
80
|
|
67
|
-
redis.publish("
|
68
|
-
redis.publish("
|
81
|
+
redis.publish("#{@chain_key_prefix}:#{contract}", {key: key}.to_json)
|
82
|
+
redis.publish("#{@chain_key_prefix}:#{contract}:#{action}", {key: key}.to_json)
|
69
83
|
end
|
70
84
|
end
|
71
85
|
private
|
72
86
|
def agent
|
73
|
-
@agent ||=
|
87
|
+
@agent ||= case @chain_key_prefix
|
88
|
+
when 'steem_engine' then Agent.new
|
89
|
+
when 'hive_engine' then Meeseeker::HiveEngine::Agent.new
|
90
|
+
end
|
74
91
|
end
|
75
92
|
|
76
93
|
def agent_reset
|
@@ -100,7 +117,7 @@ module Meeseeker::SteemEngine
|
|
100
117
|
last_block_num = options[:at_block_num].to_i
|
101
118
|
else
|
102
119
|
new_sync = false
|
103
|
-
last_block_num = redis.get(Meeseeker::
|
120
|
+
last_block_num = redis.get(@chain_key_prefix + Meeseeker::LAST_STEEM_ENGINE_BLOCK_NUM_KEY_SUFFIX)
|
104
121
|
block_info = agent.latest_block_info
|
105
122
|
block_num = block_info['blockNumber']
|
106
123
|
last_block = agent.block(block_num)
|
@@ -116,13 +133,13 @@ module Meeseeker::SteemEngine
|
|
116
133
|
if Meeseeker.expire_keys == -1
|
117
134
|
last_block_num = [last_block_num, block_num].max
|
118
135
|
|
119
|
-
puts "Sync
|
136
|
+
puts "Sync #{chain_name} from: #{last_block_num}"
|
120
137
|
elsif new_sync || (Time.now.utc - last_block_timestamp > Meeseeker.expire_keys)
|
121
138
|
last_block_num = block_num + 1
|
122
139
|
|
123
|
-
puts
|
140
|
+
puts "Starting new #{chain_name} sync."
|
124
141
|
else
|
125
|
-
puts "Resuming from
|
142
|
+
puts "Resuming from #{chain_name} block #{last_block_num} ..."
|
126
143
|
end
|
127
144
|
end
|
128
145
|
|
@@ -140,7 +157,7 @@ module Meeseeker::SteemEngine
|
|
140
157
|
end
|
141
158
|
|
142
159
|
if block.nil?
|
143
|
-
sleep
|
160
|
+
sleep Meeseeker::BLOCK_INTERVAL
|
144
161
|
redo
|
145
162
|
end
|
146
163
|
|