PDO begintransaction vs MySQL db lock - php

what is the advantage of using pdo begintransaction, is this and mysql db lock are same?
I have a table with urls and status column, whenever my application loads 10 urls I need to update the status column as loaded. This application will be accessed by couple of users simultaneously, how would I prevent user B from loading the same urls loaded by user A and before the update of the status column.
Please could anyone help me.

Transactions and table locks do different things. In your case, probably the easiest way to accomplish what you want is:
Lock the table for writing
Select 10 URLs where status = new
Set those 10 URLs to be status = processing
Unlock the table
For each URL, process, and set status = done

PDO::beginTransaction will make possible to rollbak changes if something went wrong with PDO::rollback, while lock tables will not.

Related

PHP MySQL Task API, Prevent Duplicate Records

I am building a PHP RESTful-API for remote "worker" machines to self-assign tasks. The MySQL InnoDB table on the API host holds pending records that the workers can pick up from the API whenever they are ready to work on a record. How do I prevent concurrently requesting worker system from ever getting the same record?
My initial plan to prevent this is to UPDATE a single record with a uniquely generated ID in a default NULL field, and then poll for the details of the record where the unique ID field matches.
For example:
UPDATE mytable SET status = 'Assigned', uniqueidfield = '3kj29slsad'
WHERE uniqueidfield IS NULL LIMIT 1
And in the same PHP instance, the next query:
SELECT id, status, etc FROM mytable WHERE uniqueidfield = '3kj29slsad'
The resulting record from the SELECT statement above is then given to the worker. Would this prevent simultaneously requesting workers from getting the same records shown to them? I am not exactly sure on how MySQL handles the lookups within an UPDATE query, and if two UPDATES could "find" the same record, and then update it sequentially. If this works, is there a more elegant or standardized way of doing this (not sure if FOR UPDATE would need to be applied to this)? Thanks!
Nevermind my previous answer. I believe I understand what you are asking. I'll reword it so maybe it is clearer to others.
"If I issue two of the above update statements at the same time, what would happen?"
According to http://dev.mysql.com/doc/refman/5.0/en/lock-tables-restrictions.html, the second statement would not interfere with the first one.
Normally, you do not need to lock tables, because all single UPDATE
statements are atomic; no other session can interfere with any other
currently executing SQL statement.
A more elegant way is probably opinion based, but I don't see anything wrong with what you're doing.

Confusion on timestamping: solution for concurrency issue

I'm currently developing a website and while working on database design, i had some concern on concurrency issue, I'm considering using timestamping to avoid this.
My understanding in timestamping is that it works this way:
There is a field for let's say "DateModified" wherein be updated every update on that specific row.
Then whenever there are 1 or more users accessing that row like reading first then eventually update it.
In my understaning of timestamping for this to work, I need a condition that will read first the "DateModified" like in my code.
readdatemodified = Select DateModified From Transaction where ID = ?
datemodified = Select DateModified From Transaction where ID = ?
IF datemodified == readdatemodified
UPDATE Transaction where ID = ?
ELSE
Message "There's someone updated the record. Please try again".
IF: UPDATE the record successfully
ELSE: Here the record will be retrieve again by accessing the database to ensure that the record is the updated one.
I solved the concurrency issue here but my new concern is how I access the database.
I will accessed the database multiple times every update?
Is there a way wherein I could minimize the database access using timestamping?
If you want the concurrency / checks in application logic, then try a CAS (Check And Set) algorithm, if you want concurrent changes to not happen, use transactions (as mentioned by Acyclic Tau)
Have you considered not using timestamping, but using transactions and locking reads:
http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
This might be a better solution to your problem. MySQL 'select for update' behaviour shows some examples of behaviour in the question.
The capabilities provided by locking are dependent on the underlying database engine you use:
MyISAM - Table level locking
InnoDB - Row level locking
A good overal description of capabilities and advantages can be found on the MySQL site here: http://dev.mysql.com/doc/refman/5.0/en/internal-locking.html

Could one user updates while other users cannot select

Is there a way in MySQL and PHP to allow only the person performing an update to view information about a particular record?
For example, if one user loads the page they are presented with a record which must be updated, until that user finishes updating this record, any other user accessing this page should not be able to view this particular record.
You'll have to manually lock the row (by adding a IsLocked column) when someone requests the edit page, since the connection to the database is lost as soon as the PHP script execution ends (despite pooling et al, script execution is stopped so you cannot unlock from the same thread again since that connection may go to another script).
Don't forget to create a kind of unlocking script, initiated by cron for example, to unlock rows that have been locked for more than a given amount of time.
I don't recommend adding any column to data table in question. I'd rather create special locks table to hold the information:
create table locks (
tablename varchar,
primarykey int,
userid int,
locktime datetime);
Then take the following principles into consideration:
each PHP request is a standalone mysql connection, that's why solutions like SELECT ... FOR UPDATE won't work and that's why you need to keep userid of a person - who actually did first request and performed the lock
every access to locks table must lock the table as a whole (using MySQL's LOCK statement) to avoid concurrency in locking the same row
if there is no way to know, whether particular uses has abandoned editing the row by closing the window with record - then either locktime timeout must be short or you should provide some ping (i.e. AJAX) mechanism that would reset locktime as long as user is working on locked record
user can save changes to record as long as he/she owns the lock and locktime did not expire
tablename and primarykey are of course samples and you should adjust them to your needs :-)
add a "locked" column to the table, and once a user calls the edit form, set the "locked" db value to the user_id, and after save set it back to false/null.
In your view action, check the locked value
You can either add a field named like locked where you set a status. Maybe you also add a field like lockedtime where you save a timestamp how long the lock is active. That depends on your needs.
There are also possibilities to do this native. Like
SELECT * FROM table WHERE primarykey = x FOR UPDATE;

Properly locking my database during a script run

I have no knowledge of locking whatsoever. I have been looking through some MySQL documentation and can't fully understand how this whole process goes about. What I need, is for the following events in my script to happen:
step 1) table user gets locked
step 2) my script selects two rows from table user
step 3) my script makes an update to table user
step 4) table user gets unlocked because the script is done
How do I go about this exactly? And what happens when another user runs this same script while the table is locked? Is there a way for the script to know when to proceed (when the table becomes unlocked?). I have looked into start transaction and select for update but the documentation is very unclear. Any help is appreciated. And yes, the table is innodb.
I believe what you are look for is the SELECT ... FOR UPDATE syntax available for InnoDB tables. This will lock only the records you want to update. You do need to wrap it in a transaction.
http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
For example, run your queries like this:
START TRANSACTION
SELECT ... FOR UPDATE
UPDATE ...
COMMIT
Eliminate step 2 by performing your select query as part of your update call. Then MySQL takes care of the rest. Only one write query can run at the same time, others will be queued behind.

Table [tablename] is not locked

I am writing a MySQL query that locks a table:
"LOCK TABLE table_1 WRITE"
After that i am executing some functions, and in one of those functions, I am executing another query, on another table that I haven't locked:
"SELECT * FROM completely_different_table_2"
Then i get the following error message as result:
Table 'completely_different_table_2' was not locked with LOCKED TABLES
Indeed, MySql is right to tell me that the table is not locked. But why does it throws an error? Anyone any ideas how I could solve this?
Thanks in advance.
You have to lock every table, that you want to use until the LOCK is released. You can give completely_different_table_2 only a READ LOCK, which allows other processes to read this table while it is locked:
LOCK TABLES table_1 WRITE, completely_different_table_2 READ;
PS: MySQL has a reason to do so. If you request a LOCK, you want to freeze a consistent state of your data. If you read data from completely_different_table_2 inside your LOCK, your data written to table_1 will in some way depend on this other table. Therefore you don’t want anyone to change this table during your LOCK and request a READ LOCK for this second table as well. If your data written to table_1 doesn’t depend on the other table, simply don’t query it until the LOCK is released.

Categories