/// <summary> /// Shrinks database transactions and reduces file size on disk /// </summary> /// <returns></returns> public async Task <ShrinkInfo> Shrink() { ShrinkInfo info = null; try { Stream stream = File.GetStream(); List <string> msgs; long position; int count; await WaitForLock(); try { count = _deletedMessages.Count; position = stream.Position; msgs = _deletedMessages.Count > 0 ? new List <string>(_deletedMessages) : new List <string>(); } finally { ReleaseLock(); } info = await _shrinkManager.Shrink(position, msgs); //sync deleted messages array if (info.Successful) { if (count > 0) { await WaitForLock(); try { if (_deletedMessages.Count > count) { _deletedMessages = _deletedMessages.GetRange(count, _deletedMessages.Count - count); } else { _deletedMessages.Clear(); } } finally { ReleaseLock(); } } } OnShrink?.Invoke(this, info); } catch (Exception ex) { if (info == null) { info = new ShrinkInfo(); } info.Error = ex; TriggerError(ErrorHint.Shrink, ex); } return(info); }
/// <summary> /// Shrinks database file from begin to position pointer. /// This is a partial shrink and can run almost without any lock and thread-block operations. /// </summary> public async Task <ShrinkInfo> Shrink(long position, List <string> deletedMessages) { _info = new ShrinkInfo(); if (_shrinking) { return(_info); } Stopwatch sw = new Stopwatch(); sw.Start(); try { _shrinking = true; _end = position; _deletingMessages = new HashSet <string>(deletedMessages); _info.OldSize = _end; if (_source != null) { await DisposeSource(); } await using (FileStream file = new FileStream(_database.File.Filename, FileMode.Open, FileAccess.Read)) { int capacity = Convert.ToInt32(_end); _source = new MemoryStream(capacity); await CopyStream(file, _source, capacity); _source.Position = 0; } _target = new FileStream(_database.File.Filename + ".shrink", FileMode.Create, FileAccess.Write); sw.Stop(); _info.PreparationDuration = sw.Elapsed; sw.Reset(); sw.Start(); bool proceed = await ProcessShrink(); sw.Stop(); _info.TruncateDuration = sw.Elapsed; sw.Reset(); if (!proceed) { _shrinking = false; return(_info); } sw.Start(); bool sync = await SyncShrink(); sw.Stop(); _info.SyncDuration = sw.Elapsed; _info.Successful = sync; return(_info); } finally { ShrinkRequired = false; _shrinking = false; } }