Apply refreshed peer priorities without replacing group requests
This commit is contained in:
1 parent
a7ffaf497e
commit
21d8b3844e
3 files changed
+47
-9
No files matched your search
@@ -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]++;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user