2525#include < string>
2626#include < thread>
2727#include < utility>
28+ #include < vector>
2829#include " nlohmann/json.hpp"
2930
3031namespace sdklogs = opentelemetry::sdk::logs;
@@ -195,7 +196,7 @@ class FakeRequest : public http_client::Request
195196 void SetRetryPolicy (const http_client::RetryPolicy &) noexcept override {}
196197};
197198
198- using EventScript = std::function<void (http_client::EventHandler &)>;
199+ using EventScript = std::function<void (const std::shared_ptr< http_client::EventHandler> &)>;
199200
200201class FakeSession : public http_client ::Session
201202{
@@ -207,12 +208,8 @@ class FakeSession : public http_client::Session
207208 }
208209 void SendRequest (std::shared_ptr<http_client::EventHandler> handler) noexcept override
209210 {
210- last_handler_ = handler;
211- script_ (*handler);
211+ script_ (handler);
212212 }
213-
214- // Kept so a test can deliver a completion after the flush has started waiting.
215- std::shared_ptr<http_client::EventHandler> last_handler_;
216213 bool IsSessionActive () noexcept override { return false ; }
217214 bool CancelSession () noexcept override { return true ; }
218215 bool FinishSession () noexcept override { return true ; }
@@ -227,11 +224,8 @@ class FakeHttpClient : public http_client::HttpClient
227224 explicit FakeHttpClient (EventScript script) : script_(std::move(script)) {}
228225 std::shared_ptr<http_client::Session> CreateSession (nostd::string_view) noexcept override
229226 {
230- last_session_ = std::make_shared<FakeSession>(script_);
231- return last_session_;
227+ return std::make_shared<FakeSession>(script_);
232228 }
233-
234- std::shared_ptr<FakeSession> last_session_;
235229 bool CancelAllSessions () noexcept override { return true ; }
236230 bool FinishAllSessions () noexcept override { return true ; }
237231 void SetMaxSessionsPerConnection (std::size_t ) noexcept override {}
@@ -289,7 +283,7 @@ void ExportOnce(logs_exporter::ElasticsearchLogRecordExporter &exporter)
289283// so when the caller's deadline runs out rather than when the response timeout does.
290284TEST (ElasticsearchForceFlushTests, ReportsFailureWhenTheFlushDoesNotComplete)
291285{
292- auto fixture = MakeExporter ([](http_client::EventHandler &) {});
286+ auto fixture = MakeExporter ([](const std::shared_ptr< http_client::EventHandler> &) {});
293287 ExportOnce (*fixture.exporter );
294288
295289 const auto start = std::chrono::steady_clock::now ();
@@ -304,7 +298,7 @@ TEST(ElasticsearchForceFlushTests, ReportsFailureWhenTheFlushDoesNotComplete)
304298// Nothing outstanding, so there is nothing to wait for.
305299TEST (ElasticsearchForceFlushTests, ReturnsImmediatelyWithNothingInFlight)
306300{
307- auto fixture = MakeExporter ([](http_client::EventHandler &) {});
301+ auto fixture = MakeExporter ([](const std::shared_ptr< http_client::EventHandler> &) {});
308302
309303 const auto start = std::chrono::steady_clock::now ();
310304 EXPECT_TRUE (fixture.exporter ->ForceFlush (std::chrono::milliseconds{20 }));
@@ -318,9 +312,9 @@ TEST(ElasticsearchForceFlushTests, ReturnsImmediatelyWithNothingInFlight)
318312// completion is already published when the predicate is first evaluated.
319313TEST (ElasticsearchForceFlushTests, SucceedsWhenTheSessionFinishedBeforeTheWait)
320314{
321- auto fixture = MakeExporter ([](http_client::EventHandler &handler) {
315+ auto fixture = MakeExporter ([](const std::shared_ptr< http_client::EventHandler> &handler) {
322316 FakeResponse response (200 , kAcceptedBody );
323- handler. OnResponse (response);
317+ handler-> OnResponse (response);
324318 });
325319 ExportOnce (*fixture.exporter );
326320
@@ -332,13 +326,14 @@ TEST(ElasticsearchForceFlushTests, SucceedsWhenTheSessionFinishedBeforeTheWait)
332326TEST (ElasticsearchForceFlushTests, PartialCompletionIsNotSuccess)
333327{
334328 bool respond = true ;
335- auto fixture = MakeExporter ([&respond](http_client::EventHandler &handler) {
336- if (respond)
337- {
338- FakeResponse response (200 , kAcceptedBody );
339- handler.OnResponse (response);
340- }
341- });
329+ auto fixture =
330+ MakeExporter ([&respond](const std::shared_ptr<http_client::EventHandler> &handler) {
331+ if (respond)
332+ {
333+ FakeResponse response (200 , kAcceptedBody );
334+ handler->OnResponse (response);
335+ }
336+ });
342337 ExportOnce (*fixture.exporter );
343338 respond = false ;
344339 ExportOnce (*fixture.exporter );
@@ -350,10 +345,14 @@ TEST(ElasticsearchForceFlushTests, PartialCompletionIsNotSuccess)
350345// the half the inline scripts above cannot reach.
351346TEST (ElasticsearchForceFlushTests, ACompletionAfterTheWaiterParksWakesIt)
352347{
353- auto fixture = MakeExporter ([](http_client::EventHandler &) {});
348+ // Held by the test, not by the fakes: a handler owns its session, so a session that also owned
349+ // its handler would be a reference cycle and leak.
350+ std::shared_ptr<http_client::EventHandler> captured;
351+ auto fixture =
352+ MakeExporter ([&captured](const std::shared_ptr<http_client::EventHandler> &handler) {
353+ captured = handler;
354+ });
354355 ExportOnce (*fixture.exporter );
355- ASSERT_NE (fixture.client ->last_session_ , nullptr );
356- const auto captured = fixture.client ->last_session_ ->last_handler_ ;
357356 ASSERT_NE (captured, nullptr );
358357
359358 std::thread responder ([&captured] {
@@ -366,4 +365,27 @@ TEST(ElasticsearchForceFlushTests, ACompletionAfterTheWaiterParksWakesIt)
366365 responder.join ();
367366 EXPECT_TRUE (flushed);
368367}
368+
369+ // A second caller gets its own deadline. Serialising the calls is fine, making the second one
370+ // wait out the first one's is not, since its timeout would mean nothing.
371+ TEST (ElasticsearchForceFlushTests, AConcurrentFlushKeepsItsOwnDeadline)
372+ {
373+ std::vector<std::shared_ptr<http_client::EventHandler>> kept;
374+ auto fixture = MakeExporter ([&kept](const std::shared_ptr<http_client::EventHandler> &handler) {
375+ kept.push_back (handler);
376+ });
377+ ExportOnce (*fixture.exporter );
378+
379+ std::thread slow ([&fixture] { fixture.exporter ->ForceFlush (std::chrono::seconds{3 }); });
380+ std::this_thread::sleep_for (std::chrono::milliseconds{100 });
381+
382+ const auto start = std::chrono::steady_clock::now ();
383+ EXPECT_FALSE (fixture.exporter ->ForceFlush (std::chrono::milliseconds{20 }));
384+ const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
385+ std::chrono::steady_clock::now () - start)
386+ .count ();
387+ slow.join ();
388+
389+ EXPECT_LT (ms, 1000 ) << " waited behind the first caller instead of its own deadline" ;
390+ }
369391#endif // ENABLE_ASYNC_EXPORT
0 commit comments