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 async Task <ITaskResult> TryReadResultAsync(ServiceId serviceId, MethodId methodId, string intentId, Type resultValueType, CancellationToken ct) { var tableName = GetTableQualifiedName(serviceId, methodId); var key = new StorageRecord { service = serviceId.Name, method = methodId.Name, intent_id = intentId }; var query = new StringBuilder("SELECT status, outcome, format, task_result, result, error 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) { return(null); } var record = ReadValues(row); if (!record.outcome.HasValue) { return(null); } if (record.outcome.Value == (int)Outcomes.Canceled) { return(TaskResult.Create(resultValueType, null, null, isCanceled: true)); } var serializer = !string.IsNullOrEmpty(record.format) ? _serializerProvider.GetSerializer(record.format) : _serializer; var taskResult = TaskResult.CreateEmpty(resultValueType); serializer.Populate(record.task_result, (IValueContainer)taskResult); return(taskResult); }
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 async Task WriteResultAsync(ServiceId serviceId, MethodId methodId, string intentId, ITaskResult result) { var tableName = GetTableQualifiedName(serviceId, methodId); var key = new StorageRecord { service = serviceId.Name, method = methodId.Name, intent_id = intentId }; var record = new StorageRecord { etag = DateTimeOffset.UtcNow.Ticks, status = (int)Statuses.Complete, outcome = (int)(result.IsCanceled ? Outcomes.Canceled : (result.IsFaulted() ? Outcomes.Failed : Outcomes.Succeeded)), updated_at = DateTimeOffset.Now, format = _serializer.Format, task_result = _serializer.SerializeToBytes(result) }; var query = new StringBuilder("UPDATE ").Append(tableName); if (_settings.ResultTTL.HasValue) { query.Append(" USING TTL ").Append((int)_settings.ResultTTL.Value.TotalSeconds); } query.Append(" SET "); WriteValues(query, record, delimiter: ", "); query.Append(", execution_state = null, method_state = null, flow_context = null, continuation = null, continuation_state = null"); query.Append(" WHERE "); WriteValues(query, key, delimiter: " AND "); await ExecuteQueryAsync(serviceId, methodId, query.ToString()); }
private static StringBuilder WriteValues(StringBuilder sb, StorageRecord record, string delimiter) { if (record.service != null) { sb.Append("service = '").Append(record.service).Append('\'').Append(delimiter); } if (record.method != null) { sb.Append("method = '").Append(record.method).Append('\'').Append(delimiter); } if (record.intent_id != null) { sb.Append("intent_id = '").Append(record.intent_id).Append('\'').Append(delimiter); } if (record.etag != null) { sb.Append("etag = ").Append(record.etag).Append(delimiter); } if (record.status.HasValue) { sb.Append("status = ").Append(record.status.Value).Append(delimiter); } if (record.outcome.HasValue) { sb.Append("outcome = ").Append(record.outcome.Value).Append(delimiter); } if (record.last_intent_id != null) { sb.Append("last_intent_id = '").Append(record.last_intent_id).Append(delimiter); } if (record.invoked_at.HasValue) { sb.Append("invoked_at = ").AppendCqlTimestamp(record.invoked_at.Value).Append(delimiter); } if (record.started_at.HasValue) { sb.Append("started_at = ").AppendCqlTimestamp(record.started_at.Value).Append(delimiter); } if (record.updated_at.HasValue) { sb.Append("updated_at = ").AppendCqlTimestamp(record.updated_at.Value).Append(delimiter); } if (record.duration.HasValue) { sb.Append("duration = ").AppendCqlDuration(record.duration.Value).Append(delimiter); } if (record.cancel_at.HasValue) { sb.Append("cancel_at = ").AppendCqlTimestamp(record.cancel_at.Value).Append(delimiter); } if (record.cancellation_id != null) { sb.Append("cancellation_id = '").Append(record.cancellation_id).Append('\''); } if (record.transition_count.HasValue) { sb.Append("transition_count = ").Append(record.transition_count.Value).Append(delimiter); } if (record.caller_service != null) { sb.Append("caller_service = '").Append(record.caller_service).Append('\'').Append(delimiter); } if (record.caller_proxy != null) { sb.Append("caller_proxy = '").Append(record.caller_proxy).Append('\'').Append(delimiter); } if (record.caller_method != null) { sb.Append("caller_method = '").Append(record.caller_method).Append('\'').Append(delimiter); } if (record.caller_event != null) { sb.Append("caller_event = '").Append(record.caller_event).Append('\'').Append(delimiter); } if (record.caller_intent_id != null) { sb.Append("caller_intent_id = '").Append(record.caller_intent_id).Append('\'').Append(delimiter); } if (record.format != null) { sb.Append("format = '").Append(record.format).Append('\'').Append(delimiter); } if (record.execution_state?.Length > 0) { sb.Append("execution_state = ").AppendCqlBlob(record.execution_state).Append(delimiter); } if (record.method_state?.Length > 0) { sb.Append("method_state = ").AppendCqlBlob(record.method_state).Append(delimiter); } if (record.flow_context?.Length > 0) { sb.Append("flow_context = ").AppendCqlBlob(record.flow_context).Append(delimiter); } if (record.task_result?.Length > 0) { sb.Append("task_result = ").AppendCqlBlob(record.task_result).Append(delimiter); } if (record.result?.Length > 0) { sb.Append("result = ").AppendCqlBlob(record.result).Append(delimiter); } if (record.error?.Length > 0) { sb.Append("error = ").AppendCqlBlob(record.error).Append(delimiter); } if (record.continuation?.Length > 0) { sb.Append("continuation = ").AppendCqlBlob(record.continuation).Append(delimiter); } if (record.continuation_state?.Length > 0) { sb.Append("continuation_state = ").AppendCqlBlob(record.continuation_state).Append(delimiter); } // Remove last delimiter sb.Remove(sb.Length - delimiter.Length, delimiter.Length); return(sb); }