nip/docs/remote-cache.md

591 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Remote Binary Cache Guide
## Overview
The NIP remote binary cache enables sharing of compiled artifacts across machines and teams. This dramatically speeds up builds in CI/CD pipelines and development environments by allowing teams to share build results.
## Features
- **Automatic Upload/Download**: Artifacts are automatically uploaded after successful builds and downloaded before builds
- **Team Sharing**: Share builds across team members and CI/CD runners
- **HTTP API**: Simple REST API for cache operations
- **Authentication**: API key-based authentication for secure access
- **Fallback**: Gracefully falls back to local cache if remote is unavailable
- **Bandwidth Efficient**: Only transfers artifacts when needed
## Quick Start
### 1. Configure Remote Cache
```bash
# Set remote cache URL
nip cache remote config --url https://cache.example.com
# Set API key for authentication
nip cache remote config --api-key your-api-key-here
# Enable remote cache
nip cache remote config --enable
```
### 2. Check Status
```bash
nip cache remote status
```
Output:
```
Remote Cache Status
===================
Enabled: Yes
URL: https://cache.example.com
API Key: ***configured***
Timeout: 300 seconds
Testing connection...
✅ Remote cache is available
```
### 3. Build with Remote Cache
Remote cache is now automatically used during builds:
```bash
# First build - compiles and uploads to remote cache
nip build vim +python+ruby
# On another machine - downloads from remote cache
nip build vim +python+ruby
```
## Configuration
### Configuration File
Remote cache settings are stored in `~/.config/nip/remote-cache.json`:
```json
{
"url": "https://cache.example.com",
"apiKey": "your-api-key-here",
"timeout": 300,
"enabled": true
}
```
### Configuration Options
| Option | Description | Default |
|--------|-------------|---------|
| `url` | Remote cache server URL | "" |
| `apiKey` | Authentication API key | "" |
| `timeout` | Request timeout in seconds | 300 |
| `enabled` | Enable/disable remote cache | false |
### Environment Variables
You can also configure via environment variables:
```bash
export NIP_REMOTE_CACHE_URL="https://cache.example.com"
export NIP_REMOTE_CACHE_API_KEY="your-api-key-here"
export NIP_REMOTE_CACHE_ENABLED="true"
```
## Usage
### Automatic Mode (Recommended)
Remote cache works automatically during builds:
```bash
# Build package - automatically checks remote cache first
nip build firefox +wayland
# If not in remote cache:
# 1. Checks local cache
# 2. Builds from source
# 3. Uploads to remote cache
# 4. Uploads to local cache
# If in remote cache:
# 1. Downloads to local cache
# 2. Uses cached artifact (instant!)
```
### Manual Operations
#### Pull from Remote Cache
```bash
# Pull specific package from remote cache
nip cache remote pull vim 9.0
```
#### Push to Remote Cache
```bash
# Push specific package to remote cache
nip cache remote push vim 9.0
```
#### Check Remote Status
```bash
# Test remote cache connectivity
nip cache remote status
```
## Cache Lookup Flow
```
┌─────────────────────────────────────────────────────────────┐
│ Build Request │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Check Local Cache │
│ ~/.cache/nip/binary-cache/ │
└─────────────────────────────────────────────────────────────┘
┌───────┴───────┐
│ │
Found Not Found
│ │
│ ▼
│ ┌─────────────────────────────────────┐
│ │ Check Remote Cache │
│ │ (if enabled) │
│ └─────────────────────────────────────┘
│ │
│ ┌───────┴───────┐
│ │ │
│ Found Not Found
│ │ │
│ ▼ ▼
│ ┌─────────┐ ┌─────────────┐
│ │Download │ │Build from │
│ │to Local │ │Source │
│ └─────────┘ └─────────────┘
│ │ │
│ │ ▼
│ │ ┌─────────────┐
│ │ │Upload to │
│ │ │Remote Cache │
│ │ └─────────────┘
│ │ │
│ └───────┬───────┘
│ │
└───────┬───────┘
┌─────────────────────────────────────────────────────────────┐
│ Use Cached Artifact │
└─────────────────────────────────────────────────────────────┘
```
## Remote Cache Server
### API Endpoints
The remote cache server implements a simple HTTP API:
#### Health Check
```
GET /v1/health
Response: 200 OK
```
#### Lookup Artifact
```
GET /v1/artifacts/{cache-key}
Response: 200 OK with download URL, or 404 Not Found
```
#### Get Metadata
```
GET /v1/artifacts/{cache-key}/metadata
Response: 200 OK with JSON metadata
```
#### Upload Artifact
```
POST /v1/artifacts/{cache-key}
Content-Type: multipart/form-data
- file: artifact file
- metadata: JSON metadata
Response: 201 Created
```
### Authentication
All requests (except health check) require authentication:
```
Authorization: Bearer <api-key>
```
### Example Server Implementation
A simple reference server implementation:
```python
from flask import Flask, request, jsonify, send_file
import os
import json
app = Flask(__name__)
CACHE_DIR = "/var/cache/nip"
API_KEY = os.environ.get("NIP_CACHE_API_KEY")
def check_auth():
auth = request.headers.get("Authorization")
if not auth or not auth.startswith("Bearer "):
return False
token = auth.split(" ")[1]
return token == API_KEY
@app.route("/v1/health")
def health():
return jsonify({"status": "ok"})
@app.route("/v1/artifacts/<cache_key>")
def get_artifact(cache_key):
if not check_auth():
return jsonify({"error": "unauthorized"}), 401
artifact_path = os.path.join(CACHE_DIR, cache_key, "artifact.tar.gz")
if os.path.exists(artifact_path):
return jsonify({"downloadUrl": f"/v1/download/{cache_key}"})
return jsonify({"error": "not found"}), 404
@app.route("/v1/download/<cache_key>")
def download_artifact(cache_key):
if not check_auth():
return jsonify({"error": "unauthorized"}), 401
artifact_path = os.path.join(CACHE_DIR, cache_key, "artifact.tar.gz")
return send_file(artifact_path)
@app.route("/v1/artifacts/<cache_key>/metadata")
def get_metadata(cache_key):
if not check_auth():
return jsonify({"error": "unauthorized"}), 401
metadata_path = os.path.join(CACHE_DIR, cache_key, "metadata.json")
if os.path.exists(metadata_path):
with open(metadata_path) as f:
return jsonify(json.load(f))
return jsonify({"error": "not found"}), 404
@app.route("/v1/artifacts/<cache_key>", methods=["POST"])
def upload_artifact(cache_key):
if not check_auth():
return jsonify({"error": "unauthorized"}), 401
cache_dir = os.path.join(CACHE_DIR, cache_key)
os.makedirs(cache_dir, exist_ok=True)
# Save artifact
file = request.files["file"]
artifact_path = os.path.join(cache_dir, "artifact.tar.gz")
file.save(artifact_path)
# Save metadata
metadata = json.loads(request.form["metadata"])
metadata_path = os.path.join(cache_dir, "metadata.json")
with open(metadata_path, "w") as f:
json.dump(metadata, f)
return jsonify({"status": "created"}), 201
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)
```
## CI/CD Integration
### GitHub Actions
```yaml
name: Build with NIP Cache
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install NIP
run: |
curl -sSL https://nip.example.com/install.sh | sh
- name: Configure Remote Cache
env:
NIP_CACHE_API_KEY: ${{ secrets.NIP_CACHE_API_KEY }}
run: |
nip cache remote config --url https://cache.example.com
nip cache remote config --api-key $NIP_CACHE_API_KEY
nip cache remote config --enable
- name: Build Package
run: nip build myapp +production
```
### GitLab CI
```yaml
build:
image: ubuntu:latest
before_script:
- curl -sSL https://nip.example.com/install.sh | sh
- nip cache remote config --url https://cache.example.com
- nip cache remote config --api-key $NIP_CACHE_API_KEY
- nip cache remote config --enable
script:
- nip build myapp +production
variables:
NIP_CACHE_API_KEY: $CI_CACHE_API_KEY
```
## Performance Impact
### Build Time Comparison
| Scenario | Without Remote Cache | With Remote Cache | Speedup |
|----------|---------------------|-------------------|---------|
| First build (team) | 15 minutes | 15 minutes | 1x |
| Second build (same machine) | <1 second (local) | <1 second (local) | - |
| Second build (different machine) | 15 minutes | <1 second | 900x |
| CI/CD pipeline | 15 minutes | <1 second | 900x |
### Real-World Example
**Development Team (5 developers)**
Without remote cache:
- Each developer builds from scratch: 5 × 15 min = 75 minutes total
- CI builds from scratch: +15 minutes
- **Total: 90 minutes of build time**
With remote cache:
- First developer builds: 15 minutes
- Other developers use cache: 4 × <1 second 0 minutes
- CI uses cache: <1 second
- **Total: 15 minutes of build time**
**Savings: 75 minutes (83% reduction)**
## Security Considerations
### API Key Management
- **Never commit API keys to version control**
- Use environment variables or secret management systems
- Rotate keys regularly
- Use different keys for different teams/projects
### Network Security
- **Always use HTTPS** for remote cache URLs
- Consider VPN or private network for sensitive builds
- Implement rate limiting on server
- Monitor for unusual access patterns
### Access Control
- Implement read/write permissions
- Separate keys for CI vs developers
- Audit log all cache operations
- Implement cache expiration policies
## Troubleshooting
### Remote Cache Not Available
```bash
❌ Remote cache is not available
```
**Solutions:**
1. Check network connectivity: `ping cache.example.com`
2. Verify URL is correct: `nip cache remote status`
3. Check server is running: `curl https://cache.example.com/v1/health`
4. Verify firewall rules allow outbound HTTPS
### Authentication Failed
```bash
❌ Remote cache lookup failed: 401 Unauthorized
```
**Solutions:**
1. Verify API key is correct
2. Check API key hasn't expired
3. Ensure Authorization header is being sent
4. Contact cache server administrator
### Download Failed
```bash
❌ Download failed: timeout
```
**Solutions:**
1. Increase timeout: Edit `~/.config/nip/remote-cache.json`
2. Check network bandwidth
3. Try again later (temporary network issue)
4. Fall back to local build: `nip build --no-remote-cache`
### Upload Failed
```bash
⚠️ Remote upload failed (local cache still available)
```
**Solutions:**
1. Check disk space on server
2. Verify write permissions
3. Check artifact size limits
4. Build still succeeded - artifact is in local cache
## Best Practices
### For Developers
1. **Enable remote cache** for all team members
2. **Use consistent build configurations** to maximize cache hits
3. **Don't disable cache** unless debugging build issues
4. **Report cache issues** to team lead
### For Teams
1. **Set up dedicated cache server** for team
2. **Use separate API keys** per project
3. **Monitor cache hit rates** to optimize configurations
4. **Implement cache retention policies** (e.g., 30 days)
5. **Document cache server URL** in team wiki
### For CI/CD
1. **Always enable remote cache** in CI pipelines
2. **Use read-only keys** for pull requests from forks
3. **Use read-write keys** for main branch builds
4. **Monitor cache storage** and implement cleanup
5. **Set appropriate timeouts** for CI environment
## Advanced Configuration
### Multiple Cache Servers
Configure fallback cache servers:
```json
{
"servers": [
{
"url": "https://cache-primary.example.com",
"priority": 1
},
{
"url": "https://cache-backup.example.com",
"priority": 2
}
]
}
```
### Cache Policies
Configure cache behavior:
```json
{
"policies": {
"uploadOnBuild": true,
"downloadBeforeBuild": true,
"fallbackToLocal": true,
"retryAttempts": 3,
"retryDelay": 5
}
}
```
## Monitoring
### Cache Metrics
Track these metrics for optimal performance:
- **Hit Rate**: Percentage of builds using cache
- **Upload Success Rate**: Percentage of successful uploads
- **Download Success Rate**: Percentage of successful downloads
- **Average Download Time**: Time to download artifacts
- **Cache Size**: Total storage used
- **Cache Age**: Average age of cached artifacts
### Example Monitoring Dashboard
```bash
# Get cache statistics
nip cache stats
# Get remote cache status
nip cache remote status
# List recent cache operations
nip cache list --recent
```
## Migration Guide
### From Local-Only to Remote Cache
1. **Set up remote cache server**
2. **Configure all team members**:
```bash
nip cache remote config --url https://cache.example.com
nip cache remote config --api-key <team-key>
nip cache remote config --enable
```
3. **Push existing local cache** (optional):
```bash
for pkg in $(nip cache list --format=simple); do
nip cache remote push $pkg
done
```
4. **Update CI/CD pipelines** with remote cache config
5. **Monitor adoption** and cache hit rates
## FAQ
**Q: Does remote cache slow down builds?**
A: No, remote cache checks are fast (<1 second). If remote is slow or unavailable, it falls back to local cache or building from source.
**Q: How much bandwidth does remote cache use?**
A: Only when downloading artifacts. A typical package is 10-100MB. With good cache hit rates, bandwidth usage is minimal.
**Q: Can I use remote cache without local cache?**
A: No, local cache is always used. Remote cache supplements local cache for team sharing.
**Q: What happens if remote cache is down?**
A: Builds continue normally using local cache or building from source. Remote cache is optional and non-blocking.
**Q: How do I clear remote cache?**
A: Contact your cache server administrator. Remote cache clearing is typically done server-side with retention policies.
**Q: Can I host my own cache server?**
A: Yes! See the "Example Server Implementation" section for a reference implementation.
## See Also
- [Binary Cache Guide](binary-cache.md) - Local cache documentation
- [Build System Guide](source-build-guide.md) - Building from source
- [Configuration Guide](configuration.md) - NIP configuration options