Implement DATA-free stream probes with ranges, paths and load offers
This commit is contained in:
1 parent
984153b90a
commit
35ad47c653
1 file changed
+86
-20
+86
-20
@@ -324,7 +324,7 @@ static struct STREAM_RECEIVE *flow_new(struct stream_peer *p, const struct strea
|
||||
f->admitted_ms = get_time_tb() / 10; stream_load_init(&f->load, f->admitted_ms);
|
||||
stream_metrics_init(&f->metrics); stream_metrics_init(&f->offered_metrics);
|
||||
if (!outgoing) { stream_load_changed(&p->load); stream_load_changed(&p->network->load); }
|
||||
f->info.peer = p->node_id; f->deadline = get_time_tb() + STREAM_CONNECT_TB;
|
||||
f->info.peer = p->node_id; f->info.subblock = sub; f->deadline = get_time_tb() + STREAM_CONNECT_TB;
|
||||
f->next = p->flows; p->flows = f; p->network->flow_count++; p->idle = 0; return f;
|
||||
}
|
||||
|
||||
@@ -356,7 +356,8 @@ static int info_payload(struct stream_local *local, uint8_t sub, uint64_t seq, u
|
||||
stream_path_write(payload + STREAM_INFO_PATH_OFF, &path); return 0;
|
||||
}
|
||||
|
||||
static int parse_info(const struct stream_message *m, const struct stream_descriptor *d, uint64_t peer, uint64_t self, uint64_t *start) {
|
||||
static int parse_info(const struct stream_message *m, const struct stream_descriptor *d, uint64_t peer, uint64_t self,
|
||||
struct stream_receive_info *info, uint64_t *start) {
|
||||
struct stream_path path; size_t used;
|
||||
if (m->length < STREAM_INFO_SIZE || m->payload[32] != d->n || m->payload[33] != d->r
|
||||
|| stream_read_be(m->payload + 34, 2) != d->block_size
|
||||
@@ -368,7 +369,52 @@ static int parse_info(const struct stream_message *m, const struct stream_descri
|
||||
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "stream INFO/ACCEPT invalid command=%u bytes=%zu", m->command, m->length);
|
||||
return STREAM_ERR_FORMAT;
|
||||
}
|
||||
*start = stream_read_be(m->payload + 8, 8); return STREAM_OK;
|
||||
const uint8_t *offer = m->payload + STREAM_INFO_OFFER_OFF;
|
||||
if ((offer[0] & ~15u) || stream_read_be(offer + 3, 2) > 10000) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "stream invalid offer flags=%u cpu=%llu", offer[0],
|
||||
(unsigned long long)stream_read_be(offer + 3, 2)); return STREAM_ERR_FORMAT;
|
||||
}
|
||||
*start = stream_read_be(m->payload + 8, 8);
|
||||
info->path = path; info->first_available = stream_read_be(m->payload + 16, 8);
|
||||
info->last_available = stream_read_be(m->payload + 24, 8); info->info_ms = get_time_tb() / 10;
|
||||
info->accepting = (offer[0] & 1) != 0; info->path_rtt_valid = (offer[0] & 2) != 0;
|
||||
info->progress_valid = (offer[0] & 4) != 0; info->cpu_valid = (offer[0] & 8) != 0;
|
||||
info->free_slots = (uint16_t)stream_read_be(offer + 1, 2); info->cpu_basis_points = (uint16_t)stream_read_be(offer + 3, 2);
|
||||
info->supplier_queue_bytes = stream_read_be(offer + 5, 8); info->progress_age_ms = stream_read_be(offer + 13, 8);
|
||||
info->path_rtt_ms = (uint32_t)stream_read_be(offer + 21, 4); return STREAM_OK;
|
||||
}
|
||||
|
||||
/* Предложение привязано к фактическому пути последнего блока; другой upstream не подтверждает его RTT. */
|
||||
static void offer_payload(struct stream_peer *p, struct stream_local *local, uint8_t sub, uint8_t *payload) {
|
||||
struct stream_network *net = p->network; uint64_t now = get_time_tb() / 10;
|
||||
uint8_t *offer = payload + STREAM_INFO_OFFER_OFF;
|
||||
unsigned count = 0; for (struct STREAM_RECEIVE *f = p->flows; f; f = f->next) count++;
|
||||
unsigned slots = STREAM_PEER_FLOW_LIMIT - count;
|
||||
if (slots > STREAM_FLOW_LIMIT - net->flow_count) slots = STREAM_FLOW_LIMIT - net->flow_count;
|
||||
offer[0] = !p->pressure.active && !p->cooling ? 1 : 0;
|
||||
stream_write_be(offer + 1, slots, 2);
|
||||
double cpu = net->cpu.valid ? net->cpu.average_percent : 0;
|
||||
if (cpu < 0) cpu = 0; if (cpu > 100) cpu = 100;
|
||||
if (net->cpu.valid) offer[0] |= 8;
|
||||
stream_write_be(offer + 3, (uint16_t)(cpu * 100), 2); stream_write_be(offer + 5, p->load.queued.bytes, 8);
|
||||
struct stream_metric_snapshot snapshot = {0};
|
||||
int valid = stream_local_metrics(net->inst, &local->descriptor, sub, now, &snapshot) == STREAM_OK;
|
||||
stream_metric_write(payload + STREAM_INFO_METRIC_OFF, &snapshot, valid);
|
||||
if (valid) offer[0] |= 4;
|
||||
stream_write_be(offer + 13, snapshot.progress_age_ms, 8);
|
||||
if (local->publisher) { offer[0] |= 2; return; }
|
||||
struct stream_path path; size_t used;
|
||||
if (stream_path_parse(payload + STREAM_INFO_PATH_OFF, 1 + payload[STREAM_INFO_PATH_OFF] * 8, &path, &used) != STREAM_OK) return;
|
||||
for (struct stream_peer *up = net->peers; up; up = up->next) {
|
||||
for (struct STREAM_RECEIVE *f = up->flows; f; f = f->next) {
|
||||
if (!f->outgoing || f->info.probe_only || f->info.state != STREAM_RECEIVE_ACTIVE || f->subblock != sub
|
||||
|| !same_stream(&f->descriptor, &local->descriptor) || !f->info.rtt_valid || !f->info.path_rtt_valid
|
||||
|| now - f->info.info_ms > 30000 || path.count != f->info.path.count + 1
|
||||
|| memcmp(path.nodes, f->info.path.nodes, f->info.path.count * sizeof(uint64_t))) continue;
|
||||
uint64_t total = (uint64_t)f->info.rtt_ms + f->info.path_rtt_ms;
|
||||
offer[0] |= 2; stream_write_be(offer + 21, total > UINT32_MAX ? UINT32_MAX : total, 4); return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Один пакет на свободный порог; собственный буфер переживает синхронное освобождение entry в etcp_send. */
|
||||
@@ -394,7 +440,7 @@ static void receive_progress(struct stream_network *net, const struct stream_des
|
||||
uint8_t seen[RS_MAX_BLOCKS] = {0}; unsigned distinct = 0; uint64_t before = UINT64_MAX;
|
||||
for (struct stream_peer *p = net->peers; p; p = p->next) {
|
||||
for (struct STREAM_RECEIVE *f = p->flows; f; f = f->next) {
|
||||
if (!f->outgoing || f->info.state == STREAM_RECEIVE_CLOSED || !same_stream(&f->descriptor, d)) continue;
|
||||
if (!f->outgoing || f->info.probe_only || f->info.state == STREAM_RECEIVE_CLOSED || !same_stream(&f->descriptor, d)) continue;
|
||||
if (f->info.state < STREAM_RECEIVE_ACTIVE) return;
|
||||
if (!seen[f->subblock]) { seen[f->subblock] = 1; distinct++; }
|
||||
if (f->info.next < before) before = f->info.next;
|
||||
@@ -471,7 +517,7 @@ static void prepare_requests(struct stream_peer *p) {
|
||||
stream_write_be(probe, f->probe, 8);
|
||||
if (control_put(p, m, probe, sizeof(probe)) == STREAM_OK) { f->info_sent = 1; f->info.state = STREAM_RECEIVE_INFO; }
|
||||
}
|
||||
if (!f->info_ready || !p->other_epoch) continue;
|
||||
if (!f->info_ready || f->info.probe_only || !p->other_epoch) continue;
|
||||
uint64_t requested = f->requested;
|
||||
struct stream_local local;
|
||||
if (stream_local_get(p->network->inst, &f->descriptor, &local) != STREAM_OK) { flow_close(f, STREAM_REASON_STOPPED, 0); continue; }
|
||||
@@ -588,9 +634,7 @@ static void subscribe_request(struct stream_peer *p, struct stream_message m, st
|
||||
return;
|
||||
}
|
||||
f->deadline = get_time_tb() + STREAM_LEASE_TB;
|
||||
struct stream_metric_snapshot snapshot = {0};
|
||||
int valid = stream_local_metrics(p->network->inst, &local->descriptor, m.subblock, get_time_tb() / 10, &snapshot) == STREAM_OK;
|
||||
stream_metric_write(payload + STREAM_INFO_METRIC_OFF, &snapshot, valid);
|
||||
offer_payload(p, local, m.subblock, payload);
|
||||
m.command = STREAM_CMD_ACCEPT;
|
||||
if (control_put(p, m, payload, STREAM_INFO_SIZE + payload[STREAM_INFO_PATH_OFF] * 8) != STREAM_OK) {
|
||||
flow_close(f, STREAM_REASON_LIMIT, 0); return;
|
||||
@@ -648,9 +692,7 @@ static void dispatch(struct stream_network *net, uint64_t sender, const uint8_t
|
||||
if (reason) {
|
||||
uint8_t failure[9] = {(uint8_t)reason}; memcpy(failure + 1, m.payload, 8); control_put(p, m, failure, sizeof(failure));
|
||||
} else {
|
||||
struct stream_metric_snapshot snapshot = {0};
|
||||
int valid = stream_local_metrics(net->inst, &local.descriptor, m.subblock, get_time_tb() / 10, &snapshot) == STREAM_OK;
|
||||
stream_metric_write(payload + STREAM_INFO_METRIC_OFF, &snapshot, valid);
|
||||
offer_payload(p, &local, m.subblock, payload);
|
||||
control_put(p, m, payload, STREAM_INFO_SIZE + payload[STREAM_INFO_PATH_OFF] * 8);
|
||||
}
|
||||
return;
|
||||
@@ -669,9 +711,9 @@ static void dispatch(struct stream_network *net, uint64_t sender, const uint8_t
|
||||
uint64_t start;
|
||||
if (m.length < 8 || stream_read_be(m.payload, 8) != f->probe) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_STREAM, "stream stale INFO probe peer=%llx", (unsigned long long)sender);
|
||||
} else if (parse_info(&m, &f->descriptor, sender, net->inst->node_id, &start) == STREAM_OK
|
||||
} else if (parse_info(&m, &f->descriptor, sender, net->inst->node_id, &f->info, &start) == STREAM_OK
|
||||
&& stream_metric_read(m.payload + STREAM_INFO_METRIC_OFF, &f->info.supplier_metrics, &f->info.supplier_metrics_valid) == STREAM_OK) {
|
||||
p->other_epoch = m.sender_epoch; f->info_ready = 1; peer_schedule(p);
|
||||
p->other_epoch = m.sender_epoch; f->info_ready = f->info.info_ready = 1; peer_schedule(p);
|
||||
uint64_t rtt = get_time_tb() / 10 - f->probe_sent_ms;
|
||||
f->info.rtt_valid = f->probe_sent_ms != 0; f->info.rtt_ms = rtt > UINT32_MAX ? UINT32_MAX : (uint32_t)rtt;
|
||||
} else flow_close(f, STREAM_REASON_PROTOCOL, 0);
|
||||
@@ -681,7 +723,7 @@ static void dispatch(struct stream_network *net, uint64_t sender, const uint8_t
|
||||
} else subscribe_request(p, m, &local);
|
||||
} else if (m.command == STREAM_CMD_ACCEPT && f) {
|
||||
uint64_t start;
|
||||
if (parse_info(&m, &f->descriptor, sender, net->inst->node_id, &start) != STREAM_OK
|
||||
if (parse_info(&m, &f->descriptor, sender, net->inst->node_id, &f->info, &start) != STREAM_OK
|
||||
|| (f->requested != UINT64_MAX && start != f->requested)) flow_close(f, STREAM_REASON_PROTOCOL, 1);
|
||||
else if (f->info.state == STREAM_RECEIVE_REQUESTED) {
|
||||
f->info.start = f->info.next = start; f->info.state = STREAM_RECEIVE_ACTIVE; f->renewed = get_time_tb();
|
||||
@@ -704,7 +746,7 @@ static void dispatch(struct stream_network *net, uint64_t sender, const uint8_t
|
||||
if (result == STREAM_OK || result == STREAM_DUPLICATE || result == STREAM_EXPIRED) {
|
||||
stream_metrics_observe(&f->metrics, seq, (uint16_t)stream_read_be(block + STREAM_BLOCK_TIMESTAMP_OFF, 2),
|
||||
block[STREAM_BLOCK_FLAGS_OFF], (uint32_t)stream_block_size(&f->descriptor), get_time_tb() / 10);
|
||||
f->info.next = seq + 1; f->info.blocks++; f->deadline = get_time_tb() + STREAM_LEASE_TB;
|
||||
f->info.next = seq + 1; f->info.blocks++; f->info.path = path; f->deadline = get_time_tb() + STREAM_LEASE_TB;
|
||||
} else flow_close(f, STREAM_REASON_PROTOCOL, 1);
|
||||
}
|
||||
}
|
||||
@@ -799,7 +841,7 @@ static void network_tick(void *arg) {
|
||||
for (struct STREAM_RECEIVE *f = p->flows; f; f = f->next) {
|
||||
if (f->info.state == STREAM_RECEIVE_CLOSED) continue;
|
||||
f->info.metrics_valid = stream_metrics_poll(&f->metrics, now_ms, &f->info.metrics) == STREAM_OK;
|
||||
if ((!f->outgoing || f->info.state < STREAM_RECEIVE_ACTIVE) && now >= f->deadline)
|
||||
if ((!f->outgoing || (f->info.state < STREAM_RECEIVE_ACTIVE && !f->info.info_ready)) && now >= f->deadline)
|
||||
flow_close(f, f->info.state < STREAM_RECEIVE_ACTIVE ? STREAM_REASON_TRANSPORT : STREAM_REASON_LEASE, 1);
|
||||
else if (f->outgoing && f->info.state >= STREAM_RECEIVE_ACTIVE && now - f->renewed >= STREAM_RENEW_TB) {
|
||||
struct stream_message m = {.descriptor = f->descriptor, .handle = f->info.handle,
|
||||
@@ -892,7 +934,8 @@ void stream_network_membership_changed(struct stream_network *net, uint64_t node
|
||||
}
|
||||
}
|
||||
|
||||
int stream_receive_open(struct STREAM_HANDLE *subscription, uint64_t node, uint8_t sub, uint64_t seq, struct STREAM_RECEIVE **out) {
|
||||
static int receive_open(struct STREAM_HANDLE *subscription, uint64_t node, uint8_t sub, uint64_t seq, int probe_only,
|
||||
struct STREAM_RECEIVE **out) {
|
||||
struct UTUN_INSTANCE *inst; struct stream_descriptor d;
|
||||
if (!out || *out || stream_handle_context(subscription, &inst, &d) != STREAM_OK || sub >= d.n || !node || node == inst->node_id) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "stream receive open invalid arguments peer=%llx sub=%u", (unsigned long long)node, sub);
|
||||
@@ -907,23 +950,46 @@ int stream_receive_open(struct STREAM_HANDLE *subscription, uint64_t node, uint8
|
||||
struct stream_peer *p = peer_open(net, node);
|
||||
if (!p) return STREAM_ERR_STATE;
|
||||
for (struct STREAM_RECEIVE *f = p->flows; f; f = f->next)
|
||||
if (f->outgoing && f->info.state != STREAM_RECEIVE_CLOSED && same_stream(&f->descriptor, &d) && f->subblock == sub) {
|
||||
if (f->outgoing && f->info.state != STREAM_RECEIVE_CLOSED && same_stream(&f->descriptor, &d) && f->subblock == sub
|
||||
&& f->info.probe_only == probe_only) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "stream receive already open peer=%llx sub=%u", (unsigned long long)node, sub);
|
||||
return STREAM_ERR_STATE;
|
||||
}
|
||||
int bootstrap = 1;
|
||||
for (struct stream_peer *other = net->peers; other; other = other->next)
|
||||
for (struct STREAM_RECEIVE *f = other->flows; f; f = f->next)
|
||||
if (f->outgoing && f->info.state != STREAM_RECEIVE_CLOSED && same_stream(&f->descriptor, &d)) bootstrap = 0;
|
||||
if (f->outgoing && !f->info.probe_only && f->info.state != STREAM_RECEIVE_CLOSED && same_stream(&f->descriptor, &d)) bootstrap = 0;
|
||||
struct STREAM_RECEIVE *f = flow_new(p, &d, sub, 1);
|
||||
if (!f) return STREAM_ERR_MEMORY;
|
||||
f->owned = 1; f->requested = seq; f->bootstrap = bootstrap; *out = f; peer_schedule(p);
|
||||
f->owned = 1; f->requested = seq; f->bootstrap = bootstrap; f->info.probe_only = probe_only; *out = f; peer_schedule(p);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "stream receive opened peer=%llx group=%llx source=%llx epoch=%llx sub=%u index=%llu bootstrap=%d",
|
||||
(unsigned long long)node, (unsigned long long)d.group_id, (unsigned long long)d.source_node_id,
|
||||
(unsigned long long)d.source_epoch, sub, (unsigned long long)seq, bootstrap);
|
||||
return STREAM_OK;
|
||||
}
|
||||
|
||||
int stream_receive_open(struct STREAM_HANDLE *subscription, uint64_t node, uint8_t sub, uint64_t seq, struct STREAM_RECEIVE **out) {
|
||||
return receive_open(subscription, node, sub, seq, 0, out);
|
||||
}
|
||||
|
||||
int stream_probe_open(struct STREAM_HANDLE *subscription, uint64_t node, uint8_t sub, struct STREAM_RECEIVE **out) {
|
||||
return receive_open(subscription, node, sub, UINT64_MAX, 1, out);
|
||||
}
|
||||
|
||||
int stream_probe_next(struct STREAM_RECEIVE *f, uint8_t sub) {
|
||||
if (!f || !f->peer || !f->info.probe_only || !f->info.info_ready || sub >= f->descriptor.n) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "stream probe next invalid state/sub=%u", sub); return STREAM_ERR_STATE;
|
||||
}
|
||||
for (struct STREAM_RECEIVE *other = f->peer->flows; other; other = other->next)
|
||||
if (other != f && other->outgoing && !other->info.handle && other->subblock == sub
|
||||
&& same_stream(&other->descriptor, &f->descriptor)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "stream probe next conflicts with pending INFO sub=%u", sub); return STREAM_WAIT;
|
||||
}
|
||||
f->subblock = f->info.subblock = sub; f->info_sent = f->info_ready = f->info.info_ready = 0;
|
||||
f->probe = f->probe_sent_ms = 0; f->deadline = get_time_tb() + STREAM_CONNECT_TB;
|
||||
f->info.state = STREAM_RECEIVE_CONNECTING; peer_schedule(f->peer); return STREAM_OK;
|
||||
}
|
||||
|
||||
void stream_receive_close(struct STREAM_RECEIVE *f) {
|
||||
if (!f) return;
|
||||
f->owned = 0;
|
||||
|
||||
Reference in new issue
Block a user