Reputation: 133
In our application, we are using service fabric indexed dictionaries to store data. We are also using Service Fabric Queryable package to add querying capabilities on our collections.
In our case, we are having a transaction in which we do multiple updates and commit all of them at once. We have also created indexes on some properties for faster retrieval of data. Due to this indexing a record is internally created in a index dictionary with the indexed property as the key and the object as the value. This is also an AddOrUpdate operation and is included in the same transaction. And the transaction which is used to perform these update operation acquires an exclusive lock on all the newly created records.
Using the same transaction, when we try to perform a query on the queryable indexed dictionary using the property which has been indexed, we are getting a Timeout Exception.
We have gone through the source code in the Queryable extensions package and found that the query which we run on the queryable dictionary creates a new transaction to fetch the filtered records. But this transaction is unable to access the records as the previous transaction in our code has acquired an Exclusive lock on the record.
Below is some sample code which describes our scenario.
public async Task MyMethod()
{
using (var tx = this.StateManager.CreateTransaction())
{
foreach(var record in records)
{
// Here we perform an update operation
// The transaction takes an exclusive lock on the created record to perform this operation and releases only after it is committed
var result = await indexedDictionary.AddOrUpdateAsync(tx, myKey, myValue, (key, value) => myValue);
// Creating a Queryable dictionary
var queryableDictionary = new QueryableReliableIndexedDictionary(indexedDictionary, this.StateManager);
// Running a query on the queryableDictionary
var filteredRecords = queryableDictionary.Where(x => x.indexedProperty== propertyValue).ToList();
// The code to filter the data exists in the Service Fabric Queryable package where a new transaction is being created.
// Once we query with where condition on the queryable dictionary, the Execute method gets called in the package where the actual filtering takes place.
// That code looks similar to the code snippet given below.
}
await tx.CommitAsync();
}
}
Below is the code snippet from the Service Fabric Queryable package.
internal static async Task<object> Execute<TKey, TValue>(Expression expression)
{
// This transaction is being created which tries to access the records actual dictionary
using (var tx = stateManager.CreateTransaction())
{
// This statement tries to access the dictionary.. but it can't as the previous transaction has acquired an exclusive lock on the record it is trying to access
// It waits for 4 seconds and throws a Timeout Exception
IEnumerable<KeyValuePair<TKey, TValue>> pairs = await indexedDictionary.GetAllAsync(tx, keys, TimeSpan.FromSeconds(4), new CancellationToken()).AsEnumerable();
values = new KeyValueToValueEnumerable<TKey, TValue>(pairs);
await tx.CommitAsync();
}
}
Is there any work around for us to be able to query the queryable dictionary without having to commit the previous transaction ?
Upvotes: 0
Views: 171
Reputation: 11470
You could create a Pull Request in which you add an overload that takes your current transaction as a parameter, and uses that instead of creating one within the method.
Upvotes: 0