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

org.projectnessie.versioned.tests.AbstractSingleBranch Maven / Gradle / Ivy

There is a newer version: 0.100.2
Show newest version
/*
 * Copyright (C) 2022 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.tests;

import static java.util.Collections.emptyList;
import static java.util.Objects.requireNonNull;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.projectnessie.versioned.testworker.OnRefOnly.newOnRef;
import static org.projectnessie.versioned.testworker.OnRefOnly.onRef;

import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.IntFunction;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.projectnessie.model.CommitMeta;
import org.projectnessie.model.Content;
import org.projectnessie.model.ContentKey;
import org.projectnessie.model.ImmutableCommitMeta;
import org.projectnessie.versioned.BranchName;
import org.projectnessie.versioned.Commit;
import org.projectnessie.versioned.CommitResult;
import org.projectnessie.versioned.ContentResult;
import org.projectnessie.versioned.Hash;
import org.projectnessie.versioned.Operation;
import org.projectnessie.versioned.Put;
import org.projectnessie.versioned.ReferenceConflictException;
import org.projectnessie.versioned.ReferenceNotFoundException;
import org.projectnessie.versioned.VersionStore;

public abstract class AbstractSingleBranch extends AbstractNestedVersionStore {
  protected AbstractSingleBranch(VersionStore store) {
    super(store);
  }

  static class SingleBranchParam {
    final String branchName;
    final IntFunction tableNameGen;
    final boolean allowInconsistentValueException;

    SingleBranchParam(
        String branchName,
        IntFunction tableNameGen,
        boolean allowInconsistentValueException) {
      this.branchName = branchName;
      this.tableNameGen = tableNameGen;
      this.allowInconsistentValueException = allowInconsistentValueException;
    }

    @Override
    public String toString() {
      return "branchName='"
          + branchName
          + '\''
          + ", tableNameGen="
          + tableNameGen
          + ", allowInconsistentValueException="
          + allowInconsistentValueException;
    }
  }

  @SuppressWarnings("unused")
  static List singleBranchManyUsersCases() {
    return Arrays.asList(
        new SingleBranchParam("singleBranchManyUsersSingleTable", user -> "single-table", true),
        new SingleBranchParam(
            "singleBranchManyUsersDistinctTables",
            user -> String.format("user-table-%d", user),
            false));
  }

  /**
   * Use case simulation matrix: single branch, multiple users, each or all user updating a separate
   * or single table.
   */
  @ParameterizedTest
  @MethodSource("singleBranchManyUsersCases")
  void singleBranchManyUsers(SingleBranchParam param) throws Exception {
    BranchName branch = BranchName.of(param.branchName);

    int numUsers = 3;
    int numCommits = 20;

    Hash[] hashesKnownByUser = new Hash[numUsers];
    Hash createHash = store().create(branch, Optional.empty()).getHash();
    Arrays.fill(hashesKnownByUser, createHash);

    List expectedValues = new ArrayList<>();
    Hash parent = createHash;
    for (int commitNum = 0; commitNum < numCommits; commitNum++) {
      for (int user = 0; user < numUsers; user++) {
        Hash hashKnownByUser = hashesKnownByUser[user];

        ImmutableCommitMeta.Builder msg =
            CommitMeta.builder()
                .message(String.format("user %03d/commit %03d", user, commitNum))
                .addParentCommitHashes(parent.asString());

        ContentKey key = ContentKey.of(param.tableNameGen.apply(user));
        List ops =
            singleBranchManyUsersOps(branch, commitNum, user, hashKnownByUser, key);

        CommitResult commitHash;
        try {
          commitHash = store().commit(branch, Optional.of(hashKnownByUser), msg.build(), ops);
        } catch (ReferenceConflictException inconsistentValueException) {
          if (param.allowInconsistentValueException) {
            hashKnownByUser = store().hashOnReference(branch, Optional.empty(), emptyList());
            ops = singleBranchManyUsersOps(branch, commitNum, user, hashKnownByUser, key);
            commitHash = store().commit(branch, Optional.of(hashKnownByUser), msg.build(), ops);
          } else {
            throw inconsistentValueException;
          }
        }

        parent = commitHash.getCommitHash();

        expectedValues.add(msg.hash(parent.asString()).build());

        assertNotEquals(hashKnownByUser, commitHash);

        hashesKnownByUser[user] = parent;
      }
    }

    // Verify that all commits are there and that the order of the commits is correct
    List committedValues =
        commitsListMap(branch, Integer.MAX_VALUE, Commit::getCommitMeta);
    Collections.reverse(expectedValues);
    assertThat(committedValues).containsExactlyElementsOf(expectedValues);
  }

  private List singleBranchManyUsersOps(
      BranchName branch, int commitNum, int user, Hash hashKnownByUser, ContentKey key)
      throws ReferenceNotFoundException {
    List ops;
    ContentResult existing =
        store()
            .getValue(
                store.hashOnReference(branch, Optional.of(hashKnownByUser), emptyList()),
                key,
                false);
    Content value =
        existing != null
            ? onRef(
                String.format("data_file_%03d_%03d", user, commitNum),
                requireNonNull(existing.content()).getId())
            : newOnRef(String.format("data_file_%03d_%03d", user, commitNum));
    ops = ImmutableList.of(Put.of(key, value));
    return ops;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy