Conversation
* added bypass logic to create template_pair from 6 tables, fallback for hard filters * Removed batching, checking against per pair * follow try-with-resources design * unit tests for the bypass logic * Fix cross-zone template lookup in listTemplates Phase 2 When temp_zone_pair has dcId=0 (cross-zone template with no data_center), use IS NULL predicate instead of broken EQ/IN with literal 0 which never matches NULL rows. * Add filter for non-root domain-admin users --------- Co-authored-by: anishadas <[email protected]> Co-authored-by: Aaron Chung <[email protected]>
Co-authored-by: anishadas <[email protected]> (cherry picked from commit f6c9fd1081f0e1d9386db08e86485b482bf064ab)
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## 4.22 #13566 +/- ##
==========================================
Coverage 18.00% 18.00%
- Complexity 16219 16230 +11
==========================================
Files 5936 5937 +1
Lines 535716 536058 +342
Branches 65596 65670 +74
==========================================
+ Hits 96459 96531 +72
- Misses 428268 428511 +243
- Partials 10989 11016 +27
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…ove-listtemplates-perf
|
@blueorangutan package |
|
@vladimirpetrov a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress. |
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 18552 |
|
thanks @Pearl1594 , do you have any performance figures? (before vs. after?) |
…unt is correct, but lists all templates
|
| import com.cloud.storage.dao.StoragePoolAndAccessGroupMapDao; | ||
| import com.cloud.cluster.ManagementServerHostPeerJoinVO; | ||
|
|
||
| import com.cloud.template.VirtualMachineTemplate; |
There was a problem hiding this comment.
Nit: these new imports break alphabetical order here, and the same classes (AccountVO, SSHKeyPairVO, SSHKeyPairDao, InstanceGroupVMMapVO, NicVO, InstanceGroupVMMapDao, NicDao) get removed from their previously-correct alphabetized spots further down in this same diff.
|
|
||
| ConfigKey<Boolean> BypassTemplateView = new ConfigKey<>("Advanced", Boolean.class, "template.list.bypass.view", | ||
| "false", | ||
| "If true, uses an optimized query path for listing templates and ISOs, which can improve performance on " + |
There was a problem hiding this comment.
Nit: the concatenated description string has inconsistent leading/trailing space placement across the lines, worth double-checking the rendered text doesn't end up with a missing or doubled space.
| return new Pair<List<TemplateJoinVO>, Integer>(objects, count); | ||
| } | ||
|
|
||
| // ============================================================================ |
There was a problem hiding this comment.
Nit: this banner-style comment block doesn't match the rest of the codebase's usual /** javadoc */ or short // comment style.
| */ | ||
| private String buildFromClause(TemplateListFilter filter) { | ||
| StringBuilder from = new StringBuilder() | ||
| .append("cloud.vm_template vt") |
There was a problem hiding this comment.
Nit: hardcoding the cloud. schema prefix here is unusual for this codebase's DAOs, worth checking this holds for all deployments.
| try { | ||
| TEMPLATE_JOIN_ID_FIELD = findFieldUpHierarchy(TemplateJoinVO.class, "id"); | ||
| TEMPLATE_JOIN_PAIR_FIELD = findFieldUpHierarchy(TemplateJoinVO.class, "tempZonePair"); | ||
| TEMPLATE_JOIN_ID_FIELD.setAccessible(true); |
There was a problem hiding this comment.
Nit: reflection to populate TemplateJoinVO's private fields feels like a fairly invasive way to build this VO, a package-visible setter would be less fragile against future field renames.
|
@blueorangutan test |
|
@vladimirpetrov a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests |
|
[SF] Trillian Build Failed (tid-16613) |
|
@blueorangutan test |
|
@Pearl1594 a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests |
|
[SF] Trillian Build Failed (tid-16615) |
|
@blueorangutan package |
|
@vladimirpetrov a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress. |
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 18696 |
|
@Pearl1594 can you look at the comments/suggestions? |
There was a problem hiding this comment.
Took the help of AI in testing
cc @Pearl1594 @nvazquez
shell scripts
whichpath-pr13566.sh
compare-pr13566.sh
setup-pr13566.sh
Test results for template.list.bypass.view
Tested on a KVM environment with the PR build: 2 zones, about 118 templates and 8 ISOs.
Test data. Owned by root admin, a domain admin, users in a child domain and a user in an unrelated domain, plus a project (owner, member and non-member). The templates include:
- public/featured, private, cross-zone and multi-zone
- direct-download
- one in an error state (failed download)
- one with tags
- one deleted
- one shared with another account
- bootable and non-bootable ISOs
- direct-download bulk templates to give paging something to work on
Method. listTemplates/listIsos calls covering these filters: all, executable, self, selfexecutable, featured, community, shared and sharedexecutable. They also cover zoneid, isready, keyword, name, hypervisor, id/ids, paging, showunique, templatetype, bootable, ispublic, projectid=<id>/-1, tags and showremoved.
Each call ran once with the flag set to false and once set to true,
I compared count plus id, name, zoneid, isready and projectid for every row.
I used the MySQL general log to check which query actually ran: AS distinct_key means the new SQL, and SELECT DISTINCT temp_zone_pair / template_view.id FROM template_view means the old view.
Routing works as intended. With the flag on, all (admin), executable, self/selfexecutable, ISOs and project-scoped calls use the new SQL. featured, community, shared, sharedexecutable, tags, showremoved and non-admin all with projectid=-1 stay on the view.
Results match in 51 of 53 cases. The two mismatches are below.
1. templatetype (system and probably isvnf / forcks) is ignored on the new path
cmk list templates templatefilter=all templatetype=SYSTEM
flag=false -> count 2
flag=true -> count 118 (every template)
templateChecks() adds templateType, isVnf and forCks to the SearchCriteria, but buildTemplateListFilter() doesn't take them, and TemplateListFilter has no fields for them. The new SQL drops these filters without any error. I didn't test isvnf or forcks, but reading the code they hit the same gap. The UI uses both: isvnf for the VNF template lists and forcks for CKS node templates.
Suggested fix: add these fields to TemplateListFilter and appendCommonWhere() (vt.type = ?, vt.type = 'VNF' / != 'VNF', vt.for_cks = ?). Or, at minimum, make canBypass() return false when any of them is set.
2. showunique=true with paging returns different templates on a page
cmk list templates templatefilter=all page=2 pagesize=10 showunique=true
The count is the same, but page 2 has different templates. With the flag on, ptest-domadmin-public and ptest-user1-private appear on page 2. They don't appear there with the flag off.
The view path always sorts by sort_key, temp_zone_pair, which is the "<id>_<zone>" string. With showUnique, the new SQL sorts by sort_key, distinct_key, where distinct_key is the numeric vt.id. When sort_key ties (all templates here have 0), the two orders differ ("10_1" < "9_1" but 9 < 10), so the page boundaries move. Without showunique, both paths sort by the same string, and paging matched (I checked pages 1 and 4).
Suggested fix: always use CONCAT(vt.id, '_', IFNULL(dc.id, 0)) as the second sort column, even when the SELECT key is vt.id.
3. Possible gap: EXTERNAL format with onlyReady (from reading the code, not tested)
The view path treats ImageFormat.BAREMETAL and ImageFormat.EXTERNAL as ready:
readySc.addOr("format", SearchCriteria.Op.IN, ImageFormat.BAREMETAL, ImageFormat.EXTERNAL);The new SQL only has vt.format = 'BAREMETAL'. With the flag on, EXTERNAL-format templates (Extensions) would likely disappear from isready=true / executable lists.
4. Description and comments are out of date: domain-admin self
The description says self/selfexecutable for DOMAIN_ADMIN/RESOURCE_DOMAIN_ADMIN falls back to the view. In practice they run on the new SQL: domainPathLike is set without requiresViewFallback, canBypass() doesn't check it, and the SQL handles domain.path LIKE ?. Results matched in my tests, so the behaviour looks correct. The PR description, the Javadoc on buildTemplateListFilter / findDistinctTempZonePairs, and the IllegalArgumentException message (which lists domainPath as unsupported) should be updated to match.
Suggested test additions. Add unit tests covering templatetype, isvnf, forcks and EXTERNAL + onlyReady on the new path, and one that pages through showunique=true and checks that the IDs on each page match the view path.
… format and view-consistent showunique paging Add templateType, isVnf and forCks to TemplateListFilter and the bypass WHERE clause, treat EXTERNAL like BAREMETAL for onlyReady, and tie-break showunique pages on CONCAT(vt.id, '_') so page boundaries match the view path. Update stale domainPath fallback comments and add unit tests.
|
@kiranchavala Ive addressed the comments. Thanks for the review. |
|
@blueorangutan package |
|
@kiranchavala a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress. |
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 19407 |


Description
This PR Adds a fast-path for listTemplates Phase 1 that bypasses template_view,
behind a runtime config flag (default off).
Why
The captured incident query against template_view runs 7,000+ seconds and
drains the DB pool. View materializes ~71k rows for ~4.7k templates due to
LEFT-JOIN row multiplication, OR-join on data_center, and predicates on a
computed column. Phase 1 only needs 6 of the 13 tables.
What changed
BypassTemplateViewConfigKey (template.list.bypass.view, defaultfalse,Global, runtime-toggleable).
TemplateListFilterPOJO +canBypass()predicate.TemplateJoinDao.findDistinctTempZonePairs(filter)— hand-tuned SQL overvm_template + account + template_store_ref + image_store + template_zone_ref
COALESCE.QueryManagerImplbuilds the filter and dispatches to bypass when flag ison AND
canBypass()is true. Otherwise falls through to existing path.showuniquepages tie-break on the same"<id>_"string order as the view path,so page boundaries match with the flag on or off.
Coverage
Bypass handles:
id,name,keyword,hypervisor,format,type,templatetype,isvnf,forcks,public,featured,bootable,parenttemplateid,accountType,accountIdIN,zoneid,templateState,removed,onlyReady(Ready, BAREMETAL/EXTERNAL format, or ISO + PERHOST),showunique, pagination,templatefilter∈ {self/selfexecutable(including DOMAIN_ADMIN/RESOURCE_DOMAIN_ADMIN, scoped via
domain.path LIKE),executable,all(admin; non-admin without project resources)}.Falls back to view path:
tags,showremoved,templatefilter∈ {featured,community,sharedexecutable,shared,allnon-admin with projectresources (e.g.
projectid=-1)}.Types of changes
Feature/Enhancement Scale or Bug Severity
Feature/Enhancement Scale
Bug Severity
Screenshots (if appropriate):
How Has This Been Tested?
How did you try to break this feature and the system with this change?