Merged PR 705049: Remove distinction between readonly and readwrite cache sessions

This PR removes the concept of cache sessions that can only be read from. This:
1. Isn't actually used in practice by anyone, and it is a hinderance when changing the code.
2. Is actually a lie, because the cache doesn't truly support being read-only. Read-only operations can and do trigger mutations inside of the cache.

All cache sessions are now writeable.
This commit is contained in:
Julian Bayardo 2023-03-03 00:32:16 +00:00
Родитель 142c874129
Коммит ac5ae894ea
86 изменённых файлов: 4709 добавлений и 5181 удалений

Просмотреть файл

@ -107,20 +107,6 @@ namespace BuildXL.Cache.ContentStore.Distributed.Blobs
timeout: _configuration.StorageInteractionTimeout);
}
/// <inheritdoc />
public CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
using var guard = TrackShutdown(context, default);
var operationContext = guard.Context;
return operationContext.PerformOperation(Tracer, () =>
{
return new CreateSessionResult<IReadOnlyContentSession>(CreateSessionCore(name, implicitPin));
},
traceOperationStarted: false,
messageFactory: _ => $"Name=[{name}] ImplicitPin=[{implicitPin}]");
}
/// <inheritdoc />
public CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -12,7 +12,7 @@ using BuildXL.Cache.ContentStore.Tracing.Internal;
namespace BuildXL.Cache.ContentStore.Distributed.Sessions
{
/// <summary>
/// Performs remote pinning for <see cref="IReadOnlyContentSession.PinAsync(BuildXL.Cache.ContentStore.Interfaces.Tracing.Context, System.Collections.Generic.IReadOnlyList{BuildXL.Cache.ContentStore.Hashing.ContentHash}, CancellationToken, UrgencyHint)"/>
/// Performs remote pinning for <see cref="IContentSession.PinAsync(BuildXL.Cache.ContentStore.Interfaces.Tracing.Context, System.Collections.Generic.IReadOnlyList{BuildXL.Cache.ContentStore.Hashing.ContentHash}, CancellationToken, UrgencyHint)"/>
/// </summary>
public delegate Task<IEnumerable<Task<Indexed<PinResult>>>> RemotePinAsync(
OperationContext context,

Просмотреть файл

@ -99,8 +99,8 @@ namespace BuildXL.Cache.ContentStore.Distributed.Stores
private readonly DistributedContentCopier _distributedCopier;
private readonly DisposableDirectory _copierWorkingDirectory;
private Lazy<Task<Result<ReadOnlyDistributedContentSession>>>? _proactiveCopySession;
internal Lazy<Task<Result<ReadOnlyDistributedContentSession>>> ProactiveCopySession => NotNull(_proactiveCopySession, nameof(_proactiveCopySession));
private Lazy<Task<Result<DistributedContentSession>>>? _proactiveCopySession;
internal Lazy<Task<Result<DistributedContentSession>>> ProactiveCopySession => NotNull(_proactiveCopySession, nameof(_proactiveCopySession));
private readonly DistributedContentSettings? _distributedContentSettings;
@ -226,7 +226,7 @@ namespace BuildXL.Cache.ContentStore.Distributed.Stores
return $"Origin=[{info.Origin}] IsPresentInGcs=[{isPresentInGcs}]";
}
private Task<Result<ReadOnlyDistributedContentSession>> CreateCopySession(Context context)
private Task<Result<DistributedContentSession>> CreateCopySession(Context context)
{
var sessionId = Guid.NewGuid().ToString();
@ -235,11 +235,11 @@ namespace BuildXL.Cache.ContentStore.Distributed.Stores
async () =>
{
// NOTE: We use ImplicitPin.None so that the OpenStream calls triggered by RequestCopy will only pull the content, NOT pin it in the local store.
var sessionResult = CreateReadOnlySession(operationContext, $"{sessionId}-DefaultCopy", ImplicitPin.None).ThrowIfFailure();
var sessionResult = CreateSession(operationContext, $"{sessionId}-DefaultCopy", ImplicitPin.None).ThrowIfFailure();
var session = sessionResult.Session!;
await session.StartupAsync(context).ThrowIfFailure();
return Result.Success((ReadOnlyDistributedContentSession)session);
return Result.Success((DistributedContentSession)session);
});
}
@ -250,7 +250,7 @@ namespace BuildXL.Cache.ContentStore.Distributed.Stores
var startupTask = base.StartupAsync(context);
_proactiveCopySession = new Lazy<Task<Result<ReadOnlyDistributedContentSession>>>(() => CreateCopySession(context));
_proactiveCopySession = new Lazy<Task<Result<DistributedContentSession>>>(() => CreateCopySession(context));
if (_settings.SetPostInitializationCompletionAfterStartup)
{
@ -324,7 +324,7 @@ namespace BuildXL.Cache.ContentStore.Distributed.Stores
private Task<ProactiveReplicationResult> ProactiveReplicationIterationAsync(
OperationContext context,
ReadOnlyDistributedContentSession proactiveCopySession,
DistributedContentSession proactiveCopySession,
ILocalContentStore localContentStore,
TransitioningContentLocationStore contentLocationStore)
{
@ -476,31 +476,6 @@ namespace BuildXL.Cache.ContentStore.Distributed.Stores
return BoolResult.Success;
}
/// <inheritdoc />
public CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
return CreateReadOnlySessionCall.Run(_tracer, OperationContext(context), name, () =>
{
CreateSessionResult<IContentSession> innerSessionResult = InnerContentStore.CreateSession(context, name, implicitPin);
if (innerSessionResult.Succeeded)
{
var session = new ReadOnlyDistributedContentSession(
name,
innerSessionResult.Session,
ContentLocationStore,
_distributedCopier,
this,
LocalMachineLocation,
ColdStorage,
settings: _settings);
return new CreateSessionResult<IReadOnlyContentSession>(session);
}
return new CreateSessionResult<IReadOnlyContentSession>(innerSessionResult, "Could not initialize inner content session with error");
});
}
/// <inheritdoc />
public CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{
@ -513,7 +488,7 @@ namespace BuildXL.Cache.ContentStore.Distributed.Stores
var session = new DistributedContentSession(
name,
innerSessionResult.Session,
_contentLocationStore,
ContentLocationStore,
_distributedCopier,
this,
LocalMachineLocation,

Просмотреть файл

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;

Просмотреть файл

@ -20,7 +20,7 @@ using BuildXL.Cache.ContentStore.Service.Grpc;
using FluentAssertions;
using Xunit;
using static BuildXL.Cache.ContentStore.Distributed.Sessions.ReadOnlyDistributedContentSession.Counters;
using static BuildXL.Cache.ContentStore.Distributed.Sessions.DistributedContentSession.Counters;
namespace ContentStoreTest.Distributed.Sessions
{

Просмотреть файл

@ -75,7 +75,7 @@ namespace ContentStoreTest.Distributed.Sessions
}, implicitPin);
}
protected override Task RunReadOnlyTestAsync(ImplicitPin implicitPin, Func<Context, IReadOnlyContentSession, Task> funcAsync)
protected override Task RunReadOnlyTestAsync(ImplicitPin implicitPin, Func<Context, IContentSession, Task> funcAsync)
{
return RunTestAsync(implicitPin, null, (ctx, session) => funcAsync(ctx, session));
}

Просмотреть файл

@ -1009,7 +1009,7 @@ namespace ContentStoreTest.Distributed.Sessions
}
protected async Task OpenStreamReturnsExpectedFile(
IReadOnlyContentSession session, Context context, ContentHash hash, byte[] expected)
IContentSession session, Context context, ContentHash hash, byte[] expected)
{
OpenStreamResult openResult = await session.OpenStreamAsync(context, hash, Token);
_tracer.Debug(context, $"Validating stream for content hash {hash} returned result {openResult.Code} with diagnostics {openResult} with ErrorMessage {openResult.ErrorMessage} diagnostics {openResult.Diagnostics}");

Просмотреть файл

@ -52,7 +52,7 @@ namespace BuildXL.Cache.ContentStore.Distributed.Test
throw new NotImplementedException();
}
protected override async Task RunReadOnlyTestAsync(ImplicitPin implicitPin, Func<Context, IReadOnlyContentSession, Task> funcAsync)
protected override async Task RunReadOnlyTestAsync(ImplicitPin implicitPin, Func<Context, IContentSession, Task> funcAsync)
{
using var directory = new DisposableDirectory(FileSystem);

Просмотреть файл

@ -50,12 +50,6 @@ namespace ContentStoreTest.Grpc
return Task.FromResult(BoolResult.Success);
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
return new CreateSessionResult<IReadOnlyContentSession>(new TestHangingContentSession(name, _useCancellationToken, _hangHasStartedSemaphore));
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{

Просмотреть файл

@ -48,13 +48,6 @@ namespace ContentStoreTest.Grpc
return Task.FromResult(BoolResult.Success);
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
return new CreateSessionResult<IReadOnlyContentSession>(new TestTempFileDeletingContentSession(name, _fileSystem));
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{
return new CreateSessionResult<IContentSession>(new TestTempFileDeletingContentSession(name, _fileSystem));

Просмотреть файл

@ -1,12 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Hashing;
using BuildXL.Cache.ContentStore.Interfaces.FileSystem;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Stores;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
// ReSharper disable UnusedParameter.Global
@ -15,8 +17,60 @@ namespace BuildXL.Cache.ContentStore.Interfaces.Sessions
/// <summary>
/// A related set of accesses to a content store.
/// </summary>
public interface IContentSession : IReadOnlyContentSession
public interface IContentSession : IName, IStartupShutdown, IConfigurablePin
{
/// <summary>
/// Ensure content does not get deleted.
/// </summary>
Task<PinResult> PinAsync(
Context context,
ContentHash contentHash,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal);
/// <summary>
/// Open a stream to content.
/// </summary>
Task<OpenStreamResult> OpenStreamAsync(
Context context,
ContentHash contentHash,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal);
/// <summary>
/// Materialize content to the filesystem.
/// </summary>
Task<PlaceFileResult> PlaceFileAsync(
Context context,
ContentHash contentHash,
AbsolutePath path,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal);
/// <summary>
/// Ensure content does not get deleted in bulk.
/// </summary>
Task<IEnumerable<Task<Indexed<PinResult>>>> PinAsync(
Context context,
IReadOnlyList<ContentHash> contentHashes,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal);
/// <summary>
/// Materialize content to the filesystem in bulk.
/// </summary>
Task<IEnumerable<Task<Indexed<PlaceFileResult>>>> PlaceFileAsync(
Context context,
IReadOnlyList<ContentHashWithPath> hashesWithPaths,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal);
/// <summary>
/// Add content from a file.
/// </summary>

Просмотреть файл

@ -1,72 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Hashing;
using BuildXL.Cache.ContentStore.Interfaces.FileSystem;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Stores;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
namespace BuildXL.Cache.ContentStore.Interfaces.Sessions
{
/// <summary>
/// A related set of read accesses to a content store.
/// </summary>
public interface IReadOnlyContentSession : IName, IStartupShutdown, IConfigurablePin
{
/// <summary>
/// Ensure content does not get deleted.
/// </summary>
Task<PinResult> PinAsync(
Context context,
ContentHash contentHash,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal);
/// <summary>
/// Open a stream to content.
/// </summary>
Task<OpenStreamResult> OpenStreamAsync(
Context context,
ContentHash contentHash,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal);
/// <summary>
/// Materialize content to the filesystem.
/// </summary>
Task<PlaceFileResult> PlaceFileAsync(
Context context,
ContentHash contentHash,
AbsolutePath path,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal);
/// <summary>
/// Ensure content does not get deleted in bulk.
/// </summary>
Task<IEnumerable<Task<Indexed<PinResult>>>> PinAsync(
Context context,
IReadOnlyList<ContentHash> contentHashes,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal);
/// <summary>
/// Materialize content to the filesystem in bulk.
/// </summary>
Task<IEnumerable<Task<Indexed<PlaceFileResult>>>> PlaceFileAsync(
Context context,
IReadOnlyList<ContentHashWithPath> hashesWithPaths,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal);
}
}

Просмотреть файл

@ -26,11 +26,6 @@ namespace BuildXL.Cache.ContentStore.Interfaces.Stores
/// </summary>
public interface IContentStore : IStartupShutdown
{
/// <summary>
/// Create a new session that can only read.
/// </summary>
CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin);
/// <summary>
/// Create a new session that can add content as well as read.
/// </summary>

Просмотреть файл

@ -63,7 +63,7 @@ namespace BuildXL.Cache.ContentStore.InterfacesTest.Sessions
protected abstract IContentStore CreateStore(DisposableDirectory testDirectory, ContentStoreConfiguration configuration);
protected virtual async Task RunReadOnlyTestAsync(ImplicitPin implicitPin, Func<Context, IReadOnlyContentSession, Task> funcAsync)
protected virtual async Task RunReadOnlyTestAsync(ImplicitPin implicitPin, Func<Context, IContentSession, Task> funcAsync)
{
var context = new Context(Logger);
using (var directory = new DisposableDirectory(FileSystem))
@ -76,7 +76,7 @@ namespace BuildXL.Cache.ContentStore.InterfacesTest.Sessions
{
await store.StartupAsync(context).ShouldBeSuccess();
var createResult = store.CreateReadOnlySession(context, Name, implicitPin).ShouldBeSuccess();
var createResult = store.CreateSession(context, Name, implicitPin).ShouldBeSuccess();
using (var session = createResult.Session)
{
try

Просмотреть файл

@ -2,10 +2,13 @@
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Hashing;
using BuildXL.Cache.ContentStore.Interfaces.Extensions;
using BuildXL.Cache.ContentStore.Interfaces.FileSystem;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
@ -13,22 +16,165 @@ using BuildXL.Cache.ContentStore.Interfaces.Stores;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
using BuildXL.Cache.ContentStore.Sessions.Internal;
using BuildXL.Cache.ContentStore.Stores;
using BuildXL.Cache.ContentStore.Tracing;
using BuildXL.Cache.ContentStore.Tracing.Internal;
using BuildXL.Utilities.Tracing;
namespace BuildXL.Cache.ContentStore.Sessions
{
/// <summary>
/// An <see cref="IContentSession"/> implemented over a <see cref="FileSystemContentStoreInternal"/>
/// </summary>
public class FileSystemContentSession : ReadOnlyFileSystemContentSession, ITrustedContentSession
public class FileSystemContentSession : ContentSessionBase, ITrustedContentSession, IHibernateContentSession
{
/// <summary>
/// Initializes a new instance of the <see cref="FileSystemContentSession" /> class.
/// The internal content store backing the session.
/// </summary>
public FileSystemContentSession(string name, ImplicitPin implicitPin, FileSystemContentStoreInternal store)
: base(name, store, implicitPin)
protected readonly FileSystemContentStoreInternal Store;
private readonly PinContext _pinContext;
private readonly ImplicitPin _implicitPin;
/// <inheritdoc />
protected override Tracer Tracer { get; } = new Tracer(nameof(FileSystemContentSession));
/// <inheritdoc />
protected override bool TraceErrorsOnly => true; // This type adds nothing in terms of tracing. So configure it to trace errors only.
/// <summary>
/// Initializes a new instance of the <see cref="ReadOnlyFileSystemContentSession" /> class.
/// </summary>
public FileSystemContentSession(string name, FileSystemContentStoreInternal store, ImplicitPin implicitPin)
: base(name)
{
Store = store;
_pinContext = Store.CreatePinContext();
_implicitPin = implicitPin;
}
/// <inheritdoc />
protected override async Task<BoolResult> ShutdownCoreAsync(OperationContext operationContext)
{
await _pinContext.DisposeAsync();
var statsResult = await Store.GetStatsAsync(operationContext);
if (statsResult.Succeeded)
{
Tracer.TraceStatisticsAtShutdown(operationContext, statsResult.CounterSet, prefix: "FileSystemContentSessionStats");
}
return BoolResult.Success;
}
/// <inheritdoc />
protected override void DisposeCore()
{
base.DisposeCore();
_pinContext.Dispose();
}
/// <inheritdoc />
protected override Task<PinResult> PinCoreAsync(
OperationContext operationContext,
ContentHash contentHash,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return Store.PinAsync(operationContext, contentHash, _pinContext);
}
/// <inheritdoc />
protected override Task<OpenStreamResult> OpenStreamCoreAsync(
OperationContext operationContext,
ContentHash contentHash,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return Store.OpenStreamAsync(operationContext, contentHash, MakePinRequest(ImplicitPin.Get));
}
/// <inheritdoc />
protected override Task<PlaceFileResult> PlaceFileCoreAsync(
OperationContext operationContext,
ContentHash contentHash,
AbsolutePath path,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return Store.PlaceFileAsync(
operationContext,
contentHash,
path,
accessMode,
replacementMode,
realizationMode,
MakePinRequest(ImplicitPin.Put));
}
/// <inheritdoc />
protected override async Task<IEnumerable<Task<Indexed<PinResult>>>> PinCoreAsync(
OperationContext operationContext,
IReadOnlyList<ContentHash> contentHashes,
UrgencyHint urgencyHint,
Counter retryCounter,
Counter fileCounter)
{
return EnumerableExtensions.AsTasks<Indexed<PinResult>>(
(await Store.PinAsync(operationContext, contentHashes, _pinContext, options: null)));
}
/// <inheritdoc />
protected override Task<IEnumerable<Task<Indexed<PlaceFileResult>>>> PlaceFileCoreAsync(
OperationContext operationContext,
IReadOnlyList<ContentHashWithPath> hashesWithPaths,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return Store.PlaceFileAsync(
operationContext,
hashesWithPaths,
accessMode,
replacementMode,
realizationMode,
MakePinRequest(ImplicitPin.Get));
}
/// <inheritdoc />
public IEnumerable<ContentHash> EnumeratePinnedContentHashes()
{
return _pinContext.GetContentHashes();
}
/// <inheritdoc />
async Task IHibernateContentSession.PinBulkAsync(Context context, IEnumerable<ContentHash> contentHashes)
{
var contentHashList = contentHashes as List<ContentHash> ?? contentHashes.ToList();
// Passing 'RePinFromHibernation' to use more optimal pinning logic.
var results = Enumerable.ToList<Indexed<PinResult>>(
(await Store.PinAsync(context, contentHashList, _pinContext, new PinBulkOptions() { RePinFromHibernation = true })));
var failed = results.Where(r => !r.Item.Succeeded);
foreach (var result in failed)
{
Tracer.Warning(context, $"Failed to pin contentHash=[{contentHashList[result.Index]}]");
}
}
/// <inheritdoc />
Task<BoolResult> IHibernateContentSession.ShutdownEvictionAsync(Context context)
{
return Store.ShutdownEvictionAsync(context);
}
/// <summary>
/// Build a PinRequest based on whether auto-pin configuration matches request.
/// </summary>
protected PinRequest? MakePinRequest(ImplicitPin implicitPin)
{
return (implicitPin & _implicitPin) != ImplicitPin.None ? new PinRequest(_pinContext) : (PinRequest?)null;
}
/// <inheritdoc />

Просмотреть файл

@ -1,158 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Hashing;
using BuildXL.Cache.ContentStore.Interfaces.Extensions;
using BuildXL.Cache.ContentStore.Interfaces.FileSystem;
using BuildXL.Cache.ContentStore.Interfaces.Logging;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
using BuildXL.Cache.ContentStore.Interfaces.Stores;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
using BuildXL.Cache.ContentStore.Stores;
using BuildXL.Cache.ContentStore.Tracing;
using BuildXL.Cache.ContentStore.Tracing.Internal;
using BuildXL.Utilities.Tracing;
namespace BuildXL.Cache.ContentStore.Sessions
{
/// <summary>
/// An <see cref="IReadOnlyContentSession"/> implemented over a <see cref="FileSystemContentStoreInternal"/>
/// </summary>
public class ReadOnlyFileSystemContentSession : ContentSessionBase, IHibernateContentSession
{
/// <summary>
/// The internal content store backing the session.
/// </summary>
protected readonly FileSystemContentStoreInternal Store;
private readonly PinContext _pinContext;
private readonly ImplicitPin _implicitPin;
/// <inheritdoc />
protected override Tracer Tracer { get; } = new Tracer(nameof(FileSystemContentSession));
/// <inheritdoc />
protected override bool TraceErrorsOnly => true; // This type adds nothing in terms of tracing. So configure it to trace errors only.
/// <summary>
/// Initializes a new instance of the <see cref="ReadOnlyFileSystemContentSession" /> class.
/// </summary>
public ReadOnlyFileSystemContentSession(string name, FileSystemContentStoreInternal store, ImplicitPin implicitPin)
: base(name)
{
Store = store;
_pinContext = Store.CreatePinContext();
_implicitPin = implicitPin;
}
/// <inheritdoc />
protected override async Task<BoolResult> ShutdownCoreAsync(OperationContext operationContext)
{
await _pinContext.DisposeAsync();
var statsResult = await Store.GetStatsAsync(operationContext);
if (statsResult.Succeeded)
{
Tracer.TraceStatisticsAtShutdown(operationContext, statsResult.CounterSet, prefix: "FileSystemContentSessionStats");
}
return BoolResult.Success;
}
/// <inheritdoc />
protected override void DisposeCore()
{
base.DisposeCore();
_pinContext.Dispose();
}
/// <inheritdoc />
protected override Task<PinResult> PinCoreAsync(OperationContext operationContext, ContentHash contentHash, UrgencyHint urgencyHint, Counter retryCounter)
{
return Store.PinAsync(operationContext, contentHash, _pinContext);
}
/// <inheritdoc />
protected override Task<OpenStreamResult> OpenStreamCoreAsync(
OperationContext operationContext, ContentHash contentHash, UrgencyHint urgencyHint, Counter retryCounter)
{
return Store.OpenStreamAsync(operationContext, contentHash, MakePinRequest(ImplicitPin.Get));
}
/// <inheritdoc />
protected override Task<PlaceFileResult> PlaceFileCoreAsync(
OperationContext operationContext,
ContentHash contentHash,
AbsolutePath path,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return Store.PlaceFileAsync(operationContext, contentHash, path, accessMode, replacementMode, realizationMode, MakePinRequest(ImplicitPin.Put));
}
/// <inheritdoc />
protected override async Task<IEnumerable<Task<Indexed<PinResult>>>> PinCoreAsync(
OperationContext operationContext,
IReadOnlyList<ContentHash> contentHashes,
UrgencyHint urgencyHint,
Counter retryCounter,
Counter fileCounter)
{
return (await Store.PinAsync(operationContext, contentHashes, _pinContext, options: null)).AsTasks();
}
/// <inheritdoc />
protected override Task<IEnumerable<Task<Indexed<PlaceFileResult>>>> PlaceFileCoreAsync(
OperationContext operationContext,
IReadOnlyList<ContentHashWithPath> hashesWithPaths,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return Store.PlaceFileAsync(operationContext, hashesWithPaths, accessMode, replacementMode, realizationMode, MakePinRequest(ImplicitPin.Get));
}
/// <inheritdoc />
public IEnumerable<ContentHash> EnumeratePinnedContentHashes()
{
return _pinContext.GetContentHashes();
}
/// <inheritdoc />
async Task IHibernateContentSession.PinBulkAsync(Context context, IEnumerable<ContentHash> contentHashes)
{
var contentHashList = contentHashes as List<ContentHash> ?? contentHashes.ToList();
// Passing 'RePinFromHibernation' to use more optimal pinning logic.
var results = (await Store.PinAsync(context, contentHashList, _pinContext, new PinBulkOptions() { RePinFromHibernation = true })).ToList();
var failed = results.Where(r => !r.Item.Succeeded);
foreach (var result in failed)
{
Tracer.Warning(context, $"Failed to pin contentHash=[{contentHashList[result.Index]}]");
}
}
/// <inheritdoc />
Task<BoolResult> IHibernateContentSession.ShutdownEvictionAsync(Context context)
{
return Store.ShutdownEvictionAsync(context);
}
/// <summary>
/// Build a PinRequest based on whether auto-pin configuration matches request.
/// </summary>
protected PinRequest? MakePinRequest(ImplicitPin implicitPin)
{
return (implicitPin & _implicitPin) != ImplicitPin.None ? new PinRequest(_pinContext) : (PinRequest?)null;
}
}
}

Просмотреть файл

@ -1,274 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.ContractsLight;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.FileSystem;
using BuildXL.Cache.ContentStore.Hashing;
using BuildXL.Cache.ContentStore.Interfaces.FileSystem;
using BuildXL.Cache.ContentStore.Interfaces.Logging;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
using BuildXL.Cache.ContentStore.Interfaces.Stores;
using BuildXL.Cache.ContentStore.Service;
using BuildXL.Cache.ContentStore.Service.Grpc;
using BuildXL.Cache.ContentStore.Stores;
using BuildXL.Cache.ContentStore.Tracing;
using BuildXL.Cache.ContentStore.Tracing.Internal;
using BuildXL.Cache.ContentStore.UtilitiesCore;
using BuildXL.Cache.ContentStore.Utils;
using BuildXL.Utilities.Tracing;
namespace BuildXL.Cache.ContentStore.Sessions
{
/// <summary>
/// An IReadOnlyContentSession implemented over a ServiceClientContentStore.
/// </summary>
public class ReadOnlyServiceClientContentSession : ContentSessionBase
{
/// <summary>
/// The filesystem backing the session.
/// </summary>
protected readonly IAbsFileSystem FileSystem;
/// <summary>
/// Generator of temporary, seekable streams.
/// </summary>
protected readonly TempFileStreamFactory TempFileStreamFactory;
/// <inheritdoc />
protected override Tracer Tracer { get; } = new Tracer(nameof(ReadOnlyServiceClientContentSession));
/// <summary>
/// Request to server retry policy.
/// </summary>
protected readonly IRetryPolicy RetryPolicy;
/// <summary>
/// The client backing the session.
/// </summary>
protected readonly IRpcClient RpcClient;
/// <nodoc />
protected readonly ServiceClientContentStoreConfiguration Configuration;
/// <nodoc />
protected readonly ServiceClientContentSessionTracer SessionTracer;
/// <nodoc />
protected readonly ILogger Logger;
/// <nodoc />
protected readonly ImplicitPin ImplicitPin;
/// <inheritdoc />
protected override bool TraceOperationStarted => Configuration.TraceOperationStarted;
/// <summary>
/// Initializes a new instance of the <see cref="ReadOnlyServiceClientContentSession"/> class.
/// </summary>
public ReadOnlyServiceClientContentSession(
OperationContext context,
string name,
ImplicitPin implicitPin,
ILogger logger,
IAbsFileSystem fileSystem,
ServiceClientContentSessionTracer sessionTracer,
ServiceClientContentStoreConfiguration configuration,
Func<IRpcClient>? rpcClientFactory = null)
: base(name)
{
Contract.Requires(name != null);
Contract.Requires(logger != null);
Contract.Requires(fileSystem != null);
ImplicitPin = implicitPin;
SessionTracer = sessionTracer;
Logger = logger;
FileSystem = fileSystem;
Configuration = configuration;
TempFileStreamFactory = new TempFileStreamFactory(FileSystem);
RpcClient = (rpcClientFactory ?? (() => GetRpcClient(context)))();
RetryPolicy = configuration.RetryPolicy;
}
/// <nodoc />
protected virtual IRpcClient GetRpcClient(OperationContext context)
{
var rpcConfiguration = Configuration.RpcConfiguration;
return new GrpcContentClient(context, SessionTracer, FileSystem, rpcConfiguration, Configuration.Scenario);
}
/// <inheritdoc />
protected override async Task<BoolResult> StartupCoreAsync(OperationContext operationContext)
{
BoolResult result;
try
{
result = await RetryPolicy.ExecuteAsync(() => RpcClient.CreateSessionAsync(operationContext, Name, Configuration.CacheName, ImplicitPin), CancellationToken.None);
}
catch (Exception ex)
{
result = new BoolResult(ex);
}
if (!result)
{
await RetryPolicy.ExecuteAsync(() => RpcClient.ShutdownAsync(operationContext), CancellationToken.None).ThrowIfFailure();
}
return result;
}
/// <inheritdoc />
protected override async Task<BoolResult> ShutdownCoreAsync(OperationContext operationContext)
{
var result = await RetryPolicy.ExecuteAsync(() => RpcClient.ShutdownAsync(operationContext), CancellationToken.None);
var counterSet = new CounterSet();
counterSet.Merge(GetCounters(), $"{Tracer.Name}.");
Tracer.TraceStatisticsAtShutdown(operationContext, counterSet, prefix: "ServiceClientContentSessionStats");
return result;
}
/// <inheritdoc />
protected override void DisposeCore()
{
base.DisposeCore();
RpcClient.Dispose();
TempFileStreamFactory.Dispose();
}
/// <inheritdoc />
protected override Task<PinResult> PinCoreAsync(OperationContext operationContext, ContentHash contentHash, UrgencyHint urgencyHint, Counter retryCounter)
{
return PerformRetries(
operationContext,
() => RpcClient.PinAsync(operationContext, contentHash, urgencyHint),
retryCounter: retryCounter);
}
/// <inheritdoc />
protected override Task<OpenStreamResult> OpenStreamCoreAsync(
OperationContext operationContext, ContentHash contentHash, UrgencyHint urgencyHint, Counter retryCounter)
{
return PerformRetries(
operationContext,
() => RpcClient.OpenStreamAsync(operationContext, contentHash, urgencyHint),
retryCounter: retryCounter);
}
/// <inheritdoc />
protected override Task<PlaceFileResult> PlaceFileCoreAsync(
OperationContext operationContext,
ContentHash contentHash,
AbsolutePath path,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
UrgencyHint urgencyHint,
Counter retryCounter)
{
if (replacementMode != FileReplacementMode.ReplaceExisting && FileSystem.FileExists(path))
{
if (replacementMode == FileReplacementMode.SkipIfExists)
{
return Task.FromResult(PlaceFileResult.AlreadyExists);
}
else if (replacementMode == FileReplacementMode.FailIfExists)
{
return Task.FromResult(new PlaceFileResult(
PlaceFileResult.ResultCode.Error,
$"File exists at destination {path} with FailIfExists specified"));
}
}
return PerformRetries(
operationContext,
() => RpcClient.PlaceFileAsync(operationContext, contentHash, path, accessMode, replacementMode, realizationMode, urgencyHint),
retryCounter: retryCounter);
}
/// <inheritdoc />
protected override async Task<IEnumerable<Task<Indexed<PinResult>>>> PinCoreAsync(
OperationContext operationContext,
IReadOnlyList<ContentHash> contentHashes,
UrgencyHint urgencyHint,
Counter retryCounter,
Counter fileCounter)
{
var retry = 0;
try
{
return await RetryPolicy.ExecuteAsync(PinBulkFunc, operationContext.Token);
}
catch (Exception ex)
{
Tracer.Warning(operationContext, $"PinBulk failed with exception {ex}");
return contentHashes.Select((hash, index) => Task.FromResult(new PinResult(ex).WithIndex(index)));
}
async Task<IEnumerable<Task<Indexed<PinResult>>>> PinBulkFunc()
{
var sw = Stopwatch.StartNew();
try
{
fileCounter.Add(contentHashes.Count);
if (retry > 0)
{
Tracer.Debug(operationContext, $"{Tracer.Name}.PinBulk retry #{retry}");
retryCounter.Increment();
}
return await RpcClient.PinAsync(operationContext, contentHashes, urgencyHint);
}
finally
{
Tracer.Debug(operationContext, $"{Tracer.Name}.PinBulk({contentHashes.Count}) stop {sw.Elapsed.TotalMilliseconds}ms. Hashes: [{string.Join(",", contentHashes)}]");
}
}
}
/// <inheritdoc />
protected override Task<IEnumerable<Task<Indexed<PlaceFileResult>>>> PlaceFileCoreAsync(OperationContext operationContext, IReadOnlyList<ContentHashWithPath> hashesWithPaths, FileAccessMode accessMode, FileReplacementMode replacementMode, FileRealizationMode realizationMode, UrgencyHint urgencyHint, Counter retryCounter)
{
throw new NotImplementedException();
}
/// <nodoc />
protected Task<T> PerformRetries<T>(OperationContext operationContext, Func<Task<T>> action, Action<int>? onRetry = null, Counter? retryCounter = null, [CallerMemberName] string? operationName = null)
{
Contract.Requires(operationName != null);
var retry = 0;
return RetryPolicy.ExecuteAsync(Wrapper, operationContext.Token);
Task<T> Wrapper()
{
if (retry > 0)
{
// Normalize operation name
operationName = operationName.Replace("Async", "").Replace("Core", "");
Tracer.Debug(operationContext, $"{Tracer.Name}.{operationName} retry #{retry}");
Tracer.TrackMetric(operationContext, $"{operationName}Retry", 1);
retryCounter?.Increment();
onRetry?.Invoke(retry);
}
retry++;
return action();
}
}
}
}

Просмотреть файл

@ -2,8 +2,15 @@
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.ContractsLight;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.FileSystem;
using BuildXL.Cache.ContentStore.Hashing;
using BuildXL.Cache.ContentStore.Interfaces.FileSystem;
using BuildXL.Cache.ContentStore.Interfaces.Logging;
@ -11,21 +18,56 @@ using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
using BuildXL.Cache.ContentStore.Interfaces.Stores;
using BuildXL.Cache.ContentStore.Service;
using BuildXL.Cache.ContentStore.Service.Grpc;
using BuildXL.Cache.ContentStore.Stores;
using BuildXL.Cache.ContentStore.Tracing;
using BuildXL.Cache.ContentStore.Tracing.Internal;
using BuildXL.Cache.ContentStore.UtilitiesCore;
using BuildXL.Cache.ContentStore.Utils;
using BuildXL.Utilities.Tracing;
namespace BuildXL.Cache.ContentStore.Sessions
{
/// <summary>
/// An IContentSession implemented over a ServiceClientsContentStore.
/// </summary>
public class ServiceClientContentSession : ReadOnlyServiceClientContentSession, IContentSession
public class ServiceClientContentSession : ContentSessionBase, IContentSession
{
/// <summary>
/// The filesystem backing the session.
/// </summary>
protected readonly IAbsFileSystem FileSystem;
/// <summary>
/// Generator of temporary, seekable streams.
/// </summary>
protected readonly TempFileStreamFactory TempFileStreamFactory;
/// <inheritdoc />
protected override Tracer Tracer { get; } = new Tracer(nameof(ServiceClientContentSession));
/// <summary>
/// Request to server retry policy.
/// </summary>
protected readonly IRetryPolicy RetryPolicy;
/// <summary>
/// The client backing the session.
/// </summary>
protected readonly IRpcClient RpcClient;
/// <nodoc />
protected readonly ServiceClientContentStoreConfiguration Configuration;
/// <nodoc />
protected readonly ServiceClientContentSessionTracer SessionTracer;
/// <nodoc />
protected readonly ILogger Logger;
/// <nodoc />
protected readonly ImplicitPin ImplicitPin;
/// <inheritdoc />
protected override bool TraceOperationStarted => Configuration.TraceOperationStarted;
/// <summary>
/// Initializes a new instance of the <see cref="ServiceClientContentSession"/> class.
/// </summary>
@ -38,13 +80,227 @@ namespace BuildXL.Cache.ContentStore.Sessions
ServiceClientContentSessionTracer sessionTracer,
ServiceClientContentStoreConfiguration configuration,
Func<IRpcClient>? rpcClientFactory = null)
: base(context, name, implicitPin, logger, fileSystem, sessionTracer, configuration, rpcClientFactory)
: base(name)
{
Contract.Requires(name != null);
Contract.Requires(logger != null);
Contract.Requires(fileSystem != null);
ImplicitPin = implicitPin;
SessionTracer = sessionTracer;
Logger = logger;
FileSystem = fileSystem;
Configuration = configuration;
TempFileStreamFactory = new TempFileStreamFactory(FileSystem);
RpcClient = (rpcClientFactory ?? (() => GetRpcClient(context)))();
RetryPolicy = configuration.RetryPolicy;
}
/// <nodoc />
protected virtual IRpcClient GetRpcClient(OperationContext context)
{
var rpcConfiguration = Configuration.RpcConfiguration;
return new GrpcContentClient(context, SessionTracer, FileSystem, rpcConfiguration, Configuration.Scenario);
}
/// <inheritdoc />
protected override async Task<BoolResult> StartupCoreAsync(OperationContext operationContext)
{
BoolResult result;
try
{
result = await RetryPolicy.ExecuteAsync(
() => RpcClient.CreateSessionAsync(operationContext, Name, Configuration.CacheName, ImplicitPin),
CancellationToken.None);
}
catch (Exception ex)
{
result = new BoolResult(ex);
}
if (!result)
{
await RetryPolicy.ExecuteAsync(() => RpcClient.ShutdownAsync(operationContext), CancellationToken.None).ThrowIfFailure();
}
return result;
}
/// <inheritdoc />
protected override async Task<BoolResult> ShutdownCoreAsync(OperationContext operationContext)
{
var result = await RetryPolicy.ExecuteAsync(() => RpcClient.ShutdownAsync(operationContext), CancellationToken.None);
var counterSet = new CounterSet();
counterSet.Merge(GetCounters(), $"{Tracer.Name}.");
Tracer.TraceStatisticsAtShutdown(operationContext, counterSet, prefix: "ServiceClientContentSessionStats");
return result;
}
/// <inheritdoc />
protected override void DisposeCore()
{
base.DisposeCore();
RpcClient.Dispose();
TempFileStreamFactory.Dispose();
}
/// <inheritdoc />
protected override Task<PinResult> PinCoreAsync(
OperationContext operationContext,
ContentHash contentHash,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return PerformRetries(
operationContext,
() => RpcClient.PinAsync(operationContext, contentHash, urgencyHint),
retryCounter: retryCounter);
}
/// <inheritdoc />
protected override Task<OpenStreamResult> OpenStreamCoreAsync(
OperationContext operationContext,
ContentHash contentHash,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return PerformRetries(
operationContext,
() => RpcClient.OpenStreamAsync(operationContext, contentHash, urgencyHint),
retryCounter: retryCounter);
}
/// <inheritdoc />
protected override Task<PlaceFileResult> PlaceFileCoreAsync(
OperationContext operationContext,
ContentHash contentHash,
AbsolutePath path,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
UrgencyHint urgencyHint,
Counter retryCounter)
{
if (replacementMode != FileReplacementMode.ReplaceExisting && FileSystem.FileExists(path))
{
if (replacementMode == FileReplacementMode.SkipIfExists)
{
return Task.FromResult(PlaceFileResult.AlreadyExists);
}
else if (replacementMode == FileReplacementMode.FailIfExists)
{
return Task.FromResult(
new PlaceFileResult(
PlaceFileResult.ResultCode.Error,
$"File exists at destination {path} with FailIfExists specified"));
}
}
return PerformRetries(
operationContext,
() => RpcClient.PlaceFileAsync(operationContext, contentHash, path, accessMode, replacementMode, realizationMode, urgencyHint),
retryCounter: retryCounter);
}
/// <inheritdoc />
protected override async Task<IEnumerable<Task<Indexed<PinResult>>>> PinCoreAsync(
OperationContext operationContext,
IReadOnlyList<ContentHash> contentHashes,
UrgencyHint urgencyHint,
Counter retryCounter,
Counter fileCounter)
{
var retry = 0;
try
{
return await RetryPolicy.ExecuteAsync(PinBulkFunc, operationContext.Token);
}
catch (Exception ex)
{
Tracer.Warning(operationContext, $"PinBulk failed with exception {ex}");
return contentHashes.Select((hash, index) => Task.FromResult(new PinResult(ex).WithIndex(index)));
}
async Task<IEnumerable<Task<Indexed<PinResult>>>> PinBulkFunc()
{
var sw = Stopwatch.StartNew();
try
{
fileCounter.Add(contentHashes.Count);
if (retry > 0)
{
Tracer.Debug(operationContext, $"{Tracer.Name}.PinBulk retry #{retry}");
retryCounter.Increment();
}
return await RpcClient.PinAsync(operationContext, contentHashes, urgencyHint);
}
finally
{
Tracer.Debug(
operationContext,
$"{Tracer.Name}.PinBulk({contentHashes.Count}) stop {sw.Elapsed.TotalMilliseconds}ms. Hashes: [{string.Join(",", contentHashes)}]");
}
}
}
/// <inheritdoc />
protected override Task<IEnumerable<Task<Indexed<PlaceFileResult>>>> PlaceFileCoreAsync(
OperationContext operationContext,
IReadOnlyList<ContentHashWithPath> hashesWithPaths,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
UrgencyHint urgencyHint,
Counter retryCounter)
{
throw new NotImplementedException();
}
/// <nodoc />
protected Task<T> PerformRetries<T>(
OperationContext operationContext,
Func<Task<T>> action,
Action<int>? onRetry = null,
Counter? retryCounter = null,
[CallerMemberName] string? operationName = null)
{
Contract.Requires(operationName != null);
var retry = 0;
return RetryPolicy.ExecuteAsync(Wrapper, operationContext.Token);
Task<T> Wrapper()
{
if (retry > 0)
{
// Normalize operation name
operationName = operationName.Replace("Async", "").Replace("Core", "");
Tracer.Debug(operationContext, $"{Tracer.Name}.{operationName} retry #{retry}");
Tracer.TrackMetric(operationContext, $"{operationName}Retry", 1);
retryCounter?.Increment();
onRetry?.Invoke(retry);
}
retry++;
return action();
}
}
/// <inheritdoc />
protected override Task<PutResult> PutStreamCoreAsync(
OperationContext operationContext, HashType hashType, Stream stream, UrgencyHint urgencyHint, Counter retryCounter)
OperationContext operationContext,
HashType hashType,
Stream stream,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return PutStreamCoreAsync(
operationContext,
@ -55,7 +311,11 @@ namespace BuildXL.Cache.ContentStore.Sessions
/// <inheritdoc />
protected override Task<PutResult> PutStreamCoreAsync(
OperationContext operationContext, ContentHash contentHash, Stream stream, UrgencyHint urgencyHint, Counter retryCounter)
OperationContext operationContext,
ContentHash contentHash,
Stream stream,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return PutStreamCoreAsync(
operationContext,
@ -64,7 +324,11 @@ namespace BuildXL.Cache.ContentStore.Sessions
args => RpcClient.PutStreamAsync(operationContext, contentHash, args.putStream, args.createDirectory, urgencyHint));
}
private async Task<PutResult> PutStreamCoreAsync(OperationContext operationContext, Stream stream, Counter retryCounter, Func<(Stream putStream, bool createDirectory), Task<PutResult>> putStreamAsync)
private async Task<PutResult> PutStreamCoreAsync(
OperationContext operationContext,
Stream stream,
Counter retryCounter,
Func<(Stream putStream, bool createDirectory), Task<PutResult>> putStreamAsync)
{
// We need a seekable stream, that can give its length. If the input stream is seekable, we can use it directly.
// Otherwise, we need to create a temp file for this purpose.

Просмотреть файл

@ -169,22 +169,12 @@ namespace BuildXL.Cache.ContentStore.Stores
_directoryLock.Dispose();
}
/// <inheritdoc />
public virtual CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
return CreateReadOnlySessionCall.Run(_tracer, OperationContext(context), name, () =>
{
var session = new ReadOnlyFileSystemContentSession(name, Store, implicitPin);
return new CreateSessionResult<IReadOnlyContentSession>(session);
});
}
/// <inheritdoc />
public virtual CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{
return CreateSessionCall.Run(_tracer, OperationContext(context), name, () =>
{
var session = new FileSystemContentSession(name, implicitPin, Store);
var session = new FileSystemContentSession(name, Store, implicitPin);
return new CreateSessionResult<IContentSession>(session);
});
}

Просмотреть файл

@ -22,24 +22,21 @@ namespace BuildXL.Cache.ContentStore.Stores
/// <summary>
/// ContentStore that is empty and does not accept content. Useful when we want to disable content; specifically, to bypass talking to VSTS/CASaaS when unnecessary.
/// </summary>
public class ReadOnlyEmptyContentStore : StartupShutdownBase, IContentStore
public class EmptyContentStore : StartupShutdownBase, IContentStore
{
private readonly ContentStoreTracer _tracer = new ContentStoreTracer(nameof(ReadOnlyEmptyContentStore));
private readonly ContentStoreTracer _tracer = new ContentStoreTracer(nameof(EmptyContentStore));
/// <inheritdoc />
protected override Tracer Tracer => _tracer;
/// <inheritdoc />
public CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin) => new CreateSessionResult<IReadOnlyContentSession>(new ReadOnlyEmptyContentSession(name));
/// <inheritdoc />
public CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin) => new CreateSessionResult<IContentSession>(new ReadOnlyEmptyContentSession(name));
public CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin) => new CreateSessionResult<IContentSession>(new EmptyContentSession(name));
/// <inheritdoc />
public Task<GetStatsResult> GetStatsAsync(Context context) => Task.FromResult(new GetStatsResult(_tracer.GetCounters()));
/// <inheritdoc />
Task<DeleteResult> IContentStore.DeleteAsync(Context context, ContentHash contentHash, DeleteContentOptions? deleteOptions) => Task.FromResult(new DeleteResult(DeleteResult.ResultCode.ContentNotDeleted, $"{nameof(ReadOnlyEmptyContentStore)} cannot contain any content to delete"));
Task<DeleteResult> IContentStore.DeleteAsync(Context context, ContentHash contentHash, DeleteContentOptions? deleteOptions) => Task.FromResult(new DeleteResult(DeleteResult.ResultCode.ContentNotDeleted, $"{nameof(EmptyContentStore)} cannot contain any content to delete"));
/// <inheritdoc />
public void PostInitializationCompleted(Context context, BoolResult result) { }
@ -48,10 +45,10 @@ namespace BuildXL.Cache.ContentStore.Stores
/// <summary>
/// ContentSession is empty and does not accept content. Useful when we want to disable content; specifically, to bypass talking to VSTS/CASaaS when unnecessary.
/// </summary>
public class ReadOnlyEmptyContentSession : ContentSessionBase
public class EmptyContentSession : ContentSessionBase
{
/// <nodoc />
public ReadOnlyEmptyContentSession(string name)
public EmptyContentSession(string name)
: base(name)
{
}
@ -59,7 +56,7 @@ namespace BuildXL.Cache.ContentStore.Stores
private const string ErrorMessage = "Unsupported operation.";
/// <inheritdoc />
protected override Tracer Tracer { get; } = new Tracer(nameof(ReadOnlyEmptyContentSession));
protected override Tracer Tracer { get; } = new Tracer(nameof(EmptyContentSession));
/// <inheritdoc />
protected override Task<OpenStreamResult> OpenStreamCoreAsync(OperationContext operationContext, ContentHash contentHash, UrgencyHint urgencyHint, Counter retryCounter)

Просмотреть файл

@ -121,18 +121,6 @@ namespace BuildXL.Cache.ContentStore.Stores
return base.ShutdownCoreAsync(context);
}
/// <inheritdoc />
public CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
return CreateReadOnlySessionCall.Run(_tracer, OperationContext(context), name, () =>
{
#pragma warning disable CS8604 // Possible null reference argument.
var session = new RocksDbFileSystemContentSession(name, _lockSet, _fileSystem, _clock, _rootPath, _storePath, _tempDisposableDirectory, _accessor);
#pragma warning restore CS8604 // Possible null reference argument.
return new CreateSessionResult<IReadOnlyContentSession>(session);
});
}
/// <inheritdoc />
public CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{

Просмотреть файл

@ -194,25 +194,6 @@ namespace BuildXL.Cache.ContentStore.Stores
_grpcClient?.Dispose();
}
/// <inheritdoc />
public virtual CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(
Context context, string name, ImplicitPin implicitPin)
{
return CreateReadOnlySessionCall.Run(ExecutionTracer, OperationContext(context), name, () =>
{
var session = new ReadOnlyServiceClientContentSession(
// Its fine to re-create an operation context without cancellation tokens because its only used for tracing purposes.
new OperationContext(context),
name,
implicitPin,
Logger,
FileSystem,
SessionTracer,
Configuration);
return new CreateSessionResult<IReadOnlyContentSession>(session);
});
}
/// <inheritdoc />
public virtual CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{

Просмотреть файл

@ -54,35 +54,6 @@ namespace BuildXL.Cache.ContentStore.Stores
/// <inheritdoc />
protected override ContentStoreTracer Tracer => _tracer;
/// <inheritdoc />
public override CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(
Context context,
string name,
ImplicitPin implicitPin)
{
return CreateReadOnlySessionCall.Run(_tracer, new OperationContext(context), name, () =>
{
var sessionForStream = ContentStoreForStream.CreateSession(context, name, implicitPin);
if (!sessionForStream.Succeeded)
{
return new CreateSessionResult<IReadOnlyContentSession>(sessionForStream, "creation of stream content session failed");
}
var sessionForPath = ContentStoreForPath.CreateSession(context, name, implicitPin);
if (!sessionForPath.Succeeded)
{
return new CreateSessionResult<IReadOnlyContentSession>(sessionForPath, "creation of path content session failed");
}
var session = new StreamPathContentSession(
name,
sessionForStream.Session,
sessionForPath.Session);
return new CreateSessionResult<IReadOnlyContentSession>(session);
});
}
/// <inheritdoc />
public override CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{

Просмотреть файл

@ -167,14 +167,11 @@ namespace BuildXL.Cache.ContentStore.Stores
}
/// <inheritdoc />
public abstract CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(
public abstract CreateSessionResult<IContentSession> CreateSession(
Context context,
string name,
ImplicitPin implicitPin);
/// <inheritdoc />
public abstract CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin);
/// <inheritdoc />
public virtual Task<GetStatsResult> GetStatsAsync(Context context)
{

Просмотреть файл

@ -39,22 +39,6 @@ namespace BuildXL.Cache.ContentStore.Tracing
}
public void CreateReadOnlySessionStart(Context context, string name)
{
if (context.IsEnabled)
{
Debug(context, $"{Name}.CreateReadOnlySession({name}) start");
}
}
public void CreateReadOnlySessionStop(Context context, CreateSessionResult<IReadOnlyContentSession> result)
{
if (context.IsEnabled)
{
TracerOperationFinished(context, result, $"{Name}.CreateReadOnlySession() stop {result.DurationMs}ms result=[{result}]");
}
}
public void CreateSessionStart(Context context, string name)
{
if (context.IsEnabled)

Просмотреть файл

@ -1,50 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
using BuildXL.Cache.ContentStore.Tracing.Internal;
namespace BuildXL.Cache.ContentStore.Tracing
{
/// <summary>
/// Instance of a CreateReadOnlySession operation for tracing purposes.
/// </summary>
public sealed class CreateReadOnlySessionCall
: TracedCall<ContentStoreTracer, CreateSessionResult<IReadOnlyContentSession>>, IDisposable
{
/// <summary>
/// Run the call.
/// </summary>
public static CreateSessionResult<IReadOnlyContentSession> Run(
ContentStoreTracer tracer, OperationContext context, string name, Func<CreateSessionResult<IReadOnlyContentSession>> func)
{
using (var call = new CreateReadOnlySessionCall(tracer, context, name))
{
return call.Run(func);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="CreateReadOnlySessionCall"/> class.
/// </summary>
private CreateReadOnlySessionCall(ContentStoreTracer tracer, OperationContext context, string name)
: base(tracer, context)
{
Tracer.CreateReadOnlySessionStart(Context, name);
}
/// <inheritdoc />
protected override CreateSessionResult<IReadOnlyContentSession> CreateErrorResult(Exception exception)
{
return new CreateSessionResult<IReadOnlyContentSession>(exception);
}
/// <inheritdoc />
public void Dispose()
{
Tracer.CreateReadOnlySessionStop(Context, Result);
}
}
}

Просмотреть файл

@ -42,7 +42,7 @@ namespace ContentStoreTest.Performance.Sessions
private const int SmallFileSize = 1024;
private const int LargeFileSize = 5 * 1024 * 1024;
private const HashType ContentHashType = HashType.Vso0;
private static readonly Func<IReadOnlyContentSession, Task> EmptySetupFuncAsync = session => Task.FromResult(0);
private static readonly Func<IContentSession, Task> EmptySetupFuncAsync = session => Task.FromResult(0);
private static readonly CancellationToken Token = CancellationToken.None;
private readonly Context _context;
private readonly int _itemCount;
@ -62,7 +62,7 @@ namespace ContentStoreTest.Performance.Sessions
protected abstract IContentStore CreateStore(AbsolutePath rootPath, string cacheName, ContentStoreConfiguration configuration);
protected abstract Task<IReadOnlyList<ContentHash>> EnumerateContentHashesAsync(IReadOnlyContentSession session);
protected abstract Task<IReadOnlyList<ContentHash>> EnumerateContentHashesAsync(IContentSession session);
protected ContentPerformanceTests
(
@ -118,7 +118,7 @@ namespace ContentStoreTest.Performance.Sessions
}
private async Task PinAsync(
IReadOnlyContentSession session, IReadOnlyCollection<ContentHash> hashes, List<PinResult> results)
IContentSession session, IReadOnlyCollection<ContentHash> hashes, List<PinResult> results)
{
var tasks = hashes.Select(contentHash => Task.Run(async () =>
await session.PinAsync(_context, contentHash, Token)));
@ -164,7 +164,7 @@ namespace ContentStoreTest.Performance.Sessions
}
private async Task OpenStreamAsync(
IReadOnlyContentSession session, IReadOnlyCollection<ContentHash> hashes, List<OpenStreamResult> results)
IContentSession session, IReadOnlyCollection<ContentHash> hashes, List<OpenStreamResult> results)
{
var tasks = hashes.Select(contentHash => Task.Run(async () =>
await session.OpenStreamAsync(_context, contentHash, Token)));
@ -223,7 +223,7 @@ namespace ContentStoreTest.Performance.Sessions
}
private async Task PlaceFileAsync(
IReadOnlyContentSession session, IReadOnlyCollection<Tuple<ContentHash, AbsolutePath>> args, List<PlaceFileResult> results)
IContentSession session, IReadOnlyCollection<Tuple<ContentHash, AbsolutePath>> args, List<PlaceFileResult> results)
{
var tasks = args.Select(t => Task.Run(async () => await session.PlaceFileAsync(
_context,
@ -384,7 +384,7 @@ namespace ContentStoreTest.Performance.Sessions
(
string method,
Func<IContentSession, Task> setupFuncAsync,
Func<IReadOnlyContentSession, Task> testFuncAsync
Func<IContentSession, Task> testFuncAsync
)
{
return RunImpl(method, AccessNeeded.ReadOnly, setupFuncAsync, async session =>

Просмотреть файл

@ -37,7 +37,7 @@ namespace ContentStoreTest.Performance.Sessions
return new TestFileSystemContentStore(FileSystem, SystemClock.Instance, rootPath, configurationModel);
}
protected override Task<IReadOnlyList<ContentHash>> EnumerateContentHashesAsync(IReadOnlyContentSession session)
protected override Task<IReadOnlyList<ContentHash>> EnumerateContentHashesAsync(IContentSession session)
{
var testSession = (TestFileSystemContentSession)session;
return testSession.EnumerateHashes();

Просмотреть файл

@ -56,7 +56,7 @@ namespace ContentStoreTest.Performance.Sessions
FileSystem, Logger, cacheName, _scenario, null, serviceConfig);
}
protected override Task<IReadOnlyList<ContentHash>> EnumerateContentHashesAsync(IReadOnlyContentSession session)
protected override Task<IReadOnlyList<ContentHash>> EnumerateContentHashesAsync(IContentSession session)
{
var testSession = (TestServiceClientContentSession)session;
return testSession.EnumerateHashes();

Просмотреть файл

@ -55,7 +55,7 @@ namespace ContentStoreTest.Performance.Sessions
serviceConfiguration);
}
protected override Task<IReadOnlyList<ContentHash>> EnumerateContentHashesAsync(IReadOnlyContentSession session)
protected override Task<IReadOnlyList<ContentHash>> EnumerateContentHashesAsync(IContentSession session)
{
var testSession = (TestServiceClientContentSession)session;
return testSession.EnumerateHashes();

Просмотреть файл

@ -14,7 +14,7 @@ namespace ContentStoreTest.Sessions
public class TestFileSystemContentSession : FileSystemContentSession
{
public TestFileSystemContentSession(string name, ImplicitPin implicitPin, FileSystemContentStoreInternal store)
: base(name, implicitPin, store)
: base(name, store, implicitPin)
{
}

Просмотреть файл

@ -48,12 +48,6 @@ namespace ContentStoreTest.Stores
return Task.FromResult(new BoolResult(FailureMessage));
}
// <inheritdoc />
public CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
return new CreateSessionResult<IReadOnlyContentSession>(FailureMessage);
}
// <inheritdoc />
public CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{

Просмотреть файл

@ -27,12 +27,6 @@ namespace ContentStoreTest.Stores
public ContentStoreConfiguration Configuration => Store.Configuration;
public override CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
var session = new TestFileSystemContentSession(name, implicitPin, Store);
return new CreateSessionResult<IReadOnlyContentSession>(session);
}
public override CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{
var session = new TestFileSystemContentSession(name, implicitPin, Store);

Просмотреть файл

@ -163,29 +163,8 @@ namespace ContentStoreTest.Stores
return new ServiceClientRpcConfiguration(grpcPort, _heartbeatInterval);
}
public override CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(
public override CreateSessionResult<IContentSession> CreateSession(
Context context, string name, ImplicitPin implicitPin)
{
return CreateReadOnlySessionCall.Run(ExecutionTracer, OperationContext(context), name, () =>
{
var session = new TestServiceClientContentSession(
new OperationContext(context),
name,
implicitPin,
Configuration.RetryPolicy,
_configuration.DataRootPath,
_overrideCacheName ?? Configuration.CacheName,
context.Logger,
FileSystem,
Configuration.Scenario,
this,
SessionTracer,
GetRpcConfig());
return new CreateSessionResult<IReadOnlyContentSession>(session);
});
}
public override CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{
return CreateSessionCall.Run(ExecutionTracer, OperationContext(context), name, () =>
{

Просмотреть файл

@ -117,29 +117,6 @@ namespace ContentStoreTest.Stores
return new ServiceClientRpcConfiguration(grpcPort, _heartbeatInterval);
}
public override CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(
Context context, string name, ImplicitPin implicitPin)
{
return CreateReadOnlySessionCall.Run(ExecutionTracer, OperationContext(context), name, () =>
{
var session = new TestServiceClientContentSession(
new OperationContext(context),
name,
implicitPin,
Configuration.RetryPolicy,
_configuration.DataRootPath,
_overrideCacheName ?? Configuration.CacheName,
context.Logger,
FileSystem,
Configuration.Scenario,
this,
SessionTracer,
GetRpcConfig()
);
return new CreateSessionResult<IReadOnlyContentSession>(session);
});
}
public override CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{
return CreateSessionCall.Run(ExecutionTracer, OperationContext(context), name, () =>

Просмотреть файл

@ -42,7 +42,7 @@ using OperationContext = BuildXL.Cache.ContentStore.Tracing.Internal.OperationCo
namespace BuildXL.Cache.ContentStore.Vsts
{
/// <summary>
/// IReadOnlyContentSession for BlobBuildXL.ContentStore.
/// IContentSession for BlobBuildXL.ContentStore.
/// </summary>
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public class BlobReadOnlyContentSession : ContentSessionBase, IReadOnlyBackingContentSession

Просмотреть файл

@ -34,7 +34,7 @@ using OperationContext = BuildXL.Cache.ContentStore.Tracing.Internal.OperationCo
namespace BuildXL.Cache.ContentStore.Vsts
{
/// <summary>
/// IReadOnlyContentSession for DedupContentStore.
/// IContentSession for DedupContentStore.
/// </summary>
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public class DedupReadOnlyContentSession : ContentSessionBase, IReadOnlyBackingContentSession

Просмотреть файл

@ -12,7 +12,7 @@ using BuildXL.Cache.ContentStore.Tracing.Internal;
namespace BuildXL.Cache.ContentStore.Vsts
{
/// <nodoc />
public interface IReadOnlyBackingContentSession : IReadOnlyContentSession
public interface IReadOnlyBackingContentSession : IContentSession
{
/// <summary>
/// Bulk operations for pins with a specific TTL

Просмотреть файл

@ -2,23 +2,31 @@
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Diagnostics.ContractsLight;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Hashing;
using BuildXL.Cache.ContentStore.Interfaces.FileSystem;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
using BuildXL.Cache.ContentStore.Sessions;
using BuildXL.Cache.ContentStore.Tracing;
using BuildXL.Cache.ContentStore.Tracing.Internal;
using BuildXL.Utilities.Tracing;
namespace BuildXL.Cache.Host.Service.Internal
{
/// <summary>
/// Session which aggregates a local and backing content store. The backing content store is
/// used to populate local content store in cases of local misses.
/// </summary>
public class MultiLevelContentSession : MultiLevelReadOnlyContentSession<IContentSession>, IContentSession
public class MultiLevelContentSession : ContentSessionBase, IContentSession, IHibernateContentSession
{
protected readonly IContentSession LocalSession;
protected readonly IContentSession BackingSession;
/// <inheritdoc />
protected override Tracer Tracer { get; } = new Tracer(nameof(MultiLevelContentStore));
/// <summary>
/// Initializes a new instance of the <see cref="MultiLevelContentSession"/> class.
/// </summary>
@ -26,32 +34,202 @@ namespace BuildXL.Cache.Host.Service.Internal
string name,
IContentSession localSession,
IContentSession backingSession)
: base(name, localSession, backingSession, isLocalWritable: true)
: base(name)
{
Contract.Requires(name != null);
Contract.Requires(localSession != null);
Contract.Requires(backingSession != null);
LocalSession = localSession;
BackingSession = backingSession;
}
/// <inheritdoc />
protected override Task<PutResult> PutFileCoreAsync(OperationContext operationContext, ContentHash contentHash, AbsolutePath path, FileRealizationMode realizationMode, UrgencyHint urgencyHint, Counter retryCounter)
protected override async Task<BoolResult> StartupCoreAsync(OperationContext context)
{
return MultiLevelWriteAsync(session => session.PutFileAsync(
return (await LocalSession.StartupAsync(context) & await BackingSession.StartupAsync(context));
}
/// <inheritdoc />
protected override async Task<BoolResult> ShutdownCoreAsync(OperationContext context)
{
return (await LocalSession.ShutdownAsync(context) & await BackingSession.ShutdownAsync(context));
}
private async Task<TResult> MultiLevelReadAsync<TResult>(
OperationContext context,
ContentHash hash,
Func<IContentSession, Task<TResult>> runAsync)
where TResult : ResultBase
{
var result = await runAsync(LocalSession);
if (!result.Succeeded)
{
var ensureLocalResult = await EnsureLocalAsync(context, hash);
if (ensureLocalResult.Succeeded)
{
return await runAsync(LocalSession);
}
}
return result;
}
private Task<PutResult> EnsureLocalAsync(OperationContext context, ContentHash hash)
{
return context.PerformOperationAsync<PutResult>(
Tracer,
async () =>
{
var streamResult = await BackingSession.OpenStreamAsync(context, hash, context.Token).ThrowIfFailure();
return await LocalSession.PutStreamAsync(context, hash, streamResult.Stream, context.Token);
});
}
/// <inheritdoc />
public IEnumerable<ContentHash> EnumeratePinnedContentHashes()
{
return LocalSession is IHibernateContentSession session
? session.EnumeratePinnedContentHashes()
: Enumerable.Empty<ContentHash>();
}
/// <inheritdoc />
public Task PinBulkAsync(Context context, IEnumerable<ContentHash> contentHashes)
{
return LocalSession is IHibernateContentSession session
? session.PinBulkAsync(context, contentHashes)
: BoolResult.SuccessTask;
}
/// <inheritdoc />
public Task<BoolResult> ShutdownEvictionAsync(Context context)
{
return LocalSession is IHibernateContentSession session
? session.ShutdownEvictionAsync(context)
: BoolResult.SuccessTask;
}
/// <inheritdoc />
protected override Task<OpenStreamResult> OpenStreamCoreAsync(
OperationContext operationContext,
ContentHash contentHash,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return MultiLevelReadAsync(
operationContext,
contentHash,
path,
CoerceRealizationMode(realizationMode, session),
operationContext.Token,
urgencyHint));
session => session.OpenStreamAsync(operationContext, contentHash, operationContext.Token, urgencyHint));
}
/// <inheritdoc />
protected override Task<PutResult> PutFileCoreAsync(OperationContext operationContext, HashType hashType, AbsolutePath path, FileRealizationMode realizationMode, UrgencyHint urgencyHint, Counter retryCounter)
protected override Task<PinResult> PinCoreAsync(
OperationContext operationContext,
ContentHash contentHash,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return MultiLevelWriteAsync(session => session.PutFileAsync(
// TODO: decide if we want to pin on the backing session as well. The issue here is as follows: on the use-
// case we need this for (permanent distributed CASaaS running + local CAS on a different drive with drop
// as client against distributed), it doesn't really matters what pin does. For the general scenario, it
// depends.
return LocalSession.PinAsync(operationContext, contentHash, operationContext.Token, urgencyHint);
}
/// <inheritdoc />
protected override Task<IEnumerable<Task<Indexed<PinResult>>>> PinCoreAsync(
OperationContext operationContext,
IReadOnlyList<ContentHash> contentHashes,
UrgencyHint urgencyHint,
Counter retryCounter,
Counter fileCounter)
{
// TODO: decide if we want to pin on the backing session as well. The issue here is as follows: on the use-
// case we need this for (permanent distributed CASaaS running + local CAS on a different drive with drop
// as client against distributed), it doesn't really matters what pin does. For the general scenario, it
// depends.
return LocalSession.PinAsync(operationContext, contentHashes, operationContext.Token, urgencyHint);
}
/// <inheritdoc />
protected override Task<PlaceFileResult> PlaceFileCoreAsync(
OperationContext operationContext,
ContentHash contentHash,
AbsolutePath path,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return MultiLevelReadAsync(
operationContext,
hashType,
path,
CoerceRealizationMode(realizationMode, session),
operationContext.Token,
urgencyHint));
contentHash,
session => session.PlaceFileAsync(
operationContext,
contentHash,
path,
accessMode,
replacementMode,
realizationMode,
operationContext.Token,
urgencyHint));
}
/// <inheritdoc />
protected override Task<IEnumerable<Task<Indexed<PlaceFileResult>>>> PlaceFileCoreAsync(
OperationContext operationContext,
IReadOnlyList<ContentHashWithPath> hashesWithPaths,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
UrgencyHint urgencyHint,
Counter retryCounter)
{
// NOTE: Most of the IContentSession implementations throw NotImplementedException, most notably
// the ReadOnlyServiceClientContentSession which is used to communicate with this session. Given that,
// it is safe for this method to not be implemented here as well.
throw new NotImplementedException();
}
/// <inheritdoc />
protected override Task<PutResult> PutFileCoreAsync(
OperationContext operationContext,
ContentHash contentHash,
AbsolutePath path,
FileRealizationMode realizationMode,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return MultiLevelWriteAsync(
session => session.PutFileAsync(
operationContext,
contentHash,
path,
CoerceRealizationMode(realizationMode, session),
operationContext.Token,
urgencyHint));
}
/// <inheritdoc />
protected override Task<PutResult> PutFileCoreAsync(
OperationContext operationContext,
HashType hashType,
AbsolutePath path,
FileRealizationMode realizationMode,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return MultiLevelWriteAsync(
session => session.PutFileAsync(
operationContext,
hashType,
path,
CoerceRealizationMode(realizationMode, session),
operationContext.Token,
urgencyHint));
}
private FileRealizationMode CoerceRealizationMode(FileRealizationMode mode, IContentSession session)
@ -66,13 +244,24 @@ namespace BuildXL.Cache.Host.Service.Internal
}
/// <inheritdoc />
protected override Task<PutResult> PutStreamCoreAsync(OperationContext operationContext, ContentHash contentHash, Stream stream, UrgencyHint urgencyHint, Counter retryCounter)
protected override Task<PutResult> PutStreamCoreAsync(
OperationContext operationContext,
ContentHash contentHash,
Stream stream,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return MultiLevelWriteAsync(session => session.PutStreamAsync(operationContext, contentHash, stream, operationContext.Token, urgencyHint));
return MultiLevelWriteAsync(
session => session.PutStreamAsync(operationContext, contentHash, stream, operationContext.Token, urgencyHint));
}
/// <inheritdoc />
protected override Task<PutResult> PutStreamCoreAsync(OperationContext operationContext, HashType hashType, Stream stream, UrgencyHint urgencyHint, Counter retryCounter)
protected override Task<PutResult> PutStreamCoreAsync(
OperationContext operationContext,
HashType hashType,
Stream stream,
UrgencyHint urgencyHint,
Counter retryCounter)
{
return MultiLevelWriteAsync(session => session.PutStreamAsync(operationContext, hashType, stream, operationContext.Token, urgencyHint));
}

Просмотреть файл

@ -58,18 +58,6 @@ namespace BuildXL.Cache.Host.Service.Internal
_backingContentStore.Dispose();
}
/// <nodoc />
public CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
return CreateReadOnlySessionCall.Run((ContentStoreTracer)Tracer, new OperationContext(context), name, () =>
{
var localSession = _localContentStore.CreateSession(context, name, implicitPin).ThrowIfFailure();
var backingSession = _backingContentStore.CreateReadOnlySession(context, name, implicitPin).ThrowIfFailure();
return new CreateSessionResult<IReadOnlyContentSession>(new MultiLevelReadOnlyContentSession<IReadOnlyContentSession>(name, localSession.Session, backingSession.Session, isLocalWritable: true));
});
}
/// <nodoc />
public CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{

Просмотреть файл

@ -1,168 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Diagnostics.ContractsLight;
using System.Linq;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Hashing;
using BuildXL.Cache.ContentStore.Interfaces.FileSystem;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
using BuildXL.Cache.ContentStore.Sessions;
using BuildXL.Cache.ContentStore.Tracing;
using BuildXL.Cache.ContentStore.Tracing.Internal;
using BuildXL.Utilities.Tracing;
namespace BuildXL.Cache.Host.Service.Internal
{
using PlaceBulkResult = IEnumerable<Task<Indexed<PlaceFileResult>>>;
/// <summary>
/// Session which aggregates a local and backing content store. If local content store is marked writable, the
/// backing content store is used to populate local content store in cases of local misses.
/// </summary>
public class MultiLevelReadOnlyContentSession<TSession> : ContentSessionBase, IHibernateContentSession
where TSession : IReadOnlyContentSession
{
protected readonly TSession LocalSession;
protected readonly TSession BackingSession;
/// <summary>
/// Gets a local session in which content can be put if writes are allowed
/// </summary>
protected readonly IContentSession WritableLocalSession;
/// <inheritdoc />
protected override Tracer Tracer { get; } = new Tracer(nameof(MultiLevelContentStore));
/// <summary>
/// Initializes a new instance of the <see cref="MultiLevelReadOnlyContentSession{TSession}"/> class.
/// </summary>
public MultiLevelReadOnlyContentSession(
string name,
TSession localSession,
TSession backingSession,
bool isLocalWritable)
: base(name)
{
Contract.Requires(name != null);
Contract.Requires(localSession != null);
Contract.Requires(backingSession != null);
LocalSession = localSession;
BackingSession = backingSession;
WritableLocalSession = isLocalWritable ? (IContentSession)localSession : null;
}
/// <inheritdoc />
protected override async Task<BoolResult> StartupCoreAsync(OperationContext context)
{
return (await LocalSession.StartupAsync(context) & await BackingSession.StartupAsync(context));
}
/// <inheritdoc />
protected override async Task<BoolResult> ShutdownCoreAsync(OperationContext context)
{
return (await LocalSession.ShutdownAsync(context) & await BackingSession.ShutdownAsync(context));
}
private async Task<TResult> MultiLevelReadAsync<TResult>(
OperationContext context,
ContentHash hash,
Func<TSession, Task<TResult>> runAsync)
where TResult : ResultBase
{
var result = await runAsync(LocalSession);
if (!result.Succeeded && WritableLocalSession != null)
{
var ensureLocalResult = await EnsureLocalAsync(context, hash);
if (ensureLocalResult.Succeeded)
{
return await runAsync(LocalSession);
}
}
return result;
}
private Task<PutResult> EnsureLocalAsync(OperationContext context, ContentHash hash)
{
return context.PerformOperationAsync(
Tracer,
async () =>
{
var streamResult = await BackingSession.OpenStreamAsync(context, hash, context.Token).ThrowIfFailure();
return await WritableLocalSession.PutStreamAsync(context, hash, streamResult.Stream, context.Token);
});
}
/// <inheritdoc />
public IEnumerable<ContentHash> EnumeratePinnedContentHashes()
{
return LocalSession is IHibernateContentSession session
? session.EnumeratePinnedContentHashes()
: Enumerable.Empty<ContentHash>();
}
/// <inheritdoc />
public Task PinBulkAsync(Context context, IEnumerable<ContentHash> contentHashes)
{
return LocalSession is IHibernateContentSession session
? session.PinBulkAsync(context, contentHashes)
: BoolResult.SuccessTask;
}
/// <inheritdoc />
public Task<BoolResult> ShutdownEvictionAsync(Context context)
{
return LocalSession is IHibernateContentSession session
? session.ShutdownEvictionAsync(context)
: BoolResult.SuccessTask;
}
/// <inheritdoc />
protected override Task<OpenStreamResult> OpenStreamCoreAsync(OperationContext operationContext, ContentHash contentHash, UrgencyHint urgencyHint, Counter retryCounter)
{
return MultiLevelReadAsync(operationContext, contentHash, session => session.OpenStreamAsync(operationContext, contentHash, operationContext.Token, urgencyHint));
}
/// <inheritdoc />
protected override Task<PinResult> PinCoreAsync(OperationContext operationContext, ContentHash contentHash, UrgencyHint urgencyHint, Counter retryCounter)
{
// TODO: decide if we want to pin on the backing session as well. The issue here is as follows: on the use-
// case we need this for (permanent distributed CASaaS running + local CAS on a different drive with drop
// as client against distributed), it doesn't really matters what pin does. For the general scenario, it
// depends.
return LocalSession.PinAsync(operationContext, contentHash, operationContext.Token, urgencyHint);
}
/// <inheritdoc />
protected override Task<IEnumerable<Task<Indexed<PinResult>>>> PinCoreAsync(OperationContext operationContext, IReadOnlyList<ContentHash> contentHashes, UrgencyHint urgencyHint, Counter retryCounter, Counter fileCounter)
{
// TODO: decide if we want to pin on the backing session as well. The issue here is as follows: on the use-
// case we need this for (permanent distributed CASaaS running + local CAS on a different drive with drop
// as client against distributed), it doesn't really matters what pin does. For the general scenario, it
// depends.
return LocalSession.PinAsync(operationContext, contentHashes, operationContext.Token, urgencyHint);
}
/// <inheritdoc />
protected override Task<PlaceFileResult> PlaceFileCoreAsync(OperationContext operationContext, ContentHash contentHash, AbsolutePath path, FileAccessMode accessMode, FileReplacementMode replacementMode, FileRealizationMode realizationMode, UrgencyHint urgencyHint, Counter retryCounter)
{
return MultiLevelReadAsync(operationContext, contentHash, session => session.PlaceFileAsync(operationContext, contentHash, path, accessMode, replacementMode, realizationMode, operationContext.Token, urgencyHint));
}
/// <inheritdoc />
protected override Task<PlaceBulkResult> PlaceFileCoreAsync(OperationContext operationContext, IReadOnlyList<ContentHashWithPath> hashesWithPaths, FileAccessMode accessMode, FileReplacementMode replacementMode, FileRealizationMode realizationMode, UrgencyHint urgencyHint, Counter retryCounter)
{
// NOTE: Most of the IContentSession implementations throw NotImplementedException, most notably
// the ReadOnlyServiceClientContentSession which is used to communicate with this session. Given that,
// it is safe for this method to not be implemented here as well.
throw new NotImplementedException();
}
}
}

Просмотреть файл

@ -3,26 +3,416 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.ContractsLight;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Distributed.Utilities;
using BuildXL.Cache.ContentStore.Hashing;
using BuildXL.Cache.ContentStore.Interfaces.FileSystem;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
using BuildXL.Cache.ContentStore.Sessions.Internal;
using BuildXL.Cache.ContentStore.Tracing;
using BuildXL.Cache.ContentStore.Tracing.Internal;
using BuildXL.Cache.ContentStore.Utils;
namespace BuildXL.Cache.Host.Service.Internal
{
public class MultiplexedContentSession : MultiplexedReadOnlyContentSession, IContentSession, ITrustedContentSession
public class MultiplexedContentSession : StartupShutdownBase, IContentSession, ITrustedContentSession, IHibernateContentSession
{
/// <nodoc />
public readonly IContentSession PreferredContentSession;
/// <nodoc />
public readonly IDictionary<string, IContentSession> SessionsByCacheRoot;
protected readonly MultiplexedContentStore Store;
/// <inheritdoc />
protected override Tracer Tracer { get; } = new Tracer(nameof(MultiplexedContentSession));
/// <inheritdoc />
public string Name { get; }
/// <inheritdoc />
protected override async Task<BoolResult> StartupCoreAsync(OperationContext context)
{
var finalResult = BoolResult.Success;
var sessions = Enumerable.ToArray<IContentSession>(SessionsByCacheRoot.Values);
for (var i = 0; i < sessions.Length; i++)
{
var canHibernate = sessions[i] is IHibernateContentSession ? "can" : "cannot";
Tracer.Debug(context, $"Session {sessions[i].Name} {canHibernate} hibernate");
var startupResult = await sessions[i].StartupAsync(context).ConfigureAwait(false);
if (!startupResult.Succeeded)
{
finalResult = startupResult;
for (var j = 0; j < i; j++)
{
var shutdownResult = await sessions[j].ShutdownAsync(context).ConfigureAwait(false);
if (!shutdownResult.Succeeded)
{
finalResult = new BoolResult(finalResult, shutdownResult.ErrorMessage);
}
}
}
}
return finalResult;
}
/// <inheritdoc />
protected override async Task<BoolResult> ShutdownCoreAsync(OperationContext context)
{
var finalResult = BoolResult.Success;
foreach (var session in SessionsByCacheRoot.Values)
{
var result = await session.ShutdownAsync(context).ConfigureAwait(false);
if (!result.Succeeded)
{
finalResult = new BoolResult(finalResult, result.ErrorMessage);
}
}
return finalResult;
}
/// <inheritdoc />
protected override void DisposeCore()
{
foreach (var session in SessionsByCacheRoot.Values)
{
session.Dispose();
}
}
/// <inheritdoc />
public Task<PinResult> PinAsync(
Context context,
ContentHash contentHash,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return PerformAggregateSessionOperationAsync<IContentSession, PinResult>(
context,
session => session.PinAsync(context, contentHash, cts, urgencyHint),
(r1, r2) => r1.Succeeded ? r1 : r2,
shouldBreak: r => r.Succeeded);
}
/// <inheritdoc />
public Task<OpenStreamResult> OpenStreamAsync(
Context context,
ContentHash contentHash,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return PerformAggregateSessionOperationAsync<IContentSession, OpenStreamResult>(
context,
session => session.OpenStreamAsync(context, contentHash, cts, urgencyHint),
(r1, r2) => r1.Succeeded ? r1 : r2,
shouldBreak: r => r.Succeeded);
}
/// <inheritdoc />
public Task<PlaceFileResult> PlaceFileAsync(
Context context,
ContentHash contentHash,
AbsolutePath path,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
IContentSession hardlinkSession = null;
if (realizationMode == FileRealizationMode.HardLink)
{
var drive = path.GetPathRoot();
if (SessionsByCacheRoot.TryGetValue(drive, out var session) && session is IContentSession writeableSession)
{
hardlinkSession = writeableSession;
}
else
{
return Task.FromResult(new PlaceFileResult("Requested hardlink but there is no session on the same drive as destination path."));
}
}
return PerformAggregateSessionOperationAsync<IContentSession, PlaceFileResult>(
context,
executeAsync: placeFileCore,
(r1, r2) => r1.Succeeded ? r1 : r2,
shouldBreak: r => r.Succeeded,
pathHint: path);
async Task<PlaceFileResult> placeFileCore(IContentSession session)
{
// If we exclusively want a hardlink, we should make sure that we can copy from other drives to satisfy the request.
if (realizationMode != FileRealizationMode.HardLink || session == hardlinkSession)
{
return await session.PlaceFileAsync(context, contentHash, path, accessMode, replacementMode, realizationMode, cts, urgencyHint);
}
// See if session has the content.
var streamResult = await session.OpenStreamAsync(context, contentHash, cts, urgencyHint).ThrowIfFailure();
// Put it into correct store
var putResult = await hardlinkSession.PutStreamAsync(context, contentHash, streamResult.Stream, cts, urgencyHint).ThrowIfFailure();
// Try the hardlink on the correct drive.
return await hardlinkSession.PlaceFileAsync(
context,
contentHash,
path,
accessMode,
replacementMode,
realizationMode,
cts,
urgencyHint);
}
}
/// <inheritdoc />
public Task<IEnumerable<Task<Indexed<PinResult>>>> PinAsync(
Context context,
IReadOnlyList<ContentHash> contentHashes,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return MultiLevelUtilities.RunManyLevelAsync(
GetSessionsInOrder<IContentSession>().ToArray(),
contentHashes,
(session, hashes) => session.PinAsync(context, hashes, cts, urgencyHint),
p => p.Succeeded);
}
public Task<IEnumerable<Task<Indexed<PinResult>>>> PinAsync(
Context context,
IReadOnlyList<ContentHash> contentHashes,
PinOperationConfiguration config)
{
return MultiLevelUtilities.RunManyLevelAsync(
GetSessionsInOrder<IContentSession>().ToArray(),
contentHashes,
(session, hashes) => session.PinAsync(context, hashes, config),
p => p.Succeeded);
}
protected TCache GetCache<TCache>(AbsolutePath path = null)
{
if (path != null)
{
var drive = path.GetPathRoot();
if (SessionsByCacheRoot.TryGetValue(drive, out var contentSession))
{
return (TCache)contentSession;
}
}
return (TCache)PreferredContentSession;
}
public Task<IEnumerable<Task<Indexed<PlaceFileResult>>>> PlaceFileAsync(
Context context,
IReadOnlyList<ContentHashWithPath> hashesWithPaths,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
// NOTE: this goes around the FileSystemContentStore's bulk place, rendering it useless. The reason
// we do it this way is so that the hardlink logic stays consistent. This means that multiple place
// operations from the same request may run at a higher rate than estipulated by the store.
IEnumerable<Task<Indexed<PlaceFileResult>>> materializations = hashesWithPaths.Select(
async (hashWithPath, index) =>
{
var result = await PlaceFileAsync(
context,
hashWithPath.Hash,
hashWithPath.Path,
accessMode,
replacementMode,
realizationMode,
cts,
urgencyHint);
return new Indexed<PlaceFileResult>(result, index);
});
return Task.FromResult(materializations.ToList().AsEnumerable());
}
/// <inheritdoc />
public IEnumerable<ContentHash> EnumeratePinnedContentHashes()
{
return PerformAggregateSessionOperationCoreAsync<IHibernateContentSession, Result<IEnumerable<ContentHash>>>(
session =>
{
var hashes = session.EnumeratePinnedContentHashes();
return Task.FromResult(Result.Success(hashes));
},
(r1, r2) => Result.Success(r1.Value.Concat(r2.Value)),
shouldBreak: r => false).GetAwaiter().GetResult().Value;
}
/// <inheritdoc />
public Task PinBulkAsync(Context context, IEnumerable<ContentHash> contentHashes)
{
return PerformAggregateSessionOperationAsync<IHibernateContentSession, BoolResult>(
context,
async session =>
{
await session.PinBulkAsync(context, contentHashes);
return BoolResult.Success;
},
(r1, r2) => r1 & r2,
shouldBreak: r => false);
}
/// <inheritdoc />
public Task<BoolResult> ShutdownEvictionAsync(Context context)
{
return PerformAggregateSessionOperationAsync<IHibernateContentSession, BoolResult>(
context,
session => session.ShutdownEvictionAsync(context),
(r1, r2) => r1 & r2,
shouldBreak: r => false);
}
private IEnumerable<TSession> GetSessionsInOrder<TSession>(AbsolutePath path = null)
{
var drive = path != null ? path.GetPathRoot() : Store.PreferredCacheDrive;
if (!SessionsByCacheRoot.ContainsKey(drive))
{
drive = Store.PreferredCacheDrive;
}
if (SessionsByCacheRoot[drive] is TSession session)
{
yield return session;
}
foreach (var kvp in SessionsByCacheRoot)
{
if (StringComparer.OrdinalIgnoreCase.Equals((string)kvp.Key, drive))
{
// Already yielded the preferred cache
continue;
}
if (kvp.Value is TSession otherSession)
{
yield return otherSession;
}
}
}
private async Task<TResult> PerformSessionOperationAsync<TSession, TResult>(Func<TSession, Task<TResult>> executeAsync)
where TResult : ResultBase
{
TResult result = null;
foreach (var session in GetSessionsInOrder<TSession>())
{
result = await executeAsync(session);
if (result.Succeeded)
{
return result;
}
}
return result ?? new ErrorResult(
$"Could not find a content session which implements {typeof(TSession).Name} in {nameof(MultiplexedContentSession)}.")
.AsResult<TResult>();
}
private Task<TResult> PerformAggregateSessionOperationAsync<TSession, TResult>(
Context context,
Func<TSession, Task<TResult>> executeAsync,
Func<TResult, TResult, TResult> aggregate,
Func<TResult, bool> shouldBreak,
AbsolutePath pathHint = null,
[CallerMemberName] string caller = null)
where TResult : ResultBase
{
var operationContext = context is null ? new OperationContext() : new OperationContext(context);
return operationContext.PerformOperationAsync(
Tracer,
() => PerformAggregateSessionOperationCoreAsync(executeAsync, aggregate, shouldBreak, pathHint),
traceOperationStarted: false,
traceOperationFinished: false,
caller: caller);
}
private async Task<TResult> PerformAggregateSessionOperationCoreAsync<TSession, TResult>(
Func<TSession, Task<TResult>> executeAsync,
Func<TResult, TResult, TResult> aggregate,
Func<TResult, bool> shouldBreak,
AbsolutePath pathHint = null)
where TResult : ResultBase
{
TResult result = null;
// Go through all the sessions
foreach (var session in GetSessionsInOrder<TSession>(pathHint))
{
var priorResult = result;
try
{
result = await executeAsync(session);
}
catch (Exception e)
{
result = new ErrorResult(e).AsResult<TResult>();
}
// Aggregate with previous result
if (priorResult != null)
{
result = aggregate(priorResult, result);
}
// If result is sufficient, stop trying other stores and return result
if (shouldBreak(result))
{
return result;
}
}
Contract.Assert(
result != null,
$"Could not find a content session which implements {typeof(TSession).Name} in {nameof(MultiplexedContentSession)}.");
return result;
}
/// <summary>
/// Initializes a new instance of the <see cref="MultiplexedContentSession"/> class.
/// </summary>
public MultiplexedContentSession(Dictionary<string, IReadOnlyContentSession> cacheSessionsByRoot, string name, MultiplexedContentStore store)
: base(cacheSessionsByRoot, name, store)
public MultiplexedContentSession(Dictionary<string, IContentSession> cacheSessionsByRoot, string name, MultiplexedContentStore store)
{
Contract.Requires(name != null);
Contract.Requires(cacheSessionsByRoot != null);
Contract.Requires(cacheSessionsByRoot.Count > 0);
Name = name;
SessionsByCacheRoot = cacheSessionsByRoot;
Store = store;
if (!SessionsByCacheRoot.TryGetValue(store.PreferredCacheDrive, out PreferredContentSession))
{
throw new ArgumentException(nameof(store.PreferredCacheDrive));
}
}
/// <inheritdoc />
@ -76,7 +466,13 @@ namespace BuildXL.Cache.Host.Service.Internal
}
/// <inheritdoc />
public Task<PutResult> PutTrustedFileAsync(Context context, ContentHashWithSize contentHashWithSize, AbsolutePath path, FileRealizationMode realizationMode, CancellationToken cts, UrgencyHint urgencyHint)
public Task<PutResult> PutTrustedFileAsync(
Context context,
ContentHashWithSize contentHashWithSize,
AbsolutePath path,
FileRealizationMode realizationMode,
CancellationToken cts,
UrgencyHint urgencyHint)
{
var session = GetCache<ITrustedContentSession>(path);
return session.PutTrustedFileAsync(context, contentHashWithSize, path, realizationMode, cts, urgencyHint);

Просмотреть файл

@ -106,38 +106,12 @@ namespace BuildXL.Cache.Host.Service.Internal
}
}
/// <nodoc />
public CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
return CreateReadOnlySessionCall.Run(StoreTracer, new OperationContext(context), name, () =>
{
var sessions = new Dictionary<string, IReadOnlyContentSession>(StringComparer.OrdinalIgnoreCase);
foreach (KeyValuePair<string, IContentStore> entry in DrivesWithContentStore)
{
var result = entry.Value.CreateReadOnlySession(context, name, implicitPin);
if (!result.Succeeded)
{
foreach (var session in sessions.Values)
{
session.Dispose();
}
return new CreateSessionResult<IReadOnlyContentSession>(result);
}
sessions.Add(entry.Key, result.Session);
}
var multiCacheSession = new MultiplexedReadOnlyContentSession(sessions, name, this);
return new CreateSessionResult<IReadOnlyContentSession>(multiCacheSession);
});
}
/// <nodoc />
public CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{
return CreateSessionCall.Run(StoreTracer, new OperationContext(context), name, () =>
{
var sessions = new Dictionary<string, IReadOnlyContentSession>(StringComparer.OrdinalIgnoreCase);
var sessions = new Dictionary<string, IContentSession>(StringComparer.OrdinalIgnoreCase);
foreach (KeyValuePair<string, IContentStore> entry in DrivesWithContentStore)
{
var result = entry.Value.CreateSession(context, name, implicitPin);

Просмотреть файл

@ -1,397 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Diagnostics.ContractsLight;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Distributed.Utilities;
using BuildXL.Cache.ContentStore.Hashing;
using BuildXL.Cache.ContentStore.Interfaces.FileSystem;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
using BuildXL.Cache.ContentStore.Tracing;
using BuildXL.Cache.ContentStore.Tracing.Internal;
using BuildXL.Cache.ContentStore.Utils;
namespace BuildXL.Cache.Host.Service.Internal
{
public class MultiplexedReadOnlyContentSession : StartupShutdownBase, IReadOnlyContentSession, IHibernateContentSession
{
/// <nodoc />
public readonly IReadOnlyContentSession PreferredContentSession;
/// <nodoc />
public readonly IDictionary<string, IReadOnlyContentSession> SessionsByCacheRoot;
protected readonly MultiplexedContentStore Store;
/// <inheritdoc />
protected override Tracer Tracer { get; } = new Tracer(nameof(MultiplexedContentSession));
/// <inheritdoc />
public string Name { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MultiplexedReadOnlyContentSession"/> class.
/// </summary>
public MultiplexedReadOnlyContentSession(
Dictionary<string, IReadOnlyContentSession> sessionsByCacheRoot,
string name,
MultiplexedContentStore store)
{
Contract.Requires(name != null);
Contract.Requires(sessionsByCacheRoot != null);
Contract.Requires(sessionsByCacheRoot.Count > 0);
Name = name;
SessionsByCacheRoot = sessionsByCacheRoot;
Store = store;
if (!SessionsByCacheRoot.TryGetValue(store.PreferredCacheDrive, out PreferredContentSession))
{
throw new ArgumentException(nameof(store.PreferredCacheDrive));
}
}
/// <inheritdoc />
protected override async Task<BoolResult> StartupCoreAsync(OperationContext context)
{
var finalResult = BoolResult.Success;
var sessions = SessionsByCacheRoot.Values.ToArray();
for (var i = 0; i < sessions.Length; i++)
{
var canHibernate = sessions[i] is IHibernateContentSession ? "can" : "cannot";
Tracer.Debug(context, $"Session {sessions[i].Name} {canHibernate} hibernate");
var startupResult = await sessions[i].StartupAsync(context).ConfigureAwait(false);
if (!startupResult.Succeeded)
{
finalResult = startupResult;
for (var j = 0; j < i; j++)
{
var shutdownResult = await sessions[j].ShutdownAsync(context).ConfigureAwait(false);
if (!shutdownResult.Succeeded)
{
finalResult = new BoolResult(finalResult, shutdownResult.ErrorMessage);
}
}
}
}
return finalResult;
}
/// <inheritdoc />
protected override async Task<BoolResult> ShutdownCoreAsync(OperationContext context)
{
var finalResult = BoolResult.Success;
foreach (var session in SessionsByCacheRoot.Values)
{
var result = await session.ShutdownAsync(context).ConfigureAwait(false);
if (!result.Succeeded)
{
finalResult = new BoolResult(finalResult, result.ErrorMessage);
}
}
return finalResult;
}
/// <inheritdoc />
protected override void DisposeCore()
{
foreach (var session in SessionsByCacheRoot.Values)
{
session.Dispose();
}
}
/// <inheritdoc />
public Task<PinResult> PinAsync(
Context context,
ContentHash contentHash,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return PerformAggregateSessionOperationAsync<IReadOnlyContentSession, PinResult>(
context,
session => session.PinAsync(context, contentHash, cts, urgencyHint),
(r1, r2) => r1.Succeeded ? r1 : r2,
shouldBreak: r => r.Succeeded);
}
/// <inheritdoc />
public Task<OpenStreamResult> OpenStreamAsync(
Context context,
ContentHash contentHash,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return PerformAggregateSessionOperationAsync<IReadOnlyContentSession, OpenStreamResult>(
context,
session => session.OpenStreamAsync(context, contentHash, cts, urgencyHint),
(r1, r2) => r1.Succeeded ? r1 : r2,
shouldBreak: r => r.Succeeded);
}
/// <inheritdoc />
public Task<PlaceFileResult> PlaceFileAsync(
Context context,
ContentHash contentHash,
AbsolutePath path,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
IContentSession hardlinkSession = null;
if (realizationMode == FileRealizationMode.HardLink)
{
var drive = path.GetPathRoot();
if (SessionsByCacheRoot.TryGetValue(drive, out var session) && session is IContentSession writeableSession)
{
hardlinkSession = writeableSession;
}
else
{
return Task.FromResult(new PlaceFileResult("Requested hardlink but there is no session on the same drive as destination path."));
}
}
return PerformAggregateSessionOperationAsync<IReadOnlyContentSession, PlaceFileResult>(
context,
executeAsync: placeFileCore,
(r1, r2) => r1.Succeeded ? r1 : r2,
shouldBreak: r => r.Succeeded,
pathHint: path);
async Task<PlaceFileResult> placeFileCore(IReadOnlyContentSession session)
{
// If we exclusively want a hardlink, we should make sure that we can copy from other drives to satisfy the request.
if (realizationMode != FileRealizationMode.HardLink || session == hardlinkSession)
{
return await session.PlaceFileAsync(context, contentHash, path, accessMode, replacementMode, realizationMode, cts, urgencyHint);
}
// See if session has the content.
var streamResult = await session.OpenStreamAsync(context, contentHash, cts, urgencyHint).ThrowIfFailure();
// Put it into correct store
var putResult = await hardlinkSession.PutStreamAsync(context, contentHash, streamResult.Stream, cts, urgencyHint).ThrowIfFailure();
// Try the hardlink on the correct drive.
return await hardlinkSession.PlaceFileAsync(context, contentHash, path, accessMode, replacementMode, realizationMode, cts, urgencyHint);
}
}
/// <inheritdoc />
public Task<IEnumerable<Task<Indexed<PinResult>>>> PinAsync(
Context context,
IReadOnlyList<ContentHash> contentHashes,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return MultiLevelUtilities.RunManyLevelAsync(
GetSessionsInOrder<IReadOnlyContentSession>().ToArray(),
contentHashes,
(session, hashes) => session.PinAsync(context, hashes, cts, urgencyHint),
p => p.Succeeded);
}
public Task<IEnumerable<Task<Indexed<PinResult>>>> PinAsync(
Context context,
IReadOnlyList<ContentHash> contentHashes,
PinOperationConfiguration config)
{
return MultiLevelUtilities.RunManyLevelAsync(
GetSessionsInOrder<IReadOnlyContentSession>().ToArray(),
contentHashes,
(session, hashes) => session.PinAsync(context, hashes, config),
p => p.Succeeded);
}
protected TCache GetCache<TCache>(AbsolutePath path = null)
{
if (path != null)
{
var drive = path.GetPathRoot();
if (SessionsByCacheRoot.TryGetValue(drive, out var contentSession))
{
return (TCache)contentSession;
}
}
return (TCache)PreferredContentSession;
}
public Task<IEnumerable<Task<Indexed<PlaceFileResult>>>> PlaceFileAsync(
Context context,
IReadOnlyList<ContentHashWithPath> hashesWithPaths,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
// NOTE: this goes around the FileSystemContentStore's bulk place, rendering it useless. The reason
// we do it this way is so that the hardlink logic stays consistent. This means that multiple place
// operations from the same request may run at a higher rate than estipulated by the store.
IEnumerable<Task<Indexed<PlaceFileResult>>> materializations = hashesWithPaths.Select(async (hashWithPath, index) =>
{
var result = await PlaceFileAsync(context, hashWithPath.Hash, hashWithPath.Path, accessMode, replacementMode, realizationMode, cts, urgencyHint);
return new Indexed<PlaceFileResult>(result, index);
});
return Task.FromResult(materializations.ToList().AsEnumerable());
}
/// <inheritdoc />
public IEnumerable<ContentHash> EnumeratePinnedContentHashes()
{
return PerformAggregateSessionOperationCoreAsync<IHibernateContentSession, Result<IEnumerable<ContentHash>>>(
session =>
{
var hashes = session.EnumeratePinnedContentHashes();
return Task.FromResult(Result.Success(hashes));
},
(r1, r2) => Result.Success(r1.Value.Concat(r2.Value)),
shouldBreak: r => false).GetAwaiter().GetResult().Value;
}
/// <inheritdoc />
public Task PinBulkAsync(Context context, IEnumerable<ContentHash> contentHashes)
{
return PerformAggregateSessionOperationAsync<IHibernateContentSession, BoolResult>(
context,
async session =>
{
await session.PinBulkAsync(context, contentHashes);
return BoolResult.Success;
},
(r1, r2) => r1 & r2,
shouldBreak: r => false);
}
/// <inheritdoc />
public Task<BoolResult> ShutdownEvictionAsync(Context context)
{
return PerformAggregateSessionOperationAsync<IHibernateContentSession, BoolResult>(
context,
session => session.ShutdownEvictionAsync(context),
(r1, r2) => r1 & r2,
shouldBreak: r => false);
}
private IEnumerable<TSession> GetSessionsInOrder<TSession>(AbsolutePath path = null)
{
var drive = path != null ? path.GetPathRoot() : Store.PreferredCacheDrive;
if (!SessionsByCacheRoot.ContainsKey(drive))
{
drive = Store.PreferredCacheDrive;
}
if (SessionsByCacheRoot[drive] is TSession session)
{
yield return session;
}
foreach (var kvp in SessionsByCacheRoot)
{
if (StringComparer.OrdinalIgnoreCase.Equals(kvp.Key, drive))
{
// Already yielded the preferred cache
continue;
}
if (kvp.Value is TSession otherSession)
{
yield return otherSession;
}
}
}
private async Task<TResult> PerformSessionOperationAsync<TSession, TResult>(Func<TSession, Task<TResult>> executeAsync)
where TResult : ResultBase
{
TResult result = null;
foreach (var session in GetSessionsInOrder<TSession>())
{
result = await executeAsync(session);
if (result.Succeeded)
{
return result;
}
}
return result ?? new ErrorResult($"Could not find a content session which implements {typeof(TSession).Name} in {nameof(MultiplexedContentSession)}.").AsResult<TResult>();
}
private Task<TResult> PerformAggregateSessionOperationAsync<TSession, TResult>(
Context context,
Func<TSession, Task<TResult>> executeAsync,
Func<TResult, TResult, TResult> aggregate,
Func<TResult, bool> shouldBreak,
AbsolutePath pathHint = null,
[CallerMemberName] string caller = null)
where TResult : ResultBase
{
var operationContext = context is null ? new OperationContext() : new OperationContext(context);
return operationContext.PerformOperationAsync(
Tracer,
() => PerformAggregateSessionOperationCoreAsync(executeAsync, aggregate, shouldBreak, pathHint),
traceOperationStarted: false,
traceOperationFinished: false,
caller: caller);
}
private async Task<TResult> PerformAggregateSessionOperationCoreAsync<TSession, TResult>(
Func<TSession, Task<TResult>> executeAsync,
Func<TResult, TResult, TResult> aggregate,
Func<TResult, bool> shouldBreak,
AbsolutePath pathHint = null)
where TResult : ResultBase
{
TResult result = null;
// Go through all the sessions
foreach (var session in GetSessionsInOrder<TSession>(pathHint))
{
var priorResult = result;
try
{
result = await executeAsync(session);
}
catch (Exception e)
{
result = new ErrorResult(e).AsResult<TResult>();
}
// Aggregate with previous result
if (priorResult != null)
{
result = aggregate(priorResult, result);
}
// If result is sufficient, stop trying other stores and return result
if (shouldBreak(result))
{
return result;
}
}
Contract.Assert(result != null, $"Could not find a content session which implements {typeof(TSession).Name} in {nameof(MultiplexedContentSession)}.");
return result;
}
}
}

Просмотреть файл

@ -25,7 +25,7 @@ using BuildXL.Utilities.Core.Tasks;
namespace BuildXL.Cache.MemoizationStore.Distributed.Sessions
{
internal class PublishingCacheSession : StartupShutdownBase, ICacheSession, IReadOnlyCacheSessionWithLevelSelectors, IHibernateCacheSession, IConfigurablePin, IAsyncShutdown
internal class PublishingCacheSession : StartupShutdownBase, ICacheSession, ICacheSessionWithLevelSelectors, IHibernateCacheSession, IConfigurablePin, IAsyncShutdown
{
public string Name { get; }
protected override Tracer Tracer { get; } = new Tracer(nameof(PublishingCacheSession));
@ -228,7 +228,7 @@ namespace BuildXL.Cache.MemoizationStore.Distributed.Sessions
/// <inheritdoc />
public Task<IEnumerable<Task<Indexed<PinResult>>>> PinAsync(Context context, IReadOnlyList<ContentHash> contentHashes, CancellationToken cts, UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return ((IReadOnlyContentSession)_local).PinAsync(context, contentHashes, cts, urgencyHint);
return ((IContentSession)_local).PinAsync(context, contentHashes, cts, urgencyHint);
}
/// <inheritdoc />
@ -331,17 +331,17 @@ namespace BuildXL.Cache.MemoizationStore.Distributed.Sessions
#endregion
#region IReadOnlyCacheSessionWithLevelSelectors
#region ICacheSessionWithLevelSelectors
/// <inheritdoc />
public Task<Result<LevelSelectors>> GetLevelSelectorsAsync(Context context, Fingerprint weakFingerprint, CancellationToken cts, int level)
{
if (_local is IReadOnlyCacheSessionWithLevelSelectors withSelectors)
if (_local is ICacheSessionWithLevelSelectors withSelectors)
{
return withSelectors.GetLevelSelectorsAsync(context, weakFingerprint, cts, level);
}
return Task.FromResult(new Result<LevelSelectors>($"{nameof(_local)} does not implement {nameof(IReadOnlyCacheSessionWithLevelSelectors)}."));
return Task.FromResult(new Result<LevelSelectors>($"{nameof(_local)} does not implement {nameof(ICacheSessionWithLevelSelectors)}."));
}
#endregion

Просмотреть файл

@ -87,12 +87,6 @@ namespace BuildXL.Cache.MemoizationStore.Distributed.Stores
_local.Dispose();
}
/// <inheritdoc />
public virtual CreateSessionResult<IReadOnlyCacheSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
return ((ICache)_local).CreateReadOnlySession(context, name, implicitPin);
}
/// <inheritdoc />
public virtual CreateSessionResult<ICacheSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{
@ -149,12 +143,6 @@ namespace BuildXL.Cache.MemoizationStore.Distributed.Stores
return ((ICache)_local).GetStatsAsync(context);
}
/// <inheritdoc />
CreateSessionResult<IReadOnlyContentSession> IContentStore.CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
return ((IContentStore)_local).CreateReadOnlySession(context, name, implicitPin);
}
/// <inheritdoc />
CreateSessionResult<IContentSession> IContentStore.CreateSession(Context context, string name, ImplicitPin implicitPin)
{

Просмотреть файл

@ -3,7 +3,6 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Hashing;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Stores;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
@ -11,7 +10,6 @@ using BuildXL.Cache.ContentStore.Tracing;
using BuildXL.Cache.MemoizationStore.Interfaces.Caches;
using BuildXL.Cache.MemoizationStore.Interfaces.Sessions;
using BuildXL.Cache.MemoizationStore.Tracing;
using CreateReadOnlySessionCall = BuildXL.Cache.MemoizationStore.Tracing.CreateReadOnlySessionCall;
using CreateSessionCall = BuildXL.Cache.MemoizationStore.Tracing.CreateSessionCall;
namespace BuildXL.Cache.MemoizationStore.Distributed.Stores
@ -148,12 +146,12 @@ namespace BuildXL.Cache.MemoizationStore.Distributed.Stores
public bool ShutdownStarted { get; private set; }
/// <inheritdoc />
public CreateSessionResult<IReadOnlyCacheSession> CreateReadOnlySession(
public CreateSessionResult<ICacheSession> CreateSession(
Context context,
string name,
ImplicitPin implicitPin)
{
return CreateReadOnlySessionCall.Run(
return CreateSessionCall.Run(
_tracer,
context,
name,
@ -162,34 +160,24 @@ namespace BuildXL.Cache.MemoizationStore.Distributed.Stores
CreateSessionResult<ICacheSession> localSession = _localCache.CreateSession(context, name, implicitPin);
if (!localSession.Succeeded)
{
return new CreateSessionResult<IReadOnlyCacheSession>(localSession);
return new CreateSessionResult<ICacheSession>(localSession);
}
CreateSessionResult<ICacheSession> remoteSession = _remoteCache.CreateSession(context, name, implicitPin);
if (!remoteSession.Succeeded)
{
return new CreateSessionResult<IReadOnlyCacheSession>(remoteSession);
return new CreateSessionResult<ICacheSession>(remoteSession);
}
IReadOnlyCacheSession cacheSession = new TwoLevelCacheSession(
ICacheSession cacheSession = new TwoLevelCacheSession(
name,
localSession.Session,
remoteSession.Session,
_config);
return new CreateSessionResult<IReadOnlyCacheSession>(cacheSession);
return new CreateSessionResult<ICacheSession>(cacheSession);
});
}
/// <inheritdoc />
public CreateSessionResult<ICacheSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{
return CreateSessionCall.Run(
_tracer,
context,
name,
() => CreateSessionAsync(context, name, implicitPin).GetAwaiter().GetResult());
}
private async Task<CreateSessionResult<ICacheSession>> CreateSessionAsync(Context context, string name, ImplicitPin implicitPin)
{
(CreateSessionResult<ICacheSession> localResult, CreateSessionResult<ICacheSession> remoteResult) = await MultiLevel(cache => cache.CreateSession(context, name, implicitPin));

Просмотреть файл

@ -203,9 +203,6 @@ namespace BuildXL.Cache.MemoizationStore.Distributed.Test
protected override Tracer Tracer { get; } = new Tracer(nameof(PublishingCacheToContentStore));
public CreateSessionResult<ICacheSession> CreatePublishingSession(Context context, string name, ImplicitPin implicitPin, PublishingCacheConfiguration publishingConfig, string pat) => _inner.CreatePublishingSession(context, name, implicitPin, publishingConfig, pat);
public CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
=> new CreateSessionResult<IReadOnlyContentSession>(_inner.CreateReadOnlySession(context, name, implicitPin).ShouldBeSuccess().Session);
public CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
=> new CreateSessionResult<IContentSession>(_inner.CreateSession(context, name, implicitPin).ShouldBeSuccess().Session);
@ -219,7 +216,6 @@ namespace BuildXL.Cache.MemoizationStore.Distributed.Test
public IAsyncEnumerable<StructResult<StrongFingerprint>> EnumerateStrongFingerprints(Context context) => _inner.EnumerateStrongFingerprints(context);
public Task<GetStatsResult> GetStatsAsync(Context context) => _inner.GetStatsAsync(context);
public void PostInitializationCompleted(Context context, BoolResult result) { }
CreateSessionResult<IReadOnlyCacheSession> ICache.CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin) => _inner.CreateReadOnlySession(context, name, implicitPin);
CreateSessionResult<ICacheSession> ICache.CreateSession(Context context, string name, ImplicitPin implicitPin) => _inner.CreateSession(context, name, implicitPin);
}

Просмотреть файл

@ -145,7 +145,7 @@ namespace BuildXL.Cache.MemoizationStore.Test.Sessions
}
/// <inheritdoc />
public override CreateSessionResult<IReadOnlyCacheSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
public override CreateSessionResult<ICacheSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{
var sessionResult = CreatePublishingSession(
context,
@ -156,15 +156,11 @@ namespace BuildXL.Cache.MemoizationStore.Test.Sessions
if (!sessionResult.Succeeded)
{
return new CreateSessionResult<IReadOnlyCacheSession>(sessionResult);
return new CreateSessionResult<ICacheSession>(sessionResult);
}
return new CreateSessionResult<IReadOnlyCacheSession>(sessionResult.Session);
return new CreateSessionResult<ICacheSession>(sessionResult.Session);
}
/// <inheritdoc />
public override CreateSessionResult<ICacheSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
=> base.CreatePublishingSession(context, name, implicitPin, _configFactory(), pat: _pat);
}
internal class BlockingPublishingStore : StartupShutdownSlimBase, IPublishingStore
@ -226,9 +222,6 @@ namespace BuildXL.Cache.MemoizationStore.Test.Sessions
protected override Tracer Tracer { get; } = new Tracer(nameof(CacheToContentStore));
public CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
=> new CreateSessionResult<IReadOnlyContentSession>(_inner.CreateReadOnlySession(context, name, implicitPin).ShouldBeSuccess().Session);
public CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
=> new CreateSessionResult<IContentSession>(_inner.CreateSession(context, name, implicitPin).ShouldBeSuccess().Session);
@ -239,7 +232,6 @@ namespace BuildXL.Cache.MemoizationStore.Test.Sessions
public IAsyncEnumerable<StructResult<StrongFingerprint>> EnumerateStrongFingerprints(Context context) => _inner.EnumerateStrongFingerprints(context);
public Task<GetStatsResult> GetStatsAsync(Context context) => _inner.GetStatsAsync(context);
public void PostInitializationCompleted(Context context, BoolResult result) { }
CreateSessionResult<IReadOnlyCacheSession> ICache.CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin) => _inner.CreateReadOnlySession(context, name, implicitPin);
CreateSessionResult<ICacheSession> ICache.CreateSession(Context context, string name, ImplicitPin implicitPin) => _inner.CreateSession(context, name, implicitPin);
}
}

Просмотреть файл

@ -25,11 +25,6 @@ namespace BuildXL.Cache.MemoizationStore.Interfaces.Caches
/// </remarks>
Guid Id { get; }
/// <summary>
/// Create a new session that can only read.
/// </summary>
CreateSessionResult<IReadOnlyCacheSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin);
/// <summary>
/// Create a new session that can change the cache.
/// </summary>

Просмотреть файл

@ -5,10 +5,13 @@ using BuildXL.Cache.ContentStore.Interfaces.Sessions;
namespace BuildXL.Cache.MemoizationStore.Interfaces.Sessions
{
/// <summary>
/// Writable cache session.
/// </summary>
public interface ICacheSession : IReadOnlyCacheSession, IMemoizationSession, IContentSession
/// <nodoc />
public interface ICacheSession : IMemoizationSession, IContentSession, IConfigurablePin
{
}
/// <nodoc />
public interface ICacheSessionWithLevelSelectors : ICacheSession, IMemoizationSessionWithLevelSelectors
{
}
}

Просмотреть файл

@ -6,6 +6,7 @@ using System.Threading;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
using BuildXL.Cache.ContentStore.Interfaces.Stores;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
using BuildXL.Cache.MemoizationStore.Interfaces.Results;
@ -14,8 +15,27 @@ namespace BuildXL.Cache.MemoizationStore.Interfaces.Sessions
/// <summary>
/// A related set of accesses to a cache.
/// </summary>
public interface IMemoizationSession : IReadOnlyMemoizationSession
public interface IMemoizationSession : IName, IStartupShutdown
{
/// <summary>
/// Gets known selectors for a given weak fingerprint.
/// </summary>
System.Collections.Generic.IAsyncEnumerable<GetSelectorResult> GetSelectors(
Context context,
Fingerprint weakFingerprint,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal
);
/// <summary>
/// Load a ContentHashList.
/// </summary>
Task<GetContentHashListResult> GetContentHashListAsync(
Context context,
StrongFingerprint strongFingerprint,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal);
/// <summary>
/// Store a ContentHashList
/// </summary>

Просмотреть файл

@ -1,21 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
namespace BuildXL.Cache.MemoizationStore.Interfaces.Sessions
{
/// <summary>
/// Read-only cache session.
/// </summary>
public interface IReadOnlyCacheSession : IReadOnlyMemoizationSession, IReadOnlyContentSession, IConfigurablePin
{
}
/// <summary>
/// Read-only cache session.
/// </summary>
public interface IReadOnlyCacheSessionWithLevelSelectors : IReadOnlyCacheSession, IReadOnlyMemoizationSessionWithLevelSelectors
{
}
}

Просмотреть файл

@ -11,35 +11,10 @@ using BuildXL.Cache.MemoizationStore.Interfaces.Results;
namespace BuildXL.Cache.MemoizationStore.Interfaces.Sessions
{
/// <summary>
/// A related set of read accesses to a cache.
/// </summary>
public interface IReadOnlyMemoizationSession : IName, IStartupShutdown
{
/// <summary>
/// Gets known selectors for a given weak fingerprint.
/// </summary>
System.Collections.Generic.IAsyncEnumerable<GetSelectorResult> GetSelectors(
Context context,
Fingerprint weakFingerprint,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal
);
/// <summary>
/// Load a ContentHashList.
/// </summary>
Task<GetContentHashListResult> GetContentHashListAsync(
Context context,
StrongFingerprint strongFingerprint,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal);
}
/// <summary>
/// A related set of read accesses to a cache with support for multi-level GetSelectors.
/// </summary>
public interface IReadOnlyMemoizationSessionWithLevelSelectors : IReadOnlyMemoizationSession, ILevelSelectorsProvider
public interface IMemoizationSessionWithLevelSelectors : IMemoizationSession, ILevelSelectorsProvider
{
}
@ -52,7 +27,7 @@ namespace BuildXL.Cache.MemoizationStore.Interfaces.Sessions
/// Gets known selectors for a given weak fingerprint for a given "level".
/// </summary>
/// <remarks>
/// Unlike <see cref="IReadOnlyMemoizationSession.GetSelectors"/>, this method is RPC friendly.
/// Unlike <see cref="IMemoizationSession.GetSelectors"/>, this method is RPC friendly.
/// </remarks>
Task<Result<LevelSelectors>> GetLevelSelectorsAsync(
Context context,

Просмотреть файл

@ -15,7 +15,7 @@ using BuildXL.Cache.MemoizationStore.Interfaces.Results;
namespace BuildXL.Cache.MemoizationStore.Interfaces.Sessions
{
/// <summary>
/// Set of extension methods for <see cref="IReadOnlyMemoizationSession"/> interface.
/// Set of extension methods for <see cref="IMemoizationSession"/> interface.
/// </summary>
public static class ReadOnlyMemoizationSessionExtensions
{

Просмотреть файл

@ -16,11 +16,6 @@ namespace BuildXL.Cache.MemoizationStore.Interfaces.Stores
/// </summary>
public interface IMemoizationStore : IStartupShutdown
{
/// <summary>
/// Create a new session that can only read.
/// </summary>
CreateSessionResult<IReadOnlyMemoizationSession> CreateReadOnlySession(Context context, string name);
/// <summary>
/// Create a new session that can add as well as read.
/// </summary>

Просмотреть файл

@ -9,28 +9,28 @@ using Xunit;
namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Results
{
public class CreateSessionResultTests : ResultTests<CreateSessionResult<IReadOnlyCacheSession>>
public class CreateSessionResultTests : ResultTests<CreateSessionResult<ICacheSession>>
{
protected override CreateSessionResult<IReadOnlyCacheSession> CreateFrom(Exception exception)
protected override CreateSessionResult<ICacheSession> CreateFrom(Exception exception)
{
return new CreateSessionResult<IReadOnlyCacheSession>(exception);
return new CreateSessionResult<ICacheSession>(exception);
}
protected override CreateSessionResult<IReadOnlyCacheSession> CreateFrom(string errorMessage)
protected override CreateSessionResult<ICacheSession> CreateFrom(string errorMessage)
{
return new CreateSessionResult<IReadOnlyCacheSession>(errorMessage);
return new CreateSessionResult<ICacheSession>(errorMessage);
}
protected override CreateSessionResult<IReadOnlyCacheSession> CreateFrom(string errorMessage, string diagnostics)
protected override CreateSessionResult<ICacheSession> CreateFrom(string errorMessage, string diagnostics)
{
return new CreateSessionResult<IReadOnlyCacheSession>(errorMessage, diagnostics);
return new CreateSessionResult<ICacheSession>(errorMessage, diagnostics);
}
[Fact]
public void ConstructFromResultBase()
{
var other = new BoolResult("error");
Assert.False(new CreateSessionResult<IReadOnlyCacheSession>(other, "message").Succeeded);
Assert.False(new CreateSessionResult<ICacheSession>(other, "message").Succeeded);
}
[Fact]
@ -56,14 +56,14 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Results
{
using (var session = new ThrowingCacheSession())
{
Assert.True(new CreateSessionResult<IReadOnlyCacheSession>(session).Succeeded);
Assert.True(new CreateSessionResult<ICacheSession>(session).Succeeded);
}
}
[Fact]
public void CodePropertyError()
{
Assert.False(new CreateSessionResult<IReadOnlyCacheSession>("error").Succeeded);
Assert.False(new CreateSessionResult<ICacheSession>("error").Succeeded);
}
[Fact]
@ -71,7 +71,7 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Results
{
using (var session = new ThrowingCacheSession())
{
Assert.Equal(session, new CreateSessionResult<IReadOnlyCacheSession>(session).Session);
Assert.Equal(session, new CreateSessionResult<ICacheSession>(session).Session);
}
}
@ -80,8 +80,8 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Results
{
using (var session = new ThrowingCacheSession())
{
var v1 = new CreateSessionResult<IReadOnlyCacheSession>(session);
var v2 = new CreateSessionResult<IReadOnlyCacheSession>(session) as object;
var v1 = new CreateSessionResult<ICacheSession>(session);
var v2 = new CreateSessionResult<ICacheSession>(session) as object;
Assert.True(v1.Equals(v2));
}
}
@ -91,7 +91,7 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Results
{
using (var session = new ThrowingCacheSession())
{
var v1 = new CreateSessionResult<IReadOnlyCacheSession>(session);
var v1 = new CreateSessionResult<ICacheSession>(session);
var v2 = new object();
Assert.False(v1.Equals(v2));
}
@ -102,8 +102,8 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Results
{
using (var session = new ThrowingCacheSession())
{
var v1 = new CreateSessionResult<IReadOnlyCacheSession>(session);
var v2 = new CreateSessionResult<IReadOnlyCacheSession>(session);
var v1 = new CreateSessionResult<ICacheSession>(session);
var v2 = new CreateSessionResult<ICacheSession>(session);
Assert.True(v1.Equals(v2));
}
}
@ -111,8 +111,8 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Results
[Fact]
public void EqualsTrueForInvalidSessions()
{
var v1 = new CreateSessionResult<IReadOnlyCacheSession>("error1");
var v2 = new CreateSessionResult<IReadOnlyCacheSession>("error1");
var v1 = new CreateSessionResult<ICacheSession>("error1");
var v2 = new CreateSessionResult<ICacheSession>("error1");
Assert.True(v1.Equals(v2));
}
@ -122,8 +122,8 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Results
using (var session1 = new ThrowingCacheSession())
using (var session2 = new ThrowingCacheSession())
{
var v1 = new CreateSessionResult<IReadOnlyCacheSession>(session1);
var v2 = new CreateSessionResult<IReadOnlyCacheSession>(session2);
var v1 = new CreateSessionResult<ICacheSession>(session1);
var v2 = new CreateSessionResult<ICacheSession>(session2);
Assert.True(v1.Equals(v2));
}
}
@ -133,8 +133,8 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Results
{
using (var session1 = new ThrowingCacheSession())
{
var v1 = new CreateSessionResult<IReadOnlyCacheSession>(session1);
var v2 = new CreateSessionResult<IReadOnlyCacheSession>("error");
var v1 = new CreateSessionResult<ICacheSession>(session1);
var v2 = new CreateSessionResult<ICacheSession>("error");
Assert.False(v1.Equals(v2));
}
}
@ -145,8 +145,8 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Results
using (var session1 = new ThrowingCacheSession("session1"))
using (var session2 = new ThrowingCacheSession("session2"))
{
var v1 = new CreateSessionResult<IReadOnlyCacheSession>(session1);
var v2 = new CreateSessionResult<IReadOnlyCacheSession>(session2);
var v1 = new CreateSessionResult<ICacheSession>(session1);
var v2 = new CreateSessionResult<ICacheSession>(session2);
Assert.False(v1.Equals(v2));
}
}
@ -157,8 +157,8 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Results
using (var session1 = new ThrowingCacheSession("session1"))
using (var session2 = new ThrowingCacheSession("session1"))
{
var v1 = new CreateSessionResult<IReadOnlyCacheSession>(session1);
var v2 = new CreateSessionResult<IReadOnlyCacheSession>(session2);
var v1 = new CreateSessionResult<ICacheSession>(session1);
var v2 = new CreateSessionResult<ICacheSession>(session2);
Assert.Equal(v1.GetHashCode(), v2.GetHashCode());
}
}
@ -169,8 +169,8 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Results
using (var session1 = new ThrowingCacheSession("session1"))
using (var session2 = new ThrowingCacheSession("session2"))
{
var v1 = new CreateSessionResult<IReadOnlyCacheSession>(session1);
var v2 = new CreateSessionResult<IReadOnlyCacheSession>(session2);
var v1 = new CreateSessionResult<ICacheSession>(session1);
var v2 = new CreateSessionResult<ICacheSession>(session2);
Assert.NotEqual(v1.GetHashCode(), v2.GetHashCode());
}
}
@ -179,7 +179,7 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Results
public void ToStringWithError()
{
Assert.Contains(
"something", new CreateSessionResult<IReadOnlyCacheSession>("something").ToString(), StringComparison.OrdinalIgnoreCase);
"something", new CreateSessionResult<ICacheSession>("something").ToString(), StringComparison.OrdinalIgnoreCase);
}
[Fact]
@ -187,7 +187,7 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Results
{
using (var session = new ThrowingCacheSession())
{
var result = new CreateSessionResult<IReadOnlyCacheSession>(session);
var result = new CreateSessionResult<ICacheSession>(session);
Assert.Contains("Success", result.ToString(), StringComparison.OrdinalIgnoreCase);
}
}

Просмотреть файл

@ -182,22 +182,6 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Sessions
return RunTestAsync(context, (ICache cache) => Task.FromResult(0));
}
[Fact]
public Task CreateReadOnlySession()
{
var context = new Context(Logger);
return RunTestAsync(context, async cache =>
{
var result = cache.CreateReadOnlySession(context, Name, ImplicitPin.None).ShouldBeSuccess();
using (IReadOnlyCacheSession session = result.Session)
{
await session.StartupAsync(context).ShouldBeSuccess();
await session.ShutdownAsync(context).ShouldBeSuccess();
}
});
}
[Fact]
public Task CreateSession()
{
@ -1119,19 +1103,19 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Sessions
return strongFingerprints;
}
private Task RunReadOnlySessionTestAsync(Context context, Func<IReadOnlyCacheSession, Task> funcAsync)
private Task RunReadOnlySessionTestAsync(Context context, Func<ICacheSession, Task> funcAsync)
{
return RunReadOnlySessionTestAsync(context, funcAsync, CreateCache);
}
protected virtual Task RunReadOnlySessionTestAsync(
Context context, Func<IReadOnlyCacheSession, Task> funcAsync, Func<DisposableDirectory, ICache> createCacheFunc)
Context context, Func<ICacheSession, Task> funcAsync, Func<DisposableDirectory, ICache> createCacheFunc)
{
return RunTestAsync(
context,
async cache =>
{
var createSessionResult = cache.CreateReadOnlySession(context, Name, ImplicitPinPolicy).ShouldBeSuccess();
var createSessionResult = cache.CreateSession(context, Name, ImplicitPinPolicy).ShouldBeSuccess();
using (var session = createSessionResult.Session)
{
try

Просмотреть файл

@ -424,11 +424,11 @@ namespace BuildXL.Cache.MemoizationStore.InterfacesTest.Sessions
}
}
private Task RunReadOnlyTestAsync(Context context, Func<IReadOnlyMemoizationSession, Task> funcAsync)
private Task RunReadOnlyTestAsync(Context context, Func<IMemoizationSession, Task> funcAsync)
{
return RunTestAsync(context, async store =>
{
var createResult = store.CreateReadOnlySession(context, Name);
var createResult = store.CreateSession(context, Name);
createResult.ShouldBeSuccess();
using (var session = createResult.Session)
{

Просмотреть файл

@ -126,7 +126,7 @@ namespace BuildXL.Cache.MemoizationStore.Sessions.Grpc
async c =>
{
Fingerprint fingerprint = request.WeakFingerprint.DeserializeFingerprintFromGrpc();
if (c.Session is IReadOnlyMemoizationSessionWithLevelSelectors withSelectors)
if (c.Session is IMemoizationSessionWithLevelSelectors withSelectors)
{
var result = await withSelectors.GetLevelSelectorsAsync(c.Context, fingerprint, c.Context.Token, request.Level).ThrowIfFailure();
var selectors = result.Value!.Selectors.Select(s => s.ToGrpc());

Просмотреть файл

@ -43,19 +43,6 @@ namespace BuildXL.Cache.MemoizationStore.Service
{
}
/// <nodoc />
public new CreateSessionResult<IReadOnlyCacheSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
var operationContext = OperationContext(context);
return operationContext.PerformOperation(
Tracer,
() =>
{
var session = new ServiceClientCacheSession(new OperationContext(context), name, implicitPin, Logger, FileSystem, SessionTracer, Configuration);
return new CreateSessionResult<IReadOnlyCacheSession>(session);
});
}
/// <nodoc />
public new virtual CreateSessionResult<ICacheSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{
@ -76,13 +63,7 @@ namespace BuildXL.Cache.MemoizationStore.Service
}
/// <inheritdoc />
public CreateSessionResult<IReadOnlyMemoizationSession> CreateReadOnlySession(Context context, string name)
{
return CreateReadOnlySession(context, name, ImplicitPin.None).Map(session => (IReadOnlyMemoizationSession)session);
}
/// <inheritdoc />
public CreateSessionResult<IMemoizationSession> CreateSession(Context context, string name)
CreateSessionResult<IMemoizationSession> IMemoizationStore.CreateSession(Context context, string name)
{
return CreateSession(context, name, ImplicitPin.None).Map(session => (IMemoizationSession)session);
}

Просмотреть файл

@ -30,7 +30,7 @@ using BuildXL.Utilities.Tracing;
namespace BuildXL.Cache.MemoizationStore.Service
{
/// <todoc />
public class ServiceClientCacheSession : ServiceClientContentSession, ICacheSession, IReadOnlyMemoizationSessionWithLevelSelectors
public class ServiceClientCacheSession : ServiceClientContentSession, ICacheSession, IMemoizationSessionWithLevelSelectors
{
private CounterCollection<MemoizationStoreCounters> _memoizationCounters { get; } = new CounterCollection<MemoizationStoreCounters>();

Просмотреть файл

@ -60,12 +60,6 @@ namespace BuildXL.Cache.MemoizationStore.Sessions
return _cache.GetStatsAsync(context);
}
/// <inheritdoc />
public CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
return _cache.CreateReadOnlySession(context, name, implicitPin).Map<IReadOnlyContentSession>(s => s);
}
/// <inheritdoc />
public CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{

Просмотреть файл

@ -56,12 +56,6 @@ namespace BuildXL.Cache.MemoizationStore.Sessions
return await base.ShutdownCoreAsync(context);
}
/// <inheritdoc />
public CreateSessionResult<IReadOnlyMemoizationSession> CreateReadOnlySession(Context context, string name)
{
return _cache.CreateReadOnlySession(context, name, ImplicitPin.None).Map<IReadOnlyMemoizationSession>(s => s);
}
/// <inheritdoc />
public CreateSessionResult<IMemoizationSession> CreateSession(Context context, string name)
{

Просмотреть файл

@ -2,22 +2,59 @@
// Licensed under the MIT License.
using System.Collections.Generic;
using System.Diagnostics.ContractsLight;
using System.Threading;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
using BuildXL.Cache.ContentStore.Tracing;
using BuildXL.Cache.ContentStore.Utils;
using BuildXL.Cache.MemoizationStore.Interfaces.Results;
using BuildXL.Cache.MemoizationStore.Interfaces.Sessions;
using BuildXL.Cache.MemoizationStore.Stores;
namespace BuildXL.Cache.MemoizationStore.Sessions
{
/// <summary>
/// An IMemoizationSession implemented using a database
/// </summary>
public class DatabaseMemoizationSession : ReadOnlyDatabaseMemoizationSession, IMemoizationSession
/// <nodoc />
public class DatabaseMemoizationSession : StartupShutdownBase, IMemoizationSession, IMemoizationSessionWithLevelSelectors
{
/// <inheritdoc />
public string Name { get; }
/// <inheritdoc />
protected override Tracer Tracer { get; }
/// <nodoc />
protected readonly DatabaseMemoizationStore MemoizationStore;
/// <inheritdoc />
public Task<GetContentHashListResult> GetContentHashListAsync(
Context context,
StrongFingerprint strongFingerprint,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
bool preferShared = urgencyHint == UrgencyHint.PreferShared;
return MemoizationStore.GetContentHashListAsync(context, strongFingerprint, cts, preferShared);
}
/// <inheritdoc />
public Task<Result<LevelSelectors>> GetLevelSelectorsAsync(Context context, Fingerprint weakFingerprint, CancellationToken cts, int level)
{
return MemoizationStore.GetLevelSelectorsAsync(context, weakFingerprint, cts, level);
}
/// <inheritdoc />
public System.Collections.Generic.IAsyncEnumerable<GetSelectorResult> GetSelectors(
Context context,
Fingerprint weakFingerprint,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return this.GetSelectorsAsAsyncEnumerable(context, weakFingerprint, cts, urgencyHint);
}
private readonly IContentSession _contentSession;
/// <summary>
@ -30,8 +67,13 @@ namespace BuildXL.Cache.MemoizationStore.Sessions
/// overwritten because we're unable to check whether or not content is missing.
/// </remarks>
public DatabaseMemoizationSession(string name, DatabaseMemoizationStore memoizationStore, IContentSession contentSession = null)
: base(name, memoizationStore)
{
Contract.Requires(name != null);
Contract.Requires(memoizationStore != null);
Tracer = new Tracer(nameof(DatabaseMemoizationSession));
Name = name;
MemoizationStore = memoizationStore;
_contentSession = contentSession;
}
@ -44,7 +86,11 @@ namespace BuildXL.Cache.MemoizationStore.Sessions
UrgencyHint urgencyHint)
{
return MemoizationStore.AddOrGetContentHashListAsync(
context, strongFingerprint, contentHashListWithDeterminism, _contentSession, cts);
context,
strongFingerprint,
contentHashListWithDeterminism,
_contentSession,
cts);
}
/// <inheritdoc />

Просмотреть файл

@ -2,21 +2,22 @@
// Licensed under the MIT License.
using System.Collections.Generic;
using System.Diagnostics.ContractsLight;
using System.Threading;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
using BuildXL.Cache.ContentStore.Tracing;
using BuildXL.Cache.ContentStore.Utils;
using BuildXL.Cache.MemoizationStore.Interfaces.Results;
using BuildXL.Cache.MemoizationStore.Interfaces.Sessions;
using BuildXL.Cache.MemoizationStore.Stores;
namespace BuildXL.Cache.MemoizationStore.Sessions
{
/// <summary>
/// An IMemoizationSession implemented in Memory
/// </summary>
public class MemoryMemoizationSession : ReadOnlyMemoryMemoizationSession, IMemoizationSession
/// <nodoc />
public class MemoryMemoizationSession : StartupShutdownBase, IMemoizationSession, IMemoizationSessionWithLevelSelectors
{
private readonly IContentSession _contentSession;
@ -30,8 +31,13 @@ namespace BuildXL.Cache.MemoizationStore.Sessions
/// overwritten because we're unable to check whether or not content is missing.
/// </remarks>
public MemoryMemoizationSession(string name, MemoryMemoizationStore memoizationStore, IContentSession contentSession = null)
: base(name, memoizationStore)
{
Contract.Requires(name != null);
Contract.Requires(memoizationStore != null);
Name = name;
Tracer = new Tracer(nameof(MemoryMemoizationSession));
MemoizationStore = memoizationStore;
_contentSession = contentSession;
}
@ -44,14 +50,56 @@ namespace BuildXL.Cache.MemoizationStore.Sessions
UrgencyHint urgencyHint)
{
return MemoizationStore.AddOrGetContentHashListAsync(
context, strongFingerprint, contentHashListWithDeterminism, _contentSession, cts);
context,
strongFingerprint,
contentHashListWithDeterminism,
_contentSession,
cts);
}
/// <inheritdoc />
public Task<BoolResult> IncorporateStrongFingerprintsAsync(
Context context, IEnumerable<Task<StrongFingerprint>> strongFingerprints, CancellationToken cts, UrgencyHint urgencyHint)
Context context,
IEnumerable<Task<StrongFingerprint>> strongFingerprints,
CancellationToken cts,
UrgencyHint urgencyHint)
{
return Task.FromResult(BoolResult.Success);
}
/// <nodoc />
protected readonly MemoryMemoizationStore MemoizationStore;
/// <inheritdoc />
protected override Tracer Tracer { get; }
/// <inheritdoc />
public string Name { get; }
/// <inheritdoc />
public System.Collections.Generic.IAsyncEnumerable<GetSelectorResult> GetSelectors(
Context context,
Fingerprint weakFingerprint,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return this.GetSelectorsAsAsyncEnumerable(context, weakFingerprint, cts, urgencyHint);
}
/// <inheritdoc />
public Task<Result<LevelSelectors>> GetLevelSelectorsAsync(Context context, Fingerprint weakFingerprint, CancellationToken cts, int level)
{
return Task.FromResult(LevelSelectors.Single<Selector[]>(MemoizationStore.GetSelectorsCore(context, weakFingerprint, cts)));
}
/// <inheritdoc />
public Task<GetContentHashListResult> GetContentHashListAsync(
Context context,
StrongFingerprint strongFingerprint,
CancellationToken cts,
UrgencyHint urgencyHint)
{
return MemoizationStore.GetContentHashListAsync(context, strongFingerprint, cts);
}
}
}

Просмотреть файл

@ -185,35 +185,6 @@ namespace BuildXL.Cache.MemoizationStore.Sessions
}
}
/// <inheritdoc />
public CreateSessionResult<IReadOnlyCacheSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
Contract.Requires(ContentStore != null);
Contract.Requires(MemoizationStore != null);
return Tracing.CreateReadOnlySessionCall.Run(CacheTracer, context, name, () =>
{
var createContentResult = ContentStore.CreateReadOnlySession(context, name, implicitPin);
if (!createContentResult.Succeeded)
{
return new CreateSessionResult<IReadOnlyCacheSession>(createContentResult, "Content session creation failed");
}
var contentReadOnlySession = createContentResult.Session;
var createMemoizationResult = MemoizationStore.CreateReadOnlySession(context, name);
if (!createMemoizationResult.Succeeded)
{
return new CreateSessionResult<IReadOnlyCacheSession>(createMemoizationResult, "Memoization session creation failed");
}
var memoizationReadOnlySession = createMemoizationResult.Session;
var session = new ReadOnlyOneLevelCacheSession(this, name, implicitPin, memoizationReadOnlySession, contentReadOnlySession);
return new CreateSessionResult<IReadOnlyCacheSession>(session);
});
}
/// <inheritdoc />
public CreateSessionResult<ICacheSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{
@ -241,7 +212,7 @@ namespace BuildXL.Cache.MemoizationStore.Sessions
var memoizationSession = createMemoizationResult.Session;
var session = new OneLevelCacheSession(this, name, implicitPin, memoizationSession, contentSession);
var session = new OneLevelCacheSession(this, name, implicitPin, memoizationSession!, contentSession!);
return new CreateSessionResult<ICacheSession>(session);
});
}
@ -339,11 +310,6 @@ namespace BuildXL.Cache.MemoizationStore.Sessions
return new BoolResult($"{ContentStore} does not implement {nameof(ICopyRequestHandler)} in {nameof(OneLevelCache)}.");
}
CreateSessionResult<IReadOnlyContentSession> IContentStore.CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
return CreateReadOnlySession(context, name, implicitPin).Map(session => (IReadOnlyContentSession)session);
}
CreateSessionResult<IContentSession> IContentStore.CreateSession(Context context, string name, ImplicitPin implicitPin)
{
return CreateSession(context, name, implicitPin).Map(session => (IContentSession)session);

Просмотреть файл

@ -3,25 +3,405 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.ContractsLight;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Hashing;
using BuildXL.Cache.ContentStore.Interfaces.Extensions;
using BuildXL.Cache.ContentStore.Interfaces.FileSystem;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
using BuildXL.Cache.ContentStore.Interfaces.Stores;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
using BuildXL.Cache.ContentStore.Interfaces.Utils;
using BuildXL.Cache.MemoizationStore.Interfaces.Results;
using BuildXL.Cache.MemoizationStore.Sessions;
#nullable enable
namespace BuildXL.Cache.MemoizationStore.Interfaces.Sessions
{
/// <summary>
/// An ICacheSession implemented with one level of content and memoization.
/// </summary>
public class OneLevelCacheSession : ReadOnlyOneLevelCacheSession, ICacheSession
/// <nodoc />
public class OneLevelCacheSession : ICacheSessionWithLevelSelectors, IHibernateCacheSession
{
/// <summary>
/// Auto-pinning behavior configuration.
/// </summary>
protected readonly ImplicitPin ImplicitPin;
private IContentSession? _contentReadOnlySession;
private IMemoizationSession? _memoizationReadOnlySession;
/// <summary>
/// The content session backing the session.
/// </summary>
protected IContentSession ContentReadOnlySession
{
get
{
if (_disposed)
{
throw new InvalidOperationException("Can't obtain an inner session because the instance was already being disposed.");
}
return _contentReadOnlySession!;
}
}
/// <summary>
/// The memoization store backing the session.
/// </summary>
protected IMemoizationSession MemoizationReadOnlySession
{
get
{
if (_disposed)
{
throw new InvalidOperationException("Can't obtain an inner session because the instance was already being disposed.");
}
return _memoizationReadOnlySession!;
}
}
private bool _disposed;
/// <nodoc />
protected OneLevelCacheBase? Parent;
/// <summary>
/// Initializes a new instance of the <see cref="OneLevelCacheSession" /> class.
/// </summary>
public OneLevelCacheSession(
OneLevelCacheBase? parent,
string name,
ImplicitPin implicitPin,
IMemoizationSession memoizationSession,
IContentSession contentSession)
{
Contract.Requires(name != null);
Contract.Requires(memoizationSession != null);
Contract.Requires(contentSession != null);
Parent = parent;
Name = name;
ImplicitPin = implicitPin;
_memoizationReadOnlySession = memoizationSession;
_contentReadOnlySession = contentSession;
}
/// <inheritdoc />
public string Name { get; }
/// <inheritdoc />
public bool StartupStarted { get; private set; }
/// <inheritdoc />
public bool StartupCompleted { get; private set; }
/// <inheritdoc />
public bool ShutdownStarted { get; private set; }
/// <inheritdoc />
public bool ShutdownCompleted { get; private set; }
/// <inheritdoc />
public virtual async Task<BoolResult> StartupAsync(Context context)
{
StartupStarted = true;
var startupContentResult = await ContentReadOnlySession.StartupAsync(context).ConfigureAwait(false);
if (!startupContentResult.Succeeded)
{
StartupCompleted = true;
return new BoolResult(startupContentResult, "Content session startup failed");
}
var startupMemoizationResult = await MemoizationReadOnlySession.StartupAsync(context).ConfigureAwait(false);
if (!startupMemoizationResult.Succeeded)
{
var sb = new StringBuilder();
var shutdownContentResult = await ContentReadOnlySession.ShutdownAsync(context).ConfigureAwait(false);
if (!shutdownContentResult.Succeeded)
{
sb.Append($"Content session shutdown failed, error=[{shutdownContentResult}]");
}
sb.Append(sb.Length > 0 ? ", " : string.Empty);
sb.Append($"Memoization session startup failed, error=[{startupMemoizationResult}]");
StartupCompleted = true;
return new BoolResult(sb.ToString());
}
StartupCompleted = true;
return BoolResult.Success;
}
/// <inheritdoc />
public virtual async Task<BoolResult> ShutdownAsync(Context context)
{
ShutdownStarted = true;
var shutdownMemoizationResult = MemoizationReadOnlySession != null
? await MemoizationReadOnlySession.ShutdownAsync(context).ConfigureAwait(false)
: BoolResult.Success;
var shutdownContentResult = ContentReadOnlySession != null
? await ContentReadOnlySession.ShutdownAsync(context).ConfigureAwait(false)
: BoolResult.Success;
BoolResult result;
if (shutdownMemoizationResult.Succeeded && shutdownContentResult.Succeeded)
{
result = BoolResult.Success;
}
else
{
var sb = new StringBuilder();
if (!shutdownMemoizationResult.Succeeded)
{
sb.Append($"Memoization session shutdown failed, error=[{shutdownMemoizationResult}]");
}
if (!shutdownContentResult.Succeeded)
{
sb.Append($"Content session shutdown failed, error=[{shutdownContentResult}]");
}
result = new BoolResult(sb.ToString());
}
ShutdownCompleted = true;
return result;
}
/// <inheritdoc />
public void Dispose()
{
if (_disposed)
{
return;
}
Dispose(true);
GC.SuppressFinalize(this);
_disposed = true;
}
/// <summary>
/// Dispose pattern.
/// </summary>
private void Dispose(bool disposing)
{
if (disposing)
{
_memoizationReadOnlySession?.Dispose();
_memoizationReadOnlySession = null;
_contentReadOnlySession?.Dispose();
_contentReadOnlySession = null;
}
}
/// <inheritdoc />
public IAsyncEnumerable<GetSelectorResult> GetSelectors(
Context context,
Fingerprint weakFingerprint,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return ReadOnlyMemoizationSessionExtensions.GetSelectorsAsAsyncEnumerable(this, context, weakFingerprint, cts, urgencyHint);
}
/// <inheritdoc />
public async Task<Result<LevelSelectors>> GetLevelSelectorsAsync(
Context context,
Fingerprint weakFingerprint,
CancellationToken cts,
int level)
{
if (MemoizationReadOnlySession is IMemoizationSessionWithLevelSelectors withLevelSelectors)
{
var result = await withLevelSelectors.GetLevelSelectorsAsync(context, weakFingerprint, cts, level);
if (result.Succeeded && Parent is not null)
{
foreach (var selector in result.Value.Selectors)
{
Parent.AddOrExtendPin(context, selector.ContentHash);
}
}
return result;
}
throw new NotSupportedException(
$"ReadOnlyMemoization session {MemoizationReadOnlySession.GetType().Name} does not support GetLevelSelectors functionality.");
}
/// <inheritdoc />
public async Task<GetContentHashListResult> GetContentHashListAsync(
Context context,
StrongFingerprint strongFingerprint,
CancellationToken cts,
UrgencyHint urgencyHint)
{
var result = await MemoizationReadOnlySession.GetContentHashListAsync(context, strongFingerprint, cts, urgencyHint);
if (result.Succeeded && Parent is not null && result.ContentHashListWithDeterminism.ContentHashList is not null)
{
Parent.AddOrExtendPin(context, strongFingerprint.Selector.ContentHash);
var contentHashList = result.ContentHashListWithDeterminism.ContentHashList.Hashes;
foreach (var contentHash in contentHashList)
{
Parent.AddOrExtendPin(context, contentHash);
}
}
return result;
}
/// <inheritdoc />
public Task<PinResult> PinAsync(Context context, ContentHash contentHash, CancellationToken cts, UrgencyHint urgencyHint)
{
if (Parent is not null && Parent.CanElidePin(context, contentHash))
{
return PinResult.SuccessTask;
}
return ContentReadOnlySession.PinAsync(context, contentHash, cts, urgencyHint);
}
/// <inheritdoc />
public Task<IEnumerable<Task<Indexed<PinResult>>>> PinAsync(
Context context,
IReadOnlyList<ContentHash> contentHashes,
PinOperationConfiguration configuration)
{
return Workflows.RunWithFallback(
contentHashes,
initialFunc: (contentHashes) =>
{
return Task.FromResult(
contentHashes.Select(
contentHash =>
{
if (Parent is not null && Parent.CanElidePin(context, contentHash))
{
return PinResult.Success;
}
return PinResult.ContentNotFound;
}).AsIndexedTasks());
},
fallbackFunc: (contentHashes) => { return ContentReadOnlySession.PinAsync(context, contentHashes, configuration); },
isSuccessFunc: r => r.Succeeded);
}
/// <inheritdoc />
public Task<OpenStreamResult> OpenStreamAsync(
Context context,
ContentHash contentHash,
CancellationToken cts,
UrgencyHint urgencyHint)
{
return ContentReadOnlySession.OpenStreamAsync(context, contentHash, cts, urgencyHint);
}
/// <inheritdoc />
public Task<PlaceFileResult> PlaceFileAsync(
Context context,
ContentHash contentHash,
AbsolutePath path,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
CancellationToken cts,
UrgencyHint urgencyHint
)
{
return ContentReadOnlySession.PlaceFileAsync(context, contentHash, path, accessMode, replacementMode, realizationMode, cts, urgencyHint);
}
/// <inheritdoc />
public Task<IEnumerable<Task<Indexed<PinResult>>>> PinAsync(
Context context,
IReadOnlyList<ContentHash> contentHashes,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return Workflows.RunWithFallback(
contentHashes,
initialFunc: (contentHashes) =>
{
return Task.FromResult(
contentHashes.Select(
contentHash =>
{
if (Parent is not null && Parent.CanElidePin(context, contentHash))
{
return PinResult.Success;
}
return PinResult.ContentNotFound;
}).AsIndexedTasks());
},
fallbackFunc: (contentHashes) => { return ContentReadOnlySession.PinAsync(context, contentHashes, cts, urgencyHint); },
isSuccessFunc: r => r.Succeeded);
}
/// <inheritdoc />
public Task<IEnumerable<Task<Indexed<PlaceFileResult>>>> PlaceFileAsync(
Context context,
IReadOnlyList<ContentHashWithPath> hashesWithPaths,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return ContentReadOnlySession.PlaceFileAsync(context, hashesWithPaths, accessMode, replacementMode, realizationMode, cts, urgencyHint);
}
/// <inheritdoc />
public IEnumerable<ContentHash> EnumeratePinnedContentHashes()
{
return ContentReadOnlySession is IHibernateContentSession session
? session.EnumeratePinnedContentHashes()
: Enumerable.Empty<ContentHash>();
}
/// <inheritdoc />
public Task PinBulkAsync(Context context, IEnumerable<ContentHash> contentHashes)
{
return ContentReadOnlySession is IHibernateContentSession session
? session.PinBulkAsync(context, contentHashes)
: Task.FromResult(0);
}
/// <inheritdoc />
public Task<BoolResult> ShutdownEvictionAsync(Context context)
{
return ContentReadOnlySession is IHibernateContentSession session
? session.ShutdownEvictionAsync(context)
: BoolResult.SuccessTask;
}
/// <inheritdoc />
public IList<PublishingOperation> GetPendingPublishingOperations()
=> MemoizationReadOnlySession is IHibernateCacheSession session
? session.GetPendingPublishingOperations()
: new List<PublishingOperation>();
/// <inheritdoc />
public Task SchedulePublishingOperationsAsync(Context context, IEnumerable<PublishingOperation> pendingOperations)
=> MemoizationReadOnlySession is IHibernateCacheSession session
? session.SchedulePublishingOperationsAsync(context, pendingOperations)
: Task.FromResult(0);
/// <summary>
/// Gets the writable content session.
/// </summary>
@ -32,19 +412,6 @@ namespace BuildXL.Cache.MemoizationStore.Interfaces.Sessions
/// </summary>
public IMemoizationSession MemoizationSession => (IMemoizationSession)MemoizationReadOnlySession;
/// <summary>
/// Initializes a new instance of the <see cref="OneLevelCacheSession" /> class.
/// </summary>
public OneLevelCacheSession(
OneLevelCacheBase parent,
string name,
ImplicitPin implicitPin,
IMemoizationSession memoizationSession,
IContentSession contentSession)
: base(parent, name, implicitPin, memoizationSession, contentSession)
{
}
/// <inheritdoc />
public async Task<AddOrGetContentHashListResult> AddOrGetContentHashListAsync(
Context context,
@ -54,7 +421,11 @@ namespace BuildXL.Cache.MemoizationStore.Interfaces.Sessions
UrgencyHint urgencyHint)
{
var result = await MemoizationSession.AddOrGetContentHashListAsync(
context, strongFingerprint, contentHashListWithDeterminism, cts, urgencyHint);
context,
strongFingerprint,
contentHashListWithDeterminism,
cts,
urgencyHint);
if (result.Succeeded && Parent is not null && result.ContentHashListWithDeterminism.ContentHashList is not null)
{
@ -93,15 +464,14 @@ namespace BuildXL.Cache.MemoizationStore.Interfaces.Sessions
}
/// <inheritdoc />
public Task<PutResult> PutFileAsync
(
public Task<PutResult> PutFileAsync(
Context context,
ContentHash contentHash,
AbsolutePath path,
FileRealizationMode realizationMode,
CancellationToken cts,
UrgencyHint urgencyHint
)
)
{
return ContentSession.PutFileAsync(context, contentHash, path, realizationMode, cts, urgencyHint);
}

Просмотреть файл

@ -1,62 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Diagnostics.ContractsLight;
using System.Threading;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
using BuildXL.Cache.ContentStore.Tracing;
using BuildXL.Cache.ContentStore.Utils;
using BuildXL.Cache.MemoizationStore.Interfaces.Results;
using BuildXL.Cache.MemoizationStore.Interfaces.Sessions;
using BuildXL.Cache.MemoizationStore.Stores;
namespace BuildXL.Cache.MemoizationStore.Sessions
{
/// <summary>
/// An IReadOnlyMemoizationSession implemented in RocksDb
/// </summary>
public class ReadOnlyDatabaseMemoizationSession : StartupShutdownBase, IReadOnlyMemoizationSessionWithLevelSelectors
{
/// <inheritdoc />
public string Name { get; }
/// <inheritdoc />
protected override Tracer Tracer { get; }
/// <nodoc />
protected readonly DatabaseMemoizationStore MemoizationStore;
/// <nodoc />
public ReadOnlyDatabaseMemoizationSession(string name, DatabaseMemoizationStore memoizationStore)
{
Contract.Requires(name != null);
Contract.Requires(memoizationStore != null);
Tracer = new Tracer(nameof(ReadOnlyDatabaseMemoizationSession));
Name = name;
MemoizationStore = memoizationStore;
}
/// <inheritdoc />
public Task<GetContentHashListResult> GetContentHashListAsync(Context context, StrongFingerprint strongFingerprint, CancellationToken cts, UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
bool preferShared = urgencyHint == UrgencyHint.PreferShared;
return MemoizationStore.GetContentHashListAsync(context, strongFingerprint, cts, preferShared);
}
/// <inheritdoc />
public Task<Result<LevelSelectors>> GetLevelSelectorsAsync(Context context, Fingerprint weakFingerprint, CancellationToken cts, int level)
{
return MemoizationStore.GetLevelSelectorsAsync(context, weakFingerprint, cts, level);
}
/// <inheritdoc />
public System.Collections.Generic.IAsyncEnumerable<GetSelectorResult> GetSelectors(Context context, Fingerprint weakFingerprint, CancellationToken cts, UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return this.GetSelectorsAsAsyncEnumerable(context, weakFingerprint, cts, urgencyHint);
}
}
}

Просмотреть файл

@ -1,62 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Diagnostics.ContractsLight;
using System.Threading;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
using BuildXL.Cache.ContentStore.Tracing;
using BuildXL.Cache.ContentStore.Utils;
using BuildXL.Cache.MemoizationStore.Interfaces.Results;
using BuildXL.Cache.MemoizationStore.Interfaces.Sessions;
using BuildXL.Cache.MemoizationStore.Stores;
namespace BuildXL.Cache.MemoizationStore.Sessions
{
/// <summary>
/// An <see cref="IReadOnlyMemoizationSession"/> implemented in memory
/// </summary>
public class ReadOnlyMemoryMemoizationSession : StartupShutdownBase, IReadOnlyMemoizationSessionWithLevelSelectors
{
/// <nodoc />
protected readonly MemoryMemoizationStore MemoizationStore;
/// <inheritdoc />
protected override Tracer Tracer { get; }
/// <nodoc />
public ReadOnlyMemoryMemoizationSession(string name, MemoryMemoizationStore memoizationStore)
{
Contract.Requires(name != null);
Contract.Requires(memoizationStore != null);
Name = name;
Tracer = new Tracer(nameof(ReadOnlyMemoryMemoizationSession));
MemoizationStore = memoizationStore;
}
/// <inheritdoc />
public string Name { get; }
/// <inheritdoc />
public System.Collections.Generic.IAsyncEnumerable<GetSelectorResult> GetSelectors(Context context, Fingerprint weakFingerprint, CancellationToken cts, UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return this.GetSelectorsAsAsyncEnumerable(context, weakFingerprint, cts, urgencyHint);
}
/// <inheritdoc />
public Task<Result<LevelSelectors>> GetLevelSelectorsAsync(Context context, Fingerprint weakFingerprint, CancellationToken cts, int level)
{
return Task.FromResult(LevelSelectors.Single(MemoizationStore.GetSelectorsCore(context, weakFingerprint, cts)));
}
/// <inheritdoc />
public Task<GetContentHashListResult> GetContentHashListAsync(
Context context, StrongFingerprint strongFingerprint, CancellationToken cts, UrgencyHint urgencyHint)
{
return MemoizationStore.GetContentHashListAsync(context, strongFingerprint, cts);
}
}
}

Просмотреть файл

@ -1,384 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Diagnostics.ContractsLight;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BuildXL.Cache.ContentStore.Hashing;
using BuildXL.Cache.ContentStore.Interfaces.Extensions;
using BuildXL.Cache.ContentStore.Interfaces.FileSystem;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Sessions;
using BuildXL.Cache.ContentStore.Interfaces.Stores;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
using BuildXL.Cache.ContentStore.Interfaces.Utils;
using BuildXL.Cache.MemoizationStore.Interfaces.Results;
using BuildXL.Cache.MemoizationStore.Sessions;
#nullable enable
namespace BuildXL.Cache.MemoizationStore.Interfaces.Sessions
{
/// <summary>
/// An IReadOnlyCacheSession implemented with one level of content and memoization.
/// </summary>
public class ReadOnlyOneLevelCacheSession : IReadOnlyCacheSessionWithLevelSelectors, IHibernateCacheSession, IConfigurablePin
{
/// <summary>
/// Auto-pinning behavior configuration.
/// </summary>
protected readonly ImplicitPin ImplicitPin;
private IReadOnlyContentSession? _contentReadOnlySession;
private IReadOnlyMemoizationSession? _memoizationReadOnlySession;
/// <summary>
/// The content session backing the session.
/// </summary>
protected IReadOnlyContentSession ContentReadOnlySession
{
get
{
if (_disposed)
{
throw new InvalidOperationException("Can't obtain an inner session because the instance was already being disposed.");
}
return _contentReadOnlySession!;
}
}
/// <summary>
/// The memoization store backing the session.
/// </summary>
protected IReadOnlyMemoizationSession MemoizationReadOnlySession
{
get
{
if (_disposed)
{
throw new InvalidOperationException("Can't obtain an inner session because the instance was already being disposed.");
}
return _memoizationReadOnlySession!;
}
}
private bool _disposed;
/// <nodoc />
protected OneLevelCacheBase? Parent;
/// <summary>
/// Initializes a new instance of the <see cref="ReadOnlyOneLevelCacheSession" /> class.
/// </summary>
public ReadOnlyOneLevelCacheSession(
OneLevelCacheBase? parent,
string name,
ImplicitPin implicitPin,
IReadOnlyMemoizationSession memoizationSession,
IReadOnlyContentSession contentSession)
{
Contract.Requires(name != null);
Contract.Requires(memoizationSession != null);
Contract.Requires(contentSession != null);
Parent = parent;
Name = name;
ImplicitPin = implicitPin;
_memoizationReadOnlySession = memoizationSession;
_contentReadOnlySession = contentSession;
}
/// <inheritdoc />
public string Name { get; }
/// <inheritdoc />
public bool StartupStarted { get; private set; }
/// <inheritdoc />
public bool StartupCompleted { get; private set; }
/// <inheritdoc />
public bool ShutdownStarted { get; private set; }
/// <inheritdoc />
public bool ShutdownCompleted { get; private set; }
/// <inheritdoc />
public virtual async Task<BoolResult> StartupAsync(Context context)
{
StartupStarted = true;
var startupContentResult = await ContentReadOnlySession.StartupAsync(context).ConfigureAwait(false);
if (!startupContentResult.Succeeded)
{
StartupCompleted = true;
return new BoolResult(startupContentResult, "Content session startup failed");
}
var startupMemoizationResult = await MemoizationReadOnlySession.StartupAsync(context).ConfigureAwait(false);
if (!startupMemoizationResult.Succeeded)
{
var sb = new StringBuilder();
var shutdownContentResult = await ContentReadOnlySession.ShutdownAsync(context).ConfigureAwait(false);
if (!shutdownContentResult.Succeeded)
{
sb.Append($"Content session shutdown failed, error=[{shutdownContentResult}]");
}
sb.Append(sb.Length > 0 ? ", " : string.Empty);
sb.Append($"Memoization session startup failed, error=[{startupMemoizationResult}]");
StartupCompleted = true;
return new BoolResult(sb.ToString());
}
StartupCompleted = true;
return BoolResult.Success;
}
/// <inheritdoc />
public virtual async Task<BoolResult> ShutdownAsync(Context context)
{
ShutdownStarted = true;
var shutdownMemoizationResult = MemoizationReadOnlySession != null
? await MemoizationReadOnlySession.ShutdownAsync(context).ConfigureAwait(false)
: BoolResult.Success;
var shutdownContentResult = ContentReadOnlySession != null
? await ContentReadOnlySession.ShutdownAsync(context).ConfigureAwait(false)
: BoolResult.Success;
BoolResult result;
if (shutdownMemoizationResult.Succeeded && shutdownContentResult.Succeeded)
{
result = BoolResult.Success;
}
else
{
var sb = new StringBuilder();
if (!shutdownMemoizationResult.Succeeded)
{
sb.Append($"Memoization session shutdown failed, error=[{shutdownMemoizationResult}]");
}
if (!shutdownContentResult.Succeeded)
{
sb.Append($"Content session shutdown failed, error=[{shutdownContentResult}]");
}
result = new BoolResult(sb.ToString());
}
ShutdownCompleted = true;
return result;
}
/// <inheritdoc />
public void Dispose()
{
if (_disposed)
{
return;
}
Dispose(true);
GC.SuppressFinalize(this);
_disposed = true;
}
/// <summary>
/// Dispose pattern.
/// </summary>
private void Dispose(bool disposing)
{
if (disposing)
{
_memoizationReadOnlySession?.Dispose();
_memoizationReadOnlySession = null;
_contentReadOnlySession?.Dispose();
_contentReadOnlySession = null;
}
}
/// <inheritdoc />
public System.Collections.Generic.IAsyncEnumerable<GetSelectorResult> GetSelectors(Context context, Fingerprint weakFingerprint, CancellationToken cts, UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return this.GetSelectorsAsAsyncEnumerable(context, weakFingerprint, cts, urgencyHint);
}
/// <inheritdoc />
public async Task<Result<LevelSelectors>> GetLevelSelectorsAsync(Context context, Fingerprint weakFingerprint, CancellationToken cts, int level)
{
if (MemoizationReadOnlySession is IReadOnlyMemoizationSessionWithLevelSelectors withLevelSelectors)
{
var result = await withLevelSelectors.GetLevelSelectorsAsync(context, weakFingerprint, cts, level);
if (result.Succeeded && Parent is not null)
{
foreach (var selector in result.Value.Selectors)
{
Parent.AddOrExtendPin(context, selector.ContentHash);
}
}
return result;
}
throw new NotSupportedException($"ReadOnlyMemoization session {MemoizationReadOnlySession.GetType().Name} does not support GetLevelSelectors functionality.");
}
/// <inheritdoc />
public async Task<GetContentHashListResult> GetContentHashListAsync(Context context,
StrongFingerprint strongFingerprint,
CancellationToken cts,
UrgencyHint urgencyHint)
{
var result = await MemoizationReadOnlySession.GetContentHashListAsync(context, strongFingerprint, cts, urgencyHint);
if (result.Succeeded && Parent is not null && result.ContentHashListWithDeterminism.ContentHashList is not null)
{
Parent.AddOrExtendPin(context, strongFingerprint.Selector.ContentHash);
var contentHashList = result.ContentHashListWithDeterminism.ContentHashList.Hashes;
foreach (var contentHash in contentHashList)
{
Parent.AddOrExtendPin(context, contentHash);
}
}
return result;
}
/// <inheritdoc />
public Task<PinResult> PinAsync(Context context, ContentHash contentHash, CancellationToken cts, UrgencyHint urgencyHint)
{
if (Parent is not null && Parent.CanElidePin(context, contentHash))
{
return PinResult.SuccessTask;
}
return ContentReadOnlySession.PinAsync(context, contentHash, cts, urgencyHint);
}
/// <inheritdoc />
public Task<IEnumerable<Task<Indexed<PinResult>>>> PinAsync(Context context, IReadOnlyList<ContentHash> contentHashes, PinOperationConfiguration configuration)
{
return Workflows.RunWithFallback(
contentHashes,
initialFunc: (contentHashes) =>
{
return Task.FromResult(contentHashes.Select(contentHash =>
{
if (Parent is not null && Parent.CanElidePin(context, contentHash))
{
return PinResult.Success;
}
return PinResult.ContentNotFound;
}).AsIndexedTasks());
},
fallbackFunc: (contentHashes) => {
return ContentReadOnlySession.PinAsync(context, contentHashes, configuration);
},
isSuccessFunc: r => r.Succeeded);
}
/// <inheritdoc />
public Task<OpenStreamResult> OpenStreamAsync(
Context context, ContentHash contentHash, CancellationToken cts, UrgencyHint urgencyHint)
{
return ContentReadOnlySession.OpenStreamAsync(context, contentHash, cts, urgencyHint);
}
/// <inheritdoc />
public Task<PlaceFileResult> PlaceFileAsync
(
Context context,
ContentHash contentHash,
AbsolutePath path,
FileAccessMode accessMode,
FileReplacementMode replacementMode,
FileRealizationMode realizationMode,
CancellationToken cts,
UrgencyHint urgencyHint
)
{
return ContentReadOnlySession.PlaceFileAsync(context, contentHash, path, accessMode, replacementMode, realizationMode, cts, urgencyHint);
}
/// <inheritdoc />
public Task<IEnumerable<Task<Indexed<PinResult>>>> PinAsync(
Context context,
IReadOnlyList<ContentHash> contentHashes,
CancellationToken cts,
UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return Workflows.RunWithFallback(
contentHashes,
initialFunc: (contentHashes) =>
{
return Task.FromResult(contentHashes.Select(contentHash =>
{
if (Parent is not null && Parent.CanElidePin(context, contentHash))
{
return PinResult.Success;
}
return PinResult.ContentNotFound;
}).AsIndexedTasks());
},
fallbackFunc: (contentHashes) => {
return ContentReadOnlySession.PinAsync(context, contentHashes, cts, urgencyHint);
},
isSuccessFunc: r => r.Succeeded);
}
/// <inheritdoc />
public Task<IEnumerable<Task<Indexed<PlaceFileResult>>>> PlaceFileAsync(Context context, IReadOnlyList<ContentHashWithPath> hashesWithPaths, FileAccessMode accessMode, FileReplacementMode replacementMode, FileRealizationMode realizationMode, CancellationToken cts, UrgencyHint urgencyHint = UrgencyHint.Nominal)
{
return ContentReadOnlySession.PlaceFileAsync(context, hashesWithPaths, accessMode, replacementMode, realizationMode, cts, urgencyHint);
}
/// <inheritdoc />
public IEnumerable<ContentHash> EnumeratePinnedContentHashes()
{
return ContentReadOnlySession is IHibernateContentSession session
? session.EnumeratePinnedContentHashes()
: Enumerable.Empty<ContentHash>();
}
/// <inheritdoc />
public Task PinBulkAsync(Context context, IEnumerable<ContentHash> contentHashes)
{
return ContentReadOnlySession is IHibernateContentSession session
? session.PinBulkAsync(context, contentHashes)
: Task.FromResult(0);
}
/// <inheritdoc />
public Task<BoolResult> ShutdownEvictionAsync(Context context)
{
return ContentReadOnlySession is IHibernateContentSession session
? session.ShutdownEvictionAsync(context)
: BoolResult.SuccessTask;
}
/// <inheritdoc />
public IList<PublishingOperation> GetPendingPublishingOperations()
=> MemoizationReadOnlySession is IHibernateCacheSession session
? session.GetPendingPublishingOperations()
: new List<PublishingOperation>();
/// <inheritdoc />
public Task SchedulePublishingOperationsAsync(Context context, IEnumerable<PublishingOperation> pendingOperations)
=> MemoizationReadOnlySession is IHibernateCacheSession session
? session.SchedulePublishingOperationsAsync(context, pendingOperations)
: Task.FromResult(0);
}
}

Просмотреть файл

@ -70,13 +70,6 @@ namespace BuildXL.Cache.MemoizationStore.Stores
Database = database;
}
/// <inheritdoc />
public CreateSessionResult<IReadOnlyMemoizationSession> CreateReadOnlySession(Context context, string name)
{
var session = new ReadOnlyDatabaseMemoizationSession(name, this);
return new CreateSessionResult<IReadOnlyMemoizationSession>(session);
}
/// <inheritdoc />
public CreateSessionResult<IMemoizationSession> CreateSession(Context context, string name)
{

Просмотреть файл

@ -60,13 +60,6 @@ namespace BuildXL.Cache.MemoizationStore.Stores
return Task.FromResult(BoolResult.Success);
}
/// <inheritdoc />
public CreateSessionResult<IReadOnlyMemoizationSession> CreateReadOnlySession(Context context, string name)
{
var session = new ReadOnlyMemoryMemoizationSession(name, this);
return new CreateSessionResult<IReadOnlyMemoizationSession>(session);
}
/// <inheritdoc />
public CreateSessionResult<IMemoizationSession> CreateSession(Context context, string name)
{

Просмотреть файл

@ -17,7 +17,7 @@ namespace BuildXL.Cache.MemoizationStore.Tracing
{
private readonly CallCounter _statsCounter = new CallCounter("CacheTracer.Stats");
private readonly CallCounter _sessionCounter = new CallCounter("CacheTracer.CreateSession");
private readonly CallCounter _readSessionCounter = new CallCounter("CacheTracer.CreateReadOnlySession");
private readonly CallCounter _readSessionCounter = new CallCounter("CacheTracer.CreateSession");
public CacheTracer(string name)
: base(name)
@ -35,24 +35,6 @@ namespace BuildXL.Cache.MemoizationStore.Tracing
base.GetStatsStop(context, result);
}
public void CreateReadOnlySessionStart(Context context, string name)
{
_readSessionCounter.Started();
if (context.IsEnabled)
{
Debug(context, $"{Name}.CreateReadOnlySession({name}) start");
}
}
public void CreateReadOnlySessionStop(Context context, CreateSessionResult<IReadOnlyCacheSession> result)
{
_readSessionCounter.Completed(result.Duration.Ticks);
if (context.IsEnabled)
{
Debug(context, $"{Name}.CreateReadOnlySession() stop {result.DurationMs}ms result=[{result}]");
}
}
public void CreateSessionStart(Context context, string name)
{
_sessionCounter.Started();

Просмотреть файл

@ -1,51 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using BuildXL.Cache.ContentStore.Interfaces.Results;
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
using BuildXL.Cache.ContentStore.Tracing;
using BuildXL.Cache.MemoizationStore.Interfaces.Sessions;
namespace BuildXL.Cache.MemoizationStore.Tracing
{
/// <summary>
/// Instance of a CreateReadOnlySession operation for tracing purposes.
/// </summary>
public sealed class CreateReadOnlySessionCall
: TracedCall<CacheTracer, CreateSessionResult<IReadOnlyCacheSession>>, IDisposable
{
/// <summary>
/// Run the call.
/// </summary>
public static CreateSessionResult<IReadOnlyCacheSession> Run(
CacheTracer tracer, Context context, string name, Func<CreateSessionResult<IReadOnlyCacheSession>> func)
{
using (var call = new CreateReadOnlySessionCall(tracer, context, name))
{
return call.Run(func);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="CreateReadOnlySessionCall"/> class.
/// </summary>
private CreateReadOnlySessionCall(CacheTracer tracer, Context context, string name)
: base(tracer, context)
{
Tracer.CreateReadOnlySessionStart(context, name);
}
/// <inheritdoc />
protected override CreateSessionResult<IReadOnlyCacheSession> CreateErrorResult(Exception exception)
{
return new CreateSessionResult<IReadOnlyCacheSession>(exception);
}
/// <inheritdoc />
public void Dispose()
{
Tracer.CreateReadOnlySessionStop(Context, Result);
}
}
}

Просмотреть файл

@ -37,11 +37,6 @@ namespace BuildXL.Cache.MemoizationStore.Test.Sessions
_contentSession = testContentSession;
}
public CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
return new CreateSessionResult<IReadOnlyContentSession>(_contentSession);
}
public CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
{
return new CreateSessionResult<IContentSession>(_contentSession);
@ -184,11 +179,6 @@ namespace BuildXL.Cache.MemoizationStore.Test.Sessions
_memoizationSession = iMemoizationSession;
}
public CreateSessionResult<IReadOnlyMemoizationSession> CreateReadOnlySession(Context context, string name)
{
return new CreateSessionResult<IReadOnlyMemoizationSession>(_memoizationSession);
}
public CreateSessionResult<IMemoizationSession> CreateSession(Context context, string name)
{
return new CreateSessionResult<IMemoizationSession>(_memoizationSession);
@ -224,7 +214,7 @@ namespace BuildXL.Cache.MemoizationStore.Test.Sessions
}
}
internal class TestMemoizationSession : IMemoizationSession, IReadOnlyMemoizationSessionWithLevelSelectors
internal class TestMemoizationSession : IMemoizationSession, IMemoizationSessionWithLevelSelectors
{
public HashSet<Fingerprint> GetSelectorsParams = new HashSet<Fingerprint>();
public HashSet<StrongFingerprint> GetContentHashListAsyncParams = new HashSet<StrongFingerprint>();

Просмотреть файл

@ -250,7 +250,7 @@ namespace BuildXL.Cache.MemoizationStore.Test.Sessions
testDirectory => new OneLevelCache(() => _mockContentStore, () => _mockMemoizationStore, CacheDeterminism.NewCacheGuid()));
}
private Task RunMockReadOnlySessionTestAsync(Context context, Func<IReadOnlyCacheSession, Task> funcAsync)
private Task RunMockReadOnlySessionTestAsync(Context context, Func<ICacheSession, Task> funcAsync)
{
return RunReadOnlySessionTestAsync(
context,

Просмотреть файл

@ -82,9 +82,6 @@ namespace BuildXL.Cache.MemoizationStore.Test.Sessions
/// <inheritdoc />
public Guid Id => _client.Id;
/// <inheritdoc />
public CreateSessionResult<IReadOnlyCacheSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin) => _client.CreateReadOnlySession(context, name, implicitPin);
/// <inheritdoc />
public CreateSessionResult<ICacheSession> CreateSession(Context context, string name, ImplicitPin implicitPin) => _client.CreateSession(context, name, implicitPin);

Просмотреть файл

@ -159,8 +159,6 @@ namespace BuildXL.Cache.MemoizationStore.Test.Synchronization
/// </summary>
public class MockContentStore : StartupShutdownMock, IContentStore
{
public CreateSessionResult<IReadOnlyContentSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin) => null;
public CreateSessionResult<IContentSession> CreateSession(Context context, string name, ImplicitPin implicitPin) => null;
public Task<GetStatsResult> GetStatsAsync(Context context) => Task.FromResult(new GetStatsResult(new CounterSet()));
@ -177,8 +175,6 @@ namespace BuildXL.Cache.MemoizationStore.Test.Synchronization
/// </summary>
public class MockMemoizationStore : StartupShutdownMock, IMemoizationStore
{
public CreateSessionResult<IReadOnlyMemoizationSession> CreateReadOnlySession(Context context, string name) => null;
public CreateSessionResult<IMemoizationSession> CreateSession(Context context, string name) => null;
public CreateSessionResult<IMemoizationSession> CreateSession(Context context, string name, IContentSession contentSession) => null;

Просмотреть файл

@ -320,61 +320,6 @@ namespace BuildXL.Cache.MemoizationStore.Vsts
/// <inheritdoc />
public bool StartupStarted { get; private set; }
/// <inheritdoc />
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public CreateSessionResult<IReadOnlyCacheSession> CreateReadOnlySession(Context context, string name, ImplicitPin implicitPin)
{
return Tracing.CreateReadOnlySessionCall.Run(_tracer, context, name, () =>
{
var backingContentSessionResult = _backingContentStore.CreateSession(context, name);
if (!backingContentSessionResult.Succeeded)
{
return new CreateSessionResult<IReadOnlyCacheSession>(backingContentSessionResult);
}
IContentSession writeThroughContentSession = null;
if (_writeThroughContentStore != null)
{
var writeThroughContentSessionResult = _writeThroughContentStore.CreateSession(context, name, implicitPin);
if (!writeThroughContentSessionResult.Succeeded)
{
return new CreateSessionResult<IReadOnlyCacheSession>(writeThroughContentSessionResult);
}
writeThroughContentSession = writeThroughContentSessionResult.Session;
}
return new CreateSessionResult<IReadOnlyCacheSession>(
new BuildCacheReadOnlySession(
_fileSystem,
name,
implicitPin,
_cacheNamespace,
Id,
_contentHashListAdapterFactory.Create(backingContentSessionResult.Session, _includeDownloadUris),
backingContentSessionResult.Session,
_maxFingerprintSelectorsToFetch,
_minimumTimeToKeepContentHashLists,
_rangeOfTimeToKeepContentHashLists,
_fingerprintIncorporationEnabled,
_maxDegreeOfParallelismForIncorporateRequests,
_maxFingerprintsPerIncorporateRequest,
writeThroughContentSession,
_sealUnbackedContentHashLists,
_overrideUnixFileAccessMode,
_tracer,
_enableEagerFingerprintIncorporation,
_inlineFingerprintIncorporationExpiry,
_eagerFingerprintIncorporationNagleInterval,
_eagerFingerprintIncorporationNagleBatchSize,
_manuallyExtendContentLifetime,
_forceUpdateOnAddContentHashList)
{
RequiredContentKeepUntil = _backingContentStoreConfiguration.RequiredContentKeepUntil
});
});
}
/// <inheritdoc />
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public CreateSessionResult<ICacheSession> CreateSession(Context context, string name, ImplicitPin implicitPin)
@ -479,16 +424,6 @@ namespace BuildXL.Cache.MemoizationStore.Vsts
return AsyncEnumerable.Empty<StructResult<StrongFingerprint>>();
}
/// <inheritdoc />
public CreateSessionResult<IReadOnlyMemoizationSession> CreateReadOnlySession(Context context, string name)
{
var result = CreateReadOnlySession(context, name, ImplicitPin.None);
return result.Succeeded
? new CreateSessionResult<IReadOnlyMemoizationSession>(result.Session)
: new CreateSessionResult<IReadOnlyMemoizationSession>(result);
}
/// <inheritdoc />
public CreateSessionResult<IMemoizationSession> CreateSession(Context context, string name)
{

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -212,7 +212,7 @@ namespace BuildXL.Cache.MemoizationStoreAdapter
Contract.Requires(!IsShutdown);
var context = new Context(m_logger);
var createSessionResult = m_cache.CreateReadOnlySession(
var createSessionResult = m_cache.CreateSession(
context,
$"{CacheId}-Anonymous",
m_implicitPin);

Просмотреть файл

@ -32,7 +32,7 @@ namespace BuildXL.Cache.MemoizationStoreAdapter
/// <summary>
/// Backing ReadOnlyCacheSession for content and fingerprint calls.
/// </summary>
protected readonly IReadOnlyCacheSession ReadOnlyCacheSession;
protected readonly MemoizationStore.Interfaces.Sessions.ICacheSession ReadOnlyCacheSession;
/// <summary>
/// The set of strong fingerprints touched by this cache session.
@ -67,7 +67,7 @@ namespace BuildXL.Cache.MemoizationStoreAdapter
/// <param name="sessionId">Telemetry ID for the session.</param>
/// <param name="replaceExistingOnPlaceFile">When true, replace existing file when placing file.</param>
public MemoizationStoreAdapterCacheReadOnlySession(
IReadOnlyCacheSession readOnlyCacheSession,
MemoizationStore.Interfaces.Sessions.ICacheSession readOnlyCacheSession,
BuildXL.Cache.MemoizationStore.Interfaces.Caches.ICache cache,
CacheId cacheId,
ILogger logger,