Logger
-
-@Data - @RendererInfo.Name
-
-
-
-
-
-
-@code {
-
- private bool doingStuff = false;
-
- private PersistingComponentStateSubscription persistingSubscription;
-
- public sealed class FormInput
- {
- public required string Text { get; set; }
- public required string[] Existing { get; set; }
- }
-
- [SupplyParameterFromForm] public FormInput Item { get; set; } = new()
- {
- Text = string.Empty,
- Existing = []
- };
-
- public Guid Data = Guid.NewGuid();
-
- protected override Task OnInitializedAsync()
- {
- persistingSubscription =
- ApplicationState.RegisterOnPersisting(PersistData);
-
- if (!ApplicationState.TryTakeFromJson("TOKEN", out Data))
- {
- Data = Guid.NewGuid();
- Logger.LogInformation("Data needs to be persisted! {Data}", Data);
- }
- else
- {
- Logger.LogInformation("Using persisted DATA");
- }
-
- return Task.CompletedTask;
- }
-
- private Task PersistData()
- {
- Logger.LogInformation("Persisting! {Data}", Data);
- ApplicationState.PersistAsJson("TOKEN", Data);
-
- return Task.CompletedTask;
- }
-
- private async Task Callback()
- {
- Item = new FormInput
- {
- Text = string.Empty,
- Existing = [..Item.Existing, Item.Text, RendererInfo.Name]
- };
- }
-}
\ No newline at end of file
diff --git a/Pushy/Pushy/Components/Pages/Home.razor b/Pushy/Pushy/Components/Pages/Home.razor
index d382f59..9a41617 100644
--- a/Pushy/Pushy/Components/Pages/Home.razor
+++ b/Pushy/Pushy/Components/Pages/Home.razor
@@ -27,8 +27,8 @@
@{
string key = CreatedItems.GetPrimaryKeyString();
}
- @Nav.ToAbsoluteUri($"/t/{key}")
-
+ @Nav.ToAbsoluteUri($"/t/{key}")
+
}
@@ -38,19 +38,21 @@
[Inject] public ILogger Logger { get; set; } = null!;
- [Inject] public LinkGenerator ItemGenerator { get; set; } = null!;
+ [Inject] public ILinkGenerator ItemGenerator { get; set; } = null!;
- [Inject] public IClusterClient ClusterClient { get; set; } = null!;
+ [Inject] public IGrainFactory ClusterClient { get; set; } = null!;
public ITextItem? CreatedItems;
private async Task SubmitInput()
{
- ITextItem item = await ItemGenerator.GenerateTextShare(Form.InputText);
-
- CreatedItems = item;
-
- Form = new UploadForm();
+ LinkResult result = await ItemGenerator.GenerateTextShare(Form.InputText);
+
+ if (result.Item is not null)
+ {
+ CreatedItems = result.Item;
+ Form = new UploadForm();
+ }
Logger.LogInformation("A new item was created!");
}
diff --git a/Pushy/Pushy/Grains/LinkGenerator.cs b/Pushy/Pushy/Grains/LinkGenerator.cs
new file mode 100644
index 0000000..0b975aa
--- /dev/null
+++ b/Pushy/Pushy/Grains/LinkGenerator.cs
@@ -0,0 +1,49 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace Pushy.Grains;
+
+[Immutable]
+[GenerateSerializer]
+[Alias("Pushy.Grains.LinkResult")]
+public record LinkResult(ITextItem? Item);
+
+public interface ILinkGenerator : IGrainWithGuidKey
+{
+ [Alias("GenerateLink")]
+ public ValueTask GenerateTextShare([Immutable] string text);
+}
+
+public sealed class LinkGenerator : IGrainBase, ILinkGenerator
+{
+ private readonly ILogger _logger;
+ private readonly IClusterClient _clusterClient;
+
+ public IGrainContext GrainContext { get; }
+
+ public LinkGenerator(
+ IGrainContext grainContext,
+ IClusterClient clusterClient,
+ ILogger logger)
+ {
+ _clusterClient = clusterClient;
+ _logger = logger;
+ GrainContext = grainContext;
+ }
+
+ public async ValueTask GenerateTextShare(string text)
+ {
+ string item = $"{Guid.CreateVersion7():N}"[9..];
+ var textGrain = _clusterClient.GetGrain(item);
+ try
+ {
+ await textGrain.SetText(text);
+ return new LinkResult(textGrain);
+ }
+ catch (Exception)
+ {
+ //TODO: Write log message of why it fails?
+ //Swallow small change of error
+ return await GenerateTextShare(text);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Pushy/Pushy/Grains/TextItemGrain.cs b/Pushy/Pushy/Grains/TextItemGrain.cs
index 590e273..1d926b4 100644
--- a/Pushy/Pushy/Grains/TextItemGrain.cs
+++ b/Pushy/Pushy/Grains/TextItemGrain.cs
@@ -2,6 +2,13 @@ using Orleans.Concurrency;
namespace Pushy.Grains;
+[GenerateSerializer]
+public enum TextSetResult
+{
+ Failure,
+ Success
+}
+
public sealed class TextItem
{
public string? Text { get; set; }
@@ -9,11 +16,8 @@ public sealed class TextItem
public interface ITextItem : IGrainWithStringKey
{
- [Alias("IsAvailable")]
- public ValueTask IsAvailable();
-
[Alias("SetText")]
- public ValueTask SetText(string text);
+ public ValueTask SetText(string text);
[ReadOnly]
[return: Immutable]
@@ -21,44 +25,51 @@ public interface ITextItem : IGrainWithStringKey
ValueTask GetText();
}
-public sealed class TextItemGrain : Grain, ITextItem, IRemindable
+public sealed class TextItemGrain : IGrainBase, ITextItem, IRemindable
{
private readonly IPersistentState _state;
private IGrainReminder? _reminder;
public TextItemGrain(
+ IGrainContext grainContext,
[PersistentState("text")] IPersistentState state)
{
_state = state;
+ GrainContext = grainContext;
}
+ public IGrainContext GrainContext { get; }
+
///
- public override async Task OnActivateAsync(CancellationToken cancellationToken)
+ public async Task OnActivateAsync(CancellationToken cancellationToken)
{
_reminder = await this.RegisterOrUpdateReminder("clear", TimeSpan.FromDays(10), TimeSpan.FromDays(10));
}
///
- public ValueTask IsAvailable()
+ public async ValueTask SetText(string text)
{
- return ValueTask.FromResult(_state.State.Text is null);
- }
+ try
+ {
+ if (_state.State.Text is not null)
+ return TextSetResult.Failure;
- ///
- public async ValueTask SetText(string text)
- {
- if(_state.State.Text is not null)
- throw new InvalidOperationException("Text is already set");
-
- _state.State.Text = text;
- await _state.WriteStateAsync();
+ _state.State.Text = text;
+ await _state.WriteStateAsync();
+
+ return TextSetResult.Success;
+ }
+ catch (Exception)
+ {
+ return TextSetResult.Failure;
+ }
}
///
public ValueTask GetText()
{
- return ValueTask.FromResult(_state.State.Text!);
+ return ValueTask.FromResult(_state.State.Text ?? throw new InvalidOperationException("No text was available"));
}
///
@@ -68,6 +79,7 @@ public sealed class TextItemGrain : Grain, ITextItem, IRemindable
{
_state.State.Text = null;
await _state.WriteStateAsync();
+ this.DeactivateOnIdle();
}
}
}
\ No newline at end of file
diff --git a/Pushy/Pushy/LinkGenerator.cs b/Pushy/Pushy/LinkGenerator.cs
deleted file mode 100644
index 600bc1e..0000000
--- a/Pushy/Pushy/LinkGenerator.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System.Security.Cryptography;
-using System.Text;
-using Pushy.Grains;
-
-namespace Pushy;
-
-public sealed class LinkGenerator
-{
- private readonly ILogger _logger;
- private readonly IClusterClient _clusterClient;
-
- public LinkGenerator(
- IClusterClient clusterClient,
- ILogger logger)
- {
- _clusterClient = clusterClient;
- _logger = logger;
- }
-
- public async Task GenerateTextShare(string text)
- {
- string item = $"{Guid.CreateVersion7():N}"[6..];
- var sharedGrain = _clusterClient.GetGrain(item);
- if (await sharedGrain.IsAvailable())
- {
- await sharedGrain.SetText(text);
- return sharedGrain;
- }
-
- return await GenerateTextShare(text);
- }
-}
\ No newline at end of file
diff --git a/Pushy/Pushy/Program.cs b/Pushy/Pushy/Program.cs
index c96bb75..d329835 100644
--- a/Pushy/Pushy/Program.cs
+++ b/Pushy/Pushy/Program.cs
@@ -3,8 +3,8 @@ using Elastic.Extensions.Logging.Options;
using Elastic.Transport;
using Microsoft.AspNetCore.DataProtection;
using Pushy.Components;
+using Pushy.Grains;
using StackExchange.Redis;
-using LinkGenerator = Pushy.LinkGenerator;
var builder = WebApplication.CreateBuilder(args);
@@ -51,7 +51,8 @@ if (builder.Environment.IsProduction())
});
}
-builder.Services.AddScoped();
+builder.Services.AddScoped(service =>
+ service.GetRequiredService().GetGrain(Guid.Empty));
builder.Services.AddAllElasticApm();
builder.Services.AddHealthChecks();
diff --git a/Pushy/Pushy/Pushy.csproj b/Pushy/Pushy/Pushy.csproj
index b7ef52c..b4c3193 100644
--- a/Pushy/Pushy/Pushy.csproj
+++ b/Pushy/Pushy/Pushy.csproj
@@ -9,6 +9,11 @@
d8fe2296-80f7-4812-b26a-ccaa6167a6e1
+
+ true
+ true
+
+
@@ -23,6 +28,7 @@
+