Apply refreshed peer priorities without replacing group requests

This commit is contained in:
evgeny committed 2026-10-24 15:36:12 +02:00
1 parent a7ffaf497e
commit 21d8b3844e
3 files changed
+47 -9

No files matched your search

+8 -2
View File
@@ -28,6 +28,7 @@ static const char* const tgc_names[] = { "unknown", "supernode", "desktop", "mob
struct tgc_candidate {
uint64_t node_id, started, retry_at, retry_delay;
size_t priority; // текущая позиция в SQL-порядке connected/RTT/node_id
struct TOPO_PEER_REQUEST* request;
uint8_t kind, tested, manual, listed;
};
@@ -85,7 +86,7 @@ static struct tgc_candidate* tgc_add(struct TOPO_GROUP_CONNECT* gc, uint64_t id)
struct tgc_candidate* list = u_realloc(gc->candidates, (gc->count + 1) * sizeof(*list));
if (!list) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "group candidate allocation failed"); return NULL; }
gc->candidates = list;
list[gc->count] = (struct tgc_candidate){ .node_id = id, .retry_delay = TGC_PAUSE_TB };
list[gc->count] = (struct tgc_candidate){ .node_id = id, .retry_delay = TGC_PAUSE_TB, .priority = SIZE_MAX };
return &list[gc->count++];
}
@@ -101,6 +102,10 @@ static int tgc_load(struct TOPO_GROUP_CONNECT* gc) {
size_t previous_count = gc->count;
struct tgc_candidate* c = tgc_add(gc, peers[i].node_id);
if (!c) { u_free(peers); return -1; }
if (c->priority != i)
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "group candidate priority: group=%016llx node=%016llx position=%zu",
(unsigned long long)group->group_id, (unsigned long long)c->node_id, i);
c->priority = i;
uint8_t device = peers[i].client_type;
struct TOPO_NODE* node = topo_node_registry_find(group->instance->topo_groups, c->node_id);
if (node && node->timestamp) device = node->client_type;
@@ -283,7 +288,8 @@ static void tgc_step(void* arg) {
for (size_t i = 0; i < gc->count; i++) {
struct tgc_candidate* c = &gc->candidates[i];
if (!c->listed || c->kind != kind || c->request || c->retry_at > now || tgc_peer_up(gc->group, c->node_id)) continue;
if (!best || c->tested < best->tested || (c->tested == best->tested && c->retry_at < best->retry_at)) best = c;
if (!best || c->tested < best->tested || (c->tested == best->tested &&
(c->retry_at < best->retry_at || (c->retry_at == best->retry_at && c->priority < best->priority)))) best = c;
}
if (!best) break;
if (tgc_open(gc, best) == 0) pending[kind]++;
+26 -7
View File
@@ -2,10 +2,21 @@
* Суперузел подключается ко всем суперузлам канала. Desktop/mobile — к двум;
* без UP суперузлов резервируют два desktop, без них — два mobile. Проверки
* старших классов продолжаются независимо от резервных связей. Роль supernode
* не зависит от адресов. История/RTT упорядочивают кандидатов внутри класса.
* не зависит от адресов; server без роли supernode и неизвестные устройства
* автоматически не выбираются. Класс устройства берётся из подписанной записи
* реестра или сохранённого snapshot, роль — из adm_tags локальной таблицы участников.
* В классе сначала идут ещё не проверенные кандидаты, затем готовые повторы
* с меньшим retry_at. При равенстве — текущий SQL-порядок: connected DESC,
* известный RTT перед NULL, RTT ASC, node_id ASC. Обновление списка сохраняет
* запросы/backoff известных кандидатов и применяет их новый SQL-приоритет.
* Успех — транспортный UP; группа удерживает сессию до DOWN, обмен таблицами
* продолжается отдельно. На класс не более двух попыток, CONNECTING: 2 s.
* Повторы с backoff 1..30 s или в standby burst. READY/recovery не изменяются.
* Автоповторы с backoff 1,2,4,8,16,30 s; проверка списка раз в 1 s, пока цель
* суперузлов не достигнута. Без UP всех суперузлов не переходим к desktop,
* пока каждый суперузел не проверен; mobile разрешён после проверки desktop
* и лишь при отсутствии UP обоих старших классов. Уже поднятые резервы сохраняются.
* В standby новые автоматические попытки ждут burst; ручная стартует сразу.
* READY/recovery не изменяются.
* Планировщик владеет запросами, группа — NCD. Остановка отменяет попытки,
* включая ручные, но сохраняет готовые сессии. Все вызовы — в потоке uasync.
*/
@@ -17,22 +28,30 @@
struct TOPO_GROUP;
struct ETCP_CONN;
/* Включить/обновить автоматический подбор, сохранив текущие запросы. 0/-1. */
/* Включить/обновить автоматический подбор живой CHAT-группы с channel_id.
* 0=проверка запланирована, -1=неверная группа/ошибка выделения контекста.
* Текущие запросы сохраняются; ошибка отложенного запуска логируется отдельно. */
int topo_group_connect_init(struct TOPO_GROUP* group);
/* Отменить запросы, таймеры и отложенные вызовы планировщика. */
/* Отменить запросы, таймеры, standby-wait и отложенные вызовы; освободить group->connect.
* Если группа не stopping, её UP-пиры закрепляются до освобождения запросов. NULL допустим. */
void topo_group_connect_destroy(struct TOPO_GROUP* group);
/* Запланировать проверку изменившихся сессий. */
void topo_group_connect_changed(struct TOPO_GROUP* group);
/* Учесть появление пира; для UP записать connected в БД. */
void topo_group_connect_on_up(struct TOPO_GROUP* group, struct ETCP_CONN* conn);
/* Учесть потерю пира и продолжить подбор кандидатов. */
/* Учесть потерю пира (connected=0 в БД) и продолжить подбор кандидатов. */
void topo_group_connect_on_down(struct TOPO_GROUP* group, struct ETCP_CONN* conn);
/* Число текущих UP-пиров группы, независимо от READY. */
int topo_group_connect_active_count(struct TOPO_GROUP* group);
/* Сбросить backoff после изменения сети, сохранив UP и незавершённые запросы. */
/* Сбросить backoff и standby-wait автоматического подбора после изменения сети;
* сохранить признак tested, UP и незавершённые запросы. Ручной режим не меняется. */
void topo_group_connect_restart(struct TOPO_GROUP* group);
/* Одна ручная попытка, общий запрос к сессии группы. 0=принято, -1=ошибка. */
/* Одна ручная попытка к ненулевому node_id != self в живой CHAT-группе.
* Создаёт планировщик без автоподбора, если его ещё нет; проверяет членство через peer_open.
* 0=принято/запрос уже есть, -1=ошибка. Ручная попытка обходит класс/backoff/standby,
* но завершается по общему CONNECTING-таймауту и сама не повторяется.
* В автоматическом режиме этот же кандидат после завершения подчиняется обычной политике. */
int topo_group_connect_node_once(struct TOPO_GROUP* group, uint64_t node_id);
#endif
+13
View File
@@ -201,6 +201,18 @@ static void all_supernodes(void) {
destroy(&f);
}
/* Обновлённый SQL-порядок должен менять выбор уже известных кандидатов. */
static void refreshed_priority(void) {
struct fixture f; create(&f, CLIENT_TYPE_DESKTOP, 0); start(&f);
fail(&f, 0); fail(&f, 1); fail(&f, 2);
assert(peer(&f, 3) && peer(&f, 4));
assert(topo_node_sqlite_set_connected(f.inst->topo_sqlite_db, "42", f.ids[2], 1) == 0);
topo_node_sqlite_update_rtt(f.inst->topo_sqlite_db, f.ids[2], 0);
topo_group_connect_restart(f.group); poll_events(&f);
assert(peer(&f, 2) && peer(&f, 0) && !peer(&f, 1));
destroy(&f);
}
/* Тип берётся из сохранённого snapshot; приватный адрес и пустые адреса не снимают роль. */
static void persisted_classification(void) {
struct fixture f; create(&f, CLIENT_TYPE_DESKTOP, 0);
@@ -264,6 +276,7 @@ int main(void) {
fallback(CLIENT_TYPE_DESKTOP);
fallback(CLIENT_TYPE_MOBILE);
all_supernodes();
refreshed_priority();
#ifdef UTUN_HAVE_STANDBY
standby_down();
#endif