How to properly utilize UpdateAsync

A lot of people have made posts on how to convert their systems to use UpdateAsync because UpdateAsync is better according to this post by @ForeverHD.

This post has a lot of already existing opinions and it does share a lot of what I am gonna say, however I’ll try packing in more updated and more specific details.


What is different with :UpdateAsync?

First of all, UpdateAsync will try to re-run calls if the set call is done after another set call happened, until every UpdateAsync call was done in order.

UpdateAsync is a smart Get-Set combo as stated by colbert in the replies of FHD’s topic.

A UpdateAsync call will accept a key, and a function.

This function will be ran with the first argument being the data that is currently there, you can use that data for comparation, getting data from it… etc.

Example:


ds:UpdateAsync("userId", function(pastData)
    -- compare past data, get stuff from it, etc.
end)


From this call, you have to return back the data you wanna save, in case you return nil, it will keep the data as is, and not save and not do any write requests.

Another example:


local newData = {
    Cash = 100;
}

DataStore:UpdateAsync(player.UserId, function(pastData)
    return newData
end)

It will now save the new data, yes!
…but it doesn’t seem to do much different.


What should I use UpdateAsync with?

If you didn’t notice already, you should be saving data with tables and making everything save under one key, if it’s related data. If you’re not doing that, learn how to do that before thing to understand this. UpdateAsync has no use for people who save data separately.


UpdateAsync, should be used to compare data, and decide if it should be overwrite it or not.

A simple example, but pretty good and popular example is to compare version numbers.

An example would be:


ds:UpdateAsync(key, function(pastData)
    if pastData.DataVersion ~= player.DataVersion.Value then
        return nil --// Cancels saving data, doesn't do a write request.
    end

    return {
        --// Data is safe to overwrite!
        DataVersion = player.DataVersion.Value + 1
    }
end)

This example is showing how you should keep a number which increases every save.

This number gets compared when trying to save, it needs to be the save as the number we got when we loaded data.

If it isn’t the same, don’t try saving any new data. Cancel that!

This example prevents trying to write data twice (overwriting).
This tecnic is popular and you should use it in your data saving system if UpdateAsync calls are being used!


There’s also tecnics like session locking, which use UpdateAsync in everything to achieve their effect, it’s something that’s really nice to look into.

When should I not use UpdateAsync?

This is pretty simple.

  • If you have anything that Is only ever written to once, and that overwriting doesn’t matter
  • Leaderboards (the same for any OrderedDataStore use)
  • Just data that doesn’t overall matter.

If you’re using leaderboards, PLEASE DON’T USE UPDATEASYNC.

I would like to point that out because I see so many people blindly recommending using UpdateAsync in everything for no reason.

Leaderboards only mimic data! You should never care about data loss in a leaderboard anyway. Ordered DataStores can only save numbers anyway! You’re not even comparing ANYTHING.


Don’t use UpdateAsync calls for things that are supposed to be written to only once.

If you’re saving data that isn’t a table anyway, for the most part, you’re not able to compare data, therefore UpdateAsync turns useless.


Additional info:

  1. Don’t yield (task.wait, Async calls, etc) inside UpdateAsync functions.
  2. Try comparing the most facts about data you can, things that wouldn’t cause an issue comparing, but would if something’s wrong.
46 Likes

Huh?

I don’t quite understand what’s that since I never found that property…

I still have no idea how I can use UpdateAsync.

1 Like

It really depends on what you’re doing. If it’s basic player saving data, yes probably you should use UpdateAsync.

UpdateAsync is simply a get and set combo, you can compare the data and DECIDE if you wanna save it.

There’s where the entire comparing “versions” comes from.

If you need help with the actual API and how to use it, you can send me a DM, i don’t have anything to do :P

How did you know there’s this property?

1 Like

This is not a property inside normal datastores, that’s something you add to the table you’re saving, this number increases every time you save, when saving that number should be the same as the one when you loaded the data in the first place. If it is the same, save everything and increase that number by 1. So it’s more of a “tecnic”.

So by any chance, if this value somehow gets updated, that’s why we use conditional statement to check if it’s correct?

Yes, in this case you only save in case that value is the same as when you loaded the data.

So an example would be like:

— checking/loading data

if result then
    — load data
else
    — retry and stuffs
end

— saving data

local tableData = {
    ["Version"] = plr.leaderstats.Version.Value
    — your data
}
DS:UpdateAsync(plr.UserId, function(pastData)
    if pastData.Version ~= tableData["Version"] then
        warn("Data Version is not the same.")
        return nil
    else
        tableData["Version"] += 1
        return tableData
    end
end)
2 Likes

Exactly! Only thing you would do differently here is not put the value inside leaderstats, I would just keep it in the player. (You probably know that already)

You mean I shouldn’t put the Version inside the leaderstats?

Yes, you should just keep it inside the player object, not leaderstats. Anything on leaderstats shows up to everyone on the playerlist so.

1 Like

Thank you. This tutorial finally gave me a great clue on how to use UpdateAsync(). Thank you so much.

gonna bookmark this

2 Likes

One last question, if the version doesn’t match, what should I do before I return nil! Because if I just return nil after the player leaves the game and then I code it to save data on exit, the player will be mad that their data didn’t save… so how can I make sure?

If you’re auto saving, then I would kick the player. On player leaving, I would have just not tried saving.

1 Like

Thing is, the data would probably have been already saved. If it was different, that very likely means that maybe it was saved already, that the data was written to in a way.

This line wont work if the past data is nil (player joins in for the first time), so it will always error.


Other thing for update async, generally I like to put in a check to see if their current value is greater than their previous. This means that if for some reason they have lost their data, it doesn’t get overridden

local success, err = DataStore:UpdateAsync(key, function(old)
    if (not old) or (player.DataVersion.Value > old.DataVersion.Value)
        return {player.DataVersion.Value};
    else
        warn("PlayerData update error"); 
        return nil;
    end
end)

I would also create some other datastore functions. One to reduce player data, and one to reset/set player data (with setAsync, That’s the point of set async)

1 Like

Yes. That’s why it’s pseudo-code.

That’s just wrong… that tecnic is that you don’t overwrite data, you’re assuming the data is already overwritten? That’s pretty odd. This “tecnic” is mostly so that BindToClose() calls for example, don’t save twice from PlayerRemoving. The only thing way is to compare if it’s the same in this case.

This tutorial is not as informative, since tons of resources have explained how to properly use UpdateAsync, though it would be good for an beginner to understand how to properly save data. This tutorial also doesn’t explain how to efficiently load data with UpdateAsync, nor tells how it respects “incoming calls”. Instead this tutorial; Stop using SetAsync explains it all, this tutorial for the most part has no point or usefulness except the only thing you did was just re-phrase the old tutorial.

This function will be ran with the first argument being the data that is currently there, you can use that data for comparation, getting data from it… etc.

This is incorrect, the callback will always be called despite if there was no data for that key, it will only be not be called is when UpdateAsync fails.

This is incorrect, I didn’t say it wouldn’t run if data wasn’t there.

If data is nil, it’s still gonna get “passed as an argument” obviously.

I do, I don’t talk about it’s internal system or something, no one knows that but:

Anyways it’s supposed to be for beginners for basic use, and the post you linked is a big vague on how it works for anyone trying to understand. I still see A LOT of newer scripters NOT knowing where to use it, and blindly recommend it for random stuff.

1 Like

Even so, when creating a tutorial try to show things to people. Even just the most basic of things like checking if the data is nil will make a difference for people trying to use the code on their own. (Last thing you want is people copying your code and they can’t figure out what’s wrong)
The tutorial you linked made sure to add the effort of checking for nil, you should do the same!


Why? It still wouldn’t override data if the data is the same, because I’m using greater than rather than greater than equal. All this is doing is adding an addition check to make sure player data doesn’t get overridden in the case where for instance datastore:GetAsync() doesn’t work when the player enters the game, but UpdateAsync() works when the player leaves the game.

Example:
Player joins, GetAsync() fails, so player’s coins are 0
Then player leaves the server a little while later, and UpdateAsync() works, setting the player’s coins to 0
Now when the player joins a new server expecting their 100million coins, they are so angry and quit because they always get 0 coins when they join the server

1 Like