blob: 47678b15e3396e58fc1ef500e48785a09a358f6c [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#pragma once
/// \file iceberg/transaction.h
/// \brief Define transaction APIs.
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "iceberg/iceberg_export.h"
#include "iceberg/result.h"
#include "iceberg/type_fwd.h"
namespace iceberg {
/// \brief Whether a transaction creates a new table or updates an existing one.
enum class TransactionKind : uint8_t { kCreate, kUpdate };
/// \brief Lifecycle outcomes of a transaction. Failed transactions may only be aborted.
enum class TransactionState : uint8_t {
kReady,
kUpdatePending,
kCommitted,
kFailed,
kAborted,
kCommitStateUnknown,
};
/// \brief A transaction for performing multiple updates to a table
class ICEBERG_EXPORT Transaction : public std::enable_shared_from_this<Transaction> {
public:
~Transaction();
/// \brief Create a new transaction
static Result<std::shared_ptr<Transaction>> Make(std::shared_ptr<Table> table,
TransactionKind kind);
/// \brief Create a detached transaction from an existing context.
///
/// This overload is used by PendingUpdate::Commit for standalone updates and does not
/// attach the temporary transaction to the context.
static Result<std::shared_ptr<Transaction>> Make(
std::shared_ptr<TransactionContext> ctx);
/// \brief Return the Table that this transaction will update
const std::shared_ptr<Table>& table() const;
/// \brief Returns the base metadata without any changes
const TableMetadata* base() const;
/// \brief Return the current metadata with staged changes applied
const TableMetadata& current() const;
/// \brief Return the location of the metadata file with the given filename
///
/// \param filename the name of the metadata file
/// \return the location of the metadata file
std::string MetadataFileLocation(std::string_view filename) const;
/// \brief Apply the pending changes from all actions and commit.
///
/// \return Updated table if the transaction was committed successfully, or an error:
/// - ValidationFailed: if any update cannot be applied to the current table metadata.
/// - CommitFailed: if the updates cannot be committed due to conflicts.
Result<std::shared_ptr<Table>> Commit();
/// \brief Discard staged changes and clean owned files best effort.
/// Repeated Abort succeeds without repeating cleanup. Committed and unknown
/// transactions cannot be aborted. Destructors never perform cleanup.
Status Abort();
/// \brief Return the current state of the transaction.
TransactionState state() const { return state_; }
/// \brief Create a new UpdatePartitionSpec to update the partition spec of this table
/// and commit the changes.
Result<std::shared_ptr<UpdatePartitionSpec>> NewUpdatePartitionSpec();
/// \brief Create a new UpdateProperties to update table properties and commit the
/// changes.
Result<std::shared_ptr<UpdateProperties>> NewUpdateProperties();
/// \brief Create a new UpdateSortOrder to update the table sort order and commit the
/// changes.
Result<std::shared_ptr<UpdateSortOrder>> NewUpdateSortOrder();
/// \brief Create a new UpdateSchema to alter the columns of this table and commit the
/// changes.
Result<std::shared_ptr<UpdateSchema>> NewUpdateSchema();
/// \brief Create a new ExpireSnapshots to remove expired snapshots and commit the
/// changes.
Result<std::shared_ptr<ExpireSnapshots>> NewExpireSnapshots();
/// \brief Create a new UpdateStatistics to update table statistics and commit the
/// changes.
Result<std::shared_ptr<UpdateStatistics>> NewUpdateStatistics();
/// \brief Create a new UpdatePartitionStatistics to update partition statistics and
/// commit the changes.
Result<std::shared_ptr<UpdatePartitionStatistics>> NewUpdatePartitionStatistics();
/// \brief Create a new UpdateLocation to update the table location and commit the
/// changes.
Result<std::shared_ptr<UpdateLocation>> NewUpdateLocation();
/// \brief Create a new FastAppend to append data files and commit the changes.
Result<std::shared_ptr<FastAppend>> NewFastAppend();
/// \brief Create a new MergeAppend to append data files and merge manifests.
Result<std::shared_ptr<MergeAppend>> NewMergeAppend();
/// \brief Create a new DeleteFiles to delete data files and commit the changes.
Result<std::shared_ptr<DeleteFiles>> NewDeleteFiles();
/// \brief Create a new RowDelta to add rows and row-level deletes.
Result<std::shared_ptr<RowDelta>> NewRowDelta();
/// \brief Create a new OverwriteFiles to overwrite data files and commit the changes.
Result<std::shared_ptr<OverwriteFiles>> NewOverwrite();
/// \brief Create a new RewriteFiles to replace files in this table and commit the
/// changes.
Result<std::shared_ptr<RewriteFiles>> NewRewriteFiles();
/// \brief Create a new ReplacePartitions to dynamically overwrite partitions and commit
/// the changes.
Result<std::shared_ptr<ReplacePartitions>> NewReplacePartitions();
/// \brief Create a new SnapshotManager to manage snapshots.
Result<std::shared_ptr<SnapshotManager>> NewSnapshotManager();
/// \brief Create a new CherryPickOperation to apply the changes of an existing
/// snapshot onto the current state.
Result<std::shared_ptr<CherryPickOperation>> NewCherryPickOperation();
/// \brief Create a new SetSnapshot to set the current snapshot or rollback to a
/// previous snapshot and commit the changes.
Result<std::shared_ptr<SetSnapshot>> NewSetSnapshot();
/// \brief Create a new UpdateSnapshotReference to update snapshot references (branches
/// and tags) and commit the changes.
Result<std::shared_ptr<UpdateSnapshotReference>> NewUpdateSnapshotReference();
private:
explicit Transaction(std::shared_ptr<TransactionContext> ctx);
Status CheckReady() const;
Status AddUpdate(const std::shared_ptr<PendingUpdate>& update);
Status CommitUpdate(PendingUpdate& update);
Status ApplyUpdate(PendingUpdate& update);
Status ReplayUpdates();
// Helper methods for applying different types of updates
Status ApplyExpireSnapshots(ExpireSnapshots& update);
Status ApplySetSnapshot(SetSnapshot& update);
Status ApplyUpdateLocation(UpdateLocation& update);
Status ApplyUpdatePartitionSpec(UpdatePartitionSpec& update);
Status ApplyUpdatePartitionStatistics(UpdatePartitionStatistics& update);
Status ApplyUpdateProperties(UpdateProperties& update);
Status ApplyUpdateSchema(UpdateSchema& update);
Status ApplyUpdateSnapshot(SnapshotUpdate& update);
Status ApplyUpdateSnapshotReference(UpdateSnapshotReference& update);
Status ApplyUpdateSortOrder(UpdateSortOrder& update);
Status ApplyUpdateStatistics(UpdateStatistics& update);
Result<std::shared_ptr<Table>> CommitOnce(bool is_first_attempt);
/// \brief Whether this transaction can retry after a commit conflict.
bool CanRetry() const;
void CleanupUpdates() noexcept;
void FinalizeUpdates(const TableMetadata& committed) noexcept;
private:
friend class PendingUpdate;
// Shared context owning the table, metadata builder, and kind.
std::shared_ptr<TransactionContext> ctx_;
// Keep track of all created pending updates.
std::vector<std::shared_ptr<PendingUpdate>> pending_updates_;
TransactionState state_ = TransactionState::kReady;
};
/// \brief Shared context between Transaction and PendingUpdate instances.
class ICEBERG_EXPORT TransactionContext {
public:
TransactionContext();
~TransactionContext();
static Result<std::shared_ptr<TransactionContext>> Make(std::shared_ptr<Table> table,
TransactionKind kind);
const TableMetadata* base() const;
const TableMetadata& current() const;
std::string MetadataFileLocation(std::string_view filename) const;
Result<std::unique_ptr<LocationProvider>> NewLocationProvider() const;
std::shared_ptr<Table> table;
std::unique_ptr<TableMetadataBuilder> metadata_builder;
TransactionKind kind;
// If PendingUpdate is created directly from Table, this is nullopt;
// otherwise, it holds a weak pointer to the Transaction that created it.
std::optional<std::weak_ptr<Transaction>> transaction;
};
} // namespace iceberg