docs(member-files): clarify storage lifecycle and internal owners

This commit is contained in:
evgeny committed 2026-10-24 18:47:06 +02:00
1 parent f7b064a11a
commit c1da55ad87
3 files changed
+50 -24

No files matched your search

+29 -17
View File
@@ -1,4 +1,6 @@
/* Внутреннее состояние owner, только для member_files/; storage принадлежит единственному worker. */
/* Внутреннее состояние member_files/, все списки/таймеры принадлежат uasync.
* Disk worker последовательно владеет storage и своими снимками I/O; completion возвращает управление uasync.
* Start: worker/reserved cleanup → storage → router/tick → initialize. Stop: quiesce → drain → storage cleanup → join/free. */
#ifndef MEMBER_FILES_PRIVATE_H
#define MEMBER_FILES_PRIVATE_H
#include "member_files.h"
@@ -9,31 +11,41 @@ struct mf_job;
struct mf_transfer;
struct mf_local;
struct member_files {
struct UTUN_INSTANCE* inst;
struct mf_storage* storage;
struct mf_worker* disk;
struct mf_work* cleanup;
struct mf_pending* pending;
struct mf_job* jobs;
struct mf_job* worker;
struct mf_transfer* transfers;
struct mf_local* locals;
void* tick;
void* pump;
unsigned pending_count, job_count, transfer_count;
unsigned server_transfer_count;
unsigned local_count;
int closing, used_storage, sweep_due;
int storage_status, init_result;
struct UTUN_INSTANCE* inst; // Заимствованное ядро; ua/router/группы живут до destroy.
struct mf_storage* storage; // Собственный storage; освобождается worker после всех stream cleanup.
struct mf_worker* disk; // Единственный FIFO дискового I/O; uasync владеет handle.
struct mf_work* cleanup; // Резерв освобождения storage, создан до начала I/O.
struct mf_pending* pending; // Клиентские команды, до16; handle удаляется до callback.
struct mf_job* jobs; // До32 серверных queued/worker/reply задач, включая sweep.
struct mf_job* worker; // Текущая серверная задача; не включает startup/клиентские stream I/O.
struct mf_transfer* transfers; // Собственные client/server streams и их отложенная очистка.
struct mf_local* locals; // До32 сообщений self→self, общий FIFO запросов/ответов.
void* tick; // Таймер1с: pending/stream timeout, повторы, sweep/resume.
void* pump; // Отменяемый call_soon серверного pump, не worker.
unsigned pending_count, job_count, transfer_count; // Числа pending/server jobs/client streams.
unsigned server_transfer_count; // Серверные streams, независимо от client limit16.
unsigned local_count; // Длина локального FIFO.
int closing, used_storage, sweep_due; // Quiesce, необходимость периодического sweep и ожидающий sweep.
int storage_status; // Uasync:BUSY до startup completion, затем MF_OK либо постоянная ошибка.
int init_result; // Worker пишет результат initialize; читать только после completion/drain.
};
/* Запросить остановку streams группы (0 — всех), дождаться их I/O через worker drain отдельно. */
void mf_transfers_quiesce(struct member_files* files, uint64_t group);
/* 1 — DATA помещается в кольцо либо относится к уже неактивному потоку;0 — router должен удержать пакет. */
int mf_transfer_receive_ready(struct member_files* files, uint64_t group, uint64_t peer, int kind, uint64_t id);
/* Обработать проверенный STREAM-пакет; group/peer — end-to-end identity router. */
void mf_transfer_dispatch(struct member_files* files, uint64_t group, uint64_t peer, const struct mf_message* message);
/* Завершить потоки пары с IO_ERROR при reset/close router; прочие пары не затрагиваются. */
void mf_transfers_peer_down(struct member_files* files, uint64_t group, uint64_t peer);
/* Планировать готовые pumps / обработать прогресс и таймауты; все функции вызываются в uasync. */
void mf_transfers_wake(struct member_files* files);
void mf_transfers_tick(struct member_files* files, uint64_t now);
/* Отправить signed+encrypted либо поставить local:0 принято,1 backpressure,-1 ошибка. message копируется. */
int mf_send(struct member_files* files, uint64_t group, uint64_t peer, const struct mf_message* message);
/* 1/0: enabled, signed storage-role self, действующие peer/owner, запись только своего UID. Без I/O. */
int mf_server_allowed(struct member_files* files, uint64_t group, uint64_t peer, const struct mf_message* message);
/* Пробудить заблокированный router receive и голову local FIFO, если появились места. */
void mf_receive_resume(struct member_files* files);
/* Поставить копию ответа с указанным статусом в ограниченную очередь; отказ логируется. */
void mf_reply(struct member_files* files, uint64_t group, uint64_t peer, const struct mf_message* request, int status);
#endif
+19 -5
View File
@@ -1,17 +1,31 @@
/* Серверное хранилище. Вызывать execute/sweep/destroy последовательно, только без
* одновременно работающего worker. create не обращается к диску; initialize выполняет
* стартовый обход в worker до запросов. ACL проверяет member_files.c перед каждой работой. Файлы временных загрузок
/* Серверное хранилище. После create все initialize/execute/sweep/destroy выполняются
* последовательно одним disk worker; блокировки внутри storage нет. create не обращается к диску.
* initialize выполняет стартовый обход до запросов. ACL проверяет member_files.c перед каждой работой.
* Storage сам проверяет group/peer/owner и принадлежность мутации UID, но не членство/подписанные роли.
* Файлы временных загрузок
* закрываются после 10 секунд простоя, удаляются после upload_ttl; restart отменяет их.
* Открытые regular-файлы кешируются, максимум 64. Родительские пути не следуют ссылкам. */
* Открытые regular-файлы кешируются, максимум64 (включая temp); активных upload до16.
* Квота/числа объектов общие для UID во всех группах и восстанавливаются только при initialize.
* Успешные COMMIT/MKDIR/RENAME/REMOVE занимают одну из64 квитанций на MF_REPLAY_TB от начала обработки.
* Родительские пути не следуют ссылкам. Файлы и счётчики не образуют откатываемую транзакцию. */
#ifndef MEMBER_FILES_STORAGE_H
#define MEMBER_FILES_STORAGE_H
#include "protocol.h"
struct mf_storage;
/* root — NUL-путь1..2047 байт, quota/TTL>0 (байты и timebase0.1мс); копирует root, возвращает owner/NULL. */
struct mf_storage* mf_storage_create(const char* root, uint64_t quota, uint64_t upload_ttl_tb);
/* Однократный стартовый обход. До его завершения execute возвращает BUSY, после ошибки — её код. */
/* MF_OK/положительный mf_status: создать/открыть root, удалить старые .uploads, восстановить квоты.
* Повтор после успеха — no-op; после ошибки возвращает тот же код, нужен новый owner/restart.
* До завершения execute возвращает BUSY, после ошибки — её код, частичные счётчики не выдаются. */
int mf_storage_initialize(struct mf_storage* storage);
/* Закрыть fd/корень, удалить активные temp и освободить owner; опубликованные файлы сохраняются, NULL допустим. */
void mf_storage_destroy(struct mf_storage* storage);
/* Закрыть idle fd и удалить просроченные uploads; now — монотонный timebase0.1мс, NULL допустим. */
void mf_storage_sweep(struct mf_storage* storage, uint64_t now);
/* Выполнить LIST..REMOVE либо QUOTA; STREAM преобразует transfer.c. Аргументы обязательны и не перекрываются.
* request не меняется; response получает копию запроса и результат, status/data_len определяют пригодность данных.
* Одинаковый ID/содержимое мутации возвращает квитанцию; конфликт — BAD_PACKET. После окна гарантий повтора нет.
* Полный кеш: BUSY.cursor — остаток до ближайшего освобождения в timebase; квитанции не вытесняются. */
void mf_storage_execute(struct mf_storage* storage, uint64_t group, uint64_t peer,
const struct mf_message* request, struct mf_message* response);
/* Диагностика/тест: число файловых дескрипторов, исключая каталог корня. */
+2 -2
View File
@@ -279,8 +279,8 @@ static void tick(void* arg) {
if (t->phase == 0) {
if (t->ticks == 1) {
struct member_files* files = t->nodes[SERVER]->member_files;
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "member_files test: startup tick=%u storage_status=%d init_result=%d closing=%d server=%llx",
t->ticks, files->storage_status, files->init_result, files->closing, (unsigned long long)t->ids[SERVER]);
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "member_files test: startup tick=%u storage_status=%d closing=%d server=%llx",
t->ticks, files->storage_status, files->closing, (unsigned long long)t->ids[SERVER]);
}
int a = etcp_router_get_path(t->nodes[AUTHOR], GROUP, t->ids[SERVER], ETCP_RT_ID_MEMBER_FILES).next_hop_node_id != 0;
int b = etcp_router_get_path(t->nodes[READER], GROUP, t->ids[SERVER], ETCP_RT_ID_MEMBER_FILES).next_hop_node_id != 0;