public void WhileCreatingDocumentInTransactionTryingToWriteOutsideTransactionFail() { var transactionInformation = new TransactionInformation { Id = Guid.NewGuid(), Timeout = TimeSpan.FromMinutes(1) }; db.Put("ayende", null, JObject.Parse("{ayende:'rahien'}"), new JObject(), transactionInformation); Assert.Throws<ConcurrencyException>( () => db.Put("ayende", null, JObject.Parse("{ayende:'oren'}"), new JObject(), null)); }
public static void AssertNotModifiedByAnotherTransaction(TableStorage storage, ITransactionStorageActions transactionStorageActions, string key, PersistentDictionary.ReadResult readResult, TransactionInformation transactionInformation) { if (readResult == null) return; var txIdAsBytes = readResult.Key.Value<byte[]>("txId"); if (txIdAsBytes == null) return; var txId = new Guid(txIdAsBytes); if (transactionInformation != null && transactionInformation.Id == txId) { return; } var existingTx = storage.Transactions.Read(new JObject { { "txId", txId.ToByteArray() } }); if (existingTx == null)//probably a bug, ignoring this as not a real tx return; var timeout = existingTx.Key.Value<DateTime>("timeout"); if (DateTime.UtcNow > timeout) { transactionStorageActions.RollbackTransaction(txId); return; } throw new ConcurrencyException("Document '" + key + "' is locked by transacton: " + txId); }
public override void OnDelete(string key, TransactionInformation transactionInformation) { var document = Database.Get(key, null); if (document == null) return; Database.Delete(document.Metadata.Value<string>("Cascade-Delete"), null, null); }
public override void OnPut(string key, JObject document, JObject metadata, TransactionInformation transactionInformation) { if (VersioningContext.IsInVersioningContext) return; if (key.StartsWith("Raven/", StringComparison.InvariantCultureIgnoreCase)) return; if (excludeByEntityName.Contains(metadata.Value<string>("Raven-Entity-Name"))) return; if (metadata.Value<string>(RavenDocumentRevisionStatus) == "Historical") return; using(VersioningContext.Enter()) { var copyMetadata = new JObject(metadata); copyMetadata[RavenDocumentRevisionStatus] = JToken.FromObject("Historical"); copyMetadata.Remove(RavenDocumentRevision); var parentRevision = metadata.Value<string>(RavenDocumentRevision); if(parentRevision!=null) { copyMetadata[RavenDocumentParentRevision] = key + "/revisions/" + parentRevision; metadata[RavenDocumentParentRevision] = key + "/revisions/" + parentRevision; } PutResult newDoc = Database.Put(key + "/revisions/", null, document, copyMetadata, transactionInformation); int revision = int.Parse(newDoc.Key.Split('/').Last()); RemoveOldRevisions(key, revision, transactionInformation); metadata[RavenDocumentRevisionStatus] = JToken.FromObject("Current"); metadata[RavenDocumentRevision] = JToken.FromObject(revision); } }
public override void AfterDelete(string key, TransactionInformation transactionInformation) { if (ReplicationContext.IsInReplicationContext) return; Database.Put(key, null, new JObject(), new JObject(new JProperty("Raven-Delete-Marker", true)), transactionInformation); }
public void PutNewDocInTxAndThenGetItBeforeCommitInSameTransactionReturnsNonNull() { var transactionInformation = new TransactionInformation { Id = Guid.NewGuid(), Timeout = TimeSpan.FromMinutes(1) }; db.Put("ayende", null, JObject.Parse("{ayende:'rahien'}"), new JObject(), transactionInformation); Assert.NotNull(db.Get("ayende", transactionInformation)); }
public void DeletingDocumentInTransactionInNotVisibleBeforeCommit() { db.Put("ayende", null, JObject.Parse("{ayende:'oren'}"), new JObject(), null); var transactionInformation = new TransactionInformation { Id = Guid.NewGuid(), Timeout = TimeSpan.FromMinutes(1) }; db.Delete("ayende", null, transactionInformation); Assert.NotNull(db.Get("ayende", null)); }
public void CanModifyTxId() { var transactionInformation = new TransactionInformation { Id = Guid.NewGuid(), Timeout = TimeSpan.FromDays(7) }; using (var tx = new TransactionalStorage("test")) { tx.Write(mutator => mutator.Transactions.AddDocumentInTransaction("Ayende", null, JObject.FromObject(new { Name = "Rahien" }), new JObject(), transactionInformation)); var txInfo2 = new TransactionInformation { Id = Guid.NewGuid(), Timeout = TimeSpan.FromDays(1) }; tx.Write(mutator => mutator.Transactions.ModifyTransactionId(transactionInformation.Id, txInfo2.Id, txInfo2.Timeout)); tx.Read(viewer => Assert.NotNull(viewer.Documents.DocumentByKey("Ayende", txInfo2))); } }
public JsonDocument ProcessReadVetoes(JsonDocument document, TransactionInformation transactionInformation, ReadOperation operation) { if (document == null) return document; foreach (var readTrigger in triggers) { var readVetoResult = readTrigger.AllowRead(document.Key, document.DataAsJson ?? document.Projection, document.Metadata, operation, transactionInformation); switch (readVetoResult.Veto) { case ReadVetoResult.ReadAllow.Allow: break; case ReadVetoResult.ReadAllow.Deny: return new JsonDocument { DataAsJson = new JObject(), Metadata = new JObject( new JProperty("Raven-Read-Veto", new JObject(new JProperty("Reason", readVetoResult.Reason), new JProperty("Trigger", readTrigger.ToString()) )) ) }; case ReadVetoResult.ReadAllow.Ignore: return null; default: throw new ArgumentOutOfRangeException(readVetoResult.Veto.ToString()); } } return document; }
public void AfterCommittingCanSeeChangesWithoutTx() { var transactionInformation = new TransactionInformation { Id = Guid.NewGuid(), Timeout = TimeSpan.FromDays(7) }; using (var tx = new TransactionalStorage("test")) { tx.Write(mutator => mutator.Transactions.AddDocumentInTransaction("Ayende", null, JObject.FromObject(new { Name = "Rahien" }), new JObject(), transactionInformation)); tx.Write(mutator => mutator.Transactions.CompleteTransaction(transactionInformation.Id, data => { if (data.Delete) { JObject metadata; mutator.Documents.DeleteDocument(data.Key, null, out metadata); } else mutator.Documents.AddDocument(data.Key, null, data.Data, data.Metadata); })); tx.Read(viewer => Assert.NotNull(viewer.Documents.DocumentByKey("Ayende", null))); } }
private void RemoveOldRevisions(string key, int revision, VersioningConfiguration versioningConfiguration, TransactionInformation transactionInformation) { int latestValidRevision = revision - versioningConfiguration.MaxRevisions; if (latestValidRevision <= 1) return; Database.Delete(key + "/revisions/" + (latestValidRevision - 1), null, transactionInformation); }
public void WhileDocumentIsBeingUpdatedInTransactionCannotUpdateOutsideTransaction() { db.Put("ayende", null, JObject.Parse("{ayende:'oren'}"), new JObject(), null); var transactionInformation = new TransactionInformation { Id = Guid.NewGuid(), Timeout = TimeSpan.FromMinutes(1) }; db.Put("ayende", null, JObject.Parse("{ayende:'rahien'}"), new JObject(), transactionInformation); Assert.Throws<ConcurrencyException>( () => db.Put("ayende", null, JObject.Parse("{ayende:'oren'}"), new JObject(), null)); }
public override VetoResult AllowPut(string key, JObject document, JObject metadata, TransactionInformation transactionInformation) { var name = document["name"]; if(name != null && name.Value<string>().Any(char.IsUpper)) { return VetoResult.Deny("Can't use upper case characters in the 'name' property"); } return VetoResult.Allowed; }
public override VetoResult AllowPut(string key, JObject document, JObject metadata, TransactionInformation transactionInformation) { if (metadata.Value<string>(RavenDocumentRevisionStatus) != "Historical") return VetoResult.Allowed; if (Database.Get(key, transactionInformation) == null) return VetoResult.Allowed; return VetoResult.Deny("Modifying a historical revision is not allowed"); }
public override VetoResult AllowPut(string key, JObject document, JObject metadata, TransactionInformation transactionInformation) { if (VersioningContext.IsInVersioningContext) return VetoResult.Allowed; if (metadata.Value<string>(RavenDocumentRevisionStatus) == "Historical") return VetoResult.Deny("Modifying a historical revision is not allowed"); return VetoResult.Allowed; }
public void PutNewDocInTxCommitAndThenGetIt() { var transactionInformation = new TransactionInformation{Id = Guid.NewGuid(),Timeout = TimeSpan.FromMinutes(1)}; db.Put("ayende", null, JObject.Parse("{ayende:'rahien'}"), new JObject(), transactionInformation); db.Commit(transactionInformation.Id); Assert.NotNull(db.Get("ayende", null)); }
public void UpdateDocInTxAndThenGetItBeforeCommitInSameTx() { db.Put("ayende", null, JObject.Parse("{ayende:'oren'}"), new JObject(), null); var transactionInformation = new TransactionInformation { Id = Guid.NewGuid(), Timeout = TimeSpan.FromMinutes(1) }; db.Put("ayende", null, JObject.Parse("{ayende:'rahien'}"), new JObject(), transactionInformation); Assert.NotNull(db.Get("ayende", transactionInformation)); Assert.Equal("rahien", db.Get("ayende", transactionInformation).ToJson()["ayende"].Value<string>()); }
public void WhenUsingTransactionWillFailIfDocumentEtagDoesNotMatch() { db.Put("ayende", null, JObject.Parse("{ayende:'oren'}"), new JObject(), null); var transactionInformation = new TransactionInformation { Id = Guid.NewGuid(), Timeout = TimeSpan.FromMinutes(1) }; Assert.Throws<ConcurrencyException>( () => db.Put("ayende", Guid.NewGuid(), JObject.Parse("{ayende:'rahien'}"), new JObject(), transactionInformation)); }
public override ReadVetoResult AllowRead(string key, JObject document, JObject metadata, ReadOperation operation, TransactionInformation transactionInformation) { if (ReplicationContext.IsInReplicationContext) return ReadVetoResult.Allowed; JToken value; if (metadata.TryGetValue("Raven-Delete-Marker", out value)) return ReadVetoResult.Ignore; return ReadVetoResult.Allowed; }
public void WhenUsingTransactionWillWorkIfDocumentEtagMatch() { db.Put("ayende", null, JObject.Parse("{ayende:'oren'}"), new JObject(), null); var doc = db.Get("ayende", null); var transactionInformation = new TransactionInformation { Id = Guid.NewGuid(), Timeout = TimeSpan.FromMinutes(1) }; db.Put("ayende", doc.Etag, JObject.Parse("{ayende:'rahien'}"), new JObject(), transactionInformation); db.Commit(transactionInformation.Id); Assert.Equal("rahien", db.Get("ayende", null).ToJson()["ayende"].Value<string>()); }
public void DeletingDocumentAndThenAddingDocumentInSameTransactionCanWork() { db.Put("ayende", null, JObject.Parse("{ayende:'oren'}"), new JObject(), null); var transactionInformation = new TransactionInformation { Id = Guid.NewGuid(), Timeout = TimeSpan.FromMinutes(1) }; db.Delete("ayende", null, transactionInformation); db.Put("ayende", null, JObject.Parse("{ayende:'rahien'}"), new JObject(), transactionInformation); db.Commit(transactionInformation.Id); Assert.Equal("rahien", db.Get("ayende", null).ToJson()["ayende"].Value<string>()); }
public void AfterPuttingDocInTxWillChangeEtag() { db.Put("ayende", null, JObject.Parse("{ayende:'oren'}"), new JObject(), null); var doc = db.Get("ayende", null); var transactionInformation = new TransactionInformation { Id = Guid.NewGuid(), Timeout = TimeSpan.FromMinutes(1) }; db.Put("ayende", doc.Etag, JObject.Parse("{ayende:'rahien'}"), new JObject(), transactionInformation); var docInTx = db.Get("ayende", transactionInformation); db.Put("ayende", docInTx.Etag, JObject.Parse("{ayende:'rahien'}"), new JObject(), transactionInformation); Assert.NotEqual(doc.Etag, docInTx.Etag); }
private void RemoveOldRevisions(string key, int revision, TransactionInformation transactionInformation) { if (maxRevisions == null) return; int latestValidRevision = revision - maxRevisions.Value; if (latestValidRevision <= 1) return; Database.Delete(key + "/revisions/" + (latestValidRevision - 1), null, transactionInformation); }
public override void OnDelete(string key, TransactionInformation transactionInformation) { if (ReplicationContext.IsInReplicationContext) return; var document = Database.Get(key, transactionInformation); if (document == null) return; deletedSource.Value = document.Metadata[ReplicationConstants.RavenReplicationSource]; deletedVersion.Value = document.Metadata[ReplicationConstants.RavenReplicationVersion]; }
private JsonDocument ExecuteReadTriggersOnRead(JsonDocument resultingDocument, TransactionInformation transactionInformation, ReadOperation operation) { if (resultingDocument == null) return null; foreach (var readTrigger in triggers) { readTrigger.OnRead(resultingDocument.Key, resultingDocument.DataAsJson, resultingDocument.Metadata, operation, transactionInformation); } return resultingDocument; }
public void CommittingWillOnlyCommitSingleTransaction() { var transactionInformation = new TransactionInformation { Id = Guid.NewGuid(), Timeout = TimeSpan.FromMinutes(1) }; db.Put("ayende1", null, JObject.Parse("{ayende:'rahien'}"), new JObject(), transactionInformation); db.Put("ayende2", null, JObject.Parse("{ayende:'rahien'}"), new JObject(), new TransactionInformation { Id = Guid.NewGuid(), Timeout = TimeSpan.FromMinutes(1) }); db.Commit(transactionInformation.Id); Assert.NotNull(db.Get("ayende1", null)); Assert.Null(db.Get("ayende2", null)); }
public override VetoResult AllowDelete(string key, TransactionInformation transactionInformation) { if(allowDeletiongOfHistoricalDocuments) return VetoResult.Allowed; JsonDocument document = Database.Get(key, transactionInformation); if (document == null) return VetoResult.Allowed; if (document.Metadata.Value<string>(VersioningPutTrigger.RavenDocumentRevisionStatus) != "Historical") return VetoResult.Allowed; return VetoResult.Deny("Deleting a historical revision is not allowed"); }
public void PutTwoDocumentsAndThenCommitReversedOrder() { var transactionInformation = new TransactionInformation { Id = Guid.NewGuid(), Timeout = TimeSpan.FromMinutes(1) }; db.Put("ayende2", null, JObject.Parse("{ayende:'rahien'}"), new JObject(), transactionInformation); db.Put("ayende1", null, JObject.Parse("{ayende:'rahien'}"), new JObject(), transactionInformation); db.Commit(transactionInformation.Id); Assert.NotNull(db.Get("ayende1", null)); Assert.NotNull(db.Get("ayende2", null)); }
private void EnsureTransactionExists(TransactionInformation transactionInformation) { Api.JetSetCurrentIndex(session, Transactions, "by_tx_id"); Api.MakeKey(session, Transactions, transactionInformation.Id.ToByteArray(), MakeKeyGrbit.NewKey); var isUpdate = Api.TrySeek(session, Transactions, SeekGrbit.SeekEQ); using (var update = new Update(session, Transactions, isUpdate ? JET_prep.Replace : JET_prep.Insert)) { Api.SetColumn(session, Transactions, tableColumnsCache.TransactionsColumns["tx_id"], transactionInformation.Id.ToByteArray()); Api.SetColumn(session, Transactions, tableColumnsCache.TransactionsColumns["timeout"], DateTime.UtcNow + transactionInformation.Timeout); update.Save(); } }
public override ReadVetoResult AllowRead(string key, JObject document, JObject metadata, ReadOperation operation, TransactionInformation transactionInformation) { if(metadata == null) return ReadVetoResult.Allowed; var property = metadata.Property(RavenExpirationDate); if (property == null) return ReadVetoResult.Allowed; var dateTime = property.Value.Value<DateTime>(); if(dateTime > GetCurrentUtcDate()) return ReadVetoResult.Allowed; return ReadVetoResult.Ignore; }
private JsonDocument ExecuteReadTriggersOnRead(JsonDocument resultingDocument, TransactionInformation transactionInformation, ReadOperation operation) { if (resultingDocument == null) { return(null); } foreach (var readTrigger in ReadTriggers) { readTrigger.OnRead(resultingDocument.Key, resultingDocument.DataAsJson, resultingDocument.Metadata, operation, transactionInformation); } return(resultingDocument); }
private void AssertPutOperationNotVetoed(string key, JObject metadata, JObject document, TransactionInformation transactionInformation) { var vetoResult = PutTriggers .Select(trigger => new{ Trigger = trigger, VetoResult = trigger.AllowPut(key, document, metadata, transactionInformation) }) .FirstOrDefault(x => x.VetoResult.IsAllowed == false); if (vetoResult != null) { throw new OperationVetoedException("PUT vetoed by " + vetoResult.Trigger + " because: " + vetoResult.VetoResult.Reason); } }
public PatchResult ApplyPatch(string docId, Guid?etag, PatchRequest[] patchDoc, TransactionInformation transactionInformation) { var result = PatchResult.Patched; TransactionalStorage.Batch(actions => { var doc = actions.DocumentByKey(docId, transactionInformation); if (doc == null) { result = PatchResult.DocumentDoesNotExists; } else if (etag != null && doc.Etag != etag.Value) { throw new ConcurrencyException("Could not patch document '" + docId + "' because non current etag was used") { ActualETag = doc.Etag, ExpectedETag = etag.Value, }; } else { var jsonDoc = doc.ToJson(); new JsonPatcher(jsonDoc).Apply(patchDoc); Put(doc.Key, doc.Etag, jsonDoc, doc.Metadata, transactionInformation); result = PatchResult.Patched; } workContext.ShouldNotifyAboutWork(); }); return(result); }
public PutResult Put(string key, Guid?etag, JObject document, JObject metadata, TransactionInformation transactionInformation) { if (string.IsNullOrEmpty(key)) { Guid value; UuidCreateSequential(out value); key = value.ToString(); } RemoveReservedProperties(document); RemoveReservedProperties(metadata); TransactionalStorage.Batch(actions => { metadata["Last-Modified"] = JToken.FromObject(DateTime.UtcNow.ToString("r")); if (key.EndsWith("/")) { key += actions.GetNextIdentityValue(key); } metadata.Add("@id", new JValue(key)); if (transactionInformation == null) { AssertPutOperationNotVetoed(key, metadata, document, transactionInformation); PutTriggers.Apply(trigger => trigger.OnPut(key, document, metadata, transactionInformation)); etag = actions.AddDocument(key, etag, document, metadata); AddIndexingTask(actions, metadata, () => new IndexDocumentsTask { Keys = new[] { key } }); PutTriggers.Apply(trigger => trigger.AfterPut(key, document, metadata, transactionInformation)); } else { etag = actions.AddDocumentInTransaction(key, etag, document, metadata, transactionInformation); } workContext.ShouldNotifyAboutWork(); }); TransactionalStorage .ExecuteImmediatelyOrRegisterForSyncronization(() => PutTriggers.Apply(trigger => trigger.AfterCommit(key, document, metadata))); return(new PutResult { Key = key, ETag = (Guid)etag }); }