See http://drupal.org/node/686196#comment-2742526
My own local testing shows that queries to information_schema take at least 0.6s each - so it's extremely likely that these calls are the cause of apache timeout issues on the installer.
This patch just removes the check, tries to make the table, and catches the exception afterwards. I do get a nice big red error with the correct error message, but I also get notices in drupal_add_http_header(). I'm exceptions stupid, so anyone wants to fix that would be great.
I expect this would have some improvement on simpletest run times as well, would be interesting to compare if anyone has time.
Comment | File | Size | Author |
---|---|---|---|
#41 | 748340-mysql-exists-d7.patch | 1.66 KB | Crell |
#40 | 748340-mysql-exists-d7.patch | 1.08 KB | andypost |
#28 | remove-db_create_table.patch | 1.39 KB | bcn |
#23 | 748340-check-db-d7.patch | 2.31 KB | andypost |
#9 | createtable_3.patch | 1.27 KB | Berdir |
Comments
Comment #1
andypostdb_create_table() already without checking, so maybe we remove all checks for existance in object creation functions? db_add_*
Also return value is FALSE when exception but ???NULL when success, I think this should be unified
Comment #2
catchComment #3
catchComment #5
aspilicious CreditAttribution: aspilicious commented#3: createtable.patch queued for re-testing.
Comment #6
Crell CreditAttribution: Crell commentedCatching an exception just to throw it again without changes is useless. We want to catch it and then rethrow a DatabaseSchemaObjectExistsException for consistency with the rest of the Schema API.
Comment #7
BerdirOk, what about this?
Comment #8
Crell CreditAttribution: Crell commentedThat's more like it. We should probably document why we're doing it this way instead of a pre-check like everything else, though. Otherwise it's going to confuse the heck out of us in a year. :-)
Comment #9
BerdirOk, added a comment.
Comment #10
catchLooks good. Only issue here is I think that MySQL 5.0 is slow for all information_schema queries (this is apparently fixed in 5.1 for most things) - so should we look into making the same change elsewhere? It's less likely that we do many operations on tables apart from install, but it's still about 0.6 seconds per query extra.
Comment #11
BerdirOh, I did a short test for columns and it was quite fast (0.00something) and decided against doing the same for columns. But I'm using MySQL 5.1, obviously...
Can someone try the following query on MySQL 5.0?
SELECT SQL_NO_CACHE 1 FROM information_schema.columns WHERE table_schema = 'database_name' AND table_name = 'system' AND column_name = 'name'
And report the time...
Comment #12
Dries CreditAttribution: Dries commentedI question the correctness of DatabaseSchemaObjectExistsException. I don't think it is guaranteed that the table already exists. When I look at the code, it feels like creating the table could fail because the schema is incorrect, or because the disk is full for that matter. Thoughts?
Comment #13
BerdirCorrect.
We could run tableExists() in case of an exception and simply re-throw the PDO exception in case the table does not exist....
Comment #14
catchI still think we should just rethrow the PDO exception here (or let it bubble up), consistency is pointless if it's not correct.
Comment #15
bjaspan CreditAttribution: bjaspan commentedre #11: On a mysql 5.0 server with 2000ish databases:
Comment #16
David_Rothstein CreditAttribution: David_Rothstein commentedThis will work for MySQL but will still lead to false positives for the other database drivers, since their createTableSql() implementations run more than one SQL statement (beyond the initial table creation).
As I already commented in the other issue, I think the only way to fix that would be something like this:
On the other hand, even committing the patch as it is now would at least be trading a critical bug for a normal one, which is not such a bad tradeoff :)
****
Once we figure out the correct pattern here, we should definitely make all the others (columnExists, indexExists, etc) conform to it as well. If those queries are slow, there is no reason to do them except when necessary.
Comment #17
andypost#15 If querying information schema is a hard and most shared hostings have a lot of databases maybe better to use
select 1 from {tablename} limit 1
and catch exception if table does not exists
Comment #18
Damien Tournoud CreditAttribution: Damien Tournoud commentedI actually like the
SELECT 1 LIMIT 1
idea. Feels just hackish enough so that it could work :)Comment #19
David_Rothstein CreditAttribution: David_Rothstein commentedI like that too. Would probably work for columnExists() also, e.g:
SELECT column_name FROM {table_name} LIMIT 1
, right?Comment #20
andypostA bit hackish but it works, suppose this approach just require a comment.
Also this could help to catch and throw internal DatabaseSchemaObjectExistsException
Comment #21
bjaspan CreditAttribution: bjaspan commentedre #17-20: I thought that's what D6 did. In fact the mysql driver for D6 did SHOW TABLES LIKE escape($tablename) or SHOW COLUMNS FROM escape($tablename) LIKE escape($columnname). So I'd say SELECT 1/column_name FROM tablename LIMIT 1 and catching an exception is (a) no more hacky than what D6 did, (b) likely to be faster since SHOW LIKE might end up scanning the whole list anyway, and (c) is what I thought we were already doing anyway. :)
Comment #22
catchYeah I like SELECT 1 too, we can throw a specific exception without worrying if it's the right one, and it can go straight into createTable() and the others so we don't need to change the db_* functions around.
Comment #23
andypostPatch to start, this needs an extensive comments, so maybe some help with it
Comment #24
Crell CreditAttribution: Crell commentedIsn't this workaround only needed for MySQL? Unless Postgres and SQLite need the same hack, let's move that logic down into the MySQL driver rather than in the generic implementation. And then document why we're doing it (speed, with the possibility to remove it when we start requiring MySQL 5.1.)
I like this approach better than catching/decoding/rethrowing exceptions. It just feels cleaner.
Comment #25
andypost@Crell I think this approach is universal for any database so it could live in base class
Comment #26
Crell CreditAttribution: Crell commentedI don't mean if it would work, I mean if it's necessary. If the canonical approach works fast enough in Postgres and SQLite, I'd rather leave the canonical approach in there and just override MySQL. Either way it needs better documentation.
Comment #27
andypostAs I write before this needs good comments and benchmarks.
Comment #28
bcn CreditAttribution: bcn commentedReroll of andypost's patch from #23 with what (I presume) Crell had in mind with the comment in #24?
Comment #29
andypostJust a status change
Comment #30
David_Rothstein CreditAttribution: David_Rothstein commentedWe know that MySQL is a problem because a number of people have been trying Drupal 7 on servers that have thousands of MySQL databases on them. Has anyone tried the same for the other database drivers?
It seems like we don't know that it's a problem for those, but don't know that it's not a problem either :) In many ways it would be simpler to just make the change for all of them.
Either way, it still needs some documentation explaining why we do it this way.
Comment #31
catchThis isn't a problem only due to the number of databases, my localhost with < 20 databases it still takes about 600ms to query information_schema, the issue has been fixed in MySQL 5.1 I think.
One reason for overridding it in the MySQL driver is we could simply remove those hunks of code when we require MySQL 5.1 (so we should add a @todo for that).
Comment #32
Damien Tournoud CreditAttribution: Damien Tournoud commentedLet's do that.
Comment #33
Dries CreditAttribution: Dries commentedWe're still missing the @todo or add better documentation -- see #31.
Also, 'drupal' should be written 'Drupal'.
Comment #34
Crell CreditAttribution: Crell commented#28 needs:
1) Documentation as to why we're overriding those methods.
2) A @todo to see if we can remove it once we require MySQL 5.1.
3) It is not necessary to duplicate the docblocks from the parent class.
Once those are addressed, I'm good with this approach.
Comment #35
andypostSuppose it's better to check mysql version internaly and make something like savepoints support for sqlite in #669794: Use savepoints for nested transactions
Comment #36
catchI don't think we need to do that, this approach works equally well on either MySQL version and should be similar performance-wise, a @todo here is fine.
Comment #37
andypostPostgres also is not lightning fast on metadata, I think we need more statistics. Probably this change could make pgsql is much faster with tests
EDIT postgres tests on User
Comment #38
Crell CreditAttribution: Crell commentedThe base implementation should do the SQL standard approach.
If we need to do something else in specific drivers, do that in those drivers.
If we need to do a Postgres version of this as well, let's open a new issue and do that in the Postgres driver.
Comment #39
Crell CreditAttribution: Crell commentedBetter title
Comment #40
andypostThis patch needs help with comment
Comment #41
Crell CreditAttribution: Crell commentedRevised patch with expanded comments. I did a little googling and dropped in a link to the MySQL bug ticket, which is apparently still not fixed. I therefore took out the reference to MySQl 5.1 and instead said to remove the override when MySQL stops sucking.
Comment #43
Crell CreditAttribution: Crell commentedHow is that possible when nothing changed except comments, and the details report says green? I call shenanigans...
Comment #44
andypost@Crell I think that we should re-open #730220: One fragile test: ImageFieldDisplayTestCase because "Image field display tests" was broken so I ask to re-test and this makes green
Comment #45
Crell CreditAttribution: Crell commentedHm. Like I said, shenanigans. :-) Either way, it looks like it works now so yay!
Comment #46
Dries CreditAttribution: Dries commentedGreat job -- love the diligence. Committed to CVS HEAD.