Org Skills

aem-performance

Diagnoses and fixes Adobe Experience Manager performance — CDN and dispatcher cache hit ratio, uncacheable requests, slow JCR queries and Oak indexes, heavy Sling Models and HTL, external API calls, Sling Dynamic Include, Core Web Vitals, and author-side slowness.

Download .zip Raw Source
When agents use itUse this whenever the user says AEM pages are slow, TTFB or LCP is high, publish CPU spikes, the cache hit ratio is low, logs show "Traversed … nodes" or query limit warnings, needs an Oak index definition or explain query, a Sling Model or HTL render is slow, workflows, replication queues, asset processing or MSM rollouts back up, or asks about thread dumps, request.log, rlog, Developer Console, Lighthouse or load testing AEM. Also apply it when a slow page is really a caching, query or front-end problem rather than server capacity.

Install

Copilot (VS Code, Visual Studio, Copilot CLI and github.com) reads skills from the repository — commit them so the whole team gets them.

npx skills add AGCO-Global/org-skills --skill aem-performance -a github-copilot
# or with the org installer (adds .github/skills/aem-performance):
npx -y github:AGCO-Global/org-skills add skill aem-performance

Uses the open skills CLI. Works with Claude Code, Codex, Cursor, Copilot, Gemini CLI, OpenCode, Windsurf and 60+ others — it asks which agent to install into.

npx skills add AGCO-Global/org-skills --skill aem-performance
# user-level instead of project-level:
npx skills add AGCO-Global/org-skills --skill aem-performance -g

Installs the aem-architecture-skills plugin, which bundles all AEM / Architecture skills and keeps them updated.

/plugin marketplace add AGCO-Global/org-skills
/plugin install aem-architecture-skills@org-skills
# or just this skill, in this repository:
npx skills add AGCO-Global/org-skills --skill aem-performance -a claude-code

Use Download .zip above, then upload it under Customize → Skills → Upload skill. Team and Enterprise admins can sync this repo as a plugin marketplace instead.

Installs into .agents/skills/, which Codex reads.

npx skills add AGCO-Global/org-skills --skill aem-performance -a codex

Installs into Cursor's skills folder.

npx skills add AGCO-Global/org-skills --skill aem-performance -a cursor

Installs into Gemini CLI's skills folder.

npx skills add AGCO-Global/org-skills --skill aem-performance -a gemini-cli

Any tool with rules, instructions or custom prompts: use Copy SKILL.md above and paste it in. It is plain Markdown.

Commands use your normal git sign-in to GitHub, so they work while the repository is private. Node.js 20+ required.

Skill contents

AEM Performance

On AEM, the fastest request is the one publish never sees. Fix cacheability first, then queries, then render code, then the front end — and measure before and after each change with the same tool, because intuition about which layer is slow is usually wrong.

1. Triage

Symptom Likely cause Check Fix
High TTFB on anonymous pages Cache miss at CDN and dispatcher x-cache/Age headers, hit ratio by path Make the URL cacheable (§2)
Publish CPU high under normal traffic Low hit ratio or frequent invalidation Requests reaching publish per second; statfile touches Ignore tracking params, tune statfileslevel, TTLs
One page type slow even uncached Query or render cost request.log timing, rlog/recent requests, profiler §3, §4
Traversed N nodes / query limit exceptions Missing or wrong Oak index Explain query Add/adjust index, rewrite query (§3)
Slow only when a partner API is slow Synchronous external call without timeout Thread dump: threads stuck in socket read Timeouts + cache + fallback (§5)
Good TTFB, poor LCP/CLS Front end: images, render-blocking clientlibs, fonts Lighthouse / field CWV data §6
Periodic slowdowns Background jobs, GC, offloaded workflows on publish GC logs, Sling Jobs, scheduler activity Move work to author/offload, fix heap churn
Author sluggish, publish fine Workflow/asset backlog, large folders, rollouts Workflow console, job queues, replication queue §7

2. Cache hit ratio — the first lever

Uncacheable-request causes, most common first:

  • Query strings (utm_*, gclid, cache-busters) — ignore them in dispatcher /ignoreUrlParams and at the CDN.
  • Cookies/auth headers on anonymous paths — dispatcher won't cache requests with authorization by default; stop setting session cookies on public pages.
  • Cache-Control: private/no-store emitted by a component or filter for the whole page.
  • Personalised content in HTML — move it to client-side calls or a Sling Dynamic Include fragment.
  • Selectors/suffixes with unbounded values — cache-key explosion; validate or restrict them.
  • Over-invalidation — low /statfileslevel means every publish flushes every site.

Target > 90 % CDN hit ratio on anonymous HTML and near 100 % on clientlibs and images (fingerprinted, long TTL).

3. Queries and indexes

  • Every query in code must hit an index. Run it through the Explain Query / Query Performance tool (Developer Console in Cloud Service, Operations → Diagnosis in 6.5) and read the plan: /* traverse */ or an unexpected index = fix it.
  • Traversal warnings in the log and read-limit exceptions (Oak query limits on reads and in-memory results) are errors, not noise.
  • Index definitions are code: extend the relevant Lucene index or add a property index, with a versioned -custom-N name on Cloud Service. Include includedPaths/queryPaths, the properties you filter/sort on (propertyIndex, ordered), and async as appropriate.
/oak:index/mysite-articles-custom-1
  - type = "lucene", async = ["async","nrt"], compatVersion = 2
  - includedPaths = ["/content/mysite"], queryPaths = ["/content/mysite"]
  - evaluatePathRestrictions = true
  + indexRules/cq:Page/properties/
      + template  (name = "jcr:content/cq:template", propertyIndex = true)
      + published (name = "jcr:content/publishDate", ordered = true)
  • Constrain queries: node type, path, p.limit, p.guessTotal=true in QueryBuilder; never compute exact totals for paging over large sets.
  • Don't query per render. Precompute lists on activation, cache results in a service, or use resource traversal of a small known subtree.

4. Sling Models and HTL

  • @Model(adaptables = SlingHttpServletRequest.class, defaultInjectionStrategy = OPTIONAL) with @ValueMapValue/@ChildResource — cheap. Avoid @Inject (slow generic injector resolution) in hot components.
  • Compute in @PostConstruct once; don't call expensive getters repeatedly from HTL loops. Lazy-compute expensive parts only when the template uses them.
  • Resolve resources once: pass the Page/Resource down, don't re-walk getParent() chains or re-adapt in every child component.
  • Request-scoped cache for data shared by many components on one page (e.g. navigation built once per request in a request attribute or a model via @Self adaptation).
  • No JCR queries, HTTP calls, or ResourceResolverFactory.getServiceResourceResolver per component render.
  • Heavy navigation/footers: render once as an Experience Fragment or cached include, not recomputed on every page.

5. External calls

  • OSGi service with pooled HTTP client, connect and read timeouts (seconds, not defaults), circuit breaker/fallback, and a TTL cache (e.g. Caffeine) keyed by request parameters.
  • Refresh data in a scheduled job on author or a separate service where freshness allows; publish reads from cache or JCR.
  • Prefer client-side fetch for per-user or real-time data so the page stays cacheable.

6. Personalised fragments and front end

  • Sling Dynamic Include replaces a component with an SSI/ESI/client-side include so the page is cached and only the fragment is dynamic. Enable includes in Apache (mod_include) for SSI, and make sure the fragment URL is itself cacheable or cheap.
  • LCP: use the Core Image component with web-optimized image delivery or Dynamic Media (responsive widths, modern formats), don't lazy-load the hero image, preload it if needed, set width/height to avoid CLS.
  • Clientlibs: split per template, async/defer non-critical JS, minify, long-cache with fingerprinted URLs; audit the tag manager payload — it is often the largest script.
  • Fonts: self-host, font-display: swap, preload the one or two critical faces, subset.
  • Measure with field data (CrUX/RUM) as well as Lighthouse; lab-only scores mislead.

7. Author performance

  • Workflows: use transient workflows where history isn't needed; purge completed instances; avoid launchers firing on every node change.
  • Assets: Cloud Service processes via asset microservices — keep custom post-processing workflows light; on 6.5 tune DAM Update Asset and consider offloading. Keep folders under a few thousand children.
  • MSM: roll out in batches off-peak; avoid deep rollout configs triggered on every modification.
  • Replication/distribution queues: monitor for blocked items; fix the failing item rather than clearing the queue blindly.
  • Lucene index reindexing, large package installs and bulk tree activations belong off-peak.

8. Cloud Service specifics

  • Publish autoscales, but scaling doesn't fix a low hit ratio or a slow query — it just multiplies it.
  • No long-running or heavy background jobs on publish; use Sling Jobs on author or external processing.
  • Logs: download or stream AEM, dispatcher and CDN logs from Cloud Manager; analyse request.log for slow paths and CDN logs for hit ratio. APM tooling availability depends on the program's entitlements — confirm what's provisioned before relying on it.
  • Load-test on stage (inform Adobe support per current policy for large tests), with realistic cache hit ratios and CDN in the path.

9. Tools

Tool Use for
request.log / rlog.jar Slowest URLs and time distribution
Recent requests (Felix console, 6.5) / Developer Console (Cloud) Per-request component timing, OSGi status, queries
Explain Query / Query Performance Index usage and cost
Thread dumps (several, 5–10 s apart) Blocked threads, stuck socket reads, lock contention
Heap dumps / GC logs Memory leaks, unclosed resource resolvers
Lighthouse / WebPageTest / RUM Core Web Vitals, waterfall
Load tests (JMeter, Gatling, k6) Capacity with realistic URL mix and cache behaviour

Deliverable format

  1. Symptom and baseline — metric, value, measurement tool, URL set.
  2. Findings — table: layer · evidence · impact · fix · effort.
  3. Changes — code/config diffs (index definitions, dispatcher rules, model refactors).
  4. Verification — same measurement after the change, plus expected hit ratio and CWV impact.
  5. Follow-ups — monitoring/alerts to prevent regression.

Anti-patterns to reject

  • Adding publish capacity before measuring cache hit ratio.
  • Queries in component renders; traversal warnings ignored; p.limit=-1 with exact totals.
  • @Inject everywhere; repeated resource resolution; leaked service resource resolvers.
  • External HTTP calls without timeouts or caching on the request thread.
  • Personalised markup in cached HTML; session cookies on anonymous pages.
  • Lazy-loading the LCP image; one giant clientlib on every page; render-blocking tag manager.
  • Clearing replication or workflow queues without fixing the root cause.
  • Long-running jobs on Cloud Service publish; load tests that bypass the CDN.