Exemple #1
0
        internal async Task EvictByKey1HashExcludingKey2Hash(byte[] includingKey1Hash, byte[] excludingKey2Hash, CancellationToken cancellationToken)
        {
            // Read in a full log's worth of entries.
            var enumerable = await store.List(shardId, WriteLogsDir(), cancellationToken);

            foreach (var log in enumerable)
            {
                using (var stream = await store.ReadStream(shardId, log.KeyName, cancellationToken))
                {
                    if (stream != null)
                    {
                        var entries = await WriteEntry.ReadFrom(stream, cancellationToken);

                        // Select and delete the matching entries
                        var matches = entries.Where((entry) => entry.Key1.SequenceEqual(includingKey1Hash) && (excludingKey2Hash == null || !entry.Key2.SequenceEqual(excludingKey2Hash))).ToList();
                        if (matches.Count > 0)
                        {
                            foreach (var entry in matches)
                            {
                                var blobKey = hasher.GetBlobKey(entry.Key1, entry.Key2, entry.Key3);
                                await store.Delete(shardId, blobKey, cancellationToken);
                            }
                        }
                        // Select the remaining entries
                        var remaining = entries.Where((entry) => !entry.Key1.SequenceEqual(includingKey1Hash) || (excludingKey2Hash != null && entry.Key2.SequenceEqual(excludingKey2Hash))).ToList();
                        if (remaining.Count > 0)
                        {
                            // Write the rest of the entries back to disk
                            var remainingLogBytes = new List <byte>(WriteEntry.RowBytes() * (remaining.Count));
                            foreach (var entry in remaining)
                            {
                                entry.SerializeTo(remainingLogBytes);
                            }

                            await store.WriteBytes(shardId, GetNewLogName(), remainingLogBytes.ToArray(), cancellationToken);
                        }
                        // Delete old log file
                        await store.Delete(shardId, log.KeyName, cancellationToken);
                    }
                }
            }
        }
Exemple #2
0
        async Task WriteMultipleLogs(List <WriteEntry> entries, CancellationToken cancellationToken)
        {
            if (entries.Count == 0)
            {
                return;
            }
            var entriesPerLog = (int)options.MaxWriteLogSize / WriteEntry.RowBytes();

            var enumerable = entries.AsEnumerable();

            while (enumerable.Any())
            {
                var batch = enumerable.Take(entriesPerLog);
                enumerable = enumerable.Skip(entriesPerLog);

                var bytes = new List <byte>(batch.Count());
                foreach (var entry in batch)
                {
                    entry.SerializeTo(bytes);
                }

                await store.WriteBytes(shardId, GetNewLogName(), bytes.ToArray(), cancellationToken);
            }
        }
Exemple #3
0
        internal async Task EvictSpaceFor(int length, CancellationToken cancellationToken)
        {
            using (var lockInstance = await evictionLock.LockAsync())
            {
                var currentSize = await sizeTracker.GetCachedSize();

                if (currentSize + (ulong)length < options.MaxCachedBytes)
                {
                    return; // There's enough space already.
                }

                var   bytesToDelete = (currentSize + (ulong)length) - (ulong)(options.MaxCachedBytes - (options.FreeSpacePercentGoal / 100 * options.MaxCachedBytes));
                ulong bytesDeleted  = 0;

                while (bytesDeleted < bytesToDelete)
                {
                    // Read in a full log's worth of entries.
                    var list = (await store.List(shardId, WriteLogsDir(), cancellationToken)).ToList();
                    if (list.Count < 1)
                    {
                        return;
                    }
                    var rng          = new Random();
                    var logs         = new List <IBlobInfo>();
                    var writeEntries = new List <WriteEntry>((int)(options.MaxWriteLogSize / (ulong)WriteEntry.RowBytes()));

                    ulong logsSizeSum = 0;
                    while (true)
                    {
                        var next = list[rng.Next(0, list.Count - 1)];
                        if (logsSizeSum + next.SizeInBytes < options.MaxWriteLogSize)
                        {
                            using (var stream = await store.ReadStream(shardId, next.KeyName, cancellationToken))
                            {
                                if (stream != null)
                                {
                                    writeEntries.AddRange(await WriteEntry.ReadFrom(stream, cancellationToken));
                                    logs.Add(next);
                                    logsSizeSum += next.SizeInBytes;
                                }
                            }
                        }
                        else
                        {
                            break;
                        }
                    }

                    // Add usage info
                    for (var i = 0; i < writeEntries.Count; i++)
                    {
                        var entry = writeEntries[i];
                        entry.FrequencyExtra = usage.GetFrequency(entry.ReadId);
                        writeEntries[i]      = entry;
                    }

                    // Sort the best entries to delete
                    writeEntries.Sort((a, b) =>
                    {
                        var result = a.FrequencyExtra.CompareTo(b.FrequencyExtra);
                        if (result == 0)
                        {
                            result = ((double)a.ByteCount / Math.Max(1, (double)a.CreationCost))
                                     .CompareTo((double)b.ByteCount / Math.Max(1, (double)b.CreationCost));
                        }
                        return(result);
                    });

                    // Delete the first 10%
                    var   entriesToDeleteCount = writeEntries.Count / 10;
                    ulong bytesDeletedThisLoop = 0;
                    foreach (var entry in writeEntries.Take(entriesToDeleteCount))
                    {
                        var blobKey = hasher.GetBlobKey(entry.Key1, entry.Key2, entry.Key3);
                        await store.Delete(shardId, blobKey, cancellationToken);

                        bytesDeletedThisLoop += entry.ByteCount;
                    }

                    // Write the rest of the entries back to disk
                    await WriteMultipleLogs(writeEntries.Skip(entriesToDeleteCount).ToList(), cancellationToken);

                    // Delete the old log files
                    foreach (var logToDelete in logs)
                    {
                        await store.Delete(shardId, logToDelete.KeyName, cancellationToken);
                    }

                    bytesDeleted += bytesDeletedThisLoop;
                    await sizeTracker.OffsetBy(-(long)bytesDeletedThisLoop);
                } // Repeat until we have enough space
            }
        }
Exemple #4
0
        async Task WriteLogsMerged(List <WriteEntry> entries, CancellationToken cancellationToken)
        {
            // Write logs unmerged unless we have too few entries.
            var entryThreshold = (int)(.9 * (double)options.MaxWriteLogSize / (double)WriteEntry.RowBytes());

            if (entries.Count > entryThreshold)
            {
                await FlushLogUnmerged();

                return;
            }


            // List logs and sort for the smallest
            var logList = (await store.List(shardId, WriteLogsDir(), cancellationToken)).ToList();

            logList.Sort((a, b) => a.SizeInBytes.CompareTo(b.SizeInBytes));
            var   logs       = new List <IBlobInfo>();
            ulong totalBytes = (ulong)(entries.Count * WriteEntry.RowBytes());

            // Add logs into `logs` until we have enough bytes
            foreach (var log in logList)
            {
                if (totalBytes + log.SizeInBytes > options.MaxWriteLogSize)
                {
                    break;
                }
                else
                {
                    logs.Add(log);
                    totalBytes += log.SizeInBytes;
                }
            }

            // Use a memory stream to buffer new and old entries in
            var replacementBytes = new MemoryStream((int)totalBytes);
            // Buffer new entries
            var entryBuffer = new List <byte>(WriteEntry.RowBytes());

            foreach (var entry in entries)
            {
                entryBuffer.Clear();
                entry.SerializeTo(entryBuffer);
                replacementBytes.Write(entryBuffer.ToArray(), 0, entryBuffer.Count);
            }
            // Append bytes from old logs
            foreach (var mergeLog in logs)
            {
                using (var stream = await store.ReadStream(shardId, mergeLog.KeyName, cancellationToken))
                {
                    if (stream != null)
                    {
                        await stream.CopyToAsync(replacementBytes, 81920, cancellationToken);
                    }
                }
            }

            // Write new log
            var fileBytes = replacementBytes.ToArray();
            await store.WriteBytes(shardId, GetNewLogName(), fileBytes.ToArray(), cancellationToken);

            // Delete old logs
            foreach (var mergeLog in logs)
            {
                await store.Delete(shardId, mergeLog.KeyName, cancellationToken);
            }
        }