Populate _tags virtual column for S3Queue/ObjectStorageQueue - #108676
Conversation
Follow-up to ClickHouse#108625 (_etag). The _tags virtual column is declared for ObjectStorageQueue engines through the shared file-like virtual column list, but the read path in ObjectStorageQueueSource never populated it, so SELECT _tags always returned an empty map. Unlike _etag (returned for free by ListObjectsV2 during listing), object tags require a separate GetObjectTagging call, so they cannot just be forwarded from the listing result: the queue iterator lists objects with with_tags = false. The plain S3 engine fetches tags on demand only when _tags is requested (StorageObjectStorageSource::createReader). This mirrors that: when _tags is among the requested virtual columns, the queue source fetches object metadata with tags once per file, right after the reader is created, and forwards tags into the virtuals struct, the same way _etag is forwarded. This is S3-only: AzureObjectStorage::getObjectMetadata does not expose blob tags, so AzureQueue's _tags stays empty, the same as the plain Azure path today. A regression test is added to test_storage_s3_queue test_virtual_column_tags: it tags the object, reads _tags through a materialized view, and asserts the map is populated. The test fails before this change (empty map) and passes after. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pre-PR validation gate (click to expand)
Session id: cron:clickhouse-worker-slot-2:20260627-004000 |
|
cc @kssenii @scanhex12 — could you review this? Follow-up to #108625: |
|
Workflow [PR], commit [bca6656] Summary: ✅
AI ReviewSummaryThis PR fixes the previously declared but always-empty Final Verdict✅ No findings. |
Move the on-demand _tags fetch to after processed_files.emplace_back so a GetObjectTagging failure (denied, or key removed after listing) fails the already-claimed file through the normal commit accounting path instead of leaving it orphaned. Previously the fetch ran between createReader claiming the file as Processing and the file being appended to processed_files, so a throw reached commit() with an empty processed_files list: no failed/reset request and no queue log entry were written for the claimed file, leaving destructor cleanup as the only recovery and allowing the file to be picked again outside the retry/failure accounting. Add a regression test (object_storage_queue_fail_tags_fetch failpoint) that asserts the claimed file is recorded as Failed in system.s3queue_log when the tag fetch throws. It passes with the fix and fails when the fetch runs before emplace_back. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Re-validation for the review fix (commit 1eafa11), addressing the claimed-file accounting finding. Pre-PR validation gate (click to expand)
Session id: cron:clickhouse-worker-slot-0:20260627-165500 |
CI finish ledger — 1eafa11CI fully finished (Finish Workflow + Mergeable Check pass, 0 checks pending). Every failure below has an owner: a fixing PR (ours or external), or a full-effort fix task whose fixing-PR link will be posted here when it opens. Only
Bugfix validation (integration tests, amd64 + aarch64): SUCCESS (the changed Session id: cron:our-pr-ci-monitor:20260627-213000 |
|
|
||
| processed_files.emplace_back(file_metadata); | ||
|
|
||
| /// Tags are not fetched during listing (it lists with with_tags = false), so populate |
There was a problem hiding this comment.
Why not just modify it to include /*with_tags=*/ info.requested_virtual_columns.contains("_tags")?
There was a problem hiding this comment.
It would populate tags, but not for free: with_tags=true makes the S3 iterator issue a GetObjectTagging per listed object (S3IteratorAsync in S3ObjectStorage.cpp), since ListObjectsV2 itself does not return tags. I kept the fetch separate on purpose:
- Listing tags every object before filtering.
FileIterator::nextlists, then drops files by glob/predicate,filterProcessableFiles(already-processed/failed, plus hash-ring to this replica in Unordered mode), and finally the ZK set-processing claim. Settingwith_tagsoniteratewould issueGetObjectTaggingfor files this replica never processes, and re-tag already-processed files on every re-list pass. The on-demand fetch is exactly oneGetObjectTaggingper file we actually process. - Listing runs on a shared background thread. A tagging throw there aborts the whole batch for all processors, with no claimed file to attribute it to. Fetching after the file is claimed (
processed_files.emplace_back,Processingstate) routes a throw throughcommit->prepareFailedRequests, the same Failed /s3queue_logpath read errors use (this is the accounting fix in 1eafa11). - It mirrors the plain object storage read path, which keeps its read iterator at
with_tags=falseand fetches tags per file inStorageObjectStorageSource::nextgated onrequested_virtual_columns.contains("_tags").
Happy to switch to the iterator flag if you prefer the simpler form, with the caveat of extra GetObjectTagging calls on filtered-out and already-processed files.
There was a problem hiding this comment.
Yes, this is reasonable. Thank you!
|
Hi @kssenii, gentle ping: do you prefer I switch to threading |
CI finish ledger — ca7d8c5Every failure below has an owner: a fixing PR (ours or external), or a full-effort fix task whose fixing-PR link will be posted here when it opens. Only
Session id: cron:our-pr-ci-monitor:20260703-043000 |
|
Fixing PR for the AST fuzzer slow-query / |
|
Dear @kssenii, you haven't been active on this PR for 30 days. You will be unassigned. Will you continue working on it? If so, please feel free to reassign yourself. |
|
@groeneai, update the branch to fix the "unknown error" in fuzzer, which is likely fixed in master. |
|
Done, new head The That is fixed on master by the The merge changed nothing in the fix itself: the PR delta before and after is content-identical apart from hunk offsets, and no submodule pin moved. The merged tree builds clean. |
CI finish ledger — ee4431bEvery failure below has an owner: a fixing PR (mine or external), or a full-effort fix task
All 174 checks completed; this was the only failing job, and 173 of them are green. The
This PR only populates the The Session id: cron:our-pr-ci-monitor:20260729-010000 |
…ags-virtual-objectstoragequeue
|
Updated the branch to current master, new head The reason for the refresh is the single red on the previous head, Related: #112265 — the same external-kill family on Also confirmed on the current code: no unresolved review threads remain (the |
LLVM Coverage Report
Changed lines: Changed C/C++ lines covered: 29/29 (100.00%) · Uncovered code |
CI finish ledger - bca6656Every failure below has an owner: a fixing PR (mine or external), or a full-effort fix task
All 174 check-runs on this head are One clarification so the CIDB rows are not misread: The previous head's single red, Session id: cron:our-pr-ci-monitor:20260730-180000 |
|
Fixing PR for the mislabelled |
Related: #108625
Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):
Fixed the
_tagsvirtual column for theS3Queuetable engine: it was declared but never populated, soSELECT _tagsalways returned an empty map. It now returns the object tags, like theS3engine and thes3()table function.Description
Follow-up to #108625 (
_etag), per @ alexey-milovidov's request to check for other unpopulated virtual columns._tagsis declared forObjectStorageQueueengines through the shared file-like virtual column list, but the read path inObjectStorageQueueSourcenever filled it, soSELECT _tagsalways returned an empty map.Unlike
_etag(whichListObjectsV2returns for free during listing), object tags require a separateGetObjectTaggingcall, so they cannot simply be forwarded from the listing result: the queue iterator lists withwith_tags = false. The plainS3engine fetches tags on demand only when_tagsis requested (StorageObjectStorageSource::createReader). This change mirrors that: when_tagsis among the requested virtual columns, the queue source fetches the object metadata with tags once per file (right after the reader is created) and then forwardstagsinto the virtuals struct, the same way_etagis forwarded.This is S3-only:
AzureObjectStorage::getObjectMetadatadoes not expose blob tags, soAzureQueue's_tagsstays empty, the same as the plain Azure path today.Audit of the remaining declared file-like virtual columns on the queue read path:
_path,_file,_size,_time,_etag,_tableand_row_numberare populated._data_lake_snapshot_versionand_iceberg_metadata_file_pathare declared via the shared list but are not applicable to queue engines (they read plain objects with no Iceberg/Delta metadata).A regression test is added to
test_storage_s3_queuetest_virtual_column_tags: it tags the object, reads_tagsthrough a materialized view, and asserts the map is populated. The test fails before this change (empty map) and passes after.Version info
26.8.1.460(included in26.8and later)