Example #1
0
        public Task <PutBlobResult> PutBlobAsync(OperationContext context, ShortHash hash, byte[] blob)
        {
            var capacity     = _capacity;
            var currentGroup = Database.GetCurrentColumnGroup(RocksDbContentMetadataDatabase.Columns.Blobs);

            if (capacity?.Group != currentGroup)
            {
                Interlocked.CompareExchange(ref _capacity, new DatabaseCapacity()
                {
                    Group     = currentGroup,
                    Remaining = _configuration.MaxBlobCapacity,
                },
                                            capacity);
            }

            if (_capacity.Remaining < blob.Length)
            {
                return(Task.FromResult(PutBlobResult.OutOfCapacity(hash, blob.Length, "")));
            }

            if (Database.PutBlob(hash, blob))
            {
                Interlocked.Add(ref _capacity.Remaining, -blob.Length);
                return(Task.FromResult(PutBlobResult.NewRedisEntry(hash, blob.Length, "", Math.Max(_capacity.Remaining, 0))));
            }
            else
            {
                return(Task.FromResult(PutBlobResult.RedisHasAlready(hash, blob.Length, "")));
            }
        }
Example #2
0
        public async Task <PutBlobResult> PutBlobAsync(OperationContext context, ContentHash hash, byte[] blob)
        {
            const string ErrorMessage = "Redis value could not be updated to upload blob.";

            try
            {
                var key = GetBlobKey(hash);

                var capacityKey = GetCurrentCapacityKey(operationStart: _clock.UtcNow);

                // It's important that we check capacity before contacting Redis, since we want to avoid unnecessary trips.
                if (!IsCapacityValid(capacityKey))
                {
                    _counters[Counters.FailedReservations].Increment();
                    return(PutBlobResult.OutOfCapacity(hash, blob.Length, capacityKey));
                }

                BoolResult throttleCheck = _operationThrottle.CheckAndRegisterOperation();
                if (!throttleCheck)
                {
                    _counters[Counters.OperationsThrottled].Increment();
                    Contract.AssertNotNull(throttleCheck.ErrorMessage);
                    return(PutBlobResult.Throttled(hash, blob.Length, capacityKey, throttleCheck.ErrorMessage));
                }

                if (await _redis.KeyExistsAsync(context, key, context.Token))
                {
                    _counters[Counters.SkippedBlobs].Increment();
                    return(PutBlobResult.RedisHasAlready(hash, blob.Length, key));
                }

                var reservationResult = await TryReserveAsync(context, blob.Length, capacityKey);

                if (!reservationResult)
                {
                    _lastFailedCapacityKey = capacityKey;
                    _counters[Counters.FailedReservations].Increment();
                    Contract.AssertNotNull(reservationResult.ErrorMessage);
                    return(PutBlobResult.OutOfCapacity(hash, blob.Length, capacityKey, reservationResult.ErrorMessage));
                }

                var success = await _redis.StringSetAsync(context, key, blob, _blobExpiryTime, When.Always, context.Token);

                if (success)
                {
                    _counters[Counters.PutBlobSuccess].Increment();
                    return(PutBlobResult.NewRedisEntry(hash, blob.Length, reservationResult.Value.newCapacity, reservationResult.Value.key));
                }

                return(new PutBlobResult(hash, blob.Length, ErrorMessage));
            }
            catch (Exception e)
            {
                return(new PutBlobResult(new ErrorResult(e), ErrorMessage, hash, blob.Length));
            }
        }