public async Task <WriteShardDataResponse> WriteShardData(ShardData data, ShardOperationOptions operationType, string operationId, DateTime transactionDate) { ShardWriteOperation operation = new ShardWriteOperation() { Data = data, Id = operationId, Operation = operationType, TransactionDate = transactionDate }; ShardWriteOperation lastOperation = await GetOrPopulateOperationCache(operation.Data.ShardId.Value); //Start at 1 operation.Pos = lastOperation == null ? 1 : lastOperation.Pos + 1; var hash = lastOperation == null ? "" : lastOperation.ShardHash; operation.ShardHash = ObjectUtility.HashStrings(hash, operation.Id); _logger.LogDebug(_nodeStateService.GetNodeLogId() + "writing new operation " + operationId + " with data " + Environment.NewLine + JsonConvert.SerializeObject(data, Formatting.Indented)); //Write the data var writeOperation = await _shardRepository.AddShardWriteOperationAsync(operation); //Add shard operation if (writeOperation) { ApplyOperationToDatastore(operation); var shardMetadata = _stateMachine.GetShard(operation.Data.ShardType, operation.Data.ShardId.Value); //Mark operation as applied await _shardRepository.MarkShardWriteOperationAppliedAsync(operation.Id); //Update the cache UpdateOperationCache(operation.Data.ShardId.Value, operation); ConcurrentBag <Guid> InvalidNodes = new ConcurrentBag <Guid>(); //All allocations except for your own var tasks = shardMetadata.InsyncAllocations.Where(id => id != _nodeStateService.Id).Select(async allocation => { try { var result = await _clusterClient.Send(allocation, new ReplicateShardWriteOperation() { Operation = operation }); if (result.IsSuccessful) { _logger.LogDebug(_nodeStateService.GetNodeLogId() + "Successfully replicated all " + shardMetadata.Id + "shards."); } else { throw new Exception("Failed to replicate data to shard " + shardMetadata.Id + " to node " + allocation + " for operation " + operation.ToString() + Environment.NewLine + JsonConvert.SerializeObject(operation, Formatting.Indented)); } } catch (TaskCanceledException e) { _logger.LogError(_nodeStateService.GetNodeLogId() + "Failed to replicate shard " + shardMetadata.Id + " on node " + allocation + " for operation " + operation.Pos + " as request timed out, marking shard as not insync..."); InvalidNodes.Add(allocation); } catch (Exception e) { _logger.LogError(_nodeStateService.GetNodeLogId() + "Failed to replicate shard " + shardMetadata.Id + " for operation " + operation.Pos + " with error " + e.Message + ", marking shard as not insync..." + Environment.NewLine + e.StackTrace); InvalidNodes.Add(allocation); } }); await Task.WhenAll(tasks); if (InvalidNodes.Count() > 0) { await _clusterClient.Send(new ExecuteCommands() { Commands = new List <BaseCommand>() { new UpdateShardMetadataAllocations() { ShardId = data.ShardId.Value, Type = data.ShardType, StaleAllocationsToAdd = InvalidNodes.ToHashSet(), InsyncAllocationsToRemove = InvalidNodes.ToHashSet() } }, WaitForCommits = true }); _logger.LogInformation(_nodeStateService.GetNodeLogId() + " had stale virtual machines."); } return(new WriteShardDataResponse() { Pos = operation.Pos, ShardHash = operation.ShardHash, IsSuccessful = true }); } else { return(new WriteShardDataResponse() { IsSuccessful = false }); } }
public async Task <List <ShardWriteOperation> > GetShardWriteOperationsAsync(Guid shardId, ShardOperationOptions option, int limit) { return(await _shardWriteOperations.Find(lsm => lsm.Operation == option && lsm.Data.ShardId == shardId).SortBy(l => l.TransactionDate).Limit(limit).ToListAsync()); }