Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if grain exists by GUID in Microsoft Orleans

Tags:

orleans

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;
    }
like image 432
4imble Avatar asked May 02 '17 18:05

4imble


1 Answers

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:

  1. The grain has never been activated, but you're expecting that it has. Eg: I'm calling a method on IPlayerGrain, but the player doesn't exist.
  2. The grain has been activated previously, but you're expecting that it hasn't. Eg: I'm trying to create a new game for this player, but that id has already been taken.

In the code sample below, you can see both cases:

  1. The call to 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.
  2. The call to 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.

like image 189
Reuben Bond Avatar answered Sep 21 '22 12:09

Reuben Bond