public override async ValueTask <CommitRef?> ReadCommitRefAsync(string branch, string name, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(branch)) { throw new ArgumentNullException(nameof(branch)); } if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentNullException(nameof(name)); } var filename = DeriveCommitRefFileName(branch, name); var path = Path.Combine(_refsContainer, filename); var bytes = await ReadFileAsync(path, cancellationToken).ConfigureAwait(false); // NotFound if (bytes == null || bytes.Length == 0) { return(null); } // CommitIds are not compressed var commitId = Serializer.DeserializeCommitId(bytes); var commitRef = new CommitRef(name, commitId); return(commitRef); }
public override async ValueTask <IReadOnlyList <CommitRef> > GetBranchesAsync(string name, CancellationToken cancellationToken) { var filename = DeriveCommitRefFileName(name, null); var path = Path.Combine(_refsContainer, filename); var files = Directory.GetFiles(path, "*" + CommitExtension, SearchOption.TopDirectoryOnly); var results = new CommitRef[files.Length]; for (var i = 0; i < results.Length; i++) { var bytes = await ReadFileAsync(files[i], cancellationToken).ConfigureAwait(false); var branch = Path.ChangeExtension(Path.GetFileName(files[i]), null); var commitId = Serializer.DeserializeCommitId(bytes); results[i] = new CommitRef(branch, commitId); } return(results); }
public static void CommitRef_equality() { var commitRef1 = new CommitRef("abc", new CommitId(Sha1.Hash("abc"))); var commitRef2 = new CommitRef("abc", new CommitId(Sha1.Hash("abc"))); var commitRef3 = new CommitRef("def", new CommitId(Sha1.Hash("def"))); Assert.True(commitRef1 == commitRef2); Assert.False(commitRef1 != commitRef2); Assert.True(commitRef1.Equals((object)commitRef2)); Assert.Equal(commitRef1, commitRef2); Assert.Equal(commitRef1.GetHashCode(), commitRef2.GetHashCode()); Assert.Equal(commitRef1.ToString(), commitRef2.ToString()); Assert.NotEqual(CommitRef.Empty, commitRef1); Assert.NotEqual(CommitRef.Empty.GetHashCode(), commitRef1.GetHashCode()); Assert.NotEqual(commitRef3, commitRef1); Assert.NotEqual(commitRef3.GetHashCode(), commitRef1.GetHashCode()); Assert.NotEqual(commitRef3.ToString(), commitRef1.ToString()); }
public override async ValueTask <CommitRef?> ReadCommitRefAsync(string branch, string name, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(branch)) { throw new ArgumentNullException(nameof(branch)); } if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentNullException(nameof(name)); } var(found, commitId, _, _) = await ReadCommitRefImplAsync(branch, name, cancellationToken).ConfigureAwait(false); // NotFound if (!found) { return(null); } // Found var commitRef = new CommitRef(name, commitId); return(commitRef); }
public override Task WriteCommitRefAsync(CommitId?previousCommitId, string branch, CommitRef commitRef, CancellationToken cancellationToken) { throw new NotImplementedException(); }
public abstract Task WriteCommitRefAsync(CommitId?previousCommitId, string name, CommitRef commitRef, CancellationToken cancellationToken);
private static async Task TestRepository(IChasmRepository repository) { var g = Guid.NewGuid(); var data = g.ToByteArray(); var sha = Sha1.Hash(data); // Unknown SHA var usha = Sha1.Hash(Guid.NewGuid().ToByteArray()); var usha2 = Sha1.Hash(Guid.NewGuid().ToByteArray()); // Blob await repository.WriteObjectAsync(sha, new ArraySegment <byte>(data), false, default); var rdata = (await repository.ReadObjectAsync(sha, default)); Assert.True(rdata.HasValue); Assert.Equal(16, rdata.Value.Length); Assert.Equal(g, rdata.Value.Span.NonPortableCast <byte, Guid>()[0]); var urdata = await repository.ReadObjectAsync(usha, default); Assert.False(urdata.HasValue); // Tree var tree = new TreeNodeMap( new TreeNode("firstItem", NodeKind.Blob, sha), new TreeNode("secondItem", NodeKind.Blob, sha) ); var treeId = await repository.WriteTreeAsync(tree, default); var rtree = await repository.ReadTreeAsync(treeId, default); Assert.True(rtree.HasValue); Assert.Equal(tree, rtree.Value); var urtree = await repository.ReadTreeAsync(new TreeId(usha), default); Assert.False(urtree.HasValue); // Commit var commit = new Commit( new CommitId?(), treeId, new Audit("User1", DateTimeOffset.UtcNow.AddDays(-1)), new Audit("User2", DateTimeOffset.UtcNow), "Initial commit" ); var commitId = await repository.WriteCommitAsync(commit, default); var rcommit = await repository.ReadCommitAsync(commitId, default); Assert.True(rcommit.HasValue); Assert.Equal(commit, rcommit); var urcommit = await repository.ReadCommitAsync(new CommitId(usha), default); Assert.False(urcommit.HasValue); // CommitRef var commitRefName = Guid.NewGuid().ToString("N"); var commitRef = new CommitRef("production", commitId); await repository.WriteCommitRefAsync(null, commitRefName, commitRef, default); var rcommitRef = await repository.ReadCommitRefAsync(commitRefName, commitRef.Branch, default); Assert.True(rcommit.HasValue); Assert.Equal(commitRef, rcommitRef); var urcommitRef = await repository.ReadCommitRefAsync(commitRefName + "_", commitRef.Branch, default); Assert.False(urcommit.HasValue); await Assert.ThrowsAsync <ChasmConcurrencyException>(() => repository.WriteCommitRefAsync(null, commitRefName, new CommitRef("production", new CommitId(usha)), default)); await Assert.ThrowsAsync <ChasmConcurrencyException>(() => repository.WriteCommitRefAsync(new CommitId(usha2), commitRefName, new CommitRef("production", new CommitId(usha)), default)); await repository.WriteCommitRefAsync(null, commitRefName, new CommitRef("dev", commitId), default); await repository.WriteCommitRefAsync(null, commitRefName, new CommitRef("staging", new CommitId(usha)), default); await repository.WriteCommitRefAsync(null, commitRefName + "_1", new CommitRef("production", new CommitId(usha)), default); var names = await repository.GetNamesAsync(default);
public override async Task WriteCommitRefAsync(CommitId?previousCommitId, string branch, CommitRef commitRef, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(branch)) { throw new ArgumentNullException(nameof(branch)); } if (commitRef == CommitRef.Empty) { throw new ArgumentNullException(nameof(commitRef)); } // Load existing commit ref in order to use its etag var(found, existingCommitId, ifMatchCondition, blobRef) = await ReadCommitRefImplAsync(branch, commitRef.Name, cancellationToken).ConfigureAwait(false); // Optimistic concurrency check if (found) { // We found a previous commit but the caller didn't say to expect one if (!previousCommitId.HasValue) { throw BuildConcurrencyException(branch, commitRef.Name, null); // TODO: May need a different error } // Semantics follow Interlocked.Exchange (compare then exchange) if (existingCommitId != previousCommitId.Value) { throw BuildConcurrencyException(branch, commitRef.Name, null); } // Idempotent if (existingCommitId == commitRef.CommitId) // We already know that the name matches { return; } } // The caller expected a previous commit, but we didn't find one else if (previousCommitId.HasValue) { throw BuildConcurrencyException(branch, commitRef.Name, null); // TODO: May need a different error } try { // Required to create blob before appending to it await blobRef.CreateOrReplaceAsync(ifMatchCondition, default, default, cancellationToken).ConfigureAwait(false); // Note etag access condition
public override async Task WriteCommitRefAsync(CommitId?previousCommitId, string name, CommitRef commitRef, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentNullException(nameof(name)); } if (commitRef == CommitRef.Empty) { throw new ArgumentNullException(nameof(commitRef)); } // We only write to last repo var last = Chain[Chain.Length - 1]; await last.WriteCommitRefAsync(previousCommitId, name, commitRef, cancellationToken).ConfigureAwait(false); }
public override async Task WriteCommitRefAsync(CommitId?previousCommitId, string branch, CommitRef commitRef, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(branch)) { throw new ArgumentNullException(nameof(branch)); } if (commitRef == CommitRef.Empty) { throw new ArgumentNullException(nameof(commitRef)); } // TODO: Optimistic concurrency var filename = DeriveCommitRefFileName(branch, commitRef.Name); var path = Path.Combine(_refsContainer, filename); // CommitIds are not compressed using (var session = Serializer.Serialize(commitRef.CommitId)) { await WriteFileAsync(path, session.Result, cancellationToken).ConfigureAwait(false); } }
public override async Task WriteCommitRefAsync(CommitId?previousCommitId, string branch, CommitRef commitRef, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(branch)) { throw new ArgumentNullException(nameof(branch)); } if (commitRef == CommitRef.Empty) { throw new ArgumentNullException(nameof(commitRef)); } // Load existing commit ref in order to use its etag var(found, existingCommitId, etag) = await ReadCommitRefImplAsync(branch, commitRef.Name, Serializer, cancellationToken).ConfigureAwait(false); // Optimistic concurrency check if (found) { // We found a previous commit but the caller didn't say to expect one if (!previousCommitId.HasValue) { throw BuildConcurrencyException(branch, commitRef.Name, null); // TODO: May need a different error } // Semantics follow Interlocked.Exchange (compare then exchange) if (previousCommitId.HasValue && existingCommitId != previousCommitId.Value) { throw BuildConcurrencyException(branch, commitRef.Name, null); } // Idempotent if (existingCommitId == commitRef.CommitId) { return; } } // The caller expected a previous commit, but we didn't find one else if (previousCommitId.HasValue) { throw BuildConcurrencyException(branch, commitRef.Name, null); // TODO: May need a different error } try { var refsTable = _refsTable.Value; // CommitIds are not compressed using (var session = Serializer.Serialize(commitRef.CommitId)) { var op = DataEntity.BuildWriteOperation(branch, commitRef.Name, session.Result, etag); // Note etag access condition await refsTable.ExecuteAsync(op).ConfigureAwait(false); } } catch (StorageException se) when(se.RequestInformation.HttpStatusCode == (int)HttpStatusCode.Conflict) { throw BuildConcurrencyException(branch, commitRef.Name, se); } }