private void WriteAssetToDisk(StratusAsset asset) { ulong spaceNeeded; _activeIds.TryAdd(asset.Id, 0); // Register the asset as existing, but not yet on disk; size of 0. Failure simply indicates that the asset ID already exists. using (var memStream = new MemoryStream()) { ProtoBuf.Serializer.Serialize(memStream, asset); // This can throw, but only if something is VERY and irrecoverably wrong. spaceNeeded = (ulong)memStream.Length; memStream.Position = 0; var buffer = new byte[spaceNeeded]; Buffer.BlockCopy(memStream.GetBuffer(), 0, buffer, 0, (int)spaceNeeded); retryStorageLabel: LightningException lightningException = null; try { using (var tx = _dbenv.BeginTransaction()) using (var db = tx.OpenDatabase(DB_NAME, new DatabaseConfiguration { Flags = DatabaseOpenFlags.Create })) { tx.Put(db, asset.Id.ToByteArray(), buffer); tx.Commit(); } _activeIds.AssetSize(asset.Id, spaceNeeded); // Set the size now that it's on disk. return; } catch (LightningException e) { lightningException = e; } switch (lightningException.StatusCode) { case -30799: //LightningDB.Native.Lmdb.MDB_KEYEXIST: Not available in lib ATM... // Ignorable. LOG.Warn($"{asset.Id} already exists according to local storage. Adding to memory list - please report this as it should not be able to happen.", lightningException); lightningException = null; if (!_activeIds.TryAdd(asset.Id, spaceNeeded)) { _activeIds.AssetSize(asset.Id, spaceNeeded); } throw new AssetExistsException(asset.Id); case LightningDB.Native.Lmdb.MDB_DBS_FULL: case LightningDB.Native.Lmdb.MDB_MAP_FULL: var lockTaken = Monitor.TryEnter(_dbenv_lock); try { if (lockTaken) { LOG.Warn($"{asset.Id} got storage space full during local storage, clearing some room...", lightningException); var removedAssetIds = _activeIds.Remove(spaceNeeded * 2, out ulong bytesRemoved); var assetsWereRemoved = false; try { using (var tx = _dbenv.BeginTransaction()) using (var db = tx.OpenDatabase(DB_NAME)) { foreach (var assetId in removedAssetIds.Keys) { tx.Delete(db, assetId.ToByteArray()); } tx.Commit(); assetsWereRemoved = true; } } catch (LightningException e) { LOG.Warn($"{asset.Id} had an exception while attempting to clear some space in the local asset store.", e); } if (!assetsWereRemoved) { // The removals failed to commit, add them back to the memory cache. foreach (var metaObject in removedAssetIds.Values) { _activeIds.TryAdd(metaObject); } } } // else skip as another thread is already clearing some space. } finally { if (lockTaken) { Monitor.Exit(_dbenv_lock); } } // Retry the asset storage now that we've got some space. goto retryStorageLabel; default: LOG.Warn($"{asset.Id} got an unexpected exception during local storage.", lightningException); Thread.Sleep(200); // Give it some time. The time is a hipshot, not some magic numebr. goto retryStorageLabel; } } }
private StoreResult HandleException(LightningException ex) { switch (ex.StatusCode) { case LMDBErrorCodes.MDB_BAD_DBI: throw ex; case LMDBErrorCodes.MDB_BAD_RSLOT: throw ex; case LMDBErrorCodes.MDB_BAD_TXN: if (LoggerManager.Instance.StorageLogger != null && LoggerManager.Instance.StorageLogger.IsWarnEnabled) { LoggerManager.Instance.StorageLogger.Warn("LMDB.BAD_TXN", "MDB transaction must abort." + GetFileInfo()); } return(StoreResult.FailureReOpenTransaction); case LMDBErrorCodes.MDB_BAD_VALSIZE: throw ex; case LMDBErrorCodes.MDB_CORRUPTED: throw ex; case LMDBErrorCodes.MDB_CURSOR_FULL: throw ex; case LMDBErrorCodes.MDB_DBS_FULL: throw ex; case LMDBErrorCodes.MDB_INCOMPATIBLE: throw ex; case LMDBErrorCodes.MDB_INVALID: throw ex; case LMDBErrorCodes.MDB_KEYEXIST: throw ex; case LMDBErrorCodes.MDB_MAP_FULL: if (LoggerManager.Instance.StorageLogger != null && LoggerManager.Instance.StorageLogger.IsInfoEnabled) { LoggerManager.Instance.StorageLogger.Info("LMDB.MAP_FULL", "MDB map full occured on" + GetFileInfo()); } long size = _configuration.StorageProvider.MaxFileSize - CurrentDataSize; ChangeDataSize(size); throw ex; case LMDBErrorCodes.MDB_MAP_RESIZED: throw ex; case LMDBErrorCodes.MDB_NOTFOUND: throw ex; case LMDBErrorCodes.MDB_PAGE_FULL: throw ex; case LMDBErrorCodes.MDB_PAGE_NOTFOUND: throw ex; case LMDBErrorCodes.MDB_PANIC: throw ex; case LMDBErrorCodes.MDB_READERS_FULL: throw ex; case LMDBErrorCodes.MDB_SUCCESS: throw ex; case LMDBErrorCodes.MDB_TLS_FULL: throw ex; case LMDBErrorCodes.MDB_TXN_FULL: throw ex; case LMDBErrorCodes.MDB_VERSION_MISMATCH: throw ex; default: throw ex; } }