-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
Summary
The courseassets collection maintains a materialized join table (courseId + contentId → assetId) that is rebuilt on every content insert/update/delete. This is the primary performance bottleneck during clone operations — profiling shows postInsertHook (courseassets) takes 5-6 seconds per 242 items on the server, and with 8 concurrent multilang clones, causes 37+ seconds of contention.
The proposal is to store _assetIds: [...] directly on each content document, eliminating the separate collection and the courseassets module entirely.
Current problem
- Courseassets hooks into content's
postInsertHookand runs per-item:deleteMany+getSchema+extractAssetIds+ per-assetfindOne+insert - For 242 items with assets: 300-500+ DB round-trips per clone
- 8 concurrent multilang clones = 2,400-4,000 queries competing for the same DB connection
- Clone of 242-item × 8-language course: ~47 seconds, of which ~37 seconds is courseassets
Proposed changes
Remove courseassets module entirely:
- The only remaining logic is the asset deletion guard (~10 lines), which moves into
ContentModule.init()as apreDeleteHooktap on the assets module - Remove the
courseassetscollection,courseassetschema, routes, and package - Remove
adapt-authoring-courseassetsfrom allpeerDependenciesand workspace configs - The
RESOURCE_IN_USEerror moves to the content module's error definitions POST /api/courseassets/queryhas no frontend consumers (confirmed — the UI does not reference courseassets anywhere). Only server-side consumers areAdaptFrameworkBuildandAdaptFrameworkImport, which switch to querying content's_assetIdsfield directly
Content module:
- Move
extractAssetIdsutility from courseassets into content'slib/utils/ - Compute and store
_assetIdsduringinsert()and viapreUpdateHooktap - Add MongoDB index on
{ _assetIds: 1 } - Add asset deletion guard in
init()(tapassets.preDeleteHook) - Clone: zero changes needed —
_assetIdscopies automatically with the document
AdaptFrameworkBuild:
- Change
loadAssetDatato querycontent.find({ _courseId }, { projection: { _assetIds: 1 } })instead ofcourseassets.find({ courseId })
AdaptFrameworkImport:
- Remove courseassets dependency;
_assetIdscomputed automatically during content insert
Schema (adapt-schemas):
- Add
_assetIdsarray field to content schema witheditorOnlyflag
Migration
One-time migration to populate _assetIds on existing content documents:
- For each content document, resolve its schema and extract asset IDs
- Bulk
$set_assetIdson all documents - Drop the
courseassetscollection
Performance impact
| Phase | Before | After |
|---|---|---|
| Clone (per language) | 300-500 DB queries (5-6s) | 0 extra queries |
| Clone (8 languages) | 37-47s total | ~1-2s total |
| Regular insert | 1 + 2N queries (N = assets) | 0 extra queries (computed inline) |
| Regular update | 1 + 2N queries | 0 extra queries (computed inline) |
| Asset delete check | 1 query on courseassets | 1 query on content._assetIds (indexed) |
Rollout
Can be phased:
- Content module adds
_assetIdscomputation (backward-compatible, courseassets still works in parallel) - Run migration to populate existing content
- Switch consumers (build, import) to read
_assetIdsfrom content - Deprecate and remove courseassets module (breaking major version)
Related
- Breaking: Improve performance #108 (content performance improvements)
- New: Add dedicated tree endpoint for lightweight content structure queries #109 (clone optimisation)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels