How would I go about using UpdateAsync over SetAsync?

Hey, so for a long time I’ve been using SetAsync on a bowling game I’ve been developing, but with some checks so it makes sure the player’s data has loaded before it saves anything.

But I still think it’d be a good and safe practice to use UpdateAsync just to make sure it never overrides any existing data, and I want to assure that my players’ data is always gonna be there.

With that being said, I’m just unsure of how I would convert my current code to use UpdateAsync instead of SetAsync, due to the fact I’m saving a lot of tables and stuff like that, so I don’t really know how I’d go about doing this. I’ll show you guys what I’m saving currently with SetAsync, so here it is:

 ds:SetAsync(p.UserId,{
	bux = stuff.BowlingBucks.Value;
	xp = stuff.XP.Value;
	xpNeeded = stuff.XPNeeded.Value;
	level = stuff.Level.Value;
	activeBalls = {stuff.Ball1.Value,stuff.Ball2.Value,stuff.Ball3.Value,stuff.Ball4.Value,stuff.Ball5.Value,stuff.Ball6.Value};
	maxBalls = stuff.MaxBalls.Value;		
	items = tab;
	ball_conditions = conditions;
	day = stuff.Day.Value;
	last_reward = stuff.LastDailyReward.Value;
	cards = cards_tab;
	bowler = stuff.Bowler.Value;
	collectibles_tab = collectibles;
	achievements = achievements;
	pin_skin = stuff.PinSkin.Value;
	last_offer_purchased = stuff.LastOfferPurchased.Value;
})

Thanks for reading!

2 Likes

It should be as simple as this:

ds:UpdateAsync(p.UserId,function(oldValue)
	return --your table over there
end)
2 Likes

Thanks for the reply. Although I’m just not sure to how to use it in my scenario, because of all the tables I’m saving and if there’s existing values in those tables…and so fourth.

ds:UpdateAsync(p.UserId,function(list)
	list[xp] = list[xp] + 5
    return list
end)

I don’t understand what the issue is. You just handle it like you would any other table with tables inside.

Of course you’d have to check if the table exists (could be nil if they never played before) but you get the idea.

4 Likes

This is pointless and completely misusing UpdateAsync. The only benefit of UpdateAsync over SetAsync is the ability to check the old value. If you don’t care about the old value and aren’t using it in your function, there is no reason at all to use it over SetAsync.

5 Likes
pre-edit so colbert's post still makes sense

UpdateAsync isn’t inherently a better function to use. You should only use it if you’re going to compare the old value to the new value. An example of this being useful is for values like levels and achievements that should never go down, only up. (This is assuming no rebirth system or similar; the exact examples of levels and achievements might not apply to your game.) You could compare the old value to make sure it’s less than the new value, and use the old value if the new one doesn’t make sense. Another potential use would be if another script is using the same datastore and edits, for example, the items. If you’re not sure what the most up to date value for the items is, you could just use the one currently in there. The script handling items could just keep all the values the same except for changing the items.

Another way you could handle things is with another datastore used to store old data. When you get the old value, you could save it in a separate datastore to make sure you can recover old data. This might not be the best approach; it’s just an example of what you could do with the old values. Again, this isn’t really necessary a lot of the time. Remember, UpdateAsync is just a function, not some magical wand that appears in your game to make sure all player data gets saved perfectly regardless of how it’s coded.

I think a lot of people misunderstand what makes UpdateAsync better than SetAsync in certain situations. The function itself doesn’t stop data-loss by itself; it just gives you the old data to make preventing data-loss easier.

UpdateAsync is definitely useful, and you can use it here, but just taking the SetAsync data and returning it in the transformFunction of UpdateAsync doesn’t stop data loss. Some examples of potential ways to use UpdateAsync here:

  • For values like levels and achievements that should never go down, only up, you can compare the new value to the old and only save the higher one

  • If another script is using the same datastore and edits one specific value in a table, that script can just keep the values already in the old data and you can leave its specific value alone too (Bindables are probably better for this case, but both are an option)

  • When you get the old value, could save it in a separate datastore to allow the player to recover old data in case it somehow does get wiped

  • If data failed to load originally, you could increment the old data by what the player gained during that session to try and minimize data lost both overall and from that session (with stuff like picking a bowler you’d just overwrite of course)

27 Likes

I think you’re undermining UpdateAsync a little too much, as I’ve noticed over time through your posts.

UpdateAsync isn’t solely used to check old values - in fact, that’s hardly the purpose of the function at all. In terms of the old value being passed, there’s many circumstances in which you could use the old value for, but that all depends on how you go about your system. You shouldn’t really throw that value away though, so as far as misusage of UpdateAsync goes, I’ll bite.

There are a plethora of benefits for using UpdateAsync over SetAsync. Remember that SetAsync is a setter, so it has no regard for anything else other than forcing down a value. UpdateAsync takes a more respectful approach towards data.

Item SetAsync UpdateAsync Notes
Callback False True UpdateAsync returns the new data that was updated into a DataStore, so if you fancy using that return value for something, you can. SetAsync is one and done; you either get an error value or you don’t.
Respect calls False True If you have multiple servers potentially editing data (includes servers that write to data in less than 6 seconds apart), SetAsync calls will overstep each other and effectively brute force a value into a DataStore.
Overwrite data True False UpdateAsync takes up one request from the budget, but the function is called as many times as needed until the data is marked as saved. If respecting other calls isn’t necessary, SetAsync is good.
Data validation False True SetAsync essentially takes a value and smacks it onto a key. UpdateAsync has two forms of data validation; one done internally (as implied, I don’t quite know how it works though) as well as a developer-implemented data validation. Yes, you could accomplish it by writing a wrapper function with SetAsync, but that’s reinventing the wheel.
Reject save attempts False True Returning nil to UpdateAsync cancels a data write call. SetAsync cannot be cancelled. I’ve yet to test whether cancelling an UpdateAsync call still uses part of the budget - if it does, I’ll be pretty heartbroken.

Using UpdateAsync like SetAsync and disregarding the benefits or application of either is where the danger comes from, among other practices. Sometimes, bad habits may develop depending on the kinds of messages that circulate around (I can’t imagine someone using GetAsync pointlessly right after using SetAsync, but you never know).

Otherwise, I can’t, in good faith, agree that SetAsync is better than UpdateAsync, because that’s untrue. The benefits of either are dependent on the context of your code and the scenario you have to work with. UpdateAsync has its uses over SetAsync, as does SetAsync over UpdateAsync.

I made a lot of posts discussing UpdateAsync in a certain thread, since you aren’t alone in thinking that UpdateAsync has little merit over SetAsync. The thread itself is an isolated example that relates to player data saving, however the points I brought up mostly talk about the functions themselves rather than the presented scenario. There was also a good point brought up by another poster on that thread in regards to the usage of both.

Check out the related posts under this fold.
8 Likes
The original post I was going to make wasn't really constructive, but I'm going to hide it in here instead of just deleting it.

I’m not trying to say UpdateAsync doesn’t have its uses. I’m just saying that people seem to assume it’s somehow a magical tool that can stop any data-loss, which is not what it does. If you’re using it the same as you would use SetAsync, which is what goldenstein was doing, then there’s no purpose in using UpdateAsync. I’m not saying not to use UpdateAsync in those cases, but it isn’t really a solution to the problem of using UpdateAsync to make sure it never overrides existing data, which is what the original post was really asking.

The only real difference between SetAsync and UpdateAsync is that the function you can provide for UpdateAsync takes one parameter – the old value. There are some nice, convenient things like returning what the value ends up being, or not changing the value if the function errors, but that’s not really what the sole purpose of the function is. These are super easy to do with SetAsync– just have a variable for what you’re Setting the key to.

Saying that UpdateAsync takes a more respectful approach towards data seems to contradict your earlier point that UpdateAsync isn’t solely used to check old values. I think I misunderstood you in one of these sentences, can you please clarify what you mean by a more respectful approach?

This seems like it’s just a convenience thing, not an actual reason to use UpdateAsync. Are you saying it’s good that, when your function errors/doesn’t return, the function returns the old value? When you set something with SetAsync, you can definitely use the value you save for other stuff, and your wording is a bit unclear.

I didn’t say that UpdateAsync wasn’t useful for this. In fact, in the past, I have said that this is one of the circumstances in which you should use UpdateAsync. For this particular case, I don’t see any reason that player data would be affected from other servers in a bowling game; it’s not like the player can be in two servers at once.

Can you clarify what you mean by the data being marked as saved? There’s nothing on the wiki that says anything like this, and I couldn’t find anything with devforum search results either.

How is checking to make sure your data makes sense “reinventing the wheel?” Putting your checks inside an UpdateAsync function or before a SetAsync call doesn’t really make a big difference unless you want to compare to the old value – in which case, like I said, you should definitely be using UpdateAsync.

This is as simple as if data then whatever:SetAsync(data) end, which you should probably be doing anyway as your first check in the UpdateAsync function to make sure you’re not making a bunch of unnecessary checks.

My original post was not intending to say that UpdateAsync was useless. It was trying to show that SetAsync can be as valid as UpdateAsync in several situations, and that using UpdateAsync doesn’t necessarily mean you’re going to have less data-loss. People should be using the transformFunction argument properly, not just putting in a function that returns the same thing you’d be setting in a SetAsync call.

I completely agree. The thing is, people often say that UpdateAsync is better without actually using any of the benefits UpdateAsync over SetAsync. There are some contexts in which SetAsync can be used, and people seem to forget that a lot of the time when suggesting UpdateAsync. I really dislike the “Stop using SetAsync() to save player data” thread because it doesn’t acknowledge any of the use-cases of SetAsync and automatically assumes that UpdateAsync is always the solution to any problem. The thread essentially opens with “data loss is bad and here’s why” and immediately jumps to “so this is makes it immediately obvious that you should use UpdateAsync.”

[/details]

I feel like you misunderstand my point. My point is not that UpdateAsync doesn’t have its merits – there are lots of cases in which you should absolutely use UpdateAsync. My point was that using UpdateAsync in and of itself isn’t inherently a better function to use.

I agree with this paragraph, and that was what I was trying to communicate with my original post. Neither function is really “better,” UpdateAsync is useful in certain situations and SetAsync is just as useful in other situations. I guess you can use UpdateAsync for everything, but it isn’t really an answer to the original question of how to use UpdateAsync to keep from overriding existing data. My post wasn’t just bashing UpdateAsync, I gave ways that you can use UpdateAsync in this situation to be more helpful than just using UpdateAsync the way you would use SetAsync.

The only parts I actually said that UpdateAsync might not be necessary here was the first sentence and the last two sentences. I think if you looked at what my post was trying to get across, rather than the admittedly harsh tone (which I’ll edit to make it more constructive), you’ll find that it acknowledges the uses of UpdateAsync.

One part of your post that I actually disagree with is the whole “Stop using SetAsync to save player data” resource. The post doesn’t really explain usecases for UpdateAsync, which I’ve always tried to do even when suggesting against it; it just bashes SetAsync and blames all data-loss on it when the actual cause is usually poor usage of the function rather than the function itself.

3 Likes

I think we’re probably saying the same things but in different manners. One of the main misunderstandings I’m developing is primarily due to the reasons that I see in terms of the usage between the two, most of which encompass comparing new and old data. Given, UpdateAsync isn’t a comparison but rather a transformation of data.

Relevant:

I think a few of my posts use the example in the OP to explain better, but the resource thread is specific towards comparing use of UpdateAsync and SetAsync for player data. I actually went to discuss the functions themselves, as well as with a few developers doubtful between the benefits of UpdateAsync either over SetAsync or as a function itself. The posts I linked were direct to the functions rather than the main thread itself.

I won’t reply to the hidden thread. Feel free to DM it to me if you would like to pursue discussion.

3 Likes