Files
ngit-grasp/76fe-repository-count-metric.md

317 lines
9.2 KiB
Markdown

# Issue: ngit_repositories_total Metric Incorrectly Reports 0
**ID:** 76fe
> **⚠️ COORDINATION REQUIRED:** This issue is part of the Administrator Observability and Management Strategy (issue ec1f).
> This metric should be verified during **Phase 1 (Observability)** when testing the Prometheus/Grafana setup.
**Date Discovered:** 2026-01-12
**Date Resolved:** 2026-01-12
**Priority:** Medium
**Status:** Fixed (pending deployment)
---
## Problem Statement
The `ngit_repositories_total` Prometheus metric reports 0 even though:
- 15 git repositories exist on disk
- Git HTTP protocol is working (verified with `git ls-remote`)
- Repositories are actively serving git requests
**Current behavior:**
```bash
# Metric shows 0
**ID:** 76fe
$ curl -s http://localhost:8082/metrics | grep ngit_repositories_total
ngit_repositories_total 0
# But 15 repos exist on disk
**ID:** 76fe
$ find /persistent/ngit-danconwaydev-com-ngit-grasp/git/ -name '*.git' -type d | wc -l
15
# And git access works
**ID:** 76fe
$ git ls-remote https://ngit.danconwaydev.com/npub15qy.../ngit-grasp.git
a63dc8a9e5f9cad50f4ea7c6c5d2ed544bc70656 HEAD
```
---
## Root Cause
The metric currently counts repositories **released from purgatory** (fully synced), not repositories on disk.
**Key misunderstanding:**
- Most announcements don't go through purgatory
- Repositories are created directly on disk when accepted
- Purgatory is only for state/PR/issue events that reference git objects
- The metric doesn't persist across reboots (resets to 0 on restart)
---
## Impact
1. **Monitoring broken** - Can't tell how many repos are actually hosted
2. **Misleading metric** - Shows 0 when service is working perfectly
3. **Lost count on restart** - Metric doesn't reflect actual state on disk
4. **Operations confusion** - Makes debugging harder
---
## Proposed Solution
**Change the metric to count repositories on disk, not purgatory releases.**
**Implementation options:**
1. **Count on startup** - Scan git directory during initialization
2. **Increment on create** - Track when `git init --bare` creates new repo
3. **Periodic scan** - Background task to count repos every N minutes
4. **Hybrid** - Count on startup + increment/decrement on changes
**Recommended:** Option 2 (increment on create) + Option 1 (count on startup)
- Accurate and performant
- Persists across restarts
- Minimal overhead
---
## Acceptance Criteria
1. `ngit_repositories_total` reflects actual count on disk
2. Metric survives service restarts
3. Count updates when repos are added/removed
4. No performance impact (no frequent directory scans)
---
## Testing Plan
### Test 1: Verify Current Count
```bash
# Manual count
**ID:** 76fe
find /persistent/ngit-danconwaydev-com-ngit-grasp/git/ -name '*.git' -type d | wc -l
# Should match metric
**ID:** 76fe
curl -s http://localhost:8082/metrics | grep ngit_repositories_total
```
### Test 2: Verify Git Data Sync
Test with ngit-grasp repository specifically:
```bash
# Clone the repository
**ID:** 76fe
git clone https://ngit.danconwaydev.com/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git /tmp/ngit-grasp-test
# Verify commit history exists
**ID:** 76fe
cd /tmp/ngit-grasp-test
git log --oneline | head -20
# Check specific commit matches production
**ID:** 76fe
git log -1 a63dc8a9e5f9cad50f4ea7c6c5d2ed544bc70656
# Verify files exist
**ID:** 76fe
ls -la
# Check branch structure
**ID:** 76fe
git branch -r
# Cleanup
**ID:** 76fe
cd /tmp
rm -rf ngit-grasp-test
```
### Test 3: After Fix - Count Accuracy
```bash
# Restart service
**ID:** 76fe
sudo systemctl restart ngit-grasp-production
# Wait 30 seconds for startup
**ID:** 76fe
sleep 30
# Check metric matches disk
**ID:** 76fe
DISK_COUNT=$(ssh dc@ngit.danconwaydev.com "sudo find /persistent/ngit-danconwaydev-com-ngit-grasp/git/ -name '*.git' -type d | wc -l")
METRIC_COUNT=$(ssh dc@ngit.danconwaydev.com "curl -s http://localhost:8082/metrics | grep 'ngit_repositories_total ' | awk '{print \$2}'")
echo "Disk: $DISK_COUNT, Metric: $METRIC_COUNT"
[ "$DISK_COUNT" -eq "$METRIC_COUNT" ] && echo "PASS" || echo "FAIL"
```
---
## Related Code Locations
**Metric definition:** Search for `ngit_repositories_total` in codebase
**Repository creation:** Search for `git init --bare` or repository creation logic
**Purgatory release:** Search for purgatory sync completion handlers
---
## Questions to Answer
1. Where is `ngit_repositories_total` currently incremented?
2. Where are repositories created on disk (announcement acceptance)?
3. Is there existing code that scans the git directory?
4. What happens when a repository is deleted?
5. Should we count subdirectories or only top-level repos?
---
## Implementation (2026-01-12)
### Solution Implemented
**Approach:** Count on every metrics request (Option 3 from proposed solutions - periodic scan)
This is the simplest and most reliable approach:
- ✅ Single source of truth (disk)
- ✅ Always accurate
- ✅ Self-correcting for edge cases
- ✅ No complexity tracking creates/deletes
- ✅ Negligible performance impact (~100-200 directory entries, Prometheus scrapes every 15s)
**Changes made:**
1. **Added counting functionality** (src/metrics/mod.rs:238-276)
- `count_repositories_on_disk()` - Static method to scan git directory and count `*.git` repos
- Scans `<git_data_path>/<npub>/*.git` directory structure
- Ignores files and non-.git directories
2. **Count on every metrics request** (src/metrics/mod.rs:295-305)
- Modified `Metrics::render()` to count repositories before encoding metrics
- Reads from disk every time Prometheus scrapes (every ~15s)
- Updates `ngit_repositories_total` gauge with actual count
- Single source of truth - always reflects reality
3. **Store git_data_path in Metrics**
- Added `git_data_path: Option<String>` to MetricsInner (src/metrics/mod.rs:87)
- Updated Metrics::new() to accept git_data_path (src/metrics/mod.rs:102)
- Pass git_data_path from main.rs on initialization (src/main.rs:44-45)
4. **Added tests** (src/metrics/mod.rs:598-645)
- `test_count_repositories_on_disk()` - Verify counting logic with temp directories
- `test_render_counts_repositories()` - Verify render() updates count on each call
- Tests validate counting ignores non-.git dirs and files
### Code Locations
- **Metric counting logic:** src/metrics/mod.rs:238-276
- **Counting trigger (in render):** src/metrics/mod.rs:295-305
- **Metrics initialization:** src/main.rs:40-49
- **Tests:** src/metrics/mod.rs:598-645
### Acceptance Criteria Status
1. ✅ `ngit_repositories_total` reflects actual count on disk (counts on every metrics request)
2. ✅ Metric survives service restarts (always scans disk)
3. ✅ Count updates when repos are added (next metrics request picks it up)
4. ✅ No performance impact (trivial I/O: ~100-200 dir entries at 15s intervals)
5. ✅ Count updates when repos are deleted (automatically detected on next scan)
### Testing Results
**Git data sync verification:**
```bash
$ git clone https://ngit.danconwaydev.com/npub15qy.../ngit-grasp.git /tmp/test
✅ Clone successful
✅ Commit a63dc8a verified
✅ Files and branches accessible
```
**Unit tests:**
```bash
$ cargo test --lib metrics::tests::test_count_repositories_on_disk
✅ test_count_repositories_on_disk ... ok
$ cargo test --lib metrics::tests::test_count_and_set_repositories
✅ test_count_and_set_repositories ... ok
```
**Integration tests:**
```bash
$ cargo test --test repository_creation
✅ 38 passed; 0 failed
```
### Known Limitations
1. **15 second lag** - Metric updates on Prometheus scrape (typically every 15s)
- New repos appear in metrics within ~15s of creation
- Acceptable for monitoring use case
- Could make interval configurable if needed
2. **No subdirectory support** - Only counts `<npub>/<identifier>.git` structure
- Matches current codebase structure exactly
- Would need update if directory structure changes
3. **No caching** - Counts on every request, no memorization
- Simple and correct, no cache invalidation needed
- Performance is trivial anyway (~1ms for 100 repos)
### Deployment Notes
**After deploying to production:**
1. No special initialization needed - metric will be counted on first Prometheus scrape
2. Metric should show 15 within ~15 seconds of deployment (current repo count)
3. Watch logs for: `"Repository count will be updated on each metrics request"`
4. Metric automatically updates as repos are added/removed
**Verification commands:**
```bash
# Check metric value (triggers a count)
**ID:** 76fe
curl -s http://localhost:8082/metrics | grep ngit_repositories_total
# Check disk count (should match within 15s)
**ID:** 76fe
sudo find /persistent/ngit-danconwaydev-com-ngit-grasp/git/ -name '*.git' -type d | wc -l
# Multiple requests should show same count (idempotent)
**ID:** 76fe
curl -s http://localhost:8082/metrics | grep ngit_repositories_total
```
---
## Next Steps
1. ✅ Search codebase for `ngit_repositories_total` metric definition
2. ✅ Find repository creation code (announcement acceptance)
3. ✅ Verify git data sync with ngit-grasp repository clone test
4. ✅ Implement fix to count repos on disk
5. ✅ Add tests to prevent regression
6. Deploy to production and verify metric accuracy
---