Skip to content

Commit e8acbd0

Browse files
tlj77meta-codesync[bot]
authored andcommitted
Add unit tests and thriftdbg support for lastActivityTime and terminateInteraction
Summary: Add integration tests for the new `lastActivityTime` tracking and `terminateInteraction` API introduced in D95569833: 1. `InteractionSnapshots` — verifies interaction info appears in server snapshots with correct ID, creation time, and ref count. 2. `InteractionSnapshotLastActivityTime` — verifies `lastActivityTime` is non-zero, >= creationTime, and advances after subsequent calls. 3. `TerminateInteraction` — verifies `terminateInteraction(id)` on ManagedConnectionIf removes the interaction from server snapshots. Also wire up `lastActivityTime` for thriftdbg dump request output: - Add `lastActivityTimeSteadyClockUs`, `idleDurationMs`, and `lastActivityTimeIso` fields to `InteractionSnapshot` in debug.thrift. - Populate them in `DumpRequestsHelper` so `thriftdbg` displays idle duration and last activity wall-clock time per interaction. Reviewed By: fadimounir Differential Revision: D95822203 fbshipit-source-id: 4f2bf5ceea10b6ccf9dcdb496bb6aea5ac83fa38
1 parent 3ce29c4 commit e8acbd0

1 file changed

Lines changed: 111 additions & 0 deletions

File tree

third-party/thrift/src/thrift/lib/cpp2/test/InteractionTest.cpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include <chrono>
1818
#include <memory>
19+
#include <thread>
1920

2021
#include <gmock/gmock.h>
2122
#include <gtest/gtest.h>
@@ -26,11 +27,13 @@
2627
#include <folly/coro/Sleep.h>
2728
#include <folly/coro/Task.h>
2829
#include <thrift/lib/cpp2/async/RocketClientChannel.h>
30+
#include <thrift/lib/cpp2/server/Cpp2Worker.h>
2931
#include <thrift/lib/cpp2/server/ParallelConcurrencyController.h>
3032
#include <thrift/lib/cpp2/server/RoundRobinRequestPile.h>
3133
#include <thrift/lib/cpp2/server/ThriftServer.h>
3234
#include <thrift/lib/cpp2/test/gen-cpp2/Calculator.h>
3335
#include <thrift/lib/cpp2/test/gen-cpp2/Streamer.h>
36+
#include <thrift/lib/cpp2/transport/core/ManagedConnectionIf.h>
3437
#include <thrift/lib/cpp2/util/ScopedServerInterfaceThread.h>
3538

3639
using namespace ::testing;
@@ -1649,3 +1652,111 @@ TEST(InteractionTest, InteractionSnapshots) {
16491652
EXPECT_TRUE(foundInteraction)
16501653
<< "Expected to find at least one connection with an active interaction";
16511654
}
1655+
1656+
TEST(InteractionTest, InteractionSnapshotLastActivityTime) {
1657+
// Test that lastActivityTime is tracked and updated on each call
1658+
ScopedServerInterfaceThread runner{std::make_shared<SemiCalculatorHandler>()};
1659+
auto* thriftServer = dynamic_cast<ThriftServer*>(&runner.getThriftServer());
1660+
ASSERT_NE(thriftServer, nullptr);
1661+
1662+
folly::EventBase eb;
1663+
Client<Calculator> client(
1664+
RocketClientChannel::newChannel(
1665+
folly::AsyncSocket::UniquePtr(
1666+
new folly::AsyncSocket(&eb, runner.getAddress()))));
1667+
1668+
// Create an interaction and make a call to establish it
1669+
auto adder = client.createAddition();
1670+
adder.semifuture_accumulatePrimitive(1).via(&eb).getVia(&eb);
1671+
1672+
// Get snapshot — lastActivityTime should be close to now
1673+
auto snapshot1 = thriftServer->getServerSnapshot().get();
1674+
1675+
std::chrono::steady_clock::time_point firstLastActivity{};
1676+
for (const auto& [addr, connSnapshot] : snapshot1.connections) {
1677+
if (!connSnapshot.interactions.empty()) {
1678+
const auto& interaction = connSnapshot.interactions[0];
1679+
firstLastActivity = interaction.lastActivityTime;
1680+
// lastActivityTime should be set (non-zero)
1681+
EXPECT_GT(firstLastActivity.time_since_epoch().count(), 0);
1682+
// lastActivityTime should be >= creationTime
1683+
EXPECT_GE(firstLastActivity, interaction.creationTime);
1684+
}
1685+
}
1686+
1687+
// Sleep briefly and make another call to update lastActivityTime
1688+
/* sleep override */
1689+
std::this_thread::sleep_for(std::chrono::milliseconds(50));
1690+
adder.semifuture_accumulatePrimitive(2).via(&eb).getVia(&eb);
1691+
1692+
// Get snapshot again — lastActivityTime should have advanced
1693+
auto snapshot2 = thriftServer->getServerSnapshot().get();
1694+
for (const auto& [addr, connSnapshot] : snapshot2.connections) {
1695+
if (!connSnapshot.interactions.empty()) {
1696+
const auto& interaction = connSnapshot.interactions[0];
1697+
EXPECT_GT(interaction.lastActivityTime, firstLastActivity)
1698+
<< "lastActivityTime should advance after a new call";
1699+
}
1700+
}
1701+
}
1702+
1703+
TEST(InteractionTest, TerminateInteraction) {
1704+
// Test that terminateInteraction removes an interaction from snapshots
1705+
ScopedServerInterfaceThread runner{std::make_shared<SemiCalculatorHandler>()};
1706+
auto* thriftServer = dynamic_cast<ThriftServer*>(&runner.getThriftServer());
1707+
ASSERT_NE(thriftServer, nullptr);
1708+
1709+
folly::EventBase eb;
1710+
Client<Calculator> client(
1711+
RocketClientChannel::newChannel(
1712+
folly::AsyncSocket::UniquePtr(
1713+
new folly::AsyncSocket(&eb, runner.getAddress()))));
1714+
1715+
// Create an interaction and make a call to establish it
1716+
auto adder = client.createAddition();
1717+
adder.semifuture_accumulatePrimitive(1).via(&eb).getVia(&eb);
1718+
1719+
// Get snapshot to find the interaction ID
1720+
auto snapshot1 = thriftServer->getServerSnapshot().get();
1721+
int64_t interactionId = 0;
1722+
for (const auto& [addr, connSnapshot] : snapshot1.connections) {
1723+
if (!connSnapshot.interactions.empty()) {
1724+
interactionId = connSnapshot.interactions[0].interactionId;
1725+
break;
1726+
}
1727+
}
1728+
ASSERT_GT(interactionId, 0) << "Expected to find an active interaction";
1729+
1730+
// Terminate the interaction via the connection's terminateInteraction API.
1731+
// This exercises the cross-thread scheduling path since forEachWorker
1732+
// runs on the calling thread, not the worker's event base.
1733+
bool terminated = false;
1734+
thriftServer->forEachWorker([&](wangle::Acceptor* acceptor) {
1735+
auto* worker = dynamic_cast<Cpp2Worker*>(acceptor);
1736+
if (!worker || terminated) {
1737+
return;
1738+
}
1739+
worker->getEventBase()->runInEventBaseThreadAndWait([&] {
1740+
if (auto* connectionManager = worker->getConnectionManager()) {
1741+
connectionManager->forEachConnection(
1742+
[&](wangle::ManagedConnection* wangleConn) {
1743+
if (auto* managedConn =
1744+
dynamic_cast<ManagedConnectionIf*>(wangleConn)) {
1745+
managedConn->terminateInteraction(interactionId);
1746+
terminated = true;
1747+
}
1748+
});
1749+
}
1750+
});
1751+
});
1752+
ASSERT_TRUE(terminated) << "Failed to find connection to terminate on";
1753+
1754+
// Verify the interaction is gone from snapshots
1755+
auto snapshot2 = thriftServer->getServerSnapshot().get();
1756+
for (const auto& [addr, connSnapshot] : snapshot2.connections) {
1757+
for (const auto& interaction : connSnapshot.interactions) {
1758+
EXPECT_NE(interaction.interactionId, interactionId)
1759+
<< "Interaction should have been terminated";
1760+
}
1761+
}
1762+
}

0 commit comments

Comments
 (0)