Skip to content

[Pipe] Support receiver-side async takeover for TsFile conversion - #18421

Open
Caideyipi wants to merge 2 commits into
apache:masterfrom
Caideyipi:pipe-tsfile-async-conversion
Open

[Pipe] Support receiver-side async takeover for TsFile conversion#18421
Caideyipi wants to merge 2 commits into
apache:masterfrom
Caideyipi:pipe-tsfile-async-conversion

Conversation

@Caideyipi

Copy link
Copy Markdown
Collaborator

What does this PR do?

Adds resilient receiver-side handling for Pipe TsFile type-conversion failures.

  • Adds connector.exception.data.convert-on-type-mismatch.tsfile.async-load (and the sink.* alias), defaulting to true, and propagates the setting with a stable conversion task ID.
  • Deduplicates seal and retry requests by deterministic task ID. Retries reuse the existing task status and context instead of starting duplicate conversions.
  • On a type-mismatch conversion failure, atomically hands the TsFile and mod file to the receiver active-load async directory and marks the task receiver-owned.
  • Treats temporary unavailability and OOM as a pause of local conversion only; input files and parser progress remain retryable.
  • Reuses Pipe parser state and checkpoints across retries, including iterators, pending tablets, page and chunk metadata, file index, and pending statements.
  • When async takeover is disabled, sender retries return the current task status and never create a second conversion task.

Tests

  • Added or updated parser, receiver, transfer-request, active-load path and scanner, load utility, and conversion-task-manager unit tests; 85 targeted tests passed.
  • DataNode compilation passed.
  • Spotless and git diff --check passed.

Comment on lines +547 to +553
final String conversionTaskId =
PipeTransferTsFileSealWithModReq.generateConversionTaskId(
sinkTaskId,
events,
dataBaseName,
outputIndex,
Objects.nonNull(modFile) && clientManager.supportModsIfIsDataNodeReceiver());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generate the conversion key only when necessary (shouldAsyncLoadTsFileOnTypeMismatch == true)?

The calculation seems costly.

Comment on lines +387 to +389
case PIPE_CONVERSION_TASK_ID_KEY:
if (value == null || value.isEmpty()) {
throw new SemanticException(StorageEngineMessages.USER_NAME_MUST_NOT_BE_EMPTY);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong message

Comment on lines +276 to +279
final boolean asyncLoadOnTypeMismatch =
Boolean.parseBoolean(
attributes.getOrDefault(
ActiveLoadPathHelper.PIPE_ASYNC_LOAD_ON_TYPE_MISMATCH_KEY, "true"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is asyncLoadOnTypeMismatch used by ActiveLoader?

Comment on lines +388 to +396
private String getConversionTaskId(final ActiveLoadPendingQueue.ActiveLoadEntry entry) {
final File tsFile = new File(entry.getFile());
final File pendingDir =
entry.getPendingDir() == null
? ActiveLoadPathHelper.findPendingDirectory(tsFile)
: new File(entry.getPendingDir());
return ActiveLoadPathHelper.parseAttributes(tsFile, pendingDir)
.get(ActiveLoadPathHelper.PIPE_CONVERSION_TASK_ID_KEY);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to store the taskId in the entry?

.getTsFiles()
.forEach(
tsFile -> {
org.apache.iotdb.commons.utils.FileUtils.deleteFileIfExist(tsFile);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind the import

});
}

private static final class TableConversionContext implements AutoCloseable {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TableConversionContext -> TabletConversionContext

Comment on lines +295 to +300
int contextCount = 0;
for (final Task task : TASKS.values()) {
if (task.conversionContext != null) {
contextCount++;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to use an atomic var?

Comment on lines +330 to +332
} catch (final Exception ignored) {
// Best-effort cleanup. The task state is still authoritative.
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May leave a warn log for debugging.

Comment on lines +292 to +301
private static boolean isExistingTaskComplete(
final File transferDir, final List<File> sourceFiles) {
if (!transferDir.isDirectory()) {
return false;
}

final File[] targetFiles = transferDir.listFiles(File::isFile);
if (targetFiles == null || targetFiles.length == 0) {
return false;
}

@jt2594838 jt2594838 Aug 7, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does a non-existent or empty dir indicate a task is not complete?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants