docs(clients): ReadBulk timeout is one shared deadline for the command, not per tag
Stream E made the worker wait once for all uncached tags. Fix the Go, Python, Rust, Java and .NET library docs and CLI help text, the proto comment (default is the worker's 1000 ms), and the worker design doc. Regenerated Contracts Generated/ and the Java generated protos; both diffs are comment-only.
This commit is contained in:
@@ -641,7 +641,7 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="tagAddresses">Tag addresses to read (one per result).</param>
|
||||
/// <param name="timeout">Per-call timeout for the snapshot lifecycle path; <see cref="TimeSpan.Zero"/> uses the gateway default.</param>
|
||||
/// <param name="timeout">One shared snapshot deadline for the whole command, not a per-tag budget: the worker advises every uncached tag, waits once for all of them, then tears them down. <see cref="TimeSpan.Zero"/> uses the worker default (1000 ms).</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>One <see cref="BulkReadResult"/> per requested tag, in request order.</returns>
|
||||
public async Task<IReadOnlyList<BulkReadResult>> ReadBulkAsync(
|
||||
|
||||
@@ -571,7 +571,7 @@ func runReadBulk(ctx context.Context, args []string, stdout, stderr io.Writer) e
|
||||
sessionID := flags.String("session-id", "", "gateway session id")
|
||||
serverHandle := flags.Int("server-handle", 0, "MXAccess server handle")
|
||||
items := flags.String("items", "", "comma-separated tag addresses")
|
||||
timeoutMs := flags.Int("timeout-ms", 0, "per-tag snapshot timeout in milliseconds (0 = worker default)")
|
||||
timeoutMs := flags.Int("timeout-ms", 0, "snapshot deadline in milliseconds, shared by all tags in the command (0 = worker default, 1000 ms)")
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return err
|
||||
@@ -754,7 +754,7 @@ func runBenchReadBulk(ctx context.Context, args []string, stdout, stderr io.Writ
|
||||
tagStart := flags.Int("tag-start", 1, "first machine number")
|
||||
tagPrefix := flags.String("tag-prefix", "TestMachine_", "tag prefix (machine number appended as %03d)")
|
||||
tagAttribute := flags.String("tag-attribute", "TestChangingInt", "attribute appended to each tag prefix")
|
||||
timeoutMs := flags.Int("timeout-ms", 1500, "per-tag snapshot timeout in milliseconds")
|
||||
timeoutMs := flags.Int("timeout-ms", 1500, "snapshot deadline in milliseconds, shared by all tags in each ReadBulk command")
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return err
|
||||
|
||||
@@ -549,8 +549,9 @@ func (s *Session) WriteSecured2Bulk(ctx context.Context, serverHandle int32, ent
|
||||
// MXAccess COM has no synchronous Read; the worker satisfies this by returning the
|
||||
// most recent cached OnDataChange value when the tag is already advised (WasCached=true),
|
||||
// or by taking a full AddItem + Advise + wait + UnAdvise + RemoveItem snapshot lifecycle
|
||||
// otherwise. timeout bounds the wait per tag in the snapshot case; pass zero to use the
|
||||
// worker default. Per-tag failures (timeout, invalid tag) appear as BulkReadResult entries
|
||||
// otherwise. timeout is one shared deadline for the whole command, not a per-tag budget:
|
||||
// the worker advises every uncached tag, waits once for all of them, then tears them down.
|
||||
// Pass zero to use the worker default (1000 ms). Per-tag failures (timeout, invalid tag) appear as BulkReadResult entries
|
||||
// with WasSuccessful=false; the call never returns an error for per-tag MXAccess failures.
|
||||
//
|
||||
// A non-nil but empty tagAddresses slice is treated as a no-op and returns an empty
|
||||
|
||||
@@ -49125,11 +49125,15 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
* reply returns the cached value WITHOUT modifying the existing
|
||||
* subscription (was_cached = true).
|
||||
* - Otherwise the worker takes the snapshot lifecycle itself: AddItem +
|
||||
* Advise, wait up to `timeout_ms` for the first OnDataChange, then
|
||||
* UnAdvise + RemoveItem before returning. The session is left exactly
|
||||
* as it was before the call (was_cached = false).
|
||||
* Advise, wait for the first OnDataChange, then UnAdvise + RemoveItem
|
||||
* before returning. The session is left exactly as it was before the
|
||||
* call (was_cached = false).
|
||||
*
|
||||
* `timeout_ms == 0` uses the gateway-configured default (1000 ms).
|
||||
* `timeout_ms` is one deadline shared by the whole command, not a per-tag
|
||||
* budget: the worker adds and advises every uncached tag, waits once for all
|
||||
* of them, then tears them all down. A tag that has not delivered by the
|
||||
* deadline gets its own was_successful = false result.
|
||||
* `timeout_ms == 0` uses the worker's default (1000 ms).
|
||||
* </pre>
|
||||
*
|
||||
* Protobuf type {@code mxaccess_gateway.v1.ReadBulkCommand}
|
||||
@@ -49424,11 +49428,15 @@ public final class MxaccessGateway extends com.google.protobuf.GeneratedFile {
|
||||
* reply returns the cached value WITHOUT modifying the existing
|
||||
* subscription (was_cached = true).
|
||||
* - Otherwise the worker takes the snapshot lifecycle itself: AddItem +
|
||||
* Advise, wait up to `timeout_ms` for the first OnDataChange, then
|
||||
* UnAdvise + RemoveItem before returning. The session is left exactly
|
||||
* as it was before the call (was_cached = false).
|
||||
* Advise, wait for the first OnDataChange, then UnAdvise + RemoveItem
|
||||
* before returning. The session is left exactly as it was before the
|
||||
* call (was_cached = false).
|
||||
*
|
||||
* `timeout_ms == 0` uses the gateway-configured default (1000 ms).
|
||||
* `timeout_ms` is one deadline shared by the whole command, not a per-tag
|
||||
* budget: the worker adds and advises every uncached tag, waits once for all
|
||||
* of them, then tears them all down. A tag that has not delivered by the
|
||||
* deadline gets its own was_successful = false result.
|
||||
* `timeout_ms == 0` uses the worker's default (1000 ms).
|
||||
* </pre>
|
||||
*
|
||||
* Protobuf type {@code mxaccess_gateway.v1.ReadBulkCommand}
|
||||
|
||||
+2
-2
@@ -1262,7 +1262,7 @@ public final class MxGatewayCli implements Callable<Integer> {
|
||||
@Option(
|
||||
names = "--timeout-ms",
|
||||
defaultValue = "0",
|
||||
description = "Per-tag snapshot timeout in milliseconds (0 = worker default).")
|
||||
description = "Snapshot deadline in milliseconds, shared by all tags in the command (0 = worker default, 1000 ms).")
|
||||
int timeoutMs;
|
||||
|
||||
ReadBulkCommand(MxGatewayCliClientFactory clientFactory) {
|
||||
@@ -1510,7 +1510,7 @@ public final class MxGatewayCli implements Callable<Integer> {
|
||||
@Option(
|
||||
names = "--timeout-ms",
|
||||
defaultValue = "0",
|
||||
description = "Per-tag snapshot timeout in milliseconds (0 = worker default).")
|
||||
description = "Snapshot deadline in milliseconds, shared by all tags in the command (0 = worker default, 1000 ms).")
|
||||
int timeoutMs;
|
||||
|
||||
@Option(names = "--iterations", defaultValue = "10", description = "Number of ReadBulk calls to perform.")
|
||||
|
||||
+6
-4
@@ -555,15 +555,17 @@ public final class MxGatewaySession implements AutoCloseable {
|
||||
* {@code OnDataChange} value for any tag that is already advised
|
||||
* ({@code wasCached == true}) without modifying the existing subscription,
|
||||
* and falls back to a full AddItem + Advise + wait + UnAdvise + RemoveItem
|
||||
* snapshot lifecycle otherwise. The supplied {@code timeout} bounds the
|
||||
* per-tag wait in the snapshot case; pass {@link Duration#ZERO} (or
|
||||
* {@code null}) to use the worker default (1000 ms). Per-tag failures
|
||||
* snapshot lifecycle otherwise. The supplied {@code timeout} is one shared
|
||||
* deadline for the whole command, not a per-tag budget: the worker advises
|
||||
* every uncached tag, waits once for all of them, then tears them down.
|
||||
* Pass {@link Duration#ZERO} (or {@code null}) to use the worker default
|
||||
* (1000 ms). Per-tag failures
|
||||
* appear as {@link BulkReadResult} entries with {@code wasSuccessful == false};
|
||||
* this method does not throw for per-tag MXAccess failures.
|
||||
*
|
||||
* @param serverHandle the {@code ServerHandle} owning the items
|
||||
* @param tagAddresses the tag addresses to read
|
||||
* @param timeout per-tag snapshot timeout (zero or null = worker default)
|
||||
* @param timeout snapshot deadline shared by all tags in the command (zero or null = worker default)
|
||||
* @return a per-tag {@link BulkReadResult} list
|
||||
* @throws MxGatewayException on transport or protocol failure
|
||||
* @throws NullPointerException if {@code tagAddresses} is {@code null}
|
||||
|
||||
@@ -448,8 +448,10 @@ class Session:
|
||||
``OnDataChange`` value for any tag that is already advised (``was_cached =
|
||||
True``) without modifying the existing subscription, and falls back to
|
||||
a full AddItem + Advise + wait + UnAdvise + RemoveItem snapshot lifecycle
|
||||
otherwise. ``timeout_ms`` bounds the per-tag wait in the snapshot case;
|
||||
pass ``0`` to use the worker default (1000 ms).
|
||||
otherwise. ``timeout_ms`` is one shared deadline for the whole command,
|
||||
not a per-tag budget: the worker advises every uncached tag, waits once
|
||||
for all of them, then tears them down. Pass ``0`` to use the worker
|
||||
default (1000 ms).
|
||||
"""
|
||||
if tag_addresses is None:
|
||||
raise TypeError("tag_addresses is required")
|
||||
|
||||
@@ -392,7 +392,7 @@ def unsubscribe_bulk(**kwargs: Any) -> None:
|
||||
@click.option("--server-handle", required=True, type=int, help="MXAccess server handle.")
|
||||
@click.option("--items", required=True, help="Comma-separated MXAccess tag addresses.")
|
||||
@click.option("--timeout-ms", default=0, type=int, show_default=True,
|
||||
help="Per-tag snapshot timeout in milliseconds. 0 = worker default.")
|
||||
help="Snapshot deadline in milliseconds, shared by all tags in the command. 0 = worker default (1000 ms).")
|
||||
@click.option("--correlation-id", default="", help="Client correlation id.")
|
||||
@click.option("--json", "output_json", is_flag=True, help="Emit JSON output.")
|
||||
def read_bulk(**kwargs: Any) -> None:
|
||||
|
||||
@@ -215,7 +215,7 @@ enum Command {
|
||||
server_handle: i32,
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
items: Vec<String>,
|
||||
/// Per-tag snapshot timeout in milliseconds. `0` uses the worker default (1000 ms).
|
||||
/// Snapshot deadline in milliseconds, shared by all tags in the command. `0` uses the worker default (1000 ms).
|
||||
#[arg(long, default_value_t = 0)]
|
||||
timeout_ms: u32,
|
||||
#[arg(long)]
|
||||
|
||||
@@ -533,7 +533,10 @@ impl Session {
|
||||
/// returning the most recent cached `OnDataChange` value when the tag is
|
||||
/// already advised (`was_cached = true`), or by taking a full AddItem +
|
||||
/// Advise + wait + UnAdvise + RemoveItem snapshot lifecycle otherwise.
|
||||
/// `timeout_ms == 0` lets the worker pick its default (1000 ms).
|
||||
/// `timeout_ms` is one shared deadline for the whole command, not a
|
||||
/// per-tag budget: the worker advises every uncached tag, waits once for
|
||||
/// all of them, then tears them down. `timeout_ms == 0` lets the worker
|
||||
/// pick its default (1000 ms).
|
||||
/// Per-tag failures appear as `BulkReadResult` entries with
|
||||
/// `was_successful = false`; the call never errors on per-tag failure.
|
||||
///
|
||||
|
||||
@@ -996,7 +996,7 @@ has been stale for longer than `HeartbeatStuckCeiling`, the watchdog fires
|
||||
healthy long-running commands: `StaRuntime.PumpPendingMessages()` refreshes
|
||||
`LastStaActivityUtc` (via `MarkActivity()`) every time it runs, and long-hold
|
||||
STA commands invoke it on every wait iteration (`ReadBulk` routes its
|
||||
per-tag wait through the `pumpStep` wired from `StaRuntime.PumpPendingMessages`).
|
||||
shared snapshot wait through the `pumpStep` wired from `StaRuntime.PumpPendingMessages`).
|
||||
A command that keeps pumping therefore keeps its activity timestamp fresh and
|
||||
never reaches the ceiling, while a genuinely stuck STA — one that has stopped
|
||||
pumping — accrues staleness and faults correctly. The ceiling is thus the
|
||||
|
||||
@@ -16205,11 +16205,15 @@ namespace ZB.MOM.WW.MxGateway.Contracts.Proto {
|
||||
/// reply returns the cached value WITHOUT modifying the existing
|
||||
/// subscription (was_cached = true).
|
||||
/// - Otherwise the worker takes the snapshot lifecycle itself: AddItem +
|
||||
/// Advise, wait up to `timeout_ms` for the first OnDataChange, then
|
||||
/// UnAdvise + RemoveItem before returning. The session is left exactly
|
||||
/// as it was before the call (was_cached = false).
|
||||
/// Advise, wait for the first OnDataChange, then UnAdvise + RemoveItem
|
||||
/// before returning. The session is left exactly as it was before the
|
||||
/// call (was_cached = false).
|
||||
///
|
||||
/// `timeout_ms == 0` uses the gateway-configured default (1000 ms).
|
||||
/// `timeout_ms` is one deadline shared by the whole command, not a per-tag
|
||||
/// budget: the worker adds and advises every uncached tag, waits once for all
|
||||
/// of them, then tears them all down. A tag that has not delivered by the
|
||||
/// deadline gets its own was_successful = false result.
|
||||
/// `timeout_ms == 0` uses the worker's default (1000 ms).
|
||||
/// </summary>
|
||||
[global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")]
|
||||
public sealed partial class ReadBulkCommand : pb::IMessage<ReadBulkCommand>
|
||||
|
||||
@@ -524,11 +524,15 @@ message WriteSecured2BulkEntry {
|
||||
// reply returns the cached value WITHOUT modifying the existing
|
||||
// subscription (was_cached = true).
|
||||
// - Otherwise the worker takes the snapshot lifecycle itself: AddItem +
|
||||
// Advise, wait up to `timeout_ms` for the first OnDataChange, then
|
||||
// UnAdvise + RemoveItem before returning. The session is left exactly
|
||||
// as it was before the call (was_cached = false).
|
||||
// Advise, wait for the first OnDataChange, then UnAdvise + RemoveItem
|
||||
// before returning. The session is left exactly as it was before the
|
||||
// call (was_cached = false).
|
||||
//
|
||||
// `timeout_ms == 0` uses the gateway-configured default (1000 ms).
|
||||
// `timeout_ms` is one deadline shared by the whole command, not a per-tag
|
||||
// budget: the worker adds and advises every uncached tag, waits once for all
|
||||
// of them, then tears them all down. A tag that has not delivered by the
|
||||
// deadline gets its own was_successful = false result.
|
||||
// `timeout_ms == 0` uses the worker's default (1000 ms).
|
||||
message ReadBulkCommand {
|
||||
int32 server_handle = 1;
|
||||
repeated string tag_addresses = 2;
|
||||
|
||||
Reference in New Issue
Block a user