예제 #1
0
        /// <inheritdoc />
        public async Task <DataPersisterUpdateResult <T> > Update(CancellationToken ct, DataPersisterUpdaterWithContext <T> updater)
        {
            using (await _updateGate.LockAsync(ct))
            {
                var result = await _inner.Update(ct, updater);

                if (result.IsUpdated)
                {
                    _update.OnNext(result);
                }

                return(result);
            }
        }
        /// <inheritdoc />
        public async Task <DataPersisterUpdateResult <T> > Update(CancellationToken ct, DataPersisterUpdaterWithContext <T> updater)
        {
            using (await _lock.LockAsync(ct))
            {
                // 1) Lock the file (see LOCKING PROCESS below)
                using (await GetFileLock(ct))
                {
                    // 2) Read the COMMITTED file
                    T    data   = default(T);
                    bool exists = false;
                    DataPersisterTransactionContext <T> control;
                    try
                    {
                        if (File.Exists(_committedFile))
                        {
                            using (var stream = File.OpenRead(_committedFile))
                            {
                                data = await _read(ct, stream);

                                exists = true;
                            }
                        }
                        control = new DataPersisterTransactionContext <T>(data, exists);
                    }
                    catch (FileNotFoundException)
                    {
                        control = new DataPersisterTransactionContext <T>(data, isValuePresent: false);
                    }
                    catch (Exception ex)
                    {
                        var exceptionInfo = ExceptionDispatchInfo.Capture(ex);
                        control = new DataPersisterTransactionContext <T>(exceptionInfo);
                    }

                    // 3) Call code updater (update callback func)
                    updater(control);

                    if (!control.IsCommitted)
                    {
                        // 4) If the context is not committed, go to step 9
                        return(new DataPersisterUpdateResult <T>(data, exists, isUpdated: false));
                    }

                    // x) Alternate flow if the updater ask for a delete
                    if (control.IsRemoved)
                    {
                        if (exists)
                        {
                            File.Delete(_committedFile);
                            return(new DataPersisterUpdateResult <T>(default(T), isValuePresent: false, isUpdated: true));
                        }

                        return(new DataPersisterUpdateResult <T>(default(T), isValuePresent: false, isUpdated: false));
                    }

                    // 5) Save the result in the NEW file, flush & close the file.
                    using (var stream = File.OpenWrite(_newFile))
                    {
                        await _write(ct, control.CommittedValue, stream);
                    }

                    if (File.Exists(_committedFile))
                    {
                        // 6) Rename the COMMITTED as OLD file (atomic operation in OS)  -- STARTING HERE THE CHANGE IS DURABLE
                        File.Move(_committedFile, _oldFile);

                        // 7) Rename the NEW as COMMITTED file (atomic operation in OS)
                        File.Move(_newFile, _committedFile);

                        // 8) Delete the OLD file
                        File.Delete(_oldFile);
                    }
                    else
                    {
                        // 6-7-8) Rename the NEW as COMMITTED file (atomic operation in OS)
                        File.Move(_newFile, _committedFile);
                    }

                    return(new DataPersisterUpdateResult <T>(control.CommittedValue, isValuePresent: true, isUpdated: true));

                    // 9) Close & delete the LOCK file
                }
            }
        }
예제 #3
0
        /// <inheritdoc />
        public async Task <DataPersisterUpdateResult <T> > Update(CancellationToken ct, DataPersisterUpdaterWithContext <T> updater)
        {
            var innerUpdated = false;

            var result = await _inner.Update(
                ct,
                context =>
            {
                innerUpdated = false;

                var adjustedContext = GetAdjustedReadResult(context);
                var innerContext    = new DataPersisterTransactionContext <T>(adjustedContext);

                updater(innerContext);

                if (innerContext.IsCommitted)
                {
                    innerUpdated = true;

                    if (innerContext.IsRemoved)
                    {
                        context.RemoveAndCommit();
                    }
                    else
                    {
                        var value = innerContext.CommittedValue;
                        if (CheckMode(DefaultValueDataPersisterDecoratorMode.WriteCustomDefaultToEmpty) && _comparer.Equals(value, _customDefaultValue))
                        {
                            context.RemoveAndCommit();
                        }
                        else if (CheckMode(DefaultValueDataPersisterDecoratorMode.WriteDefaultToEmpty) && _comparer.Equals(value, default(T)))
                        {
                            context.RemoveAndCommit();
                        }
                        else
                        {
                            context.Commit(value);
                        }
                    }
                }
            }
                );

            var adjustedResult = GetAdjustedReadResult(result);

            return(new DataPersisterUpdateResult <T>(adjustedResult, innerUpdated || result.IsUpdated));
        }