I am currently working on a project to uses Yii and stumbled across something that made me scratch my head. I started a db transaction using Yii (which just calls PDO::beginTransaction) and did some database stuff and at the end, store a flash message for the user and do a redirect. I forgot though to commit the transaction so nothing got stored in my database, but what caught my attention is that my flash message also did not appear. Doing a commit or rollback makes the flash message appear just fine.
Basically, I noticed that I could not store any session related data and have it stick after a redirect if I started a transaction and didn't commit/rollback. I normally don't leave transactions hanging so I never noticed this behavior before.
So is there a relationship between the 2 that would prevent Sessions from working properly?
Session is written to the database at the end of the request. If you make an explicit rollback, it still gets written to the db outside of the transaction. If you don't, the rollback happens implicitly AFTER the session saving queries are run.
Related
We have a SPA app with big traffic and sometimes occasionally double rows are inserted in several parts of application.
For example user registration. Normally the validation mechanism will do the trick checking if email address already exists, however I was able to reproduce the problem by dispatching the same request twice using axios resulting doubled user in the database.
I initially thought that the second request should throw validation error but apparently it's too quick and checks user before the first request was able to store it.
So I put 500ms delay between those requests and yes it worked, the second request thrown a validation error.
My question is, what are the techniques to prevent double inserts IF TWO REQUESTS ARE ALREADY DISPATCHED IN THE SAME FRACTION OF A SECOND?
Of course we have blocked submit form button (since the beginning) after making first request, yet people somehow manages to dispatch requests twice.
One option I've utilized in the past is database locking. I'm a bit rusty on this, but in your case:
Request WRITE LOCK for the table
Run SELECT on table to find user.
Run INSERT on table.
Release WRITE LOCK.
This post on DB locking should give you a better idea of what locks apply what affect. Note: some database systems may implement locks differently.
Edit: I should also note that there will be additional performance issues using database locks.
We've recently upgraded an old Codeigniter app from 2.1.0 to 3.1.9, and everything has gone smoothly. Except, that the new session locking is causing issues and I'm wondering the proper way to fix it.
The app uses AJAX heavily, however most of the AJAX calls don't write to the session and don't seem to break it.
Here is an example of the issue: there is a GUI with checkboxes, and when the input is changed (a checkbox is checked or unchecked) an AJAX call was made. On the other end of that AJAX call which boxes were checked were written to session so that they would be remembered from visit to visit. However, if you checked/unchecked multiple boxes causing multiple AJAX calls to go out, you would end up getting logged out. Similar behavior has been discovered around the app, all where session writes are happening.
I've tried implementing session_write_close() as suggested by the Codeigniter documentation but that only half worked in some spots, and caused more issues in area where there were no issues before. The app has a few endpoints that do all the work and all work flows share, so fixing the endpoint where the session writes are happening with session_write_close() breaks other script calls when they continue to need the session.
The short term solution I've come up with is to debounce the AJAX calls (which helps but doesn't solve the problem by itself) and to disable inputs until the AJAX call has finished.
Is there a better long term solution? Ultimately this app is being phased out, so spending a long time rewriting it isn't feasible.
The only long-term solution is to properly use session_write_close().
As you undoubtedly understand, session data is locked so only one script at any time can write to the session's persistent datastore. Session locking prevents hard to troubleshoot concurrency bugs and is more secure.
Without seeing your implementation it's really hard, er... impossible to offer any precise advice. Here are some things to consider that might help sort out the mess.
Either do ALL or NONE of the session writes in the AJAX response functions. (By "AJAX response function" I mean the PHP controller/method value of the AJAX url.)
With the ALL approach call session_write_close() in the "main" script before making any AJAX requests. Keep in mind that $_SESSION is not affected by session_write_close(). All $_SESSION items in the main script will remain accessible so you can reliably read the values. However, changes made to $_SESSION will not be written because, as far as PHP is concerned, the session is closed. But that's only true for the script that calls session_write_close().
With the NONE approach you may still need to read session data. In that case it would be wise to have the AJAX response functions call session_write_close as soon as possible to minimize the time concurrent requests are blocked. The call is more important for functions that require significant time to execute. If the script execution time is short then the explicit call to session_write_close() is not needed. If at all possible, i.e. no need to read session data, then not loading the session class might result in cleaner code. It would definitely eliminate any chance of concurrent request blocking.
Don't try to test session behavior by using multiple tabs to the same app on the same browser.
Consider using $config['sess_time_to_update'] = 0; and then explicitly call $this->sess_regenerate((bool) config_item('sess_regenerate_destroy')); when and where it makes sense that the session id needs to be changed, i.e. right after login; right after a redirect to a "sensitive" page; etc.
What follows next is offered with a large amount of trepidation. I've tested this using the "files" driver, but not extensively. So, buyer beware.
I found that it is possible to "re-start" a session by calling the PHP function session_start() after session_write_close() has been used. CodeIgniter will open and read the session datastore and rebuild the $_SESSION superglobal. It's now possible to change session data and it will be written when script execution ends - or with another call to session_write_close().
This makes sense because session_write_close() does not "do" anything to the CodeIgniter session object. The class is still instantiated and configured. CodeIgniter's custom SessionHandlerInterface is used to open, read, and write session data after session_start() is called.
Maybe this apparent functionality can be used to solve your problems. In case I wasn't clear earlier - use at your own risk!
I have a php application where the user can make some changes to an oracle database with adodb.
After the request is executed, the page is refreshed and the user can see the result.
How would I add an undo option of this UPDATE after refreshing the page?
I've tried beginTrans(), but it seems like it automatically rollbacks after the php script is executed.
Database transactions are tied to a single connection. Connections are normally closed when the PHP script finishes and trying to make a connection persist for the same user on multiple requests would be very problematic.
As much as possible, it's best to treat HTTP requests as stateless. Meaning, changes should be committed to the database at the end of every request and an undo in HTTP should probably not be concerned with rolling back a previous transaction, but also actually committing changes in the database.
I am developing an app for owncloud and am using the Owncloud API, not the App Framework.
In this environment I can start SQL-Transactions via \OCP\DB::beginTransaction(); and I can commit the transaction cia \OCP\DB::commit();.
But I can't find a way to rollback a transaction. I googled it all day and searched through the Owncloud core files but couldn't find a way to do it.
Does anyone know how to do this? Right now I can just leave the transaction uncommited in my ajax requests, because they have only one transaction. But in other scripts I have to do multiple transactions one after another which are independent from another. I have to manually delete all my inserted rows in case anything goes wrong, which is not very nice.
Edit 2014/07/30:
I have found out that the OC_DB_StatementWrapper-Class, which is return by \OCP\DB::prepare of Owncloud does not provide a method to do this. However, it passes all unknown calls to the underlying \Doctrine\DBAL\Driver\Statement-object. This class is described here: Doctrine.DBAL.Statement
It has a private $_conn (instance of \Doctrine\DBAL\Connection) which has a method rollback to rollback a transaction. However, $_conn is private, so I can not access it.
I have finally found a solution myself. To those interested in how it works, here is the solution:
$conn = \OC::$server->getDatabaseConnection();
$conn->rollBack();
This will rollback the previously via \OCP\DB::beginTransaction() opened transaction. To start a new transaction, just call \OCP\DB::beginTransaction() again - works like a charm.
If I have a database.php class (singleton) which reads and writes information for users of my web application, what happens when simultaneous requests for the same database function is called?
Is it possible that the database class will return the wrong information to other users accessing the same function at the same time?
What other similar problems could occur?
what happens when simultaneous requests for the same database function is called? Is it possible that the database class will return the wrong information to other users accessing the same function at the same time?
Absolutely not.
Each PHP request is handled entirely in it's own process space. There is no threading, no application server connection pool, no shared memory, nothing funky like that. Nothing is shared unless you've gone out of your way to do so (like caching things in APC/memcached).
Every time the application starts, your Singleton will get created. When the request ends, so does the script. When the script exits, all of the variables, including your Singleton, go away with it.
What other similar problems could occur?
Unless you are using transactions (and if you're using MySQL, using a transaction-safe table type like InnoDB), it is possible that users could see partial updates. For example, let's say that you need to perform an update to three tables to update one set of data properly. After the first update has completed but before the other two have completed, it's possible for another process to come along and request data from the three tables and grab the now inconsistent data. This is one form of race condition.