Ejemplo n.º 1
0
        private async Task Upgrade <K, V>(UUID seriesId, PersistentSeries <K, V> series)
        {
            if (!series.IsWriter)
            {
                try {
                    _writeSeriesLocks.Add(seriesId, Pid);
                    series.IsWriter = true;
                    LogAcquireLock(seriesId, series.Version);
                    return;
                } catch (ArgumentException) { }
            }
            while (true)
            {
                // wait if a writer from the same repo releases lock
                var released = await series.LockReleaseEvent.WaitAsync(1000);

                if (released)
                {
                    try {
                        // TODO TryAdd atomic method
                        _writeSeriesLocks.Add(seriesId, Pid);
                        series.IsWriter = true;
                        LogAcquireLock(seriesId, series.Version);
                        break;
                    } catch (ArgumentException) {
                        Trace.WriteLine("Could not upgrade after lock release, some one jumped ahead of us");
                    }
                }
                else
                {
                    int pid;
                    if (_writeSeriesLocks.TryGetValue(seriesId, out pid))
                    {
                        try {
                            Process.GetProcessById(pid & ((1 << 16) - 1));
                            Trace.TraceWarning("Tried to steal a lock but the owner process was alive.");
                        } catch (ArgumentException) {
                            // pid is not running anymore, steal lock
                            Trace.TraceWarning($"Current process {Pid} has stolen a lock left by a dead process {pid}. If you see this often then dispose SeriesRepository properly before application exit.");
                            _writeSeriesLocks[seriesId] = Pid;
                            series.IsWriter             = true;
                            LogAcquireLock(seriesId, series.Version);
                            break;
                        }
                    }
                }
            }
        }
Ejemplo n.º 2
0
        protected virtual async Task <PersistentSeries <K, V> > GetSeries <K, V>(string seriesId, bool isWriter, bool allowBatches = false)
        {
            seriesId = seriesId.ToLowerInvariant().Trim();
            var            exSeriesId = GetExtendedSeriesId <K, V>(seriesId);
            var            uuid       = new UUID(exSeriesId.UUID);
            IAcceptCommand series;

            if (_openStreams.TryGetValue(uuid, out series))
            {
                var ps = (PersistentSeries <K, V>)series;
                Interlocked.Increment(ref ps.RefCounter);
                if (isWriter && !ps.IsWriter)
                {
                    var hasLockAcquired = await RequestWriteLock(uuid);

                    if (!hasLockAcquired)
                    {
                        throw new SingleWriterException();
                    }
                    // Upgrade to writer
                    ps.IsWriter = true;
                }
                return(ps);
            }

            // NB We restrict only opening more than once, once opened a series object could be modified by many threads

            if (isWriter)
            {
                var hasLockAcquired = await RequestWriteLock(uuid);

                if (!hasLockAcquired)
                {
                    throw new SingleWriterException();
                }
            }

            // NB: Writers now have lock acquired. Other logic is the same for both writers and readers.

            Action <bool, bool> disposeCallback = (remove, downgrade) => {
                IAcceptCommand temp;
                if (remove)
                {
                    // NB this callback is called from temp.Dispose();
                    var removed = _openStreams.TryRemove(uuid, out temp);
                    Trace.Assert(removed);
                }
                if (downgrade)
                {
                    Downgrade(uuid);
                }
            };

            var ipom    = base.GetSeries <K, V>(exSeriesId.Id, exSeriesId.Version, false);
            var pSeries = new PersistentSeries <K, V>(_appendLog, _pid, uuid, ipom, allowBatches, isWriter, disposeCallback);

            // NB this is done in consturctor: pSeries.RefCounter++;
            _openStreams[uuid] = pSeries;

            await RequestSubscribeSynced(uuid, pSeries.Version, exSeriesId.ToString());

            return(pSeries);
        }
Ejemplo n.º 3
0
        protected virtual async Task <PersistentSeries <K, V> > GetSeries <K, V>(string seriesId, bool isWriter, bool allowBatches = false)
        {
            seriesId = seriesId.ToLowerInvariant().Trim();
            var            exSeriesId = GetExtendedSeriesId <K, V>(seriesId);
            var            uuid       = new UUID(exSeriesId.UUID);
            IAcceptCommand series;

            if (_openSeries.TryGetValue(uuid, out series))
            {
                var ps = (PersistentSeries <K, V>)series;
                Interlocked.Increment(ref ps.RefCounter);
                if (isWriter && !ps.IsWriter)
                {
                    await Upgrade(uuid, ps);
                }
                return(ps);
            }

            Action <bool, bool> disposeCallback = (remove, downgrade) => {
                IAcceptCommand temp;
                if (remove)
                {
                    // NB this callback is called from temp.Dispose();
                    var removed = _openSeries.TryRemove(uuid, out temp);
                    Trace.Assert(removed);
                }
                if (downgrade)
                {
                    Downgrade(uuid);
                }
            };

            var ipom    = base.GetSeries <K, V>(exSeriesId.Id, exSeriesId.Version, false);
            var pSeries = new PersistentSeries <K, V>(_appendLog, _pid, uuid,
                                                      ipom, allowBatches, isWriter,
                                                      disposeCallback);

            // NB this is done in consturctor: pSeries.RefCounter++;
            _openSeries[uuid] = pSeries;

            LogSubscribe(uuid, pSeries.Version, exSeriesId.ToString());

            if (isWriter)
            {
                try {
                    _writeSeriesLocks.Add(uuid, Pid);
                    LogAcquireLock(uuid, pSeries.Version);
                } catch (ArgumentException) {
                    // NB do not wait // await Upgrade(uuid, pSeries);
                    throw new InvalidOperationException("Series is already opened for write. Only single writer is allowed.");
                }
            }
            else
            {
                // wait for flush if there is a live writer
                int pid;
                if (_writeSeriesLocks.TryGetValue(uuid, out pid))
                {
                    try {
                        Process.GetProcessById(pid & ((1 << 16) - 1));
                        await pSeries.FlushEvent.WaitAsync(-1);
                    } catch (ArgumentException) {
                        // pid is not running anymore, steal lock
                        Trace.TraceWarning($"Current process {Pid} has removed a lock left by a dead process {pid}. If you see this often then dispose SeriesRepository properly before application exit.");
                        _writeSeriesLocks.Remove(uuid);
                        LogReleaseLock(uuid);
                    }
                }
            }
            return(pSeries);
        }