DataStore2 overwriting table data when called by 2 async threads

  1. The context
    I’m making a In-Game purchase system

  2. What is the issue?
    I have a Module Script with the code bellow that (in a specific situation) is called at the same time by 2 asynchronous theads. Both functions are called in both threads.

function RobuxStoreDataModule.getTransaction(player, purchaseId)
	local TransactionDataStore = DataStore2(GlobalEnums.playerDataKeys['ROBUX_PRODUCT_TRANSACTION'], player)
	local transctionsTable = TransactionDataStore:GetTable({})

	return transctionsTable[purchaseId]
end

function RobuxStoreDataModule.storeTransaction(player, purchaseId, success)
	local TransactionDataStore = DataStore2(GlobalEnums.playerDataKeys['ROBUX_PRODUCT_TRANSACTION'], player)
	
	TransactionDataStore:Update(function(currentValue)
		currentValue[purchaseId] = success
		return currentValue
	end)
end

The problem is that they are saving data in the same table and basically one transaction end up being overwrited. Here is an example to illustrate:
Output before run threads:

{
  ["transaction_1"] = true,
}

Output given after each thread runs storeTransaction function:
thread 1:

{
  ["transaction_1"] = true,
  ["transaction_2"] = true,
}

thread 2:

{
  ["transaction_1"] = true,
  ["transaction_3"] = true,
}
  1. What solutions have you tried so far?
    I tried using DataStore:Set(), but result is the same.

4 What I want to achieve?
One solution I can think of is to split every single product into a separate DataStore2 key, but I want to avoid that to not lose organization. How to solve this overwriting problem?

5 Aditional code
If it helps here’s the code where module functions are called:

-- The core 'ProcessReceipt' callback function
local function processReceipt(receiptInfo)
	-- Determine if the product was already granted by checking the data store  
	local productId = tostring(receiptInfo.ProductId)
	local purchased = false
	local success, errorMessage = pcall(function()
		local plr = Players:GetPlayerByUserId(receiptInfo.PlayerId)
		purchased = RobuxStoreDataModule.getTransaction(plr, receiptInfo.PurchaseId)
	end)
	-- If purchase was recorded, the product was already granted
	if success and purchased then
		print('granted')
		return Enum.ProductPurchaseDecision.PurchaseGranted
	elseif not success then
		error("Data store error:" .. errorMessage)
	end

	-- Find the player who made the purchase in the server
	local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
	if not player then
		-- The player probably left the game
		-- If they come back, the callback will be called again
		return Enum.ProductPurchaseDecision.NotProcessedYet
	end

	-- Call the handler function and catch any errors
	local success, result = pcall(function()
		if verifyAvailability(player, productId) then
			RewardsModule.giveRobuxProduct(player, productId)
			return true
		end
		return false
	end)
	if not success or not result then
		warn("Error occurred while processing a product purchase")
		print(result)
		print("\nProductId:", productId)
		print("\nPlayer:", player)
		WarnRobuxPurchaseErrorRE:FireClient(player, productId)
		return Enum.ProductPurchaseDecision.NotProcessedYet
	end

	-- Record transaction in data store so it isn't granted again
	local success, errorMessage = pcall(function()
		RobuxStoreDataModule.storeTransaction(player, receiptInfo.PurchaseId, true)
	end)
	if not success then
		error("Cannot save purchase data: " .. errorMessage)
	end

	-- IMPORTANT: Tell Roblox that the game successfully handled the purchase
	print('granted')
	return Enum.ProductPurchaseDecision.PurchaseGranted
end

-- Set the callback; this can only be done once by one script on the server! 
MarketplaceService.ProcessReceipt = processReceipt