diff --git a/src/chat/db_sync.c b/src/chat/db_sync.c index 6b52aa98..8f081e20 100644 --- a/src/chat/db_sync.c +++ b/src/chat/db_sync.c @@ -13,6 +13,9 @@ #include "../../lib/mem.h" #include "../../lib/u_async.h" #include "../../lib/platform_compat.h" +#ifdef UTUN_HAVE_STANDBY +#include "standby.h" +#endif #include #include @@ -65,6 +68,9 @@ struct DB_SYNC { struct DB_SYNC_INSTANCE** instances; int instance_count, instance_capacity; void* peer_check_timer; +#ifdef UTUN_HAVE_STANDBY + void* peer_check_wait; /* Отменяемое ожидание активной фазы Android. */ +#endif struct db_sync_done_cbk_entry* done_cbks; unsigned dispatch_depth; struct db_notice* notices; @@ -932,12 +938,26 @@ schedule: static void db_sync_resume_peer_check(struct DB_SYNC* db) { if (!db || !db->enabled || db->peer_check_timer) return; +#ifdef UTUN_HAVE_STANDBY + if (db->peer_check_wait) return; +#endif db->peer_check_timer = uasync_set_timeout(db->inst->ua, DB_SYNC_PEER_CHECK_INTERVAL * 10000ULL, db, db_sync_peer_check_cb, "history_retry"); if (!db->peer_check_timer) DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "history retry timer allocation"); } static void db_sync_peer_check_cb(void* arg) { struct DB_SYNC* db = arg; db->peer_check_timer = NULL; int pending = 0; if (!db->enabled) return; +#ifdef UTUN_HAVE_STANDBY + db->peer_check_wait = NULL; + if (standby_get_sleep_tb() > 0) { + db->peer_check_wait = standby_wait(db, db_sync_peer_check_cb); + if (!db->peer_check_wait) { + DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "history standby wait allocation; retry later"); + db_sync_resume_peer_check(db); + } + return; + } +#endif for (int i = 0; i < db->instance_count; i++) { struct DB_SYNC_INSTANCE* si = db->instances[i]; if (si->index.pending) { @@ -1048,6 +1068,9 @@ void db_sync_destroy(struct UTUN_INSTANCE* inst) { if (!db) return; db->enabled = 0; if (db->peer_check_timer) uasync_cancel_timeout(inst->ua, db->peer_check_timer); +#ifdef UTUN_HAVE_STANDBY + if (db->peer_check_wait) standby_wait_cancel(db->peer_check_wait); +#endif while (db->instance_count) db_sync_instance_remove(db->instances[db->instance_count - 1]); while (db->notices) db_notice_abort(db->notices); while (db->done_cbks) { diff --git a/src/chat/db_sync.h b/src/chat/db_sync.h index a23d28ec..611279ab 100644 --- a/src/chat/db_sync.h +++ b/src/chat/db_sync.h @@ -30,19 +30,13 @@ struct DB_SYNC_INSTANCE; // PUSH и записи MESSAGES: [message_id:32][ts:8BE][author:8BE][dlen:4BE][data][sig:64]. // ACK_PUSH wire: [message_id:32]. Подтверждает конкретную запись; повторы идемпотентны. -// Defaults -#define DB_SYNC_DEFAULT_TTL 86400 -#define DB_SYNC_DEFAULT_MAPSIZE (100UL * 1024 * 1024) +// Интервалы служебных таймеров в секундах. #define DB_SYNC_PEER_CHECK_INTERVAL 5 -#define DB_SYNC_SYNC_TIMEOUT 15 #define DB_SYNC_TTL_INTERVAL 3600 // Record flags #define DB_REC_FLAG_WAS_SENT 0x01 -// Sync protocol constants -#define DB_SEND_DATA_MAX 32 - // Ed25519 signature size #define DB_SIG_SIZE 64 @@ -91,8 +85,8 @@ int db_sync_select(struct DB_SYNC_INSTANCE* si, uint32_t offset, uint32_t l // Insert callback: fired after local or peer insert succeeds. // author_node_id — исходный автор записи, не обязательно переславший её пир. -// initial_sync = 1 если запись пришла в рамках bulk-обмена SEND_DATA (initial/re-sync), -// 0 для live-PUSH и локальных вставок. +// initial_sync = 1 для страницы MESSAGES, 0 для live-PUSH и локальной вставки. +// Вызов после commit: для страниц отложен до выхода из обработчика Merkle. typedef void (*db_sync_insert_cb)(struct DB_SYNC_INSTANCE* si, const uint8_t message_id[32], uint64_t record_timestamp, diff --git a/src/chat/merkle_sync.h b/src/chat/merkle_sync.h index 81b36291..622b8a19 100644 --- a/src/chat/merkle_sync.h +++ b/src/chat/merkle_sync.h @@ -46,7 +46,7 @@ struct merkle_sync_data_ops { /* Немедленный атомарный приём с проверкой границ листа/курсора; при наличии заменяет apply_items. */ int (*apply_page)(void* ctx, const char* ns, uint64_t peer, uint64_t prefix, int after_valid, const merkle_key_t* after, const merkle_key_t* next, int more, const uint8_t* data, size_t len); - int (*validate_peer)(void* ctx, const char* ns, uint64_t peer); // необязательная проверка: только 1 разрешает приём + int (*validate_peer)(void* ctx, const char* ns, uint64_t peer); // допуск пира: только 1 разрешает обмен /* Политика работы (например sleep/sound), 1 разрешает обмен; без callback спящий пир отложен. */ int (*can_run)(void* ctx, const char* ns, uint64_t peer); /* Результат каждой контрольной точки/ошибки, включая автоматические раунды. @@ -88,7 +88,8 @@ void merkle_sync_destroy(struct UTUN_INSTANCE* inst); * Успех — подтверждение одинакового корня конкретного раунда, не вечный статус. * Повторные запросы объединяются; каждый callback вызывается один раз, обход не сбрасывается. * После первого start изменения дерева автоматически синхронизируются с этим пиром. - * cb может быть NULL. Нужен доступный UP-пир, не self и не peer_sleep_phase. + * cb может быть NULL. Нужен UP-пир, не self, с допуском validate_peer и can_run. + * Без can_run обмен со спящим пиром откладывается. * <0: запрос не принят, callback не вызывается. DOWN/DELETE/REINIT завершают запросы ошибкой. * При блокировке исходящего пакета или ожидании ответа действует таймаут 30 секунд. * Явные cancel/destroy освобождают запросы без callback.