From 29492da25fd1d3f958ed2cd3f77cdc8ac7480a5a Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Mon, 2 Mar 2026 11:10:23 +1100 Subject: [PATCH 1/6] Ingest records immediately when next payload exceeds limit --- agent/build/agent.phar | Bin 850186 -> 850791 bytes agent/build/signature.txt | 2 +- agent/src/Ingest.php | 10 ++++++++++ agent/src/NullBuffer.php | 5 +++++ agent/src/StreamBuffer.php | 5 +++++ agent/tests/Feature/IngestTest.php | 2 +- 6 files changed, 22 insertions(+), 2 deletions(-) diff --git a/agent/build/agent.phar b/agent/build/agent.phar index 98040a0b612bf96d8895e3f159cfc10b26e02c98..f300184cce3c597d929671f8deb9b989137ef95d 100755 GIT binary patch delta 432 zcmeDBWc>V_@djrmfhRHy3=OR%=^*<3Z1cd$!AzS3G?^fRAiB9G?9^mY=A{CqED%8u zefh}A1Dj7V2L()TU}xi=>>t80xjK}`L_tkWL8T-kvsl+IsWdGuwMf^lJToW9wIVq+ zH6 zg>+88*Tl*>U7($vefkA$cDBu$A+xQf%l&2%5XWKcWXC??$;qu!)9seAi%#B- zUzO>-A||D+pix{>l#`l=aG!#%f{~_zU9-uOc9SKHK+FWh%s|Wn#H>Kf z2E^<@%mKulK+FZi+}ll-@brave4lmtW9%%c)s7;qYmPXFW=Eb-wY`<{pl%Us^rTl? wd8S#3sAg}>Jna0oTku3x7323S=AM%@_SeUJe4MuFRoIIrU{Eu-JNY^R0P%ICW&i*H delta 212 zcmaF<&A986@djrmfj(&lhKAOXbP#Q9=rCh)Fw-W1!;BC?5Z!N3&^B3=d8xn^W{4n& ze%Lqn{^nE6K>?Fi@NTXToo%(bVM4EH^SWj2>y|MBF%u9o12GE_vjQ<25VHd@2M}`t zF&7YXZ(p~Jr!d5WxzougqqWaz_t$g57K;L-8?{6h^FMt)$6Ipw;mb!t)DE&q#h9E* nH(=r1CCL|IFB|>Ga{IJ6)v(gruOzrimmXqaU|?`}@^u0LR@_zX diff --git a/agent/build/signature.txt b/agent/build/signature.txt index b20cd3c2d..329e0bf41 100644 --- a/agent/build/signature.txt +++ b/agent/build/signature.txt @@ -1 +1 @@ -0389423268858E42BBF5CE5338A2515B812A14A30FE5E79C4B19A7C3D3C45426C1061A5C34CA67300409BA190E583F1D5BFC39B7965E255675DBEA180A75A5C2 +F79ACBF15D9A1AAB411485ACC443556B59C8253DDA64E07EA2055B92EAB50C963A14256BB169C343ED8B11C87A7A01F7D437499228BF7F5CF1E366B2EA56E882 diff --git a/agent/src/Ingest.php b/agent/src/Ingest.php index cd5eaefa0..6c48501ac 100644 --- a/agent/src/Ingest.php +++ b/agent/src/Ingest.php @@ -58,6 +58,16 @@ public function __construct( public function write(string $payload): void { + if ($this->buffer->isNotEmpty() && $this->buffer->willExceedThresholdWith($payload)) { + if ($this->sendBufferAfterDelayTimer !== null) { + $this->loop->cancelTimer($this->sendBufferAfterDelayTimer); + + $this->sendBufferAfterDelayTimer = null; + } + + $this->digest(); + } + $this->buffer->write($payload); if ($this->buffer->reachedThreshold()) { diff --git a/agent/src/NullBuffer.php b/agent/src/NullBuffer.php index 0dd1f55bc..027dc85d6 100644 --- a/agent/src/NullBuffer.php +++ b/agent/src/NullBuffer.php @@ -14,6 +14,11 @@ public function reachedThreshold(): bool return false; } + public function willExceedThresholdWith(string $payload): bool + { + return false; + } + /** * @return non-empty-string */ diff --git a/agent/src/StreamBuffer.php b/agent/src/StreamBuffer.php index a709f7620..a81e22178 100644 --- a/agent/src/StreamBuffer.php +++ b/agent/src/StreamBuffer.php @@ -31,6 +31,11 @@ public function reachedThreshold(): bool return strlen($this->buffer) >= $this->threshold; } + public function willExceedThresholdWith(string $payload): bool + { + return (strlen($this->buffer) + (strlen($payload) - 2)) > $this->threshold; + } + /** * @return non-empty-string */ diff --git a/agent/tests/Feature/IngestTest.php b/agent/tests/Feature/IngestTest.php index 5d1933b65..5ab9236b2 100644 --- a/agent/tests/Feature/IngestTest.php +++ b/agent/tests/Feature/IngestTest.php @@ -703,7 +703,7 @@ public function test_it_ingests_payloads_under_the_threshold_after_10_seconds(): $ingestDetailsBrowser->assertPending([]); } - public function test_it_ingests_payloads_before_10_seconds_if_the_buffer_exceeds_the_threshold(): void + public function test_it_ingests_payloads_before_10_seconds_if_the_buffer_reaches_the_threshold(): void { $loop = new LoopFake(runForSeconds: 11); $server = new TcpServerFake; From ad08ce61cd698be7429a91fb0f2ec8219ce8297f Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Mon, 2 Mar 2026 12:43:20 +1100 Subject: [PATCH 2/6] add comment --- agent/src/StreamBuffer.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/agent/src/StreamBuffer.php b/agent/src/StreamBuffer.php index a81e22178..67e085f0b 100644 --- a/agent/src/StreamBuffer.php +++ b/agent/src/StreamBuffer.php @@ -33,6 +33,8 @@ public function reachedThreshold(): bool public function willExceedThresholdWith(string $payload): bool { + // -2 to account for the removal of the `[` and `]` characters when + // appending to the stream. return (strlen($this->buffer) + (strlen($payload) - 2)) > $this->threshold; } From 0dc4add1e96988409fbe52518b4a7a1e9fc0d941 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Mon, 2 Mar 2026 12:49:09 +1100 Subject: [PATCH 3/6] add test --- agent/tests/Feature/IngestTest.php | 65 +++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/agent/tests/Feature/IngestTest.php b/agent/tests/Feature/IngestTest.php index 5ab9236b2..e39ece182 100644 --- a/agent/tests/Feature/IngestTest.php +++ b/agent/tests/Feature/IngestTest.php @@ -724,7 +724,7 @@ public function test_it_ingests_payloads_before_10_seconds_if_the_buffer_reaches ingestBrowser: $ingestBrowser, loop: $loop, server: $server, - maxBufferLength: 63, + maxBufferLength: 62, ); $this->assertNull($e, $e?->getMessage() ?? ''); @@ -803,6 +803,69 @@ public function test_it_ingests_immediately_when_buffer_is_empty_and_a_payload_o $ingestDetailsBrowser->assertPending([]); } + public function test_it_ingests_immediately_when_incoming_payload_will_put_buffer_over_the_threshold(): void + { + $loop = new LoopFake(runForSeconds: 14); + $server = new TcpServerFake; + $ingestDetailsBrowser = new BrowserFake([ + Response::jwt(), + ]); + $ingestBrowser = new BrowserFake([ + Response::ingested(), + Response::ingested(), + ]); + $loop->addTimer(0, $server->pendingConnection([['t' => 'request']])); + $loop->addTimer(1, $server->pendingConnection([['t' => 'request']])); + $loop->addTimer(2, $server->pendingConnection([['t' => 'request']])); + $loop->addTimer(3, $server->pendingConnection([['t' => 'request']])); + + [$output, $e] = $this->runAgent( + via: 'source', + ingestDetailsBrowser: $ingestDetailsBrowser, + ingestBrowser: $ingestBrowser, + loop: $loop, + server: $server, + maxBufferLength: 61, + ); + + $this->assertNull($e, $e?->getMessage() ?? ''); + $this->assertLogMatches(<<<'OUTPUT' + {date} {info} Authentication successful {duration} + {date} {info} Ingest successful {duration} + {date} {info} Ingest successful {duration} + OUTPUT, $output); + $ingestBrowser->assertSent([ + Request::ingest([ + ['t' => 'request'], + ['t' => 'request'], + ['t' => 'request'], + ]), + Request::ingest([ + ['t' => 'request'], + ]), + ]); + $ingestBrowser->assertProcessing([]); + $ingestBrowser->assertPending([]); + $loop->assertRun([ + new Timer(interval: 0, runAt: 0, scheduledAt: 0, scheduledBy: $this->functionName()), + new Timer(interval: 1, runAt: 1, scheduledAt: 0, scheduledBy: $this->functionName()), + new Timer(interval: 2, runAt: 2, scheduledAt: 0, scheduledBy: $this->functionName()), + new Timer(interval: 3, runAt: 3, scheduledAt: 0, scheduledBy: $this->functionName()), + new Timer(interval: 10, runAt: 13, scheduledAt: 3, scheduledBy: 'Laravel\NightwatchAgent\Ingest::write'), + ]); + $loop->assertCanceled([ + new Timer(interval: 10, canceledAt: 3, scheduledAt: 0, scheduledBy: 'Laravel\NightwatchAgent\Ingest::write'), + ]); + $loop->assertPending([ + new Timer(interval: 3_600, runAt: 3_600, scheduledAt: 0, scheduledBy: 'Laravel\NightwatchAgent\IngestDetailsRepository::scheduleRefreshIn'), + ]); + $ingestDetailsBrowser->assertSent([ + Request::json('/api/agent-auth'), + ]); + $ingestDetailsBrowser->assertPending([]); + } + + public function test_it_stops_ingesting_data_when_exceeding_quota_during_request(): void { $loop = new LoopFake(runForSeconds: 60); From c96599e9be33189e66dce83eae315cd7482df75b Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 01:50:02 +0000 Subject: [PATCH 4/6] Fix code styling --- agent/tests/Feature/IngestTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/agent/tests/Feature/IngestTest.php b/agent/tests/Feature/IngestTest.php index e39ece182..d35c2e8fd 100644 --- a/agent/tests/Feature/IngestTest.php +++ b/agent/tests/Feature/IngestTest.php @@ -865,7 +865,6 @@ public function test_it_ingests_immediately_when_incoming_payload_will_put_buffe $ingestDetailsBrowser->assertPending([]); } - public function test_it_stops_ingesting_data_when_exceeding_quota_during_request(): void { $loop = new LoopFake(runForSeconds: 60); From 1d1a7e5eb09df8150a5ee6c420ced5ab066685c6 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 01:50:34 +0000 Subject: [PATCH 5/6] Bump agent version to 859B5CB714BE47686CD645444022C0336586C814B1C783F48E8AD843750845E011E19E53D7CAC9C92382E1242D4A83933DF9A0686B5A9A6E0330D69100C8A047 --- agent/build/agent.phar | Bin 850791 -> 850903 bytes agent/build/signature.txt | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/build/agent.phar b/agent/build/agent.phar index f300184cce3c597d929671f8deb9b989137ef95d..02279700535fa457d258c61310870538b1a0344f 100755 GIT binary patch delta 251 zcmaF<&G`Cv;|;r*1)j1nFf_E5q=V=}XLrNRrN@iaAbopaUqRrvU+QXMI0x=U1GXpUT5VHa?8xXStF$WNH0x=g5b8in{#&aUX zqjh%7c9DJV89CQnT^y7S7^k+K5ZQRV`Ac8d4d+r0*9U?R=LKIsb@HTg(?b_E%`#GF9P1;pIjpD*KC5#sTE*6EM2v!qr#inOjd;vAYCc|z6p zR?36AMXb@2UTx)>W+kGUy)pB!^V@F06IE4=->;Z^PSV(4AM^2X+NM`wFPeZ(VQ_cy GbpinNo>30~ diff --git a/agent/build/signature.txt b/agent/build/signature.txt index 329e0bf41..2b8bda11d 100644 --- a/agent/build/signature.txt +++ b/agent/build/signature.txt @@ -1 +1 @@ -F79ACBF15D9A1AAB411485ACC443556B59C8253DDA64E07EA2055B92EAB50C963A14256BB169C343ED8B11C87A7A01F7D437499228BF7F5CF1E366B2EA56E882 +859B5CB714BE47686CD645444022C0336586C814B1C783F48E8AD843750845E011E19E53D7CAC9C92382E1242D4A83933DF9A0686B5A9A6E0330D69100C8A047 From 9c7051c158f42edefb860b7482bdaf9fc2321611 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Mon, 2 Mar 2026 12:52:43 +1100 Subject: [PATCH 6/6] CI