Пример #1
0
 static RevisionsStorage()
 {
     using (StorageEnvironment.GetStaticContext(out var ctx))
     {
         Slice.From(ctx, "RevisionsChangeVector", ByteStringType.Immutable, out var changeVectorSlice);
         Slice.From(ctx, "RevisionsIdAndEtag", ByteStringType.Immutable, out IdAndEtagSlice);
         Slice.From(ctx, "DeleteRevisionEtag", ByteStringType.Immutable, out DeleteRevisionEtagSlice);
         Slice.From(ctx, "AllRevisionsEtags", ByteStringType.Immutable, out AllRevisionsEtagsSlice);
         Slice.From(ctx, "CollectionRevisionsEtags", ByteStringType.Immutable, out CollectionRevisionsEtagsSlice);
         Slice.From(ctx, "RevisionsCount", ByteStringType.Immutable, out RevisionsCountSlice);
         Slice.From(ctx, nameof(ResolvedFlagByEtagSlice), ByteStringType.Immutable, out ResolvedFlagByEtagSlice);
         Slice.From(ctx, RevisionsTombstones, ByteStringType.Immutable, out RevisionsTombstonesSlice);
         Slice.From(ctx, CollectionName.GetTablePrefix(CollectionTableType.Revisions), ByteStringType.Immutable, out RevisionsPrefix);
         RevisionsSchema.DefineKey(new TableSchema.SchemaIndexDef
         {
             StartIndex = (int)RevisionsTable.ChangeVector,
             Count      = 1,
             Name       = changeVectorSlice,
             IsGlobal   = true
         });
         RevisionsSchema.DefineIndex(new TableSchema.SchemaIndexDef
         {
             StartIndex = (int)RevisionsTable.LowerId,
             Count      = 3,
             Name       = IdAndEtagSlice,
             IsGlobal   = true
         });
         RevisionsSchema.DefineFixedSizeIndex(new TableSchema.FixedSizeSchemaIndexDef
         {
             StartIndex = (int)RevisionsTable.Etag,
             Name       = AllRevisionsEtagsSlice,
             IsGlobal   = true
         });
         RevisionsSchema.DefineFixedSizeIndex(new TableSchema.FixedSizeSchemaIndexDef
         {
             StartIndex = (int)RevisionsTable.Etag,
             Name       = CollectionRevisionsEtagsSlice
         });
         RevisionsSchema.DefineIndex(new TableSchema.SchemaIndexDef
         {
             StartIndex = (int)RevisionsTable.DeletedEtag,
             Count      = 1,
             Name       = DeleteRevisionEtagSlice,
             IsGlobal   = true
         });
         RevisionsSchema.DefineIndex(new TableSchema.SchemaIndexDef
         {
             StartIndex = (int)RevisionsTable.Resolved,
             Count      = 2,
             Name       = ResolvedFlagByEtagSlice,
             IsGlobal   = true
         });
     }
 }
Пример #2
0
        public bool Update(UpdateStep step)
        {
            var legacyCounterCollectionTables = new List <string>();

            using (var it = step.ReadTx.LowLevelTransaction.RootObjects.Iterate(prefetch: false))
            {
                if (it.Seek(Slices.BeforeAllKeys) == false)
                {
                    return(true);
                }

                var legacyCounterCollectionTablePrefix = CollectionName.GetTablePrefix(CollectionTableType.Counters);

                do
                {
                    var current         = it.CurrentKey;
                    var currentAsString = current.ToString();

                    if (currentAsString.StartsWith(legacyCounterCollectionTablePrefix, StringComparison.OrdinalIgnoreCase))
                    {
                        var type = step.ReadTx.GetRootObjectType(current);

                        if (type != RootObjectType.Table)
                        {
                            continue; // precaution
                        }
                        legacyCounterCollectionTables.Add(currentAsString);
                    }
                } while (it.MoveNext());
            }

            // this schema update uses DocumentsStorage.GenerateNextEtag() so we need to read and set LastEtag in storage
            step.DocumentsStorage.InitializeLastEtag(step.ReadTx);

            step.DocumentsStorage.CountersStorage = new CountersStorage(step.DocumentsStorage.DocumentDatabase, step.WriteTx);

            _dbId = ReadDbId(step);

            // legacy counters processing

            string currentDocId = null;
            var    batch        = new CounterBatchUpdate();
            var    dbIds        = new HashSet <string>();

            var done = false;

            while (done == false)
            {
                var readTable = new Table(LegacyCountersSchema, step.WriteTx);

                var countersTree = readTable.GetTree(LegacyCountersSchema.Key);

                if (countersTree == null)
                {
                    return(true);
                }

                var processedInCurrentTx = 0;

                var toDeleteCounterEtagsByCollection  = new Dictionary <CollectionName, List <long> >();
                var toDeleteEtagsForCurrentDocumentId = new List <long>();

                using (step.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext context))
                {
                    context.TransactionMarkerOffset = (short)step.WriteTx.LowLevelTransaction.Id;

                    var commit = false;

                    foreach (var item in GetCounters(readTable, context))
                    {
                        var counterDetail = item.Counter;

                        if (currentDocId == counterDetail.DocumentId)
                        {
                            if (batch.Counters.TryGetValue(counterDetail.CounterName, out var list) == false)
                            {
                                list = new List <CounterDetail>();
                                batch.Counters.Add(counterDetail.CounterName, list);
                            }

                            list.Add(counterDetail);
                        }
                        else
                        {
                            if (currentDocId != null)
                            {
                                var docCollection = PutCounters(step, context, dbIds, batch.Counters, currentDocId);

                                if (docCollection != null)
                                {
                                    if (toDeleteCounterEtagsByCollection.TryGetValue(docCollection, out var toDeleteList) == false)
                                    {
                                        toDeleteList = new List <long>();
                                        toDeleteCounterEtagsByCollection.Add(docCollection, toDeleteList);
                                    }

                                    toDeleteList.AddRange(toDeleteEtagsForCurrentDocumentId);
                                    toDeleteEtagsForCurrentDocumentId.Clear();
                                }

                                batch.Clear();

                                if (processedInCurrentTx >= NumberOfCountersToMigrateInSingleTransaction)
                                {
                                    foreach (var toDeleteForCollection in toDeleteCounterEtagsByCollection)
                                    {
                                        var table = step.WriteTx.OpenTable(LegacyCountersSchema, toDeleteForCollection.Key.GetTableName(CollectionTableType.Counters));

                                        DeleteMigratedLegacyCounters(table, toDeleteForCollection.Value);
                                    }

                                    toDeleteCounterEtagsByCollection.Clear();

                                    commit = true;
                                    break;
                                }
                            }

                            batch.Clear();

                            currentDocId = counterDetail.DocumentId;

                            batch.Counters.Add(counterDetail.CounterName, new List <CounterDetail>
                            {
                                counterDetail
                            });
                        }

                        toDeleteEtagsForCurrentDocumentId.Add(item.Counter.Etag);

                        using (var dbId = ExtractDbId(context, counterDetail.CounterKey))
                        {
                            dbIds.Add(dbId.ToString());
                        }

                        processedInCurrentTx++;
                    }

                    if (commit)
                    {
                        step.Commit(context);
                        step.RenewTransactions();

                        step.DocumentsStorage.CountersStorage = new CountersStorage(step.DocumentsStorage.DocumentDatabase, step.WriteTx);

                        currentDocId = null;
                        continue;
                    }

                    if (batch.Counters.Count > 0)
                    {
                        PutCounters(step, context, dbIds, batch.Counters, currentDocId);
                        batch.Clear();
                    }

                    if (toDeleteCounterEtagsByCollection.Count > 0)
                    {
                        foreach (var toDeleteForCollection in toDeleteCounterEtagsByCollection)
                        {
                            var table = step.WriteTx.OpenTable(LegacyCountersSchema, toDeleteForCollection.Key.GetTableName(CollectionTableType.Counters));

                            DeleteMigratedLegacyCounters(table, toDeleteForCollection.Value);
                        }
                    }

                    done = true;
                }

                // we must delete tables first before deleting any global index trees from the root that can be in use by tables
                foreach (var tableName in legacyCounterCollectionTables)
                {
                    step.WriteTx.DeleteTable(tableName);
                }

                // let's remove remaining counter trees from the root

                if (step.WriteTx.LowLevelTransaction.RootObjects.Read(CounterKeysSlice) != null)
                {
                    step.WriteTx.DeleteTree(CounterKeysSlice);
                }

                if (step.WriteTx.LowLevelTransaction.RootObjects.Read(AllCountersEtagSlice) != null)
                {
                    step.WriteTx.DeleteFixedTree(AllCountersEtagSlice.ToString());
                }
            }

            // legacy counter tombstones processing

            var counterTombstones = step.ReadTx.OpenTable(TombstonesSchema, CountersTombstonesSlice);

            if (counterTombstones != null)
            {
                // for each counter tombstone, delete the matching
                // counter from the new table (if exists)
                using (step.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext context))
                {
                    foreach (var result in counterTombstones.SeekByPrimaryKeyPrefix(Slices.BeforeAllKeys, Slices.Empty, 0))
                    {
                        var t = new Tombstone
                        {
                            LowerId = TableValueToString(context, (int)TombstoneTable.LowerId, ref result.Value.Reader),
                            Type    = *(Tombstone.TombstoneType *)result.Value.Reader.Read((int)TombstoneTable.Type, out _),
                        };

                        if (t.Type != Tombstone.TombstoneType.Counter)
                        {
                            continue;
                        }

                        DeleteCounter(step, t.LowerId, context);

                        t.LowerId.Dispose();
                    }

                    // delete counter-tombstones from Tombstones table
                    var countersTombstoneTable = step.WriteTx.OpenTable(TombstonesSchema, CountersTombstonesSlice);
                    DeleteFromTable(context, countersTombstoneTable, TombstonesSchema.Key, tvh =>
                    {
                        var type = *(Tombstone.TombstoneType *)tvh.Reader.Read((int)TombstoneTable.Type, out _);
                        return(type != Tombstone.TombstoneType.Counter);
                    });
                }

                step.WriteTx.DeleteTable(CountersTombstonesSlice.ToString());
            }

            return(true);
        }