github-actions[bot] commented on code in PR #68032:
URL: https://github.com/apache/doris/pull/68032#discussion_r4259921833
##########
be/src/exec/spill/spill_file_manager.cpp:
##########
@@ -345,68 +489,153 @@ void SpillFileManager::delete_spill_file(SpillFileSPtr
spill_file) {
void SpillFileManager::delete_query_spill_directory(const std::string&
query_id,
SpillDataDir* data_dir) {
- PendingQuerySpillDirectory pending_directory {
- .query_dir = data_dir->get_spill_data_path(query_id),
+ if (data_dir->is_remote()) {
+ // Remote spill is deleted per spill file, when the SpillFile is
destroyed (retried by the
+ // GC thread when that failed); nothing is deleted by directory on
object storage.
+ return;
+ }
+ PendingSpillDirectory pending_directory {
+ .failed_count = 0,
+ .dir = data_dir->get_spill_data_path(query_id),
+ .data_dir = data_dir,
+ .fs = nullptr,
+ .charged_bytes = 0,
+ .persisted_bytes = 0,
};
- auto status = _try_delete_query_spill_directory(pending_directory);
+ auto status = _try_delete_spill_directory(pending_directory);
if (!status.ok()) {
- std::lock_guard lock(_pending_query_spill_directories_mutex);
++pending_directory.failed_count;
-
_pending_query_spill_directories.emplace_back(std::move(pending_directory));
+ _add_pending_directory(std::move(pending_directory));
+ }
+}
+
+void SpillFileManager::retry_spill_directory_deletion(SpillDataDir* data_dir,
io::FileSystemSPtr fs,
+ std::string dir, int64_t
charged_bytes,
+ int64_t persisted_bytes)
{
+ _add_pending_directory({.failed_count = 1,
+ .dir = std::move(dir),
+ .data_dir = data_dir,
+ .fs = std::move(fs),
+ .charged_bytes = charged_bytes,
+ .persisted_bytes = persisted_bytes});
+}
+
+void SpillFileManager::_add_pending_directory(PendingSpillDirectory
pending_directory) {
+ auto is_under = [](const std::string& dir, const std::string& ancestor) {
+ return dir.size() > ancestor.size() && dir.starts_with(ancestor) &&
+ dir[ancestor.size()] == '/';
+ };
+ std::lock_guard lock(_pending_spill_directories_mutex);
+ for (auto& pending : _pending_spill_directories) {
+ if (pending.data_dir == pending_directory.data_dir && pending.fs ==
pending_directory.fs &&
+ (pending.dir == pending_directory.dir ||
+ is_under(pending_directory.dir, pending.dir))) {
+ // Deleting the pending ancestor deletes these objects too.
+ pending.charged_bytes += pending_directory.charged_bytes;
+ pending.persisted_bytes += pending_directory.persisted_bytes;
+ return;
+ }
}
+ std::erase_if(_pending_spill_directories, [&](const PendingSpillDirectory&
pending) {
+ if (pending.data_dir == pending_directory.data_dir && pending.fs ==
pending_directory.fs &&
+ is_under(pending.dir, pending_directory.dir)) {
+ pending_directory.charged_bytes += pending.charged_bytes;
+ pending_directory.persisted_bytes += pending.persisted_bytes;
+ return true;
+ }
+ return false;
+ });
+ _pending_spill_directories.emplace_back(std::move(pending_directory));
}
-Status SpillFileManager::_try_delete_query_spill_directory(
- const PendingQuerySpillDirectory& pending_directory) {
+Status SpillFileManager::_try_delete_spill_directory(
+ const PendingSpillDirectory& pending_directory) {
{
- std::lock_guard lock(_pending_query_spill_directories_mutex);
- if
(_external_spill_directory_leases.contains(pending_directory.query_dir)) {
+ std::lock_guard lock(_pending_spill_directories_mutex);
+ if (_external_spill_directory_leases.contains(pending_directory.dir)) {
return Status::InternalError("external spill directory is still in
use: {}",
- pending_directory.query_dir);
+ pending_directory.dir);
}
}
DBUG_EXECUTE_IF("fault_inject::spill_file_manager::delete_query_spill_directory",
{
return Status::Error<INTERNAL_ERROR>("injected query spill directory
deletion failure");
});
- const auto& fs = io::global_local_filesystem();
- return fs->delete_directory(pending_directory.query_dir);
+ // Every pending directory was written through the store, which was ready
by then and
+ // never goes back.
+ auto fs = pending_directory.fs != nullptr ? pending_directory.fs
+ :
pending_directory.data_dir->fs();
+ DORIS_CHECK(fs != nullptr) << "spill store " <<
pending_directory.data_dir->path()
+ << " is not ready";
+ return fs->delete_directory(pending_directory.dir);
+}
+
+void SpillFileManager::_retry_pending_local_spill_directories() {
+ MonotonicStopWatch watch;
+ watch.start();
+ _retry_pending_spill_directories(watch,
std::numeric_limits<int64_t>::max(),
+ /*retry_remote=*/false);
}
-void SpillFileManager::_retry_pending_query_spill_directories() {
- std::vector<PendingQuerySpillDirectory> pending_directories;
+void SpillFileManager::_retry_pending_spill_directories(const
MonotonicStopWatch& watch,
+ int64_t
max_work_time_ns,
+ bool retry_remote) {
+ std::vector<PendingSpillDirectory> pending_directories;
{
- std::lock_guard lock(_pending_query_spill_directories_mutex);
- pending_directories.swap(_pending_query_spill_directories);
+ std::lock_guard lock(_pending_spill_directories_mutex);
+ pending_directories.swap(_pending_spill_directories);
}
- DBUG_EXECUTE_IF(
-
"fault_inject::spill_file_manager::retry_pending_query_spill_directories_after_drain",
- { DBUG_RUN_CALLBACK(); });
+
DBUG_EXECUTE_IF("fault_inject::spill_file_manager::retry_pending_spill_directories_after_drain",
+ { DBUG_RUN_CALLBACK(); });
// Limit repeated warnings for a persistently unavailable directory while
retaining it for
// every subsequent retry.
constexpr int log_interval = 5;
- std::vector<PendingQuerySpillDirectory> failed_directories;
+ std::vector<PendingSpillDirectory> untried_directories;
+ std::vector<PendingSpillDirectory> failed_directories;
+ size_t tried = 0;
for (auto& pending_directory : pending_directories) {
- auto status = _try_delete_query_spill_directory(pending_directory);
+ // A remote deletion can wait for a request timeout: after the time
slice is used up the
+ // remaining directories are queued again without being tried, ahead
of the ones that
+ // just failed, so that a slow failing directory cannot starve the
others.
+ if ((!retry_remote && pending_directory.data_dir->is_remote()) ||
+ (tried > 0 && watch.elapsed_time() >= max_work_time_ns)) {
+ untried_directories.emplace_back(std::move(pending_directory));
+ continue;
+ }
+ ++tried;
+ auto status = _try_delete_spill_directory(pending_directory);
if (status.ok()) {
+
pending_directory.data_dir->release(pending_directory.charged_bytes);
+
pending_directory.data_dir->release_persisted_bytes(pending_directory.persisted_bytes);
continue;
}
+ if (pending_directory.data_dir->is_remote()) {
+ auto reconcile_status = reconcile_remote_spill_bytes(
+ pending_directory.data_dir, pending_directory.fs,
pending_directory.dir,
+ &pending_directory.persisted_bytes);
+ if (!reconcile_status.ok()) {
+ LOG_EVERY_T(WARNING, 60)
+ << "failed to reconcile remote spill bytes after
deletion: "
+ << reconcile_status;
+ }
+ }
+
++pending_directory.failed_count;
if (pending_directory.failed_count % log_interval == 0) {
LOG(WARNING) << fmt::format(
- "failed to retry deleting spill query directory, dir {},
error: {}",
- pending_directory.query_dir, status.to_string());
+ "failed to retry deleting spill directory, dir {}, error:
{}",
+ pending_directory.dir, status.to_string());
}
failed_directories.emplace_back(std::move(pending_directory));
}
- if (!failed_directories.empty()) {
- std::lock_guard lock(_pending_query_spill_directories_mutex);
- for (auto& pending_directory : failed_directories) {
-
_pending_query_spill_directories.emplace_back(std::move(pending_directory));
- }
+ for (auto& pending_directory : untried_directories) {
+ _add_pending_directory(std::move(pending_directory));
Review Comment:
[P2] Preserve the backlog without quadratic reinsertion. When remote DELETEs
fail, partitioned spills leave many unrelated per-file prefixes here (remote
query cleanup does not coalesce them). After the first slow retry exhausts the
GC time slice, this loop still re-adds every untouched entry through
_add_pending_directory, which scans the growing vector twice. N surviving
prefixes therefore require N*(N-1) entry visits outside the time budget; 10,000
prefixes cause roughly 100 million visits under the pending-directory mutex on
every GC pass. stop() and the destructor repeat this even while skipping all
remote requests. Preserve/rotate the already-deduplicated backlog in bulk, and
merge concurrent additions without pairwise rescanning; include a large-backlog
case alongside the current three-entry test. This is separate from the existing
network-timeout retry finding.
##########
fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java:
##########
@@ -274,6 +274,10 @@ private static void logAuditLogImpl(ConnectContext ctx,
String origStmt, Stateme
statistics.getSpillWriteBytesToLocalStorage())
.setSpillReadBytesFromLocalStorage(statistics == null ? 0 :
statistics.getSpillReadBytesFromLocalStorage())
+ // Remote spill totals arrive in final BE reports;
WorkloadRuntimeStatusMgr
+ // waits for the participants, with a bounded fallback if a BE
never reports.
+ .setSpillWriteBytesToRemoteStorage(0)
Review Comment:
[P2] Retain final remote totals until audit consumes them. A MySQL query can
finish its BE execution while the FE is still sending the last buffered rows to
a slow client. After its final statistics RPC succeeds,
RuntimeQueryStatisticsMgr removes the query and an otherwise idle BE stops
reporting; clearReportTimeoutBeStatistics then drops its entire FE entry after
the default 60 seconds, even though the query is still registered. A 90-second
client stall is allowed by the default 600-second net_write_timeout. When
result delivery finally completes, these zero-seeded fields are audited after
the participant wait expires: the final report already arrived and was evicted,
and PQueryStatistics carries no remote spill fallback. Retain final statistics
through result delivery and pending audit consumption, or transport the remote
pair in the final result statistics. Add an
early-final-report/slow-result-delivery test; the current tests only delay
final reports until after audit is queue
d.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]