72 lines
2.2 KiB
Go
72 lines
2.2 KiB
Go
//go:build commercial
|
|
|
|
// Package edition - Backup mode detection for Commercial Edition.
|
|
// This file is built ONLY when the "commercial" build tag is specified.
|
|
//
|
|
// Backup POPs serve read-only traffic when primary is down.
|
|
// Community Edition does not have backup functionality.
|
|
package edition
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"os"
|
|
)
|
|
|
|
// BackupModeContextKey is used to store backup mode in request context.
|
|
type BackupModeContextKey struct{}
|
|
|
|
// isBackupMode returns true if this POP is currently operating as a backup.
|
|
// Assigned to edition.IsBackupMode in commercial builds.
|
|
func isBackupMode() bool {
|
|
// Check environment variable first
|
|
if os.Getenv("CLAVITOR_BACKUP_MODE") == "true" {
|
|
return true
|
|
}
|
|
// TODO: Check with control plane if this POP has been promoted to active
|
|
return false
|
|
}
|
|
|
|
// BackupModeMiddleware detects if this is a backup POP and marks context.
|
|
// Rejects write operations with 503 and X-Primary-Location header.
|
|
func BackupModeMiddleware(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if !isBackupMode() {
|
|
next.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
|
|
// This is a backup POP - mark context
|
|
ctx := context.WithValue(r.Context(), BackupModeContextKey{}, true)
|
|
r = r.WithContext(ctx)
|
|
|
|
// Check if this is a write operation
|
|
if isWriteMethod(r.Method) {
|
|
// Tell client where the primary is
|
|
primaryURL := ""
|
|
if globalConfig != nil && globalConfig.ReplicationConfig != nil {
|
|
// TODO: Need to add primary_pop URL to config for backup role
|
|
primaryURL = globalConfig.TelemetryHost // Fallback - should be primary POP URL
|
|
}
|
|
if primaryURL != "" {
|
|
w.Header().Set("X-Primary-Location", primaryURL)
|
|
}
|
|
http.Error(w, "Write operations not available on backup POP", http.StatusServiceUnavailable)
|
|
return
|
|
}
|
|
|
|
// Read operations allowed
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
func isWriteMethod(method string) bool {
|
|
return method == "POST" || method == "PUT" || method == "DELETE" || method == "PATCH"
|
|
}
|
|
|
|
// isBackupRequest returns true if the request context indicates backup mode.
|
|
func isBackupRequest(ctx context.Context) bool {
|
|
v, _ := ctx.Value(BackupModeContextKey{}).(bool)
|
|
return v
|
|
}
|