@@ -1612,6 +1612,17 @@ void testInflightRetryCountHealthMetricExactlyOnce() throws Exception {
16121612 assertTrue (healthCheckFields .responseCodes .containsKey (Status .Code .OK .value ()));
16131613 assertEquals (3 , healthCheckFields .responseCodes .get (Status .Code .OK .value ()));
16141614 assertEquals ("projects/p1/datasets/d1/tables/t1/streams/s1" , healthCheckFields .streamName );
1615+
1616+ AppendRowsRequest secondRequest = testBigQueryWrite .getAppendRequests ().get (1 );
1617+ assertThat (secondRequest .getClientStats ().getRequestStats ().hasQueuedRequestsCount ()).isTrue ();
1618+ assertThat (secondRequest .getClientStats ().getRequestStats ().getQueuedRequestsCount ())
1619+ .isEqualTo (2 );
1620+ // When both requests are retried together in localQueue, only the first request in localQueue
1621+ // should have window_stats populated.
1622+ AppendRowsRequest retryFirstInLocalQueue = testBigQueryWrite .getAppendRequests ().get (2 );
1623+ AppendRowsRequest retrySecondInLocalQueue = testBigQueryWrite .getAppendRequests ().get (3 );
1624+ assertThat (retryFirstInLocalQueue .getClientStats ().hasWindowStats ()).isTrue ();
1625+ assertThat (retrySecondInLocalQueue .getClientStats ().hasWindowStats ()).isFalse ();
16151626 }
16161627
16171628 @ Test
@@ -1722,6 +1733,114 @@ void testEarliestSendTime_retryScenario() throws Exception {
17221733
17231734 // Once exhausted, state should be null
17241735 assertThat (connectionWorker .getEarliestSendTime ()).isNull ();
1736+
1737+ AppendRowsRequest initialRequest = testBigQueryWrite .getAppendRequests ().get (0 );
1738+ AppendRowsRequest retryRequest = testBigQueryWrite .getAppendRequests ().get (1 );
1739+ assertThat (initialRequest .getClientStats ().getRequestStats ().hasSendTimeMillis ()).isTrue ();
1740+ assertThat (retryRequest .getClientStats ().getRequestStats ().hasSendTimeMillis ()).isTrue ();
1741+ assertThat (retryRequest .getClientStats ().getRequestStats ().getSendTimeMillis ())
1742+ .isAtLeast (initialRequest .getClientStats ().getRequestStats ().getSendTimeMillis ());
1743+ assertThat (initialRequest .getClientStats ().getRequestStats ().hasQueuedRequestsCount ())
1744+ .isTrue ();
1745+ assertThat (initialRequest .getClientStats ().getRequestStats ().getQueuedRequestsCount ())
1746+ .isEqualTo (1 );
1747+ assertThat (retryRequest .getClientStats ().getRequestStats ().hasQueuedRequestsCount ()).isTrue ();
1748+ assertThat (retryRequest .getClientStats ().getRequestStats ().getQueuedRequestsCount ())
1749+ .isEqualTo (1 );
1750+ }
1751+ }
1752+
1753+ @ Test
1754+ void testWindowStats () throws Exception {
1755+ long beforeCreationMillis = Instant .now ().toEpochMilli ();
1756+ ProtoSchema schema1 = createProtoSchema ("foo" );
1757+ try (StreamWriter sw1 =
1758+ StreamWriter .newBuilder (TEST_STREAM_1 , client )
1759+ .setLocation ("us" )
1760+ .setWriterSchema (schema1 )
1761+ .build ();
1762+ ConnectionWorker connectionWorker = createConnectionWorker ()) {
1763+ long afterCreationMillis = Instant .now ().toEpochMilli ();
1764+ int msecResponseDelay = 300 ;
1765+ testBigQueryWrite .setResponseSleep (Duration .ofMillis (msecResponseDelay ));
1766+ testBigQueryWrite .addResponse (createAppendResponse (0 ));
1767+ testBigQueryWrite .addResponse (createAppendResponse (1 ));
1768+ testBigQueryWrite .addResponse (createAppendResponse (2 ));
1769+
1770+ // First request: no prior responses or wait times have been recorded yet.
1771+ ApiFuture <AppendRowsResponse > future1 =
1772+ sendTestMessage (connectionWorker , sw1 , createFooProtoRows (new String [] {"0" }), 0 );
1773+ future1 .get ();
1774+
1775+ AppendRowsRequest firstRequest = testBigQueryWrite .getAppendRequests ().get (0 );
1776+ long sizePerRequest = firstRequest .getProtoRows ().getSerializedSize ();
1777+ assertThat (firstRequest .getClientStats ().hasWindowStats ()).isTrue ();
1778+ ClientStats .WindowStats firstWindowStats = firstRequest .getClientStats ().getWindowStats ();
1779+ assertThat (firstWindowStats .hasMaxResponseLatencyMillis ()).isFalse ();
1780+ assertThat (firstWindowStats .hasAvgResponseLatencyMillis ()).isFalse ();
1781+ assertThat (firstWindowStats .hasLongestWaitNoResponseMillis ()).isFalse ();
1782+ assertThat (firstWindowStats .hasRequestsSentCount ()).isTrue ();
1783+ assertThat (firstWindowStats .getRequestsSentCount ()).isEqualTo (1 );
1784+ assertThat (firstWindowStats .hasResponsesReceivedCount ()).isFalse ();
1785+ assertThat (firstWindowStats .hasBytesSentCount ()).isTrue ();
1786+ assertThat (firstWindowStats .getBytesSentCount ()).isEqualTo (sizePerRequest );
1787+ assertThat (firstWindowStats .hasWindowStartTimeEpochMillis ()).isTrue ();
1788+ assertThat (firstWindowStats .getWindowStartTimeEpochMillis ()).isAtLeast (beforeCreationMillis );
1789+ assertThat (firstWindowStats .getWindowStartTimeEpochMillis ()).isAtMost (afterCreationMillis );
1790+ assertThat (firstWindowStats .hasWindowMillis ()).isTrue ();
1791+ assertThat (firstWindowStats .getWindowMillis ()).isAtLeast (0L );
1792+
1793+ // Second request: sent after firstRequest waited in-flight (>100ms) and received a response
1794+ // (>=300ms latency), so all expected WindowStats fields are now non-zero and populated.
1795+ ApiFuture <AppendRowsResponse > future2 =
1796+ sendTestMessage (connectionWorker , sw1 , createFooProtoRows (new String [] {"1" }), 1 );
1797+ future2 .get ();
1798+
1799+ AppendRowsRequest secondRequest = testBigQueryWrite .getAppendRequests ().get (1 );
1800+ assertThat (secondRequest .getClientStats ().hasWindowStats ()).isTrue ();
1801+ ClientStats .WindowStats secondWindowStats = secondRequest .getClientStats ().getWindowStats ();
1802+ assertThat (secondWindowStats .hasMaxResponseLatencyMillis ()).isTrue ();
1803+ assertThat (secondWindowStats .getMaxResponseLatencyMillis ()).isAtLeast (msecResponseDelay );
1804+ assertThat (secondWindowStats .hasAvgResponseLatencyMillis ()).isTrue ();
1805+ assertThat (secondWindowStats .getAvgResponseLatencyMillis ()).isAtLeast (msecResponseDelay );
1806+ assertThat (secondWindowStats .hasLongestWaitNoResponseMillis ()).isTrue ();
1807+ assertThat (secondWindowStats .getLongestWaitNoResponseMillis ()).isGreaterThan (0L );
1808+ assertThat (secondWindowStats .hasRequestsSentCount ()).isTrue ();
1809+ assertThat (secondWindowStats .getRequestsSentCount ()).isEqualTo (2 );
1810+ assertThat (secondWindowStats .hasResponsesReceivedCount ()).isTrue ();
1811+ assertThat (secondWindowStats .getResponsesReceivedCount ()).isEqualTo (1 );
1812+ assertThat (secondWindowStats .hasBytesSentCount ()).isTrue ();
1813+ assertThat (secondWindowStats .getBytesSentCount ()).isEqualTo (2 * sizePerRequest );
1814+ assertThat (secondWindowStats .hasWindowStartTimeEpochMillis ()).isTrue ();
1815+ assertThat (secondWindowStats .getWindowStartTimeEpochMillis ())
1816+ .isEqualTo (firstWindowStats .getWindowStartTimeEpochMillis ());
1817+ assertThat (secondWindowStats .hasWindowMillis ()).isTrue ();
1818+ assertThat (secondWindowStats .getWindowMillis ()).isAtLeast (msecResponseDelay );
1819+
1820+ // Trigger a health check window rollover and verify WindowStats resets for the new window.
1821+ connectionWorker .setTestOnlyHealthCheckInterval (Duration .ofMillis (100 ));
1822+ Thread .sleep (250 );
1823+
1824+ ApiFuture <AppendRowsResponse > future3 =
1825+ sendTestMessage (connectionWorker , sw1 , createFooProtoRows (new String [] {"2" }), 2 );
1826+ future3 .get ();
1827+
1828+ AppendRowsRequest thirdRequest = testBigQueryWrite .getAppendRequests ().get (2 );
1829+ assertThat (thirdRequest .getClientStats ().hasWindowStats ()).isTrue ();
1830+ ClientStats .WindowStats thirdWindowStats = thirdRequest .getClientStats ().getWindowStats ();
1831+ assertThat (thirdWindowStats .hasMaxResponseLatencyMillis ()).isFalse ();
1832+ assertThat (thirdWindowStats .hasAvgResponseLatencyMillis ()).isFalse ();
1833+ assertThat (thirdWindowStats .hasLongestWaitNoResponseMillis ()).isFalse ();
1834+ assertThat (thirdWindowStats .hasRequestsSentCount ()).isTrue ();
1835+ assertThat (thirdWindowStats .getRequestsSentCount ()).isEqualTo (1 );
1836+ assertThat (thirdWindowStats .hasResponsesReceivedCount ()).isFalse ();
1837+ assertThat (thirdWindowStats .hasBytesSentCount ()).isTrue ();
1838+ assertThat (thirdWindowStats .getBytesSentCount ()).isEqualTo (sizePerRequest );
1839+ assertThat (thirdWindowStats .hasWindowStartTimeEpochMillis ()).isTrue ();
1840+ assertThat (thirdWindowStats .getWindowStartTimeEpochMillis ())
1841+ .isGreaterThan (secondWindowStats .getWindowStartTimeEpochMillis ());
1842+ assertThat (thirdWindowStats .hasWindowMillis ()).isTrue ();
1843+ assertThat (thirdWindowStats .getWindowMillis ()).isAtLeast (0L );
17251844 }
17261845 }
17271846}
0 commit comments