Voting system that being run at the same time not all saved - php

so i am working at voting system that have code like this
public function storeVote(Request $request)
{
$voting = Voting::findOrFail($request->voting_id);
if($voting->status == 1){
$checkVote = vote::where('voting_id',$request->voting_id)->where('name',$request->name)->where('voting_candidate_id',null)->first();
if($checkVote){
\DB::beginTransaction();
try{
$candidate = candidate::findOrFail($request->voting_candidate_id);
$skor = $candidate->skor + 1;
$candidate->skor = $skor;
$candidate->update();
$checkVote->voting_candidate_id = $request->voting_candidate_id;
$checkVote->update();
$vote_ok = $voting->vote_ok + 1;
$voting->vote_ok = $vote_ok;
$voting->update();
event(new VotingEvent($skor, $voting->id, $candidate->id));
CandidateProfile::flushCache();
\DB::commit();
return response()
->json([
'saved' => true,
'message' => 'Voting done.',
]);
} catch (\Exception $e){
\DB::rollBack();
abort(500, $e->getMessage());
}
}else{
return response()
->json([
'saved' => false,
'message' => 'sorry, you already vote'
]);
}
}else{
return response()
->json([
'saved' => false,
'message' => 'Sorry, Voting session not started yet'
]);
}
}
so this function act as a way for user to vote, the participant have a unique link where they only need to choose the candidate and then it will be trigger the function above
the problem is when i tested to do like 30 vote at the same time, half of them not saved.
any idea why?
update:
the data that are not saved:
candidate skor is not updated or not multiplied
voting information about vote_ok which mean total vote that being use

Note there is a catch when you use update queries. For eg: in you above code you are updating the candicate_skor using;
$skor = $candidate->skor + 1;
$candidate->skor = $skor;
$candidate->update();
The problem arises when your server gets multiple concurrent requests for the same route. For each of the requests (let's say you have 5 requests) the function retrieves the old candidate_skore value let's say it was equal to 1. Now when each of them updates the value DB value it will be equal to 2. Even though you have 5 upvote requests that should update the DB value to 6 it updates to just 2, causing you to lose 4 votes.
Ideally, you should keep a relation table for all the votes received and only insert it into that relation table. That way even if simultaneous requests are served all of them will insert new entries to the table. Finally, your total vote should be equal to the count of all rows in that relation table.

Related

Laravel can not update value in time. Simultaneously requests

I have PHP 8.3, and Laravel 9 project.
I have a post route for updating the balance column value. And function in controller below
public function loadFunds(FundToCardRequest $request)
{
$user = auth()->user();
$request['clientUsername'] = 'username';
$request['username'] = $user->username;
$sum = $request['amount'];
$request['amount'] *= (1 - config('commissions.credit_card_from_wallet') / 100);
$response = SomeService::post('updateBalace', $request->toArray())->collect();
if ($response->get('code') == 200) {
DB::transaction(function () use ($user, $request, $sum) {
$balance = $user->wallets()->where('currency', 'USD')->first()->pivot->balance;
$user->wallets()->updateExistingPivot(1, ['balance' => $balance - $sum]);
$user->transactions()->create([
The function receives a custom request with the following rules.
public function rules()
{
$balance_usd = auth()->user()->wallets()->where('currency', 'USD')->first()->pivot->balance;
return [
'amount' => ['numeric', 'required', new NotZeroAmount(), new SendMoneyBalance($balance_usd)],
'cardId' => ['required'],
'ArrayHashId' => ['required'],
];
}
There is a rule SendMoneyBalance that checking is the current balance enough to send amount of money.
The problem is the following. Sometimes clients can send two simultaneous requests. The first request take time for processing after which the balance should be decreased and the final amount in the balance should be not enough. But the second request passes the rules because while first balance can't update. After this two requests balance goes to a negative value.
Are there any techniques to prevent this kind of simultaneous request? Or something like await structures like in other languages.
This is called a race condition and what you basically want to do is to create some sort or unique lock per request per user or your preference.
Example
Cache::lock('foo', 10)->block(5, function () {
// Lock acquired after waiting a maximum of 5 seconds...
});
See here for ref

Prevent Multiple users executing query same time and redirecting to different sessions php/mysql

I have created schedule calls. each schedule have 1 unique session. In my case initially schedule session will be empty. If two or more members wants to join in schedule call they will click on start button. at that time i am checking session exists or not in table. If it is empty then i am generating session and updating session in db and returning that session.
But some times, multiple users clicks on start button at same time, they are going to else condition and updating different sessions and returning with respective session id. then they are redirecting to different session rooms. So how can i prevent this and return same session for all users for single schedule calleven they clicks at same time.
Below is my code which resulting different sessions for users when they click on same time.
$schInfo = Schedule::where('status', '=', 1)->where('id', '=', $scheduleId)->first();
if(isset($schInfo ->session) && !empty($schInfo ->session))
{
$schedule_session = $schInfo ->session;
} else
{
$schedule_session = CreateSession();
$res = Schedule::where('id', $scheduleId)->update(['session' => $schedule_session]);
}
return $schedule_session;
I have updated this like below. But i am not sure this is the right solution
$schInfo = Schedule::where('status', '=', 1)->where('id', '=', $scheduleId)->first();
if(isset($schInfo ->session) && !empty($schInfo ->session))
{
$schedule_session = $schInfo ->session;
} else
{
$schedule_session = CreateSession();
sleep(rand(1,5));
$res = Schedule::where('id', $scheduleId)->whereNull('session')->update(['session' => $schedule_session]);
$schInfo = Schedule::where('status', '=', 1)->where('id', '=', $scheduleId)->first();
$schedule_session = $schInfo ->session;
}
return $schedule_session;
In 1st code snippet out of 10 attempts 8 times generating different sessions. Using second snippet out of 10 times 1 or 2 times generating different sessions.
Please let me know is there any way to do?
This is no way a code solution, the code will not be use-able. However, the logic will be. Your issue is that you store the session before queuing the users to join the call. You should first make the user join a queue, then the hosting caller checks the queue every n seconds for participants (n seconds will allow time for multiple click issues). Then use the first participant in the calling queue to generate the session and append to the call.
You'll need feedback to the participant joining the call which I have not provided in this, but hopefully you get the gist of what I'm saying.
Front-End example of usage:
$(function() {
// Pull out the first connection in the queue, if any
var checkIncomingCallConnections = setInterval(function() {
$.get('/api/calls/incoming', function(response) {
if(response.hasUser()) {
// Your logic for updating or creating the call interface
// ...
clearInterval(this);
}
});
}, 1000);
$('#call-btn').click(function() {
// Generate the queue...
$.post('/api/calls/outgoing', function(response) {
// Your logic for updating or creating the call interface
// ...
checkIncomingCallConnections();
});
});
})(jQuery);
Back-End example of usage:
$schInfo = Schedule::where('status', '=', 1)->where('id', '=', $scheduleId)->first();
if(isset($schInfo ->session):
$schedule_session = $schInfo->session;
else:
$schedule_session = CreateSession();
$res = Schedule::where('id', $scheduleId)->update(['session' => $schedule_session]);
endif;
return $schedule_session;
API Outgoing:
Route::post('/calls/outgoing', function() {
CallQueue::create(referenceAboveCode()); // Should return $schedule_session
});
Route::get('/calls/incoming', function() {
$queue = CallQueue::list(referenceAboveCode()); // Should return $schedule_session
// $queue[0] joins the call
// update database with this session
});

Axios is returning Error: "Network error" when a query is too large

I have a query to create agenda based on 2 dates, 2 times, minutes, days of week and some other information that is stored in another table.
So each x minutes I'm storing a row in the agenda table, for each row I'm storing more rows in the other table pointing to the row stored in agenda.
The query works fine to an extent, I'm able to store data but if I want to store agenda for 5 months and above, I get Error: "Network Error" from Axios and my local server stops working so I have to restart it, Telescope is not telling me much other than a request with status 200, and the database won't store anything because I imagine the query is rolled back since it's wrapped in a Laravel transaction helper.
Clearly this is happening due to the "massive" amount of data that is trying to store, but I don't know why, it's not a timeout because I get the error like 8 seconds later and I raised that limit number up to 300 seconds or so in the php config.
createAgenda() {
Event.$emit('toggle-loading-screen');
this.errors = [];
axios.post('createagenda', {
date_start: this.agendaItem.start,
date_end: this.agendaItem.end,
time_start: this.agendaItem.from,
time_end: this.agendaItem.to,
valid_days: this.agendaItem.dayList,
minutes: this.agendaItem.minutes
entity_ids: this.agendaItem.entityList
}).then(response => {
if (response.data.success) {
Event.$emit('list-dates', false, true);
if (!response.data.intercepted) {
this.$emit('check-agenda');
Event.$emit('simple-modal', response.data.message, false, 0, true);
} else {
Event.$emit('simple-modal', response.data.message);
}
}
}).catch(error => {
if (error.response.status === 401 || error.response.status === 419) {
Event.$emit('reload-modal', error.response.data.message);
} else if (error.response.status === 500) {
Event.$emit('error-modal', error.response.data.message);
} else {
this.errors = error.response.data.errors;
Event.$emit('simple-modal', 'You have validation errors.');
}
}).finally(() => {
Event.$emit('toggle-loading-screen');
this.setExtraFlag('false');
});
},
When logging Error, All I get is this:
And this:
I need to know the exact cause of this error so I can prevent it somehow, but I'm honestly expecting to allow people to create agenda for at least 1 year.
Thanks in advance.

Codeigniter Unilevel MLM earning Distribution

I am here to have some help from you.
I am making a Unilevel MLM using Codeigniter
and now I can sucessfully add new member
But the problem is I need to distribute the earnings to other level
after a new member is successfully Added
See pic below:
Distribution of earnings
I need to distribute like the image above.
I hope you can help me with this guys.
Okay, I have a solution for you. The process i used is based on my understanding of the question.
So this is it, first i checked for a registration post, if a post request is made, i use the referral id from the post to fetch the number of registrations tied to that referral id that has not been given awarded the 100 earning. If the count of the result of this query is equal to 4, i loop through all of them and give them the earning of 100 and update their paid status to reflect that they have been paid then i insert the record, else i just insert the record.
So too much text, lets see the code
//this is the controller code
//first check for post of registration
if($_POST){
//kindly do your form validation here
$register = array(
"name" => $this->input->post('name'),
"refid" => $this->input->post('refID')
);
//during post, get the referral id from the post
$refID = $this->input->post('refID');
//before registering, use referral id to get the referred that have not been given earnings yet
$thereffered = $this->referral_m->getReferred($refID);
//check for the number of registration
if(count($thereffered) == 4){
//then get their ids and give them their earnings and update them to paid
foreach($thereffered as $referred){
$earnings = array(
"userID" => $referred->id,
"amount" => 100
);
$paid = array(
"paid" => 1
);
//give earning
$this->referral_m->giveEarning($earnings); //this adds the user id to earning table and give it an amount of 100
$this->referral_m->updateUser($paid, $referred->id); //this updates the user with the paid status
}
//then proceed to register the new record
$this->referral_m->register($register);
}else{
//register the new record
$this->referral_m->register($register);
}
//redirect after registration
redirect();
}else{
//load view here
}
This is how the model looks like
function getReferred($refID){
return $this->db->get_where('referral', array("refid" => $refID, "paid" => '0'))->result();
}
function giveEarning($record){
$this->db->insert('earnings', $record);
}
function register($array){
$this->db->insert('referral', $array);
}
function updateUser($array, $id){
$this->db->where('id', $id);
$this->db->update('referral', $array);
}
From the model, you would discover that i created 2 database tables, I assume you already have those tables created, just use the logic to update your code. If you find any difficulty, kindly comment lets sort it out

CakePHP how to add data from another field of a related model

This is one of my first applications out of tutorials so I don't know how to express my issue well.
Well I have these 2 tables:
User ( id, code )
Hours ( id, user_id, created)
I want to know how I can add an entry to the Hours table using the user_code.
I tried to grab the data of the User table with the code value and then findBy and pass for the patchEntity but it did not work.
I don't have a whole lot of information to work with, but I'll give it a go.
I want to know how I can add an entry to the Hours table using the
user_code
You mention using patchEntity, so that's updating information that's already there. Assuming user_code is the 'code' column you're talking about there, first find the user by his code:
$users_tbl = TableRegistry::get('Users');
// find the user
$user = $users_tbl->findByCode($user_code)->first();
if ($user) {
// replace '$this->request->data() with whatever patch data you wanted
$users_tbl->patchEntity($user, $this->request->data(), [
'associated' => ['Hours']
]
if ($users_tbl->save($user)) {
// success!
} else {
// error!
}
} else {
// error!
}
It will also depend on how you have the data you passed in (where my '$this->request->data() is, or whatever your array might be) - it needs to match the right column names and be in the correct format listed here.
However, this is updating the data. Just adding the data, you can load the hours table and add a new entry with the user_id acquired from the user search:
$hours_tbl = TableRegistry::get('Hours');
$hours = $hours_tbl->newEntity([
'user_id' => $user->id // $user populated from same method earlier
]);
/* assumed 'id' was autoincrementing and 'created' was populated
through Timestamp behavior */
if ($hours_tbl->save($hours)) {
// yay!
} else {
// boo
}

Categories