Skip to content

fix: use LEFT JOIN in projectBasicSQL to include projects without quota records#23520

Open
cdarninsuang-bamfunds wants to merge 1 commit into
goharbor:mainfrom
cdarninsuang-bamfunds:23519-exporter-project-basic-sql-quota-left-join
Open

fix: use LEFT JOIN in projectBasicSQL to include projects without quota records#23520
cdarninsuang-bamfunds wants to merge 1 commit into
goharbor:mainfrom
cdarninsuang-bamfunds:23519-exporter-project-basic-sql-quota-left-join

Conversation

@cdarninsuang-bamfunds

Copy link
Copy Markdown

Fixes #23519

What does this PR fix?

When quota_per_project_enable is false, Harbor does not create rows in the quota or quota_usage tables for new projects. The exporter's projectBasicSQL used INNER JOIN on both tables, so any project missing those rows was silently excluded from the in-memory project map built on each scrape.

The three subsequent collector functions (updateProjectMemberInfo, updateProjectRepoInfo, updateProjectArtifactInfo) query those same projects via independent JOINs on the project table directly, and call log.Errorf for each project ID not found in the map. With a large number of projects missing quota records this produces hundreds of error log writes per scrape, increasing scrape latency and risking Prometheus timeouts that drop all Harbor metrics.

Changes

  • src/pkg/exporter/project_collector.go: Convert projectBasicSQL from INNER JOIN to LEFT JOIN on quota, quota_usage, and project_metadata. Join-condition filtering (e.g. quota.reference='project', project_metadata.name='public') is moved into the ON clause as required for correct LEFT JOIN semantics. COALESCE defaults ensure projects without quota records emit {} (storage = 0) rather than being dropped. The quota join now casts project_id to text rather than casting reference_id to integer, which is safer if the quota table ever contains non-integer reference IDs.

  • src/pkg/exporter/project_collector_test.go: Add testPro3, a project created without quota records, and assert it appears in the collector map with empty quota/usage values — covering the previously broken path.

Testing

Existing unit tests continue to pass for projects with quota records. The new testPro3 case verifies the fix.

Tested against a live Harbor instance where quota_per_project_enable=false and 529 of 611 projects had no quota rows — after the fix, all 611 projects appear in the map and the project not found error flood is eliminated.

Notes

  • No database schema changes.
  • No behavioural change for projects that have quota records.

Signed-off-by: cdarninsuang cdarninsuang@bamfunds.com

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a bug in Harbor's Prometheus exporter (ProjectCollector) where projects were silently dropped from the scraped metrics map when quota_per_project_enable=false. In that mode Harbor creates no quota/quota_usage rows, and the previous INNER JOIN-based projectBasicSQL excluded those projects, which then triggered a flood of project not found error logs from the member/repo/artifact collectors on every scrape (risking Prometheus timeouts). The fix converts the joins to LEFT JOIN, moves join-condition filters into the ON clauses (required for correct outer-join semantics), and adds COALESCE defaults so quota-less projects report empty quota/usage (storage 0) instead of being dropped.

Changes:

  • Rewrote projectBasicSQL to use LEFT JOIN on project_metadata, quota, and quota_usage, with COALESCE defaults ('false', '{}', '{}') and the filter predicates moved into the ON clauses.
  • Changed the quota join to compare project.project_id::text = reference_id instead of casting reference_id to integer, which is safer given reference_id is varchar.
  • Added test project testPro3 (created without quota records) and assertions that it appears in the collector map with empty quota/usage.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/pkg/exporter/project_collector.go Converts projectBasicSQL from INNER to LEFT JOINs with COALESCE defaults so projects lacking quota records are still collected.
src/pkg/exporter/project_collector_test.go Adds testPro3 (no quota rows) and asserts it is present in the map with {} quota/usage, plus updates teardown cleanup to include the new project.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…ta records

When quota_per_project_enable is false, Harbor does not create quota or
quota_usage rows for new projects. The previous INNER JOIN on those tables
silently excluded every project without a quota record from the collector
map, causing the member/repo/artifact update functions to log a
"project not found" error for each missing project on every scrape.

With ~500+ affected projects this produced hundreds of error log writes
per scrape, increasing scrape latency and risking Prometheus timeouts.
Affected projects also had no metrics emitted for quota, member, repo, or
artifact dimensions.

Switch to LEFT JOIN with join-condition filtering (moving name='public'
and reference='project' predicates into the ON clause) and COALESCE
defaults so every non-deleted project is always present in the map.
Projects without quota records emit 0 for storage quota/usage.

Also cast project_id to text for the quota join rather than casting
reference_id to integer, which is safer when the quota table contains
non-integer reference IDs from other reference types.

Add a test case that creates a project without quota records and asserts
it appears in the collector map with empty quota/usage values.

Signed-off-by: cdarninsuang <cdarninsuang+github@bamfunds.com>
@cdarninsuang-bamfunds
cdarninsuang-bamfunds force-pushed the 23519-exporter-project-basic-sql-quota-left-join branch from e84126a to 0a77d00 Compare July 22, 2026 11:15
Copilot AI review requested due to automatic review settings July 22, 2026 11:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

exporter: projectBasicSQL INNER JOIN on quota tables drops projects when quota_per_project_enable=false

5 participants