Починить восстановление blockchain_state при full resync

This commit is contained in:
AidarKC 2026-06-26 17:30:10 +04:00
parent d49661fa29
commit 44a1ba01f3
4 changed files with 85 additions and 4 deletions

View File

@ -159,6 +159,7 @@ Full resync запускается только тогда, когда:
- настройка `sync.importUserProfileFromPartner.enabled=true` - настройка `sync.importUserProfileFromPartner.enabled=true`
- в этом режиме сервер **не ходит в Solana RPC** для создания локальной цепочки во время sync; - в этом режиме сервер **не ходит в Solana RPC** для создания локальной цепочки во время sync;
- вместо этого он запрашивает у сервера-партнёра `GetSyncUserProfile` и создаёт локальную запись по данным партнёра. - вместо этого он запрашивает у сервера-партнёра `GetSyncUserProfile` и создаёт локальную запись по данным партнёра.
- если локальный `solana_users` уже существует, sync восстанавливает только `blockchain_state` и не трогает identity-слой.
Это временная практическая заплатка, чтобы clean-start sync не зависел от rate limit внешнего Solana endpoint. Это временная практическая заплатка, чтобы clean-start sync не зависел от rate limit внешнего Solana endpoint.

View File

@ -138,6 +138,54 @@ public final class BlockchainStateDAO {
} }
} }
/**
* Строгая вставка state только если записи ещё нет.
*
* Нужна для recovery / resync:
* - identity пользователя уже может существовать в solana_users;
* - в таком случае нам надо восстановить только blockchain_state;
* - если запись уже есть, метод просто ничего не меняет.
*/
public boolean insertIfMissing(Connection c, BlockchainStateEntry e) throws SQLException {
String sql = """
INSERT INTO blockchain_state (
blockchain_name,
login,
blockchain_key,
size_limit,
file_size_bytes,
last_block_number,
last_block_hash,
updated_at_ms
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(blockchain_name) DO NOTHING
""";
try (PreparedStatement ps = c.prepareStatement(sql)) {
int i = 1;
ps.setString(i++, e.getBlockchainName());
ps.setString(i++, nn(e.getLogin()));
ps.setString(i++, nn(e.getBlockchainKey()));
ps.setLong(i++, e.getSizeLimit());
ps.setLong(i++, e.getFileSizeBytes());
ps.setInt(i++, e.getLastBlockNumber());
setBytesNullable(ps, i++, e.getLastBlockHash());
ps.setLong(i++, e.getUpdatedAtMs());
return ps.executeUpdate() > 0;
}
}
public boolean insertIfMissing(BlockchainStateEntry e) throws SQLException {
try (Connection c = db.getConnection()) {
return insertIfMissing(c, e);
}
}
/** /**
* Атомарно увеличить file_size_bytes на deltaBytes, но только если НЕ превысим size_limit. * Атомарно увеличить file_size_bytes на deltaBytes, но только если НЕ превысим size_limit.
*/ */

View File

@ -12,6 +12,7 @@ import shine.db.dao.BlockchainResyncCleanupDAO;
import shine.db.dao.BlockchainStateDAO; import shine.db.dao.BlockchainStateDAO;
import shine.db.dao.SyncServersDAO; import shine.db.dao.SyncServersDAO;
import shine.db.dao.UserCreateDAO; import shine.db.dao.UserCreateDAO;
import shine.db.dao.SolanaUsersDAO;
import shine.db.entities.BlockchainStateEntry; import shine.db.entities.BlockchainStateEntry;
import shine.db.entities.SyncServerEntry; import shine.db.entities.SyncServerEntry;
import server.sync.BlockchainResyncGuard; import server.sync.BlockchainResyncGuard;
@ -55,6 +56,7 @@ public final class PeriodicBlockchainSyncService {
private static final UserCreateDAO USER_CREATE_DAO = UserCreateDAO.getInstance(); private static final UserCreateDAO USER_CREATE_DAO = UserCreateDAO.getInstance();
private static final BlockchainResyncCleanupDAO RESYNC_CLEANUP_DAO = BlockchainResyncCleanupDAO.getInstance(); private static final BlockchainResyncCleanupDAO RESYNC_CLEANUP_DAO = BlockchainResyncCleanupDAO.getInstance();
private static final FileStoreUtil FILE_STORE = FileStoreUtil.getInstance(); private static final FileStoreUtil FILE_STORE = FileStoreUtil.getInstance();
private static final SolanaUsersDAO SOLANA_USERS_DAO = SolanaUsersDAO.getInstance();
private static final String CONFIG_IMPORT_PROFILE_FROM_PARTNER = "sync.importUserProfileFromPartner.enabled"; private static final String CONFIG_IMPORT_PROFILE_FROM_PARTNER = "sync.importUserProfileFromPartner.enabled";
private PeriodicBlockchainSyncService() {} private PeriodicBlockchainSyncService() {}
@ -386,6 +388,13 @@ public final class PeriodicBlockchainSyncService {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
long sizeLimit = profile.blockchainSizeLimitBytes() > 0 ? profile.blockchainSizeLimitBytes() : 100_000L; long sizeLimit = profile.blockchainSizeLimitBytes() > 0 ? profile.blockchainSizeLimitBytes() : 100_000L;
BlockchainStateEntry state = buildStateFromProfile(profile, sizeLimit, now);
if (SOLANA_USERS_DAO.existsByLogin(profile.login())) {
STATE_DAO.insertIfMissing(state);
return STATE_DAO.getByBlockchainName(profile.blockchainName()) != null;
}
boolean inserted = USER_CREATE_DAO.insertUserWithBlockchain( boolean inserted = USER_CREATE_DAO.insertUserWithBlockchain(
profile.login(), profile.login(),
profile.blockchainName(), profile.blockchainName(),
@ -396,10 +405,33 @@ public final class PeriodicBlockchainSyncService {
now now
); );
if (!inserted) { if (inserted) {
return STATE_DAO.getByBlockchainName(profile.blockchainName()) != null; return STATE_DAO.getByBlockchainName(profile.blockchainName()) != null;
} }
return STATE_DAO.getByBlockchainName(profile.blockchainName()) != null;
// Если пользователь уже успел существовать локально, но chain_state отсутствует,
// добиваем только state и не пытаемся пересоздать identity.
if (SOLANA_USERS_DAO.existsByLogin(profile.login())) {
STATE_DAO.insertIfMissing(state);
return STATE_DAO.getByBlockchainName(profile.blockchainName()) != null;
}
return false;
}
private static BlockchainStateEntry buildStateFromProfile(RemoteBlockchainSyncClient.RemoteSyncUserProfile profile,
long sizeLimit,
long nowMs) {
BlockchainStateEntry state = new BlockchainStateEntry();
state.setBlockchainName(profile.blockchainName());
state.setLogin(profile.login());
state.setBlockchainKey(profile.blockchainKey());
state.setSizeLimit(sizeLimit);
state.setFileSizeBytes(0L);
state.setLastBlockNumber(-1);
state.setLastBlockHash(null);
state.setUpdatedAtMs(nowMs);
return state;
} }
private static String normalize(String value) { private static String normalize(String value) {

View File

@ -1,2 +1,2 @@
client.version=1.2.277 client.version=1.2.278
server.version=1.2.257 server.version=1.2.258