mirror of
https://github.com/discourse/discourse.git
synced 2026-08-02 09:29:37 -05:00
Fixes a memory leak reported by @small-lovely-cat in https://meta.discourse.org/t/perf-there-seems-to-be-a-memory-leakage-in-frontend-store/393510/1 ## Problem The store's identity map holds strong references to every model instance ever fetched during a session. These references are never released, causing memory to grow unbounded as users navigate through the application. Models for topics, posts, categories, and other resources accumulate indefinitely, even after the user has navigated away and the app no longer needs them. ## Why not FIFO/LRU? Traditional cache eviction strategies don't work well for an identity map. If we evict an entry while the app still holds a reference to that model (e.g., it's rendered in a component), the object remains in memory anyway. Worse, the next fetch for that ID creates a *new* instance—consuming more memory and breaking identity semantics where the same ID should always return the same object. ## Solution Introduce `WeakValueMap`, a Map that holds weak references to its values. When a model instance is no longer referenced anywhere else in the application, it becomes eligible for garbage collection, and the identity map entry is automatically cleaned up. **Key implementation details:** - `WeakRef` wraps cached values, allowing GC when unreferenced - `FinalizationRegistry` removes stale map entries after values are collected - Probabilistic sweep (~1% of `get()` calls) as a backup cleanup mechanism This makes the identity map self-regulating—it naturally shrinks when models are no longer in use, while still preserving identity semantics for active models.
87 lines
4.3 KiB
JSON
87 lines
4.3 KiB
JSON
{
|
|
"private": true,
|
|
"repository": "https://github.com/discourse/discourse",
|
|
"author": "Discourse",
|
|
"license": "GPL-2.0-only",
|
|
"devDependencies": {
|
|
"@babel/plugin-proposal-decorators": "^7.28.0",
|
|
"@discourse/lint-configs": "^2.37.1",
|
|
"@discourse/moment-timezone-names-translations": "^1.0.0",
|
|
"@fortawesome/fontawesome-free": "7.1.0",
|
|
"@glint/ember-tsc": "^1.0.8",
|
|
"@glint/tsserver-plugin": "^2.0.8",
|
|
"@rdil/parallel-prettier": "^3.0.0",
|
|
"@swc/core": "^1.15.7",
|
|
"chrome-launcher": "^1.2.1",
|
|
"chrome-remote-interface": "^0.33.3",
|
|
"concurrently": "^9.2.1",
|
|
"ember-template-lint": "7.9.3",
|
|
"esbuild": "^0.25.11",
|
|
"eslint": "9.39.2",
|
|
"jsdoc": "^4.0.5",
|
|
"lefthook": "^2.0.13",
|
|
"licensee": "^11.1.1",
|
|
"lint-to-the-future": "^2.6.4",
|
|
"lint-to-the-future-ember-template": "^3.1.0",
|
|
"lint-to-the-future-eslint": "^3.3.0",
|
|
"playwright": "1.57.0",
|
|
"prettier": "3.8.0",
|
|
"puppeteer-core": "^24.34.0",
|
|
"stylelint": "17.0.0",
|
|
"terser": "^5.44.1",
|
|
"typescript": "^5.9.3"
|
|
},
|
|
"scripts": {
|
|
"dev": "concurrently \"bin/ember-cli server --environment=development\" \"RAILS_ENV=development bin/rails server\"",
|
|
"lint": "concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\"",
|
|
"lint:fix": "concurrently \"pnpm:lint:*:fix\" --names \"fix:\"",
|
|
"lint:css": "pnpm stylelint 'app/assets/stylesheets/**/*.scss' $(script/list_bundled_plugins '/assets/stylesheets/**/*.scss') 'themes/**/*.scss'",
|
|
"lint:css:fix": "pnpm stylelint --fix 'app/assets/stylesheets/**/*.scss' $(script/list_bundled_plugins '/assets/stylesheets/**/*.scss') 'themes/**/*.scss'",
|
|
"lint:js": "eslint ./frontend $(script/list_bundled_plugins) ./themes --cache --no-error-on-unmatched-pattern",
|
|
"lint:js:fix": "eslint --fix ./frontend $(script/list_bundled_plugins) ./themes --no-error-on-unmatched-pattern",
|
|
"lint:hbs": "ember-template-lint 'frontend/**/*.gjs' 'plugins/*/assets/javascripts/**/*.gjs' 'plugins/*/admin/assets/javascripts/**/*.gjs' 'themes/**/*.gjs'",
|
|
"lint:hbs:fix": "ember-template-lint 'frontend/**/*.gjs' 'plugins/*/assets/javascripts/**/*.gjs' 'plugins/*/admin/assets/javascripts/**/*.gjs' 'themes/**/*.gjs' --fix",
|
|
"lint:prettier": "pnpm pprettier --list-different 'app/assets/stylesheets/**/*.scss' 'frontend/**/*.{js,gjs,css}' $(script/list_bundled_plugins '/assets/stylesheets/**/*.scss') $(script/list_bundled_plugins '/{assets,admin/assets,test}/javascripts/**/*.{js,gjs}') 'themes/**/*.{js,gjs,scss}'",
|
|
"lint:prettier:fix": "pnpm prettier -w --no-error-on-unmatched-pattern 'app/assets/stylesheets/**/*.scss' 'frontend/**/*.{js,gjs,css}' $(script/list_bundled_plugins '/assets/stylesheets/**/*.scss') $(script/list_bundled_plugins '/{assets,admin/assets,test}/javascripts/**/*.{js,gjs}') 'themes/**/*.{js,gjs,scss}'",
|
|
"lint:types": "ember-tsc -b",
|
|
"lttf:ignore": "lint-to-the-future ignore",
|
|
"lttf:output": "lint-to-the-future output -o ./lint-progress/",
|
|
"lint-progress": "pnpm lttf:output && npx html-pages ./lint-progress --no-cache",
|
|
"ember": "pnpm --dir=frontend/discourse ember",
|
|
"playwright-install": "playwright install --no-shell chromium"
|
|
},
|
|
"engines": {
|
|
"node": ">= 20",
|
|
"npm": "please-use-pnpm",
|
|
"yarn": "please-use-pnpm",
|
|
"pnpm": "^10"
|
|
},
|
|
"packageManager": "pnpm@10.28.0",
|
|
"pnpm": {
|
|
"patchedDependencies": {
|
|
"ember-this-fallback@0.4.0": "patches/ember-this-fallback@0.4.0.patch",
|
|
"babel-plugin-debug-macros@0.3.4": "patches/babel-plugin-debug-macros@0.3.4.patch",
|
|
"licensee@11.1.1": "patches/licensee@11.1.1.patch",
|
|
"@ember-compat/tracked-built-ins@0.9.1": "patches/@ember-compat__tracked-built-ins@0.9.1.patch",
|
|
"ember-source@6.6.0": "patches/ember-source@6.6.0.patch",
|
|
"http-proxy@1.18.1": "patches/http-proxy@1.18.1.patch"
|
|
},
|
|
"peerDependencyRules": {
|
|
"allowedVersions": {
|
|
"lint-to-the-future-eslint>eslint": "*",
|
|
"@mixer/parallel-prettier>prettier": "*",
|
|
"lint-to-the-future-ember-template>ember-template-lint": "*",
|
|
"ember-this-fallback>ember-source": "*",
|
|
"ember-this-fallback>ember-cli-htmlbars": "*",
|
|
"@fullcalendar/moment-timezone>moment-timezone": "*"
|
|
},
|
|
"ignoreMissing": [
|
|
"webpack"
|
|
]
|
|
},
|
|
"overrides": {
|
|
"@ember/test-waiters": "4.1.1"
|
|
}
|
|
}
|
|
}
|