DatastoreX - Work faster and better with datastores!

DatastoreX

Work faster and better with datastores!

This module is inspired by DataStore2!

Hello everybody!
I was tired of having to code too much when working with datastores.
So, I developed a faster mode to work with them!

DataStoreX is a module to make working with datastores faster and also create backups!


Example code:

local DataStoreX = require(game.ServerScriptService.DataStoreX)
local Coins = DataStoreX.connect("coins")
Coins.set({
"Xsticcy" = 1000;
"OtherPlayer" = 2000;
"CoolGuy" = 1500;
})

Tutorial

Of course this plugin can do more than you see in the example!
In this tutorial you will learn to use DatastoreX!

See tutorial

1) Setup the module

Once you downloaded the module from the library,
put it into ServerScriptService!

2) Connect to datastore

Create a Script in workspace for example
Open up the script and write the following line of code:

local DataStoreX = require(game.ServerScriptService.DataStoreX)

Next, we are going to connect to a datastore.
To do this write:

local testDS = DataStoreX.connect("TestDatastore")

3) Set keys

Now comes the fun part.

Unlike normal datastore, in DatastoreX you can set multiple
key’s value at once

This is pretty easy to do:

local testDS = DataStoreX.connect("TestDatastore") --connect to datastore
testDS.set({
"TestKey" = 10;
"TestKey2" = 20;
})

The following code will create two keys (named TestKey and TestKey2) and set
their values (TestKey’s value to 10, TestKey2’s value to 20).

If the keys already exists, then they value will be replaced to the new one.

4) Get keys

This is very similar to set.
You can get multiple key’s value at once!

local testDS = DataStoreX.connect("TestDatastore") --connect to datastore
local values = testDS.get({"TestKey", "TestKey2"})
print(values)
print("TestKey's value is:", values.TestKey)

You need to give a list of keys you want to get.
(Output:

{
["TestKey1"] = 10,
["TestKey2"] = 20
}

)

5) Remove keys.

Works the same way as get.

local testDS = DataStoreX.connect("TestDatastore") --connect to datastore
local values = testDS.remove({"TestKey", "TestKey2"})

You need to give a list of keys you want to remove.

(Now you know the basics, but there’s more!)

6) Backups

Yes, you can create backups too!

local testDS = DataStoreX.connect("TestDatastore") --connect to datastore
local values = testDS.createBackup({"TestKey", "TestKey2"})

You need to give a list of keys you want to backup.
But what’s the point of backups if you can’t get them?

Of course, you can!

local testDS = DataStoreX.connect("TestDatastore") --connect to datastore
local values = testDS.get({"TestKey", "TestKey2"}, true)

If you give a second parameter (and set it to true) when getting keys then
if the key’s value is nil then the module is going to look for the value
in the backups.

7) setAll

With setAll you can set multiple key’s value to x.
For example:

local testDS = DataStoreX.connect("TestDatastore") --connect to datastore
local values = testDS.setAll({"TestKey", "TestKey2"}, 120)

You need to give a list of keys you want to set.
And for the second parameter, you need to give the value.

8) increase and decrease

With increase you can increase multiple key’s value by x.
For example:

local testDS = DataStoreX.connect("TestDatastore") --connect to datastore
local values = testDS.increase({"TestKey", "TestKey2"}, 10)

You need to give a list of keys you want to set.
And for the second parameter, you need to give the x.

Same with decrease:

local testDS = DataStoreX.connect("TestDatastore") --connect to datastore
local values = testDS.decrease({"TestKey", "TestKey2"}, 10)

9) Caching

When you set/get a key’s value, DSX is going to save it into a variable.
For example:

local testDS = DataStoreX.connect('testDS')
testDS.set({
"Key" = 10;
})

--do stuff

print(testDS.Key) -- output: 10

9+1) Rules

This is an extra feature that can be useful sometimes!
I will show one example code:

local testDS = DataStoreX.connect("TestDatastore") --connect to datastore
local values = testDS.get("@[TestKey&1-5]") -- the rule is: @[TestKey&1-5]

This will get the value of TestKey1,TestKey2,TestKey3,TestKey4 and TestKey5.
You can just memorize the syntax and change the numbers and the text.
Let me show you more rules:

@[Player&1-10] -- get the value of: Player1, Player2, ..., Player10
@[Building&5-20] -- get the value of: Building5, Building6, ..., Building20
@[Save&1-5] -- get the value of: Save1, Save2, ..., Save5

I hope you can understand the syntax now.

You can use rules in the following functions:
setAll, remove, get, createBackup, increase, decrease



Other things made by me(Xsticcy)

[OPEN-SOURCE] Free Data Editor plugin!

City Generator - Generate simple cities fast!

Xsticcy UI - Create UIs fast!

When working with datastores, it sometimes hard to see what are you doing and what is the value of the keys. I recommend Data Editor!

I would be pretty happy if you support me: Support my work! - Roblox

Get the module

Share your problems, ideas and feedbacks in the replies!


Goodbye!
31 Likes

I still haven’t used or experimented with data stores yet but when I do this will help a lot!

3 Likes

Unlike DataStore2, this module does not do any caching. It is mostly just an elaborate wrapper for vanilla DataStore functions and because of the way it is set up, it is very inviting towards dangerous DataStore anti-patterns (most specifically being careless with respect to the rate limits) which will result in frequent throttles and thus queued or dropped requests.

I’m not so sure DSX makes working with DataStores faster because it would require you to write your own handlers on top of it to account for its shortcomings and it definitely does not make it better because of the aforementioned issues. Consolidation is important with DataStores and avoiding too much unnecessary or overuse of them.

9 Likes

Actually, I didn’t said that in the tutorial!
DSX does cache someting too.
For example:
If you set/get a key’s value once. Then DSX will save it into a variable.
Like that:

local testDS = DataStoreX.connect('testDS')
testDS.set({
"Key" = 10;
})

--do stuff

print(testDS.Key) -- output: 10

I’m not sure if I can call this caching but I will put this into the tutorial too!

Thanks for the feedback!

Does this support OrderedBackups from DS2 or is it different? Does it have any form of backups?

1 Like

Did you read the tutorial?
Yes, it has a backup feature!

Oh yeah, it does. How do these backups work…? Where are they saved?

It doesn’t really matter where they are saved. But if you really want to know they are saved in datastores like: “TestDataStore_Backup_12”. And the plugin can get the values from these backups if needed.

1 Like

That’s not really the kind of caching I was hoping to find in a DataStore module though. Every time set is called, it does update an internal sort of cache for the value yes but it also calls SetAsync every time which is bad practice, hence my concern for tripping rate limits.

DataStore2’s strong point is being able to work with DataStores almost like normal except operations that actually make it through to a DataStore need to be explicitly ran by a developer or are ran at certain points where it’s warranted that data is saved (e.g. when a player leaves).

Throughout a game’s lifetime, use of the DataStore module should only be updating an internal cache of data for the game session which can then later be saved either by a triggering circumstance (player leave) or an explicit method call. You should not be running DataStore operations every time a function is used here. That’s primarily what I meant, though I do recognise (now) that this module has a low-level amount of caching involved in it.

4 Likes

Is there a given reason on why I should use DatastoreX over ProfileSerivce or DataStore2? Can you list some feautures that would function better than the 2 good Datastore modules I listed?

Looking at your backups example it seems you’re just saving the data to a separate datastore, and correct me if I’m wrong but this backup method won’t work in the event that Roblox servers are down.

Edit:

	function datastore.createBackup(Keys)
		backups.Value += 1
		local DataStore = DataStoreService:GetDataStore(DataStoreName..'_Backup_'..tostring(backups.Value))
		DataStoreService:GetDataStore('BACKUPS'):SetAsync('CNT', backups.Value)
		if type(Keys) == "string" then
			if rule(Keys) ~= nil then
				Keys = rule(Keys)
			else
				local pmsg = ""
				if Keys[1] ~= '@' then
					pmsg = " (missing '@' symbol.)"
				end
				warn('Unknown rule: '..Keys..pmsg)
				return
			end
		end
		for _, key in ipairs(Keys) do
			local value = DataStoreService:GetDataStore(DataStoreName):GetAsync(key)
			if value ~= nil then
				DataStore:SetAsync(key, value)
			else
				warn("The value of "..key.." is nil. Can't save it to backup!")
			end
		end
	end

Taken from your code, you’re not using pcalls for your backups when calling GlobalDataStore requests.

2 Likes

The good feature is that you can work with multiple keys at once.
For example:
With normal DS:

local DataStoreService = game:GetService("DataStoreService")
local DataStore = DataStoreService:GetDataStore("Test")
DataStore:SetAsync("Key1", 10)
DataStore:SetAsync("Key2", 20)
DataStore:SetAsync("Key3", 30)

with DSX:

local DataStoreX = require(game.ServerScriptService.DatastoreX)
local DataStore = DataStoreX.connect("Test")
DataStore.set({
"Key1"=10;
"Key2"=20;
"Key3"=30;
})

Same for get.


DSX is good because you can write less. You can write shorter codes, in less time.
When I say faster I mean write code faster. You don’t need to do repetitive code, and you can write
less characters wich means you can write your code faster.

Sorry? I’m lost here. Regarding your DSX example, I can’t exactly pinpoint a reason as to why you’d explicitly save player data via hardcoding instead of doing it methodolically. As for saving player data, I can’t fathom a reason to use this over DS2 or PS, let alone the normal datastore service.

If I were to use this to save player data, I’d feel like I’m attempting to make practical use of a mimic of a generic barebones data saving system with a not-so-great layer of “abstraction” on top of a not-so-great interface. Overall, as much as I hate to say it, in my eyes, this module doesn’t really serve any major features that a developer couldn’t implement within five minutes and a cup of coffee. The only thing remotely unique as far as I can tell is the ability to save multiple keys within one call, which can also be covered under the hood with nigh no effort at all.

B-but, Fifkee, it has a cache!

You’re already covering a major step, sure, but most, if not all marginally efficient datastore systems utilize a caching system that periodically gets saved / saved on leave; and I guarantee that a large sum of datastore tutorials put strong emphasis on how important a cache is for complying with ratelimit systems, and for datastores as a whole. All of @colbert2677’s issues are very much valid - and I’m certain that if I keep on going down the path that I am, I’ll likely end up regurgitating their concerns; however, one thing I can say for certain is that you’ve been narrowly avoiding their responses, which for one, isn’t very cash-money.

If it were up to me, I’d say that we should refrain on making datastore modules as we have two (relatively) fantastic datastore modules that covers up most concerns that a developer would with respect to data storage unless we can have one that supplements for the shortcomings that the two modules might have.


Anywho, was just my two cents on this topic–actually, maybe two quarters.

Cheers.

2 Likes

That still doesn’t answer my question on why I should use this over DS2 or PS. Saving with multiple keys can already be easily done with no effort at all, as @Fifkee said. DataStore2 and ProfileService provide multiple unique feautures that give a good reason for you to use them, such as data-caching, session-locking (ProfileService exclusive) and most importantly, the ability to give you as much freedom as you want to configure it.

With all due respect, I would rather write my own code for managing Datastores and have barely any restrictions on how I write or configure it or even better, use fantastically-made Datastore modules that are already out there, open-sourced and come with important feautures I just don’t want to include in my own code due to laziness than your module.

Perhaps you should consider feautures in your module that allow for flexibility, trust and usability that could give a better reason for using this module over others. Best of luck though on your datastore module!

1 Like

Okay, I didn’t said to anyone that you should use this over anything. I really appreciate
and love DS2 and PS. I just challenged myself because I never wrote modules before. I don’t want to people to use this over DS2. I just wanted to share what I created. I know that the other modules out there are much better but I don’t care. If somebody wants, then he/she can use it.

Thanks for the feedback btw.

6 Likes

I’m not the best with data stores, could you explain to me how you would save and load values using this?

It was written down in the tutorial.
But it’s easy.

local datastore = DataStoreX.connect("MyDatastore")
--save data
datastore.set({
'MyKey1' = 10;
'MyKey2' = 20;
})

--get data
local values = datastore.get({'MyKey1', 'MyKey2'})
print(values) -- 10, 20

1 Like

How would I implement that into a script that loads data when the player joins and saves data when they leave?

How do you get the values from an exact player? Is this only client sided?

Well, I’m sure there are a lot of tutorials how to do that. And you can read the tutorial of this module too.

How do you get the values of the exact player? Isn’t this a server sided script? Wouldn’t you need to specify a player?

1 Like

Do you know anything about datastores? First learn the default datastore system I think. After that, you should understand this too.