Skip to content

Commit c0dd224

Browse files
committed
[BUG] Stop the Elasticsearch async ForceFlush reporting success without waiting
ForceFlush() returned true straight away when async export was enabled, so a caller had no way to know whether anything had been delivered. It now waits on the sessions that were in flight when it was called. Sessions are identified rather than counted: each export takes the next id and joins a set, and the wait ends when no id below the entry watermark is left, so a completion cannot satisfy a flush that started after it. Counting alone lets a later export stand in for an earlier one. The wait uses one deadline for the call, so a wakeup that is not a completion resumes against what is left instead of restarting the wait, and the result is the predicate rather than the leftover duration. The ids are uint64_t rather than size_t. The wait compares them by order, which only holds while they keep increasing, and a 32 bit counter reaches its end in days at a rate this exporter is meant to sustain; past that the next watermark is small enough for a still running session to satisfy it. AsyncResponseHandler reports at most once, through a compare and exchange. The HTTP client can deliver both a response and a terminal session event for one request, and the exporter counts one finished session per export. An export registers its session before anything that can return, so a flush asked from the moment the records arrive waits for them, and a guard reports through the same completion on every early exit. Without it a return added later would strand a waiter on a session that can never finish. What a true return means is documented on the declaration, and it is weaker than "the session ended". Both completion paths publish the outcome before the session is torn down: OnResponse calls CompleteOnce() ahead of its logging, and the handler destructor calls it ahead of FinishSession(). So a true return means every snapshotted export has reported an outcome, and transport cleanup may still be running. Reported in #4336 Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
1 parent 5af96de commit c0dd224

5 files changed

Lines changed: 1083 additions & 94 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ Increment the:
5656
deprecated C headers (`stdint.h`, `stddef.h`, `stdlib.h`, `string.h`,
5757
`stdio.h`, `ctype.h`, `limits.h`, `assert.h`) with their C++ equivalents
5858
([#4349](https://github.com/open-telemetry/opentelemetry-cpp/pull/4349))
59+
* [BUG] Stop the Elasticsearch async ForceFlush reporting success without
60+
waiting for the sessions it was asked about
61+
[#4337](https://github.com/open-telemetry/opentelemetry-cpp/pull/4337)
5962

6063
* [CONFIGURATION] Add SDK component builder interfaces to the registry
6164
[#4358](https://github.com/open-telemetry/opentelemetry-cpp/issues/4358)

‎exporters/elasticsearch/CMakeLists.txt‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,10 @@ if(BUILD_TESTING)
5555
TARGET es_log_record_exporter_test
5656
TEST_PREFIX exporter.
5757
TEST_LIST es_log_record_exporter_test)
58+
59+
# AnIndefiniteFlushReturnsOnceEverythingIsFinished exercises the branch that
60+
# waits without a deadline, so a regression there does not fail, it stops.
61+
# CTest's default bound is 25 minutes, which is a long time to spend learning
62+
# that.
63+
set_tests_properties(${es_log_record_exporter_test} PROPERTIES TIMEOUT 30)
5864
endif() # BUILD_TESTING

‎exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_record_exporter.h‎

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616

1717
#ifdef ENABLE_ASYNC_EXPORT
1818
# include <condition_variable>
19-
# include <cstddef>
19+
# include <cstdint>
2020
# include <mutex>
21+
# include <set>
2122
#endif
2223

2324
OPENTELEMETRY_BEGIN_NAMESPACE
@@ -122,8 +123,16 @@ class ElasticsearchLogRecordExporter final : public opentelemetry::sdk::logs::Lo
122123

123124
/**
124125
* Force flush the exporter.
126+
*
127+
* Waits for the asynchronous sessions already started when the call was made, bounded by the
128+
* caller's timeout rather than by the exporter's response timeout.
129+
*
125130
* @param timeout an option timeout, default to max.
126-
* @return return true when all data are exported, and false when timeout
131+
* @return true when each of those exports has reported a terminal outcome, false on timeout.
132+
* The outcome is published before the session is torn down, so a true return does not
133+
* mean FinishSession() has run. Nor does it mean the batch reached Elasticsearch: a
134+
* failed export reports too, through the internal log. Surfacing that here is
135+
* [#3075](https://github.com/open-telemetry/opentelemetry-cpp/issues/3075).
127136
*/
128137
bool ForceFlush(
129138
std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override;
@@ -149,11 +158,17 @@ class ElasticsearchLogRecordExporter final : public opentelemetry::sdk::logs::Lo
149158
#ifdef ENABLE_ASYNC_EXPORT
150159
struct SynchronizationData
151160
{
152-
std::atomic<std::size_t> session_counter_{0};
153-
std::atomic<std::size_t> finished_session_counter_{0};
161+
// Identified rather than counted, so a completion can only satisfy its own waiter. Guarded by
162+
// force_flush_cv_m, the mutex the wait uses, so starting and finishing cannot interleave with a
163+
// waiter's snapshot or predicate.
164+
//
165+
// Sized independently of the platform's size_t. The wait compares ids by order, which only
166+
// holds while they keep increasing, and a 32 bit counter reaches its end in days at a rate
167+
// this exporter is meant to sustain.
168+
std::uint64_t next_session_id{0};
169+
std::set<std::uint64_t> running_sessions;
154170
std::condition_variable force_flush_cv;
155171
std::mutex force_flush_cv_m;
156-
std::recursive_mutex force_flush_m;
157172
};
158173
nostd::shared_ptr<SynchronizationData> synchronization_data_;
159174
#endif

0 commit comments

Comments
 (0)