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, ""))); } }
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)); } }