fixed bug when maintenance sync spanned multiple files

This commit is contained in:
Lex Berezhny 2020-08-03 16:53:40 -04:00
parent 9ab8a7dd81
commit 2952609972
5 changed files with 78 additions and 13 deletions

View file

@ -112,7 +112,8 @@ class BlockchainDB:
file as file_number, file as file_number,
COUNT(hash) as blocks, COUNT(hash) as blocks,
SUM(txcount) as txs, SUM(txcount) as txs,
MAX(height) as best_height MAX(height) as best_height,
MIN(height) as start_height
FROM block_info FROM block_info
WHERE status&1 AND status&4 WHERE status&1 AND status&4
""" """

View file

@ -4,7 +4,7 @@ from sqlalchemy import table, bindparam, text, func, union
from sqlalchemy.future import select from sqlalchemy.future import select
from sqlalchemy.schema import CreateTable from sqlalchemy.schema import CreateTable
from lbry.db.tables import Block as BlockTable, TX, TXO, TXI from lbry.db.tables import Block as BlockTable, TX, TXO, TXI, Claim, Support
from lbry.db.tables import ( from lbry.db.tables import (
pg_add_tx_constraints_and_indexes, pg_add_tx_constraints_and_indexes,
pg_add_txo_constraints_and_indexes, pg_add_txo_constraints_and_indexes,
@ -190,3 +190,17 @@ def get_block_tx_addresses(block_hash=None, tx_hash=None):
.where((TXI.c.address.isnot_(None)) & constraint), .where((TXI.c.address.isnot_(None)) & constraint),
) )
) )
@event_emitter("blockchain.sync.rewind.main", "steps")
def rewind(height: int, p: ProgressContext):
deletes = [
BlockTable.delete().where(BlockTable.c.height >= height),
TXI.delete().where(TXI.c.height >= height),
TXO.delete().where(TXO.c.height >= height),
TX.delete().where(TX.c.height >= height),
Claim.delete().where(Claim.c.height >= height),
Support.delete().where(Support.c.height >= height),
]
for delete in p.iter(deletes):
p.ctx.execute(delete)

View file

@ -103,11 +103,13 @@ class BlockchainSync(Sync):
))[0] ))[0]
tx_count += chain_file['txs'] tx_count += chain_file['txs']
block_count += chain_file['blocks'] block_count += chain_file['blocks']
file_start_height = chain_file['start_height']
starting_height = min( starting_height = min(
our_best_file_height+1 if starting_height is None else starting_height, our_best_file_height+1 file_start_height if starting_height is None else starting_height,
file_start_height
) )
tasks.append(self.db.run( tasks.append(self.db.run(
block_phase.sync_block_file, chain_file['file_number'], our_best_file_height+1, block_phase.sync_block_file, chain_file['file_number'], file_start_height,
chain_file['txs'], self.TX_FLUSH_SIZE chain_file['txs'], self.TX_FLUSH_SIZE
)) ))
with Progress(self.db.message_queue, BLOCKS_MAIN_EVENT) as p: with Progress(self.db.message_queue, BLOCKS_MAIN_EVENT) as p:
@ -302,3 +304,6 @@ class BlockchainSync(Sync):
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
await self.stop() await self.stop()
async def rewind(self, height):
await self.db.run(block_phase.rewind, height)

View file

@ -892,7 +892,7 @@ class EventGenerator:
def claims_stakes(self): def claims_stakes(self):
yield from self.generate( yield from self.generate(
"blockchain.sync.claims.stakes", ("claims",), 0, None, (self.stakes,), (1,) "blockchain.sync.claims.stakes", ("claims",), 0, None, (self.stakes,), (self.stakes,)
) )
def claims_vacuum(self): def claims_vacuum(self):

View file

@ -400,6 +400,18 @@ class TestMultiBlockFileSyncing(BasicBlockchainTestCase):
funded = await self.chain.fund_raw_transaction(hexlify(tx.raw).decode()) funded = await self.chain.fund_raw_transaction(hexlify(tx.raw).decode())
signed = await self.chain.sign_raw_transaction_with_wallet(funded['hex']) signed = await self.chain.sign_raw_transaction_with_wallet(funded['hex'])
await self.chain.send_raw_transaction(signed['hex']) await self.chain.send_raw_transaction(signed['hex'])
tx = Transaction(unhexlify(signed['hex']))
claim = None
for txo in tx.outputs:
if txo.is_claim:
claim = txo
break
support_tx = Transaction().add_outputs([
Output.pay_support_pubkey_hash(CENT, claim.claim_name, claim.claim_id, address),
])
funded = await self.chain.fund_raw_transaction(hexlify(support_tx.raw).decode())
signed = await self.chain.sign_raw_transaction_with_wallet(funded['hex'])
await self.chain.send_raw_transaction(signed['hex'])
await self.chain.generate(1) await self.chain.generate(1)
# supports \w data aren't supported until block 350, fast forward a little # supports \w data aren't supported until block 350, fast forward a little
@ -430,12 +442,12 @@ class TestMultiBlockFileSyncing(BasicBlockchainTestCase):
# get_block_files # get_block_files
self.assertEqual( self.assertEqual(
[(0, 191, 280), (1, 89, 178), (2, 73, 86)], [(0, 191, 369), (1, 89, 267), (2, 73, 98)],
[(file['file_number'], file['blocks'], file['txs']) [(file['file_number'], file['blocks'], file['txs'])
for file in await db.get_block_files()] for file in await db.get_block_files()]
) )
self.assertEqual( self.assertEqual(
[(1, 29, 58)], [(1, 29, 87)],
[(file['file_number'], file['blocks'], file['txs']) [(file['file_number'], file['blocks'], file['txs'])
for file in await db.get_block_files(1, 251)] for file in await db.get_block_files(1, 251)]
) )
@ -465,7 +477,7 @@ class TestMultiBlockFileSyncing(BasicBlockchainTestCase):
self.assertEqual(0, await db.get_claim_metadata_count(500, 1000)) self.assertEqual(0, await db.get_claim_metadata_count(500, 1000))
# get_support_metadata_count # get_support_metadata_count
self.assertEqual(2, await db.get_support_metadata_count(0, 500)) self.assertEqual(192, await db.get_support_metadata_count(0, 500))
self.assertEqual(0, await db.get_support_metadata_count(500, 1000)) self.assertEqual(0, await db.get_support_metadata_count(500, 1000))
# get_support_metadata # get_support_metadata
@ -473,7 +485,7 @@ class TestMultiBlockFileSyncing(BasicBlockchainTestCase):
[{'name': b'two', 'activation_height': 359, 'expiration_height': 852}, [{'name': b'two', 'activation_height': 359, 'expiration_height': 852},
{'name': b'two', 'activation_height': 359, 'expiration_height': 852}], {'name': b'two', 'activation_height': 359, 'expiration_height': 852}],
[{'name': c['name'], 'activation_height': c['activation_height'], 'expiration_height': c['expiration_height']} [{'name': c['name'], 'activation_height': c['activation_height'], 'expiration_height': c['expiration_height']}
for c in await db.get_support_metadata(0, 500)] for c in await db.get_support_metadata(350, 500)]
) )
@staticmethod @staticmethod
@ -507,9 +519,9 @@ class TestMultiBlockFileSyncing(BasicBlockchainTestCase):
initial_sync=True, initial_sync=True,
start=0, end=352, start=0, end=352,
block_files=[ block_files=[
(0, 191, 280, ((100, 0), (191, 280))), (0, 191, 369, ((100, 0), (191, 369))),
(1, 89, 178, ((89, 178),)), (1, 89, 267, ((89, 267),)),
(2, 73, 86, ((73, 86),)), (2, 73, 98, ((73, 98),)),
], ],
claims=[ claims=[
(102, 120, 361, 361), (102, 120, 361, 361),
@ -524,7 +536,16 @@ class TestMultiBlockFileSyncing(BasicBlockchainTestCase):
(273, 291, 361, 361), (273, 291, 361, 361),
], ],
supports=[ supports=[
(352, 352, 2, 2), (102, 121, 20, 20),
(122, 141, 20, 20),
(142, 160, 19, 19),
(161, 179, 19, 19),
(180, 198, 19, 19),
(199, 217, 19, 19),
(218, 236, 19, 19),
(237, 255, 19, 19),
(256, 274, 19, 19),
(275, 352, 19, 19),
] ]
).events) ).events)
) )
@ -566,6 +587,30 @@ class TestMultiBlockFileSyncing(BasicBlockchainTestCase):
).events) ).events)
) )
# test non-initial sync across multiple files
await self.sync.rewind(250)
await asyncio.sleep(1) # give it time to collect events
events.clear()
await self.sync.advance()
await asyncio.sleep(1) # give it time to collect events
self.assertEqual(
self.sorted_events(events),
list(EventGenerator(
initial_sync=False,
start=250, end=354,
block_files=[
(1, 30, 90, ((30, 90),)),
(2, 75, 102, ((75, 102),)),
],
claims=[(250, 354, 799, 1084)],
takeovers=[(250, 354, 1, 1)],
stakes=43,
supports=[
(250, 354, 45, 45),
]
).events)
)
class TestGeneralBlockchainSync(SyncingBlockchainTestCase): class TestGeneralBlockchainSync(SyncingBlockchainTestCase):