All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.projectnessie.versioned.VersionStore Maven / Gradle / Ivy

/*
 * Copyright (C) 2020 Dremio
 *
 * Licensed 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.
 */
package org.projectnessie.versioned;

import static org.projectnessie.versioned.DefaultMetadataRewriter.DEFAULT_METADATA_REWRITER;

import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import java.util.stream.Stream;
import org.immutables.value.Value;
import org.projectnessie.error.BaseNessieClientServerException;
import org.projectnessie.model.CommitMeta;
import org.projectnessie.model.Content;
import org.projectnessie.model.ContentKey;
import org.projectnessie.model.IdentifiedContentKey;
import org.projectnessie.model.MergeBehavior;
import org.projectnessie.model.MergeKeyBehavior;
import org.projectnessie.model.Operation;
import org.projectnessie.model.RepositoryConfig;
import org.projectnessie.versioned.paging.PaginationIterator;

/**
 * A storage interface that maintains multiple versions of the VALUE type with each commit having an
 * associated CommitMeta value.
 */
public interface VersionStore {

  @Nonnull
  RepositoryInformation getRepositoryInformation();

  /**
   * Verifies that the given {@code namedReference} exists and that {@code hashOnReference}, if
   * present, is reachable via that reference.
   *
   * @return verified {@code hashOnReference} or, if {@code hashOnReference} is not present, the
   *     current HEAD of {@code namedReference}
   * @throws ReferenceNotFoundException if {@code namedReference} does not exist or {@code
   *     hashOnReference}, if present, is not reachable from that reference
   */
  Hash hashOnReference(
      NamedRef namedReference,
      Optional hashOnReference,
      List relativeLookups)
      throws ReferenceNotFoundException;

  /**
   * Retrieve the hash for "no ancestor" (or "beginning of time"), which is a hash for which no
   * commit exists. "no ancestor" or "beginning of time" are the initial hash of the default branch
   * and branches that are created via {@link #create(NamedRef, Optional)} without specifying the
   * {@code targetHash}.
   *
   * 

This "no ancestor" value is readable for all users, and it is a valid hash for every named * reference. * *

The result of this function must not change for any store instance, but it can be different * for different backends and even for different instances of the same backend. */ @Nonnull Hash noAncestorHash(); /** * Create a new commit and add to a branch. * *

If {@code referenceHash} is not empty, for each key referenced by one of the operations, the * current key's value is compared with the stored value for referenceHash's tree, and {@code * ReferenceConflictException} is thrown if values are not matching. * * @param branch The branch to commit to. * @param referenceHash The hash to use as a reference for conflict detection. If not present, do * not perform conflict detection * @param metadata The metadata associated with the commit. * @param operations The set of operations to apply. * @param validator Gets called during the atomic commit operations, callers can implement * validation logic. * @param addedContents callback that receives the content-ID of _new_ content per content-key * @throws ReferenceConflictException if {@code referenceHash} values do not match the stored * values for {@code branch} * @throws ReferenceNotFoundException if {@code branch} is not present in the store * @throws NullPointerException if one of the argument is {@code null} */ CommitResult commit( @Nonnull BranchName branch, @Nonnull Optional referenceHash, @Nonnull CommitMeta metadata, @Nonnull List operations, @Nonnull CommitValidator validator, @Nonnull BiConsumer addedContents) throws ReferenceNotFoundException, ReferenceConflictException; default CommitResult commit( @Nonnull BranchName branch, @Nonnull Optional referenceHash, @Nonnull CommitMeta metadata, @Nonnull List operations) throws ReferenceNotFoundException, ReferenceConflictException { return commit(branch, referenceHash, metadata, operations, x -> {}, (k, c) -> {}); } List getRepositoryConfig(Set repositoryConfigTypes); RepositoryConfig updateRepositoryConfig(RepositoryConfig repositoryConfig) throws ReferenceConflictException; @FunctionalInterface interface CommitValidator { void validate(CommitValidation commitValidation) throws BaseNessieClientServerException, VersionStoreException; } @SuppressWarnings("immutables:untype") interface MergeTransplantOpBase { /** The named ref we are merging/transplanting from. */ NamedRef fromRef(); /** The branch that we are merging/transplanting into. */ BranchName toBranch(); /** The current head of the branch to validate before updating (optional). */ @Value.Default default Optional expectedHash() { return Optional.empty(); } /** Function that rewrites the commit metadata. */ @Value.Default default MetadataRewriter updateCommitMetadata() { return DEFAULT_METADATA_REWRITER; } /** Merge behaviors per content key. */ Map mergeKeyBehaviors(); /** Default merge behavior for all keys not present in {@link #mergeKeyBehaviors()}. */ @Value.Default default MergeBehavior defaultMergeBehavior() { return MergeBehavior.NORMAL; } /** Whether to try the merge, check for conflicts, but do not commit. */ @Value.Default default boolean dryRun() { return false; } /** Whether to fetch additional commit information like commit-operations and parent. */ @Value.Default default boolean fetchAdditionalInfo() { return false; } @Value.Default default CommitValidator validator() { return commitValidation -> {}; } } @Value.Immutable interface MergeOp extends MergeTransplantOpBase { /** The hash we are using to get additional commits. */ Hash fromHash(); static ImmutableMergeOp.Builder builder() { return ImmutableMergeOp.builder(); } } @Value.Immutable interface TransplantOp extends MergeTransplantOpBase { /** The sequence of hashes to transplant. */ List sequenceToTransplant(); static ImmutableTransplantOp.Builder builder() { return ImmutableTransplantOp.builder(); } } /** * Transplant a series of commits to a target branch. * *

This is done as an atomic operation such that only the last of the sequence is ever visible * to concurrent readers/writers. The sequence to transplant must be contiguous, in order and * share a common ancestor with the target branch. * * @return merge result * @throws ReferenceConflictException if {@code referenceHash} values do not match the stored * values for {@code branch} * @throws ReferenceNotFoundException if {@code branch} or if any of the hashes from {@code * sequenceToTransplant} is not present in the store. */ TransplantResult transplant(TransplantOp transplantOp) throws ReferenceNotFoundException, ReferenceConflictException; /** * Merge items from an existing hash into the requested branch. The merge is always a rebase + * fast-forward merge and is only completed if the rebase is conflict free. The set of commits * added to the branch will be all of those until we arrive at a common ancestor. Depending on the * underlying implementation, the number of commits allowed as part of this operation may be * limited * *

Throws if any of the following are true: * *

    *
  • the hash or the branch do not exists *
  • the rebase has conflicts *
  • the expected branch hash does not match the actual branch hash *
* * @return merge result * @throws ReferenceConflictException if {@code expectedBranchHash} doesn't match the stored hash * for {@code toBranch} * @throws ReferenceNotFoundException if {@code toBranch} or {@code fromHash} is not present in * the store. */ MergeResult merge(MergeOp mergeOp) throws ReferenceNotFoundException, ReferenceConflictException; /** * Assign the NamedRef to point to a particular hash. * *

{@code ref} should already exists. If {@code expectedHash} is not empty, its value is * compared with the current stored value for {@code ref} and an exception is thrown if values do * not match. * * @param ref The named ref to be assigned * @param expectedHash The current head of the NamedRef to validate before updating (required). * @param targetHash The hash that this ref should refer to. * @return A {@link ReferenceAssignedResult} containing the previous and current head of the * reference * @throws ReferenceNotFoundException if {@code ref} is not present in the store or if {@code * targetHash} is not present in the store * @throws ReferenceConflictException if {@code expectedHash} is not empty and its value doesn't * match the stored hash for {@code ref} */ ReferenceAssignedResult assign(NamedRef ref, Hash expectedHash, Hash targetHash) throws ReferenceNotFoundException, ReferenceConflictException; /** * Assign the NamedRef to point to a particular hash. If the NamedRef does not exist, it will be * created. * * @param ref The named ref we're assigning * @param targetHash The hash that this ref should refer to (optional). Otherwise will reference * the beginning of time. * @return A {@link ReferenceCreatedResult} containing the head of the created reference * @throws ReferenceNotFoundException if {@code targetHash} is not empty and not present in the * store * @throws ReferenceAlreadyExistsException if {@code ref} already exists */ ReferenceCreatedResult create(NamedRef ref, Optional targetHash) throws ReferenceNotFoundException, ReferenceAlreadyExistsException; /** * Delete the provided NamedRef * *

Throws exception if the optional hash does not match the provided ref. * * @param ref The NamedRef to be deleted. * @param hash The expected hash (required). The operation will only succeed if the branch is * pointing at the provided hash. * @return A {@link ReferenceDeletedResult} containing the head of the deleted reference * @throws ReferenceNotFoundException if {@code ref} is not present in the store * @throws ReferenceConflictException if {@code hash} doesn't match the stored hash for {@code * ref} */ ReferenceDeletedResult delete(NamedRef ref, Hash hash) throws ReferenceNotFoundException, ReferenceConflictException; /** * Resolve the given {@link NamedRef} and return information about it, which at least contains the * current HEAD commit hash plus, optionally, additional information. * *

This is a functionally equivalent to {@link #hashOnReference(NamedRef, Optional, List) * hashOnReference(ref, Optional.empty(), Collections.emptyList())}. * * @param ref The branch or tag to lookup. * @param params options that control which information shall be returned in {@link * ReferenceInfo}, see {@link GetNamedRefsParams} for details. * @return Requested information about the requested reference. * @throws NullPointerException if {@code ref} is {@code null}. * @throws ReferenceNotFoundException if the reference cannot be found */ ReferenceInfo getNamedRef(String ref, GetNamedRefsParams params) throws ReferenceNotFoundException; /** * Retrieve the recorded recent history of a reference. A reference's history is a size and time * limited record of changes of the reference's current pointer, aka HEAD. The size and time * limits are configured in the Nessie server configuration. * * @return recorded reference history */ ReferenceHistory getReferenceHistory(String refName, Integer headCommitsToScan) throws ReferenceNotFoundException; /** * List named refs. * *

IMPORTANT NOTE: The returned {@link Stream} must be closed! * * @param params options that control which information shall be returned in each {@link * ReferenceInfo}, see {@link ReferenceInfo} for details. * @param pagingToken paging token to start at * @return All refs and their associated hashes. */ PaginationIterator> getNamedRefs( GetNamedRefsParams params, String pagingToken) throws ReferenceNotFoundException; /** * Get a stream of all ancestor commits to a provided ref. * * @param ref the stream to get commits for. * @param fetchAdditionalInfo include additional information like operations and parent hash * @return A stream of commits. * @throws ReferenceNotFoundException if {@code ref} is not present in the store */ PaginationIterator getCommits(Ref ref, boolean fetchAdditionalInfo) throws ReferenceNotFoundException; @Value.Immutable interface KeyRestrictions { KeyRestrictions NO_KEY_RESTRICTIONS = KeyRestrictions.builder().build(); /** Optional, if not {@code null}: the minimum key to return. */ @Nullable ContentKey minKey(); /** Optional, if not {@code null}: the maximum key to return. */ @Nullable ContentKey maxKey(); /** Optional, if not {@code null}: the prefix of the keys to return. */ @Nullable ContentKey prefixKey(); /** Filter predicate, can be {@code null}. */ @Nullable BiPredicate contentKeyPredicate(); static ImmutableKeyRestrictions.Builder builder() { return ImmutableKeyRestrictions.builder(); } } /** * Get a stream of all available keys for the given ref. * * @param ref The ref to get keys for. * @param pagingToken paging token to start at * @param withContent whether to populate {@link KeyEntry#getContent()} * @return The stream of keys available for this ref. * @throws ReferenceNotFoundException if {@code ref} is not present in the store */ PaginationIterator getKeys( Ref ref, String pagingToken, boolean withContent, KeyRestrictions keyRestrictions) throws ReferenceNotFoundException; List getIdentifiedKeys(Ref ref, Collection keys) throws ReferenceNotFoundException; /** * Get the value for a provided ref. * * @param ref Any ref type allowed * @param key The key for the specific value * @param returnNotFound whether to return a non-{@code null} result object with a {@code null} * value in {@link ContentResult#content()} instead of a {@code null} return-value for * non-existing content * @return The value or {@code null} if the key does not exist and {@code returnNotFound} is * {@code false}. If the content does not exist and {@code returnNotFound} is {@code true}, * {@link ContentResult#content()} will be {@code null}. * @throws ReferenceNotFoundException if {@code ref} is not present in the store */ ContentResult getValue(Ref ref, ContentKey key, boolean returnNotFound) throws ReferenceNotFoundException; /** * Get the values for a list of keys. * * @param ref The ref to use. * @param keys An ordered list of keys to retrieve within the provided ref. * @param returnNotFound whether to return non-{@code null} values with a {@code null} value in * {@link ContentResult#content()} instead of omitting non-existing content in the returned * map * @return Map of keys to content results. For keys that do not exist: if {@code returnNotFound} * is {@code false}, the map will not contain an entry for non-existing keys.If {@code * returnNotFound} is {@code true}, the map will return entries for all requested keys, but * {@link ContentResult#content()} will be {@code null} for non-existing keys. * @throws ReferenceNotFoundException if {@code ref} is not present in the store */ Map getValues( Ref ref, Collection keys, boolean returnNotFound) throws ReferenceNotFoundException; /** * Get list of diffs between two refs. * * @param from The from part of the diff. * @param to The to part of the diff. * @param pagingToken paging token to start at * @return A stream of values that are different. */ PaginationIterator getDiffs( Ref from, Ref to, String pagingToken, KeyRestrictions keyRestrictions) throws ReferenceNotFoundException; }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy