{"action":"created","issue":{"url":"https://api.github.com/repos/silverstripe/silverstripe-cms/issues/1977","repository_url":"https://api.github.com/repos/silverstripe/silverstripe-cms","labels_url":"https://api.github.com/repos/silverstripe/silverstripe-cms/issues/1977/labels{/name}","comments_url":"https://api.github.com/repos/silverstripe/silverstripe-cms/issues/1977/comments","events_url":"https://api.github.com/repos/silverstripe/silverstripe-cms/issues/1977/events","html_url":"https://github.com/silverstripe/silverstripe-cms/issues/1977","id":260465305,"number":1977,"title":"SiteTree load extremely inefficient when you have many page types + many pages in tree (~2k calls to ->canCreate)","user":{"login":"patricknelson","id":4269377,"avatar_url":"https://avatars0.githubusercontent.com/u/4269377?v=4","gravatar_id":"","url":"https://api.github.com/users/patricknelson","html_url":"https://github.com/patricknelson","followers_url":"https://api.github.com/users/patricknelson/followers","following_url":"https://api.github.com/users/patricknelson/following{/other_user}","gists_url":"https://api.github.com/users/patricknelson/gists{/gist_id}","starred_url":"https://api.github.com/users/patricknelson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/patricknelson/subscriptions","organizations_url":"https://api.github.com/users/patricknelson/orgs","repos_url":"https://api.github.com/users/patricknelson/repos","events_url":"https://api.github.com/users/patricknelson/events{/privacy}","received_events_url":"https://api.github.com/users/patricknelson/received_events","type":"User","site_admin":false},"labels":[{"id":447170478,"url":"https://api.github.com/repos/silverstripe/silverstripe-cms/labels/affects/v3","name":"affects/v3","color":"d4c5f9","default":false},{"id":386316077,"url":"https://api.github.com/repos/silverstripe/silverstripe-cms/labels/affects/v4","name":"affects/v4","color":"5319e7","default":false},{"id":86289552,"url":"https://api.github.com/repos/silverstripe/silverstripe-cms/labels/change/minor","name":"change/minor","color":"f9d0c4","default":false},{"id":587650549,"url":"https://api.github.com/repos/silverstripe/silverstripe-cms/labels/effort/hard","name":"effort/hard","color":"f9d0c4","default":false},{"id":238980222,"url":"https://api.github.com/repos/silverstripe/silverstripe-cms/labels/impact/high","name":"impact/high","color":"eb6420","default":false},{"id":34208844,"url":"https://api.github.com/repos/silverstripe/silverstripe-cms/labels/type/enhancement","name":"type/enhancement","color":"0e8a16","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/silverstripe/silverstripe-cms/milestones/50","html_url":"https://github.com/silverstripe/silverstripe-cms/milestone/50","labels_url":"https://api.github.com/repos/silverstripe/silverstripe-cms/milestones/50/labels","id":2818929,"number":50,"title":"Recipe 4.1.0","description":"","creator":{"login":"tractorcow","id":936064,"avatar_url":"https://avatars2.githubusercontent.com/u/936064?v=4","gravatar_id":"","url":"https://api.github.com/users/tractorcow","html_url":"https://github.com/tractorcow","followers_url":"https://api.github.com/users/tractorcow/followers","following_url":"https://api.github.com/users/tractorcow/following{/other_user}","gists_url":"https://api.github.com/users/tractorcow/gists{/gist_id}","starred_url":"https://api.github.com/users/tractorcow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tractorcow/subscriptions","organizations_url":"https://api.github.com/users/tractorcow/orgs","repos_url":"https://api.github.com/users/tractorcow/repos","events_url":"https://api.github.com/users/tractorcow/events{/privacy}","received_events_url":"https://api.github.com/users/tractorcow/received_events","type":"User","site_admin":false},"open_issues":3,"closed_issues":2,"state":"open","created_at":"2017-10-06T03:53:19Z","updated_at":"2017-11-28T20:24:24Z","due_on":null,"closed_at":null},"comments":9,"created_at":"2017-09-26T01:46:46Z","updated_at":"2017-11-29T03:38:38Z","closed_at":null,"author_association":"CONTRIBUTOR","body":"My site currently has **54** page types and **36** pages visible at the first two levels. This means that SilverStripe performs **1,944** unique hits to `->canCreate($member, $context)`. It calls that method for every single page at those first two levels for every single page type.  The end result is that page load times *just* for the requests going to `/admin/pages/treeview` end up being between 3 to 7 seconds, depending on the server (more on local dev, less on prod). I was able to verify this as the bottle neck when I could reduce the high 7 second figure down to about 1.5 seconds when I cached the first response from `->canCreate()` (which of course is not properly accounting for parent context).\r\n\r\nI can see this getting even more out of hand. As time goes by, the number of calls to `->canCreate()` will only continue to increase by 36x for every new page type and 54x for every new page at the first two levels.  Ultimately, this seems inefficient to me because the only apparent purpose of this is to facilitate visibility of options in a right click menu. This is a relatively rarely used option, given that this overhead occurs repeatedly on _every single load_ of the site tree.\r\n\r\nPossible solutions:\r\n\r\n- Perform some sort of **caching**, which varies (that is, `Vary` in the caching sense) by:\r\n    - Page type\r\n    - Parent page instance (or just type? currently instance is passed)\r\n    - Member\r\n    - Also: Need to think about how to bust cache in case of custom implementations  of `->canCreate(...)`  which may rely on unknown external factors.\r\n- Generate **on-demand**: Completely re-think the use of this sort of option in the right click menu. Maybe only generate it on-demand after the user right clicks?\r\n\r\nThis may not be worthwhile for most smaller sites, however it becomes noticeable for us as our site is fairly large both in complexity (number of page types) and in scale (number of pages) as well as the number of back-end users (and thus impacting overall server load)."},"comment":{"url":"https://api.github.com/repos/silverstripe/silverstripe-cms/issues/comments/347743211","html_url":"https://github.com/silverstripe/silverstripe-cms/issues/1977#issuecomment-347743211","issue_url":"https://api.github.com/repos/silverstripe/silverstripe-cms/issues/1977","id":347743211,"user":{"login":"unclecheese","id":654636,"avatar_url":"https://avatars1.githubusercontent.com/u/654636?v=4","gravatar_id":"","url":"https://api.github.com/users/unclecheese","html_url":"https://github.com/unclecheese","followers_url":"https://api.github.com/users/unclecheese/followers","following_url":"https://api.github.com/users/unclecheese/following{/other_user}","gists_url":"https://api.github.com/users/unclecheese/gists{/gist_id}","starred_url":"https://api.github.com/users/unclecheese/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/unclecheese/subscriptions","organizations_url":"https://api.github.com/users/unclecheese/orgs","repos_url":"https://api.github.com/users/unclecheese/repos","events_url":"https://api.github.com/users/unclecheese/events{/privacy}","received_events_url":"https://api.github.com/users/unclecheese/received_events","type":"User","site_admin":false},"created_at":"2017-11-29T03:38:38Z","updated_at":"2017-11-29T03:38:38Z","author_association":"CONTRIBUTOR","body":"Caching the `canCreate()` calls in `SiteTree::getTreeTitle()` along with the much less expensive `CMSMain::SiteTreeHints()` results in a ~50% performance gain.\r\n\r\nThe caching on `getTreeTitle` is not as aggressive as it could be, as the calls to `canCreate` pass `['Parent' => $this]` as context, so the cache key needs to include the current page ID.\r\n"}}
{"actor":{"display_login":"unclecheese"}}