Ignore description, description_html, cached_markdown_version columns from projects
What does this MR do and why?
This MR moves description, description_html, and cached_markdown_version read/write from the projects table to namespace_details (via the project's project_namespace), then adds ignore_columns so Rails stops selecting those columns from projects.
It is the projects counterpart to what was done for namespaces in Ignore description related columns from namespa... (!188090 - merged) • Abdul Wadood • 18.1.
The BackfillProjectNamespaceDetails batched background migration is already finalized (finalized_by: 20260120232929), so all data is already in namespace_details.
Reading and writing
- A description can be assigned before
project_namespaceexists (it is built in abefore_validation), andproject_namespacemay be rebuilt afterwards, so the value is buffered on a detachedNamespace::Detailand copied across in anafter_save. It has to beafter_save, becauseNamespacecreates its ownnamespace_detailsin anafter_createthat would otherwise clobber an earlier assignment. - The
descriptionlength validation stays onProjectas well asNamespace::Detail, so an over-long description still fails validation on:descriptioninstead of raising from theafter_save. Project#after_importrefreshes the markdown cache onnamespace_details, not on the project: refreshing on the project writescached_markdown_versionunconditionally, and that column is now ignored.Namespace::Detailexposesprojectandgroup, whichCacheMarkdownField#banzai_render_contextneeds so that references inside a description still render as links.GroupChildEntityrelies on this when rendering a project child, which otherwise 500s the group children endpoint.
Preloading
Since description now lives on namespace_details, every path that renders a project description has to preload project_namespace: :namespace_details or it costs two extra queries per row. The with_project_namespace_details scope wraps that preload and is chained everywhere a project description is rendered:
Project.with_api_entity_associationsProject.with_web_entity_associationsProject.with_group_child_entity_associations- the GraphQL look-ahead preloads
Explore::ProjectsController(the HTML list and the Atom feed)Projects::ForksControllerAdmin::UsersController
Search
Project.searchandsorted_by_similarity_descno longer match or rank on description, only onpathandname.- Joining
namespace_detailswas tried and rejected: it makes these scopes structurally incompatible with theorused in the archived scopes, raisingArgumentErrorand a 500 from the projects GraphQL query. namespace_details.descriptionhas no index, unlikeprojects.description, which had a gin trigram index that goes away with the column.- This aligns
Project.searchandProject.sorted_by_similarity_descwithNamespace.searchandGroup.sorted_by_similarity_desc, which already dropped description matching; the now-unusedexclude_description:keyword argument is removed from the twoProjectmethods. - User-facing behaviour change:
GET /projects?search=and project search in the UI no longer match on descriptions. Worth a follow-up issue if that capability is needed back (would require a gin trigram index onnamespace_details.description).
Audit events
- Project description changes are now audited by
Namespaces::NamespaceDetailChangesAuditor, the class that already audits group descriptions, generalized to accept a group or a project container. - The
project_description_updatedevent name does not change, and its scope andtarget_detailsare still the project. What changes is the event'starget, now theNamespace::Detailrecord instead of theProject, the same shape the group event already has. project_description_updatedissaved_to_database: false, streamed: true, so this payload change only reaches streaming consumers; no rows inaudit_eventsare affected. This is deliberate, per the request in the issue that introduced the event: #377769 (closed)
Closes #590461
References
- Reference MR for namespaces: Ignore description related columns from namespa... (!188090 - merged) • Abdul Wadood • 18.1
- Issue: Ignore description, description_html, cached_ma... (#590461) • Unassigned • 19.6
- Epic: Move description-related columns from namespace... (&7869) • Unassigned
How to set up and validate locally
In the rails console:
project = Project.first; project.descriptionreads fromnamespace_details.project.update!(description: 'new desc')persists tonamespace_details.project.namespace_details.descriptionreturns'new desc'.Project.create!(namespace: Namespace.first, name: 'x', path: 'x', description: 'set at creation', creator: User.first).reload.descriptionreturns'set at creation'.Project.search('new desc')returns the project.- With an external streaming destination configured, updating a project description streams a
project_description_updatedevent whose target is the project's namespace details record.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist.