# Supabase SDK Integration with Health Tracking
This module provides a health-aware wrapper around the `supabase_rs` SDK, adding circuit breaker patterns, automatic failure tracking, and retry logic.
## Features
- ✅ **Health Tracking**: Monitors connection health and automatically circuits break on repeated failures
- ✅ **Circuit Breaker**: Temporarily blocks requests to failing hosts to prevent cascade failures
- ✅ **Automatic Recovery**: Hosts automatically recover after the offline period expires
- ✅ **Detailed Logging**: Structured logging with `tracing` for all operations
- ✅ **Drop-in Replacement**: Compatible API with the underlying `supabase_rs` SDK
- ✅ **Shared State**: Uses the same global health tracker as Scylla driver for unified monitoring
## Architecture
```
┌─────────────────────────────────────────┐
│ HealthAwareSupabaseClient │
│ ┌───────────────────────────────────┐ │
│ │ SupabaseClient (supabase_rs) │ │
│ └───────────────────────────────────┘ │
│ ▲ │
│ │ wraps │
│ ┌───────────┴───────────────────────┐ │
│ │ HostHealthTracker (global) │ │
│ │ - Failure counting │ │
│ │ - Offline window management │ │
│ │ - Circuit breaker logic │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
```
## Usage
### Basic Example
```rust
use athena_rs::drivers::supabase::client::{
HealthAwareSupabaseClient,
SupabaseConnectionInfo
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create connection info
let info = SupabaseConnectionInfo::new(
"https://xxx.supabase.co".to_string(),
"your-anon-key".to_string(),
);
// Create health-aware client
let client = HealthAwareSupabaseClient::new(info)?;
// Execute queries with automatic health tracking
let users = client
.select("users")
.columns(vec!["id", "email"])
.limit(10)
.execute()
.await?;
println!("Got {} users", users.len());
Ok(())
}
```
### From Environment Variables
```rust
use athena_rs::drivers::supabase::client::HealthAwareSupabaseClient;
// Load from SUPABASE_URL and SUPABASE_KEY env vars
let athena = HealthAwareSupabaseClient::from_env(
"SUPABASE_URL",
"SUPABASE_KEY"
)?;
let results = athena
.select("products")
.eq("status", "active")
.order("created_at", false)
.execute()
.await?;
```
### Query Building
The client supports the full query builder API:
```rust
let results = client
.select("orders")
.columns(vec!["id", "user_id", "total", "status"])
.eq("status", "completed") // Equality filter
.gt("total", "100") // Greater than
.lt("created_at", "2024-01-01") // Less than
.order("created_at", false) // Order descending
.limit(50) // Limit results
.offset(100) // Pagination
.execute()
.await?;
```
### Insert, Update, Delete
```rust
use serde_json::json;
// Insert
let new_user = json!({
"email": "user@example.com",
"name": "John Doe"
});
let user_id = client.insert("users", new_user).await?;
// Update
let updates = json!({
"name": "Jane Doe",
"updated_at": "2024-02-08T12:00:00Z"
});
client.update("users", &user_id, updates).await?;
// Delete
client.delete("users", &user_id).await?;
```
### Health Monitoring
```rust
// Check if host is currently offline
if let Some(offline_until) = client.is_offline() {
println!("Host is offline until {:?}", offline_until);
} else {
println!("Host is healthy");
}
// Force offline for testing (e.g., maintenance window)
use std::time::Duration;
client.force_offline(Duration::from_secs(300)); // 5 minutes
// Reset health tracking (clear all failures)
client.reset_health();
```
## Configuration
### Health Tracking Defaults
The global health tracker uses these defaults (shared with Scylla driver):
```rust
const FAILURE_THRESHOLD: usize = 5; // Failures before circuit break
const FAILURE_WINDOW: Duration = 60s; // Window to count failures
const OFFLINE_DURATION: Duration = 300s; // How long to stay offline
```
These can be customized by creating your own `HostHealthTracker` instance.
### Environment Variables
```bash
# Standard Supabase connection
export SUPABASE_URL="https://xxx.supabase.co"
export SUPABASE_KEY="your-anon-or-service-role-key"
# Multiple projects
export SUPABASE_XBP_URL="https://yyy.supabase.co"
export SUPABASE_XBP_KEY="another-key"
# Logging level
export RUST_LOG="info,athena_rs::drivers::supabase=debug"
```
## Error Handling
The client can return two main error types:
### 1. HostOffline Error
When the circuit breaker trips:
```rust
match client.select("users").execute().await {
Err(e) if e.is::<HostOffline>() => {
let offline = e.downcast::<HostOffline>()?;
println!("Host {} is offline until {:?}",
offline.host(), offline.until());
}
Err(e) => println!("Other error: {}", e),
Ok(data) => println!("Success!"),
}
```
### 2. Query/Connection Errors
Standard errors from the underlying SDK:
```rust
match client.select("users").execute().await {
Ok(data) => process_data(data),
Err(e) => {
// Could be network error, auth error, query syntax, etc.
log::error!("Query failed: {}", e);
}
}
```
## Integration with Existing Code
### Migrating from Direct `supabase_rs` Usage
**Before:**
```rust
use supabase_rs::SupabaseClient;
let client = SupabaseClient::new(url, key)?;
let data = client.select("users")
.columns(vec!["id", "email"])
.execute()
.await?;
```
**After:**
```rust
use athena_rs::drivers::supabase::client::{
HealthAwareSupabaseClient,
SupabaseConnectionInfo
};
let info = SupabaseConnectionInfo::new(url, key);
let client = HealthAwareSupabaseClient::new(info)?;
let data = client.select("users")
.columns(vec!["id", "email"])
.execute()
.await?;
```
The API is nearly identical, with added health tracking benefits!
## Testing
Run the example:
```bash
cargo run --example supabase_health_tracking
```
Run tests:
```bash
cargo test --package athena_rs --lib drivers::supabase::client
```
## Advanced: Custom Health Tracker
If you need different health tracking parameters:
```rust
use athena_rs::drivers::scylla::health::{HostHealthTracker, SystemClock};
use std::time::Duration;
// Create custom tracker with stricter thresholds
let tracker = HostHealthTracker::new(
SystemClock::default(),
3, // Fail after 3 errors
Duration::from_secs(30), // 30 second window
Duration::from_secs(60), // 1 minute offline
);
// Note: You'd need to modify the client to accept a custom tracker
// or use it directly for manual health checks
```
## Logging
The module uses `tracing` for structured logging:
```rust
// Enable debug logging
std::env::set_var("RUST_LOG", "debug");
tracing_subscriber::fmt::init();
// Logs include:
// - host: The Supabase host being accessed
// - table: Table name for queries
// - rows: Result row counts
// - error: Error details on failures
```
Example log output:
```
INFO athena_rs::drivers::supabase::client: Supabase query succeeded
host: xxx.supabase.co
table: users
rows: 42
ERROR athena_rs::drivers::supabase::client: Supabase query failed
host: xxx.supabase.co
table: orders
error: Connection timeout
WARN athena_rs::drivers::supabase::client: Forcing Supabase host offline
host: xxx.supabase.co
duration_secs: 300
```
## Comparison with Legacy Functions
The module still exports legacy helper functions for backward compatibility:
```rust
// Legacy (still works)
use athena_rs::drivers::supabase::{supabase, supabase_xbp};
let client = supabase().await?;
// New (recommended)
use athena_rs::drivers::supabase::client::HealthAwareSupabaseClient;
let client = HealthAwareSupabaseClient::from_env("SUPABASE_URL", "SUPABASE_KEY")?;
```
The new client is recommended for all new code due to:
- Health tracking and circuit breaking
- Better error handling
- Detailed logging
- Consistent API with other drivers
## See Also
- [Supabase RS SDK Documentation](https://docs.rs/supabase_rs)
- [Scylla Health Tracking](../scylla/health.rs)
- [Circuit Breaker Pattern](https://martinfowler.com/bliki/CircuitBreaker.html)