From 44a1ba01f36dbe1245d2b50fb711082a08c98da6e14f2d95f365c4c44bbc0681 Mon Sep 17 00:00:00 2001 From: AidarKC Date: Fri, 26 Jun 2026 17:30:10 +0400 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D1=87=D0=B8=D0=BD=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D0=B2=D0=BE=D1=81=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20blockchain=5Fstate=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=20full=20resync?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dev_Docs/Blockchain/sync-between-servers.md | 1 + .../java/shine/db/dao/BlockchainStateDAO.java | 48 +++++++++++++++++++ .../sync/PeriodicBlockchainSyncService.java | 36 +++++++++++++- VERSION.properties | 4 +- 4 files changed, 85 insertions(+), 4 deletions(-) diff --git a/Dev_Docs/Blockchain/sync-between-servers.md b/Dev_Docs/Blockchain/sync-between-servers.md index 8a2f1d6..63a9378 100644 --- a/Dev_Docs/Blockchain/sync-between-servers.md +++ b/Dev_Docs/Blockchain/sync-between-servers.md @@ -159,6 +159,7 @@ Full resync запускается только тогда, когда: - настройка `sync.importUserProfileFromPartner.enabled=true` - в этом режиме сервер **не ходит в Solana RPC** для создания локальной цепочки во время sync; - вместо этого он запрашивает у сервера-партнёра `GetSyncUserProfile` и создаёт локальную запись по данным партнёра. +- если локальный `solana_users` уже существует, sync восстанавливает только `blockchain_state` и не трогает identity-слой. Это временная практическая заплатка, чтобы clean-start sync не зависел от rate limit внешнего Solana endpoint. diff --git a/SHiNE-server/shine-server-db/src/main/java/shine/db/dao/BlockchainStateDAO.java b/SHiNE-server/shine-server-db/src/main/java/shine/db/dao/BlockchainStateDAO.java index 334cd2d..4087e5a 100644 --- a/SHiNE-server/shine-server-db/src/main/java/shine/db/dao/BlockchainStateDAO.java +++ b/SHiNE-server/shine-server-db/src/main/java/shine/db/dao/BlockchainStateDAO.java @@ -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. */ diff --git a/SHiNE-server/src/main/java/server/sync/PeriodicBlockchainSyncService.java b/SHiNE-server/src/main/java/server/sync/PeriodicBlockchainSyncService.java index d2ce69c..ddf7f7e 100644 --- a/SHiNE-server/src/main/java/server/sync/PeriodicBlockchainSyncService.java +++ b/SHiNE-server/src/main/java/server/sync/PeriodicBlockchainSyncService.java @@ -12,6 +12,7 @@ import shine.db.dao.BlockchainResyncCleanupDAO; import shine.db.dao.BlockchainStateDAO; import shine.db.dao.SyncServersDAO; import shine.db.dao.UserCreateDAO; +import shine.db.dao.SolanaUsersDAO; import shine.db.entities.BlockchainStateEntry; import shine.db.entities.SyncServerEntry; import server.sync.BlockchainResyncGuard; @@ -55,6 +56,7 @@ public final class PeriodicBlockchainSyncService { private static final UserCreateDAO USER_CREATE_DAO = UserCreateDAO.getInstance(); private static final BlockchainResyncCleanupDAO RESYNC_CLEANUP_DAO = BlockchainResyncCleanupDAO.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 PeriodicBlockchainSyncService() {} @@ -386,6 +388,13 @@ public final class PeriodicBlockchainSyncService { long now = System.currentTimeMillis(); 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( profile.login(), profile.blockchainName(), @@ -396,10 +405,33 @@ public final class PeriodicBlockchainSyncService { now ); - if (!inserted) { + if (inserted) { 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) { diff --git a/VERSION.properties b/VERSION.properties index df84d46..8135c2b 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.277 -server.version=1.2.257 +client.version=1.2.278 +server.version=1.2.258