/// <summary> /// Adds matching entities and removes removed entities from indexes /// </summary> private void UpdateIndexes(RedisTransactionWrapper transaction, string hashKeyValue, IDictionary <EntityKey, Document> addedEntities, IDictionary <EntityKey, Document> modifiedEntities, ICollection <EntityKey> removedEntities) { string indexListKey = this.GetIndexListKeyInCache(hashKeyValue); foreach (var indexEntry in this._redis.GetHashFieldsWithRetries(indexListKey)) { string indexKey = indexEntry.Name; // The index itself might be dropped from cache already. We don't want the transaction to fail because of this if (!this._redis.HashFieldExistsWithRetries(indexKey, IndexVersionField.Name)) { this._redis.RemoveHashFieldsWithRetries(indexListKey, indexKey); continue; } var filter = indexEntry.Value.ToObject <SearchConditions>(); if (this.IsProjectionIndex(indexKey)) { this.UpdateProjectionIndex(transaction, hashKeyValue, indexKey, filter, addedEntities, modifiedEntities, removedEntities); } else { this.UpdateIndex(transaction, indexKey, filter, addedEntities, modifiedEntities, removedEntities); } } }
public RedisIndex(RedisTableCache parent, string indexKey, SearchConditions searchConditions) { this._parent = parent; this.IndexKeyInCache = indexKey; this._searchConditions = searchConditions; try { // (re)registering it in the list of indexes (it should be visible for update operations) this._parent._redis.SetHashWithRetries(this._parent.GetIndexListKeyInCache(), this.IndexKeyInCache, this._searchConditions.ToRedisValue()); // asynchronously checking the total number of indexes Task.Run(() => this.TryKeepIndexListSlim(indexKey)); // creating an index and marking it as being rebuilt this._parent._redis.CreateNewHashWithRetries(this.IndexKeyInCache, IndexVersionField.Name, IndexVersionField.IsBeingRebuiltValue); this.RedisTransaction = this._parent._redis.BeginTransaction(IndexVersionField.IsBeingRebuiltCondition(this.IndexKeyInCache)); this._parent.Log("Index ({0}) was marked as being rebuilt", this._searchConditions.Key); } catch (Exception ex) { this._parent.Log("Failed to start creating index: {0}", ex); // if something goes wrong - dropping the index (but only from the list - we don't want to let parallel update transaction to fail) this._parent._redis.RemoveHashFieldsWithRetries(this._parent.GetIndexListKeyInCache(), this.IndexKeyInCache); throw; } }
/// <summary> /// Adds matching entities and removes removed entities from index /// </summary> private void UpdateIndex(RedisTransactionWrapper transaction, string indexKey, SearchConditions filter, IDictionary <EntityKey, Document> addedEntities, IDictionary <EntityKey, Document> modifiedEntities, ICollection <EntityKey> removedEntities) { bool indexChanged = false; // adding added entities, if they match the index conditions foreach ( var entityPair in addedEntities.Union(modifiedEntities).Where ( entityPair => filter.MatchesSearchConditions(entityPair.Value, this._tableEntityType) ) ) { transaction.HashSet(indexKey, entityPair.Key.ToRedisValue(), string.Empty); indexChanged = true; } // removing modified entities, if they do not match the index conditions any more foreach ( var entityPair in modifiedEntities.Where ( entityPair => !filter.MatchesSearchConditions(entityPair.Value, this._tableEntityType) ) ) { transaction.HashRemove(indexKey, entityPair.Key.ToRedisValue()); indexChanged = true; } // removing removed entities foreach (var entityKey in removedEntities) { transaction.HashRemove(indexKey, entityKey.ToRedisValue()); indexChanged = true; } if (indexChanged) { // The index should exist in the cache at the moment transaction is being executed. // Otherwise this update operation will cause the index to occasionally "resurrect" after being expired. transaction.AddHashFieldExistsCondition(indexKey, IndexVersionField.Name); // also incrementing the version field transaction.HashIncrement(indexKey, IndexVersionField.Name); } }
/// <summary> /// Checks if a projection index should be dropped because of some added/modified/removed entities /// </summary> private void UpdateProjectionIndex(RedisTransactionWrapper transaction, string hashKeyValue, string indexKey, SearchConditions filter, IDictionary <EntityKey, Document> addedEntities, IDictionary <EntityKey, Document> modifiedEntities, ICollection <EntityKey> removedEntities) { if ( (modifiedEntities.Count > 0) || (removedEntities.Count > 0) || addedEntities.Values.Any // or some entities were added, that satisfy the index condition ( en => filter.MatchesSearchConditions(en, this._tableEntityType) ) ) { // then the only option for us is to drop the index - as we don't know, if these entities conform to index's conditions or not this.Log("Projection index ({0}) removed because of some modified entities", indexKey); transaction.HashRemove(this.GetIndexListKeyInCache(hashKeyValue), indexKey); transaction.Remove(indexKey); } }