Ejemplo n.º 1
0
        // Marks the partition populated unless it is already switched.
        async Task TryMarkPartitionPopulatedAsync(string partitionKey, TableRequestOptions requestOptions, OperationContext operationContext)
        {
            var markPopulatedBatch = new TableBatchOperation();

            markPopulatedBatch.Insert(new MTableEntity
            {
                PartitionKey   = partitionKey,
                RowKey         = ROW_KEY_PARTITION_META,
                partitionState = MTablePartitionState.POPULATED
            });
            markPopulatedBatch.Insert(new DynamicTableEntity
            {
                PartitionKey = partitionKey,
                RowKey       = ROW_KEY_PARTITION_POPULATED_ASSERTION
            });
            try
            {
                await oldTable.ExecuteBatchAsync(markPopulatedBatch, requestOptions, operationContext);
            }
            // XXX: Optimization opportunity: if we swap the order of the
            // inserts, we can tell here if the partition is already switched.
            catch (StorageException ex) {
                if (ex.GetHttpStatusCode() != HttpStatusCode.Conflict)
                {
                    throw ChainTableUtils.GenerateInternalException(ex);
                }
            }
            await monitor.AnnotateLastBackendCallAsync();
        }
Ejemplo n.º 2
0
        // NOTE: Mutates entity
        internal async Task TryCopyEntityToNewTableAsync(MTableEntity entity, TableRequestOptions requestOptions, OperationContext operationContext)
        {
            try
            {
                await newTable.ExecuteAsync(TableOperation.Insert(entity), requestOptions, operationContext);
            }
            catch (StorageException ex)
            {
                if (ex.GetHttpStatusCode() != HttpStatusCode.Conflict)
                {
                    throw ChainTableUtils.GenerateInternalException(ex);
                }
                await monitor.AnnotateLastBackendCallAsync();

                return;
            }
            await monitor.AnnotateLastBackendCallAsync(
                spuriousETagChanges : new List <SpuriousETagChange> {
                new SpuriousETagChange(entity.PartitionKey, entity.RowKey, entity.ETag)
            });
        }
Ejemplo n.º 3
0
        async Task CleanupAsync(TableRequestOptions requestOptions, OperationContext operationContext)
        {
            // Clean up MTable-specific data from new table.
            // Future: Query only ones with properties we need to clean up.
            IQueryStream <MTableEntity> newTableStream = await newTable.ExecuteQueryStreamedAsync(
                new TableQuery <MTableEntity>(), requestOptions, operationContext);

            MTableEntity newEntity;

            while ((newEntity = await newTableStream.ReadRowAsync()) != null)
            {
                // XXX: Consider factoring out this "query and retry" pattern
                // into a separate method.
Attempt:
                TableOperation cleanupOp = newEntity.deleted ? TableOperation.Delete(newEntity)
                    : TableOperation.Replace(newEntity.Export <DynamicTableEntity>());
                TableResult cleanupResult;
                try
                {
                    cleanupResult = await newTable.ExecuteAsync(cleanupOp, requestOptions, operationContext);
                }
                catch (StorageException ex)
                {
                    if (ex.GetHttpStatusCode() == HttpStatusCode.NotFound)
                    {
                        // Someone else deleted it concurrently.  Nothing to do.
                        await monitor.AnnotateLastBackendCallAsync();

                        continue;
                    }
                    else if (ex.GetHttpStatusCode() == HttpStatusCode.PreconditionFailed)
                    {
                        await monitor.AnnotateLastBackendCallAsync();

                        // Unfortunately we can't assume that anyone who concurrently modifies
                        // the row while the table is in state USE_NEW_HIDE_METADATA will
                        // clean it up, because of InsertOrMerge.  (Consider redesign?)

                        // Re-retrieve row.
                        TableResult retrieveResult = await newTable.ExecuteAsync(
                            TableOperation.Retrieve <MTableEntity>(newEntity.PartitionKey, newEntity.RowKey),
                            requestOptions, operationContext);

                        await monitor.AnnotateLastBackendCallAsync();

                        if ((HttpStatusCode)retrieveResult.HttpStatusCode == HttpStatusCode.NotFound)
                        {
                            continue;
                        }
                        else
                        {
                            newEntity = (MTableEntity)retrieveResult.Result;
                            goto Attempt;
                        }
                    }
                    else
                    {
                        throw ChainTableUtils.GenerateInternalException(ex);
                    }
                }
                await monitor.AnnotateLastBackendCallAsync(
                    spuriousETagChanges :
                    cleanupResult.Etag == null?null : new List <SpuriousETagChange> {
                    new SpuriousETagChange(newEntity.PartitionKey, newEntity.RowKey, cleanupResult.Etag)
                });
            }

            // Delete everything from old table!  No one should be modifying it concurrently.
            IQueryStream <MTableEntity> oldTableStream = await oldTable.ExecuteQueryStreamedAsync(
                new TableQuery <MTableEntity>(), requestOptions, operationContext);

            MTableEntity oldEntity;

            while ((oldEntity = await oldTableStream.ReadRowAsync()) != null)
            {
                await oldTable.ExecuteAsync(TableOperation.Delete(oldEntity), requestOptions, operationContext);

                await monitor.AnnotateLastBackendCallAsync();
            }
        }
Ejemplo n.º 4
0
        internal async Task EnsurePartitionSwitchedAsync(string partitionKey, TableRequestOptions requestOptions, OperationContext operationContext)
        {
            var metaQuery = new TableQuery <MTableEntity>
            {
                FilterString = ChainTableUtils.GeneratePointRetrievalFilterCondition(
                    new PrimaryKey(partitionKey, ROW_KEY_PARTITION_META))
            };

Recheck:
            MTablePartitionState? state;
            if (IsBugEnabled(MTableOptionalBug.EnsurePartitionSwitchedFromPopulated))
            {
                state = null;
            }
            else
            {
                state =
                    (from r in (await oldTable.ExecuteQueryAtomicAsync(metaQuery, requestOptions, operationContext))
                     select r.partitionState).SingleOrDefault();
                await monitor.AnnotateLastBackendCallAsync();
            }
            switch (state)
            {
            case null:
                try
                {
                    await oldTable.ExecuteAsync(TableOperation.Insert(new MTableEntity
                    {
                        PartitionKey   = partitionKey,
                        RowKey         = ROW_KEY_PARTITION_META,
                        partitionState = MTablePartitionState.SWITCHED
                    }), requestOptions, operationContext);
                }
                catch (StorageException ex)
                {
                    if (ex.GetHttpStatusCode() != HttpStatusCode.Conflict)
                    {
                        throw ChainTableUtils.GenerateInternalException(ex);
                    }
                    if (!IsBugEnabled(MTableOptionalBug.EnsurePartitionSwitchedFromPopulated))
                    {
                        await monitor.AnnotateLastBackendCallAsync();

                        // We could now be in POPULATED or SWITCHED.
                        // XXX: In production, what's more likely?  Is it faster
                        // to recheck first or just try the case below?
                        goto Recheck;
                    }
                }
                await monitor.AnnotateLastBackendCallAsync();

                return;

            case MTablePartitionState.POPULATED:
                try
                {
                    var batch = new TableBatchOperation();
                    batch.Replace(new MTableEntity
                    {
                        PartitionKey   = partitionKey,
                        RowKey         = ROW_KEY_PARTITION_META,
                        ETag           = ChainTable2Constants.ETAG_ANY,
                        partitionState = MTablePartitionState.SWITCHED
                    });
                    batch.Delete(new MTableEntity
                    {
                        PartitionKey = partitionKey,
                        RowKey       = ROW_KEY_PARTITION_POPULATED_ASSERTION,
                        ETag         = ChainTable2Constants.ETAG_ANY,
                    });
                    await oldTable.ExecuteBatchAsync(batch, requestOptions, operationContext);
                }
                catch (ChainTableBatchException ex)
                {
                    // The only way this can fail (within the semantics) is
                    // if someone else moved the partition to SWITCHED.
                    if (!(ex.FailedOpIndex == 1 && ex.GetHttpStatusCode() == HttpStatusCode.NotFound))
                    {
                        throw ChainTableUtils.GenerateInternalException(ex);
                    }
                }
                await monitor.AnnotateLastBackendCallAsync();

                return;

            case MTablePartitionState.SWITCHED:
                // Nothing to do
                return;
            }
        }
Ejemplo n.º 5
0
        async Task CleanupAsync(TableRequestOptions requestOptions, OperationContext operationContext)
        {
            string previousPartitionKey = null;

            await WalkTableInParallel(newTable, requestOptions, operationContext, async (newEntity) =>
            {
                if (newEntity.PartitionKey != previousPartitionKey)
                {
                    if (previousPartitionKey != null)
                    {
                        Console.WriteLine("Cleaned Partition: {0}", previousPartitionKey);
                    }
                    previousPartitionKey = newEntity.PartitionKey;
                }

                // XXX: Consider factoring out this "query and retry" pattern
                // into a separate method.
Attempt:
                TableOperation cleanupOp = newEntity.deleted ? TableOperation.Delete(newEntity)
                    : TableOperation.Replace(newEntity.Export <DynamicTableEntity>());
                TableResult cleanupResult;
                StorageException ex;
                try
                {
                    cleanupResult = await newTable.ExecuteAsync(cleanupOp, requestOptions, operationContext);
                    ex            = null;
                }
                catch (StorageException exToHandle)
                {
                    cleanupResult = null;
                    ex            = exToHandle;
                }
                if (ex != null)
                {
                    if (ex.GetHttpStatusCode() == HttpStatusCode.NotFound)
                    {
                        // Someone else deleted it concurrently.  Nothing to do.
                        //await monitor.AnnotateLastBackendCallAsync();
                        return((IQueryStream <MTableEntity>)null);
                    }
                    else if (ex.GetHttpStatusCode() == HttpStatusCode.PreconditionFailed)
                    {
                        //await monitor.AnnotateLastBackendCallAsync();

                        // Unfortunately we can't assume that anyone who concurrently modifies
                        // the row while the table is in state USE_NEW_HIDE_METADATA will
                        // clean it up, because of InsertOrMerge.  (Consider redesign?)

                        // Re-retrieve row.
                        TableResult retrieveResult = await newTable.ExecuteAsync(
                            TableOperation.Retrieve <MTableEntity>(newEntity.PartitionKey, newEntity.RowKey),
                            requestOptions, operationContext);
                        //await monitor.AnnotateLastBackendCallAsync();
                        if ((HttpStatusCode)retrieveResult.HttpStatusCode == HttpStatusCode.NotFound)
                        {
                            return(null);
                        }
                        else
                        {
                            newEntity = (MTableEntity)retrieveResult.Result;
                            goto Attempt;
                        }
                    }
                    else
                    {
                        throw ChainTableUtils.GenerateInternalException(ex);
                    }
                }
                else
                {
                    await monitor.AnnotateLastBackendCallAsync(
                        spuriousETagChanges:
                        cleanupResult.Etag == null ? null : new List <SpuriousETagChange>
                    {
                        new SpuriousETagChange(newEntity.PartitionKey, newEntity.RowKey, cleanupResult.Etag)
                    });
                }

                return((IQueryStream <MTableEntity>)null);
            });

            await WalkTableInParallel(oldTable, requestOptions, operationContext, async (oldEntity) =>
            {
                await oldTable.ExecuteAsync(TableOperation.Delete(oldEntity), requestOptions, operationContext);
                await monitor.AnnotateLastBackendCallAsync();

                return((IQueryStream <MTableEntity>)null);
            });
        }