I am trying to work with postgresQL with graphql in express server.
The problem I am facing is I want to update the database's value when user call the API in client. When it is called twice in short time, as the user's money and product's quantity is not yet updated, both function will get the some money, execute and the product's quantity and user's money will be negative which is not correct.
What is a simple implementation of the lock function so that that specific user's datarow and product's datarow cannot be access and modify until the previous process is done so that it is thread-safety?
Edit: I know I can added a lock in local cache, but I would like to ask what is the typical handle method? Such as I will just return the process is running when it is locked? Or I just create a loop to wait until first request is done? And if there is any good example of code that I can follow on.
#Mutation(() => Boolean)
async pay(#Arg('amount') amount: number) {
let userId = 1
let proeductId = 1
const user = await User.findOne(userId); // user = {userId: 1, money: 50}
const product = await Product.findOne(userId); // product= {userId:1, quantity: 1, price: 50}
await new Promise(resolve => setTimeout(resolve, 3000)) // some logic
if(user && product && user?.money >= product.price && product.quantity > 0 ){
await getConnection().getRepository(User).decrement({ userId }, 'money', 50);
await getConnection().getRepository(product).decrement({ userId }, 'quantity', 1);
}
}
}
Using .setLock("pessimistic_write") will solve the problem
const user= await getRepository(User)
.createQueryBuilder()
.useTransaction(true)
.setLock("pessimistic_write")
.where({id:userId})
.getOne();
const product = ...
if(user && product && user?.money >= product.price && product.quantity > 0 ){
await getRepository(User)
.createQueryBuilder()
.useTransaction(true)
.update(User)
.set({money: user.money- amount})
.where({id:userId})
.execute();
await getRepository(product)...
}
Related
I am working on a React Js, Firebase BIRTHDAYS web app which allows login using firebase. Users can add their friends birthdays in there. It will show the personalized list of birthdays.
So, Here I want to send notifications every day at some specific time for all the users using Firebase Cloud Messaging. And also the body of it should be different for every user and should contain the list of birthdays they are having that day(when the notification is sent) from Firebase Database. I tried a lot. But none of them worked. Is there a way that I can achieve this?
Thanks.
Things I tried
Used only React to send notification. But the problem here is that we should not close the web app and for Mobiles addEventListener("visibilitychange", () => {}) is not working.
let notification
let interval
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") {
let date = new Date()
let dateNumber = date.getDate()
let monthId = date.getMonth() + 1
let hours = date.getHours()
let minutes = date.getMinutes()
if (hours === 0) {
let requiredNames = []
if (allBirthdays.length !== 0) {
requiredNames = allBirthdays
.filter((p) => p.monthId == monthId && p.day == dateNumber)
.map((p) => {
return `${p.name}`;
})
}
notification = new Notification(`Today's Birthdays(${dateNumber}-${monthId})`, {
body: `${requiredNames.length !== 0 ? requiredNames : "No Birthdays"}`,
icon: logo,
tag: "Same Time"
})
}
interval = setInterval(() => {
date = new Date()
dateNumber = date.getDate()
monthId = date.getMonth() + 1
hours = date.getHours()
minutes = date.getMinutes()
if (hours === 0 && minutes < 15) {
let requiredNames = []
if (allBirthdays.length !== 0) {
requiredNames = allBirthdays
.filter((p) => p.monthId == monthId && p.day == dateNumber)
.map((p) => {
return `${p.name}`;
})
}
notification = new Notification(`Today's Birthdays(${dateNumber}-${monthId})`, {
body: `${requiredNames.length !== 0 ? requiredNames : "No Birthdays"}`,
icon: logo,
tag: "Next Interval"
})
}
}, 900000);
} else {
if (interval) clearInterval(interval)
if (notification) notification.close()
}
})
I found some videos on it but all explain only till we Test push notification from firebase but not the real-time implementation.
FCM basically just a service that you can send message from server to client no matter app is in background or terminated. The server need to know which device you want to deliver.
The way can achive this is:
Register all users token to server
Setup a scheduled cloud functions for run once per day
Run query from rtdb to get which users friend today is his birthday, Also grab these users token then deliver message to device.
I'm in the process of debugging the /purge command for my Discord bot.
My intention is to fetch the entirety of a text channel, and delete any amount of messages, by calling the TextChannel.bulkDelete method multiple times, since that method has a limit of deleting 100 messages at a time. This is my code:
async purgeDelete(
channel: TextChannel,
amount: number | undefined,
target: GuildMember | undefined,
keyword: string | undefined
): Promise<number> {
// Most confused about this line: Am I doing the right thing?
const messages = await channel.messages.fetch();
const twoWeeksAgo = new Date();
twoWeeksAgo.setDate(twoWeeksAgo.getDate() - 14);
const purgelist = messages.filter(message => (
(!target || message.author.id === target.id)
&& (!keyword || message.content.includes(keyword))
&& this.resultMessage?.id !== message.id
&& message.createdAt > twoWeeksAgo
));
let purgeAmount: number;
if (amount === undefined) {
purgeAmount = purgelist.size;
} else {
console.log(purgelist.size, messages.size);
purgeAmount = Math.min(amount, purgelist.size);
}
const slicedPurgelist = purgelist.first(purgeAmount);
const partitionedPurgelist = [];
for (let i = 0; i < slicedPurgelist.length; i += 100) {
partitionedPurgelist.push(slicedPurgelist.slice(i, i + 100));
}
await Promise.all(partitionedPurgelist.map(messages => channel.bulkDelete(messages)));
return purgeAmount;
}
I'm pretty sure the only line that matters is the fetch() call. When called in my program, the API is giving me 50 messages. Is that intentional? I know there is an option for limit, but that only goes up to 100. If there is any workarounds to this, please let me know!
The Discord API has a hard limit of 100 messages per GET request. Unfortunately, this is a hard limit you can't bypass, and is intentional on Discord's part.
Furthermore, fetching the entirety of a text channel is probably a bad idea, especially with larger servers which could have 100k+ messages per channel.
A "sort-of" workaround is to use the before param in FetchMessageOptions plus a loop to continue fetching messages. See below for an example:
const messages = [];
const messagesToFetch = 1000
while(messages.length < messagesToFetch) {
// Handle first run
if(!messages.length) {
const msg = await channel.messages.fetch({ limit: 100 })
messages.push(msg)
continue;
}
// Fetch messages before the oldest message in the array
messages.push(await channel.messages.fetch({ limit: 100, before: messages[0].id }))
}
My goal is to get multiple data based on a list of data the customer requested so I put the codes inside useEffect. If the array contains the list of things the customer wants, then it grab those data from the server so the user can manipulate it. So far, it works fine but when the database updates, onValue is not triggered to grab the new data to update the render.
Here is my code. Thank you for helping me in advance.
// Getting data
useEffect(() => {
if (empDataArr.length > 1) {
let fromDay = parseInt(dateHandler(startDate).dateStamp);
let toDay = parseInt(dateHandler(endDate).dateStamp);
let tempLogArr = [];
empDataArr.forEach((emp) => {
let qLogEvent = query(child(shopRef(shopId), emp.id + "/log_events"), orderByChild("dateStamp"), startAt(fromDay), endAt(toDay));
// This is the part I need help
onValue(qLogEvent, (snap) => {
let logEventArr = [];
let val = snap.val();
if (val === null) {
} else {
Object.keys(val).forEach((key) => {
let id = key;
let dateStamp = val[key].dateStamp;
let direction = val[key].direction;
let time = val[key].timeStamp + "";
let timeStamp = time.substring(8, 10) + ":" + time.substring(10, 12);
logEventArr.push({ direction: direction, timeStamp: timeStamp, dateStamp: dateStamp, id: id });
});
tempLogArr.push({
id: emp.id,
logEvent: logEventArr,
});
}
});
});
setLogDataArr(tempLogArr.map((x) => x));
}
}, [empDataArr, shopId, startDate, endDate]);
useEffect(() => {
console.log(logDataArr);
}, [logDataArr]);
I have tried using return onValue() and const logData = onValue() but they do not work (and I do not expect the former one to work either).
I am 90% done with my application system but I am missing one thing
I am trying to add roles when someone applies,
I tried doing it with this
let teamRole = message.guild.roles.cache.find(role => role.id == "761996603434598460")
member.roles.add(teamRole)
but it does not add the roles (I don't get any errors doing it)
is there any way I can do it with the code below for the interaction?
client.ws.on("INTERACTION_CREATE", async (interaction) => {
// If component type is a button
if (interaction.data.component_type === 2) {
const guildId = interaction.guild_id;
const userId = interaction.member.user.id;
const buttonId = interaction.data.custom_id;
const member = client.guilds.resolve(guildId).member(userId);
if (buttonId == "send_application") {
// Reply to an interaction, so we don't get "This interaction failed" error
client.api.interactions(interaction.id, interaction.token).callback.post({
data: {
type: 4,
data: {
content: "I have started the application process in your DM's.",
flags: 64 // make the message ephemeral
}
}
});
I would appreciate the help with this
When you add a role it should be a snowflake value, so you should add using the ID and not the role it self
Incorrect:
let teamRole = message.guild.roles.cache.find(role => role.id == "761996603434598460")
member.roles.add(teamRole)
Correct:
// using the ID Directly
member.roles.add('761996603434598460')
I'm trying to do is whenever someone joins the server, the bot sends a rich embed with their ID, their user creation date and the new total members, but whenever i run it and test it, it says that .createdAt() is not a valid function, so i'm completely lost as to what to do.
client.on("guildMemberAdd", member => {
let mlogchannel = member.guild.channels.find((channel => channel.name === "member-logging"));
if (mlogchannel) {
console.log(client.users.find(user => user.id === member.id).createdAt())
var cdate = moment.utc(User.createdAt()).format("dddd, MMMM Do YYYY, HH:mm");
const sInfo = new Discord.RichEmbed()
.setTitle(`Member joined`)
.setAuthor(`${member.displayName}`)
.setColor(8528115)
.setFooter(`User ID: ${member.id}`)
.setTimestamp()
.setThumbnail(member.user.createdAt())
.addField("Total members", `${Guild.members.filter(member => !member.user.bot).size}`, true)
.addField("Creation Date:", `${cdate}`, true);
let ageS = moment(cdate).fromNow(true)
let ageA = ageS.split(" ");
if (ageA[1] = "days" && ageA[2] >= 30) {
Guild.channels.find((channel => channel.name === "member-logging").send(sInfo));
mlogchannel.send("**WARNING!**\nThis account is less than 30 days old and may have been made to bypass a server mute or ban!")
}
if (ageA[1] != "days") {
mlogchannel.send(sInfo)
}
if (!mlogchannel) {
return console.log(`${Guild.name}:${Guild.ID} Has not set up a member log channel!`)
}
}
})
User.createdAt is a property of User, not a method. So instead of .setThumbnail(member.user.createdAt()), it would be .setThumbnail(member.user.createdAt).