How to use DataStore2 - Data Store caching and data loss prevention

The key is defined when you use DataStore2(dataStoreName, player), dataStoreName being the key (it’s different to the key that’s used internally, but that doesn’t matter for a user).

The :Get() function will grab the value of the key you defined when you used the DataStore2 function. defaultValue is the value you want :Get() to return and save if the value in the data store is nil. Variant just means that it returns a Variant, AKA any data type.

I gave comments on the example code about unrelated things so that people don’t conflict what is and isn’t DataStore2.

I’m not sure how I’d improve the documentation, what would you suggest?

27 Likes

Thanks for letting me know. I think just putting information like that into the documentation would be a lot more helpful. It would be incredible if your example showed how you retrieved data because (from what I can tell) it doesn’t. And more comments explaining what you’re doing with DataStore2.

As for the functions in the documentation, I just personally dislike people putting things in there that you wouldn’t necessarily use in the script. I’m not sure how to explain it, but basically people putting stuff in there besides what I should be using just makes it harder for me to read.

Anyway, I can tell you put a lot of work into this. I appreciate your quick answer to my questions as well

22 Likes

Both my examples retrieve data through :Get(), are you referring to something else?

20 Likes

Ok, so I was originally confused by the “100” in the Get() function in the example. I didn’t realize that was the default value, so I guess it was really my fault for not noticing. I think you could still benefit from leaving a comment there explaining that you’re getting the data, and if it isn’t there, setting the default value to 100.

This is where your documentation gets confusing:

Variant DataStore2:Get(defaultValue=nil, dontAttemptGet=false)
Example usage: coinStore:Get(0)
If there is no cached value, it will attempt to get the value in the data store. Otherwise, it’ll return the cached value. If whatever value it gets is nil, it will return the defaultValue passed. If dontAttemptGet is true, then it will return nil if there is no cached value.

I think it could be worded better. Simply changing the first sentences to something like “The module will attempt to get the value in the data store. If the data is cached, the cached data will be returned instead” would probably be beneficial since it explains what the function does clearly, and then explains that it could also be cached. (If that’s even how it works) You could also explain what the arguments are and what they do bit better too. Something as simple as “If no data is found, defaultValue will be returned” would help me.

Again, I can tell you spent a lot of time on this so don’t worry about changing the documentation or whatever if you think it’s fine. I’m just having some OCD. I’m going to just experiment with it and see what happens. And thanks for making this thing free too!

26 Likes

Added Fairy World to the list of games with millions of visits using that I am aware of that use DataStore2 (3 million visits).

21 Likes

It really is a great system you’ve made, so I don’t see why they wouldn’t use it.

22 Likes

Now that I have figured out how to use it this module has been pretty awesome. Thanks again.

22 Likes

video tutorial pls? :wink:

22 Likes

Not really into making video tutorials rip
But we do need someone willing to take on the challenge.

21 Likes

I’m trying to wrap my head around this and using Code as an Example so is this correct?

How I Understand It
--DataStore2

local DataStoreService = {} -- This is the same as game:GetService('DataStoreService')

local PlrDataStore = DataStoreService[plr] -- Using The Player As The Key, game:GetService('DataStoreService'):GetDataStore(Player_As_Key)

PlrDataStore = {

Time0 = {},

Time1 = {},

Time2 = {},

Time3 = {},

-- Key = Value (Using Time Stamps as the Key)

}

print(PlrDataStore[Time0])

-- Normal DataStore

local DataStoreService = {} -- This is the same as game:GetService('DataStoreService')

DataStoreService[Key] = { -- Using a String as The Key, game:GetService('DataStoreService'):GetDataStore(SomeString)

Plr1Data = {},

Plr2Data = {},

Plr3Data = {},

}

print(DataStoreService[Key][Plr1Data])

-- In other words the Player has their own DataStoreObj rather than Sharing the same :GetDataStore(SomeString) they have :GetDataStore(Player_As_Key)

DataStore Limits

Per Place Limit

Each place in a game is budgeted a certain number of data store requests based on the number of players that are present (more players means more data will be needed).

The limit rates for a place are based on the kind of request being made.

Request type Methods Requests allowed per minute
Gets GetAsync 60 + numPlayers * 10
Sets SetAsync, IncrementAsync, UpdateAsync, RemoveAsync 60 + numPlayers * 10
GetSorted (per page) GetSortedAsync 5 + numPlayers * 2
OnUpdate OnUpdate 30 + numPlayers * 5

Is this per Player or for the entire Place or Game?

What I mean is if I use DataStore2 could I Send more Request than not using it or it’s the same?

17 Likes

None of this looks like DataStore2 code? I’m not exactly sure what you’re asking. Every DataStore2 instance is specific to their specific player.

As for throttles, they’re per place.

What I mean is if I use DataStore2 could I Send more Request than not using it or it’s the same?

DataStore2 only sends requests the first time you call :Get() and on :Save() (called also when the player leaves). You can run any of the DataStore2 functions (other than :Save()) as much as you want.

18 Likes

It’s not from DataStore2, it’s my code of how I understand it.

So basically Each player has their own :GetDataStore(Player_As_Key) rather than sharing the same :GetDataStore(Key)?


14 Likes

Yes, every data store key is unique per player.

Whether or not you use DataStore2 doesn’t impact your throttles unless you’re using ordered data stores and are excessively using DataStore2.

27 Likes

Alright, I read the script but I didn’t understand it so I had to ask.

Especially that bit that you captured as an Image.


I understand that the Script Saves the Data to Datastore and os.time() to OrderedDataStore

uses the OrderedDataStore as a key for Datastore

but I don’t understand how the script works to help prevent Dataloss or Caching or exceeding the Limits, maybe we should take this to DMs, I don’t want to make your Thread a mess.


Great module :+1:t2:

18 Likes

I don’t understand how the script works to help prevent Dataloss

Read the excerpt from berezaa, specifically:

Ever since I implemented this, pretty much no one has ever lost data. There’s no caches to worry about either because you’re never overriding any keys. Plus, it has the added benefit of allowing you to restore lost data, since every save doubles as a backup which can be easily found with the ordereddatastore

This prevents exceeding the limits because it caches, meaning that I never call :GetAsync except the very first time you use the module.

I don’t want to make your Thread a mess.

Other people may have similar questions, so it’s fine :yum:

23 Likes

Right, I read everything multiple times I think I get it now.

So it uses the Player as a Key for the :GetDataStore()

and then you get the Data from PlayerDataStoreObj:GetAsync(Time_Stamp)

and using OrderedDataStore for keeping the TimeStamp

Because every Key (TimeStamp) is unique it’s never overwritten or Cached

GetAsync is called only once

SaveAsync is called when you call :Save() , Auto Saved every X Minute or so, when the game closes and when the Player leaves.

Is that right?


I’m gonna DM you tho I want to make sure my code is using your Module correctly. :grinning:

16 Likes

So it uses the Player as a Key for the :GetDataStore()

It uses a player-specific key.

Because every Key (TimeStamp) is unique it’s never overwritten or Cached

I’m not sure you understand what cached means? By caching I mean that I save the value you’re setting the data store to internally so it doesn’t have to fetch from a server.

SaveAsync is called when you call :Save() , Auto Saved every X Minute or so, when the game closes and when the Player leaves.

SetAsync is called on :Save(), not auto-saved by default, and when a player leaves, yes.

21 Likes

This PR to add combining data stores is coming soon. No data will be lost or anything, I just want to make sure I announce it here in case anyone notices any issues in my code before I publish it.

15 Likes

I’m having some trouble with my datastore script that uses the same (or similar?) method. One Two of my players has reported that their data keeps reverting back whenever they rejoin. I checked their data and the data they keep reverting to are saved with the timestamp 1536969277 and 1536957450 respectively. However, when I enter any server right now and do print(os.time()), it says around ~1534498296 (which is correct according to this)

It seems like they somehow managed to save their data in server where os.time() was incorrect and much larger (28 days / 14th september??), whereas all servers they join now have a lower (but correct) timestamp. So their data keeps resetting to the old data which is saved at 28 days from now. Does this sound plausible? Can it be that one of the servers was unsynchronized where os.time() was set at 28 days from now?

21 Likes

os.time is consistent across all servers, so that’s not it. Other than that, I’m not sure since I don’t have your code.

14 Likes