@@ -123,8 +123,6 @@ public void uncaughtException(Thread t, Throwable e) {
123
123
private MetricRecorder mockMetricRecorder = mock (MetricRecorder .class ,
124
124
delegatesTo (new MetricRecorderImpl ()));
125
125
126
- private static final long RECONNECT_BACKOFF_DELAY_NANOS = TimeUnit .SECONDS .toNanos (1 );
127
-
128
126
private final LinkedList <String > callbackInvokes = new LinkedList <>();
129
127
private final InternalSubchannel .Callback mockInternalSubchannelCallback =
130
128
new InternalSubchannel .Callback () {
@@ -1471,10 +1469,60 @@ private void createInternalSubchannel(boolean reconnectDisabled,
1471
1469
}
1472
1470
1473
1471
@ Test
1474
- public void subchannelStateChanges_triggersMetrics_disconnectionOnly () {
1475
- // 1. Mock the backoff policy
1472
+ public void subchannelStateChanges_triggersAttemptFailedMetric () {
1473
+ // 1. Setup: Standard subchannel initialization
1474
+ when (mockBackoffPolicyProvider .get ()).thenReturn (mockBackoffPolicy );
1475
+ SocketAddress addr = mock (SocketAddress .class );
1476
+ Attributes eagAttributes = Attributes .newBuilder ()
1477
+ .set (NameResolver .ATTR_BACKEND_SERVICE , BACKEND_SERVICE )
1478
+ .set (LoadBalancer .ATTR_LOCALITY_NAME , LOCALITY )
1479
+ .set (GrpcAttributes .ATTR_SECURITY_LEVEL , SECURITY_LEVEL )
1480
+ .build ();
1481
+ List <EquivalentAddressGroup > addressGroups =
1482
+ Arrays .asList (new EquivalentAddressGroup (Arrays .asList (addr ), eagAttributes ));
1483
+ InternalLogId logId = InternalLogId .allocate ("Subchannel" , /*details=*/ AUTHORITY );
1484
+ ChannelTracer subchannelTracer = new ChannelTracer (logId , 10 ,
1485
+ fakeClock .getTimeProvider ().currentTimeNanos (), "Subchannel" );
1486
+ LoadBalancer .CreateSubchannelArgs createSubchannelArgs =
1487
+ LoadBalancer .CreateSubchannelArgs .newBuilder ().setAddresses (addressGroups ).build ();
1488
+ internalSubchannel = new InternalSubchannel (
1489
+ createSubchannelArgs , AUTHORITY , USER_AGENT , mockBackoffPolicyProvider ,
1490
+ mockTransportFactory , fakeClock .getScheduledExecutorService (),
1491
+ fakeClock .getStopwatchSupplier (), syncContext , mockInternalSubchannelCallback , channelz ,
1492
+ CallTracer .getDefaultFactory ().create (), subchannelTracer , logId ,
1493
+ new ChannelLoggerImpl (subchannelTracer , fakeClock .getTimeProvider ()),
1494
+ Collections .emptyList (), AUTHORITY , mockMetricRecorder
1495
+ );
1496
+
1497
+ // --- Action: Simulate the "connecting to failed" transition ---
1498
+ // a. Initiate the connection attempt. The subchannel is now CONNECTING.
1499
+ internalSubchannel .obtainActiveTransport ();
1500
+ MockClientTransportInfo transportInfo = transports .poll ();
1501
+ assertNotNull ("A connection attempt should have been made" , transportInfo );
1502
+
1503
+ // b. Fail the transport before it can signal `transportReady()`.
1504
+ transportInfo .listener .transportShutdown (
1505
+ Status .INTERNAL .withDescription ("Simulated connect failure" ));
1506
+ fakeClock .runDueTasks (); // Process the failure event
1507
+
1508
+ // --- Verification ---
1509
+ // a. Verify that the "connection_attempts_failed" metric was recorded exactly once.
1510
+ verify (mockMetricRecorder ).addLongCounter (
1511
+ eqMetricInstrumentName ("grpc.subchannel.connection_attempts_failed" ),
1512
+ eq (1L ),
1513
+ eq (Arrays .asList (AUTHORITY )),
1514
+ eq (Arrays .asList (BACKEND_SERVICE , LOCALITY ))
1515
+ );
1516
+
1517
+ // b. Verify no other metrics were recorded. This confirms it wasn't incorrectly
1518
+ // logged as a success, disconnection, or open connection.
1519
+ verifyNoMoreInteractions (mockMetricRecorder );
1520
+ }
1521
+
1522
+ @ Test
1523
+ public void subchannelStateChanges_triggersSuccessAndDisconnectMetrics () {
1524
+ // 1. Mock the backoff policy (needed for subchannel creation)
1476
1525
when (mockBackoffPolicyProvider .get ()).thenReturn (mockBackoffPolicy );
1477
- when (mockBackoffPolicy .nextBackoffNanos ()).thenReturn (RECONNECT_BACKOFF_DELAY_NANOS );
1478
1526
1479
1527
// 2. Setup Subchannel with attributes
1480
1528
SocketAddress addr = mock (SocketAddress .class );
@@ -1500,15 +1548,16 @@ public void subchannelStateChanges_triggersMetrics_disconnectionOnly() {
1500
1548
Collections .emptyList (), AUTHORITY , mockMetricRecorder
1501
1549
);
1502
1550
1503
- // --- Action ---
1551
+ // --- Action: Successful connection ---
1504
1552
internalSubchannel .obtainActiveTransport ();
1505
1553
MockClientTransportInfo transportInfo = transports .poll ();
1506
1554
assertNotNull (transportInfo );
1507
1555
transportInfo .listener .transportReady ();
1508
- fakeClock .runDueTasks ();
1556
+ fakeClock .runDueTasks (); // Process the successful connection
1509
1557
1510
- transportInfo .listener .transportShutdown (Status .UNAVAILABLE );
1511
- fakeClock .runDueTasks ();
1558
+ // --- Action: Transport is shut down by the "peer" ---
1559
+ transportInfo .listener .transportShutdown (Status .UNAVAILABLE .withDescription ("Peer Pressure" ));
1560
+ fakeClock .runDueTasks (); // Process the shutdown
1512
1561
1513
1562
// --- Verification ---
1514
1563
InOrder inOrder = inOrder (mockMetricRecorder );
@@ -1527,14 +1576,7 @@ public void subchannelStateChanges_triggersMetrics_disconnectionOnly() {
1527
1576
eq (Arrays .asList ("privacy_and_integrity" , BACKEND_SERVICE , LOCALITY ))
1528
1577
);
1529
1578
1530
- inOrder .verify (mockMetricRecorder ).addLongCounter (
1531
- eqMetricInstrumentName ("grpc.subchannel.connection_attempts_failed" ),
1532
- eq (1L ),
1533
- eq (Arrays .asList (AUTHORITY )),
1534
- eq (Arrays .asList (BACKEND_SERVICE , LOCALITY ))
1535
- );
1536
-
1537
- // Verify disconnection and automatic failure metrics
1579
+ // Verify disconnection metrics
1538
1580
inOrder .verify (mockMetricRecorder ).addLongCounter (
1539
1581
eqMetricInstrumentName ("grpc.subchannel.disconnections" ),
1540
1582
eq (1L ),
0 commit comments