2

I am building a turn-based multiplayer game with Flash and PHP. Sometimes two users may call on the same PHP script at the same time. This script is designed to write some information to the database. But that script should not run if that information has already been written by another user, or else the game will break. If PHP processes these scripts sequentially (similar to how MySQL queues up multiple queries), then only one script should run in total and everything should be fine.

However, I find that around 10% of the time, BOTH user's scripts are executed. My theory is that the server sometimes receives both user requests to run the script at exactly the same time and they both run because neither detected that anything has been written to the database yet. Is it possible that both scripts were executed at the same time? If so, what are the possible solutions to this problem.

5
  • Depends on the server configuration, but usually yes, they can. Commented Jun 17, 2013 at 8:44
  • implement concurrency control. Commented Jun 17, 2013 at 8:46
  • One of the simpler solutions is to put a flag in the beginning of the script and take it off on ending. If the script sees that flag is on, it stops. This flag can be for example a database field, external file etc. Commented Jun 17, 2013 at 8:48
  • @ Voitcus, my script is currently set up to detect if a flag is present in the database before it is run. But somehow, I think they are still overwriting each other. If both scripts are run at the same time, both will first run a select statement on the database and will detect that no flag exists. As a result, both scripts will continue to run and overwrite each other. Commented Jun 17, 2013 at 9:04
  • This shouldn't happen if you're using a proper transaction isolation level on your database. Commented Jun 17, 2013 at 10:00

5 Answers 5

2

THis is indeed possible. You can try locking and unlocking tables at the beginning and end of your scripts.

Though this will slow down some requests, as they would have to first wait for the locked tables to be unlocked.

Sign up to request clarification or add additional context in comments.

Comments

1

It doesnt matter, if it is PHP, C, Java what ever. At the same time can run max only as much processes, as you have CPUs (and cores). There can be running lets say 100 processes at the same time, if you have only 2 cores. Only 2 are running, rest is waiting.

Now it depends what you see under run. If you take it as active or if you take also waiting processes. Secondly, it depends on your system configuration, how many processes can wait and on your system specs.

1 Comment

This isn't strictly about two separate and uninterrupted processes, it's about race conditions which can even happen on single CPU.
1

Sounds, at first glance, like what keeps a 2nd instance of the script to roll just does not happen fast enough, 10% of the time... I understand that you already have some kind of a 'lock' like someone told you to add, which is great; as someone mentioned above, always put this lock FIRST THING in your script, if not even before calling the script (a.k.a in parent script). Same goes for competing functions / objects etc...

Just a note though, I was directed here by google and what i wanted to find out is if script B will run IN AN IFRAME (so in a 'different window' if you wish) if script A is not finished running; basically your title is a bit blurry. Thank you very much.

Fortunately enough we're in the same pants : I'm programing an Hearthstone-like card game using php (which I know, ain't suited for this at all, but I just like challenging tasks, (and okay, that's the only language i'm familiar with)). Basically I gotta keep multiple 'instants' or actions if you prefer from triggering while another set of global event/instant - instants - sub-instants is rolling. This includes NEVER calling a function that has an event into it into the same rolling snipet, EXCEPT if I roll a while on a $_SESSION variable with value y that only does sleep(1) (that happens in scritpt A); while $_SESSION["phase"] == "EndOfTurnEffects" and then continue to roll until $_SESSION["phase"] == "StandBy" (other player's turn), and I wish script B to mofity $_SESSION["phase"]. Basically if script B does not run before script A is done executing, I'm caught in an endless loop of the while statement...

1 Comment

4.5 years later, I've returned to that project since January 2024 and it now works all in JavaScript; It took about 2-3months translating all this PHP garbage to JS...
0

That's very plausible that they do. Look into database transactions.

Briefly, database transactions are used to control concurrency between programs that access the database at the same time. You start a transaction, then execute multiple queries and finally commit the transaction. If two scripts overlap each other, one of them will fail.

Note that isolation levels can further give fine grained control of how much the two (or more) competing scripts may share. Typically all are allowed to ready from the database, but only one is allowed to write. So the error will happen at the final commit. This is fine as long as all side effects are happening in the database, but not sufficient if you have external side effects (Such as deleting a file or sending an email). In these cases you may want to lock a table or row for the duration of the transaction or set the isolation level.

2 Comments

I understand that MySQL does not allow rows to be edited by more than one query at a time, since that row is locked when it is being edited by a another query. However, the problem is that PHP may allow multiple scripts to run at the same time. These scripts both query the database and detect that the row is empty, and so they both execute their script and overwrite eachother when only one is intended to be executed.
That is exactly the type of scenario that transactions will protect you against.
0

Here is an example of SQL table locking that you can use so that the first PHP thread which grabs the DB first will lock the table (using lock name "awesome_lock_row") until it finally releases it. The second thread attempts to use the same table, and since the lock name is also "awesome_lock_row"), it keeps on waiting until the first PHP thread has unlocked the table.

For this example, you can try running the same script perhaps 100 times concurrently as a cron job and you should see "update_this_data" number field increment to 100. If the table hadn't been locked, all the concurrent 100 threads would probably first see "update_this_data" as 0 at the same time and the end result would have been just 1 instead of 100.

<?php
$db = new mysqli( 'host', 'username', 'password', 'dbname'); 

// Lock the table
$db->query( "DO GET_LOCK('awesome_lock_row', 30)" ); // Timeout 30 seconds

$result = $db->query( "SELECT * FROM table_name" );
if($result) {
    if ( $row = $result->fetch_object() )
        $output = $row;
    $result->close();  
}

$update_id = $output->some_id;

$db->query( UPDATE table_name SET update_this_data=update_this_data+1 WHERE id={$update_id} );

// Unlock the table
$db->query( "DO RELEASE_LOCK('awesome_lock_row')" ); 
?>

Hope this helps.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.