From 576179bd3f1cac2be8ee298d7473417dce1be9ab Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 10 Jul 2026 04:01:33 -0400 Subject: [PATCH] ci(tst-03): fix orphan-terminator test on Linux + refresh stale java worker codegen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two real defects the CI run surfaced (driving to fully green): - OrphanWorkerTerminatorTests hard-coded a Windows path (C:\...); on the Linux runner Path.GetFullPath does not root it, so the terminator's exact-path match found nothing (Expected 2/1 kills, Actual 0). Use an OS-appropriate normalized path so the matching logic is validated on every platform (5/5 pass locally). - clients/java generated MxaccessWorker.java was stale — missing the canonical max_frame_bytes field. Refreshed via `gradle generateProto` (protobuf 4.33.1, the pinned version — the diff is purely the field, no version churn). Broaden the ci.yml churn-revert to both generated aggregates so verify-clean still guards real drift elsewhere. Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW --- .gitea/workflows/ci.yml | 6 +- .../mxaccess_worker/v1/MxaccessWorker.java | 229 +++++++++++++----- .../Workers/OrphanWorkerTerminatorTests.cs | 19 +- 3 files changed, 193 insertions(+), 61 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 050b0b1..47f685c 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -112,7 +112,11 @@ jobs: working-directory: clients/java run: gradle test - name: Revert spurious protobuf-version churn (no .proto changed) - run: git checkout -- clients/java/src/main/generated/main/java/mxaccess_gateway/v1/MxaccessGateway.java || true + # Both generated aggregates can pick up protobuf-runtime-version churn on regen; revert + # both so verify-clean still catches a real, uncommitted proto/codegen change elsewhere. + run: | + git checkout -- clients/java/src/main/generated/main/java/mxaccess_gateway/v1/MxaccessGateway.java || true + git checkout -- clients/java/src/main/generated/main/java/mxaccess_worker/v1/MxaccessWorker.java || true - name: Verify generated tree is clean run: git diff --exit-code -- clients/java/src/main/generated diff --git a/clients/java/src/main/generated/main/java/mxaccess_worker/v1/MxaccessWorker.java b/clients/java/src/main/generated/main/java/mxaccess_worker/v1/MxaccessWorker.java index 36ee81c..3303cfc 100644 --- a/clients/java/src/main/generated/main/java/mxaccess_worker/v1/MxaccessWorker.java +++ b/clients/java/src/main/generated/main/java/mxaccess_worker/v1/MxaccessWorker.java @@ -3789,6 +3789,20 @@ public final class MxaccessWorker extends com.google.protobuf.GeneratedFile { */ com.google.protobuf.ByteString getGatewayVersionBytes(); + + /** + *
+     * Maximum worker-frame payload size, in bytes, negotiated by the gateway from its
+     * configured pipe limit. The worker adopts this as its frame-protocol MaxMessageBytes
+     * instead of a hard-coded default; 0 (an older gateway that never set the field) means
+     * "use the worker's built-in default". Sits above the public gRPC cap by an
+     * envelope-overhead margin so an accepted gRPC payload always fits one worker frame.
+     * 
+ * + * uint32 max_frame_bytes = 4; + * @return The maxFrameBytes. + */ + int getMaxFrameBytes(); } /** * Protobuf type {@code mxaccess_worker.v1.GatewayHello} @@ -3918,6 +3932,25 @@ public final class MxaccessWorker extends com.google.protobuf.GeneratedFile { } } + public static final int MAX_FRAME_BYTES_FIELD_NUMBER = 4; + private int maxFrameBytes_ = 0; + /** + *
+     * Maximum worker-frame payload size, in bytes, negotiated by the gateway from its
+     * configured pipe limit. The worker adopts this as its frame-protocol MaxMessageBytes
+     * instead of a hard-coded default; 0 (an older gateway that never set the field) means
+     * "use the worker's built-in default". Sits above the public gRPC cap by an
+     * envelope-overhead margin so an accepted gRPC payload always fits one worker frame.
+     * 
+ * + * uint32 max_frame_bytes = 4; + * @return The maxFrameBytes. + */ + @java.lang.Override + public int getMaxFrameBytes() { + return maxFrameBytes_; + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -3941,6 +3974,9 @@ public final class MxaccessWorker extends com.google.protobuf.GeneratedFile { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(gatewayVersion_)) { com.google.protobuf.GeneratedMessage.writeString(output, 3, gatewayVersion_); } + if (maxFrameBytes_ != 0) { + output.writeUInt32(4, maxFrameBytes_); + } getUnknownFields().writeTo(output); } @@ -3960,6 +3996,10 @@ public final class MxaccessWorker extends com.google.protobuf.GeneratedFile { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(gatewayVersion_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(3, gatewayVersion_); } + if (maxFrameBytes_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(4, maxFrameBytes_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -3981,6 +4021,8 @@ public final class MxaccessWorker extends com.google.protobuf.GeneratedFile { .equals(other.getNonce())) return false; if (!getGatewayVersion() .equals(other.getGatewayVersion())) return false; + if (getMaxFrameBytes() + != other.getMaxFrameBytes()) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -3998,6 +4040,8 @@ public final class MxaccessWorker extends com.google.protobuf.GeneratedFile { hash = (53 * hash) + getNonce().hashCode(); hash = (37 * hash) + GATEWAY_VERSION_FIELD_NUMBER; hash = (53 * hash) + getGatewayVersion().hashCode(); + hash = (37 * hash) + MAX_FRAME_BYTES_FIELD_NUMBER; + hash = (53 * hash) + getMaxFrameBytes(); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -4132,6 +4176,7 @@ public final class MxaccessWorker extends com.google.protobuf.GeneratedFile { supportedProtocolVersion_ = 0; nonce_ = ""; gatewayVersion_ = ""; + maxFrameBytes_ = 0; return this; } @@ -4174,6 +4219,9 @@ public final class MxaccessWorker extends com.google.protobuf.GeneratedFile { if (((from_bitField0_ & 0x00000004) != 0)) { result.gatewayVersion_ = gatewayVersion_; } + if (((from_bitField0_ & 0x00000008) != 0)) { + result.maxFrameBytes_ = maxFrameBytes_; + } } @java.lang.Override @@ -4201,6 +4249,9 @@ public final class MxaccessWorker extends com.google.protobuf.GeneratedFile { bitField0_ |= 0x00000004; onChanged(); } + if (other.getMaxFrameBytes() != 0) { + setMaxFrameBytes(other.getMaxFrameBytes()); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -4242,6 +4293,11 @@ public final class MxaccessWorker extends com.google.protobuf.GeneratedFile { bitField0_ |= 0x00000004; break; } // case 26 + case 32: { + maxFrameBytes_ = input.readUInt32(); + bitField0_ |= 0x00000008; + break; + } // case 32 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -4435,6 +4491,62 @@ public final class MxaccessWorker extends com.google.protobuf.GeneratedFile { return this; } + private int maxFrameBytes_ ; + /** + *
+       * Maximum worker-frame payload size, in bytes, negotiated by the gateway from its
+       * configured pipe limit. The worker adopts this as its frame-protocol MaxMessageBytes
+       * instead of a hard-coded default; 0 (an older gateway that never set the field) means
+       * "use the worker's built-in default". Sits above the public gRPC cap by an
+       * envelope-overhead margin so an accepted gRPC payload always fits one worker frame.
+       * 
+ * + * uint32 max_frame_bytes = 4; + * @return The maxFrameBytes. + */ + @java.lang.Override + public int getMaxFrameBytes() { + return maxFrameBytes_; + } + /** + *
+       * Maximum worker-frame payload size, in bytes, negotiated by the gateway from its
+       * configured pipe limit. The worker adopts this as its frame-protocol MaxMessageBytes
+       * instead of a hard-coded default; 0 (an older gateway that never set the field) means
+       * "use the worker's built-in default". Sits above the public gRPC cap by an
+       * envelope-overhead margin so an accepted gRPC payload always fits one worker frame.
+       * 
+ * + * uint32 max_frame_bytes = 4; + * @param value The maxFrameBytes to set. + * @return This builder for chaining. + */ + public Builder setMaxFrameBytes(int value) { + + maxFrameBytes_ = value; + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+       * Maximum worker-frame payload size, in bytes, negotiated by the gateway from its
+       * configured pipe limit. The worker adopts this as its frame-protocol MaxMessageBytes
+       * instead of a hard-coded default; 0 (an older gateway that never set the field) means
+       * "use the worker's built-in default". Sits above the public gRPC cap by an
+       * envelope-overhead margin so an accepted gRPC payload always fits one worker frame.
+       * 
+ * + * uint32 max_frame_bytes = 4; + * @return This builder for chaining. + */ + public Builder clearMaxFrameBytes() { + bitField0_ = (bitField0_ & ~0x00000008); + maxFrameBytes_ = 0; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:mxaccess_worker.v1.GatewayHello) } @@ -12552,64 +12664,65 @@ public final class MxaccessWorker extends com.google.protobuf.GeneratedFile { "rker_heartbeat\030\023 \001(\0132#.mxaccess_worker.v" + "1.WorkerHeartbeatH\000\0227\n\014worker_fault\030\024 \001(" + "\0132\037.mxaccess_worker.v1.WorkerFaultH\000B\006\n\004" + - "body\"Z\n\014GatewayHello\022\"\n\032supported_protoc" + + "body\"s\n\014GatewayHello\022\"\n\032supported_protoc" + "ol_version\030\001 \001(\r\022\r\n\005nonce\030\002 \001(\t\022\027\n\017gatew" + - "ay_version\030\003 \001(\t\"i\n\013WorkerHello\022\030\n\020proto" + - "col_version\030\001 \001(\r\022\r\n\005nonce\030\002 \001(\t\022\031\n\021work" + - "er_process_id\030\003 \001(\005\022\026\n\016worker_version\030\004 " + - "\001(\t\"\216\001\n\013WorkerReady\022\031\n\021worker_process_id" + - "\030\001 \001(\005\022\027\n\017mxaccess_progid\030\002 \001(\t\022\026\n\016mxacc" + - "ess_clsid\030\003 \001(\t\0223\n\017ready_timestamp\030\004 \001(\013" + - "2\032.google.protobuf.Timestamp\"w\n\rWorkerCo" + - "mmand\022/\n\007command\030\001 \001(\0132\036.mxaccess_gatewa" + - "y.v1.MxCommand\0225\n\021enqueue_timestamp\030\002 \001(" + - "\0132\032.google.protobuf.Timestamp\"\201\001\n\022Worker" + - "CommandReply\0222\n\005reply\030\001 \001(\0132#.mxaccess_g" + - "ateway.v1.MxCommandReply\0227\n\023completed_ti" + - "mestamp\030\002 \001(\0132\032.google.protobuf.Timestam" + - "p\"\036\n\014WorkerCancel\022\016\n\006reason\030\001 \001(\t\"Q\n\016Wor" + - "kerShutdown\022/\n\014grace_period\030\001 \001(\0132\031.goog" + - "le.protobuf.Duration\022\016\n\006reason\030\002 \001(\t\"H\n\021" + - "WorkerShutdownAck\0223\n\006status\030\001 \001(\0132#.mxac" + - "cess_gateway.v1.ProtocolStatus\":\n\013Worker" + - "Event\022+\n\005event\030\001 \001(\0132\034.mxaccess_gateway." + - "v1.MxEvent\"\245\002\n\017WorkerHeartbeat\022\031\n\021worker" + - "_process_id\030\001 \001(\005\022.\n\005state\030\002 \001(\0162\037.mxacc" + - "ess_worker.v1.WorkerState\022?\n\033last_sta_ac" + - "tivity_timestamp\030\003 \001(\0132\032.google.protobuf" + - ".Timestamp\022\035\n\025pending_command_count\030\004 \001(" + - "\r\022\"\n\032outbound_event_queue_depth\030\005 \001(\r\022\033\n" + - "\023last_event_sequence\030\006 \001(\004\022&\n\036current_co" + - "mmand_correlation_id\030\007 \001(\t\"\364\001\n\013WorkerFau" + - "lt\0229\n\010category\030\001 \001(\0162\'.mxaccess_worker.v" + - "1.WorkerFaultCategory\022\026\n\016command_method\030" + - "\002 \001(\t\022\024\n\007hresult\030\003 \001(\005H\000\210\001\001\022\026\n\016exception" + - "_type\030\004 \001(\t\022\032\n\022diagnostic_message\030\005 \001(\t\022" + - "<\n\017protocol_status\030\006 \001(\0132#.mxaccess_gate" + - "way.v1.ProtocolStatusB\n\n\010_hresult*\227\002\n\013Wo" + - "rkerState\022\034\n\030WORKER_STATE_UNSPECIFIED\020\000\022" + - "\031\n\025WORKER_STATE_STARTING\020\001\022\034\n\030WORKER_STA" + - "TE_HANDSHAKING\020\002\022!\n\035WORKER_STATE_INITIAL" + - "IZING_STA\020\003\022\026\n\022WORKER_STATE_READY\020\004\022\"\n\036W" + - "ORKER_STATE_EXECUTING_COMMAND\020\005\022\036\n\032WORKE" + - "R_STATE_SHUTTING_DOWN\020\006\022\030\n\024WORKER_STATE_" + - "STOPPED\020\007\022\030\n\024WORKER_STATE_FAULTED\020\010*\307\004\n\023" + - "WorkerFaultCategory\022%\n!WORKER_FAULT_CATE" + - "GORY_UNSPECIFIED\020\000\022+\n\'WORKER_FAULT_CATEG" + - "ORY_INVALID_ARGUMENTS\020\001\0227\n3WORKER_FAULT_" + - "CATEGORY_GATEWAY_AUTHENTICATION_FAILED\020\002" + - "\022+\n\'WORKER_FAULT_CATEGORY_PROTOCOL_MISMA" + - "TCH\020\003\022,\n(WORKER_FAULT_CATEGORY_PROTOCOL_" + - "VIOLATION\020\004\022+\n\'WORKER_FAULT_CATEGORY_PIP" + - "E_DISCONNECTED\020\005\0222\n.WORKER_FAULT_CATEGOR" + - "Y_MXACCESS_CREATION_FAILED\020\006\0221\n-WORKER_F" + - "AULT_CATEGORY_MXACCESS_COMMAND_FAILED\020\007\022" + - ":\n6WORKER_FAULT_CATEGORY_MXACCESS_EVENT_" + - "CONVERSION_FAILED\020\010\022\"\n\036WORKER_FAULT_CATE" + - "GORY_STA_HUNG\020\t\022(\n$WORKER_FAULT_CATEGORY" + - "_QUEUE_OVERFLOW\020\n\022*\n&WORKER_FAULT_CATEGO" + - "RY_SHUTDOWN_TIMEOUT\020\013B&\252\002#ZB.MOM.WW.MxGa" + - "teway.Contracts.Protob\006proto3" + "ay_version\030\003 \001(\t\022\027\n\017max_frame_bytes\030\004 \001(" + + "\r\"i\n\013WorkerHello\022\030\n\020protocol_version\030\001 \001" + + "(\r\022\r\n\005nonce\030\002 \001(\t\022\031\n\021worker_process_id\030\003" + + " \001(\005\022\026\n\016worker_version\030\004 \001(\t\"\216\001\n\013WorkerR" + + "eady\022\031\n\021worker_process_id\030\001 \001(\005\022\027\n\017mxacc" + + "ess_progid\030\002 \001(\t\022\026\n\016mxaccess_clsid\030\003 \001(\t" + + "\0223\n\017ready_timestamp\030\004 \001(\0132\032.google.proto" + + "buf.Timestamp\"w\n\rWorkerCommand\022/\n\007comman" + + "d\030\001 \001(\0132\036.mxaccess_gateway.v1.MxCommand\022" + + "5\n\021enqueue_timestamp\030\002 \001(\0132\032.google.prot" + + "obuf.Timestamp\"\201\001\n\022WorkerCommandReply\0222\n" + + "\005reply\030\001 \001(\0132#.mxaccess_gateway.v1.MxCom" + + "mandReply\0227\n\023completed_timestamp\030\002 \001(\0132\032" + + ".google.protobuf.Timestamp\"\036\n\014WorkerCanc" + + "el\022\016\n\006reason\030\001 \001(\t\"Q\n\016WorkerShutdown\022/\n\014" + + "grace_period\030\001 \001(\0132\031.google.protobuf.Dur" + + "ation\022\016\n\006reason\030\002 \001(\t\"H\n\021WorkerShutdownA" + + "ck\0223\n\006status\030\001 \001(\0132#.mxaccess_gateway.v1" + + ".ProtocolStatus\":\n\013WorkerEvent\022+\n\005event\030" + + "\001 \001(\0132\034.mxaccess_gateway.v1.MxEvent\"\245\002\n\017" + + "WorkerHeartbeat\022\031\n\021worker_process_id\030\001 \001" + + "(\005\022.\n\005state\030\002 \001(\0162\037.mxaccess_worker.v1.W" + + "orkerState\022?\n\033last_sta_activity_timestam" + + "p\030\003 \001(\0132\032.google.protobuf.Timestamp\022\035\n\025p" + + "ending_command_count\030\004 \001(\r\022\"\n\032outbound_e" + + "vent_queue_depth\030\005 \001(\r\022\033\n\023last_event_seq" + + "uence\030\006 \001(\004\022&\n\036current_command_correlati" + + "on_id\030\007 \001(\t\"\364\001\n\013WorkerFault\0229\n\010category\030" + + "\001 \001(\0162\'.mxaccess_worker.v1.WorkerFaultCa" + + "tegory\022\026\n\016command_method\030\002 \001(\t\022\024\n\007hresul" + + "t\030\003 \001(\005H\000\210\001\001\022\026\n\016exception_type\030\004 \001(\t\022\032\n\022" + + "diagnostic_message\030\005 \001(\t\022<\n\017protocol_sta" + + "tus\030\006 \001(\0132#.mxaccess_gateway.v1.Protocol" + + "StatusB\n\n\010_hresult*\227\002\n\013WorkerState\022\034\n\030WO" + + "RKER_STATE_UNSPECIFIED\020\000\022\031\n\025WORKER_STATE" + + "_STARTING\020\001\022\034\n\030WORKER_STATE_HANDSHAKING\020" + + "\002\022!\n\035WORKER_STATE_INITIALIZING_STA\020\003\022\026\n\022" + + "WORKER_STATE_READY\020\004\022\"\n\036WORKER_STATE_EXE" + + "CUTING_COMMAND\020\005\022\036\n\032WORKER_STATE_SHUTTIN" + + "G_DOWN\020\006\022\030\n\024WORKER_STATE_STOPPED\020\007\022\030\n\024WO" + + "RKER_STATE_FAULTED\020\010*\307\004\n\023WorkerFaultCate" + + "gory\022%\n!WORKER_FAULT_CATEGORY_UNSPECIFIE" + + "D\020\000\022+\n\'WORKER_FAULT_CATEGORY_INVALID_ARG" + + "UMENTS\020\001\0227\n3WORKER_FAULT_CATEGORY_GATEWA" + + "Y_AUTHENTICATION_FAILED\020\002\022+\n\'WORKER_FAUL" + + "T_CATEGORY_PROTOCOL_MISMATCH\020\003\022,\n(WORKER" + + "_FAULT_CATEGORY_PROTOCOL_VIOLATION\020\004\022+\n\'" + + "WORKER_FAULT_CATEGORY_PIPE_DISCONNECTED\020" + + "\005\0222\n.WORKER_FAULT_CATEGORY_MXACCESS_CREA" + + "TION_FAILED\020\006\0221\n-WORKER_FAULT_CATEGORY_M" + + "XACCESS_COMMAND_FAILED\020\007\022:\n6WORKER_FAULT" + + "_CATEGORY_MXACCESS_EVENT_CONVERSION_FAIL" + + "ED\020\010\022\"\n\036WORKER_FAULT_CATEGORY_STA_HUNG\020\t" + + "\022(\n$WORKER_FAULT_CATEGORY_QUEUE_OVERFLOW" + + "\020\n\022*\n&WORKER_FAULT_CATEGORY_SHUTDOWN_TIM" + + "EOUT\020\013B&\252\002#ZB.MOM.WW.MxGateway.Contracts" + + ".Protob\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -12629,7 +12742,7 @@ public final class MxaccessWorker extends com.google.protobuf.GeneratedFile { internal_static_mxaccess_worker_v1_GatewayHello_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_mxaccess_worker_v1_GatewayHello_descriptor, - new java.lang.String[] { "SupportedProtocolVersion", "Nonce", "GatewayVersion", }); + new java.lang.String[] { "SupportedProtocolVersion", "Nonce", "GatewayVersion", "MaxFrameBytes", }); internal_static_mxaccess_worker_v1_WorkerHello_descriptor = getDescriptor().getMessageType(2); internal_static_mxaccess_worker_v1_WorkerHello_fieldAccessorTable = new diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/OrphanWorkerTerminatorTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/OrphanWorkerTerminatorTests.cs index 709f16c..df0eafc 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/OrphanWorkerTerminatorTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/OrphanWorkerTerminatorTests.cs @@ -13,7 +13,19 @@ namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Workers; /// public sealed class OrphanWorkerTerminatorTests { - private const string WorkerExecutablePath = @"C:\app\src\ZB.MOM.WW.MxGateway.Worker\bin\x86\Release\ZB.MOM.WW.MxGateway.Worker.exe"; + // An OS-appropriate, already-normalized absolute path so Path.GetFullPath (used by the + // terminator to resolve the configured path) is idempotent and the exact-match compare holds + // on every platform. A hard-coded Windows literal (C:\...) is not rooted on Linux, so + // Path.GetFullPath there prepends the cwd and the match fails — the terminator is a + // Windows-only feature in production, but its matching logic must still be testable on the + // Linux CI runner. + private static readonly string WorkerExecutablePath = Path.GetFullPath(Path.Combine( + Path.GetTempPath(), + "MxGatewayOrphanTests", + "bin", + "x86", + "Release", + "ZB.MOM.WW.MxGateway.Worker.exe")); /// Verifies that orphan worker processes matching the configured executable path are killed. [Fact] @@ -59,7 +71,10 @@ public sealed class OrphanWorkerTerminatorTests // not our worker and must be left alone. FakeProcessInspector inspector = new( [ - new RunningProcessInfo(301, @"C:\other\place\ZB.MOM.WW.MxGateway.Worker.exe"), + new RunningProcessInfo(301, Path.GetFullPath(Path.Combine( + Path.GetTempPath(), + "MxGatewayOrphanTests-other", + "ZB.MOM.WW.MxGateway.Worker.exe"))), ]); OrphanWorkerTerminator terminator = CreateTerminator(inspector);