-- ============================================================================ -- cache_schema.sql — Schema Tabella Cache per Supabase A -- -- Eseguire su Supabase A (nodo analytics/cache) per creare la tabella cache -- ============================================================================ -- Tabella Cache Entries CREATE TABLE IF NOT EXISTS cache_entries ( id BIGSERIAL PRIMARY KEY, cache_key VARCHAR(32) NOT NULL UNIQUE, strategy VARCHAR(50) NOT NULL, identifier TEXT NOT NULL, value TEXT NOT NULL, ttl_seconds INTEGER DEFAULT 3600, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), expires_at TIMESTAMP WITH TIME ZONE NOT NULL, updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), -- Indici per performance CONSTRAINT cache_key_idx UNIQUE (cache_key), CONSTRAINT strategy_idx ON strategy, CONSTRAINT expires_at_idx ON expires_at ); -- Indice su expires_at per pulizia automatica CREATE INDEX IF NOT EXISTS idx_cache_expires_at ON cache_entries(expires_at); -- Indice su strategy per query veloci per strategia CREATE INDEX IF NOT EXISTS idx_cache_strategy ON cache_entries(strategy); -- Indice composito per query veloci (strategy + identifier) CREATE INDEX IF NOT EXISTS idx_cache_strategy_identifier ON cache_entries(strategy, identifier); -- Tabella Statistiche Cache CREATE TABLE IF NOT EXISTS cache_stats ( id BIGSERIAL PRIMARY KEY, hits BIGINT DEFAULT 0, misses BIGINT DEFAULT 0, sets BIGINT DEFAULT 0, deletes BIGINT DEFAULT 0, evictions BIGINT DEFAULT 0, recorded_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), CONSTRAINT cache_stats_pkey PRIMARY KEY (id) ); -- Tabella Audit Cache (opzionale) CREATE TABLE IF NOT EXISTS cache_audit ( id BIGSERIAL PRIMARY KEY, action VARCHAR(50) NOT NULL, -- 'GET', 'SET', 'DELETE', 'EVICT' strategy VARCHAR(50) NOT NULL, identifier TEXT NOT NULL, cache_key VARCHAR(32), status VARCHAR(20) NOT NULL, -- 'HIT', 'MISS', 'SUCCESS', 'FAILURE' duration_ms INTEGER, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Indice su created_at per query audit veloci CREATE INDEX IF NOT EXISTS idx_cache_audit_created_at ON cache_audit(created_at DESC); -- Indice su action per filtrare per tipo operazione CREATE INDEX IF NOT EXISTS idx_cache_audit_action ON cache_audit(action); -- ============================================================================ -- Funzione: Pulizia automatica entry scaduti -- ============================================================================ CREATE OR REPLACE FUNCTION cleanup_expired_cache() RETURNS TABLE(deleted_count INTEGER) AS $$ BEGIN DELETE FROM cache_entries WHERE expires_at < NOW(); RETURN QUERY SELECT COUNT(*)::INTEGER FROM cache_entries WHERE expires_at < NOW(); END; $$ LANGUAGE plpgsql; -- ============================================================================ -- Trigger: Aggiorna updated_at su UPDATE -- ============================================================================ CREATE OR REPLACE FUNCTION update_cache_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER cache_entries_updated_at_trigger BEFORE UPDATE ON cache_entries FOR EACH ROW EXECUTE FUNCTION update_cache_updated_at(); -- ============================================================================ -- View: Statistiche Cache Attuali -- ============================================================================ CREATE OR REPLACE VIEW cache_stats_view AS SELECT COUNT(*) as total_entries, COUNT(CASE WHEN expires_at > NOW() THEN 1 END) as valid_entries, COUNT(CASE WHEN expires_at <= NOW() THEN 1 END) as expired_entries, COUNT(DISTINCT strategy) as unique_strategies, MIN(created_at) as oldest_entry, MAX(created_at) as newest_entry, AVG(ttl_seconds) as avg_ttl_seconds FROM cache_entries; -- ============================================================================ -- View: Hit Rate (ultimi 24 ore) -- ============================================================================ CREATE OR REPLACE VIEW cache_hit_rate_24h AS SELECT COUNT(CASE WHEN status = 'HIT' THEN 1 END) as hits, COUNT(CASE WHEN status = 'MISS' THEN 1 END) as misses, COUNT(*) as total_requests, ROUND( COUNT(CASE WHEN status = 'HIT' THEN 1 END)::NUMERIC / NULLIF(COUNT(*), 0) * 100, 2 ) as hit_rate_percent FROM cache_audit WHERE created_at > NOW() - INTERVAL '24 hours'; -- ============================================================================ -- Policy RLS (Row Level Security) — Opzionale -- ============================================================================ -- Abilita RLS sulla tabella cache_entries ALTER TABLE cache_entries ENABLE ROW LEVEL SECURITY; -- Policy: Tutti possono leggere cache (read-only per A) CREATE POLICY "Allow read access to cache" ON cache_entries FOR SELECT USING (true); -- Policy: Solo service role può scrivere/modificare cache CREATE POLICY "Allow write access to cache (service role only)" ON cache_entries FOR INSERT WITH CHECK (current_user = 'postgres'); CREATE POLICY "Allow update access to cache (service role only)" ON cache_entries FOR UPDATE USING (current_user = 'postgres'); CREATE POLICY "Allow delete access to cache (service role only)" ON cache_entries FOR DELETE USING (current_user = 'postgres'); -- ============================================================================ -- Commenti Documentazione -- ============================================================================ COMMENT ON TABLE cache_entries IS 'Archivio cache distribuito su Supabase A (read-heavy, non-critical)'; COMMENT ON COLUMN cache_entries.cache_key IS 'Chiave cache univoca (SHA256 hash di strategy:identifier)'; COMMENT ON COLUMN cache_entries.strategy IS 'Strategia cache: query, memory, embedding, conversation, analytics'; COMMENT ON COLUMN cache_entries.identifier IS 'Identificatore univoco per il valore (es. session_id, query_hash)'; COMMENT ON COLUMN cache_entries.value IS 'Valore memorizzato in cache (JSON serializzato)'; COMMENT ON COLUMN cache_entries.ttl_seconds IS 'Time-to-live in secondi (tempo di scadenza)'; COMMENT ON COLUMN cache_entries.expires_at IS 'Timestamp di scadenza (created_at + ttl_seconds)'; COMMENT ON TABLE cache_stats IS 'Statistiche aggregate del cache layer'; COMMENT ON TABLE cache_audit IS 'Audit log di tutte le operazioni cache (GET, SET, DELETE, EVICT)'; COMMENT ON FUNCTION cleanup_expired_cache() IS 'Rimuove entry scaduti dalla cache'; COMMENT ON VIEW cache_stats_view IS 'Statistiche attuali della cache (totale, valide, scadute, ecc.)'; COMMENT ON VIEW cache_hit_rate_24h IS 'Hit rate del cache nelle ultime 24 ore';