Jynn
Jynn

Reputation: 287

Raven query only works sometimes

We've been having a problem where the following method which queries a raven db works, but only about 90% of the time

 member.UserId = userService.GivenUsernameGetUserId(command.EmailAddress.ToLower());

to counteract this, I made this ugly hack workaround which does seem to have fixed the problem:

member.UserId = userService.GivenUsernameGetUserId(command.EmailAddress.ToLower());
System.Threading.Thread.Sleep(1000);
if (member.UserId.IsNullOrEmpty())
{
    logger.Error("the userid was not loaded in time");
    for (int i = 0; i < 5; i++)
    {                          
        member.UserId = userService.GivenUsernameGetUserId(command.EmailAddress.ToLower());
        System.Threading.Thread.Sleep(1000);
        if (member.UserId.IsNotNullOrEmpty())
        {
            logger.Info("The userid was retrieved in a loop after some delay ");
            break;
        }
    }
    if (member.UserId.IsNullOrEmpty())
    {
        logger.Error("a loop of 5 cycles was run trying to retrieve the userId but couldn't get it.");
    }
}

Can anyone see why it might only be retrieving the correct data sometimes and whether there's a more elegant solution to making sure it keeps trying until it retrieves the data? I'm thinking whether there's some basic timeout setting that can be set in web.config or something?

Upvotes: 2

Views: 409

Answers (2)

yBother
yBother

Reputation: 718

I am using Raven DB 3.5 and using mentioned Option 3 This does work but I encountered a problem using this approach: In a specific use case this operation would take about 60 seconds to complete. I'd not recommend to make general use of WaitForIndexesAfterSaveChanges() as it obviously can lead to tremendous performance issues. Instead I'd configure queries using WaitForNonStaleResultsAsOfNow().

Upvotes: 0

Judah Gabriel Himango
Judah Gabriel Himango

Reputation: 60001

The issue is likely stale indexes: the user has been recently created, and the indexes haven't had a chance to update. (Usually this takes milliseconds, but on a large database it can take longer.)

There are 3 things you can do here to fix your problem:

  • Option 1: Make User Ids based on email address. Then you don't have to mess with indexes at all.
  • Option 2: You can leave user IDs as-is, but wait for non-stale indexes.
  • Option 3: When you create a user, wait for indexes to update.

I'll describe each of these options below:


Option 1:

Make your user IDs well-known, so that you don't have to user indexes at all.

Say your object is called User. When you register the User, your code will look like:

public void RegisterUser(string emailAddress)
{
    var user = new User
    {
        UserName = emailAddress,
        ...
    };

    // Give the User a well-known ID, so that we don't have to mess with indexes later.
    user.Id = "Users/" + emailAddress;

    ravenSession.Store(user);
}

If you do that, you won't have to mess with indexes at all. When it comes time to load your user:

public string GivenUsernameGetUserId(string userName)
{
   // Look ma, no query needed.
   return "Users/" + userName;

   // Or, need to return the User itself? You can use .Load, which will never be stale.
   // return ravenSession.Load<User>("Users/" + userName);
}

This is really your best option, and you never have to deal with indexes, therefore, you'll never have to deal with stale data.


Option 2

Option 2 is to use .WaitForNonStaleResults. It waits for indexes to become up-to-date before returning results.

public string GivenUsernameGetUserId(string userName)
{
    // Use .WaitForNonStaleResultsAsOfNow()
    return ravenSession.Query<User>()
      .Customize(x => x.WaitForNonStaleResultsAsOfNow())
      .Where(u => u.UserName == userName)
      .Select(u => u.Id)
      .FirstOrDefault();
}

Option 3

Option 3 is to wait for indexes to update when saving your user.

This requires Raven 3.5 or greater.

public void RegisterUser(string userName)
{
    ravenSession.Advanced.WaitForIndexesAfterSaveChanges(timeout: TimeSpan.FromSeconds(30));
    var user = new User {...};
    ravenSession.Store(user);
    ravenSession.SaveChanges(); // This won't return until the User is stored *AND* the indexes are updated.
};

Personally, I'd recommend using #1: well-known IDs for your users. Also, I recommend #3 even if you implement other solutions: SaveChanges will wait for the indexes to be updated before returning. This will result in fewer surprises around stale indexes, so I recommend it generally.

Upvotes: 3

Related Questions