[{"data":1,"prerenderedAt":718},["ShallowReactive",2],{"/en-us/blog/database-case-study-store-and-update-namespace-statistics/":3,"navigation-en-us":34,"banner-en-us":463,"footer-en-us":480,"Mayra Cabrera":690,"next-steps-en-us":703},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":24,"_id":27,"_type":28,"title":29,"_source":30,"_file":31,"_stem":32,"_extension":33},"/en-us/blog/database-case-study-store-and-update-namespace-statistics","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"Store and update namespace statistics in a performant manner","Explore all the different engineering approaches to store and update the namespace statistics in a performant manner.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749672677/Blog/Hero%20Images/metalgears_databasecasestudy.jpg","https://about.gitlab.com/blog/database-case-study-store-and-update-namespace-statistics","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Store and update namespace statistics in a performant manner\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Mayra Cabrera\"}],\n        \"datePublished\": \"2019-10-14\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":19,"body":20,"category":21,"tags":22},[18],"Mayra Cabrera","2019-10-14","Managing storage space on large GitLab instances, such as GitLab.com, can be\na challenge. At the moment, we only have a restriction on repository limits,\nbut no restriction on most of the other items that can consume storage\nspace: wiki, lfs objects, artifacts, and packages, to mention a few.\n\n\nWe want to facilitate a method for easily viewing the amount of storage\nconsumed by a group and allow easy management on GitLab.com by setting\n[storage and limits management for\ngroups](https://gitlab.com/groups/gitlab-org/-/epics/886). But to do that we\nneed a way to track the statistics of a namespace, whether it is a Group or\na User namespace.\n\n\n## Proposal to track the statistics of a namespace\n\n\n1. Create a new ActiveRecord model to hold the namespaces' statistics in an\naggregated form: Only for root namespaces.\n\n2. Refresh the statistics in this model every time a project belonging to\nthis namespace is changed.\n\n\nThe \"refresh\" part is the tricky one. Currently we don't have a pattern to\nupdate/refresh the namespace statistics every time a project belonging to\nthe namespace is updated.\n\n\nWe refreshed projects statistics in the following way:\n\n\n1. We have a model called `ProjectStatistics`,\n\n2. The records on `ProjectStatistics` are updated through a\n[callback](https://gitlab.com/gitlab-org/gitlab-ce/blob/v12.2.0.pre/app/models/project.rb#L90)\nevery time the project is saved.\n\n3. The summary of those statistics per namespace is then retrieved by\n[`Namespaces#with_statistics`](https://gitlab.com/gitlab-org/gitlab-ce/blob/v12.2.0.pre/app/models/namespace.rb#L70)\nscope.\n\n\nAnalyzing this query we noticed that:\n\n\n- It takes up to `1.2` seconds for namespaces with over `15 000` projects.\n\n- Any attempt to run `EXPLAIN ANALYZE` results in query timeouts (15\nseconds) when using our internal tooling.\n\n\nAdditionally, the callback to update the project statistics doesn't scale.\nIt is currently one of the most [frequently run and expensive database\nqueries](https://gitlab.com/gitlab-org/gitlab-ce/issues/62488) on\nGitLab.com. We can't add one more query to it as\n\nit will increase the transaction's length.\n\n\nBecause of these reasons, we can't apply the same pattern to store\n\nand update the namespaces' statistics, as the `namespaces` table is one\n\nof the largest tables on GitLab.com. Therefore, we have to find a performant\nand\n\nalternative method.\n\n\n## Our Attempts\n\n\n### Attempt A: PostgreSQL materialized view\n\n\nUpdate the ActiveRecord model with a refresh strategy based on project\nroutes and a [materialized\nview](https://www.postgresql.org/docs/9.6/rules-materializedviews.html):\n\n\n```sql\n\nSELECT split_part(\"rs\".path, '/', 1) as root_path,\n        COALESCE(SUM(ps.storage_size), 0) AS storage_size,\n        COALESCE(SUM(ps.repository_size), 0) AS repository_size,\n        COALESCE(SUM(ps.wiki_size), 0) AS wiki_size,\n        COALESCE(SUM(ps.lfs_objects_size), 0) AS lfs_objects_size,\n        COALESCE(SUM(ps.build_artifacts_size), 0) AS build_artifacts_size,\n        COALESCE(SUM(ps.packages_size), 0) AS packages_size\nFROM \"projects\"\n    INNER JOIN routes rs ON rs.source_id = projects.id AND rs.source_type = 'Project'\n    INNER JOIN project_statistics ps ON ps.project_id  = projects.id\nGROUP BY root_path\n\n```\n\n\nWe could then execute the query with:\n\n\n```sql\n\nREFRESH MATERIALIZED VIEW root_namespace_storage_statistics;\n\n```\n\n\nWhile this implied a single query update, it has some downsides:\n\n\n- The query itself would not be fast, as it would need to update all the\nstatistics every time it runs. Execution time of this query will increase as\nthe number of namespaces and projects grow.\n\n- Materialized views syntax varies from PostgreSQL and MySQL. At the time\nthis feature was worked on, [GitLab still supported MySQL, which it now no\nlonger supports.](/blog/removing-mysql-support/).\n\n- Rails does not have native support for materialized views. We'd need to\nuse a specialized gem to take care of the management of the database views,\nwhich implies additional work.\n\n\n### Attempt B: An update through a CTE\n\n\nUpdate the ActiveRecord model with a refresh strategy through a [Common\nTable Expression](https://www.postgresql.org/docs/9.1/queries-with.html).\n\n\n```sql\n\nWITH refresh AS (\n  SELECT split_part(\"rs\".path, '/', 1) as root_path,\n        COALESCE(SUM(ps.storage_size), 0) AS storage_size,\n        COALESCE(SUM(ps.repository_size), 0) AS repository_size,\n        COALESCE(SUM(ps.wiki_size), 0) AS wiki_size,\n        COALESCE(SUM(ps.lfs_objects_size), 0) AS lfs_objects_size,\n        COALESCE(SUM(ps.build_artifacts_size), 0) AS build_artifacts_size,\n        COALESCE(SUM(ps.packages_size), 0) AS packages_size\n  FROM \"projects\"\n        INNER JOIN routes rs ON rs.source_id = projects.id AND rs.source_type = 'Project'\n        INNER JOIN project_statistics ps ON ps.project_id  = projects.id\n  GROUP BY root_path)\nUPDATE namespace_storage_statistics\n\nSET storage_size = refresh.storage_size,\n    repository_size = refresh.repository_size,\n    wiki_size = refresh.wiki_size,\n    lfs_objects_size = refresh.lfs_objects_size,\n    build_artifacts_size = refresh.build_artifacts_size,\n    packages_size  = refresh.packages_size\nFROM refresh\n    INNER JOIN routes rs ON rs.path = refresh.root_path AND rs.source_type = 'Namespace'\nWHERE namespace_storage_statistics.namespace_id = rs.source_id\n\n```\n\n\nUnlike Attempt A, a CTE will be limited to the namespace we care about\ninstead of operating on all namespaces. The downside of it,\n\nis that earlier versions of MySQL do not support Common Table Expressions.\n\n\n### Attempt C: Get rid of the model and store the statistics on Redis\n\n\nWe could get rid of the model that stores the statistics in aggregated form\nand instead use a Redis Set.\n\nThis would be the [boring solution](https://handbook.gitlab.com/handbook/values/#boring-solutions) and\nthe fastest one\n\nto implement, as GitLab already includes\n[Redis](https://docs.gitlab.com/ee/development/architecture.html#redis) as\npart of its Architecture.\n\n\nThe downside of this approach is that Redis does not provide the same\npersistence/consistency guarantees as PostgreSQL,\n\nand the namespace statistics are information we can't afford to lose in a\ncase of a Redis failure. Also, searching for\n\ninformation like the largest namespaces per repository size will be easier\nto do in PostgreSQL than in Redis.\n\n\n### Attempt D: Tag the root namespace and its child namespaces\n\n\nDirectly relate the root namespace to its child namespaces, so\n\nwhenever a child namespace is created, it's also tagged with the\n\nroot namespace ID:\n\n\n| id | root_id | parent_id\n\n|:---|:--------|:----------\n\n| 1  | 1       | NULL\n\n| 2  | 1       | 1\n\n| 3  | 1       | 2\n\n\nTo aggregate the statistics inside a namespace we'd execute something like:\n\n\n```sql\n\nSELECT COUNT(...)\n\nFROM projects\n\nWHERE namespace_id IN (\n  SELECT id\n  FROM namespaces\n  WHERE root_id = X\n)\n\n```\n\n\nEven though this approach would make aggregating much easier, it has some\nmajor downsides:\n\n\n- We'd have to migrate **all namespaces** by adding and filling a new\ncolumn. Because of the size of the table, dealing with the time/cost will\nnot be great. The [background migration will take approximately 153\nhours](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/29772#note_182201607).\n\n- The background migration has to be shipped one release before we want to\nstart using the new data, delaying the functionality by another milestone.\n\n\n### Attempt E: Update the namespace storage statistics asynchronously\n\n\nFor this approach we continue using the incremental statistics updates we\nalready have,\n\nbut we refresh them through Sidekiq jobs and in different SQL transactions:\n\n\n1. Create a second table (`namespace_aggregation_schedules`) with two\ncolumns `id` and `namespace_id`.\n\n1. Whenever the statistics of a project changes, insert a row into\n`namespace_aggregation_schedules`\n   - We don't insert a new row if there's already one related to the root namespace.\n   - Keeping in mind the length of the transaction that involves [updating `project_statistics`](https://gitlab.com/gitlab-org/gitlab-ce/issues/62488), the insertion should be done in a different transaction and through a Sidekiq Job.\n1. After inserting the row, we schedule another worker to be executed async\nat two different moments:\n   - One enqueued for immediate execution and another one scheduled in `1.5h` hours.\n   - We only schedule the jobs if we can obtain a `1.5h` lease on Redis on a key based on the root namespace ID.\n   - If we can't obtain the lease it indicates there's another aggregation already in progress or scheduled in no more than `1.5h`.\n1. This worker will:\n   - Update the root namespace storage statistics by querying all the namespaces through a service.\n   - Delete the related `namespace_aggregation_schedules` after the update.\n1. Another Sidekiq job is also included to traverse any remaining rows on\nthe `namespace_aggregation_schedules` table and schedule jobs for every\npending row.\n   - This job is scheduled with cron to run every night (UTC).\n\nThis implementation has the following benefits:\n\n\n- All the updates are done async, so we're not increasing the length of the\ntransactions for `project_statistics`.\n\n- We're doing the update in a single SQL query.\n\n- It is compatible with PostgreSQL and MySQL.\n\n- No background migration is required.\n\n\nThe downsides of this approaches are:\n\n\n* Namespaces' statistics are updated up to `1.5` hours after the change is\ndone, which means there's a brief window in time where the statistics are\ninaccurate. This is not a major problem because we're not currently\n[enforcing storage\nlimits](https://gitlab.com/gitlab-org/gitlab-ce/issues/30421).\n\n* From the implementation perspective, this approach is more complex than\nthe migration approach (Attempt D).\n\n* `namespace_aggregation_schedules` table will see a high rate of inserts\nand deletes, which may require that we tune auto vacuuming for this table.\n\n\nWe went with *Attempt E* because updating the storage statistics\nasynchronously was the less problematic and\n\nperformant approach of aggregating the root namespaces.\n\n\n## Enabling the feature on GitLab.com\n\n\nGiven this is a performance improvement, we have to be very careful\nintroducing this change to GitLab.com: Which is why\n\nwe decided to release it under [feature\nflag](https://docs.gitlab.com/ee/development/feature_flags/) and roll it out\ngradually by:\n\n\n1. Enable it on our staging environment and measure the performance.\n\n2. Enable it on GitLab.com on different periods for the `gitlab-org` group\nand measure the performance.\n\n3. Enable it globally on GitLab.com on different periods and measure the\nperformance.\n\n\nFinally if no problem arises, we can be confident this change performs\nproperly on GitLab.com and we can\n\nremove the feature flag.\n\n\n## Measuring the performance\n\n\nTo assess the execution of this approach, we monitored the [Sidekiq\ndashboards](https://dashboards.gitlab.com/d/9GOIu9Siz/sidekiq-stats?orgId=1)\non Kibana to ensure jobs were being executed flawlessly and without using\ntoo much memory or CPU. Particularly, we observed the \"Sidekiq queue size,\"\n\"Rate of running jobs,\" and \"Running jobs\" dashboards.\n\n\n### On staging\n\n\nThe feature was enabled globally on staging and the execution of the jobs\nwas satisfactory. But there was barely any traffic to measure the impact of\nour changes:\n\n\n![Graph showing the queue size of the ScheduleAggregationWorker on\nstaging](https://about.gitlab.com/images/blogimages/namespace_statistics/staging-1.png){:\n.shadow.medium.center}\n\n\n### Enabling root namespaces on GitLab.com\n\n\nOur results were different on GitLab.com. We first enabled it for the\n`gitlab-org` group and we quickly started to observe more traffic:\n\n\n![Graph showing the queue size of the ScheduleAggregationWorker on\nGitLab.com](https://about.gitlab.com/images/blogimages/namespace_statistics/production-1.png){:\n.shadow.medium.center}\n\n\n![Graph showing the running jobs of the ScheduleAggregationWorker on\nGitLab.com](https://about.gitlab.com/images/blogimages/namespace_statistics/production-2.png){:\n.shadow.medium.center}\n\n\nOnce we enabled the feature flag globally, the rate of running jobs\nincreased considerably:\n\n\n![Graph showing the rate running jobs of the ScheduleAggregationWorker on\nGitLab.com](https://about.gitlab.com/images/blogimages/namespace_statistics/production-3.png){:\n.shadow.medium.center}\n\n\n![Graph showing the rate running jobs of the RootStatisticsWorker on\nGitLab.com](https://about.gitlab.com/images/blogimages/namespace_statistics/production-4.png){:\n.shadow.medium.center}\n\n\n## Root namespaces on GitLab.com today\n\n\nWe currently have nearly `400 000` statistics stored for root namespaces on\nGitLab.com, which are updated at a high pace.\n\nBeing able to efficiently fetch those statistics allows one to easily track\nthe top biggest repositories and/or namespaces on an instance\n\nand to start paving the way to enforce storage limits for groups on\nGitLab.com.\n\n\nLearn more about this use case by reading:\n\n\n- [The original issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/62214)\n\n- [Merge Request with the\nimplementation](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/28996)\n\n- [Details of the performance measured against staging and production\n(GitLab.com)](https://gitlab.com/gitlab-org/gitlab-ce/issues/64092)\n\n\nCover photo by [Bill\nOxford](https://unsplash.com/@bill_oxford?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)\non\n[Unsplash](https://unsplash.com/search/photos/engineering?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText).\n\n{: .note}\n","engineering",[23],"inside GitLab",{"slug":25,"featured":6,"template":26},"database-case-study-store-and-update-namespace-statistics","BlogPost","content:en-us:blog:database-case-study-store-and-update-namespace-statistics.yml","yaml","Database Case Study Store And Update Namespace Statistics","content","en-us/blog/database-case-study-store-and-update-namespace-statistics.yml","en-us/blog/database-case-study-store-and-update-namespace-statistics","yml",{"_path":35,"_dir":36,"_draft":6,"_partial":6,"_locale":7,"data":37,"_id":459,"_type":28,"title":460,"_source":30,"_file":461,"_stem":462,"_extension":33},"/shared/en-us/main-navigation","en-us",{"logo":38,"freeTrial":43,"sales":48,"login":53,"items":58,"search":390,"minimal":421,"duo":440,"pricingDeployment":449},{"config":39},{"href":40,"dataGaName":41,"dataGaLocation":42},"/","gitlab logo","header",{"text":44,"config":45},"Get free trial",{"href":46,"dataGaName":47,"dataGaLocation":42},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":49,"config":50},"Talk to sales",{"href":51,"dataGaName":52,"dataGaLocation":42},"/sales/","sales",{"text":54,"config":55},"Sign in",{"href":56,"dataGaName":57,"dataGaLocation":42},"https://gitlab.com/users/sign_in/","sign in",[59,103,201,206,311,371],{"text":60,"config":61,"cards":63,"footer":86},"Platform",{"dataNavLevelOne":62},"platform",[64,70,78],{"title":60,"description":65,"link":66},"The most comprehensive AI-powered DevSecOps Platform",{"text":67,"config":68},"Explore our Platform",{"href":69,"dataGaName":62,"dataGaLocation":42},"/platform/",{"title":71,"description":72,"link":73},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":74,"config":75},"Meet GitLab Duo",{"href":76,"dataGaName":77,"dataGaLocation":42},"/gitlab-duo/","gitlab duo ai",{"title":79,"description":80,"link":81},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":82,"config":83},"Learn more",{"href":84,"dataGaName":85,"dataGaLocation":42},"/why-gitlab/","why gitlab",{"title":87,"items":88},"Get started with",[89,94,99],{"text":90,"config":91},"Platform Engineering",{"href":92,"dataGaName":93,"dataGaLocation":42},"/solutions/platform-engineering/","platform engineering",{"text":95,"config":96},"Developer Experience",{"href":97,"dataGaName":98,"dataGaLocation":42},"/developer-experience/","Developer experience",{"text":100,"config":101},"MLOps",{"href":102,"dataGaName":100,"dataGaLocation":42},"/topics/devops/the-role-of-ai-in-devops/",{"text":104,"left":105,"config":106,"link":108,"lists":112,"footer":183},"Product",true,{"dataNavLevelOne":107},"solutions",{"text":109,"config":110},"View all Solutions",{"href":111,"dataGaName":107,"dataGaLocation":42},"/solutions/",[113,138,162],{"title":114,"description":115,"link":116,"items":121},"Automation","CI/CD and automation to accelerate deployment",{"config":117},{"icon":118,"href":119,"dataGaName":120,"dataGaLocation":42},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[122,126,130,134],{"text":123,"config":124},"CI/CD",{"href":125,"dataGaLocation":42,"dataGaName":123},"/solutions/continuous-integration/",{"text":127,"config":128},"AI-Assisted Development",{"href":76,"dataGaLocation":42,"dataGaName":129},"AI assisted development",{"text":131,"config":132},"Source Code Management",{"href":133,"dataGaLocation":42,"dataGaName":131},"/solutions/source-code-management/",{"text":135,"config":136},"Automated Software Delivery",{"href":119,"dataGaLocation":42,"dataGaName":137},"Automated software delivery",{"title":139,"description":140,"link":141,"items":146},"Security","Deliver code faster without compromising security",{"config":142},{"href":143,"dataGaName":144,"dataGaLocation":42,"icon":145},"/solutions/security-compliance/","security and compliance","ShieldCheckLight",[147,152,157],{"text":148,"config":149},"Application Security Testing",{"href":150,"dataGaName":151,"dataGaLocation":42},"/solutions/application-security-testing/","Application security testing",{"text":153,"config":154},"Software Supply Chain Security",{"href":155,"dataGaLocation":42,"dataGaName":156},"/solutions/supply-chain/","Software supply chain security",{"text":158,"config":159},"Software Compliance",{"href":160,"dataGaName":161,"dataGaLocation":42},"/solutions/software-compliance/","software compliance",{"title":163,"link":164,"items":169},"Measurement",{"config":165},{"icon":166,"href":167,"dataGaName":168,"dataGaLocation":42},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[170,174,178],{"text":171,"config":172},"Visibility & Measurement",{"href":167,"dataGaLocation":42,"dataGaName":173},"Visibility and Measurement",{"text":175,"config":176},"Value Stream Management",{"href":177,"dataGaLocation":42,"dataGaName":175},"/solutions/value-stream-management/",{"text":179,"config":180},"Analytics & Insights",{"href":181,"dataGaLocation":42,"dataGaName":182},"/solutions/analytics-and-insights/","Analytics and insights",{"title":184,"items":185},"GitLab for",[186,191,196],{"text":187,"config":188},"Enterprise",{"href":189,"dataGaLocation":42,"dataGaName":190},"/enterprise/","enterprise",{"text":192,"config":193},"Small Business",{"href":194,"dataGaLocation":42,"dataGaName":195},"/small-business/","small business",{"text":197,"config":198},"Public Sector",{"href":199,"dataGaLocation":42,"dataGaName":200},"/solutions/public-sector/","public sector",{"text":202,"config":203},"Pricing",{"href":204,"dataGaName":205,"dataGaLocation":42,"dataNavLevelOne":205},"/pricing/","pricing",{"text":207,"config":208,"link":210,"lists":214,"feature":298},"Resources",{"dataNavLevelOne":209},"resources",{"text":211,"config":212},"View all resources",{"href":213,"dataGaName":209,"dataGaLocation":42},"/resources/",[215,248,270],{"title":216,"items":217},"Getting started",[218,223,228,233,238,243],{"text":219,"config":220},"Install",{"href":221,"dataGaName":222,"dataGaLocation":42},"/install/","install",{"text":224,"config":225},"Quick start guides",{"href":226,"dataGaName":227,"dataGaLocation":42},"/get-started/","quick setup checklists",{"text":229,"config":230},"Learn",{"href":231,"dataGaLocation":42,"dataGaName":232},"https://university.gitlab.com/","learn",{"text":234,"config":235},"Product documentation",{"href":236,"dataGaName":237,"dataGaLocation":42},"https://docs.gitlab.com/","product documentation",{"text":239,"config":240},"Best practice videos",{"href":241,"dataGaName":242,"dataGaLocation":42},"/getting-started-videos/","best practice videos",{"text":244,"config":245},"Integrations",{"href":246,"dataGaName":247,"dataGaLocation":42},"/integrations/","integrations",{"title":249,"items":250},"Discover",[251,256,260,265],{"text":252,"config":253},"Customer success stories",{"href":254,"dataGaName":255,"dataGaLocation":42},"/customers/","customer success stories",{"text":257,"config":258},"Blog",{"href":259,"dataGaName":5,"dataGaLocation":42},"/blog/",{"text":261,"config":262},"Remote",{"href":263,"dataGaName":264,"dataGaLocation":42},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":266,"config":267},"TeamOps",{"href":268,"dataGaName":269,"dataGaLocation":42},"/teamops/","teamops",{"title":271,"items":272},"Connect",[273,278,283,288,293],{"text":274,"config":275},"GitLab Services",{"href":276,"dataGaName":277,"dataGaLocation":42},"/services/","services",{"text":279,"config":280},"Community",{"href":281,"dataGaName":282,"dataGaLocation":42},"/community/","community",{"text":284,"config":285},"Forum",{"href":286,"dataGaName":287,"dataGaLocation":42},"https://forum.gitlab.com/","forum",{"text":289,"config":290},"Events",{"href":291,"dataGaName":292,"dataGaLocation":42},"/events/","events",{"text":294,"config":295},"Partners",{"href":296,"dataGaName":297,"dataGaLocation":42},"/partners/","partners",{"backgroundColor":299,"textColor":300,"text":301,"image":302,"link":306},"#2f2a6b","#fff","Insights for the future of software development",{"altText":303,"config":304},"the source promo card",{"src":305},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":307,"config":308},"Read the latest",{"href":309,"dataGaName":310,"dataGaLocation":42},"/the-source/","the source",{"text":312,"config":313,"lists":315},"Company",{"dataNavLevelOne":314},"company",[316],{"items":317},[318,323,329,331,336,341,346,351,356,361,366],{"text":319,"config":320},"About",{"href":321,"dataGaName":322,"dataGaLocation":42},"/company/","about",{"text":324,"config":325,"footerGa":328},"Jobs",{"href":326,"dataGaName":327,"dataGaLocation":42},"/jobs/","jobs",{"dataGaName":327},{"text":289,"config":330},{"href":291,"dataGaName":292,"dataGaLocation":42},{"text":332,"config":333},"Leadership",{"href":334,"dataGaName":335,"dataGaLocation":42},"/company/team/e-group/","leadership",{"text":337,"config":338},"Team",{"href":339,"dataGaName":340,"dataGaLocation":42},"/company/team/","team",{"text":342,"config":343},"Handbook",{"href":344,"dataGaName":345,"dataGaLocation":42},"https://handbook.gitlab.com/","handbook",{"text":347,"config":348},"Investor relations",{"href":349,"dataGaName":350,"dataGaLocation":42},"https://ir.gitlab.com/","investor relations",{"text":352,"config":353},"Trust Center",{"href":354,"dataGaName":355,"dataGaLocation":42},"/security/","trust center",{"text":357,"config":358},"AI Transparency Center",{"href":359,"dataGaName":360,"dataGaLocation":42},"/ai-transparency-center/","ai transparency center",{"text":362,"config":363},"Newsletter",{"href":364,"dataGaName":365,"dataGaLocation":42},"/company/contact/","newsletter",{"text":367,"config":368},"Press",{"href":369,"dataGaName":370,"dataGaLocation":42},"/press/","press",{"text":372,"config":373,"lists":374},"Contact us",{"dataNavLevelOne":314},[375],{"items":376},[377,380,385],{"text":49,"config":378},{"href":51,"dataGaName":379,"dataGaLocation":42},"talk to sales",{"text":381,"config":382},"Get help",{"href":383,"dataGaName":384,"dataGaLocation":42},"/support/","get help",{"text":386,"config":387},"Customer portal",{"href":388,"dataGaName":389,"dataGaLocation":42},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":391,"login":392,"suggestions":399},"Close",{"text":393,"link":394},"To search repositories and projects, login to",{"text":395,"config":396},"gitlab.com",{"href":56,"dataGaName":397,"dataGaLocation":398},"search login","search",{"text":400,"default":401},"Suggestions",[402,404,408,410,414,418],{"text":71,"config":403},{"href":76,"dataGaName":71,"dataGaLocation":398},{"text":405,"config":406},"Code Suggestions (AI)",{"href":407,"dataGaName":405,"dataGaLocation":398},"/solutions/code-suggestions/",{"text":123,"config":409},{"href":125,"dataGaName":123,"dataGaLocation":398},{"text":411,"config":412},"GitLab on AWS",{"href":413,"dataGaName":411,"dataGaLocation":398},"/partners/technology-partners/aws/",{"text":415,"config":416},"GitLab on Google Cloud",{"href":417,"dataGaName":415,"dataGaLocation":398},"/partners/technology-partners/google-cloud-platform/",{"text":419,"config":420},"Why GitLab?",{"href":84,"dataGaName":419,"dataGaLocation":398},{"freeTrial":422,"mobileIcon":427,"desktopIcon":432,"secondaryButton":435},{"text":423,"config":424},"Start free trial",{"href":425,"dataGaName":47,"dataGaLocation":426},"https://gitlab.com/-/trials/new/","nav",{"altText":428,"config":429},"Gitlab Icon",{"src":430,"dataGaName":431,"dataGaLocation":426},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":428,"config":433},{"src":434,"dataGaName":431,"dataGaLocation":426},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":436,"config":437},"Get Started",{"href":438,"dataGaName":439,"dataGaLocation":426},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":441,"mobileIcon":445,"desktopIcon":447},{"text":442,"config":443},"Learn more about GitLab Duo",{"href":76,"dataGaName":444,"dataGaLocation":426},"gitlab duo",{"altText":428,"config":446},{"src":430,"dataGaName":431,"dataGaLocation":426},{"altText":428,"config":448},{"src":434,"dataGaName":431,"dataGaLocation":426},{"freeTrial":450,"mobileIcon":455,"desktopIcon":457},{"text":451,"config":452},"Back to pricing",{"href":204,"dataGaName":453,"dataGaLocation":426,"icon":454},"back to pricing","GoBack",{"altText":428,"config":456},{"src":430,"dataGaName":431,"dataGaLocation":426},{"altText":428,"config":458},{"src":434,"dataGaName":431,"dataGaLocation":426},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":464,"_dir":36,"_draft":6,"_partial":6,"_locale":7,"title":465,"button":466,"image":471,"config":475,"_id":477,"_type":28,"_source":30,"_file":478,"_stem":479,"_extension":33},"/shared/en-us/banner","is now in public beta!",{"text":467,"config":468},"Try the Beta",{"href":469,"dataGaName":470,"dataGaLocation":42},"/gitlab-duo/agent-platform/","duo banner",{"altText":472,"config":473},"GitLab Duo Agent Platform",{"src":474},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":476},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":481,"_dir":36,"_draft":6,"_partial":6,"_locale":7,"data":482,"_id":686,"_type":28,"title":687,"_source":30,"_file":688,"_stem":689,"_extension":33},"/shared/en-us/main-footer",{"text":483,"source":484,"edit":490,"contribute":495,"config":500,"items":505,"minimal":678},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":485,"config":486},"View page source",{"href":487,"dataGaName":488,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":491,"config":492},"Edit this page",{"href":493,"dataGaName":494,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":496,"config":497},"Please contribute",{"href":498,"dataGaName":499,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":501,"facebook":502,"youtube":503,"linkedin":504},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[506,529,585,614,648],{"title":60,"links":507,"subMenu":512},[508],{"text":509,"config":510},"DevSecOps platform",{"href":69,"dataGaName":511,"dataGaLocation":489},"devsecops platform",[513],{"title":202,"links":514},[515,519,524],{"text":516,"config":517},"View plans",{"href":204,"dataGaName":518,"dataGaLocation":489},"view plans",{"text":520,"config":521},"Why Premium?",{"href":522,"dataGaName":523,"dataGaLocation":489},"/pricing/premium/","why premium",{"text":525,"config":526},"Why Ultimate?",{"href":527,"dataGaName":528,"dataGaLocation":489},"/pricing/ultimate/","why ultimate",{"title":530,"links":531},"Solutions",[532,537,539,541,546,551,555,558,562,567,569,572,575,580],{"text":533,"config":534},"Digital transformation",{"href":535,"dataGaName":536,"dataGaLocation":489},"/topics/digital-transformation/","digital transformation",{"text":148,"config":538},{"href":150,"dataGaName":148,"dataGaLocation":489},{"text":137,"config":540},{"href":119,"dataGaName":120,"dataGaLocation":489},{"text":542,"config":543},"Agile development",{"href":544,"dataGaName":545,"dataGaLocation":489},"/solutions/agile-delivery/","agile delivery",{"text":547,"config":548},"Cloud transformation",{"href":549,"dataGaName":550,"dataGaLocation":489},"/topics/cloud-native/","cloud transformation",{"text":552,"config":553},"SCM",{"href":133,"dataGaName":554,"dataGaLocation":489},"source code management",{"text":123,"config":556},{"href":125,"dataGaName":557,"dataGaLocation":489},"continuous integration & delivery",{"text":559,"config":560},"Value stream management",{"href":177,"dataGaName":561,"dataGaLocation":489},"value stream management",{"text":563,"config":564},"GitOps",{"href":565,"dataGaName":566,"dataGaLocation":489},"/solutions/gitops/","gitops",{"text":187,"config":568},{"href":189,"dataGaName":190,"dataGaLocation":489},{"text":570,"config":571},"Small business",{"href":194,"dataGaName":195,"dataGaLocation":489},{"text":573,"config":574},"Public sector",{"href":199,"dataGaName":200,"dataGaLocation":489},{"text":576,"config":577},"Education",{"href":578,"dataGaName":579,"dataGaLocation":489},"/solutions/education/","education",{"text":581,"config":582},"Financial services",{"href":583,"dataGaName":584,"dataGaLocation":489},"/solutions/finance/","financial services",{"title":207,"links":586},[587,589,591,593,596,598,600,602,604,606,608,610,612],{"text":219,"config":588},{"href":221,"dataGaName":222,"dataGaLocation":489},{"text":224,"config":590},{"href":226,"dataGaName":227,"dataGaLocation":489},{"text":229,"config":592},{"href":231,"dataGaName":232,"dataGaLocation":489},{"text":234,"config":594},{"href":236,"dataGaName":595,"dataGaLocation":489},"docs",{"text":257,"config":597},{"href":259,"dataGaName":5,"dataGaLocation":489},{"text":252,"config":599},{"href":254,"dataGaName":255,"dataGaLocation":489},{"text":261,"config":601},{"href":263,"dataGaName":264,"dataGaLocation":489},{"text":274,"config":603},{"href":276,"dataGaName":277,"dataGaLocation":489},{"text":266,"config":605},{"href":268,"dataGaName":269,"dataGaLocation":489},{"text":279,"config":607},{"href":281,"dataGaName":282,"dataGaLocation":489},{"text":284,"config":609},{"href":286,"dataGaName":287,"dataGaLocation":489},{"text":289,"config":611},{"href":291,"dataGaName":292,"dataGaLocation":489},{"text":294,"config":613},{"href":296,"dataGaName":297,"dataGaLocation":489},{"title":312,"links":615},[616,618,620,622,624,626,628,632,637,639,641,643],{"text":319,"config":617},{"href":321,"dataGaName":314,"dataGaLocation":489},{"text":324,"config":619},{"href":326,"dataGaName":327,"dataGaLocation":489},{"text":332,"config":621},{"href":334,"dataGaName":335,"dataGaLocation":489},{"text":337,"config":623},{"href":339,"dataGaName":340,"dataGaLocation":489},{"text":342,"config":625},{"href":344,"dataGaName":345,"dataGaLocation":489},{"text":347,"config":627},{"href":349,"dataGaName":350,"dataGaLocation":489},{"text":629,"config":630},"Sustainability",{"href":631,"dataGaName":629,"dataGaLocation":489},"/sustainability/",{"text":633,"config":634},"Diversity, inclusion and belonging (DIB)",{"href":635,"dataGaName":636,"dataGaLocation":489},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":352,"config":638},{"href":354,"dataGaName":355,"dataGaLocation":489},{"text":362,"config":640},{"href":364,"dataGaName":365,"dataGaLocation":489},{"text":367,"config":642},{"href":369,"dataGaName":370,"dataGaLocation":489},{"text":644,"config":645},"Modern Slavery Transparency Statement",{"href":646,"dataGaName":647,"dataGaLocation":489},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":649,"links":650},"Contact Us",[651,654,656,658,663,668,673],{"text":652,"config":653},"Contact an expert",{"href":51,"dataGaName":52,"dataGaLocation":489},{"text":381,"config":655},{"href":383,"dataGaName":384,"dataGaLocation":489},{"text":386,"config":657},{"href":388,"dataGaName":389,"dataGaLocation":489},{"text":659,"config":660},"Status",{"href":661,"dataGaName":662,"dataGaLocation":489},"https://status.gitlab.com/","status",{"text":664,"config":665},"Terms of use",{"href":666,"dataGaName":667,"dataGaLocation":489},"/terms/","terms of use",{"text":669,"config":670},"Privacy statement",{"href":671,"dataGaName":672,"dataGaLocation":489},"/privacy/","privacy statement",{"text":674,"config":675},"Cookie preferences",{"dataGaName":676,"dataGaLocation":489,"id":677,"isOneTrustButton":105},"cookie preferences","ot-sdk-btn",{"items":679},[680,682,684],{"text":664,"config":681},{"href":666,"dataGaName":667,"dataGaLocation":489},{"text":669,"config":683},{"href":671,"dataGaName":672,"dataGaLocation":489},{"text":674,"config":685},{"dataGaName":676,"dataGaLocation":489,"id":677,"isOneTrustButton":105},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[691],{"_path":692,"_dir":693,"_draft":6,"_partial":6,"_locale":7,"content":694,"config":698,"_id":700,"_type":28,"title":18,"_source":30,"_file":701,"_stem":702,"_extension":33},"/en-us/blog/authors/mayra-cabrera","authors",{"name":18,"config":695},{"headshot":696,"ctfId":697},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749663224/Blog/Author%20Headshots/mayra-cabrera-headshot.jpg","mayracabrera",{"template":699},"BlogAuthor","content:en-us:blog:authors:mayra-cabrera.yml","en-us/blog/authors/mayra-cabrera.yml","en-us/blog/authors/mayra-cabrera",{"_path":704,"_dir":36,"_draft":6,"_partial":6,"_locale":7,"header":705,"eyebrow":706,"blurb":707,"button":708,"secondaryButton":712,"_id":714,"_type":28,"title":715,"_source":30,"_file":716,"_stem":717,"_extension":33},"/shared/en-us/next-steps","Start shipping better software faster","50%+ of the Fortune 100 trust GitLab","See what your team can do with the intelligent\n\n\nDevSecOps platform.\n",{"text":44,"config":709},{"href":710,"dataGaName":47,"dataGaLocation":711},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":49,"config":713},{"href":51,"dataGaName":52,"dataGaLocation":711},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",1758326217290]