зеркало из https://github.com/microsoft/BuildXL.git
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:
Родитель
142c874129
Коммит
ac5ae894ea
|
@ -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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче