예제 #1
0
        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);
        }
예제 #2
0
        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));
        }
예제 #3
0
        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());
        }
예제 #4
0
        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);
        }
예제 #5
0
        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));
        }
예제 #6
0
        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));
            }
        }
예제 #7
0
        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;
            }
        }
예제 #8
0
 public StateNotFoundException(ServiceId service, PersistedMethodId method) : this()
 {
     Service = service;
     Method  = method;
 }
예제 #9
0
 private string GetStateFileName(ServiceId serviceId, PersistedMethodId methodId)
 {
     // TODO: suffix for the format? e.g. '.json', '.gz'
     return($"{methodId.IntentId}.{serviceId.Name}.{methodId.Name}.state");
 }