GDRP within Roblox (Like/Dislike system)

Heya,

I’ve been wanting to try mess about with Roblox’s database API. I haven’t ran into any problems, until now. I was attempting to create a like/dislike system. I’ve played this game called ‘Theme Park Tycoon 2’ and they have exactly the same thing as I do.

image

Programming it isn’t the actual problem though. It’s the Right To Erasure (RTE) (Or GDRP/CCPA for the US) requests. As a new developer, I obviously haven’t received any, however I’d still like to prepare for the future.

But what’s the actual, actual problem? Well, how do I comply with these RTE regulations, while also being able to program a like/dislike system? I’m thinking of storing the likes (and dislikes the same way) in an array like this:

(Database code)

local database = ...

local data = database:GetLikesAsync()

print(data)

Output:

{
	[1] = {
		[1] = 1234567890, -- this is the userId of the player which liked
		[2] = 16999325.250, -- this is the time stamp of when the like was given
	},
	[2] = {
		[1] = 9876543210,
		[2] = 16989526.321,
	},
}

I think you can see the problem. If a user filed a RTE request, I’d have to search through every player’s database finding the matching user id; which is quite literally near impossible with the rate limits, plus thousands of players (hypothetically)

Are there any workarounds or nice ways to go about creating something like this?

4 Likes

Don’t know if I exactly understand what you’re trying to do, but you could have a separate Datastore that stores each one the player likes, and use that to find it and remove it.

Could be
local DataStoreService = game:GetService("DataStoreService")
local playerLikes = DataStoreService:GetDataStore("playerLikes")

local playersData = playerLikes:GetAsync(<player to retrieve user id>)

print(playersData)

With output similar to:

{
    <likedIndex1>,
    <likedIndex2>,
    <likedIndex3>,
    <likedIndex4>
}
2 Likes

That’s a workable method, however GDRP will still come knocking. The output would look something similar to this, yes?:

local database = ...

local data = database:GetLikesAsync()

print(data)

Output

{
	[1] = {
		[1] = 123, -- user id
		[2] = 25981.2, -- time stamp
	},
	[2] = {
		[1] = 321,
		[2] = 125252.226,
	},
	[3] = {
		[1] = 213,
		[2] = 158265262.2,
	},
	[4] = {
		[1] = 312,
		[2] = 1298255.2,
	},
	...,
}

Now let’s say, the user 213 files a RTE to Roblox. Legally as a company Roblox would probably reach out to me, and I’d have to comply and delete any trace of said user.

But let’s hypothetically say my game has 3,000 active users. This means that including the offline users, I’d be roughly searching 10,000+ databases trying to find any attachment to the user-id 213 and delete it.

This approach is unrealistic. Because as a developer, I have very limited access to how many requests I can queue per minute. And I don’t know how common RTE’s are on Roblox.

Thank you for your reply though, I’ll keep trying to find a suitable workaround.

Does the data stored need to be in an array?
Using a key value table/dictionary would allow you to use the userId as the index and the timestamp would be the value.

{
    ["player_1234"] = 9876543, --timestamp
    ["player_9876"] = 12345678,
   ..,
}
2 Likes

Certainly not!

The index doesn’t have to be in array form. However, this still doesn’t change the RTE problem. If the player 1234 filed a right to erasure request. I’d still have to search through every table and delete said user-id (1234) from any other database.

It’s rough trying to explain this.

Imagine this player with the user-id 195829252, has a ‘liked’ table like this:

{
	["player_257182752"] = 2529815, -- key = "player" concat with the userid. value = time stamp
	["player_81752562"] = 2623,
	["player_1258269652"] = 234235,
	["player_86987252"] = 158785,
	["player_28171326"] = 58925,
}

Everything is fine right?

Well let’s say the user-id 1258269652 (this user-id is seen in the table above) files a RTE request. As a developer, you have to delete this user-id. Now this is simple because we know that the user 195829252 has this user-id in the ‘liked’ table. But remember, this player could have liked literally, anybody.

That user-id is in hundreds, if not thousands of tables. And worst of all, I can’t get those tables because they’re under a datastore rate limit.

This is probably a terrible example, but I explained it to the best of my abilities. Thank you for the suggestion though!

1 Like

While I’m not too experienced in Datastores, but there would be a Datastore with the UserId as the key, which can be grabbed with :GetAsync(). This would be a completely seperate entry from the likes, and would store the indexes of the tables for the other datastore, with the user’s likes.

2 Likes

I have similar “concerns” with my games, but even worse as some of the UserId’s are stored on worlds (created with AssetService:CreatePlaceAsync()).
Making a datastore solely for dealing with GDRP requests is probably one of the best options.
The other option would be to brute force checking every datastore, taking advantage of every active server in the game to cycle through the datastore and remove any UserId to be erased (and also teleport players to worlds that have been inactive for near a month, disguised as a random world teleport system thing)

1 Like

People seem to hang on to table.insert() when using Arrays. You could just do it the old fashioned way, and it’ll still work.

local Likes = {}
local UserId = 1 --// Roblox's Official Account UserId

Likes[UserId] = {
	["Timestamp"] = os.time();
}

Then you could just use Likes[UserId] to find the user within the Array. Then just do

Likes[UserId] = nil

to remove it from the table entirely.

1 Like

I think I see the problem now, you have multiple datastores which might or might not contain the user ID you need to remove.

Perhaps a double entry system might work. Use a separate datastore arranged by user id as the key, with a table of places they liked (ie a list of datastores with their data in). You can then check the specific likes of the user and then check and remove them from just those datastores.

double entry because you will have to make sure any update to one datastore is also recorded in the other

2 Likes

You can make an intvalue for the amount of likes, and a boolvalue checks if the player has liked or not, and an RemoteEvent.

1 Like

Apologies for the late reply, I was sleeping.

This still is a problem. As I’ve stated in my original thread, there’s no direct issue with me storing the data (Such as fetch errors). The issue is with RTE regulations and the way the data is supposed to be stored.

My thread asks for a workaround from the regular “store the user-id in an array” due to the RTE regulations. I can’t store (a) user-id(s) in any array/dictionary because I’d have to cycle through every single ‘likes’ database for every single player.

Just like I’ve stated in my original thread, this is near impossible and finding some cheeky way around it would just be a waste of time, and it would be better to just scrap the idea as a whole.

I believe you’re semi-experienced in databases, however, this has more to do with RTE regulations and databases.

Thank you for your reply though.

Thanks for your reply,

The idea of using every server’s resource to cycle through every database sounds like a complete nightmare. I thought about a system like this until I realized, how would each server even synchronize with one another? (To not loop over the same database(s))

You mentioned, “Making a datastore solely for dealing with GDRP requests is probably one of the best options.”. This sounds like an interesting concept, however again, we run into the same RTE regulations and how the ‘likes’ are stored.

Thanks for your reply @Laqotaa,

Well once again, the database is not my worry, it’s the RTE regulations.

As an example, let’s say that data (which you showed above) was stored with my user-id (5104349571) and Roblox liked my video. Now let’s say Roblox decides to file a RTE request (Right To Erasure request) on their behalf, and now I have to remove that user-id (1) from any ‘likes’ database. Imagine how many other videos Roblox liked.

Also, directly setting the index: table[index] = value is slower than using table.insert, though feel free to correct me if I’m wrong.

Thanks for your reply @Wigglyaa,

I’m intrigued by your idea, would you be able to elaborate further? I can’t exactly wrap my head around double entries.

Thanks for your reply @ROLDSTICKS,

This has nothing to do with RTE regulations, nor would this work in a database setting. Thanks again though.

@optimizedcoder
It’s a concept used in accounting/bookkeeping for tracking transactions in different accounts (in case you wanted to find some more info about it).
But basically in your case you would need one datastore to hold the “ThisPlayerHasLiked” data and a second to hold the “ThisPlayerWasLikedBy” data.
So the datatables would look something like:

--Has liked data
{
    ["player_1111"] = {"player_2222", "player_3333"}
    ["player_2222"] = {} --not liked anyone
    [ "player_3333"] = {"player_1111"}
}
--Liked by data
{
    ["player_1111"] = {"player_3333"}
    ["player_2222"] = {"player_1111"}
    ["player_3333"] = {"player_1111"}
}

So if player_1111 submits the request, you can look at the “hasLiked” key and know that you need to remove the data from player_2222 and player_3333 in the likedBy datastore.
Similarly you can then look at player_1111 key in the LikedBy store and remove the reference to player_1111 in the HasLiked store from Player_3333.
Once all checks have been completed remove the key from both datastores.

Obviously this is just an example and might not fit exactly your requirements if you have more datastores, but let me know and I’ll try to adapt it more exactly, or provide a better example.

1 Like

Thanks for your reply,

I think this could potentially work. I’ll try and program this. This may take a while because I’m using an OS library for my data management. I’ll update you when I’ve either failed or succeeded.

Thanks a bunch, again!

1 Like

Glad @Wigglyaa found your solution.

Also, directly setting the index: table[index] = value is slower than using table.insert , though feel free to correct me if I’m wrong.

Their actually the same. Table.insert() is mostly used for arrays to create a Dictionary.
table[index] = value is just for setting a value within’ a Dictionary. Mostly used for Custom index names but if that’s not needed you can go for the more cleaner alternative Table.insert().

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.