Example #1
0
        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;
            }
        }