-
Notifications
You must be signed in to change notification settings - Fork 3
New driver fixes from manual testing #51
Conversation
ec80726
to
34999cd
Compare
…SY) in multi-process environments
@@ -735,7 +735,31 @@ public function execute_sqlite_query( string $sql, array $params = array() ): PD | |||
*/ | |||
public function begin_transaction(): void { | |||
if ( 0 === $this->transaction_level ) { | |||
$this->execute_sqlite_query( 'BEGIN' ); | |||
/* | |||
* When we're executing a statement that will write to the database, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd add this excerpt:
Transactions can be DEFERRED, IMMEDIATE, or EXCLUSIVE. The default transaction behavior is DEFERRED.
DEFERRED means that the transaction does not actually start until the database is first accessed.
IMMEDIATE causes the database connection to start a new write immediately, without waiting for a write statement. The BEGIN IMMEDIATE might fail with SQLITE_BUSY if another write transaction is already active on another database connection.
It also makes me question why BEGIN IMMEDIATE works – this passage suggests it should not. Is there any way we can add a deterministic unit test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@adamziel I added the docs: 31c319c
It also makes me question why BEGIN IMMEDIATE works – this passage suggests it should not.
I think it works because even though upgrading a read transaction to a write transaction fails immediately when another write is in progress, the BEGIN IMMEDIATE
statement can honor the busy_timeout
, as there is no need to restart a full transaction.
Is there any way we can add a deterministic unit test?
I can give it a shot. Probably not so easy to do in PHP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Maybe starting two connections in a single thread would suffice? If we can avoid synchronizing multiple processes it may be pretty easy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@adamziel I don't have any async query APIs at hand, so I'm quite limited in what I can do. I can create a locked state, and I could measure that it applies a timeout, but that's about it:
$driver1->query('CREATE TABLE t (id INT)');
$driver1->query('BEGIN');
$driver2->query('INSERT INTO t (id) VALUES (2)'); // this waits for $driver1 to finish, and timeouts
$driver1->query('COMMIT'); // but we never get to this, because all the APIs are sync
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see what you mean. Would this help? https://github.com/clue/reactphp-sqlite
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@adamziel I think so, yeah, should we add it for this test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@adamziel Ah, but installing that package will actually not give the SQLite driver the async APIs (we're using PDO), so I'm afraid it's not an easy solution either. I had some luck with pcntl_fork, maybe I can try to finish that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Symfony\Process is pretty neat FWIW. I use it in the new Blueprint runner. But it's not quite forks so pcntl might be for the best
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only have one note that may or may not be blocking. Really good work here, thank you 🙌
List of discovered issues:
_mysql_data_types_cache
are sometimes not correctly fetched, as they can be saved in multiple formates (lowercase, uppercase, with or without backticks).0
for an integer,0000-00-00 00:00:00
for a date, etc.DEFAULT CURRENT_TIMESTAMP
saves a'CURRENT_TIMESTAMP'
string rather than the correct value).BINARY
type is saved asINTEGER
, but it's actually a binary string, andBLOB
would be more suitable.2025-3-7 9:5:2
is a valid datetime/timestamp value in MySQL, but SQLite requires it to be2025-03-07 09:05:02
.)SQLITE_BUSY
).