The filter for null revisionId's here https://github.com/craftcms/cms/blob/develop/src/elements/db/ElementQuery.php#L2455 can at least double query execution time for element types with a large number of records.
For example, the get-elements query for Orders in Craft Commerce is something like:
SELECT `elements`.`id`, ... -- every field known to man
FROM (SELECT `elements`.`id` AS `elementsId`, `elements_sites`.`id` AS `elementsSitesId`, `content`.`id` AS `contentId`
FROM `craft_elements` `elements`
INNER JOIN `craft_commerce_orders` `commerce_orders` ON `commerce_orders`.`id` = `elements`.`id`
LEFT JOIN `craft_commerce_addresses` `billing_address` ON billing_address.id = `commerce_orders`.`billingAddressId`
LEFT JOIN `craft_commerce_addresses` `shipping_address` ON shipping_address.id = `commerce_orders`.`shippingAddressId`
INNER JOIN `craft_elements_sites` `elements_sites` ON `elements_sites`.`elementId` = `elements`.`id`
INNER JOIN `craft_content` `content` ON `content`.`elementId` = `elements`.`id`
WHERE (`commerce_orders`.`orderStatusId`='2') AND (`elements`.`archived`=0) AND (`elements`.`dateDeleted` IS NULL) AND (`elements`.`draftId` IS NULL) AND (`elements`.`revisionId` IS NULL)
ORDER BY `commerce_orders`.`dateUpdated`
LIMIT 100) `subquery`
INNER JOIN `craft_commerce_orders` `commerce_orders` ON `commerce_orders`.`id` = `subquery`.`elementsId`
LEFT JOIN `craft_commerce_addresses` `billing_address` ON billing_address.id = `commerce_orders`.`billingAddressId`
LEFT JOIN `craft_commerce_addresses` `shipping_address` ON shipping_address.id = `commerce_orders`.`shippingAddressId`
INNER JOIN `craft_elements` `elements` ON `elements`.`id` = `subquery`.`elementsId`
INNER JOIN `craft_elements_sites` `elements_sites` ON `elements_sites`.`id` = `subquery`.`elementsSitesId`
INNER JOIN `craft_content` `content` ON `content`.`id` = `subquery`.`contentId`
ORDER BY `commerce_orders`.`dateUpdated`;
This takes anywhere from > 8s consistently. Removing the revisionId IS NULL filter brings this down to ~800ms. This is the same for non-commerce related element types.
In this database there are just under 500k users, 550k orders and 400k subscriptions.
AFAICT only Entries support revisions at this point. Can element types which _don't_ need to handle revisions be explicit about it and drop this filter altogether for them?
AFAICT only Entries support revisions at this point. Can element types which _don't_ need to handle revisions be explicit about it and drop this filter altogether for them?
That鈥檚 one option, but there鈥檚 plenty of sites with 100,000鈥檚/millions of entries as well. so we should be solving this in a way that speeds up entries in addition to users and other element types.
I think the main issue here is that MySQL doesn鈥檛 use its draftId / revisionId indexes when you are searching for null values, since it only indexes non-null values. Just PR鈥檇 #5895, which adds a new boolean isSource column to the elements table, and replaces the draftId is null and revisionId is null condition for isSource = true, which should fix that. We鈥檒l do a little testing to verify there鈥檚 a noticeable improvement, and then pull it in for 3.5.
@johnnynotsolucky any chance we could get a database dump and a composer.json file to do some testing with on this sent to [email protected]?
Sorry @angrybrad, no can do. The production database has sensitive data, and the staging database doesn't have the same size of data.
I'm sure you could load random data into the various elements and achieve the same thing? We don't really have an extra fields on our elements. The orders table has a matrix field attached with 2 plain text fields and a dropdown.
@johnnynotsolucky We鈥檙e not able to reproduce, and my PR isn鈥檛 showing definitive results (if anything, potentially a little worse).
Any chance you can help test? If so, please do this:
craftcms/cms constraint in composer.json to "dev-feature/isSource".composer update../craft migrate/up --type=app.If that doesn鈥檛 help, point your Craft install back to the original database, but please keep the new one around, so we can try some other things.
@brandonkelly sorry for responding so late. I don't have capacity to test this out right now, @sjcallender mentioned that he should be able to though.
Most helpful comment
That鈥檚 one option, but there鈥檚 plenty of sites with 100,000鈥檚/millions of entries as well. so we should be solving this in a way that speeds up entries in addition to users and other element types.
I think the main issue here is that MySQL doesn鈥檛 use its
draftId/revisionIdindexes when you are searching fornullvalues, since it only indexes non-null values. Just PR鈥檇 #5895, which adds a new booleanisSourcecolumn to theelementstable, and replaces thedraftId is null and revisionId is nullcondition forisSource = true, which should fix that. We鈥檒l do a little testing to verify there鈥檚 a noticeable improvement, and then pull it in for 3.5.