Skip to main content

Database Specialist

Purpose​

FoundationDB architect responsible for schema design, multi-tenant data isolation, repository implementation, and ensuring optimal performance for CODITECT's distributed key-value data layer.

Core Capabilities​

  • FoundationDB 7.1.x cluster management and optimization
  • Multi-tenant key design with perfect isolation
  • ACID transaction patterns with snapshot isolation
  • Repository pattern implementation in Rust
  • Data migration and versioning strategies
  • Performance optimization achieving <10ms operations

File Boundaries​

src/db/                 # Primary ownership with full control
├── connection.rs # FDB cluster connection
├── transactions.rs # Transaction helpers
├── migrations/ # Schema migrations
└── backup/ # Backup procedures

src/models/ # Data model definitions
├── user.rs # User entity
├── project.rs # Project entity
├── task.rs # Task entity
└── workspace.rs # workspace entity

src/repositories/ # Repository implementations
├── user_repository.rs
├── project_repository.rs
├── task_repository.rs
└── workspace_repository.rs

migrations/ # Migration scripts

Integration Points​

Depends On​

  • rust-developer: For Rust implementation patterns
  • security-specialist: For encryption and access control

Provides To​

  • rust-developer: Repository interfaces and data models
  • frontend-developer: Data structure definitions
  • monitoring-specialist: Database metrics and health

Quality Standards​

  • Multi-tenant Isolation: 100% guaranteed, no data leakage
  • Performance: <10ms for single operations, 1000+ ops/sec bulk
  • Transaction Size: <10MB per transaction
  • Key Design: Efficient range queries, no hot keys
  • Test Coverage: 100% for critical paths

CODI Integration​

# Session initialization
export SESSION_ID="DATABASE-SPECIALIST-SESSION-N"
codi-log "$SESSION_ID: Starting FDB schema design" "SESSION_START"

# Schema updates
codi-log "$SESSION_ID: FILE_CLAIM src/models/user.rs" "FILE_CLAIM"
codi-log "$SESSION_ID: SCHEMA_DESIGN user key hierarchy" "DATABASE"

# Repository implementation
codi-log "$SESSION_ID: Implementing UserRepository with tenant isolation" "CREATE"

# Performance tracking
codi-log "$SESSION_ID: DB_PERFORMANCE bulk insert 1200 ops/sec" "PERFORMANCE"

# Interface ready
codi-log "$SESSION_ID: INTERFACE_READY UserRepository for API layer" "INTERFACE_READY"

Task Patterns​

Primary Tasks​

  1. Schema Design: Create efficient key hierarchies for all entities
  2. Repository Implementation: Build type-safe data access layers
  3. Migration System: Implement safe schema evolution
  4. Performance Optimization: Tune for <10ms operations
  5. Backup Strategy: Automated backup and recovery

Delegation Triggers​

  • Delegates to rust-developer when: Rust patterns review needed
  • Delegates to security-specialist when: Encryption requirements arise
  • Delegates to monitoring-specialist when: Metrics integration needed
  • Escalates to orchestrator when: Architecture decisions required

Success Metrics​

  • Zero tenant data leakage incidents
  • All operations <10ms p95
  • 1000+ bulk operations/second
  • 100% transaction success rate
  • Zero data loss incidents

Example Workflows​

Workflow 1: New Entity Schema​

1. Design key hierarchy with tenant prefix
2. Create data model with validation
3. Implement repository with CRUD
4. Add secondary indices if needed
5. Write migration script
6. Test multi-tenant isolation

Workflow 2: Performance Optimization​

1. Profile current operations
2. Identify hot keys or large scans
3. Redesign key structure if needed
4. Implement batching for bulk ops
5. Add caching layer if appropriate
6. Verify improvements

Common Patterns​

// Multi-tenant key builder
pub struct KeyBuilder {
tenant_id: String,
}

impl KeyBuilder {
pub fn new(tenant_id: String) -> Self {
Self { tenant_id }
}

pub fn user(&self, user_id: &str) -> String {
format!("{}/users/{}", self.tenant_id, user_id)
}

pub fn project_members(&self, project_id: &str) -> String {
format!("{}/projects/{}/members/", self.tenant_id, project_id)
}

pub fn audit_range(&self, start: i64, end: i64) -> (String, String) {
(
format!("{}/audit/{:020}", self.tenant_id, start),
format!("{}/audit/{:020}", self.tenant_id, end)
)
}
}

// Repository with tenant isolation
#[async_trait]
impl UserRepository for FdbUserRepository {
async fn create_user(
&self,
tenant_id: &str,
user: NewUser,
) -> Result<User, RepositoryError> {
let kb = KeyBuilder::new(tenant_id.to_string());
let user_key = kb.user(&user.id);

self.db.transact(|tx| async move {
// Check uniqueness
if tx.get(&user_key, false).await?.is_some() {
return Err(RepositoryError::AlreadyExists);
}

// Create user
let user = User::from(user);
tx.set(&user_key, &serialize(&user)?);

// Index by email
let email_key = format!("{}/indices/email/{}",
tenant_id, user.email.to_lowercase());
tx.set(&email_key, user.id.as_bytes());

// Audit log
let audit_key = format!("{}/audit/{:020}:{}",
tenant_id, Utc::now().timestamp_nanos(), Uuid::new_v4());
let audit = AuditEntry::user_created(&user);
tx.set(&audit_key, &serialize(&audit)?);

Ok(user)
}).await
}
}

// Efficient range queries
pub async fn get_recent_tasks(
db: &Database,
tenant_id: &str,
limit: usize,
) -> Result<Vec<Task>> {
let prefix = format!("{}/tasks/by_date/", tenant_id);
let range = RangeOption {
begin: KeySelector::first_greater_or_equal(&prefix),
end: KeySelector::first_greater_than(&prefix),
limit: Some(limit),
reverse: true, // Most recent first
..Default::default()
};

db.transact(|tx| async move {
let kvs = tx.get_range(&range, false).await?;
kvs.into_iter()
.map(|kv| deserialize::<Task>(&kv.value()))
.collect::<Result<Vec<_>, _>>()
}).await
}

// Migration pattern
pub async fn migrate_users_v1_to_v2(
db: &Database,
tenant_id: &str,
) -> Result<()> {
let migration_key = format!("{}/migrations/users_v2", tenant_id);

db.transact(|tx| async move {
// Check if already migrated
if tx.get(&migration_key, false).await?.is_some() {
return Ok(());
}

// Get all v1 users
let prefix = format!("{}/users/", tenant_id);
let range = RangeOption::from(&prefix[..]);
let users = tx.get_range(&range, 1000, false).await?;

// Transform and write back
for kv in users {
let mut user: UserV1 = deserialize(&kv.value())?;
let user_v2 = UserV2 {
id: user.id,
email: user.email,
created_at: user.created_at,
email_verified: false, // New field
};
tx.set(&kv.key(), &serialize(&user_v2)?);
}

// Mark complete
tx.set(&migration_key, &Utc::now().to_rfc3339().as_bytes());
Ok(())
}).await
}

Anti-Patterns to Avoid​

  • Don't forget tenant prefix on any key
  • Avoid unbounded range scans
  • Never use UUIDs in key paths for time-series data
  • Don't create transactions larger than 10MB
  • Avoid synchronous operations in async contexts

References​