Skip to content

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

Description

@cdarninsuang-bamfunds

Description

When quota_per_project_enable is set to false in Harbor's configuration, no rows are created in the quota or quota_usage tables for new projects. The Prometheus exporter's projectBasicSQL uses INNER JOIN on both tables, silently excluding every project without quota records from the in-memory project map built on each scrape.

The three subsequent collector functions (updateProjectMemberInfo, updateProjectRepoInfo, updateProjectArtifactInfo) query those same projects independently and call log.Errorf("project not found, ID %d") for each project ID absent from the map. With a large number of affected projects this produces hundreds of error log writes per scrape, increasing scrape latency and risking Prometheus scrape timeouts that drop all Harbor metrics. Affected projects also have no metrics emitted for quota, member, repo, or artifact dimensions.

Steps to Reproduce

  1. Set quota_per_project_enable=false in Harbor configuration.
  2. Create one or more projects — no rows are inserted into quota or quota_usage.
  3. Observe the exporter logs: a project not found error is logged for each affected project on every Prometheus scrape.
  4. With enough affected projects, Prometheus scrapes begin to time out intermittently.

Expected Behavior

All non-deleted projects appear in the collector map regardless of whether quota records exist. Projects without quota records should emit 0 for storage quota/usage metrics.

Actual Behavior

Projects without quota records are silently dropped from the map. The exporter floods logs with project not found errors on every scrape and Prometheus metrics are incomplete or missing.

Environment

  • Harbor version: v2.13.1
  • quota_per_project_enable: false

Fix

PR #23520 switches projectBasicSQL from INNER JOIN to LEFT JOIN on quota, quota_usage, and project_metadata, with COALESCE defaults so projects without quota records emit {} (storage = 0) rather than being dropped.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions