/// <summary> /// Reads the VBucketUUID and Sequence Number from the extras if the instance has a <see cref="VBucket"/> - /// only persistent Couchbase buckets that use VBucket Hashing support mutation tokens. /// </summary> /// <param name="buffer">The memcached response buffer.</param> public override void ReadExtras(byte[] buffer) { if (buffer.Length >= 40 && VBucket != null) { var uuid = Converter.ToInt64(buffer, 24); var seqno = Converter.ToInt64(buffer, 32); MutationToken = new MutationToken(VBucket.BucketName, (short)VBucket.Index, uuid, seqno); } }
public void AddToMutationState_FirstRealTokenThenNull_DoesNothing() { // Arrange var bucket = new Mock<IBucket>(); bucket.SetupGet(m => m.Name).Returns("default"); var db = new BucketContext(bucket.Object); var token = new MutationToken("default", 1, 2, 3); db.AddToMutationState(token); // Act db.AddToMutationState(null); // Assert Assert.IsNotNull(db.MutationState); var tokens = MutationStateToList(db.MutationState); Assert.AreEqual(1, tokens.Count); Assert.Contains(token, tokens); }
public void SubmitChanges_WithSave_AddsToMutationState() { // Arrange var token = new MutationToken("default", 1, 2, 3); var bucket = new Mock<IBucket>(); bucket.SetupGet(m => m.Name).Returns("default"); bucket .Setup(m => m.Upsert(It.IsAny<string>(), It.IsAny<object>())) .Returns(() => { var result = new Mock<IOperationResult<Beer>>(); result.SetupGet(p => p.Success).Returns(true); result.SetupGet(p => p.Token).Returns(token); return result.Object; }); var db = new Mock<BucketContext>(bucket.Object) { CallBase = true }; db.Setup(m => m.GetDocumentId(It.IsAny<Beer>())).Returns("id"); db.Object.BeginChangeTracking(); db.Object.Save(new Beer()); // Act db.Object.SubmitChanges(); // Assert db.Verify(m => m.AddToMutationState(token), Times.Once); }
public void SubmitChanges_WithRemove_AddsToMutationState() { // Arrange var token = new MutationToken("default", 1, 2, 3); var bucket = new Mock<IBucket>(); bucket.SetupGet(m => m.Name).Returns("default"); bucket .Setup(m => m.Remove(It.IsAny<string>())) .Returns(() => { var result = new Mock<IOperationResult<Beer>>(); result.SetupGet(p => p.Success).Returns(true); result.SetupGet(p => p.Token).Returns(token); return result.Object; }); var db = new Mock<BucketContext>(bucket.Object) { CallBase = true }; db.Setup(m => m.GetDocumentId(It.IsAny<object>())).Returns("id"); var document = new Mock<ITrackedDocumentNode>(); document.SetupGet(m => m.Metadata).Returns(new DocumentMetadata() { Id = "id" }); document.SetupAllProperties(); db.Object.BeginChangeTracking(); ((IChangeTrackableContext) db.Object).Track(document.Object); db.Object.Remove(document.Object); // Act db.Object.SubmitChanges(); // Assert db.Verify(m => m.AddToMutationState(token), Times.Once); }
public void AddToMutationState_TwoRealTokens_CombinesTokens() { // Arrange var bucket = new Mock<IBucket>(); bucket.SetupGet(m => m.Name).Returns("default"); var db = new BucketContext(bucket.Object); var token1 = new MutationToken("default", 1, 2, 3); var token2 = new MutationToken("default", 4, 5, 6); // Act db.AddToMutationState(token1); db.AddToMutationState(token2); // Assert Assert.IsNotNull(db.MutationState); var tokens = MutationStateToList(db.MutationState); Assert.AreEqual(2, tokens.Count); Assert.Contains(token1, tokens); Assert.Contains(token2, tokens); }
/// <summary> /// Observes the specified key using the Seqno. /// </summary> /// <param name="key">The key.</param> /// <param name="token">The token.</param> /// <param name="replicateTo">The replicate to.</param> /// <param name="persistTo">The persist to.</param> /// <returns>True if durability constraints were matched.</returns> /// <exception cref="DocumentMutationLostException">Thrown if the observed document was lost during /// a hard failover because the document did not reach the replica in time.</exception> /// <exception cref="ReplicaNotConfiguredException">Thrown if the number of replicas requested /// in the ReplicateTo parameter does not match the # of replicas configured on the server.</exception> public bool Observe(MutationToken token, ReplicateTo replicateTo, PersistTo persistTo) { var keyMapper = (VBucketKeyMapper)_configInfo.GetKeyMapper(); var p = new ObserveParams { ReplicateTo = replicateTo, PersistTo = persistTo, Token = token, VBucket = keyMapper[token.VBucketId] }; p.CheckConfiguredReplicas(); var op = new ObserveSeqno(p.Token, _transcoder, _timeout); do { var master = p.VBucket.LocatePrimary(); var result = master.Send(op); var osr = result.Value; p.CheckMutationLost(osr); p.CheckPersisted(osr); if (p.IsDurabilityMet()) { return true; } if (CheckReplicas(p, op)) { return true; } //prepare for another attempt op = (ObserveSeqno)op.Clone(); p.Reset(); } while (!op.TimedOut()); return false; }
/// <summary> /// Performs an observe event on the durability requirements specified on a key asynchronously /// </summary> /// <param name="token">The <see cref="MutationToken"/> to compare against.</param> /// <param name="replicateTo">The number of replicas that the key must be replicated to to satisfy the durability constraint.</param> /// <param name="persistTo">The number of replicas that the key must be persisted to to satisfy the durability constraint.</param> /// <returns> A <see cref="Task{bool}"/> representing the aynchronous operation.</returns> /// <exception cref="ReplicaNotConfiguredException">Thrown if the number of replicas requested /// in the ReplicateTo parameter does not match the # of replicas configured on the server.</exception> public async Task<bool> ObserveAsync(MutationToken token, ReplicateTo replicateTo, PersistTo persistTo) { var keyMapper = (VBucketKeyMapper)_configInfo.GetKeyMapper(); var obParams = new ObserveParams { ReplicateTo = replicateTo, PersistTo = persistTo, Token = token, VBucket = keyMapper[token.VBucketId] }; obParams.CheckConfiguredReplicas(); var op = new ObserveSeqno(obParams.Token, _transcoder, _timeout); using (var cts = new CancellationTokenSource((int)_timeout)) { //perform the observe operation at the set interval and terminate if not successful by the timeout var task = await ObserveEvery(async p => { IServer master; var attempts = 0; while ((master = p.VBucket.LocatePrimary()) == null) { if (attempts++ > 10) { throw new TimeoutException("Could not acquire a server."); } await Task.Delay((int)Math.Pow(2, attempts)).ContinueOnAnyContext(); } var result = master.Send(op); var osr = result.Value; p.CheckMutationLost(osr); p.CheckPersisted(osr); if (p.IsDurabilityMet()) { return true; } return await CheckReplicasAsync(p, op).ContinueOnAnyContext(); }, obParams, _interval, op, cts.Token).ContinueOnAnyContext(); return task; } }
/// <summary> /// Performs an observe event on the durability requirements specified on a key asynchronously /// </summary> /// <param name="token">The <see cref="MutationToken"/> to compare against.</param> /// <param name="replicateTo">The number of replicas that the key must be replicated to to satisfy the durability constraint.</param> /// <param name="persistTo">The number of replicas that the key must be persisted to to satisfy the durability constraint.</param> /// <param name="cts"></param> /// <returns> A <see cref="Task{boolean}"/> representing the aynchronous operation.</returns> /// <exception cref="ReplicaNotConfiguredException">Thrown if the number of replicas requested /// in the ReplicateTo parameter does not match the # of replicas configured on the server.</exception> public async Task<bool> ObserveAsync(MutationToken token, ReplicateTo replicateTo, PersistTo persistTo, CancellationTokenSource cts) { var keyMapper = (VBucketKeyMapper) _configInfo.GetKeyMapper(); var obParams = new ObserveParams { ReplicateTo = replicateTo, PersistTo = persistTo, Token = token, VBucket = keyMapper[token.VBucketId] }; obParams.CheckConfiguredReplicas(); var persisted = await CheckPersistToAsync(obParams).ContinueOnAnyContext(); var replicated = await CheckReplicasAsync(obParams).ContinueOnAnyContext(); if (persisted && replicated) { Log.DebugFormat("Persisted and replicated on first try: {0}", _key); return true; } return await ObserveEveryAsync(async p => { Log.DebugFormat("trying again: {0}", _key); persisted = await CheckPersistToAsync(obParams).ContinueOnAnyContext(); replicated = await CheckReplicasAsync(obParams).ContinueOnAnyContext(); return persisted & replicated; }, obParams, _interval, cts.Token); }