| Author | Andy Green <andy@warmcat.com> 2026-09-11 04:32 UTC | | Committer | Andy Green <andy@warmcat.com> 2026-09-11 04:32 UTC | | Tree | a93bee1e058d7745ef6d705ac898a0f08c6ec955 Raw Patch | | | builder: issue strictly increasing log chunk timestamps per nspawn | builder: issue strictly increasing log chunk timestamps per nspawn
sai-web pages a task's logs 50 rows at a time, ordered by uid,timestamp
with a "timestamp > last delivered" cursor. Any chunk that shares the
timestamp of the last row on a page is therefore never delivered. On
unix lws_now_usecs() has microsecond resolution and collisions are
rare; on Windows GetSystemTimeAsFileTime() ticks at a millisecond or
coarser, so a burst of chunks, which is exactly what a 4KB pipe read
now produces, readily shares one timestamp. That is the last gap in
Windows task logs after bca9fc1: a run of lines cut mid-line at the
same byte offset every run, because the page boundary always fell in
the same place.
Remember the last timestamp issued per nspawn and never issue one that
is not greater than it. This also makes the stored order of a burst
deterministic.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
diff --git a/src/builder/b-nspawn.c b/src/builder/b-nspawn.c
index e2d42a9..33a38dc 100644
--- a/src/builder/b-nspawn.c
+++ b/src/builder/b-nspawn.c
@@ -43,6 +43,7 @@ int
saib_log_chunk_create(struct sai_nspawn *ns, void *buf, size_t len, int channel)
{
char lj[2600 + LWS_PRE];
+ lws_usec_t us;
int n = 0;
if (!ns || !ns->spm)
@@ -67,11 +68,23 @@ saib_log_chunk_create(struct sai_nspawn *ns, void *buf, size_t len, int channel)
return 0;
}
}
+ /*
+ * The web pages a task's logs 50 rows at a time with a strictly
+ * greater-than timestamp cursor, so chunks sharing the timestamp of
+ * the last row on a page are never delivered. On Windows the clock
+ * ticks at a millisecond or coarser, so a burst of chunks readily
+ * shares one: issue strictly increasing timestamps per nspawn.
+ */
+ us = lws_now_usecs();
+ if (us <= ns->last_log_us)
+ us = ns->last_log_us + 1;
+ ns->last_log_us = us;
+
n = lws_snprintf(lj + LWS_PRE, sizeof(lj) - LWS_PRE,
"{\"schema\":\"com-warmcat-sai-logs\","
"\"task_uuid\":\"%s\", \"timestamp\": %llu,"
"\"channel\": %d, \"len\": %d, ",
- ns->task->uuid, (unsigned long long)lws_now_usecs(),
+ ns->task->uuid, (unsigned long long)us,
channel, (int)len);
if (ns->retcode_set) {
diff --git a/src/common/include/private.h b/src/common/include/private.h
index 6be9e7e..bf1d64e 100644
--- a/src/common/include/private.h
+++ b/src/common/include/private.h
@@ -266,6 +266,7 @@ struct sai_nspawn {
sai_task_t *task;
unsigned int log_count;
+ lws_usec_t last_log_us; /* last chunk timestamp issued */
unsigned int killed_for_spew:1;
struct lws *stdwsi[3];
|