Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
57b55b3
fix(admin): resolve feedback key from navigation path in mark/unmarkF…
jung-thomas Nov 11, 2026
b1671de
Merge pull request #3100 from sap-tutorials/worktree-fix-3080-feedbac…
jung-thomas Nov 11, 2026
ae27bf2
fix(attachments): correct base64url encode in render-link hook (#3098)
jung-thomas Nov 11, 2026
2d3c536
Merge pull request #3101 from sap-tutorials/worktree-fix-download-url…
jung-thomas Nov 11, 2026
91a0275
fix(content): degrade missing-BLOB race to styled page, not raw 500 (…
jung-thomas Nov 11, 2026
cce0102
Merge pull request #3104 from sap-tutorials/worktree-fix-content-serv…
jung-thomas Nov 11, 2026
f9426ea
fix(cds-caching): atomic UPSERT in KeyvCDS.set to stop PK-collision e…
jung-thomas Nov 11, 2026
999f148
build: wire patch-package postinstall for cds-caching UPSERT patch (#…
jung-thomas Nov 11, 2026
d61f30e
fix(content-serve): degrade missing-BLOB race to styled 404, not raw …
jung-thomas Nov 11, 2026
74ba3d0
docs(cds-caching): record KeyvCDS.set UPSERT patch + upgrade path (#3…
jung-thomas Nov 11, 2026
fe87b75
fix(#3095): add 1280px container base rule for concept/topic/channel/…
jung-thomas Nov 11, 2026
8d7bdda
Merge remote-tracking branch 'origin/DEV' into worktree-fix-3103-miss…
jung-thomas Nov 11, 2026
577b829
fix(#3095): cap KG-relationship tile lists at 24 with no-JS <details>…
jung-thomas Nov 11, 2026
4050ce3
Merge pull request #3105 from sap-tutorials/worktree-fix-3103-missing…
jung-thomas Nov 11, 2026
dc6508d
docs(cds-caching): link upstream issue #44 + PR #45 (#3102)
jung-thomas Nov 11, 2026
c991ee6
Merge pull request #3106 from sap-tutorials/fix-concept-page-layout-3095
jung-thomas Nov 11, 2026
4475114
Merge pull request #3107 from sap-tutorials/worktree-fix-3102-cds-cac…
jung-thomas Nov 11, 2026
678ce11
fix(deploy): move patch-package to dependencies so postinstall surviv…
jung-thomas Nov 11, 2026
cf81e85
Merge pull request #3108 from sap-tutorials/fix/patch-package-prod-dep
jung-thomas Nov 11, 2026
8a0539b
release 1.44.1 prep: version bump + whats-new refresh
jung-thomas Nov 11, 2026
5da89cc
Merge pull request #3109 from sap-tutorials/release/dev-to-main-1.44.1
jung-thomas Nov 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .deploy/mta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ ID: tutorials-ims
# Bump this on each release you deploy — it's the version shown by `cf mtas`
# and in the mtar filename (tutorials-ims_<version>.mtar). Deploy is manual:
# `cd .deploy && mbt build && cf deploy mta_archives/tutorials-ims_<version>.mtar -e ../deploy/<env>.mtaext -f`.
version: 1.44.0
version: 1.44.1

# Top-level parameters (overridable per-env via deploy/<env>.mtaext).
parameters:
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ The load-bearing few. **Full detail for every relocated item → [tutorials-ims-
- **`@cap-js/ai` for RPT-1 ValueList recommendations (#959)** — `AICore-mocked` locally, `AICore-btp` in hybrid/prod; per-field opt-out `@UI.RecommendationState: 0`. Ref: [cap-ai-plugin.md](docs/developers/reference/cap-ai-plugin.md).
- **Knowledge-graph feature flags (`KG_*`), all default OFF + DEV-only, all fail-open** — PageRank #916, WCC isolation #918, on-demand extraction #948, Louvain communities #917, orphan retirement #1115, community peers/labels #1126, community search weight #1171, coverage nudge #1172, cluster Q&A #1173. Toggles, nightly jobs, and fail-open specifics → gotchas.md "Knowledge graph feature flags".
- **HCQL protocol adapter (#2247, CAP 10.1.0)** — `POST /hcql/<svc>` paths (NOT co-mounted on OData URLs) on 5 authenticated services only (`AdminService`, `AuthorService`, `AnalyticsService`, `ExportsService`, `ConsolidationService`); requires `@sap/cds >= 10.1.0`. Malformed-CQN process-exit DoS from 10.0.3 **fixed in 10.1.0** (now returns 400, server stays up). Auth enforced before CQN parse. Kill: delete `srv/hcql-enablement.cds` + drop the `hcql` entry from `AdminService`'s `@protocol` list in `srv/admin-service-mcp.cds`, then `cds build --production` + redeploy. Ref: [hcql-support.md](docs/developers/reference/hcql-support.md).
- **cds-caching CDS-DB store + metrics ON (#1222)** — `store:"cds"` in hybrid/prod, `memory` in base/unit. CF resolve-guard needs BOTH the baked csn entities AND `srv/lib/strip-precompiled-plugin-roots.js` (both load-bearing). Ref: [cds-caching-store.md](docs/developers/reference/cds-caching-store.md).
- **cds-caching CDS-DB store + metrics ON (#1222)** — `store:"cds"` in hybrid/prod, `memory` in base/unit. CF resolve-guard needs BOTH the baked csn entities AND `srv/lib/strip-precompiled-plugin-roots.js` (both load-bearing). A committed `patch-package` patch (`patches/cds-caching+2.1.0.patch`, `postinstall`) makes `KeyvCDS.set` an atomic UPSERT to kill PROD PK-collision error noise on concurrent `kg:rl:*` writes (#3102); **re-verify the patch on any `cds-caching` bump** (patch-package fails install loudly on drift). Ref: [cds-caching-store.md](docs/developers/reference/cds-caching-store.md).
- **User-facing UI changes want a committed e2e spec** — advisory PR nudge on `app/**`/`hugo/**` changes; real coverage runs in the post-DEV-deploy `e2e` job. Ref: [e2e-coverage-pattern.md](docs/developers/reference/e2e-coverage-pattern.md).
- **`test:e2e` is post-deploy only, not on PRs** — self-skips without `SMOKE_BASE_URL`. Served tutorials render `<main>`+`<h1>`, NOT `<article>`. Runbook: `test/e2e/README.md`.
- **Freshness detector grounding needs the corpus-embedding backfill** — until `srv/jobs/freshness-corpus-embedding-job.js` runs, every API-obsolescence claim degrades to `confidence: Low`. Tutorial source from `ContentFiles.sourceContent` via `getTutorialSource(slug)`, NOT `Steps.description`. → gotchas.md "Freshness detector".
Expand Down
59 changes: 59 additions & 0 deletions docs/developers/reference/cds-caching-store.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,62 @@ command-palette concept search): `@cache: { ttl: 300000, tags: [{ value:
re-enabled metrics actually accumulate against the deployed DEV container (clear
the stale zeroed rows first), then let PROD traffic post-cutover give the
baseline before annotating more surfaces.

## `KeyvCDS.set` UPSERT patch (#3102)

**Symptom (PROD, 2026-11-11):** recurring `level:"error"` lines from the
`cds-caching` logger:

```
KeyvCDS set error for key kg:rl:api:auth:auth:<hash>: unique constraint violated:
Table(PLUGIN_CDS_CACHING_CACHESTORE), Index(_SYS_TREE_CS_#..._#P0) ... key: ID=kg:rl:...
```

Two namespaces seen — both the KG rate-limiter (`srv/lib/rate-limit/shared-limiter.js`,
key prefix `kg:rl:`): `kg:rl:api:auth:auth:<hash>` and `kg:rl:homepage:anon:<ip>`.

**Root cause:** the plugin's CDS-DB store adapter,
`node_modules/cds-caching/lib/support/KeyvCDS.js`, implemented `set()` as a
**read-then-create/update** (TOCTOU):

```js
const existing = await db.read(entity, key);
if (existing) { await db.update(entity, key).with({ value, expiresAt }); }
else { await db.create(entity, { ID: key, value, expiresAt }); }
```

Under multi-instance (HA) CF, two instances both read "no row" for the same
`kg:rl:*` counter, both take the `else` branch, and the second `create` violates
the `CacheStore` PK unique index. The adapter's own `catch` logs at **error**
level and swallows the exception — so the collision never reaches our
`shared-limiter.js` fail-open (the limiter already over-allows under races, which
is acceptable), but it spams PROD error monitoring and the losing write is
dropped.

**Fix:** a committed **patch-package** patch,
`patches/cds-caching+2.1.0.patch`, replaces that branch with a single atomic
UPSERT:

```js
await db.run(cds.ql.UPSERT.into(entity).entries({ ID: key, value, expiresAt }));
```

`CacheStore` has a single-column PK (`ID : String(900)`), so UPSERT compiles to
HANA `UPSERT ... WITH PRIMARY KEY` and to SQLite `INSERT OR REPLACE` —
last-write-wins, no read-then-write race, no unique-constraint collision. The
patch re-applies on every `npm ci` / `npm install` via the `postinstall` hook
(`patch-package`). Regression test:
`srv/lib/__tests__/keyv-cds-upsert.test.js` (drives the real `KeyvCDS` against an
in-memory CAP fixture, modelling the TOCTOU window deterministically).

**Upstream:** the same read-then-insert is still present on `cds-caching` `main`.
Filed upstream as
[mikezaschka/cds-caching#44](https://github.com/mikezaschka/cds-caching/issues/44)
(issue) +
[mikezaschka/cds-caching#45](https://github.com/mikezaschka/cds-caching/pull/45)
(PR). **Drop condition:** once a published `cds-caching` release carries the
upstream fix and we adopt it, delete `patches/cds-caching+2.1.0.patch`.
**Re-verify the patch on any `cds-caching` version bump** — `patch-package`
fails the install loudly if the target file drifted, so the drift surfaces at
`npm ci`, not silently.

Loading
Loading