How to implement pessimistic locking in a php/mysql web application? - php

How to implement pessimistic locking in a php/mysql web application?
web-user opens a page to edit one dataset (row)
web-user clicks on the button "lock", so other users are able to read but not to write this dataset
web-user makes some modifications (takes maybe 1 to 30 minutes)
web-user clicks "save" or "cancel" and the "lock" is removed
Are there standard methods in php/mysql for this scenario? What happens if the web-user never clicks on "save"/"cancel" but closes the internet-exploror?

You need to implement a LOCKDATE and LOCKWHO field in your table. Ive done that in many applications outside of PHP/Mysql and it's always the same way.
The lock is terminated when the TTL has passed, so you could do a substraction of dates using NOW and LOCKDATE to see if the object has been locked for more than 30 minutes or 1h as you wish.
Another factor is to consider if the current user is the one locking the object. So thats why you also need a LOCKWHO. This can be a user_id from your database, a session_id from PHP. But keep it to something that identifies a user, an ipaddress is not a good way to do it.
Finaly, always think of a mass-unlock feature that simply resets all LOCKDATEs and LOCKWHOs...
Cheers

I would write the locks in one centralized table instead of adding fields to all tables.
Example table structure :
tblLocks
TableName (The name of tha locked table)
RowID (Primary key of locked table row)
LockDateTime (When the row was locked)
LockUser (Who locked the row)
With this approach you can find all locks that are made by a user without having to scan all tables. You could kill all locks when user logs out for example.

Traditionally this is done with a boolean locked column on the record in the database that is flagged appropriately.
It is a function of this sort of locking that the lock has to be released, and circumstances may prevent this happening naturally (system crashes, user stupidity, dropped network packets, etc etc etc). This is why you would need to provide some manual unlock method and/or impose a time limit (maybe with a cron job?) on how long a record can be locked for. You could implement some kind of AJAX poll to keep the record locked if the browser is still open? At any rate, you would probably be best to verify the data in the record is the same as it was when the lock was aquired before you modify it.
This limitation of this type of behaviour is particularly prevalent in web applications, but is true of anything that uses this approach - Sage Line 50, for one, is a bugger for it, I regularly have to delete lock files after machine/application crashes.

Related

How to stop DB inputs for a certain amount of time from all current users

So I'm making a php website that sometimes requires a big data input from an admin-type user. This would not be frequent and only would happen to update or add certain data to the DB.
During this upload time which probably will take a minute or two, I cannot have other Users try to pull the data and and manipulate it as they would do normally as that could cause some major errors.
One solution would be to stop the servers for a time and put the site under maintenance for and admin-type user to upload the data locally.
However, there will never be more then 1-2 Users at a time on the site and these updates are short (1-2 mins) and infrequent. This makes this situation very improbable but still possible.
I was thinking of making some sort of entry in the User table that an admin could toggle before and after an update, and that my code would check before every User data manipulation. Then if after a User tries to save while that value is on, they would just have a pop-up or something that tells them to wait a few minutes.
Would this be OK? Is there a better way of going about this?
There is a way of locking a table so as to be the sole user of that table.
There includes READ locks and WRITE locks but in this situation a WRITE lock would probably be the solution.
A WRITE lock has the following features:
The only session that holds the lock of a table can read and write data from the table.
Other sessions cannot read data from and write data to the table until the WRITE lock is released.
To lock a table in mysql, simply use:
LOCK TABLE table_name WRITE;
If needed for more than one table, simply add them with a comma:
LOCK TABLES table_name1 WRITE,
table_name2 WRITE,
... ;
When the queries are finished, you can simply use UNLOCK TABLES; to unlock the tables.
Visit https://www.mysqltutorial.org/mysql-table-locking/ for a more complete tutorial on mysql table locking.

mysql - Row locking and releasing

I have two table 'reservation' and 'spot'.during a reservation process the 'spotStatus' column in spot table is checked and if free, it is to be updated. A user is allowed to reserve only one spot so to make sure that no other user can reserve the same spot, what can i do?
referring to some answers here,i found row locking,table locking as solutions. should i perform queries like
"select * from spot where spotId = id for update;"
and then performing necessary update to the status or is there other elegant ways to do it?
and my concern is what happens to the locked row if
1. Transaction doesnot complete successfully?
2. what happens if both user tries to reserve the same row at the same time? are both transactions cancelled?
and when is the lock released?
The problem here is in race conditions, that even transactions will not prevent by default if used naively - even if 2 reservations happen simultaneously, for example originating from 2 different Apache processes running PHP, transactional locking will just ensure the reservations are properly serialized, and as such the second one will still overwrite the first.
Usually this situation is of no real concern, given the speed of databases and servers as a whole, compared to the load on an average reservation site, the chances of this ever causing a problem are less than winning the state lottery twice in a row. If however you are implementing a site that's going to sell 50k Coldplay concert tickets in 30 seconds, chances rise aggressively.
A simple solution to this is to implement a sort of 'reservation intent' by not overwriting the spot reservation directly, but by appending the intent-to-reserve to a separate timestamped table. After this insertion you can then clean up this table for duplicates, preferring the oldest, and apply that one to the real-time data.
if its not successful, the database returns to the same data it was before the transaction (rollback) as if it never happened.
the same as it was not in the same time. only one of them will lock the db and the other wont be created.
If you are using a teradata you can use a queue table concept.

Data Blocking in PHP, MySQL

I'm writing a Queue Management System for a small clinic. There will be multiple users trying to do same thing, so these is a concurrency problem. I'm familiar with ACID guarantee and also understand notion of transaction. I know that two people can not change same data at the same time.
But here's my problem: I have a PHP function isFree($time) which determines if particular doctor is free for that time. I'm afraid that if both users try to call same function, both of them may get positive result and mess things up, so somehow I need to either queue concurrent users, or accept only one.
Easiest way to solve this problem would be to restrict, that my function can be called one at a time. I probably need some kind of flag or blocking system, but I have no exact idea on how to do it.
Or on the other hand, It would be even faster to only restrict those function calls, which may overlap. For example calling isFree($time) function for Monday and Tuesday at the same time won't cause any problems.
You're effectively asking for a lock.
I am guessing your queue system runs on MySQL for databases. if so, you can LOCK the table you're using (or on some database engines, the specific row you are using!). The structure is LOCK TABLES yourTableName READ.
This will effectively prevent anyone else from reading anything in the table until:
Your session is ended
You free the lock (using UNLOCK)
This is true for all database storage engines. InnoDB supports row-level locking through transactions. Instead of using a SELECT query, suffix it with FOR UPDATE to get a complete lock over the row(s) you just read.
This will hopefully shed more light on the locking mechanism of MySQL/innoDB. In order to free the lock, either UPDATE the row or commit the transaction.

"Last minute subscription" project PHP/MySQL atomicity issues

I'm working on an AJAX application. The user clicks a button and his name is saved into the database and shown inside a <div>, whose content is fetched from the database by means of an AJAX Long Polling. The database also contains a timestamp which represents an expiration: subscriptions beyond that timestamp must not be accepted. There is also a limit for users to subscribe.
I have a PHP script that is called by an AJAX request, this script queries the database and checks for expiration (the timestamp of the click is computed by JavaScript and sent via AJAX). It also checks for user limit: i have a N-to-N relationship between Users and Products (to subscribe for). These tasks obviously take time and I'm worried about possible concurrency problems. Should I use database transactions? What technique could I use to ensure the atomicity of this operation?
It depends on the kind of work that is done those "long" tasks.
Generic info:
If you're only inserting user driven data and data generated in PHP without it being read and/or cross-correlated with data fetched from the DB then transactionality should not be an issue.
If you're updating data and cross-correlating it with other elements in the DB then you need to start using transactions and to carefully choose the isolation levels of the transactions you plan on using.
Transactions can seriously affect speed when concurrency rises. Choosing a very safe isolation level may be safer than needed for your application and you may be adding a lot of unnecessary work to the MVCC.
Also using transactions as separate PHP api calls and managing the rollback logic in the application increases the overall duration of the transaction because it adds all the processing delays generated by PHP. If you can compact DB communications into a set of queries requested in one communication it would be better.
Case info:
Let's consider this scenario: there are 8 slots, 7 users subscribed. Two users click the subscribe button almost simultaneously. When the control script is launched for the last clicking user, the query for the subscription of the first clicking user might still be executed. This would imply that the system accepts both users as valid subscriptions.
This falls into the second case I explained, the case when you're cross-correlating user driven data with what you have in the DB. You're reading the state of the db before you commit the user drive data, so yes you would need transactions in this case.
There may be a possibility to speculate the inherent atomicity of one update statement. Any UPDATE table_name SET x = x+1 WHERE a = 'value'; is guaranteed to be atomic. You can use this to your advantage.
All subscribing PHP threads must first decrement a subscriber count. If the number of affected rows on the decrement is not 0 that means that the decrement was successful and they can carry on submitting the user-related data, else inform the user he was 0.3ms too slow.

What is the best method to make sure two people don't edit the same row on my web app?

I have a PHP/jQuery/AJAX/MySQL app built for managing databases. I want to implement the ability to prevent multiple users from editing the same database row at the same time.
What is this called?
Do I use a token system and who ever has the token can edit it until they release the token?
Do I use a "last edit date/time" to compare you loading the HTML form with the time in the database and if the database is the most resent edit then it warns you?
Do I lock the row using database functions?
I'm just not sure which is the best. Assuming between 10 - 15 concurrent users
There are two general approaches-- optimistic and pessimistic locking.
Optimistic locking is generally much easier to implement in a web-based environment because it is fundamentally stateless. It scales much better as well. The downside is that it assumes that your users generally won't be trying to edit the same set of rows at the same time. For most applications, that's a very reasonable assumption but you'd have to verify that your application isn't one of the outliers where users would regularly be stepping on each other's toes. In optimistic locking, you would have some sort of last_modified_timestamp column that you would SELECT when a user fetched the data and then use in the WHERE clause when you go to update the date, i.e.
UPDATE table_name
SET col1 = <<new value>>,
col2 = <<new values>>,
last_modified_timestamp = <<new timestamp>>
WHERE primary_key = <<key column>>
AND last_modified_timestamp = <<last modified timestamp you originally queried>>
If that updates 1 row, you know you were successful. Otherwise, if it updates 0 rows, you know that someone else has modified the data in the interim and you can take some action (generally showing the user the new data and asking them if they want to overwrite but you can adopt other conflict resolution approaches).
Pessimistic locking is more challenging to implement particularly in a web-based application particularly when users can close their browser without logging out or where users may start editing some data and go to lunch before hitting Submit. It makes it harder to scale and generally makes the application more difficult to administer. It's really only worth considering if users will regularly try to update the same rows or if updating a row takes a large amount of time for a user so it's worth letting them know up front that someone else has locked the row.
I was going to implement this into one of my own systems.
You could create new columns in your database of records, called timelocked.
When a record is opened, you would set the record they are opening's column for timelocked to the current time. During editing of the record, send a keepalive back to the server through ajax every 2 minutes. When sending the keepalive, the server will then increase the timelocked time to the current time the request was sent, and so fourth (this will make sense in a second). WHen the user is finished editing, set the timelocked to false.
Now, If someone went to open a record which is already open, the php would check -
if timelocked == false - would mean it's not being edited,
otherwise, the record may be being edited, but what if the user closed their browser window. that's why the keepalive is used.
if the difference between the current time and the timelocked is larger than 2 minutes, it means they're no longer lively editing, which would allow you to open it.
Hopefully you understand all that.
Don't try to prevent it. Let them decide what to do in the case of an edit conflict.
Add a timestamp to the table. Compare the timestamp of when the row was retrieved with the current timestamp. Make them aware of changes between their load and their save, and let them decide what action to take.
So yeah, number 3.
I personally would not prevent this. If it was a requirement of the job I would track the users' current / last known location and disallow someone from editing the same line someone else is editing this way. I have seen people add a row to a table saying isLocked or isBeingWorkedOn etc... but I have seen this type of system fail far more often as well, or require moderation to unlock stuck tables if someone closed it while working on it etc...
1) This is called locking. There are two main types of locking when referring to relational databases (like MySQL): table locking and row locking. Table locking ensures only one session at at time is making changes to a table, whereas row locking ensures only one session at a time is making changes to a particular row. You can think of row locking as a more fine-grained approach to concurrent access than table locking. Row locking is more complicated, but allows multiple concurrent sessions to write to the same table (important if your database has lots of concurrent writes--table locking should be fine for 10-15 users)
2-3) MySQL takes care of concurrent access for you! It automatically implements locking in the background. The type of locking (row or table) depends on which storage engine you use. For example, MyISAM uses table locking and InnoDB uses row locking. MySQL uses an internal table to manage this. You can query the status of this table (and all locks on your database) by checking the Table_locks_immediate and Table_locks_waited variables (it uses your option number 2).
When you issue an INSERT or UPDATE statement while another session is using the table (or row), the calling application (i.e. PHP in this case) will pause for a few milliseconds until the other session is done writing.
4) Again, MySQL will automatically take care of locking, but you can manually manage table locking with the LOCK TABLES and UNLOCK TABLES commands. If you are using row locking with InnoDB, there is a host of functions you can use to manually manage concurrent access.
See MySQL's page on Internal Locking for an overview of MySQL's locking system, and Concurrent Inserts for InnoDB's row locking features.
As others have said it's much easier to deal with a conflicting update.
What you are suggesting is called pesimistic locking. It's called thate because it's all too likely that two users will try and edit the same record at the same time.
Is that true?
And is it a disaster if a user has to start again, because the data they tried to update was changed by someone else.
Locking costs, you always lock in a pessimistic scheme, so you have an overhead, and that's before you start looking at related data and such.
Making it robust, dealing with no one can do it now coz sumfin' went wrong...
If I had something short of editing an entire file, that needed pessimistic locking, I'd be having a look at my design, on the basis that it isn't fit for purpose.

Categories