Skip to content
This repository has been archived by the owner on Nov 1, 2023. It is now read-only.

Storage performance fixes #3313

Merged
merged 15 commits into from
Jul 24, 2023
2 changes: 1 addition & 1 deletion src/ApiService/ApiService/Functions/Containers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ private async Async.Task<HttpResponseData> Post(HttpRequestData req) {

var post = request.OkV;
_logger.LogInformation("creating {ContainerName}", post.Name);
var sas = await _context.Containers.CreateContainer(
var sas = await _context.Containers.GetOrCreateNewContainer(
post.Name,
StorageType.Corpus,
post.Metadata);
Expand Down
25 changes: 13 additions & 12 deletions src/ApiService/ApiService/Functions/Jobs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,16 @@ private async Task<HttpResponseData> Post(HttpRequestData req, FunctionContext c
var userInfo = context.GetUserAuthInfo();

var create = request.OkV;
var cfg = new JobConfig(
Build: create.Build,
Duration: create.Duration,
Logs: create.Logs,
Name: create.Name,
Project: create.Project);

var job = new Job(
JobId: Guid.NewGuid(),
State: JobState.Init,
Config: cfg,
Config: new(
Build: create.Build,
Duration: create.Duration,
Logs: create.Logs,
Name: create.Name,
Project: create.Project),
UserInfo: new(
ObjectId: userInfo.UserInfo.ObjectId,
ApplicationId: userInfo.UserInfo.ApplicationId));
Expand All @@ -56,8 +55,11 @@ private async Task<HttpResponseData> Post(HttpRequestData req, FunctionContext c
{ "container_type", "logs" }, // TODO: use ContainerType.Logs enum somehow; needs snake case name
};

var containerName = Container.Parse($"logs-{job.JobId}");
var containerSas = await _context.Containers.CreateContainer(containerName, StorageType.Corpus, metadata);
var containerSas = await _context.Containers.CreateNewContainer(
Container.Parse($"logs-{job.JobId}"),
StorageType.Corpus,
metadata);

if (containerSas is null) {
return await _context.RequestHandling.NotOk(
req,
Expand All @@ -76,9 +78,8 @@ private async Task<HttpResponseData> Post(HttpRequestData req, FunctionContext c
return await _context.RequestHandling.NotOk(
req,
Error.Create(
ErrorCode.UNABLE_TO_CREATE,
"unable to create job"
),
ErrorCode.UNABLE_TO_CREATE,
"unable to create job"),
"job");
}

Expand Down
79 changes: 58 additions & 21 deletions src/ApiService/ApiService/onefuzzlib/Containers.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.IO;
using System.IO.Compression;
using System.Threading;
using System.Threading.Tasks;
using ApiService.OneFuzzLib.Orm;
using Azure;
Expand All @@ -16,11 +15,11 @@ namespace Microsoft.OneFuzz.Service;


public interface IContainers {
public Async.Task<(BinaryData? data, IDictionary<string, string>? tags)> GetBlob(Container container, string name, StorageType storageType);
public Async.Task<BinaryData?> GetBlob(Container container, string name, StorageType storageType);
public Async.Task<(BinaryData? data, IDictionary<string, string>? tags)> GetBlobWithTags(Container container, string name, StorageType storageType);

public Async.Task<Uri?> CreateContainer(Container container, StorageType storageType, IDictionary<string, string>? metadata);

public Async.Task<BlobContainerClient?> GetOrCreateContainerClient(Container container, StorageType storageType, IDictionary<string, string>? metadata);
public Async.Task<Uri?> CreateNewContainer(Container container, StorageType storageType, IDictionary<string, string>? metadata);
public Async.Task<Uri?> GetOrCreateNewContainer(Container container, StorageType storageType, IDictionary<string, string>? metadata);

public Async.Task<BlobContainerClient?> FindContainer(Container container, StorageType storageType);
public Async.Task<bool> DeleteContainerIfExists(Container container, StorageType storageType);
Expand Down Expand Up @@ -65,14 +64,6 @@ public Containers(
_config = config;
_cache = cache;

_getInstanceId = new Lazy<Async.Task<Guid>>(async () => {
var (data, tags) = await GetBlob(WellKnownContainers.BaseConfig, "instance_id", StorageType.Config);
if (data == null) {
throw new Exception("Blob Not Found");
}

return Guid.Parse(data.ToString());
}, LazyThreadSafetyMode.PublicationOnly);
}

public async Async.Task<Uri?> GetFileUrl(Container container, string name, StorageType storageType) {
Expand All @@ -83,22 +74,45 @@ public Containers(
return client.GetBlobClient(name).Uri;
}

public async Async.Task<(BinaryData? data, IDictionary<string, string>? tags)> GetBlob(Container container, string name, StorageType storageType) {
public async Async.Task<(BinaryData? data, IDictionary<string, string>? tags)> GetBlobWithTags(Container container, string name, StorageType storageType) {
var client = await FindContainer(container, storageType);
if (client == null) {
return (null, null);
}

var blobClient = client.GetBlobClient(name);
try {
var blobClient = client.GetBlobClient(name);
var tags = await blobClient.GetTagsAsync();
return ((await blobClient.DownloadContentAsync()).Value.Content, tags.Value.Tags);
var (tags, content) = await (blobClient.GetTagsAsync(), blobClient.DownloadContentAsync());
return (content.Value.Content, tags.Value.Tags);
} catch (RequestFailedException) {
return (null, null);
}
}

public async Task<Uri?> CreateContainer(Container container, StorageType storageType, IDictionary<string, string>? metadata) {
public async Async.Task<BinaryData?> GetBlob(Container container, string name, StorageType storageType) {
var client = await FindContainer(container, storageType);
if (client == null) {
return null;
}

var blobClient = client.GetBlobClient(name);
try {
return (await blobClient.DownloadContentAsync()).Value.Content;
} catch (RequestFailedException) {
return null;
}
}

public async Task<Uri?> CreateNewContainer(Container container, StorageType storageType, IDictionary<string, string>? metadata) {
var client = await CreateNewContainerClient(container, storageType, metadata);
if (client is null) {
return null;
}

return GetContainerSasUrlService(client, _containerCreatePermissions);
}

public async Task<Uri?> GetOrCreateNewContainer(Container container, StorageType storageType, IDictionary<string, string>? metadata) {
var client = await GetOrCreateContainerClient(container, storageType, metadata);
if (client is null) {
return null;
Expand All @@ -113,12 +127,24 @@ private static readonly BlobContainerSasPermissions _containerCreatePermissions
| BlobContainerSasPermissions.Delete
| BlobContainerSasPermissions.List;

public async Task<BlobContainerClient?> GetOrCreateContainerClient(Container container, StorageType storageType, IDictionary<string, string>? metadata) {
public async Task<BlobContainerClient?> GetOrCreateContainerClient(
Container container,
StorageType storageType,
IDictionary<string, string>? metadata) {

var containerClient = await FindContainer(container, StorageType.Corpus);
if (containerClient is not null) {
return containerClient;
}

return await CreateNewContainerClient(container, storageType, metadata);
}

public async Task<BlobContainerClient?> CreateNewContainerClient(
Container container,
StorageType storageType,
IDictionary<string, string>? metadata) {

var account = _storage.ChooseAccount(storageType);
var client = await _storage.GetBlobServiceClientForAccount(account);
var containerName = _config.OneFuzzStoragePrefix + container;
Expand Down Expand Up @@ -290,8 +316,19 @@ private async Async.Task SaveBlobInternal(Container container, string name, stri
}
}

public virtual Async.Task<Guid> GetInstanceId() => _getInstanceId.Value;
private readonly Lazy<Async.Task<Guid>> _getInstanceId;
private static readonly object _instanceIdKey = new();
public virtual Async.Task<Guid> GetInstanceId() {
return _cache.GetOrCreateAsync(_instanceIdKey, async ce => {
ce.AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(7); // should never change

var data = await GetBlob(WellKnownContainers.BaseConfig, "instance_id", StorageType.Config);
if (data == null) {
throw new Exception("Couldn't find instance_id blob");
}

return Guid.Parse(data.ToString());
});
}

public Uri GetContainerSasUrlService(
BlobContainerClient client,
Expand Down
2 changes: 1 addition & 1 deletion src/ApiService/ApiService/onefuzzlib/Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public virtual void LogEvent(BaseEvent anEvent) {
}

public async Async.Task<OneFuzzResult<DownloadableEventMessage>> GetDownloadableEvent(Guid eventId) {
var (data, tags) = await _containers.GetBlob(WellKnownContainers.Events, eventId.ToString(), StorageType.Corpus);
var (data, tags) = await _containers.GetBlobWithTags(WellKnownContainers.Events, eventId.ToString(), StorageType.Corpus);
if (data == null) {
return OneFuzzResult<DownloadableEventMessage>.Error(ErrorCode.UNABLE_TO_FIND, $"Could not find container for event with id {eventId}");
}
Expand Down
2 changes: 1 addition & 1 deletion src/ApiService/IntegrationTests/ContainersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ public async Async.Task DeleteExpiredBlobsDoesNotTouchUntaggedBlobs() {

TestFeatureManagerSnapshot.AddFeatureFlag(FeatureFlagConstants.EnableDryRunBlobRetention, enabled: false);

_ = await Context.Containers.CreateContainer(testContainer, StorageType.Corpus, null);
_ = await Context.Containers.CreateNewContainer(testContainer, StorageType.Corpus, null);
await Context.Containers.SaveBlob(testContainer, expirableBlobName, string.Empty, StorageType.Corpus, DateOnly.MinValue);
await Context.Containers.SaveBlob(testContainer, nonExpirableBlobName, string.Empty, StorageType.Corpus);

Expand Down
12 changes: 6 additions & 6 deletions src/ApiService/IntegrationTests/JinjaToScribanMigrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ protected JinjaToScribanMigrationTestBase(ITestOutputHelper output, IStorage sto
[Fact]
public async Async.Task Dry_Run_Does_Not_Make_Changes() {
var notificationContainer = Container.Parse("abc123");
var _ = await Context.Containers.CreateContainer(notificationContainer, StorageType.Corpus, null);
var _ = await Context.Containers.CreateNewContainer(notificationContainer, StorageType.Corpus, null);
var r = await Context.NotificationOperations.Create(
notificationContainer,
MigratableAdoTemplate(),
Expand Down Expand Up @@ -61,7 +61,7 @@ public async Async.Task Dry_Run_Does_Not_Make_Changes() {
[Fact]
public async Async.Task Migration_Happens_When_Not_Dry_run() {
var notificationContainer = Container.Parse("abc123");
var _ = await Context.Containers.CreateContainer(notificationContainer, StorageType.Corpus, null);
var _ = await Context.Containers.CreateNewContainer(notificationContainer, StorageType.Corpus, null);
var r = await Context.NotificationOperations.Create(
notificationContainer,
MigratableAdoTemplate(),
Expand Down Expand Up @@ -140,7 +140,7 @@ public async Async.Task All_ADO_Fields_Are_Migrated() {
"{% if org %} comment {% endif %}"
);

var _ = await Context.Containers.CreateContainer(notificationContainer, StorageType.Corpus, null);
var _ = await Context.Containers.CreateNewContainer(notificationContainer, StorageType.Corpus, null);
var r = await Context.NotificationOperations.Create(
notificationContainer,
adoTemplate,
Expand Down Expand Up @@ -189,7 +189,7 @@ public async Async.Task All_Github_Fields_Are_Migrated() {
var githubTemplate = MigratableGithubTemplate();

var notificationContainer = Container.Parse("abc123");
var _ = await Context.Containers.CreateContainer(notificationContainer, StorageType.Corpus, null);
var _ = await Context.Containers.CreateNewContainer(notificationContainer, StorageType.Corpus, null);
var r = await Context.NotificationOperations.Create(
notificationContainer,
githubTemplate,
Expand Down Expand Up @@ -241,7 +241,7 @@ public async Async.Task All_Github_Fields_Are_Migrated() {
public async Async.Task Teams_Template_Not_Migrated() {
var teamsTemplate = GetTeamsTemplate();
var notificationContainer = Container.Parse("abc123");
var _ = await Context.Containers.CreateContainer(notificationContainer, StorageType.Corpus, null);
var _ = await Context.Containers.CreateNewContainer(notificationContainer, StorageType.Corpus, null);
var r = await Context.NotificationOperations.Create(
notificationContainer,
teamsTemplate,
Expand Down Expand Up @@ -273,7 +273,7 @@ public async Async.Task Teams_Template_Not_Migrated() {
[Fact]
public async Async.Task Can_Migrate_Multiple_Notification_Configs() {
var notificationContainer = Container.Parse("abc123");
var _ = await Context.Containers.CreateContainer(notificationContainer, StorageType.Corpus, null);
var _ = await Context.Containers.CreateNewContainer(notificationContainer, StorageType.Corpus, null);

var teamsTemplate = GetTeamsTemplate();
var r = await Context.NotificationOperations.Create(
Expand Down