How do you check if a grain exists already for a particular Id?
Considering the below will create a new player grain with the Id I pass into GetGrain()
if it doesn't exist, I am not sure how to check if one already exists.
public async Task<Guid> Create(Guid playerGuid)
{
var player = GrainClient.GrainFactory.GetGrain<IPlayerGrain>(playerGuid);
var gameGuid = await player.CreateGame();
return gameGuid;
}
The short answer is to store some state so the grain knows whether or not it has been activated previously.
Grains in Orleans are never explicitly created or destroyed: they are always available to handle requests. So, technically, the concept of whether or not a grain exists does not apply to Orleans. On the other hand, we can ask "has a grain with this id ever been activated before".
There are two cases you might want to check:
IPlayerGrain
, but the player doesn't exist.In the code sample below, you can see both cases:
IPlayerGrain.CreateGame()
will throw an exception if the player has never been created before. Ignore the fact that I'm never setting Created
, that could be done in some CreatePlayer(...)
method.IGameGrain.TryCreateGame(player)
returns false if the game has already been created. In that case, IPlayerGrain.CreateGame()
will continue looping until it's found a game which hasn't been created. With a Guid
id it's unlikely that you'll ever see a collision, but I understand the desire to be cautious - just incase the stars align and consipire against you.public interface IPlayerGrain : IGrainWithGuidKey
{
Task<Guid> CreateGame();
}
public class PlayerState
{
public bool Created { get; set; }
}
public class PlayerGrain : Grain<PlayerState>, IPlayerGrain
{
public async Task<Guid> CreateGame()
{
if (!this.State.Created)
throw new InvalidOperationException("Player does not exist.");
var thisPlayer = this.AsReference<IPlayerGrain>();
var created = false;
var gameId = default(Guid);
while (!created)
{
// Get a new, random game grain
gameId = Guid.NewGuid();
// Try to create a game.
created = await this.GrainFactory.GetGrain<IGameGrain>(gameId)
.TryCreateGame(thisPlayer);
// If the game was successfully created, break out and return the id.
// Otherwise, keep looping.
}
return gameId;
}
}
public interface IGameGrain : IGrainWithGuidKey
{
// Returns true if game was created, false otherwise.
Task<bool> TryCreateGame(IPlayerGrain player);
}
public class GameState
{
public IPlayerGrain Player { get; set; }
}
public class GameGrain : Grain<GameState>, IGameGrain
{
public async Task<bool> TryCreateGame(IPlayerGrain player)
{
// If this grain already has a player, return false.
if (this.State.Player != null) return false;
// Otherwise, set the player, write it to storage, and return true.
this.State.Player = player;
await this.WriteStateAsync();
return true;
}
}
The Create
method in your question does not need to change.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With