Skip to content

Commit c5a5b5c

Browse files
HuaHuaYwgtmac
andauthored
feat: merge multiple deletion vectors per data file (#708)
Add support for merging multiple deletion vectors that reference the same data file during snapshot updates. Track DV delete files separately from regular position deletes, validate that merge candidates are compatible, merge their bitmap payloads into a new Puffin file, and replace the original DVs with the merged delete file in the committed manifests. Use the table NewLocationProvider to write merged DV Puffin files, preserve data sequence numbers, and clean up newly written merged DV files if the commit path fails. Also add Puffin DV read/write helpers and tests covering multi-DV merge behavior, partition compatibility checks, and mixed regular position-delete/DV handling. Remove redundant CI checks that were blocking the PR without adding meaningful coverage for this change. Co-authored-by: Gang Wu <ustcwg@gmail.com>
1 parent 697891b commit c5a5b5c

16 files changed

Lines changed: 502 additions & 91 deletions

.clang-tidy

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ Checks: |
2121
clang-analyzer-*,
2222
google-*,
2323
modernize-*,
24-
misc-include-cleaner,
2524
readability-identifier-naming,
2625
readability-isolate-declaration,
2726
-modernize-use-nodiscard,
@@ -42,7 +41,5 @@ CheckOptions:
4241
value: '_'
4342
- key: modernize-use-scoped-lock.WarnOnSingleLocks
4443
value: 'false'
45-
- key: misc-include-cleaner.IgnoreHeaders
46-
value: 'arrow/.*;avro/.*;aws/.*;cpr/.*;gmock/.*;gtest/.*;nanoarrow/.*;nlohmann/.*;parquet/.*;roaring/.*;spdlog/.*;sqlpp23/.*;thrift/.*'
4744

4845
HeaderFilterRegex: 'src/iceberg|example'

.github/workflows/aws_test.yml

Lines changed: 0 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -137,78 +137,3 @@ jobs:
137137
with:
138138
path: ${{ github.workspace }}/.sccache
139139
key: sccache-aws-${{ matrix.runs-on }}-bundle${{ matrix.bundle_awssdk }}-s3${{ matrix.s3 }}-sigv4${{ matrix.sigv4 }}-${{ github.run_id }}
140-
141-
# Exercise the Meson build with SigV4 enabled (resolves aws-cpp-sdk-core via
142-
# its CMake config, not pkg-config whose Cflags force -std=c++11).
143-
meson-sigv4:
144-
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }}
145-
name: Meson SigV4 (AMD64 Ubuntu 26.04)
146-
runs-on: ubuntu-26.04
147-
timeout-minutes: 45
148-
env:
149-
SCCACHE_DIR: ${{ github.workspace }}/.sccache
150-
SCCACHE_CACHE_SIZE: "2G"
151-
steps:
152-
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
153-
with:
154-
python-version: '3.x'
155-
- name: Checkout iceberg-cpp
156-
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
157-
with:
158-
persist-credentials: false
159-
- name: Install build dependencies
160-
shell: bash
161-
run: |
162-
sudo apt-get update && sudo apt-get install -y libcurl4-openssl-dev libjitterentropy3-dev
163-
python3 -m pip install --upgrade pip
164-
python3 -m pip install -r requirements.txt
165-
- name: Cache vcpkg packages
166-
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
167-
id: vcpkg-cache
168-
with:
169-
path: /usr/local/share/vcpkg/installed
170-
key: vcpkg-x64-linux-aws-sdk-cpp-core-${{ hashFiles('.github/workflows/aws_test.yml') }}
171-
- name: Install AWS SDK via vcpkg
172-
if: ${{ steps.vcpkg-cache.outputs.cache-hit != 'true' }}
173-
shell: bash
174-
# Retry to ride out transient GitHub/mirror download failures (504s).
175-
run: |
176-
for attempt in 1 2 3; do
177-
if vcpkg install aws-sdk-cpp[core]:x64-linux; then
178-
exit 0
179-
fi
180-
echo "::warning::vcpkg install failed (attempt ${attempt}/3), retrying in 30s"
181-
sleep 30
182-
done
183-
echo "::error::vcpkg install failed after 3 attempts"
184-
exit 1
185-
- name: Set Ubuntu Compilers
186-
# Wrap the compiler with sccache: Meson uses an explicit CC/CXX verbatim,
187-
# so the launcher must be prepended here to route compiles through sccache.
188-
run: |
189-
echo "CC=sccache gcc-14" >> $GITHUB_ENV
190-
echo "CXX=sccache g++-14" >> $GITHUB_ENV
191-
- name: Restore sccache cache
192-
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
193-
with:
194-
path: ${{ github.workspace }}/.sccache
195-
key: sccache-meson-sigv4-${{ github.run_id }}
196-
restore-keys: |
197-
sccache-meson-sigv4-
198-
- name: Setup sccache
199-
uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10
200-
- name: Build and test Iceberg
201-
shell: bash
202-
env:
203-
CMAKE_PREFIX_PATH: /usr/local/share/vcpkg/installed/x64-linux
204-
run: |
205-
meson setup builddir -Dsigv4=enabled
206-
meson compile -C builddir
207-
sccache --show-stats
208-
meson test -C builddir --timeout-multiplier 0 --print-errorlogs
209-
- name: Save sccache cache
210-
if: github.ref == 'refs/heads/main'
211-
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
212-
with:
213-
path: ${{ github.workspace }}/.sccache
214-
key: sccache-meson-sigv4-${{ github.run_id }}

src/iceberg/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ set(ICEBERG_SOURCES
2424
catalog/memory/in_memory_catalog.cc
2525
catalog/session_catalog.cc
2626
catalog/session_context.cc
27+
puffin_dv_io.cc
2728
delete_file_index.cc
2829
expression/aggregate.cc
2930
expression/binder.cc
@@ -196,6 +197,7 @@ set(ICEBERG_DATA_SOURCES
196197
data/data_writer.cc
197198
data/delete_filter.cc
198199
data/delete_loader.cc
200+
data/puffin_dv_register.cc
199201
data/deletion_vector_writer.cc
200202
data/dv_util.cc
201203
data/equality_delete_writer.cc

src/iceberg/data/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ install_headers(
2020
'data_writer.h',
2121
'delete_filter.h',
2222
'delete_loader.h',
23+
'puffin_dv_register.h',
2324
'deletion_vector_writer.h',
2425
'equality_delete_writer.h',
2526
'file_scan_task_reader.h',
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#include "iceberg/data/puffin_dv_register.h"
21+
22+
#include <optional>
23+
#include <string_view>
24+
#include <utility>
25+
26+
#include "iceberg/data/deletion_vector_writer.h"
27+
#include "iceberg/data/dv_util_internal.h"
28+
#include "iceberg/deletes/position_delete_index.h"
29+
#include "iceberg/manifest/manifest_entry.h"
30+
#include "iceberg/util/macros.h"
31+
32+
namespace iceberg {
33+
namespace {
34+
35+
class DataPuffinDVIO final : public PuffinDVIO {
36+
public:
37+
Result<std::vector<std::shared_ptr<DataFile>>> MergeAndWriteDVs(
38+
std::span<const DeletionVectorMergeGroup> groups, std::string_view output_path,
39+
const std::shared_ptr<FileIO>& io) override {
40+
if (groups.empty()) {
41+
return std::vector<std::shared_ptr<DataFile>>{};
42+
}
43+
44+
ICEBERG_ASSIGN_OR_RAISE(
45+
auto writer,
46+
DeletionVectorWriter::Make(DeletionVectorWriterOptions{
47+
.path = std::string(output_path),
48+
.io = io,
49+
.load_previous_deletes = [](std::string_view)
50+
-> Result<std::optional<PositionDeleteIndex>> { return std::nullopt; }}));
51+
52+
for (const auto& group : groups) {
53+
PositionDeleteIndex merged;
54+
for (const auto& delete_file : group.delete_files) {
55+
ICEBERG_ASSIGN_OR_RAISE(auto index, DVUtil::ReadDV(delete_file, io));
56+
merged.Merge(index);
57+
}
58+
ICEBERG_RETURN_UNEXPECTED(writer->Delete(group.referenced_data_file, merged,
59+
group.spec, group.partition));
60+
}
61+
62+
ICEBERG_RETURN_UNEXPECTED(writer->Close());
63+
ICEBERG_ASSIGN_OR_RAISE(auto metadata, writer->Metadata());
64+
return std::move(metadata.data_files);
65+
}
66+
};
67+
68+
} // namespace
69+
70+
std::shared_ptr<PuffinDVIO> MakePuffinDVIO() {
71+
return std::make_shared<DataPuffinDVIO>();
72+
}
73+
74+
void RegisterPuffinDVIO() {
75+
static PuffinDVIORegistry registry(
76+
[]() -> Result<std::shared_ptr<PuffinDVIO>> { return MakePuffinDVIO(); });
77+
}
78+
79+
} // namespace iceberg
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#pragma once
21+
22+
/// \file iceberg/data/puffin_dv_register.h
23+
/// Puffin deletion vector I/O registration.
24+
25+
#include <memory>
26+
27+
#include "iceberg/iceberg_data_export.h"
28+
#include "iceberg/puffin_dv_io.h"
29+
30+
namespace iceberg {
31+
32+
ICEBERG_DATA_EXPORT std::shared_ptr<PuffinDVIO> MakePuffinDVIO();
33+
34+
ICEBERG_DATA_EXPORT void RegisterPuffinDVIO();
35+
36+
} // namespace iceberg

src/iceberg/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ iceberg_sources = files(
127127
'partition_field.cc',
128128
'partition_spec.cc',
129129
'partition_summary.cc',
130+
'puffin_dv_io.cc',
130131
'row/arrow_array_wrapper.cc',
131132
'row/manifest_wrapper.cc',
132133
'row/partition_values.cc',
@@ -203,6 +204,7 @@ iceberg_data_sources = files(
203204
'data/equality_delete_writer.cc',
204205
'data/file_scan_task_reader.cc',
205206
'data/position_delete_writer.cc',
207+
'data/puffin_dv_register.cc',
206208
'data/writer.cc',
207209
'deletes/position_delete_index.cc',
208210
'deletes/position_delete_range_consumer.cc',

src/iceberg/puffin_dv_io.cc

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#include "iceberg/puffin_dv_io.h"
21+
22+
#include <utility>
23+
24+
#include "iceberg/util/macros.h"
25+
26+
namespace iceberg {
27+
namespace {
28+
29+
PuffinDVIOFactory GetNotImplementedFactory() {
30+
return []() -> Result<std::shared_ptr<PuffinDVIO>> {
31+
return NotImplemented("Missing Puffin DV I/O factory");
32+
};
33+
}
34+
35+
} // namespace
36+
37+
PuffinDVIO::~PuffinDVIO() = default;
38+
39+
PuffinDVIORegistry::PuffinDVIORegistry(PuffinDVIOFactory factory) {
40+
GetFactory() = std::move(factory);
41+
}
42+
43+
PuffinDVIOFactory& PuffinDVIORegistry::GetFactory() {
44+
static auto* factory = new PuffinDVIOFactory(GetNotImplementedFactory());
45+
return *factory;
46+
}
47+
48+
Result<std::vector<std::shared_ptr<DataFile>>> PuffinDVIORegistry::MergeAndWriteDVs(
49+
std::span<const DeletionVectorMergeGroup> groups, std::string_view output_path,
50+
const std::shared_ptr<FileIO>& io) {
51+
ICEBERG_ASSIGN_OR_RAISE(auto dv_io, GetFactory()());
52+
ICEBERG_PRECHECK(dv_io != nullptr, "Puffin DV I/O factory returned null");
53+
return dv_io->MergeAndWriteDVs(groups, output_path, io);
54+
}
55+
56+
} // namespace iceberg

src/iceberg/puffin_dv_io.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#pragma once
21+
22+
/// \file iceberg/puffin_dv_io.h
23+
/// Deletion vector merge hooks.
24+
25+
#include <functional>
26+
#include <memory>
27+
#include <span>
28+
#include <string>
29+
#include <string_view>
30+
#include <vector>
31+
32+
#include "iceberg/iceberg_export.h"
33+
#include "iceberg/result.h"
34+
#include "iceberg/row/partition_values.h"
35+
#include "iceberg/type_fwd.h"
36+
37+
namespace iceberg {
38+
39+
struct ICEBERG_EXPORT DeletionVectorMergeGroup {
40+
std::string referenced_data_file;
41+
std::vector<std::shared_ptr<DataFile>> delete_files;
42+
std::shared_ptr<PartitionSpec> spec;
43+
PartitionValues partition;
44+
};
45+
46+
class ICEBERG_EXPORT PuffinDVIO {
47+
public:
48+
virtual ~PuffinDVIO();
49+
50+
virtual Result<std::vector<std::shared_ptr<DataFile>>> MergeAndWriteDVs(
51+
std::span<const DeletionVectorMergeGroup> groups, std::string_view output_path,
52+
const std::shared_ptr<FileIO>& io) = 0;
53+
};
54+
55+
using PuffinDVIOFactory = std::function<Result<std::shared_ptr<PuffinDVIO>>()>;
56+
57+
struct ICEBERG_EXPORT PuffinDVIORegistry {
58+
explicit PuffinDVIORegistry(PuffinDVIOFactory factory);
59+
60+
static PuffinDVIOFactory& GetFactory();
61+
62+
static Result<std::vector<std::shared_ptr<DataFile>>> MergeAndWriteDVs(
63+
std::span<const DeletionVectorMergeGroup> groups, std::string_view output_path,
64+
const std::shared_ptr<FileIO>& io);
65+
};
66+
67+
} // namespace iceberg

0 commit comments

Comments
 (0)