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

Basically, Save() and SaveAll() actually calls SaveAsync(), and catches errors automatically, then retries the save until it’s confirmed to be saved? And the increment function subtracting with a positive number is surely strange.

Also, does the amount of keys affect how easily the system will throttle? And would there be a Datastore2 update that would handle teleportation? It’s been difficult to fix the issue if players losing stats from teleportation despite AfterSave confirming that it’s saved before proceeding and it’s just getting more rampant.

Is this module still recommended to be used? I used to use it back in the day and I certainly loved it. I just want to make sure it still works well and isn’t an abandoned module.

DataStore2 still works perfectly fine, though I have no real plans of updating it as I have plans to make another data store saving module with a different, less beginner friendly philosophy.

1 Like

If you are combining your keys under one (which the documentation stresses that you must be doing), then no.

I don’t know what you mean by this, because Zombie Strike proved that teleportation with DataStore2 works fine–I did not get any data loss reports.

It’s significantly more likely that your code is just bugging out somewhere. Leave some data tracking code to ensure every value you send is positive, there is no reason to believe this is a fault of DataStore2.

1 Like

Is there an advantage to saving a value in a DataStore and THEN using that to show the value on a leaderstats page via OnUpdate?

Or, say, can I go the “backward” route - save all relevant values to some Folder (with IntValues and whatnot inside it), and then just use a .Changed event listener that saves those values into DataStores?

Just wondering if the former is more reliable, or if either method should be fine. The only reason I ask is because it’s just much less of a pain to do the latter since I’ve always done that - but if it’s unreliable then I’m all for switching it up.

1 Like

All my keys are combined, sounds good. I’ve also done some testing, and what I found is that doing the idea of saving prior to teleportation apparently double saves, which can cause throttling issues. What’s the maximum save limit under OrderedBackups before it puts the requests in a queue?

And for the increment issue, the line that increments the value is done by

:Increment(math.abs(INCREMENT_VALUE), DefaultValue)

which confuses me the most.

It’s very small, I recommend explicitly changing your saving method for brand new projects nowadays.

1 Like

I say do whatever results in the cleanest code, but do note that if you want leaderboards you’re very likely going to have to switch saving methods to avoid ODS throttles.

Oof, It’s too late now as that’s ten million player’s data gone if I switch. Srry for more questions but my game involves many exit points such as teleports (just like a hub type place) and a rocket with a capacity of four, that would teleport players off at the same time. Even without calling SaveAll(), would that throttle? If so, what’s my best option? Teleportation involves using TeleportPartyAsync.

Adding into that, what would you personally recommend for teleportation? Save() or SaveAsync() which I have a boolean to take care of.

Zombie Strike dungeons are 4 players big, and we manage to save and teleport fine.

1 Like

Oh I see. Not very many, but in my case, servers don’t close after teleporting off so it will be difficult.

I would also like to bring up some strange behavior with my stats during my experiments:

I updated my stats then like 2 seconds later, I then teleport (with no SaveAll()), then in the destination place, I see the old stats,

then I leave the destination place and join back into the original place and the updated stats returned.

Then I teleport (again, no SaveAll() and this time, no updating), then it shows the old stats once again!

The cycle continues until I teleport with SaveAll(). Now because of this, I’m confused. Note that this happens once in a while.

Just adding to that, I had someone report a bug where they afk’d in the game for 11 hours, teleported, and his stats were lost, despite the 5 minute autosave and the conditions I’ve stated in my previous posts.

Hey there. While trying to update the player’s data using

DataStore2("Pets", Player):Set(pettable)

I noticed that the game experiences a lag spike for a split second. This only happens on larger tables and will get worse depending on the size of the table. To fix this I had to put in the DataStore2 module so I could edit the code. On line 217 clone(value) was commented out in favor of value:

self.value = value --clone(value)

I haven’t read through the code so I don’t know if this will bring any other complications. Therefore it would be greatly appreciated if there could be implemented a fix for this, or if there is another fix for it already a solution would be appreciated too.

It’s not a fixable solution because the deep cloning is way DataStore2 can afford to be as easy to work with as it is.

Yes, the deep cloning of DataStore2 is a massive problem–I wrote an article on my blog (that I don’t think I can actually link since it’s on another website?) talking about how my next data store library (which will not be an update to DataStore2 for numerous reasons, such as backwards compatibility but also that it’s a completely different mindset) will not have any of it in exchange for purely immutable operations. The problem is that this is simply harder to work with, especially for developers not used to it. Other systems like Roact and Rodux work on this same principle.

That’s unfortunate. Do you recommend any other datastores to use instead, or do you have an ETA on when your new datastore library will become available?

No, I still consider DataStore2 better than the rest.

Hey there,

I’ve been trying to implement a softshutdown script combined with Datastore2 but I
can’t seem to fix this without dataloss issues.

The main problem for combining datastore2 with a softshutdown script is that on the
bindtoclose() event, datastore2 parents the player to nil to fire the AncestryChanged event.

since teleporting a player parented to nil does NOT work, I’ve tried to replace AncestryChanged with a BindableEvent like so:

	local event, fired = Instance.new("BindableEvent"), false
	local playerAncestryChangedEventSubstitude = Instance.new("BindableEvent")
	
	game:BindToClose(function()
		if not fired then
			spawn(function()
				playerAncestryChangedEventSubstitude:Fire()				
				--player.Parent = nil -- Forces AncestryChanged to fire and save the data
			end)

			event.Event:wait()
		end

		local value = dataStore:Get(nil, true)

		for _, bindToClose in pairs(dataStore.bindToClose) do
			bindToClose(player, value)
		end
	end)
	
	local playerLeavingConnection
	playerLeavingConnection = playerAncestryChangedEventSubstitude.Event:Connect(function()
		if player:IsDescendantOf(game) then return end
		playerLeavingConnection:Disconnect()
		dataStore:SaveAsync():andThen(function()
			print("player left, saved " .. dataStoreName)
		end):catch(function(error)
			-- TODO: Something more elegant
			warn("error when player left! " .. error)
		end):finally(function()
			event:Fire()
			fired = true
		end)

		delay(40, function() --Give a long delay for people who haven't figured out the cache :^(
			DataStoreCache[player] = nil
		end)		
	end)	

Even with my softshutdownscript disabled, dataloss is happening when I use the code I wrote above where I am using a bindablevent instead of AncestryChanged, and I can’t find out why.

I can’t find other connections which listen to the ancestrychanged event which could be causing the dataloss issues.

Is there a workaround to fix this issue?

TL;DR dataloss is happening when I replace the ancestrychanged event with a bindableevent inside the datastore2 bindtoclose() connection to prevent the player.Parent going to nil and I don’t know why as the functionality is the same.

1 Like

IIRC, DataStore2 automatically binds to close, so you don’t have to do it manually. I could be misinterpreting your question, but I assume you’re trying to bindtoclose outside of the module.

Now I have my own question - How would you do, per say, monthly datastores using this module? I have an idea like this:

local DataStore2 = require(game:GetService("ServerScriptService").DataStore2)
local osMonth = os.date("*t", os.time())["month"]
game.Players.PlayerAdded:Connect(function(player)
     local MonthlyDonationsDataStore = DataStore2("MonthlyDonations" .. osMonth, player)
end)

But would this work? Does this also work while combining keys?

I don’t know what “monthly data stores” means, what is your specific use case?

For example, if you wanted to know the top monthly donators. That means the top donators for the current month, which would be manipulated by the key + osMonth.