-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Durable Jobs (aka Reminders v2) #9717
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
89 commits
Select commit
Hold shift + click to select a range
451fad0
Scheduled jobs wip
benjaminpetit 4b0672f
Address comments
benjaminpetit e75606e
Add README
benjaminpetit 0039ab1
Remove ShardId from interface
benjaminpetit ba4dc2c
Split extensions
benjaminpetit 1df4b02
Add metadata
benjaminpetit cb4edde
Decouple extension
benjaminpetit 6c5a85e
More robust test
benjaminpetit e8871b0
Move context
benjaminpetit cb39739
Retry wip
benjaminpetit b7f36dd
Listen to cluster changes
benjaminpetit 2ee5990
wip
benjaminpetit 5b28fce
Add metadata
benjaminpetit 7dfcfd9
Add metadata test
benjaminpetit 5cb8609
Add cancellation test
benjaminpetit 5329033
Add comments
benjaminpetit 45df0eb
Mark test as skippable
benjaminpetit 281f800
Add logging to AzureStorageJobShardManager
benjaminpetit 9be56be
Plumb job cancellation
benjaminpetit 7194243
More consistent naming
benjaminpetit 0643e8c
Fix retry
benjaminpetit 4545d02
Add comments to InMemoryJobQueue and some unit tests
benjaminpetit 3a8807c
Add tests and fix AzureStorageJobShardManager
benjaminpetit 4248b9e
Enable basic parrallelism limit on shard processing
benjaminpetit 72b9275
keep track of running shards
benjaminpetit 79d6ac5
Add logs and comments to LocalScheduledJobManager
benjaminpetit b576c61
Merge remote-tracking branch 'dotnet/main' into wip/scheduled-jobs
benjaminpetit 2008fcc
Add READMEs
benjaminpetit 2964e3a
Add READMEs
benjaminpetit de69620
Update src/Orleans.ScheduledJobs/Hosting/ScheduledJobsOptions.cs
ReubenBond 026caaa
Update src/Orleans.ScheduledJobs/InMemoryJobQueue.cs
ReubenBond e90f6b8
Apply suggestions from code review
benjaminpetit fc10282
Add Cancellation tokens to LocalScheduledJobManager
benjaminpetit 8b8a416
Remove default values in JobShard
benjaminpetit d09874c
Switch from line-based format to netstring format for blob operations
benjaminpetit 7ed5369
Refactor JobShard
benjaminpetit e3a021b
Add membership version in AzureStorageJobShard
benjaminpetit 98fa1ca
Refactor storage operations to use Channel for single-threaded writes…
benjaminpetit 2091124
Refactor job execution to run concurrently
benjaminpetit f829bd9
Split LocalScheduledJobManager into separate files for better readabi…
benjaminpetit 616e488
Move ILocalScheduledJobManager interface to its own file
benjaminpetit c5d41da
Merge branch 'wip/scheduled-jobs' of https://github.com/benjaminpetit…
benjaminpetit cee9969
Refactor job scheduling methods to use TryScheduleJobAsync for improv…
benjaminpetit e412923
Very simplistic concurrency control on shard creation
benjaminpetit bedf374
Enhance IScheduledJobReceiverExtension with improved logging and docu…
benjaminpetit 73cba74
Update ScheduledJobTests and InMemoryJobQueueTests to use UtcNow for …
benjaminpetit 4ee03f3
Refactor shard management to simplify task handling and ensure proper…
benjaminpetit ede6b13
Refactor AssignJobShardsAsync to improve owner/creator status checks
benjaminpetit 329f75d
Clarify shard start-time naming and logging; update defaults and proj…
benjaminpetit 2bb5172
Fix netstring encoding/delimiter and format MembershipVersion invaria…
benjaminpetit 9ca1cb4
Scale default MaxConcurrentJobsPerSilo by CPU count
benjaminpetit c440fae
Rename GetJobCount to GetJobCountAsync and IsComplete to IsAddingComp…
benjaminpetit c2f4c70
Return removal result from CancelJob and update tests
benjaminpetit 4f68cc0
Return removal result from RemoveJobAsync and update cancel flow & tests
benjaminpetit d4c5cd8
Consolidate scheduling API: merge ScheduleJobWithMetadataAsync into S…
benjaminpetit 043fffd
Add cross-grain scheduling test and SchedulerGrain; make ScheduleJobA…
benjaminpetit db44fad
Add job retry test and RetryTestGrain
benjaminpetit c5498d6
Extract scheduled job test logic into ScheduledJobTestsRunner and add…
benjaminpetit 069fe77
Add Azure Blob Storage hosting integration for scheduled jobs
benjaminpetit c6e8cc8
Add AzureStorageScheduledJobTests to run scheduled job tests against …
benjaminpetit 6a05384
Remove stuff
benjaminpetit f72d4c9
Remove stuff
benjaminpetit 156659a
Add CancellationToken to IClusterMembershipService.Refresh and propag…
benjaminpetit 560aae9
Add blob-prefix support to AzureStorageJobShardManager and update tests
benjaminpetit 29a0dec
Replace TaskCanceledException with OperationCanceledException
benjaminpetit e23e1ac
Cancel pending storage operations on shutdown
benjaminpetit 5b00a75
Use MembershipVersion.Value for blob metadata; rename/add InMemorySch…
benjaminpetit 173921a
Rename LocalScheduledJobManager system target grain type to "job-mana…
benjaminpetit 1413e15
Replace IScheduledJob interface with ScheduledJob concrete type
benjaminpetit 10e0565
Fix shard bucketing, correct shard assignment window, and add options…
benjaminpetit 7a5a526
Gracefully stop shard storage processor; fix caching and metadata par…
benjaminpetit 7446b8d
Dispose newly-created shard on ownership conflict; remove unused blob…
benjaminpetit 5199685
Remove unused Microsoft.CodeAnalysis using; clarify shard-too-new com…
benjaminpetit 12d694e
Introduce NetstringEncoder utility and tests; use it in AzureStorageJ…
benjaminpetit 0655f6a
Fix bad rename
benjaminpetit fc15a75
Replace NetstringEncoder with NetstringJsonSerializer; update usages …
benjaminpetit 4ac0c41
Make NetstringJsonSerializer stream-based with pooled buffers; update…
benjaminpetit fc6ba4d
Batch Azure Storage append operations; add batching options and tests
benjaminpetit 5b9ba88
Rename RegisterShard -> CreateShardAsync and simplify ownership behavior
benjaminpetit fa3eb54
Make InMemoryJobQueue synchronization and enumeration safer
benjaminpetit bc26774
Add periodic shard checker and shard activation buffer option
benjaminpetit 46fe7d0
Centralize shard lifecycle in manager; add periodic shard checker and…
benjaminpetit 8ba69f4
Unify StorageOperation completion handling and make shutdown cancellable
benjaminpetit e20e7a8
Remove Creator metadata and simplify ownership/ID handling; add blob-…
benjaminpetit 9e42a9b
Add structured logging to AzureStorageJobShard and propagate ILoggerF…
benjaminpetit 00a772e
Make InMemoryJobQueue internal, add InternalsVisibleTo
benjaminpetit e07538f
Drop explicit '= false' initializer for IsAddingCompleted property in…
benjaminpetit 5c781dc
wip
benjaminpetit c0f937b
Split test into a runner for reusability; make them pass faster; make
benjaminpetit File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
149 changes: 149 additions & 0 deletions
149
src/Azure/Orleans.ScheduledJobs.AzureStorage/AzureStorageJobShard.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.IO; | ||
| using System.Text.Json; | ||
| using System.Threading.Tasks; | ||
| using Azure; | ||
| using Azure.Storage.Blobs; | ||
| using Azure.Storage.Blobs.Models; | ||
| using Azure.Storage.Blobs.Specialized; | ||
| using Orleans.Runtime; | ||
|
|
||
| namespace Orleans.ScheduledJobs.AzureStorage; | ||
|
|
||
| internal sealed class AzureStorageJobShard : JobShard | ||
| { | ||
| internal AppendBlobClient BlobClient { get; init; } | ||
| internal ETag? ETag { get; private set; } | ||
|
|
||
| private InMemoryJobQueue _jobQueue; | ||
| private int _jobCount = 0; | ||
|
|
||
| public AzureStorageJobShard(string id, DateTimeOffset startTime, DateTimeOffset endTime, AppendBlobClient blobClient) | ||
| : base(id, startTime, endTime) | ||
| { | ||
| BlobClient = blobClient; | ||
| _jobQueue = new InMemoryJobQueue(); | ||
| } | ||
|
|
||
| public override ValueTask<int> GetJobCount() | ||
| { | ||
| return ValueTask.FromResult(_jobCount); | ||
| } | ||
|
|
||
| public override IAsyncEnumerable<IScheduledJob> ConsumeScheduledJobsAsync() | ||
| { | ||
| return _jobQueue; | ||
| } | ||
|
|
||
| public override async Task RemoveJobAsync(string jobId) | ||
| { | ||
| var operation = JobOperation.CreateRemoveOperation(jobId); | ||
|
benjaminpetit marked this conversation as resolved.
|
||
| await AppendOperation(operation); | ||
| _jobQueue.CancelJob(jobId); | ||
| _jobCount--; | ||
| } | ||
|
|
||
| public override async Task<IScheduledJob> ScheduleJobAsync(GrainId target, string jobName, DateTimeOffset dueTime) | ||
| { | ||
| if (IsComplete) | ||
| throw new InvalidOperationException("Cannot schedule job on a complete shard."); | ||
|
|
||
| var jobId = Guid.NewGuid().ToString(); | ||
| var operation = JobOperation.CreateAddOperation(jobId, jobName, dueTime, target); | ||
| await AppendOperation(operation); | ||
| _jobCount++; | ||
| var job = new ScheduledJob | ||
| { | ||
| Id = jobId, | ||
| Name = jobName, | ||
| DueTime = dueTime, | ||
| TargetGrainId = target, | ||
| ShardId = Id | ||
| }; | ||
| _jobQueue.Enqueue(job); | ||
| return job; | ||
| } | ||
|
|
||
| private async Task AppendOperation(JobOperation operation) | ||
| { | ||
| var content = BinaryData.FromObjectAsJson(operation).ToString() + Environment.NewLine; | ||
|
benjaminpetit marked this conversation as resolved.
Outdated
|
||
| using var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(content)); | ||
| var result = await BlobClient.AppendBlockAsync( | ||
|
ReubenBond marked this conversation as resolved.
Outdated
|
||
| stream, | ||
| new AppendBlobAppendBlockOptions { Conditions = new AppendBlobRequestConditions { IfMatch = ETag } }); | ||
| ETag = result.Value.ETag; | ||
| } | ||
|
|
||
| public async ValueTask InitializeAsync() | ||
| { | ||
| if (ETag is not null) return; // already initialized | ||
|
|
||
| // Load existing blob | ||
| var response = await BlobClient.DownloadAsync(); | ||
| using var stream = response.Value.Content; | ||
| using var reader = new StreamReader(stream); | ||
|
|
||
| // Rebuild state by replaying operations | ||
| var dictionary = new Dictionary<string, JobOperation>(); | ||
| while (!reader.EndOfStream) | ||
| { | ||
| var line = await reader.ReadLineAsync(); | ||
| if (string.IsNullOrWhiteSpace(line)) continue; | ||
| var operation = JsonSerializer.Deserialize<JobOperation>(line); | ||
| switch (operation.Type) | ||
| { | ||
| case JobOperation.OperationType.Add: | ||
| dictionary[operation.Id] = operation; | ||
| break; | ||
| case JobOperation.OperationType.Remove: | ||
| dictionary.Remove(operation.Id); | ||
| break; | ||
| } | ||
| } | ||
| // Rebuild the priority queue | ||
| foreach (var op in dictionary.Values) | ||
| { | ||
| _jobQueue.Enqueue(new ScheduledJob | ||
| { | ||
| Id = op.Id, | ||
| Name = op.Name!, | ||
| DueTime = op.DueTime!.Value, | ||
| TargetGrainId = op.TargetGrainId!.Value, | ||
| ShardId = Id | ||
| }); | ||
| } | ||
| _jobCount = dictionary.Count; | ||
|
|
||
| ETag = response.Value.Details.ETag; | ||
| } | ||
|
|
||
| public override Task MarkAsComplete() | ||
| { | ||
| IsComplete = true; | ||
| _jobQueue.MarkAsComplete(); | ||
| return Task.CompletedTask; | ||
| } | ||
| } | ||
|
|
||
| internal struct JobOperation | ||
| { | ||
| public enum OperationType | ||
| { | ||
| Add, | ||
| Remove | ||
| } | ||
|
|
||
| public OperationType Type { get; init; } | ||
|
|
||
| public string Id { get; init; } | ||
| public string? Name { get; init; } | ||
| public DateTimeOffset? DueTime { get; init; } | ||
| public GrainId? TargetGrainId { get; init; } | ||
|
|
||
| public static JobOperation CreateAddOperation(string id, string name, DateTimeOffset dueTime, GrainId targetGrainId) => | ||
| new() { Type = OperationType.Add, Id = id, Name = name, DueTime = dueTime, TargetGrainId = targetGrainId }; | ||
|
|
||
| public static JobOperation CreateRemoveOperation(string id) => | ||
| new() { Type = OperationType.Remove, Id = id }; | ||
| } | ||
154 changes: 154 additions & 0 deletions
154
src/Azure/Orleans.ScheduledJobs.AzureStorage/AzureStorageJobShardManager.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Threading.Tasks; | ||
| using Azure; | ||
| using Azure.Storage.Blobs; | ||
| using Azure.Storage.Blobs.Models; | ||
| using Azure.Storage.Blobs.Specialized; | ||
| using Microsoft.Extensions.Options; | ||
| using Orleans.Runtime; | ||
|
|
||
| namespace Orleans.ScheduledJobs.AzureStorage; | ||
|
|
||
| public sealed class AzureStorageJobShardManager : JobShardManager | ||
| { | ||
| private readonly BlobServiceClient _blobServiceClient; | ||
| private readonly string _containerName; | ||
| private BlobContainerClient _client = null!; | ||
| private readonly TimeSpan _maxShardDuration; | ||
| private readonly IClusterMembershipService _clusterMembership; | ||
|
|
||
| public AzureStorageJobShardManager(BlobServiceClient client, string containerName, TimeSpan maxShardDuration, IClusterMembershipService clusterMembership) | ||
| { | ||
| _blobServiceClient = client; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we ever need to support multiple concurrent accounts? For throughput / failover / etc? |
||
| _containerName = containerName; | ||
| _maxShardDuration = maxShardDuration; | ||
| _clusterMembership = clusterMembership; | ||
| } | ||
|
|
||
| public AzureStorageJobShardManager(IOptions<AzureStorageJobShardOptions> options, IClusterMembershipService clusterMembership) | ||
| : this(options.Value.BlobServiceClient, options.Value.ContainerName, options.Value.MaxShardDuration, clusterMembership) | ||
| { | ||
| } | ||
|
|
||
| public override async Task<List<JobShard>> GetJobShardsAsync(SiloAddress siloAddress, DateTimeOffset maxDateTime) | ||
| { | ||
| await InitializeIfNeeded(); | ||
| var blobs = _client.GetBlobsAsync(traits: BlobTraits.Metadata); | ||
| var result = new List<JobShard>(); | ||
| await foreach (var blob in blobs) | ||
| { | ||
| // Get the owner of the shard | ||
| var (owner, minDueTime, maxDueTime) = ParseMetadata(blob.Metadata); | ||
|
|
||
| if (minDueTime > maxDateTime) | ||
|
benjaminpetit marked this conversation as resolved.
Outdated
|
||
| { | ||
|
benjaminpetit marked this conversation as resolved.
|
||
| // This shard is too new, stop there | ||
| break; | ||
| } | ||
|
|
||
| if (owner == null || _clusterMembership.CurrentSnapshot.GetSiloStatus(owner) == SiloStatus.Dead) | ||
| { | ||
| // The owner is dead or unknown, we can take over this shard | ||
| var blobClient = _client.GetAppendBlobClient(blob.Name); | ||
| var metadata = blob.Metadata; | ||
| metadata["Owner"] = siloAddress.ToParsableString(); | ||
| try | ||
| { | ||
| await blobClient.SetMetadataAsync(metadata, conditions: new BlobRequestConditions { IfMatch = blob.Properties.ETag }); | ||
| } | ||
| catch (RequestFailedException) | ||
| { | ||
| // Someone else took over the shard | ||
| continue; | ||
| } | ||
| var shard = new AzureStorageJobShard(blob.Name, minDueTime, maxDueTime, blobClient); | ||
| await shard.InitializeAsync(); | ||
| await shard.MarkAsComplete(); | ||
|
benjaminpetit marked this conversation as resolved.
Outdated
|
||
| result.Add(shard); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| public override async Task<JobShard> RegisterShard(SiloAddress siloAddress, DateTimeOffset minDueTime, DateTimeOffset maxDueTime, IDictionary<string, string> metadata) | ||
| { | ||
| await InitializeIfNeeded(); | ||
| for (var i = 0;; i++) // TODO limit the number of attempts | ||
|
ReubenBond marked this conversation as resolved.
Outdated
|
||
| { | ||
| var shardId = $"{minDueTime:yyyyMMddHHmm}-{siloAddress.ToParsableString()}-{i}"; | ||
| var blobClient = _client.GetAppendBlobClient(shardId); | ||
| var metadataInfo = CreateMetadata(siloAddress, minDueTime, maxDueTime); | ||
| try | ||
| { | ||
| var response = await blobClient.CreateIfNotExistsAsync(metadata: metadataInfo); | ||
| if (response == null) | ||
| { | ||
| // Blob already exists, try again with a different name | ||
| continue; | ||
| } | ||
| } | ||
| catch (RequestFailedException) | ||
| { | ||
| if (i > 100) throw; // Prevent infinite loop | ||
| // Blob already exists, try again with a different name | ||
| continue; | ||
| } | ||
| var shard = new AzureStorageJobShard(shardId, minDueTime, maxDueTime, blobClient); | ||
| await shard.InitializeAsync(); | ||
| return shard; | ||
| } | ||
| } | ||
|
|
||
| public override async Task UnregisterShard(SiloAddress siloAddress, JobShard shard) | ||
| { | ||
| var azureShard = shard as AzureStorageJobShard ?? throw new ArgumentException("Shard is not an AzureStorageJobShard", nameof(shard)); | ||
| var conditions = new BlobRequestConditions { IfMatch = azureShard.ETag }; | ||
| var count = await shard.GetJobCount(); | ||
| var properties = await azureShard.BlobClient.GetPropertiesAsync(conditions); | ||
| if (count > 0) | ||
| { | ||
| // There are still jobs in the shard, unregister it | ||
|
benjaminpetit marked this conversation as resolved.
|
||
| var metadata = properties.Value.Metadata; | ||
| var (owner, _, _) = ParseMetadata(metadata); | ||
|
|
||
| if (owner != siloAddress) | ||
| throw new InvalidOperationException("Cannot unregister a shard owned by another silo"); | ||
|
|
||
| metadata.Remove("Owner"); | ||
| var response = await azureShard.BlobClient.SetMetadataAsync(metadata, conditions); | ||
| } | ||
| else | ||
| { | ||
| // No jobs left, we can delete the shard | ||
| await azureShard.BlobClient.DeleteIfExistsAsync(conditions: conditions); | ||
| } | ||
| } | ||
|
|
||
| private ValueTask InitializeIfNeeded() | ||
| { | ||
| if (_client != null) return ValueTask.CompletedTask; | ||
|
|
||
| _client = _blobServiceClient.GetBlobContainerClient(_containerName); | ||
| _client.CreateIfNotExists(); | ||
|
benjaminpetit marked this conversation as resolved.
Outdated
|
||
| return ValueTask.CompletedTask; | ||
| } | ||
|
|
||
| private static Dictionary<string, string> CreateMetadata(SiloAddress siloAddress, DateTimeOffset minDueTime, DateTimeOffset maxDueTime) | ||
| { | ||
| return new Dictionary<string, string> | ||
| { | ||
| { "Owner", siloAddress.ToParsableString() }, | ||
| { "MinDueTime", minDueTime.ToString("o") }, | ||
| { "MaxDueTime", maxDueTime.ToString("o") } | ||
| }; | ||
| } | ||
|
|
||
| private static (SiloAddress? owner, DateTime minDueTime, DateTime maxDueTime) ParseMetadata(IDictionary<string, string> metadata) | ||
| { | ||
| var owner = metadata.TryGetValue("Owner", out var ownerStr) ? SiloAddress.FromParsableString(ownerStr) : null; | ||
| var minDueTime = metadata.TryGetValue("MinDueTime", out var minDueTimeStr) && DateTime.TryParse(minDueTimeStr, out var minDt) ? minDt : DateTime.MinValue; | ||
| var maxDueTime = metadata.TryGetValue("MaxDueTime", out var maxDueTimeStr) && DateTime.TryParse(maxDueTimeStr, out var maxDt) ? maxDt : DateTime.MaxValue; | ||
| return (owner, minDueTime.ToUniversalTime(), maxDueTime.ToUniversalTime()); | ||
| } | ||
| } | ||
22 changes: 22 additions & 0 deletions
22
src/Azure/Orleans.ScheduledJobs.AzureStorage/AzureStorageJobShardOptions.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| using System; | ||
| using Azure.Storage.Blobs; | ||
|
|
||
| namespace Orleans.ScheduledJobs.AzureStorage; | ||
|
|
||
| public class AzureStorageJobShardOptions | ||
| { | ||
| /// <summary> | ||
| /// The maximum duration of a job shard. | ||
| /// </summary> | ||
| public TimeSpan MaxShardDuration { get; set; } = TimeSpan.FromHours(1); | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the <see cref="BlobServiceClient"/> instance used to store job shards. | ||
| /// </summary> | ||
| public BlobServiceClient BlobServiceClient { get; set; } = null!; | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the name of the container used to store scheduled jobs. | ||
| /// </summary> | ||
| public string ContainerName { get; set; } = "scheduled-jobs"; | ||
|
benjaminpetit marked this conversation as resolved.
Outdated
|
||
| } | ||
24 changes: 24 additions & 0 deletions
24
src/Azure/Orleans.ScheduledJobs.AzureStorage/Orleans.ScheduledJobs.AzureStorage.csproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <PackageReadmeFile>README.md</PackageReadmeFile> | ||
| <PackageId>Microsoft.Orleans.ScheduledJobs.AzureStorage</PackageId> | ||
| <Title>Microsoft Orleans Azure Storage ScheduledJobs Provider</Title> | ||
| <Description>Microsoft Orleans reminders provider backed by Azure Storage</Description> | ||
| <PackageTags>$(PackageTags) Azure Storage</PackageTags> | ||
| <TargetFrameworks>$(DefaultTargetFrameworks)</TargetFrameworks> | ||
| <AssemblyName>Orleans.ScheduledJobs.AzureStorage</AssemblyName> | ||
| <RootNamespace>Orleans.ScheduledJobs.AzureStorage</RootNamespace> | ||
| <OrleansBuildTimeCodeGen>true</OrleansBuildTimeCodeGen> | ||
| <DefineConstants>$(DefineConstants);ORLEANS_REMINDERS</DefineConstants> | ||
|
benjaminpetit marked this conversation as resolved.
Outdated
|
||
| <Nullable>enable</Nullable> | ||
| </PropertyGroup> | ||
|
benjaminpetit marked this conversation as resolved.
|
||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="$(SourceRoot)src\Orleans.Runtime\Orleans.Runtime.csproj" /> | ||
| <ProjectReference Include="$(SourceRoot)src\Orleans.ScheduledJobs\Orleans.ScheduledJobs.csproj" /> | ||
| <PackageReference Include="Azure.Core" /> | ||
| <PackageReference Include="Azure.Storage.Blobs" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.