Add periodic keep-alive timesync (opt-in)#28
Conversation
Periodic eCurrentTimeRequest keeps an otherwise-idle connection alive and
estimates the client/server clock offset. Off by default; opt in via
new studio.api.Client(url, listener, autoConnect, { enableTimeSync: true }),
period via options.timeSyncPeriodSec (default 10s, minimum 1, capped at
the runtime timer maximum).
- Three-sample cycle, smallest-RTT sample selected; delta is
packetReceived - (remoteTime + RTT/2), signed nanoseconds, measured on
an epoch-anchored monotonic clock so wall-clock steps cannot corrupt
the offset or stall the cycle
- Applied to value timestamps (delivered as signed int64 Longs; an absent
timestamp passes through unchanged); event timestamps stay in
server-clock terms as the subscribeToEvents resume token
- A delta refresh fans out to live same-host connections, and once the
system is connected also seeds later ones; changes of 20 ms or less are
treated as jitter and ignored
- A request unanswered for one period is discarded and re-emitted so
keep-alive resumes. Lacking a request id, a response that outlives its
watchdog lands in the re-emitted request's sample with a near-zero RTT
(which would win the selection) and a period-stale remoteTime; a cycle
whose sample offsets disagree by more than the samples' round-trip
times is therefore dropped and re-measured at the next period
- An error-only disconnect (no close event follows) notifies once, stops
the stall and timesync timers, and schedules the reconnect; frames from
a torn-down or replaced transport are then discarded, so a late or
old-socket frame cannot re-arm timesync or send on a dead connection
- _initTimesync, _timestampDeltaNs and _timesyncTimerActive are test
observability hooks with no production consumers
Also fixes a pre-existing lifecycle gap: onError fires onDisconnected once, via
a shared notifyDisconnectedOnce() now used by onClosed too; an error-only
disconnect previously skipped the onDisconnected notification that a close
performs.
CDP-3259
|
"event timestamps stay in server-clock terms as the subscribeToEvents resume token" - I think this should be configurable. I know this is how it is currently in CDP Studio but it is not always good. E.g. in Analyze mode when plotting, currently events and values do not match when added to same graph. |
Karmo7
left a comment
There was a problem hiding this comment.
Time sync part looks good and the options map to constructor is a good idea. I think though event timestamps syncing should be configurable as well.
And as for the timestamp publishing, maybe should discuss it for a moment. In C++ client, we have found issues with the current time adjusting, namely there are use cases where one would want to know both the original and adjusted timestamp, so maybe we could figure out how to give both to the user of the client.
One example of such use cases is plotting signals from multiple applications. If the controller clocks are out of sync, we would want to enable StudioAPI synchronizations but it would still be good to see the original timestamp in a tooltip to better match the graphed values with some other log files.
| } | ||
| } | ||
| // Event timestamps stay in server-clock domain: they are the | ||
| // subscribeToEvents(startingFrom) resume token; shifting breaks resume. |
There was a problem hiding this comment.
As we discovered in CDP Studio Alarms pane, not shifting makes it also hard to subscribe in some cases, e.g. if one want all alarm events starting from last 15 seconds (to catch periodically repeated alarm reprise events and immediately know which alarms are active). So as I wrote in another comment, I think it should be configurable whether to shift event timestamps. Or alternatively, the time diff should be provided with the original timestamp (so client code has access to both timestamps).
There was a problem hiding this comment.
I can add an adjustEventTimestamps option to the constructor, default off. When enabled, event timestamps are shifted the same way as values and subscribeToEvents(startingFrom) takes client-clock times, converted back to server clock internally.
For original+adjusted: value callbacks get the original timestamp as an optional third argument, shifted events keep an originalTimestamp field, and a getter exposes the current offset so either timestamp can be derived.
There was a problem hiding this comment.
Then the next question is that if both adjusted and original timestamps are always available, then I guess the timesync could be by default enabled? Or what does @nuubik think?
Periodic eCurrentTimeRequest keeps an otherwise-idle connection alive and estimates the client/server clock offset. Off by default; opt in via new studio.api.Client(url, listener, autoConnect, { enableTimeSync: true }), period via options.timeSyncPeriodSec (default 10s, minimum 1, capped at the runtime timer maximum).
Also fixes two connection-teardown bugs:
CDP-3259