public async Task <MethodExecutionState> ReadStateAsync(ServiceId serviceId, PersistedMethodId methodId, CancellationToken ct) { var fileName = GetStateFileName(serviceId, methodId); var filePath = Path.Combine(_stateDirectory, fileName); string etag; byte[] data; try { using (var fileStream = new FileStream( filePath, FileMode.Open, FileAccess.Read, FileShare.Read, FileBufferSize, FileOptions.Asynchronous)) { etag = TryGetETag(filePath); data = new byte[fileStream.Length]; await fileStream.ReadAsync(data, 0, data.Length); } } catch (IOException) { throw new StateNotFoundException(serviceId, methodId); } // TODO: select serializer var executionState = _serializer.Deserialize <MethodExecutionState>(data); return(executionState); }
public Task <MethodExecutionState> ReadStateAsync( ServiceId serviceId, PersistedMethodId methodId, CancellationToken ct) { MethodExecutionState executionState; string serializedFlowContext; string serializedContinuation; string continuationStateFormat; byte[] continuationStateData; lock (_entryMap) { if (!_entryMap.TryGetValue(methodId.IntentId, out var entry)) { throw new StateNotFoundException(serviceId, methodId); } executionState = new MethodExecutionState { Service = serviceId, Method = methodId, Caller = entry.TryGetValue("Caller", out var callerObj) ? callerObj as CallerDescriptor : null, MethodState = new SerializedValueContainer((string)entry["State"], _serializer) }; executionState.Method.ETag = entry.ETag; serializedFlowContext = entry.TryGetValue("FlowContext", out var flowContextObj) ? flowContextObj as string : null; serializedContinuation = entry.TryGetValue("Continuation", out var continuationObj) ? continuationObj as string : null; continuationStateFormat = entry.TryGetValue("Continuation:Format", out var continuationStateFormatObj) ? continuationStateFormatObj as string : null; continuationStateData = entry.TryGetValue("Continuation:State", out var continuationStateDataObj) ? continuationStateDataObj as byte[] : null; } if (!string.IsNullOrEmpty(serializedFlowContext)) { executionState.FlowContext = _serializer.Deserialize <Dictionary <string, string> >(serializedFlowContext); } if (!string.IsNullOrEmpty(serializedContinuation)) { executionState.Continuation = _serializer.Deserialize <ContinuationDescriptor>(serializedContinuation); } if (continuationStateData?.Length > 0) { executionState.ContinuationState = new SerializedMethodContinuationState { Format = continuationStateFormat, State = continuationStateData }; } return(Task.FromResult(executionState)); }
public async Task <string> WriteStateAsync(ServiceId serviceId, PersistedMethodId methodId, MethodExecutionState state) { var tableName = GetTableQualifiedName(serviceId, methodId); var key = new StorageRecord { service = serviceId.Name, method = methodId.Name, intent_id = methodId.IntentId }; var record = new StorageRecord { etag = DateTimeOffset.UtcNow.Ticks, status = (int)Statuses.Paused, updated_at = DateTimeOffset.Now, caller_service = state.Caller?.Service?.Name, caller_proxy = state.Caller?.Service?.Proxy, caller_method = state.Caller?.Method?.Name, caller_event = state.Caller?.Event?.Name, caller_intent_id = state.Caller?.IntentId, format = _serializer.Format, execution_state = _serializer.SerializeToBytes(state), }; var query = new StringBuilder("UPDATE ").Append(tableName); query.Append(" SET "); WriteValues(query, record, delimiter: ", "); query.Append(" WHERE "); WriteValues(query, key, delimiter: " AND "); if (!string.IsNullOrEmpty(methodId.ETag)) { query.Append(" IF etag = ").AppendCqlText(methodId.ETag); } var result = await ExecuteQueryAsync(serviceId, methodId, query.ToString()); if (result != null) { var row = result.FirstOrDefault(); if (row != null && !row.GetValue <bool>("[applied]")) { var actualETag = row.GetValueOrDefault <long>("etag"); throw new ETagMismatchException(methodId.ETag, actualETag.ToString()); } } return(record.etag.ToString()); }
public void OnRoutineStart( IServiceReference serviceRef, IMethodReference methodRef, PersistedMethodId methodId, object serviceInstance, IAsyncStateMachine routineStateMachine, CallerDescriptor caller) { Context.ServiceRef = serviceRef; Context.MethodRef = methodRef; Context.MethodId = methodId; Context.ServiceInstance = serviceInstance; Context.RoutineStateMachine = routineStateMachine; Context.Caller = caller; _intrinsicFlowController.OnRoutineStart(this); }
public async Task <MethodExecutionState> ReadStateAsync(ServiceId serviceId, PersistedMethodId methodId, CancellationToken ct) { var tableName = GetTableQualifiedName(serviceId, methodId); var key = new StorageRecord { service = serviceId.Name, method = methodId.Name, intent_id = methodId.IntentId }; var query = new StringBuilder("SELECT * FROM ") .Append(tableName).Append(" WHERE "); WriteValues(query, key, delimiter: " AND "); var result = await ExecuteQueryAsync(serviceId, methodId, query.ToString()); var row = result.FirstOrDefault(); if (row == null) { throw new StateNotFoundException(serviceId, methodId); } var record = ReadValues(row); if (record.status.HasValue && record.status.Value != (int)Statuses.Paused) { throw new InvalidOperationException("Method is not paused"); } if (!(record.execution_state?.Length > 0)) { throw new InvalidOperationException("Empty state data"); } var serializer = !string.IsNullOrEmpty(record.format) ? _serializerProvider.GetSerializer(record.format) : _serializer; return(serializer.Deserialize <MethodExecutionState>(record.execution_state)); }
public Task <string> WriteStateAsync( ServiceId serviceId, PersistedMethodId methodId, MethodExecutionState state) { var serializedState = _serializer.SerializeToString(state.MethodState); var serializedContinuation = state.Continuation != null?_serializer.SerializeToString(state.Continuation) : null; var serializedFlowContext = state.FlowContext?.Count > 0 ? _serializer.SerializeToString(state.FlowContext) : null; var expectedETag = methodId.ETag; var intentId = methodId.IntentId; lock (_entryMap) { if (!_entryMap.TryGetValue(intentId, out var entry)) { entry = new StorageEntry(); _entryMap.Add(intentId, entry); } else if (!string.IsNullOrEmpty(expectedETag) && entry.ETag != expectedETag) { throw new ETagMismatchException(expectedETag, entry.ETag); } entry.ETag = DateTimeOffset.UtcNow.Ticks.ToString(); entry["ServiceId"] = state.Service.Clone(); entry["MethodId"] = state.Method.Clone(); entry["Caller"] = state.Caller?.Clone(); entry["State"] = serializedState; entry["Continuation"] = serializedContinuation; entry["FlowContext"] = serializedFlowContext; if (state.ContinuationState != null) { entry["Continuation:Format"] = state.ContinuationState.Format; entry["Continuation:State"] = state.ContinuationState.State; } return(Task.FromResult(entry.ETag)); } }
public async Task <string> WriteStateAsync( ServiceId serviceId, PersistedMethodId methodId, MethodExecutionState state) { var fileName = GetStateFileName(serviceId, methodId); var filePath = Path.Combine(_stateDirectory, fileName); var data = _serializer.SerializeToBytes(state); EnsureDirectoryExists(_stateDirectory); @TryWrite: var tryCount = 10; try { using (var fileStream = new FileStream( filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, FileBufferSize, FileOptions.Asynchronous | FileOptions.WriteThrough)) { var etag = TryGetETag(filePath); if (fileStream.Length > 0 && !string.IsNullOrEmpty(methodId.ETag) && methodId.ETag != etag) { throw new ETagMismatchException(methodId.ETag, etag); } await fileStream.WriteAsync(data, 0, data.Length); fileStream.SetLength(fileStream.Position); return(etag); } } catch (IOException) when(tryCount > 0) { await Task.Yield(); tryCount--; goto @TryWrite; } }
public StateNotFoundException(ServiceId service, PersistedMethodId method) : this() { Service = service; Method = method; }
private string GetStateFileName(ServiceId serviceId, PersistedMethodId methodId) { // TODO: suffix for the format? e.g. '.json', '.gz' return($"{methodId.IntentId}.{serviceId.Name}.{methodId.Name}.state"); }