public static ChangeVectorEntry[] MergeVectors(ChangeVectorEntry[] vectorA, ChangeVectorEntry[] vectorB)
        {
            var merged = new ChangeVectorEntry[Math.Max(vectorA.Length, vectorB.Length)];
            var inx    = 0;

            foreach (var entryA in vectorA)
            {
                var etagA = entryA.Etag;
                ChangeVectorEntry first = new ChangeVectorEntry();
                foreach (var e in vectorB)
                {
                    if (e.DbId == entryA.DbId)
                    {
                        first = e;
                        break;
                    }
                }
                var etagB = first.Etag;

                merged[inx++] = new ChangeVectorEntry
                {
                    DbId = entryA.DbId,
                    Etag = Math.Max(etagA, etagB)
                };
            }
            return(merged);
        }
        public static ChangeVectorEntry[] ReadChangeVectorFrom(Tree tree)
        {
            var changeVector = new ChangeVectorEntry[tree.State.NumberOfEntries];

            using (var iter = tree.Iterate(false))
            {
                if (iter.Seek(Slices.BeforeAllKeys) == false)
                {
                    return(changeVector);
                }
                var buffer = new byte[sizeof(Guid)];
                int index  = 0;
                do
                {
                    var read = iter.CurrentKey.CreateReader().Read(buffer, 0, sizeof(Guid));
                    if (read != sizeof(Guid))
                    {
                        throw new InvalidDataException($"Expected guid, but got {read} bytes back for change vector");
                    }

                    changeVector[index].DbId = new Guid(buffer);
                    changeVector[index].Etag = iter.CreateReaderForCurrent().ReadBigEndianInt64();
                    index++;
                } while (iter.MoveNext());
            }
            return(changeVector);
        }
Exemple #3
0
        public async Task PUT_of_conflicted_document_with_outdated_etag_throws_concurrency_exception()
        {
            using (var storeA = GetDocumentStore())
                using (var storeB = GetDocumentStore())
                {
                    using (var session = storeA.OpenSession())
                    {
                        session.Store(new User {
                            Name = "John Doe"
                        }, "users/1");
                        session.SaveChanges();
                    }

                    using (var session = storeB.OpenSession())
                    {
                        session.Store(new User {
                            Name = "Jane Doe"
                        }, "users/1");
                        session.SaveChanges();
                    }

                    await SetupReplicationAsync(storeA, storeB);
                    await SetupReplicationAsync(storeB, storeA);

                    WaitUntilHasConflict(storeA, "users/1");

                    long maxConflictEtag;
                    using (var session = storeA.OpenSession())
                    {
                        var ex = Assert.Throws <DocumentConflictException>(() => session.Load <User>("users/1"));
                        maxConflictEtag = ex.LargestEtag;
                    }
                    //should throw concurrency exception because we use lower etag then max etag of existing conflicts
                    using (var session = storeA.OpenSession())
                    {
                        var db = GetDocumentDatabaseInstanceFor(storeA).Result;
                        var cv = new ChangeVectorEntry[1];
                        cv[0] = new ChangeVectorEntry
                        {
                            DbId = db.DbBase64Id,
                            Etag = maxConflictEtag - 1
                        };
                        session.Store(new User {
                            Name = "James Doe"
                        }, cv.SerializeVector(), "users/1");
                        Assert.Throws <ConcurrencyException>(() => session.SaveChanges());
                    }

                    //now this should _not_ throw, since we do not specify expected conflict etag, so...
                    using (var session = storeA.OpenSession())
                    {
                        session.Store(new User {
                            Name = "James Doe"
                        }, "users/1");
                        session.SaveChanges();
                    }
                }
        }
        public static ChangeVectorEntry[] GetChangeVectorEntriesFromTableValueReader(TableValueReader tvr, int index)
        {
            int size;
            var pChangeVector = (ChangeVectorEntry *)tvr.Read(index, out size);
            var changeVector  = new ChangeVectorEntry[size / sizeof(ChangeVectorEntry)];

            for (int i = 0; i < changeVector.Length; i++)
            {
                changeVector[i] = pChangeVector[i];
            }
            return(changeVector);
        }
        private ChangeVectorEntry[] GetMergedConflictChangeVectorsAndDeleteConflicts(Transaction tx, TransactionOperationContext context, Slice name, long newEtag, ChangeVectorEntry[] existing = null)
        {
            var conflictChangeVectors = DeleteConflictsFor(tx, name);

            //no conflicts, no need to merge
            if (conflictChangeVectors.Count == 0)
            {
                if (existing != null)
                {
                    return(ReplicationUtils.UpdateChangeVectorWithNewEtag(_environment.DbId, newEtag, existing));
                }

                return(new[]
                {
                    new ChangeVectorEntry
                    {
                        Etag = newEtag,
                        DbId = _environment.DbId
                    }
                });
            }

            // need to merge the conflict change vectors
            var maxEtags = new Dictionary <Guid, long>
            {
                [_environment.DbId] = newEtag
            };

            foreach (var conflictChangeVector in conflictChangeVectors)
            {
                foreach (var entry in conflictChangeVector)
                {
                    long etag;
                    if (maxEtags.TryGetValue(entry.DbId, out etag) == false ||
                        etag < entry.Etag)
                    {
                        maxEtags[entry.DbId] = entry.Etag;
                    }
                }
            }

            var changeVector = new ChangeVectorEntry[maxEtags.Count];

            var index = 0;

            foreach (var maxEtag in maxEtags)
            {
                changeVector[index].DbId = maxEtag.Key;
                changeVector[index].Etag = maxEtag.Value;
                index++;
            }
            return(changeVector);
        }
Exemple #6
0
        public async Task DELETE_of_conflicted_document_with_outdated_etag_throws_concurrency_exception()
        {
            using (var storeA = GetDocumentStore(options: new Options
            {
                ModifyDatabaseRecord = record =>
                {
                    record.ConflictSolverConfig = new ConflictSolver
                    {
                        ResolveToLatest = false,
                        ResolveByCollection = new Dictionary <string, ScriptResolver>()
                    };
                }
            }))
                using (var storeB = GetDocumentStore(options: new Options
                {
                    ModifyDatabaseRecord = record =>
                    {
                        record.ConflictSolverConfig = new ConflictSolver
                        {
                            ResolveToLatest = false,
                            ResolveByCollection = new Dictionary <string, ScriptResolver>()
                        };
                    }
                }))
                {
                    using (var session = storeA.OpenSession())
                    {
                        session.Store(new User {
                            Name = "John Doe"
                        }, "users/1");
                        session.SaveChanges();
                    }

                    using (var session = storeB.OpenSession())
                    {
                        session.Store(new User {
                            Name = "Jane Doe"
                        }, "users/1");
                        session.SaveChanges();
                    }

                    await SetupReplicationAsync(storeA, storeB);
                    await SetupReplicationAsync(storeB, storeA);

                    WaitUntilHasConflict(storeA, "users/1");

                    long maxConflictEtag;
                    using (var session = storeA.OpenSession())
                    {
                        var ex = Assert.Throws <DocumentConflictException>(() => session.Load <User>("users/1"));
                        maxConflictEtag = ex.LargestEtag;
                    }

                    //should throw concurrency exception because we use lower etag then max etag of existing conflicts
                    using (var session = storeA.OpenSession())
                    {
                        var db = Databases.GetDocumentDatabaseInstanceFor(storeA).Result;
                        var cv = new ChangeVectorEntry[1];
                        cv[0] = new ChangeVectorEntry
                        {
                            DbId = db.DbBase64Id,
                            Etag = maxConflictEtag - 1
                        };
                        session.Delete("users/1", cv.SerializeVector());
                        Assert.Throws <ConcurrencyException>(() => session.SaveChanges());
                    }

                    //now this should _not_ throw, since we do not specify expected conflict etag, so...
                    using (var session = storeA.OpenSession())
                    {
                        session.Delete("users/1");
                        session.SaveChanges();
                    }
                }
        }