internal QueryStream(MigratingTable outer, TableQuery <TElement> query, TableRequestOptions requestOptions, OperationContext operationContext) { this.outer = outer; origFilterExpr = ChainTableUtils.ParseFilterString(query.FilterString); mtableQuery = ChainTableUtils.CopyQuery <TElement, MTableEntity>(query); this.requestOptions = requestOptions; this.operationContext = operationContext; }
public override Task <IQueryStream <TElement> > ExecuteQueryStreamedAsync <TElement>(TableQuery <TElement> query, TableRequestOptions requestOptions = null, OperationContext operationContext = null) { FilterExpression filterExpr = ChainTableUtils.ParseFilterString(query.FilterString); if (query.SelectColumns != null) { throw new NotImplementedException("select"); } if (query.TakeCount != null) { throw new NotImplementedException("top"); } return(Task.FromResult((IQueryStream <TElement>) new NondeterministicQueryStream <TElement>(this, CurrentRevision, filterExpr))); }
internal async Task RunQueryStreamedAsync(TableQuery <DynamicTableEntity> query) { int startRevision = await peekProxy.GetReferenceTableRevisionAsync(); FilterExpression filterExpr = ChainTableUtils.ParseFilterString(query.FilterString); Console.WriteLine("{0} starting streaming query: {1}", machineId, query); using (IQueryStream <DynamicTableEntity> stream = await migratingTable.ExecuteQueryStreamedAsync(query)) { PrimaryKey lastKey = ChainTableUtils.FirstValidPrimaryKey; for (;;) { PrimaryKey returnedContinuationKey = await stream.GetContinuationPrimaryKeyAsync(); PSharpRuntime.Assert(returnedContinuationKey == null || returnedContinuationKey.CompareTo(lastKey) >= 0, "{0}: query stream continuation key is {1}, expected >= {2}", machineId, returnedContinuationKey, lastKey); DynamicTableEntity row = await stream.ReadRowAsync(); // may be null, meaning end of stream // Must be after ReadRowAsync, otherwise additional rows could become valid // due to a mutation between GetValidStreamReadRows and ReadRowAsync and // we would falsely report a bug if ReadRowAsync returns one of those rows. List <DynamicTableEntity> validRows = await peekProxy.GetValidStreamReadRows(startRevision, filterExpr, lastKey); // Three cheers for automatic use of covariance in overload resolution! PSharpRuntime.Assert(validRows.Contains(row, BetterComparer.Instance), "{0} query stream returned {1}, which is not one of the valid rows: {2}", machineId, BetterComparer.ToString(row), BetterComparer.ToString(validRows)); Console.WriteLine("{0} query stream returned row {1}, which is valid", machineId, BetterComparer.ToString(row)); if (row == null) { // Any returnedContinuationKey (including null) is less or equal to a row of null. break; } else { PSharpRuntime.Assert(returnedContinuationKey != null && returnedContinuationKey.CompareTo(row.GetPrimaryKey()) <= 0, "{0}: query stream continuation key is {1}, expected <= {2}", machineId, returnedContinuationKey, row.GetPrimaryKey()); lastKey = ChainTableUtils.NextValidPrimaryKeyAfter(row.GetPrimaryKey()); } } } Console.WriteLine("{0} finished streaming query", machineId); }
public override Task <IList <TElement> > ExecuteQueryAtomicAsync <TElement>( TableQuery <TElement> query, TableRequestOptions requestOptions = null, OperationContext operationContext = null) { FilterExpression filterExpr = ChainTableUtils.ParseFilterString(query.FilterString); ChainTableUtils.GetSingleTargetedPartitionKey(filterExpr); // validation, ignore result if (query.SelectColumns != null) { throw new NotImplementedException("select"); } if (query.TakeCount != null) { throw new NotImplementedException("top"); } return(Task.FromResult( (IList <TElement>)(from kvp in table where filterExpr.Evaluate(kvp.Value) select ChainTableUtils.CopyEntity <TElement>(kvp.Value)).ToList())); }
public override Task <IQueryStream <TElement> > ExecuteQueryStreamedAsync <TElement>( TableQuery <TElement> query, TableRequestOptions requestOptions = null, OperationContext operationContext = null) { FilterExpression filterExpr = ChainTableUtils.ParseFilterString(query.FilterString); if (query.SelectColumns != null) { throw new NotImplementedException("select"); } if (query.TakeCount != null) { throw new NotImplementedException("top"); } // Easy deterministic implementation compliant with IChainTable2 // API: scan a snapshot. InMemoryTableWithHistory has the // nondeterministic implementation. // XXX: Nobody calls this any more. We could delete the code and // throw NotImplementedException. return(Task.FromResult((IQueryStream <TElement>) new QueryStream <TElement>(filterExpr, table.GetEnumerator()))); }
public override Task <IQueryStream <TElement> > ExecuteQueryStreamedAsync <TElement>( TableQuery <TElement> query, TableRequestOptions requestOptions = null, OperationContext operationContext = null) { FilterExpression filterExpr = ChainTableUtils.ParseFilterString(query.FilterString); if (query.SelectColumns != null) { throw new NotImplementedException("select"); } if (query.TakeCount != null) { throw new NotImplementedException("top"); } // Easy deterministic implementation compliant with IChainTable2 // API: scan a snapshot. // XXX: At least as an option, this should choose // nondeterministically (using P#) from all behaviors compliant with // the API to verify that callers can handle it. return(Task.FromResult((IQueryStream <TElement>) new QueryStream <TElement>(filterExpr, table.GetEnumerator()))); }
public override async Task <IList <TElement> > ExecuteQueryAtomicAsync <TElement>(TableQuery <TElement> query, TableRequestOptions requestOptions = null, OperationContext operationContext = null) { if (query.SelectColumns != null) { throw new NotImplementedException("select"); } if (query.TakeCount != null) { throw new NotImplementedException("top"); } FilterExpression origFilterExpr = ChainTableUtils.ParseFilterString(query.FilterString); string partitionKey = ChainTableUtils.GetSingleTargetedPartitionKey(origFilterExpr); TableQuery <MTableEntity> mtableQuery = ChainTableUtils.CopyQuery <TElement, MTableEntity>(query); MTableConfiguration config; using (configService.Subscribe(FixedSubscriber <MTableConfiguration> .Instance, out config)) { IEnumerable <MTableEntity> results; if (config.state <= TableClientState.USE_OLD_HIDE_METADATA) { results = await oldTable.ExecuteQueryAtomicAsync(mtableQuery, requestOptions, operationContext); await monitor.AnnotateLastBackendCallAsync(wasLinearizationPoint : true); } else if (config.state >= TableClientState.USE_NEW_WITH_TOMBSTONES) { results = await newTable.ExecuteQueryAtomicAsync(mtableQuery, requestOptions, operationContext); await monitor.AnnotateLastBackendCallAsync(wasLinearizationPoint : true); } else { // Modify the filter to make sure it matches the meta row. if (origFilterExpr is ComparisonExpression) { // filterExpr must be "PartitionKey eq A", which already matches the meta row; nothing to do. } else { // filterExpr must be "(PartitionKey eq A) and (B)". // Rewrite to "(PartitionKey eq A) and ((RowKey eq ROW_KEY_PARTITION_META) or (B))". var boe = (BooleanOperatorExpression)origFilterExpr; mtableQuery.FilterString = TableQuery.CombineFilters(boe.Left.ToFilterString(), TableOperators.And, TableQuery.CombineFilters( TableQuery.GenerateFilterCondition(TableConstants.RowKey, QueryComparisons.Equal, ROW_KEY_PARTITION_META), TableOperators.Or, boe.Right.ToFilterString())); } IList <MTableEntity> oldRows = await oldTable.ExecuteQueryAtomicAsync(mtableQuery, requestOptions, operationContext); MTablePartitionState?state = (from r in oldRows where r.RowKey == ROW_KEY_PARTITION_META select r.partitionState).SingleOrDefault(); IList <MTableEntity> newRows; if (state == MTablePartitionState.SWITCHED) { await monitor.AnnotateLastBackendCallAsync(); // If the filter string includes conditions on user-defined properties, // a row in the old table can be shadowed by a row in the new table // that doesn't satisfy those conditions. To make sure we retrieve all // potential shadowing rows, retrieve the entire partition. // XXX: At a minimum, we should try to keep conditions on the // primary key, but how clever do we want to get with query rewriting? if (!IsBugEnabled(MTableOptionalBug.QueryAtomicFilterShadowing)) { mtableQuery.FilterString = TableQuery.GenerateFilterCondition( TableConstants.PartitionKey, QueryComparisons.Equal, partitionKey); } newRows = await newTable.ExecuteQueryAtomicAsync(mtableQuery, requestOptions, operationContext); await monitor.AnnotateLastBackendCallAsync(wasLinearizationPoint : true); } else { await monitor.AnnotateLastBackendCallAsync(wasLinearizationPoint : true); newRows = new List <MTableEntity>(); } // Merge lists. Hopefully this is pretty clear. Walking the lists // in lockstep might be faster but is a lot more code. var merged = new SortedDictionary <string, MTableEntity>(StringComparer.Ordinal); foreach (MTableEntity ent in oldRows) { merged[ent.RowKey] = ent; } foreach (MTableEntity ent in newRows) { merged[ent.RowKey] = ent; } results = merged.Values; } return((from ent in results where !RowKeyIsInternal(ent.RowKey) && origFilterExpr.Evaluate(ent) && !ent.deleted select ent.Export <TElement>()).ToList()); } }