{"index.json":{"schemaVersion":2,"app":"DBAtlas","updated":"2026-07-04","engines":"engines.json","rosetta":["rosetta/sessions.json","rosetta/storage.json","rosetta/performance.json","rosetta/backup.json","rosetta/security.json","rosetta/maintenance.json","rosetta/schema.json","rosetta/replication.json","rosetta/connect.json","rosetta/objects.json","rosetta/datamove.json","rosetta/monitoring.json","rosetta/automation.json","rosetta/lifecycle.json","rosetta/migration.json","rosetta/tuning.json","rosetta/tuning-adv.json","rosetta/advanced-features.json","rosetta/sql-syntax.json"],"cloud":["cloud/cloud-ops-1.json","cloud/cloud-ops-2.json"],"guide":{"bestPractices":["guide/best-practices.json","guide/best-practices-2.json","guide/best-practices-3.json","guide/best-practices-4.json"],"glossary":["guide/glossary.json","guide/glossary-2.json","guide/glossary-3.json"]},"rosettaOverlays":["rosetta/overlay-elasticsearch.json"],"types":"types/types-functions.json","errors":["errors/oracle.json","errors/postgres.json","errors/mysql.json","errors/mssql.json","errors/mongodb.json","errors/redis.json","errors/elasticsearch.json","errors/oracle-more.json","errors/postgres-more.json","errors/mysql-more.json","errors/mssql-more.json","errors/mongodb-more.json","errors/redis-more.json","errors/elasticsearch-more.json","errors/oracle-more2.json","errors/postgres-more2.json","errors/mysql-more2.json","errors/mssql-more2.json","errors/mongodb-more2.json","errors/redis-more2.json","errors/elasticsearch-more2.json","errors/oracle-more3.json","errors/postgres-more3.json","errors/mysql-more3.json","errors/mssql-more3.json","errors/mongodb-more3.json","errors/redis-more3.json","errors/elasticsearch-more3.json","errors/oracle-more4.json","errors/postgres-more4.json","errors/mysql-more4.json","errors/mssql-more4.json","errors/mongodb-more4.json","errors/redis-more4.json","errors/elasticsearch-more4.json"],"health":["health/oracle.json","health/postgres.json","health/mysql.json","health/mssql.json","health/mongodb.json","health/redis.json","health/elasticsearch.json"],"recipes":["recipes/oracle.json","recipes/oracle-ha.json","recipes/oracle-ops.json","recipes/postgres.json","recipes/postgres-ha.json","recipes/postgres-ops.json","recipes/mysql.json","recipes/mysql-ext.json","recipes/mssql.json","recipes/mssql-ext.json","recipes/mongodb.json","recipes/mongodb-ext.json","recipes/redis.json","recipes/redis-ext.json","recipes/elasticsearch.json","recipes/elasticsearch-ext.json"],"playbooks":["playbooks/playbooks.json","playbooks/hadr.json","playbooks/security.json","playbooks/cloud.json","playbooks/automation.json"],"solver":["solver/slow-query.json","solver/deadlock.json","solver/disk-full.json","solver/connection-storm.json","solver/replication-lag.json","solver/ora-01555.json","solver/backup-restore.json","solver/upgrade-wizard.json","solver/migration-wizard.json","solver/high-cpu.json","solver/memory-pressure.json","solver/install-setup.json","solver/corruption-recovery.json","solver/failover-drill.json","solver/security-incident.json","solver/audit-setup.json","solver/decide-ha.json","solver/decide-isolation.json","solver/decide-sqlnosql.json","solver/decide-shard-replica.json","solver/decide-backup.json","solver/decide-index.json","solver/decide-partition.json","solver/decide-pooling.json","solver/decide-cache.json","solver/decide-datatype.json","solver/decide-capacity.json","solver/slow-write.json","solver/io-bottleneck.json","solver/undo-temp-pressure.json","solver/long-transaction.json","solver/plan-regression.json","solver/replica-resync.json","solver/cache-hit-low.json","solver/arch-oltp.json","solver/arch-dwh.json","solver/arch-microservice.json","solver/arch-eventdriven.json","solver/arch-multiregion.json","solver/arch-highscale.json"],"reference":{"params":["reference/params-oracle.json","reference/params-postgres.json","reference/params-mysql.json","reference/params-mssql.json","reference/params-mongodb.json","reference/params-redis.json","reference/params-elasticsearch.json"],"waits":["reference/waits-oracle.json","reference/waits-postgres.json","reference/waits-mysql.json","reference/waits-mssql.json","reference/waits-mongodb.json","reference/waits-redis.json","reference/waits-elasticsearch.json"],"alerts":["reference/alerts-oracle.json","reference/alerts-postgres.json","reference/alerts-mysql.json","reference/alerts-mssql.json","reference/alerts-mongodb.json","reference/alerts-redis.json","reference/alerts-elasticsearch.json"]},"install":"install/index.json"},"engines.json":{"schemaVersion":1,"updated":"2026-07-03","engines":[{"id":"oracle","name":"Oracle","full":"Oracle Database","color":"#C74634","family":"sql","cli":"sqlplus","docs":"https://docs.oracle.com/en/database/"},{"id":"postgres","name":"PostgreSQL","full":"PostgreSQL","color":"#3461B5","family":"sql","cli":"psql","docs":"https://www.postgresql.org/docs/"},{"id":"mysql","name":"MySQL","full":"MySQL / MariaDB","color":"#00758F","family":"sql","cli":"mysql","docs":"https://dev.mysql.com/doc/"},{"id":"mssql","name":"SQL Server","full":"Microsoft SQL Server","color":"#6E3FC4","family":"sql","cli":"sqlcmd","docs":"https://learn.microsoft.com/en-us/sql/"},{"id":"mongodb","name":"MongoDB","full":"MongoDB","color":"#2E7D32","family":"document","cli":"mongosh","docs":"https://www.mongodb.com/docs/"},{"id":"redis","name":"Redis","full":"Redis","color":"#B45309","family":"keyvalue","cli":"redis-cli","docs":"https://redis.io/docs/"},{"id":"elasticsearch","name":"Elasticsearch","full":"Elasticsearch","color":"#BE2A6E","family":"search","cli":"curl / Kibana","docs":"https://www.elastic.co/guide/"}]},"rosetta/sessions.json":{"category":"Sesiones y actividad","categoryId":"sessions","order":1,"concepts":[{"id":"active-sessions","title":"Mostrar sesiones activas / consultas en ejecución","aliases":["active sessions","running queries","who is connected","current activity","active session","running query","who is connected now","processlist","current op"],"engines":{"oracle":{"command":"SELECT sid, serial#, username, status, sql_id, machine, program\nFROM v$session\nWHERE type = 'USER' AND status = 'ACTIVE';","note":"v$session muestra las sesiones activas en este momento. Para el texto SQL completo, combínela con v$sql (JOIN por sql_id).","danger":"safe"},"postgres":{"command":"SELECT pid, usename, state, wait_event_type, query\nFROM pg_stat_activity\nWHERE state <> 'idle';","note":"state = 'active' muestra las que están ejecutando una consulta en ese momento. Calcule las duraciones a partir de backend_start / query_start.","danger":"safe"},"mysql":{"command":"SELECT id, user, host, db, command, time, state, info\nFROM information_schema.processlist\nWHERE command <> 'Sleep';\n-- Shortcut: SHOW FULL PROCESSLIST;","note":"En MySQL 8 / MariaDB, performance_schema.processlist adquiere menos bloqueos.","danger":"safe"},"mssql":{"command":"SELECT r.session_id, s.login_name, r.status, r.command,\n       r.cpu_time, r.total_elapsed_time, t.text AS sql_text\nFROM sys.dm_exec_requests r\nJOIN sys.dm_exec_sessions s ON r.session_id = s.session_id\nCROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t\nWHERE s.is_user_process = 1;","note":"Para un vistazo rápido: EXEC sp_who2 'active';","danger":"safe"},"mongodb":{"command":"db.currentOp({ \"active\": true })","note":"Para filtrar operaciones de larga duración: db.currentOp({ active: true, secs_running: { $gt: 5 } })","danger":"safe"},"redis":{"command":"CLIENT LIST\n# For a summary: INFO clients","note":"Como Redis es de un solo hilo, se listan los clientes conectados en lugar de 'consultas en ejecución'. Para comandos lentos use SLOWLOG GET.","danger":"safe"}}},{"id":"blocking-sessions","title":"Encontrar sesiones bloqueantes / en espera de bloqueo","aliases":["blocking","locks","lock wait","deadlock source","blocking session","lock waiting","who is blocking","who is blocking me","blocked","lock holder","what am I waiting on"],"engines":{"oracle":{"command":"SELECT blocking_session, sid, serial#, username,\n       sql_id, seconds_in_wait, event\nFROM v$session\nWHERE blocking_session IS NOT NULL;","note":"Una sesión con blocking_session informado está bloqueada por el SID indicado en el valor de blocking_session.","danger":"safe"},"postgres":{"command":"SELECT pid, usename, pg_blocking_pids(pid) AS blocked_by,\n       wait_event, query\nFROM pg_stat_activity\nWHERE cardinality(pg_blocking_pids(pid)) > 0;","note":"El array blocked_by contiene los PID que bloquean a este pid.","danger":"safe"},"mysql":{"command":"SELECT * FROM sys.innodb_lock_waits\\G\n-- MySQL 8: SELECT * FROM performance_schema.data_lock_waits;","note":"El esquema sys presenta de forma legible el trx_id / thread en espera y el bloqueante.","danger":"safe"},"mssql":{"command":"SELECT r.session_id, r.blocking_session_id,\n       r.wait_type, r.wait_time, r.wait_resource\nFROM sys.dm_exec_requests r\nWHERE r.blocking_session_id <> 0;","note":"Las solicitudes con blocking_session_id <> 0 están bloqueadas. Se puede construir una consulta recursiva para seguir la cadena.","danger":"safe"},"mongodb":{"command":"db.currentOp({ \"waitingForLock\": true })","note":"WiredTiger bloquea a nivel de documento/colección. Puede terminar operaciones largas con killOp.","danger":"safe"},"redis":{"command":"# Not applicable: Redis commands run atomically one at a time; there are no row locks.\n# Closest diagnostic: SLOWLOG GET 10","note":"El bloqueo suele deberse a un único comando de larga duración (p. ej., KEYS *, un SORT grande); SLOWLOG los muestra.","danger":"safe"}}},{"id":"kill-session","title":"Terminar (kill) una sesión / consulta","aliases":["kill session","terminate","cancel query","terminate session","cancel the query","kill query","killop"],"engines":{"oracle":{"command":"ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;\n-- To cancel only the running statement (18c+):\n-- ALTER SYSTEM CANCEL SQL 'sid,serial#';","note":"Obtenga los valores sid y serial# de v$session. IMMEDIATE inicia el rollback de inmediato.","danger":"danger"},"postgres":{"command":"-- Cancel only the running query (the session stays):\nSELECT pg_cancel_backend(pid);\n-- Close the session entirely:\nSELECT pg_terminate_backend(pid);","note":"Pruebe primero pg_cancel_backend; si no funciona, use pg_terminate_backend.","danger":"danger"},"mysql":{"command":"-- Kill only the query:\nKILL QUERY <id>;\n-- Close the connection entirely:\nKILL CONNECTION <id>;","note":"El valor <id> es la columna Id del processlist.","danger":"danger"},"mssql":{"command":"KILL <session_id>;\n-- To see rollback progress:\n-- KILL <session_id> WITH STATUSONLY;","note":"KILL hace rollback de la transacción abierta; en transacciones grandes el rollback puede tardar mucho.","danger":"danger"},"mongodb":{"command":"db.killOp(<opid>)","note":"Tome el valor de opid del campo opid en la salida de db.currentOp().","danger":"danger"},"redis":{"command":"CLIENT KILL ID <client-id>\n# or by address: CLIENT KILL ADDR 10.0.0.5:6379","note":"Use CLIENT LIST para obtener el client-id. Un comando que ya está en ejecución no se puede interrumpir (salvo un script Lua: SCRIPT KILL); hay que esperar a que termine. Para liberar a un cliente que espera en un comando bloqueante como BLPOP/XREAD BLOCK sin cerrar su conexión, use CLIENT UNBLOCK <client-id> [TIMEOUT|ERROR]. CLIENT UNPAUSE solo reanuda los clientes pausados con CLIENT PAUSE.","danger":"caution"}}},{"id":"connection-count","title":"Ver número de conexiones y límite de conexiones","aliases":["connections","max connections","connection limit","connection count","how many connections","is the connection pool full"],"engines":{"oracle":{"command":"SELECT COUNT(*) AS current_sessions FROM v$session WHERE type = 'USER';\nSELECT name, value FROM v$parameter WHERE name IN ('processes','sessions');","note":"Los límites son los parámetros processes/sessions. Para la utilización también puede consultar la vista resource_limit.","danger":"safe"},"postgres":{"command":"SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'client backend';\nSHOW max_connections;","note":"Como pg_stat_activity incluye también procesos en segundo plano, solo se cuentan las conexiones de cliente (backend_type = 'client backend'). Las ranuras de superuser_reserved_connections están reservadas para administradores.","danger":"safe"},"mysql":{"command":"SHOW STATUS LIKE 'Threads_connected';\nSHOW VARIABLES LIKE 'max_connections';","note":"La variable de estado Max_used_connections muestra el pico.","danger":"safe"},"mssql":{"command":"SELECT COUNT(*) FROM sys.dm_exec_sessions WHERE is_user_process = 1;\nSELECT @@MAX_CONNECTIONS AS configured_max;","note":"@@MAX_CONNECTIONS es el techo teórico; el límite práctico depende de la memoria y de los worker threads.","danger":"safe"},"mongodb":{"command":"db.serverStatus().connections","note":"Devuelve los campos current, available y totalCreated. El límite lo define net.maxIncomingConnections.","danger":"safe"},"redis":{"command":"INFO clients\nCONFIG GET maxclients","note":"connected_clients es el número actual; maxclients es el techo configurado (por defecto 10000).","danger":"safe"}}}]},"rosetta/storage.json":{"category":"Espacio y almacenamiento","categoryId":"storage","order":2,"concepts":[{"id":"database-size","title":"Mostrar el tamaño total de la base de datos","aliases":["database size","db size","database size","how much space does it use","disk usage","total size"],"engines":{"oracle":{"command":"SELECT ROUND(SUM(bytes)/1024/1024/1024, 2) AS total_gb\nFROM dba_data_files;","note":"Para solo los segmentos usados, sume dba_segments. Añada dba_temp_files para TEMP.","danger":"safe"},"postgres":{"command":"SELECT pg_size_pretty(pg_database_size(current_database()));","note":"Para todas las bases de datos: SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database ORDER BY 2 DESC;","danger":"safe"},"mysql":{"command":"SELECT table_schema AS db,\n       ROUND(SUM(data_length + index_length)/1024/1024, 1) AS mb\nFROM information_schema.tables\nGROUP BY table_schema\nORDER BY mb DESC;","note":"En InnoDB este valor es una estimación; para el tamaño exacto de archivo revise ibdata/los archivos a nivel del SO.","danger":"safe"},"mssql":{"command":"EXEC sp_spaceused;\n-- All databases:\nSELECT DB_NAME(database_id) AS db,\n       CAST(SUM(size) * 8.0 / 1024 AS DECIMAL(10,1)) AS mb\nFROM sys.master_files\nGROUP BY database_id;","note":"La columna size está en páginas de 8 KB; * 8 / 1024 la convierte a MB.","danger":"safe"},"mongodb":{"command":"db.stats(1024*1024)\n// returns the dataSize, storageSize, indexSize fields in MB","note":"storageSize es el tamaño comprimido en disco; dataSize es el tamaño lógico sin comprimir.","danger":"safe"},"redis":{"command":"INFO memory\n# the used_memory_human line gives the total size in memory\nDBSIZE  # number of keys","note":"Redis trabaja en memoria; 'tamaño' significa uso de RAM. El tamaño del archivo RDB/AOF es el estado persistido en disco.","danger":"safe"}}},{"id":"largest-tables","title":"Listar las tablas / colecciones más grandes","aliases":["largest tables","biggest tables","top tables by size","largest table","tables using the most space","bigkeys"],"engines":{"oracle":{"command":"SELECT segment_name, segment_type,\n       ROUND(bytes/1024/1024, 1) AS mb\nFROM dba_segments\nWHERE segment_type IN ('TABLE','TABLE PARTITION')\nORDER BY bytes DESC\nFETCH FIRST 10 ROWS ONLY;","note":"Quite el filtro segment_type para ver también los índices.","danger":"safe"},"postgres":{"command":"SELECT relname AS table,\n       pg_size_pretty(pg_total_relation_size(relid)) AS total\nFROM pg_catalog.pg_statio_user_tables\nORDER BY pg_total_relation_size(relid) DESC\nLIMIT 10;","note":"pg_total_relation_size es la suma de tabla + índices + TOAST; use pg_relation_size solo para la tabla.","danger":"safe"},"mysql":{"command":"SELECT table_name,\n       ROUND((data_length + index_length)/1024/1024, 1) AS mb\nFROM information_schema.tables\nWHERE table_schema = DATABASE()\nORDER BY (data_length + index_length) DESC\nLIMIT 10;","note":"Para un esquema concreto, use WHERE table_schema = 'name'.","danger":"safe"},"mssql":{"command":"SELECT TOP 10 OBJECT_NAME(object_id) AS table_name,\n       SUM(reserved_page_count) * 8 / 1024 AS mb\nFROM sys.dm_db_partition_stats\nGROUP BY object_id\nORDER BY mb DESC;","note":"reserved_page_count abarca todas las páginas reservadas (datos + índices).","danger":"safe"},"mongodb":{"command":"db.getCollectionNames()\n  .map(c => ({ name: c, mb: +(db[c].stats().storageSize/1048576).toFixed(1) }))\n  .sort((a, b) => b.mb - a.mb)\n  .slice(0, 10)","note":"storageSize es el tamaño comprimido en disco. El costo de stats() es bajo incluso en colecciones grandes.","danger":"safe"},"redis":{"command":"redis-cli --bigkeys\n# To rank by memory (bytes): redis-cli --memkeys","note":"Ambos recorren todo el keyspace con SCAN (no bloquean el servidor pero añaden carga); la diferencia es la métrica: --bigkeys informa las claves más grandes por número de elementos/longitud por tipo, --memkeys por consumo de memoria mediante MEMORY USAGE. Para una sola clave use MEMORY USAGE <key>.","danger":"caution"}}},{"id":"free-space","title":"Comprobar espacio libre / porcentaje de uso","aliases":["free space","disk full","free space","usage","space left","tablespace usage","disk is full"],"engines":{"oracle":{"command":"SELECT df.tablespace_name,\n       ROUND(SUM(df.bytes)/1024/1024) AS total_mb,\n       ROUND(NVL(fs.free, 0)/1024/1024) AS free_mb,\n       ROUND((1 - NVL(fs.free,0)/SUM(df.bytes)) * 100, 1) AS pct_used\nFROM dba_data_files df\nLEFT JOIN (SELECT tablespace_name, SUM(bytes) free\n           FROM dba_free_space GROUP BY tablespace_name) fs\n  ON df.tablespace_name = fs.tablespace_name\nGROUP BY df.tablespace_name, fs.free;","note":"En archivos con AUTOEXTEND activado, el límite real es maxbytes; revise dba_data_files.maxbytes para MAXSIZE.","danger":"safe"},"postgres":{"command":"-- Datafile usage is at the OS level (df -h). Tablespace sizes from inside the DB:\nSELECT spcname, pg_size_pretty(pg_tablespace_size(spcname))\nFROM pg_tablespace;","note":"PostgreSQL depende del SO para el espacio libre en disco; el monitoreo del sistema operativo (df) es imprescindible para umbrales críticos.","danger":"safe"},"mysql":{"command":"SELECT table_schema,\n       ROUND(SUM(data_free)/1024/1024, 1) AS reclaimable_mb\nFROM information_schema.tables\nGROUP BY table_schema;","note":"data_free es espacio recuperable (fragmentado) dentro de la tabla; el espacio libre en disco está a nivel del SO.","danger":"safe"},"mssql":{"command":"SELECT name AS file_name,\n       size/128.0 AS size_mb,\n       size/128.0 - CAST(FILEPROPERTY(name,'SpaceUsed') AS int)/128.0 AS free_mb\nFROM sys.database_files;","note":"SpaceUsed está en páginas; dividir entre 128 da MB (páginas de 8 KB).","danger":"safe"},"mongodb":{"command":"var s = db.stats();\n({ fsTotalGB: +(s.fsTotalSize/1073741824).toFixed(1),\n   fsUsedGB:  +(s.fsUsedSize/1073741824).toFixed(1) })","note":"Con WiredTiger, fsTotalSize / fsUsedSize dan el tamaño total y usado del disco a nivel del SO.","danger":"safe"},"redis":{"command":"INFO memory\nCONFIG GET maxmemory\n# Usage = used_memory / maxmemory","note":"Si maxmemory es 0 no hay límite y, cuando la RAM se llena, interviene el OOM killer del SO. maxmemory-policy determina el comportamiento de eviction.","danger":"safe"}}},{"id":"row-count","title":"Obtener el número de filas / documentos (rápido)","aliases":["row count","count rows","row count","how many records","document count","how many rows"],"engines":{"oracle":{"command":"-- Statistics-based (fast, estimated):\nSELECT num_rows FROM dba_tables WHERE table_name = 'CUSTOMER';\n-- Exact: SELECT COUNT(*) FROM customer;","note":"num_rows refleja la última ejecución de ANALYZE/DBMS_STATS; use COUNT(*) para un valor exacto.","danger":"safe"},"postgres":{"command":"-- Estimated (instant):\nSELECT reltuples::bigint FROM pg_class WHERE relname = 'customer';\n-- Exact: SELECT count(*) FROM customer;","note":"reltuples es una estimación a la fecha del último ANALYZE/VACUUM; un COUNT completo es costoso en tablas grandes.","danger":"safe"},"mysql":{"command":"-- Estimated (InnoDB):\nSELECT table_rows FROM information_schema.tables\nWHERE table_name = 'customer';\n-- Exact: SELECT COUNT(*) FROM customer;","note":"En InnoDB, table_rows es una estimación y puede desviarse hasta un 50%.","danger":"safe"},"mssql":{"command":"SELECT SUM(row_count) AS rows\nFROM sys.dm_db_partition_stats\nWHERE object_id = OBJECT_ID('dbo.customer')\n  AND index_id IN (0, 1);","note":"index_id 0 (heap) o 1 (clustered) da el número de filas; mucho más rápido que un recorrido con COUNT(*).","danger":"safe"},"mongodb":{"command":"// Fast (from metadata):\ndb.customer.estimatedDocumentCount()\n// Exact (can count with a filter): db.customer.countDocuments({})","note":"estimatedDocumentCount lee metadatos (instantáneo); countDocuments realiza un recorrido real.","danger":"safe"},"redis":{"command":"DBSIZE\n# Specific pattern: count roughly with SCAN (do not use KEYS * in production)","note":"DBSIZE devuelve el número total de claves de la base de datos seleccionada en O(1).","danger":"safe"}}}]},"rosetta/performance.json":{"category":"Rendimiento","categoryId":"performance","order":3,"concepts":[{"id":"top-slow-queries","title":"Encontrar las consultas más lentas / costosas","aliases":["slow queries","top queries","slowest queries","top resource consumers","expensive queries","slowlog"],"engines":{"oracle":{"command":"SELECT sql_id,\n       ROUND(elapsed_time/1e6, 1) AS elapsed_sec,\n       executions,\n       ROUND(elapsed_time/1e6/NULLIF(executions,0), 3) AS sec_per_exec,\n       sql_text\nFROM v$sql\nORDER BY elapsed_time DESC\nFETCH FIRST 10 ROWS ONLY;","note":"Para análisis histórico use AWR: dba_hist_sqlstat. Con el sql_id, DBMS_XPLAN.DISPLAY_CURSOR muestra el plan.","danger":"safe"},"postgres":{"command":"SELECT calls,\n       ROUND(total_exec_time::numeric, 1) AS total_ms,\n       ROUND(mean_exec_time::numeric, 2) AS mean_ms,\n       query\nFROM pg_stat_statements\nORDER BY total_exec_time DESC\nLIMIT 10;","note":"Requiere la extensión pg_stat_statements (shared_preload_libraries). Reinicie con pg_stat_statements_reset().","danger":"safe"},"mysql":{"command":"SELECT digest_text,\n       count_star AS calls,\n       ROUND(sum_timer_wait/1e12, 2) AS total_sec,\n       ROUND(avg_timer_wait/1e9, 2) AS avg_ms\nFROM performance_schema.events_statements_summary_by_digest\nORDER BY sum_timer_wait DESC\nLIMIT 10;","note":"Alternativa: el slow query log con slow_query_log = ON y long_query_time.","danger":"safe"},"mssql":{"command":"SELECT TOP 10\n       qs.execution_count,\n       qs.total_elapsed_time/1000 AS total_ms,\n       qs.total_elapsed_time/qs.execution_count/1000 AS avg_ms,\n       SUBSTRING(t.text, 1, 200) AS sql_text\nFROM sys.dm_exec_query_stats qs\nCROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t\nORDER BY qs.total_elapsed_time DESC;","note":"Si Query Store está habilitado, use sys.query_store_runtime_stats para un análisis histórico más completo.","danger":"safe"},"mongodb":{"command":"// Enable the profiler for operations over 100ms:\ndb.setProfilingLevel(1, { slowms: 100 })\n// Then read the slowest ones:\ndb.system.profile.find().sort({ millis: -1 }).limit(10)","note":"El profiler añade sobrecarga de escritura; desactívelo con db.setProfilingLevel(0) tras el diagnóstico.","danger":"caution"},"redis":{"command":"SLOWLOG GET 10\n# Set the threshold (microseconds): CONFIG SET slowlog-log-slower-than 10000","note":"slowlog-log-slower-than está en microsegundos (10000 = 10 ms). Límpielo con SLOWLOG RESET.","danger":"safe"}}},{"id":"explain-plan","title":"Obtener el plan de ejecución de una consulta (EXPLAIN)","aliases":["explain","execution plan","query plan","explain plan","show plan","explain analyze","why slow"],"engines":{"oracle":{"command":"EXPLAIN PLAN FOR\nSELECT * FROM customer WHERE city = 'Ankara';\nSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);\n-- For the actual executed plan:\n-- SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id', NULL, 'ALLSTATS LAST'));","note":"EXPLAIN PLAN da el plan estimado; para el número real de filas use DISPLAY_CURSOR + STATISTICS_LEVEL=ALL.","danger":"safe"},"postgres":{"command":"EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)\nSELECT * FROM customer WHERE city = 'Ankara';","note":"ANALYZE EJECUTA REALMENTE la consulta (cuidado con INSERT/UPDATE/DELETE; pruébelo dentro de una transacción y haga ROLLBACK).","danger":"caution"},"mysql":{"command":"EXPLAIN ANALYZE\nSELECT * FROM customer WHERE city = 'Ankara';\n-- Estimate only (does not execute): EXPLAIN FORMAT=JSON SELECT ...;","note":"EXPLAIN ANALYZE (MySQL 8.0.18+) ejecuta la consulta; FORMAT=JSON da detalles sin ejecutarla.","danger":"caution"},"mssql":{"command":"SET STATISTICS PROFILE ON;\nSELECT * FROM dbo.customer WHERE city = 'Ankara';\nSET STATISTICS PROFILE OFF;\n-- Estimated plan only (does not execute):\n-- SET SHOWPLAN_XML ON;","note":"En SSMS, 'Include Actual Execution Plan' (Ctrl+M) muestra el plan gráfico.","danger":"caution"},"mongodb":{"command":"db.customer.find({ city: \"Ankara\" }).explain(\"executionStats\")","note":"executionStats muestra el número real de documentos devueltos y el uso de índices (IXSCAN vs COLLSCAN).","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no query planner.\n# The complexity of each command is documented (e.g. GET O(1), SORT O(N+M*log(M))).","note":"El rendimiento depende de la complejidad del comando y del tamaño de la clave/estructura de datos; mídalo con SLOWLOG.","danger":"safe"}}},{"id":"unused-indexes","title":"Detectar índices no utilizados","aliases":["unused indexes","unused index","redundant index","index usage","drop index candidate"],"engines":{"oracle":{"command":"-- Start monitoring:\nALTER INDEX customer_city_idx MONITORING USAGE;\n-- Check after a while (12c+, all schemas):\nSELECT owner, index_name, used FROM dba_object_usage;","note":"Si USED = NO, el índice no se usó durante el período de monitoreo. Desactive el monitoreo con NOMONITORING USAGE. La antigua v$object_usage solo muestra los índices del usuario de la sesión y está obsoleta desde 12c. En 12.2+, el uso de índices se registra automáticamente en DBA_INDEX_USAGE.","danger":"safe"},"postgres":{"command":"SELECT relname AS table, indexrelname AS index, idx_scan\nFROM pg_stat_user_indexes\nWHERE idx_scan = 0\nORDER BY relname;","note":"idx_scan = 0 significa que el índice no se ha usado desde el último reinicio de estadísticas; no elimine índices UNIQUE/PK.","danger":"safe"},"mysql":{"command":"SELECT * FROM sys.schema_unused_indexes;","note":"performance_schema debe estar habilitado y el servidor debe llevar un tiempo en ejecución; las restricciones UNIQUE quedan excluidas.","danger":"safe"},"mssql":{"command":"SELECT OBJECT_NAME(i.object_id) AS table_name, i.name AS index_name\nFROM sys.indexes i\nLEFT JOIN sys.dm_db_index_usage_stats s\n  ON i.object_id = s.object_id AND i.index_id = s.index_id\n  AND s.database_id = DB_ID()\nWHERE i.type_desc = 'NONCLUSTERED'\n  AND COALESCE(s.user_seeks + s.user_scans + s.user_lookups, 0) = 0;","note":"usage_stats se reinicia cuando se reinicia el servidor; deje una ventana de observación suficiente.","danger":"safe"},"mongodb":{"command":"db.customer.aggregate([{ $indexStats: {} }])\n// Indexes with low/0 accesses.ops are candidates","note":"accesses.ops cuenta los accesos desde la marca de tiempo accesses.since; el índice _id no se puede eliminar.","danger":"safe"},"redis":{"command":"# Not applicable: core Redis has no secondary indexes.\n# If the RediSearch module is used, check statistics with FT.INFO <index>.","note":"Como el modelo de datos es clave-valor, no existe el concepto tradicional de índice.","danger":"safe"}}},{"id":"cache-hit-ratio","title":"Medir la tasa de aciertos de caché","aliases":["cache hit ratio","buffer cache","cache hit","hit ratio","buffer pool hit"],"engines":{"oracle":{"command":"SELECT metric_name, ROUND(value, 2) AS pct\nFROM v$sysmetric\nWHERE metric_name = 'Buffer Cache Hit Ratio'\n  AND intsize_csec = (SELECT MAX(intsize_csec) FROM v$sysmetric);","note":"Una tasa baja no siempre es mala; interprétela en el contexto de la carga de trabajo, no de forma aislada.","danger":"safe"},"postgres":{"command":"SELECT ROUND(sum(blks_hit) * 100.0 / NULLIF(sum(blks_hit) + sum(blks_read), 0), 2) AS cache_hit_pct\nFROM pg_stat_database;","note":"El objetivo suele ser 99%+. Si es baja, revise shared_buffers y la caché del SO.","danger":"safe"},"mysql":{"command":"SELECT ROUND((1 - (\n    (SELECT variable_value FROM performance_schema.global_status WHERE variable_name='Innodb_buffer_pool_reads') /\n    (SELECT variable_value FROM performance_schema.global_status WHERE variable_name='Innodb_buffer_pool_read_requests')\n  )) * 100, 2) AS buffer_pool_hit_pct;","note":"reads son lecturas desde disco, read_requests son el total de lecturas. Si es baja, aumente innodb_buffer_pool_size.","danger":"safe"},"mssql":{"command":"SELECT ROUND(\n  100.0 * (SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name='Buffer cache hit ratio')\n        / NULLIF((SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name='Buffer cache hit ratio base'), 0), 2) AS pct;","note":"En SQL Server moderno, Page Life Expectancy (PLE) es un indicador más significativo de la presión de memoria.","danger":"safe"},"mongodb":{"command":"var c = db.serverStatus().wiredTiger.cache;\n({ configuredMB: +(c['maximum bytes configured']/1048576).toFixed(0),\n   inCacheMB:    +(c['bytes currently in the cache']/1048576).toFixed(0),\n   readIntoCache: c['pages read into cache'],\n   evictedPages:  c['unmodified pages evicted'] })","note":"Valores altos de 'pages read into cache' y de eviction indican que la caché de WiredTiger es demasiado pequeña.","danger":"safe"},"redis":{"command":"INFO stats\n# keyspace_hits / (keyspace_hits + keyspace_misses) = hit ratio","note":"Una tasa de aciertos persistentemente baja indica que hay que revisar la configuración de TTL/eviction o el patrón de acceso.","danger":"safe"}}}]},"rosetta/backup.json":{"category":"Backup y recuperación","categoryId":"backup","order":4,"concepts":[{"id":"logical-dump","title":"Realizar un backup lógico (dump)","aliases":["backup","dump","export","take backup","logical dump","logical backup","pg_dump","mysqldump","mongodump"],"engines":{"oracle":{"command":"expdp system/password DIRECTORY=dpump_dir \\\n  SCHEMAS=hr DUMPFILE=hr_%U.dmp LOGFILE=hr_exp.log PARALLEL=4","note":"Usa Data Pump. El objeto DIRECTORY debe definirse previamente con CREATE DIRECTORY. Use RMAN para backups físicos.","danger":"safe"},"postgres":{"command":"pg_dump -U postgres -h localhost -Fc -f db.dump mydb\n# -Fc = compressed custom format (restored with pg_restore)","note":"-Fc permite restauraciones flexibles (tablas selectivas/en paralelo). Use pg_dumpall para todo el clúster (incluidos los roles).","danger":"safe"},"mysql":{"command":"mysqldump -u root -p --single-transaction \\\n  --routines --triggers --events mydb > db.sql","note":"--single-transaction da un backup consistente sin bloqueos en InnoDB. Opciones en paralelo para grandes volúmenes: mydumper/myloader o MySQL Shell util.dumpInstance()/dumpSchemas() (mysqlpump quedó obsoleto en 8.0.34 y se eliminó en 8.4).","danger":"safe"},"mssql":{"command":"BACKUP DATABASE [MyDB]\nTO DISK = N'C:\\backup\\MyDB_full.bak'\nWITH COMPRESSION, CHECKSUM, INIT;","note":"INIT sobrescribe el archivo existente; use NOINIT para añadir. Para backups de log use BACKUP LOG (en el modelo de recuperación FULL).","danger":"safe"},"mongodb":{"command":"mongodump --uri=\"mongodb://localhost:27017\" \\\n  --archive=db.archive.gz --gzip --db=mydb","note":"En un clúster con sharding, realícelo a través de mongos con el balancer detenido para garantizar la consistencia.","danger":"safe"},"redis":{"command":"BGSAVE\n# Takes an RDB snapshot in the background (dump.rdb).\n# Non-blocking copy: redis-cli --rdb /backup/dump.rdb","note":"BGSAVE se ejecuta en segundo plano mediante fork; SAVE bloquea el servidor (no lo use en producción). Con AOF habilitado se pierden menos datos.","danger":"safe"}}},{"id":"restore-dump","title":"Restaurar desde un backup","aliases":["restore","import","restore backup","restore from backup","recover","pg_restore","mongorestore"],"engines":{"oracle":{"command":"impdp system/password DIRECTORY=dpump_dir \\\n  SCHEMAS=hr DUMPFILE=hr_%U.dmp LOGFILE=hr_imp.log","note":"Use REMAP_SCHEMA para cargar en otro esquema y REMAP_TABLESPACE para otro tablespace.","danger":"danger"},"postgres":{"command":"# custom format:\npg_restore -U postgres -d target_db --clean --if-exists db.dump\n# plain SQL: psql -U postgres -d target_db -f db.sql","note":"--clean elimina los objetos existentes. Use -j <n> para una restauración en paralelo.","danger":"danger"},"mysql":{"command":"mysql -u root -p target_db < db.sql","note":"La base de datos de destino debe existir previamente (CREATE DATABASE). Para archivos grandes se prefiere el comando source.","danger":"danger"},"mssql":{"command":"RESTORE DATABASE [MyDB]\nFROM DISK = N'C:\\backup\\MyDB_full.bak'\nWITH REPLACE, RECOVERY;","note":"Para point-in-time, restaure el backup completo WITH NORECOVERY y luego aplique los backups de log con STOPAT.","danger":"danger"},"mongodb":{"command":"mongorestore --uri=\"mongodb://localhost:27017\" \\\n  --archive=db.archive.gz --gzip --drop","note":"--drop elimina las colecciones de destino antes de restaurar. Use --nsInclude para una restauración selectiva.","danger":"danger"},"redis":{"command":"# 1) Stop Redis   2) Copy dump.rdb to the 'dir' location\n# 3) Start Redis (the RDB is loaded automatically at startup)\nredis-cli CONFIG GET dir   # find the target directory","note":"Con AOF habilitado se carga el AOF en lugar del RDB; para restaurar desde el RDB, inicie con appendonly no o reconstruya el AOF.","danger":"danger"}}},{"id":"last-backup-status","title":"Comprobar estado / hora del último backup","aliases":["last backup","backup status","when was the last backup","backup state","backup history","rpo"],"engines":{"oracle":{"command":"SELECT session_key, input_type, status,\n       start_time, end_time,\n       output_bytes_display\nFROM v$rman_backup_job_details\nORDER BY start_time DESC\nFETCH FIRST 10 ROWS ONLY;","note":"El historial de RMAN también se puede ver con RMAN> LIST BACKUP SUMMARY.","danger":"safe"},"postgres":{"command":"-- WAL archiving health (for physical backup/PITR):\nSELECT last_archived_wal, last_archived_time,\n       failed_count, last_failed_time\nFROM pg_stat_archiver;","note":"Los backups lógicos (pg_dump) no se registran dentro de la BD; revise los logs de la herramienta/job de backup.","danger":"safe"},"mysql":{"command":"-- There is no built-in backup record; check the backup tool's log.\n-- For the binlog-based PITR window:\nSHOW BINARY LOGS;","note":"SHOW BINARY LOGS muestra los archivos binlog disponibles (la ventana de PITR); el historial de backups está en el log de la herramienta (Xtrabackup, etc.).","danger":"safe"},"mssql":{"command":"SELECT database_name,\n       MAX(backup_finish_date) AS last_backup,\n       CASE type WHEN 'D' THEN 'Full' WHEN 'I' THEN 'Diff'\n                 WHEN 'L' THEN 'Log' END AS backup_type\nFROM msdb.dbo.backupset\nGROUP BY database_name, type\nORDER BY database_name;","note":"msdb.dbo.backupset registra cada backup realizado por SQL Server.","danger":"safe"},"mongodb":{"command":"// There is no built-in backup record. PITR window = oplog duration:\nrs.printReplicationInfo()  // oplog first/last event times","note":"La ventana del oplog determina hasta qué punto atrás puede hacer PITR desde el replica set; el historial de backups está en el log de la herramienta.","danger":"safe"},"redis":{"command":"INFO persistence\n# check the rdb_last_save_time (epoch), rdb_last_bgsave_status,\n# aof_last_bgrewrite_status lines","note":"rdb_last_save_time da la hora del último guardado RDB correcto (Unix epoch); rdb_changes_since_last_save muestra cuántas escrituras aún no tienen backup.","danger":"safe"}}}]},"rosetta/security.json":{"category":"Usuarios y privilegios","categoryId":"security","order":5,"concepts":[{"id":"list-users","title":"Listar usuarios / roles","aliases":["list users","show users","list users","roles","who has access","accounts"],"engines":{"oracle":{"command":"SELECT username, account_status, default_tablespace, created\nFROM dba_users\nORDER BY username;","note":"account_status muestra los estados LOCKED/EXPIRED. Para roles, use dba_roles.","danger":"safe"},"postgres":{"command":"SELECT rolname, rolsuper, rolcanlogin, rolvaliduntil\nFROM pg_roles\nORDER BY rolname;\n-- Shortcut (psql): \\du","note":"En PostgreSQL un usuario es un rol con el atributo LOGIN; rolcanlogin lo distingue.","danger":"safe"},"mysql":{"command":"SELECT user, host, account_locked, password_expired\nFROM mysql.user\nORDER BY user;","note":"En MySQL una identidad es un par usuario + host ('app'@'%' y 'app'@'localhost' son distintos).","danger":"safe"},"mssql":{"command":"-- Server logins:\nSELECT name, type_desc, is_disabled FROM sys.server_principals\nWHERE type IN ('S','U','G');\n-- Database users:\nSELECT name, type_desc FROM sys.database_principals\nWHERE type IN ('S','U','G');","note":"Un login es de nivel servidor y un user de nivel base de datos; ambos se vinculan mediante el SID.","danger":"safe"},"mongodb":{"command":"// Users in the current database:\ndb.getUsers()\n// All users in the system: use admin; db.system.users.find({}, {user:1, db:1, roles:1})","note":"Los usuarios se definen en la base de datos donde se crearon (authenticationDatabase).","danger":"safe"},"redis":{"command":"ACL LIST\n# Names only: ACL USERS\n# Your own identity: ACL WHOAMI","note":"El sistema ACL de Redis 6+. Antes había un único usuario 'default' y requirepass.","danger":"safe"}}},{"id":"create-user-grant","title":"Crear un usuario y otorgar privilegios","aliases":["create user","grant","add user","create user","grant privilege","grant permission","new user"],"engines":{"oracle":{"command":"CREATE USER app IDENTIFIED BY \"Strong#Pass1\";\nGRANT CREATE SESSION TO app;\nGRANT SELECT, INSERT, UPDATE ON hr.employee TO app;","note":"Sin CREATE SESSION el usuario no puede conectarse. Para una cuota: ALTER USER app QUOTA 100M ON users;","danger":"caution"},"postgres":{"command":"CREATE ROLE app LOGIN PASSWORD 'Strong#Pass1';\nGRANT CONNECT ON DATABASE mydb TO app;\nGRANT USAGE ON SCHEMA public TO app;\nGRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO app;","note":"Para tablas que se creen en el futuro: ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO app;","danger":"caution"},"mysql":{"command":"CREATE USER 'app'@'%' IDENTIFIED BY 'Strong#Pass1';\nGRANT SELECT, INSERT, UPDATE ON mydb.* TO 'app'@'%';","note":"'%' permite conexiones desde cualquier host; por seguridad, indique una IP/host concreto cuando sea posible. CREATE USER/GRANT surten efecto de inmediato; FLUSH PRIVILEGES solo es necesario si se ejecutó DML directamente sobre las tablas de privilegios.","danger":"caution"},"mssql":{"command":"CREATE LOGIN app WITH PASSWORD = 'Strong#Pass1';\nUSE mydb;\nCREATE USER app FOR LOGIN app;\nGRANT SELECT, INSERT, UPDATE ON SCHEMA::dbo TO app;","note":"Un login abarca todo el servidor; un user se crea por separado en cada base de datos. Para un rol: ALTER ROLE db_datareader ADD MEMBER app;","danger":"caution"},"mongodb":{"command":"db.createUser({\n  user: \"app\",\n  pwd: \"Strong#Pass1\",\n  roles: [ { role: \"readWrite\", db: \"mydb\" } ]\n})","note":"La db en la que se crea el usuario pasa a ser su auth db. Roles integrados: read, readWrite, dbAdmin, clusterAdmin...","danger":"caution"},"redis":{"command":"ACL SETUSER app on >Strong#Pass1 ~app:* +@read +@write\n# To persist: ACL SAVE if an aclfile is defined, otherwise CONFIG REWRITE (users are written to redis.conf)","note":"on = habilitado, >password añade una contraseña, ~app:* es el patrón de claves, +@read/+@write son categorías de comandos. Si no se persiste, se pierde al reiniciar: ACL SAVE solo funciona cuando hay un aclfile configurado y, en caso contrario, devuelve un error; en ese caso use CONFIG REWRITE.","danger":"caution"}}},{"id":"show-privileges","title":"Mostrar los privilegios de un usuario","aliases":["show grants","privileges","show privileges","who can access what","permissions","roles of user"],"engines":{"oracle":{"command":"SELECT privilege FROM dba_sys_privs WHERE grantee = 'APP'\nUNION ALL SELECT granted_role FROM dba_role_privs WHERE grantee = 'APP'\nUNION ALL SELECT privilege||' ON '||owner||'.'||table_name\n          FROM dba_tab_privs WHERE grantee = 'APP';","note":"El grantee se almacena en mayúsculas. Para privilegios indirectos otorgados mediante roles, recorra también role_role_privs.","danger":"safe"},"postgres":{"command":"SELECT grantee, table_schema, table_name, privilege_type\nFROM information_schema.role_table_grants\nWHERE grantee = 'app';\n-- Role memberships (direct): \\drg app (PG16+; earlier \\du app)","note":"Las pertenencias a roles (herencia) se almacenan en pg_auth_members; \\du/\\drg solo muestran pertenencias directas, use pg_has_role('app', '<role>', 'MEMBER') para la pertenencia indirecta.","danger":"safe"},"mysql":{"command":"SHOW GRANTS FOR 'app'@'%';","note":"Para roles (MySQL 8), primero SET ROLE ALL, o use SHOW GRANTS FOR 'app'@'%' USING 'role_name';","danger":"safe"},"mssql":{"command":"SELECT dp.name AS principal, p.permission_name,\n       p.state_desc, o.name AS object\nFROM sys.database_permissions p\nJOIN sys.database_principals dp ON p.grantee_principal_id = dp.principal_id\nLEFT JOIN sys.objects o ON p.major_id = o.object_id\nWHERE dp.name = 'app';","note":"Para permisos otorgados mediante roles, revise también las pertenencias en sys.database_role_members.","danger":"safe"},"mongodb":{"command":"db.getUser(\"app\", { showPrivileges: true, showCredentials: false })","note":"showPrivileges también lista los privilegios expandidos (efectivos) de los roles.","danger":"safe"},"redis":{"command":"ACL GETUSER app","note":"Devuelve los comandos permitidos, los patrones de claves (~), los canales pub/sub (&) y los hashes de contraseña.","danger":"safe"}}},{"id":"change-password","title":"Cambiar la contraseña de un usuario","aliases":["change password","reset password","change password","reset password","alter password"],"engines":{"oracle":{"command":"ALTER USER app IDENTIFIED BY \"New#Pass2\";","note":"Para desbloquear una cuenta bloqueada: ALTER USER app ACCOUNT UNLOCK; Si la contraseña ha caducado, el mismo comando la restablece.","danger":"caution"},"postgres":{"command":"ALTER ROLE app WITH PASSWORD 'New#Pass2';","note":"Para que la contraseña no aparezca en los logs, el metacomando \\password app (psql) aplica el hash a la contraseña en el lado del cliente.","danger":"caution"},"mysql":{"command":"ALTER USER 'app'@'%' IDENTIFIED BY 'New#Pass2';","note":"Asegúrese de elegir el host correcto; 'app'@'localhost' es una cuenta distinta. ALTER USER surte efecto de inmediato; FLUSH PRIVILEGES no es necesario.","danger":"caution"},"mssql":{"command":"ALTER LOGIN app WITH PASSWORD = 'New#Pass2';\n-- For a user changing their own password: add OLD_PASSWORD","note":"MUST_CHANGE puede forzar el cambio en el primer inicio de sesión: WITH PASSWORD='...' MUST_CHANGE, CHECK_POLICY=ON.","danger":"caution"},"mongodb":{"command":"db.changeUserPassword(\"app\", \"New#Pass2\")","note":"Asegúrese de haber cambiado a la base de datos de autenticación del usuario (use mydb).","danger":"caution"},"redis":{"command":"# Clear old passwords and set the new one:\nACL SETUSER app resetpass >New#Pass2\n# Persistence: ACL SAVE if an aclfile is defined, otherwise CONFIG REWRITE","note":"resetpass elimina todas las contraseñas existentes; > solo añade una. Para la persistencia, ejecute ACL SAVE si se usa un aclfile, o CONFIG REWRITE si los usuarios están definidos en redis.conf (ACL SAVE devuelve un error sin aclfile).","danger":"caution"}}}]},"rosetta/maintenance.json":{"category":"Mantenimiento y administración","categoryId":"maintenance","order":6,"concepts":[{"id":"update-statistics","title":"Actualizar estadísticas (para el optimizador)","aliases":["update statistics","analyze","update stats","gather stats","optimizer stats","analyze for planner"],"engines":{"oracle":{"command":"EXEC DBMS_STATS.GATHER_TABLE_STATS('HR', 'EMPLOYEE');\n-- Whole schema: EXEC DBMS_STATS.GATHER_SCHEMA_STATS('HR');","note":"La tarea AUTO suele recopilarlas por la noche; recópilelas manualmente cuando surja un problema. Se recomienda ESTIMATE_PERCENT=>AUTO_SAMPLE_SIZE.","danger":"safe"},"postgres":{"command":"ANALYZE customer;\n-- Both cleanup and analyze: VACUUM ANALYZE customer;","note":"Autovacuum lo hace automáticamente; un ANALYZE manual tras una carga de datos repentina da planes precisos.","danger":"safe"},"mysql":{"command":"ANALYZE TABLE customer;","note":"Adquiere un breve bloqueo de lectura en InnoDB; en tablas grandes ejecútelo en momentos de baja actividad.","danger":"safe"},"mssql":{"command":"UPDATE STATISTICS dbo.customer WITH FULLSCAN;\n-- Whole database: EXEC sp_updatestats;","note":"FULLSCAN es el más preciso pero el más costoso; en tablas grandes puede preferirse el muestreo (SAMPLE).","danger":"safe"},"mongodb":{"command":"// There are no manual statistics; the planner measures at runtime.\n// To clear a stale plan cache:\ndb.customer.getPlanCache().clear()","note":"En lugar de estimar costos, MongoDB ejecuta pruebas reales (plan racing); no se necesitan estadísticas manuales.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no query optimizer / statistics.","note":"La complejidad de los comandos es fija; el rendimiento depende de la elección de la estructura de datos.","danger":"safe"}}},{"id":"rebuild-index","title":"Reconstruir / reorganizar un índice","aliases":["rebuild index","reindex","refresh index","reorganize","index maintenance","defrag index"],"engines":{"oracle":{"command":"ALTER INDEX customer_city_idx REBUILD ONLINE;","note":"ONLINE reconstruye sin bloquear la tabla (Enterprise Edition). Para moverlo a otro tablespace: ... REBUILD TABLESPACE new_ts.","danger":"caution"},"postgres":{"command":"REINDEX INDEX CONCURRENTLY customer_city_idx;\n-- Whole table: REINDEX TABLE CONCURRENTLY customer;","note":"CONCURRENTLY (PG 12+) reconstruye sin bloquear las escrituras en la tabla; más lento pero seguro.","danger":"caution"},"mysql":{"command":"-- Rebuild InnoDB indexes + table:\nALTER TABLE customer ENGINE=InnoDB, ALGORITHM=INPLACE, LOCK=NONE;\n-- Alternative: OPTIMIZE TABLE customer;","note":"LOCK=NONE permite escrituras concurrentes; INPLACE puede no ser posible para todos los ALTER.","danger":"caution"},"mssql":{"command":"-- Rebuild for high fragmentation:\nALTER INDEX ALL ON dbo.customer REBUILD WITH (ONLINE = ON);\n-- Lighter option for low fragmentation:\n-- ALTER INDEX ALL ON dbo.customer REORGANIZE;","note":"Umbral habitual: 5-30% de fragmentación REORGANIZE, >30% REBUILD. ONLINE es una función de Enterprise.","danger":"caution"},"mongodb":{"command":"db.customer.reIndex()","note":"Reconstruye todos los índices en primer plano y bloquea la colección. En 6.0+ solo se ejecuta en un standalone (no se permite en miembros de un replica set); en un replica set, reconstruya un índice con dropIndex + createIndex, nodo por nodo (rolling).","danger":"danger"},"redis":{"command":"# Not applicable (core). If RediSearch is used:\n# FT.ALTER / rebuilding is done per the module documentation.","note":"El modelo clave-valor no tiene mantenimiento de índices tradicional.","danger":"safe"}}},{"id":"reclaim-space","title":"Recuperar espacio libre (shrink / vacuum / compact)","aliases":["reclaim space","vacuum","shrink","compact","free up space","clean up bloat","bloat"],"engines":{"oracle":{"command":"ALTER TABLE customer ENABLE ROW MOVEMENT;\nALTER TABLE customer SHRINK SPACE;","note":"SHRINK reduce el high water mark (HWM). Para reducir también los índices, use SHRINK SPACE CASCADE.","danger":"caution"},"postgres":{"command":"-- Online, light locking (does not return space to the OS):\nVACUUM (VERBOSE, ANALYZE) customer;\n-- Return space to the OS (locks the table!):\n-- VACUUM FULL customer;","note":"VACUUM FULL adquiere un AccessExclusiveLock y reescribe la tabla; no lo ejecute en horas pico. Alternativa: pg_repack.","danger":"caution"},"mysql":{"command":"OPTIMIZE TABLE customer;","note":"En InnoDB reconstruye la tabla y libera el espacio data_free; tarda mucho en tablas grandes.","danger":"caution"},"mssql":{"command":"-- Generally NOT RECOMMENDED (causes fragmentation). If really needed:\nDBCC SHRINKFILE (N'MyDB_data', 1024);  -- target 1024 MB","note":"Reducir un archivo de datos provoca fragmentación de índices; haga REBUILD de los índices después. Para el log, haga primero un backup.","danger":"danger"},"mongodb":{"command":"db.runCommand({ compact: \"customer\" })","note":"En WiredTiger intenta devolver el espacio libre al sistema operativo (no siempre es posible). En 4.4+ no bloquea lecturas/escrituras, solo ciertas operaciones DDL sobre la misma colección; como genera mucha E/S, es más seguro ejecutarlo en un secondary y en una ventana de mantenimiento.","danger":"danger"},"redis":{"command":"MEMORY PURGE\n# Tries to return jemalloc fragmentation to the OS","note":"La opción activedefrag yes realiza la desfragmentación automática en línea; MEMORY DOCTOR ofrece recomendaciones.","danger":"caution"}}},{"id":"version-uptime","title":"Mostrar versión y tiempo de actividad","aliases":["version","uptime","server version","how long has it been running","server version","days since startup"],"engines":{"oracle":{"command":"SELECT banner_full FROM v$version;\nSELECT startup_time, ROUND(SYSDATE - startup_time, 1) AS up_days\nFROM v$instance;","note":"v$instance también proporciona detalles de la instancia como status y host_name.","danger":"safe"},"postgres":{"command":"SELECT version();\nSELECT pg_postmaster_start_time() AS started,\n       now() - pg_postmaster_start_time() AS uptime;","note":"server_version_num es útil para comparaciones numéricas (p. ej., 160002 = 16.2).","danger":"safe"},"mysql":{"command":"SELECT VERSION();\nSHOW GLOBAL STATUS LIKE 'Uptime';  -- seconds","note":"Uptime está en segundos; divida entre 86400 para obtener días.","danger":"safe"},"mssql":{"command":"SELECT @@VERSION;\nSELECT sqlserver_start_time,\n       DATEDIFF(DAY, sqlserver_start_time, GETDATE()) AS up_days\nFROM sys.dm_os_sys_info;","note":"SERVERPROPERTY('ProductVersion') y ('Edition') dan detalles de versión/edición.","danger":"safe"},"mongodb":{"command":"db.version()\ndb.serverStatus().uptime   // seconds","note":"serverStatus() también incluye información de host, process y connections.","danger":"safe"},"redis":{"command":"INFO server\n# redis_version and uptime_in_seconds / uptime_in_days lines","note":"INFO server también proporciona información de os, run_id y tcp_port.","danger":"safe"}}},{"id":"show-config-param","title":"Ver un parámetro de configuración","aliases":["show config","parameter","show setting","config parameter","setting","memory setting","variable"],"engines":{"oracle":{"command":"SHOW PARAMETER sga_target;\n-- or: SELECT name, value, isdefault FROM v$parameter WHERE name = 'sga_target';","note":"Para cambiarlo: ALTER SYSTEM SET ... SCOPE=BOTH; (algunos solo con SCOPE=SPFILE + reinicio).","danger":"safe"},"postgres":{"command":"SHOW shared_buffers;\n-- Details: SELECT name, setting, unit, context FROM pg_settings WHERE name = 'shared_buffers';","note":"La columna context indica si el cambio requiere un reinicio o una recarga (SELECT pg_reload_conf()).","danger":"safe"},"mysql":{"command":"SHOW VARIABLES LIKE 'innodb_buffer_pool_size';","note":"Tenga en cuenta la distinción GLOBAL vs SESSION: SHOW GLOBAL VARIABLES muestra el valor a nivel de servidor.","danger":"safe"},"mssql":{"command":"SELECT name, value, value_in_use, is_dynamic\nFROM sys.configurations\nWHERE name LIKE '%max server memory%';","note":"Para cambiarlo: sp_configure + RECONFIGURE; las opciones con is_dynamic=0 requieren un reinicio.","danger":"safe"},"mongodb":{"command":"db.adminCommand({ getParameter: 1, cursorTimeoutMillis: 1 })\n// All startup options: db.adminCommand({ getCmdLineOpts: 1 })","note":"Use setParameter para los que se pueden cambiar en tiempo de ejecución; la configuración persistente está en el archivo mongod.conf.","danger":"safe"},"redis":{"command":"CONFIG GET maxmemory\n# Wildcard: CONFIG GET max*","note":"Use CONFIG SET para cambiar en tiempo de ejecución; CONFIG REWRITE para la persistencia (escribe en redis.conf).","danger":"safe"}}}]},"rosetta/schema.json":{"category":"Esquema y objetos","categoryId":"schema","order":7,"concepts":[{"id":"list-tables","title":"Listar tablas / colecciones","aliases":["list tables","show tables","list all tables","table list","list collections","collections","which tables exist"],"engines":{"oracle":{"command":"SELECT table_name FROM user_tables ORDER BY table_name;\n-- All schemas: SELECT owner, table_name FROM dba_tables ORDER BY owner, table_name;","note":"user_tables devuelve las tablas del esquema actual; all_tables, las accesibles.","danger":"safe"},"postgres":{"command":"SELECT table_schema, table_name\nFROM information_schema.tables\nWHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog','information_schema')\nORDER BY 1, 2;\n-- psql shortcut: \\dt *.*","note":"Solo para el esquema public: WHERE table_schema = 'public'.","danger":"safe"},"mysql":{"command":"SHOW TABLES;\n-- Another schema: SHOW TABLES FROM mydb;","note":"Para más detalle: SELECT table_name, table_rows FROM information_schema.tables WHERE table_schema = DATABASE();","danger":"safe"},"mssql":{"command":"SELECT s.name AS schema_name, t.name AS table_name\nFROM sys.tables t\nJOIN sys.schemas s ON t.schema_id = s.schema_id\nORDER BY s.name, t.name;","note":"Atajo: SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE';","danger":"safe"},"mongodb":{"command":"show collections\n// or: db.getCollectionNames()","note":"Para ver también las colecciones del sistema: db.getCollectionInfos().","danger":"safe"},"redis":{"command":"SCAN 0 MATCH * COUNT 100\n# Continue with SCAN <cursor> ... using the returned cursor","note":"Redis no tiene 'tablas'; se listan claves. NUNCA use KEYS * en producción (bloquea); SCAN se basa en cursor.","danger":"caution"}}},{"id":"describe-table","title":"Mostrar las columnas / estructura de una tabla (describe)","aliases":["describe","desc table","columns","table columns","table structure","show schema","field types","fields"],"engines":{"oracle":{"command":"SELECT column_name, data_type, data_length, nullable\nFROM user_tab_columns\nWHERE table_name = 'EMPLOYEE'\nORDER BY column_id;\n-- Shortcut (SQL*Plus): DESC employee","note":"Los nombres de tabla se almacenan en mayúsculas (si se crearon sin comillas).","danger":"safe"},"postgres":{"command":"SELECT column_name, data_type, is_nullable, column_default\nFROM information_schema.columns\nWHERE table_name = 'customer'\nORDER BY ordinal_position;\n-- psql shortcut: \\d customer","note":"\\d también muestra índices, restricciones y claves foráneas.","danger":"safe"},"mysql":{"command":"DESCRIBE customer;\n-- or: SHOW COLUMNS FROM customer;","note":"Para la definición completa use SHOW CREATE TABLE customer; (vea el concepto 'Generar DDL' más abajo).","danger":"safe"},"mssql":{"command":"SELECT c.name, t.name AS type, c.max_length, c.is_nullable\nFROM sys.columns c\nJOIN sys.types t ON c.user_type_id = t.user_type_id\nWHERE c.object_id = OBJECT_ID('dbo.customer')\nORDER BY c.column_id;\n-- Shortcut: EXEC sp_help 'dbo.customer';","note":"sp_help lista índices y restricciones además de las columnas.","danger":"safe"},"mongodb":{"command":"// The schema is flexible; infer it from a sample document:\ndb.customer.findOne()\n// For field coverage: db.customer.aggregate([{ $sample: { size: 100 } }])","note":"MongoDB no tiene un esquema fijo; los campos pueden variar de un documento a otro. Si hay definido un validador $jsonSchema, se puede ver con db.getCollectionInfos.","danger":"safe"},"redis":{"command":"TYPE mykey        # string | list | set | hash | zset | stream\nOBJECT ENCODING mykey   # internal encoding","note":"No hay esquema fijo; el tipo y la codificación de cada clave se consultan por separado. Use HGETALL para los campos de un hash.","danger":"safe"}}},{"id":"list-indexes","title":"Listar los índices de una tabla","aliases":["list indexes","show indexes","show table indexes","which indexes","table indexes","getindexes"],"engines":{"oracle":{"command":"SELECT index_name, column_name, column_position\nFROM user_ind_columns\nWHERE table_name = 'EMPLOYEE'\nORDER BY index_name, column_position;","note":"Combínela con user_indexes (JOIN) para el tipo de índice/unicidad.","danger":"safe"},"postgres":{"command":"SELECT indexname, indexdef\nFROM pg_indexes\nWHERE tablename = 'customer';","note":"indexdef da la sentencia CREATE INDEX completa; \\d customer en psql también los lista.","danger":"safe"},"mysql":{"command":"SHOW INDEX FROM customer;","note":"Las columnas Non_unique, Seq_in_index y Cardinality muestran la estructura y la selectividad del índice.","danger":"safe"},"mssql":{"command":"EXEC sp_helpindex 'dbo.customer';\n-- Details: join sys.indexes + sys.index_columns","note":"sp_helpindex devuelve nombre, tipo y columnas del índice; use las vistas sys para columnas filtradas/incluidas.","danger":"safe"},"mongodb":{"command":"db.customer.getIndexes()","note":"Devuelve las claves de cada índice y opciones como unicidad y TTL/parcial.","danger":"safe"},"redis":{"command":"# Not applicable (core). If the RediSearch module is present:\n# FT._LIST        (all indexes)\n# FT.INFO idx     (details of one index)","note":"El modelo clave-valor no tiene índices secundarios; solo llegan con módulos como RediSearch/RedisJSON.","danger":"safe"}}},{"id":"create-index","title":"Crear un índice","aliases":["create index","add index","build index","new index","createindex"],"engines":{"oracle":{"command":"CREATE INDEX customer_city_idx ON customer(city) ONLINE;","note":"ONLINE lo crea sin bloquear la tabla. Use CREATE UNIQUE INDEX para índices únicos; (a, b) para compuestos.","danger":"caution"},"postgres":{"command":"CREATE INDEX CONCURRENTLY customer_city_idx ON customer(city);","note":"CONCURRENTLY no bloquea las escrituras en la tabla, pero no puede ejecutarse dentro de un bloque de transacción y es más lento.","danger":"caution"},"mysql":{"command":"CREATE INDEX customer_city_idx ON customer(city);\n-- Lock-free: ... ALGORITHM=INPLACE, LOCK=NONE;","note":"InnoDB añade la mayoría de los índices en línea; en tablas grandes permita escrituras con LOCK=NONE.","danger":"caution"},"mssql":{"command":"CREATE NONCLUSTERED INDEX IX_customer_city\nON dbo.customer (city)\nWITH (ONLINE = ON);","note":"ONLINE es una función de Enterprise. Para columnas de cobertura use INCLUDE (col1, col2).","danger":"caution"},"mongodb":{"command":"db.customer.createIndex({ city: 1 })\n// Unique: { city: 1 }, { unique: true }\n// Built in the background without blocking (default behavior in 4.2+)","note":"1 es orden ascendente, -1 descendente. En colecciones grandes, crearlo primero en un secondary reduce la carga.","danger":"caution"},"redis":{"command":"# Not applicable (core). If RediSearch is present:\n# FT.CREATE idx ON HASH PREFIX 1 customer: SCHEMA city TAG","note":"Los índices secundarios solo llegan con el módulo RediSearch; el acceso en Redis básico es por clave.","danger":"safe"}}},{"id":"show-ddl","title":"Generar el DDL de un objeto (sentencia CREATE)","aliases":["show ddl","get ddl","create statement","generate ddl","table definition","show create table","generate script"],"engines":{"oracle":{"command":"SELECT DBMS_METADATA.GET_DDL('TABLE', 'EMPLOYEE', 'HR') FROM dual;","note":"Use 'INDEX' para índices, 'PACKAGE' para paquetes, etc. Evite que se trunque una salida larga con SET LONG 100000.","danger":"safe"},"postgres":{"command":"-- There is no single function in SQL; from the shell:\npg_dump -t public.customer --schema-only mydb","note":"En psql, \\d customer muestra la estructura de forma legible; para el DDL completo use pg_dump --schema-only.","danger":"safe"},"mysql":{"command":"SHOW CREATE TABLE customer\\G","note":"Devuelve el CREATE TABLE completo (columnas, índices, engine, charset) en un solo comando.","danger":"safe"},"mssql":{"command":"-- T-SQL has no single built-in command. Options:\n-- 1) SSMS: right-click > Script Table as > CREATE To\n-- 2) EXEC sp_help 'dbo.customer';  (structure summary)\n-- 3) mssql-scripter tool (command line)","note":"Para el cuerpo de un procedimiento/vista: SELECT OBJECT_DEFINITION(OBJECT_ID('dbo.usp_name'));","danger":"safe"},"mongodb":{"command":"// There is no DDL (schemaless). View collection options/validator:\ndb.getCollectionInfos({ name: \"customer\" })","note":"Si está definido options.validator ($jsonSchema), es la única 'definición de esquema'; los índices se obtienen por separado con getIndexes().","danger":"safe"},"redis":{"command":"# Not applicable: there is no schema/DDL concept.","note":"Las estructuras de datos se definen en el código de la aplicación; la persistencia está en los archivos RDB/AOF.","danger":"safe"}}}]},"rosetta/replication.json":{"category":"Replicación y HA","categoryId":"replication","order":8,"concepts":[{"id":"replication-status","title":"Mostrar estado y retraso de la replicación","aliases":["replication status","replica lag","replication status","lag","slave lag","standby lag","how far behind"],"engines":{"oracle":{"command":"-- Role and mode:\nSELECT database_role, open_mode FROM v$database;\n-- Data Guard lag:\nSELECT name, value, unit FROM v$dataguard_stats\nWHERE name LIKE '%lag%';","note":"Apply lag y transport lag son las principales métricas de retraso de Data Guard; ejecútelo en el standby.","danger":"safe"},"postgres":{"command":"-- On the PRIMARY:\nSELECT client_addr, state, sync_state,\n       write_lag, flush_lag, replay_lag\nFROM pg_stat_replication;\n-- On the STANDBY:\nSELECT now() - pg_last_xact_replay_timestamp() AS replay_lag;","note":"replay_lag es el retraso de aplicación; sync_state = 'sync' indica una réplica síncrona.","danger":"safe"},"mysql":{"command":"SHOW REPLICA STATUS\\G\n-- Before 8.0.22: SHOW SLAVE STATUS\\G","note":"Seconds_Behind_Source da el retraso; Replica_IO_Running y Replica_SQL_Running deben ser 'Yes'. SHOW SLAVE STATUS es un alias en 8.0.22–8.3 y se eliminó en 8.4.","danger":"safe"},"mssql":{"command":"SELECT ar.replica_server_name, drs.synchronization_state_desc,\n       drs.log_send_queue_size, drs.redo_queue_size\nFROM sys.dm_hadr_database_replica_states drs\nJOIN sys.availability_replicas ar ON drs.replica_id = ar.replica_id;","note":"Para Always On. Un redo_queue_size grande significa que la réplica secundaria se está quedando atrás.","danger":"safe"},"mongodb":{"command":"rs.printSecondaryReplicationInfo()\n// Detailed status: rs.status()","note":"Muestra cuántos segundos va cada secondary por detrás del primary; se comparan los valores de optimeDate.","danger":"safe"},"redis":{"command":"INFO replication\n# Check the role, connected_slaves, master_repl_offset,\n# and slaveN:...,offset=... lines","note":"La diferencia entre master_repl_offset y el offset del slave da el retraso; master_link_status debe ser 'up'.","danger":"safe"}}},{"id":"list-replicas","title":"Listar réplicas / nodos del clúster","aliases":["list replicas","show replicas","list replicas","nodes","cluster members","slaves","standby list"],"engines":{"oracle":{"command":"-- Standby destinations:\nSELECT dest_id, destination, status, target\nFROM v$archive_dest\nWHERE status <> 'INACTIVE';\n-- RAC instances: SELECT inst_id, instance_name, status FROM gv$instance;","note":"La configuración de Data Guard también se puede ver con DGMGRL> SHOW CONFIGURATION.","danger":"safe"},"postgres":{"command":"SELECT application_name, client_addr, state, sync_state\nFROM pg_stat_replication;","note":"Lista las réplicas conectadas solo en el PRIMARY; cada fila es un standby.","danger":"safe"},"mysql":{"command":"SHOW REPLICAS;\n-- Before 8.0.22: SHOW SLAVE HOSTS;","note":"Ejecútelo en el servidor de origen; muestra las réplicas que tienen report-host configurado. SHOW SLAVE HOSTS es un alias en 8.0.22–8.3 y se eliminó en 8.4.","danger":"safe"},"mssql":{"command":"SELECT ar.replica_server_name, ars.role_desc,\n       ars.connected_state_desc, ars.synchronization_health_desc\nFROM sys.dm_hadr_availability_replica_states ars\nJOIN sys.availability_replicas ar ON ars.replica_id = ar.replica_id;","note":"role_desc distingue PRIMARY/SECONDARY; synchronization_health_desc da el estado general.","danger":"safe"},"mongodb":{"command":"rs.status().members.map(m => ({ name: m.name, state: m.stateStr, health: m.health }))","note":"stateStr devuelve PRIMARY/SECONDARY/ARBITER; health 1 significa sano.","danger":"safe"},"redis":{"command":"INFO replication\n# If Sentinel is used:\n# SENTINEL replicas <master-name>","note":"Las líneas slave0, slave1... dan la ip/port/offset de cada réplica.","danger":"safe"}}},{"id":"promote-failover","title":"Promover una réplica / hacer failover (promote)","aliases":["promote","failover","switchover","promote","role change","stepdown","make master","make primary"],"engines":{"oracle":{"command":"-- Data Guard broker (DGMGRL):\nSWITCHOVER TO 'standby_db';     -- planned, no data loss\n-- FAILOVER TO 'standby_db';     -- on loss of the primary, emergency","note":"SWITCHOVER es una transición de roles planificada; FAILOVER se realiza cuando el primary no está accesible y conlleva riesgo de pérdida de datos.","danger":"danger"},"postgres":{"command":"-- Promote the standby to primary:\nSELECT pg_promote();\n-- or from the shell: pg_ctl promote -D /var/lib/pgsql/data","note":"Antes de promover, asegúrese de que el antiguo primary esté realmente detenido (riesgo de split-brain). Tras la promoción, reincorporar el antiguo primary como réplica del nuevo primary requiere pg_rewind; para ello debe estar habilitado wal_log_hints = on o los data checksums.","danger":"danger"},"mysql":{"command":"-- On the replica (manual):\n-- 1) Stop receiving new events, wait until the received relay log is fully applied:\nSTOP REPLICA IO_THREAD;\nSELECT RECEIVED_TRANSACTION_SET FROM performance_schema.replication_connection_status;\nSELECT @@GLOBAL.gtid_executed;  -- the received set must be fully contained here\n-- 2) Remove replication and enable writes:\nSTOP REPLICA;\nRESET REPLICA ALL;\nSET GLOBAL read_only = OFF;  -- also turns off super_read_only\n-- 3) Fence the old source against writes, then redirect the application.","note":"Si se ejecuta RESET REPLICA ALL antes de que el relay log esté totalmente aplicado, se pierden las transacciones recibidas pero no aplicadas. Las réplicas suelen ejecutarse con read_only/super_read_only habilitado; no se aceptan escrituras hasta que se desactiven. Si el antiguo origen sigue admitiendo escrituras, hay riesgo de split-brain. La sintaxis es de 8.0.22+ (antes STOP SLAVE / RESET SLAVE ALL; la forma antigua se eliminó en 8.4). Para un failover automático y seguro en producción, prefiera InnoDB Cluster/Group Replication, Orchestrator o MHA.","danger":"danger"},"mssql":{"command":"-- On the target secondary replica (synchronous):\nALTER AVAILABILITY GROUP [AG_Name] FAILOVER;\n-- Force it, with risk of data loss:\n-- ALTER AVAILABILITY GROUP [AG_Name] FORCE_FAILOVER_ALLOW_DATA_LOSS;","note":"Un failover planificado a una réplica síncrona no tiene pérdida de datos; FORCE solo se usa en un escenario de desastre.","danger":"danger"},"mongodb":{"command":"// Trigger an election on the current primary:\nrs.stepDown(60)   // step down from primary for 60 s","note":"stepDown inicia una nueva elección; para favorecer un nodo concreto, ajuste los valores de priority de los miembros.","danger":"danger"},"redis":{"command":"# Make the replica an independent primary:\nREPLICAOF NO ONE\n# With Sentinel (recommended): SENTINEL FAILOVER <master-name>","note":"Si usa Sentinel/Cluster, use SENTINEL FAILOVER en lugar de un REPLICAOF manual; de lo contrario hay riesgo de split-brain.","danger":"danger"}}},{"id":"open-transactions","title":"Encontrar transacciones abiertas / de larga duración","aliases":["open transactions","long running transaction","open transaction","long transaction","idle in transaction","oldest transaction","undo"],"engines":{"oracle":{"command":"SELECT s.sid, s.serial#, s.username,\n       t.start_time, t.used_ublk AS undo_blocks\nFROM v$transaction t\nJOIN v$session s ON t.ses_addr = s.saddr\nORDER BY t.start_time;","note":"Un used_ublk alto significa que una transacción grande está consumiendo mucho undo; puede estar esperando un commit/rollback.","danger":"safe"},"postgres":{"command":"SELECT pid, usename, state,\n       now() - xact_start AS xact_age, query\nFROM pg_stat_activity\nWHERE xact_start IS NOT NULL\nORDER BY xact_start;","note":"Las transacciones largas en estado 'idle in transaction' bloquean VACUUM y la limpieza de tuplas; vigílelas.","danger":"safe"},"mysql":{"command":"SELECT trx_id, trx_state, trx_started,\n       TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS age_sec,\n       trx_mysql_thread_id\nFROM information_schema.innodb_trx\nORDER BY trx_started;","note":"Use trx_mysql_thread_id para correlacionar con PROCESSLIST y hacer KILL si es necesario.","danger":"safe"},"mssql":{"command":"DBCC OPENTRAN;\n-- Detailed:\nSELECT st.session_id, dt.database_transaction_begin_time\nFROM sys.dm_tran_database_transactions dt\nJOIN sys.dm_tran_session_transactions st ON st.transaction_id = dt.transaction_id\nWHERE dt.database_id = DB_ID()\nORDER BY dt.database_transaction_begin_time;","note":"Las transacciones abiertas largas impiden el truncado del log y hacen crecer el archivo de log.","danger":"safe"},"mongodb":{"command":"db.adminCommand({\n  currentOp: 1,\n  \"transaction\": { $exists: true }\n})","note":"Las transacciones multidocumento largas se abortan automáticamente tras transactionLifetimeLimitSeconds (por defecto 60 s).","danger":"safe"},"redis":{"command":"# Not applicable: MULTI/EXEC transactions are short and atomic; they do not stay open.","note":"Las transacciones de Redis no mantienen bloqueos; para comandos individuales de larga duración, revise SLOWLOG.","danger":"safe"}}}]},"rosetta/connect.json":{"category":"Conexión y CLI","categoryId":"connect","order":9,"concepts":[{"id":"connect-cli","title":"Conectarse desde la línea de comandos (CLI / cadena de conexión)","aliases":["connect","connection string","connect","how do i connect","cli connect","login","dsn","uri"],"engines":{"oracle":{"command":"sqlplus app_user/password@//host:1521/service_name\n# As SYSDBA: sqlplus sys/password@//host:1521/orcl AS SYSDBA","note":"//host:port/service es el formato 'Easy Connect'; como alternativa, use un alias TNS de tnsnames.ora.","danger":"safe"},"postgres":{"command":"psql -h host -p 5432 -U postgres -d mydb\n# With a URI: psql \"postgresql://app_user:password@host:5432/mydb?sslmode=require\"","note":"Para no escribir la contraseña en la línea de comandos, use un archivo ~/.pgpass o la variable de entorno PGPASSWORD.","danger":"safe"},"mysql":{"command":"mysql -h host -P 3306 -u root -p mydb\n# Require TLS: --ssl-mode=REQUIRED","note":"Pase -p solo (se le pide la contraseña); la forma 'mysql -pPassword' expone la contraseña en la lista de procesos.","danger":"safe"},"mssql":{"command":"sqlcmd -S host,1433 -U sa -P password -d mydb\n# Windows authentication: sqlcmd -S host -E -d mydb","note":"En el go-sqlcmd más reciente, pueden ser necesarias las opciones -C (confiar en el certificado del servidor) y -N (cifrar) para TLS.","danger":"safe"},"mongodb":{"command":"mongosh \"mongodb://app_user:password@host:27017/mydb?authSource=admin\"\n# Replica set: ...mongodb://h1,h2,h3/db?replicaSet=rs0","note":"authSource suele ser la base de datos donde está definido el usuario (casi siempre admin).","danger":"safe"},"redis":{"command":"redis-cli -h host -p 6379 -a password\n# With TLS: redis-cli -h host -p 6379 --tls -a password\n# Redis 6+ ACL: redis-cli -h host --user app --pass password","note":"-a pasa la contraseña en la línea de comandos (muestra una advertencia); para un inicio de sesión seguro use la variable de entorno REDISCLI_AUTH.","danger":"safe"}}},{"id":"list-databases","title":"Listar bases de datos / esquemas","aliases":["list databases","show databases","list databases","which databases","list schemas","pdbs","show dbs"],"engines":{"oracle":{"command":"-- Schemas (users):\nSELECT username FROM dba_users ORDER BY username;\n-- Multitenant (CDB) PDBs:\nSELECT name, open_mode FROM v$pdbs;","note":"En Oracle una 'base de datos' corresponde a la instancia; la separación se hace a nivel de esquema (usuario) o a nivel de PDB dentro de una CDB.","danger":"safe"},"postgres":{"command":"SELECT datname FROM pg_database WHERE datistemplate = false;\n-- psql shortcut: \\l","note":"Para esquemas: SELECT schema_name FROM information_schema.schemata;","danger":"safe"},"mysql":{"command":"SHOW DATABASES;","note":"En MySQL, 'database' y 'schema' son sinónimos.","danger":"safe"},"mssql":{"command":"SELECT name, state_desc FROM sys.databases ORDER BY name;","note":"state_desc muestra el estado de la base de datos, como ONLINE/OFFLINE/RESTORING.","danger":"safe"},"mongodb":{"command":"show dbs\n// or: db.adminCommand({ listDatabases: 1 })","note":"Las bases de datos vacías (sin datos escritos aún) no aparecen en la lista.","danger":"safe"},"redis":{"command":"INFO keyspace\n# db0:keys=..., db1:keys=... lines show the logical DBs in use","note":"Redis tiene DB lógicas numeradas (por defecto 0-15); en modo Cluster solo se usa la DB 0.","danger":"safe"}}},{"id":"switch-database","title":"Cambiar la base de datos / esquema activo","aliases":["switch database","use database","change database","switch","change schema","change schema","use db"],"engines":{"oracle":{"command":"-- Change the default schema:\nALTER SESSION SET CURRENT_SCHEMA = hr;\n-- Switch to a PDB (inside a CDB):\nALTER SESSION SET CONTAINER = pdb1;","note":"CURRENT_SCHEMA solo cambia la resolución de nombres de objetos, no los privilegios.","danger":"safe"},"postgres":{"command":"-- Switch to another DB inside psql (reconnects):\n\\c mydb\n-- Schema search path (same connection):\nSET search_path TO hr, public;","note":"En PostgreSQL una conexión está ligada a una única base de datos; cambiar de BD implica volver a conectarse.","danger":"safe"},"mysql":{"command":"USE mydb;","note":"Cambia el esquema activo sin cerrar la conexión.","danger":"safe"},"mssql":{"command":"USE mydb;","note":"Cambia el contexto del lote; los comandos siguientes se ejecutan en esta base de datos.","danger":"safe"},"mongodb":{"command":"use mydb","note":"Cambia aunque la base de datos no exista; se crea realmente cuando se escribe el primer documento.","danger":"safe"},"redis":{"command":"SELECT 1\n# Switches to a logical DB index between 0 and 15","note":"En modo Cluster SELECT solo admite 0; use prefijos de clave en lugar de separar por DB lógicas.","danger":"safe"}}}]},"rosetta/objects.json":{"category":"Gestión de objetos","categoryId":"objects","order":10,"concepts":[{"id":"create-partition","title":"Crear / dividir / eliminar una partición","aliases":["partition","partition","split","split partition","drop partition","add partition","remove partition"],"engines":{"oracle":{"command":"-- Add a RANGE partition\nALTER TABLE sales ADD PARTITION p2026 VALUES LESS THAN (TO_DATE('2027-01-01','YYYY-MM-DD'));\n\n-- Split an existing partition\nALTER TABLE sales SPLIT PARTITION pmax AT (TO_DATE('2027-01-01','YYYY-MM-DD')) INTO (PARTITION p2026, PARTITION pmax);\n\n-- Drop a partition\nALTER TABLE sales DROP PARTITION p2020 UPDATE INDEXES;","note":"Admite particionamiento RANGE/LIST/HASH. DROP PARTITION elimina todas las filas de esa partición; con UPDATE INDEXES, los índices globales siguen siendo válidos. El particionamiento por intervalos puede añadir particiones automáticamente.","danger":"caution","priv":"ALTER sobre la tabla","minver":"Partitioning Option"},"postgres":{"command":"-- Declarative partitioning: parent table\nCREATE TABLE sales (id bigint, created_at date, amount numeric) PARTITION BY RANGE (created_at);\n\n-- Add a new partition\nCREATE TABLE sales_2026 PARTITION OF sales FOR VALUES FROM ('2026-01-01') TO ('2027-01-01');\n\n-- Attach a standalone (existing) table as a partition\nCREATE TABLE sales_2027 (LIKE sales INCLUDING ALL);\nALTER TABLE sales ATTACH PARTITION sales_2027 FOR VALUES FROM ('2027-01-01') TO ('2028-01-01');\n\n-- Detach a partition (the table is not dropped; it remains a standalone table)\nALTER TABLE sales DETACH PARTITION sales_2026;","note":"PostgreSQL básico no tiene SPLIT/MERGE PARTITION (a fecha de PG17); para dividir, haga DETACH de la partición, cree dos particiones con los nuevos límites, mueva los datos y haga ATTACH. DETACH ... CONCURRENTLY (PG14+) reduce la duración del bloqueo.","danger":"caution","priv":"propietario de la tabla","minver":"PG10"},"mysql":{"command":"-- Partitioned table\nCREATE TABLE sales (id BIGINT, created_at DATE) PARTITION BY RANGE (YEAR(created_at)) (\n  PARTITION p2024 VALUES LESS THAN (2025),\n  PARTITION p2025 VALUES LESS THAN (2026),\n  PARTITION pmax VALUES LESS THAN MAXVALUE\n);\n\n-- Add / split / drop a partition\nALTER TABLE sales REORGANIZE PARTITION pmax INTO (\n  PARTITION p2026 VALUES LESS THAN (2027),\n  PARTITION pmax VALUES LESS THAN MAXVALUE\n);\nALTER TABLE sales DROP PARTITION p2024;","note":"La columna de particionamiento debe formar parte de toda PRIMARY KEY / clave UNIQUE. Para RANGE, la división/fusión se hace con REORGANIZE PARTITION. DROP PARTITION también elimina las filas (RANGE/LIST).","danger":"caution","priv":"ALTER, DROP sobre la tabla","minver":"5.7"},"mssql":{"command":"-- 1) Partition function and scheme\nCREATE PARTITION FUNCTION pf_date (date) AS RANGE RIGHT FOR VALUES ('2025-01-01','2026-01-01');\nCREATE PARTITION SCHEME ps_date AS PARTITION pf_date ALL TO ([PRIMARY]);\n\n-- 2) Add a new boundary (split)\nALTER PARTITION SCHEME ps_date NEXT USED [PRIMARY];\nALTER PARTITION FUNCTION pf_date() SPLIT RANGE ('2027-01-01');\n\n-- 3) Remove a boundary (merge)\nALTER PARTITION FUNCTION pf_date() MERGE RANGE ('2025-01-01');","note":"Se definen una partition function + scheme y la tabla se crea sobre ese scheme con CREATE TABLE ... ON ps_date(created_at). El filegroup NEXT USED debe establecerse antes de SPLIT. Para retirar datos, muévalos a una tabla de staging con SWITCH PARTITION.","danger":"caution","priv":"ALTER sobre la tabla, ALTER ANY DATASPACE","minver":"2016 SP1 (todas las ediciones)"},"mongodb":{"command":"// MongoDB uses sharding instead of table partitioning\nsh.shardCollection(\"db.sales\", { created_at: 1 });\n\n// Native time-series collection for time series\ndb.createCollection(\"measurements\", { timeseries: { timeField: \"ts\", granularity: \"hours\" } });","note":"No hay particionamiento en el sentido clásico. La división horizontal se hace mediante chunks con sharding; para datos temporales, las time-series collections proporcionan bucketing automático.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no table/partition concept; scaling is done with Redis Cluster hash slots (16384).","note":"Redis Cluster distribuye las claves entre 16384 hash slots; es una división a nivel de aplicación, no particionamiento de tablas. No existe un comando de partición manual.","danger":"safe"},"elasticsearch":{"command":"# Time-based automatic splitting (rollover) with an ILM policy + data stream\nPUT _ilm/policy/logs-policy\n{ \"policy\": { \"phases\": { \"hot\": { \"actions\": { \"rollover\": { \"max_size\": \"50gb\", \"max_age\": \"30d\" } } } } } }\n\nPUT _index_template/logs-tmpl\n{ \"index_patterns\": [\"logs*\"], \"data_stream\": {}, \"template\": { \"settings\": { \"index.lifecycle.name\": \"logs-policy\" } } }","note":"En Elasticsearch, la división se logra repartiendo un índice en shards y, para series temporales, creando nuevos backing indices con ILM + data streams (rollover). Los índices antiguos se pueden eliminar con ILM.","danger":"safe"}}},{"id":"sequence","title":"Crear una secuencia / valor autoincremental","aliases":["sequence","sequence","auto increment","identity","auto-increment","nextval","serial"],"engines":{"oracle":{"command":"-- Sequence\nCREATE SEQUENCE customer_seq START WITH 1 INCREMENT BY 1 NOCACHE;\nSELECT customer_seq.NEXTVAL FROM dual;\n\n-- or an identity column (12c+)\nCREATE TABLE customer (id NUMBER GENERATED ALWAYS AS IDENTITY, name VARCHAR2(100));","note":"El método clásico es CREATE SEQUENCE + NEXTVAL/CURRVAL. Desde 12c también se admiten columnas GENERATED AS IDENTITY. Un valor de CACHE mejora el rendimiento, pero pueden producirse huecos.","danger":"safe","priv":"CREATE SEQUENCE","minver":"12c para IDENTITY"},"postgres":{"command":"-- Sequence\nCREATE SEQUENCE customer_seq START 1 INCREMENT 1;\nSELECT nextval('customer_seq');\n\n-- Modern method: identity column\nCREATE TABLE customer (id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name text);\n-- Classic: serial (creates a sequence behind the scenes)\nCREATE TABLE logs (id serial PRIMARY KEY, message text);","note":"GENERATED ... AS IDENTITY es estándar SQL y la opción recomendada; serial es la abreviatura heredada. Acceda manualmente con nextval/currval. El valor se puede restablecer con setval.","danger":"safe","priv":"CREATE sobre el esquema","minver":"PG10 para IDENTITY"},"mysql":{"command":"-- AUTO_INCREMENT column\nCREATE TABLE customer (id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100));\n\n-- Set the starting value\nALTER TABLE customer AUTO_INCREMENT = 1000;\n\n-- Get the last inserted value\nSELECT LAST_INSERT_ID();","note":"MySQL no tiene un objeto de secuencia independiente (estándar); se usa una columna AUTO_INCREMENT (una por tabla, debe estar indexada). MariaDB 10.3+ admite CREATE SEQUENCE.","danger":"safe","priv":"CREATE, ALTER sobre la tabla"},"mssql":{"command":"-- IDENTITY column\nCREATE TABLE customer (id INT IDENTITY(1,1) PRIMARY KEY, name NVARCHAR(100));\n\n-- Standalone sequence object\nCREATE SEQUENCE customer_seq AS INT START WITH 1 INCREMENT BY 1;\nSELECT NEXT VALUE FOR customer_seq;","note":"IDENTITY(seed,increment) está ligado a una columna de tabla. CREATE SEQUENCE (2012+) proporciona un contador independiente de las tablas que puede compartirse entre tablas; los valores se obtienen con NEXT VALUE FOR.","danger":"safe","priv":"CREATE SEQUENCE / ALTER sobre la tabla","minver":"2012 para SEQUENCE"},"mongodb":{"command":"// MongoDB has no auto counter; counter pattern with findOneAndUpdate\ndb.counters.findOneAndUpdate(\n  { _id: \"customer\" },\n  { $inc: { seq: 1 } },\n  { upsert: true, returnDocument: \"after\" }\n);","note":"El _id por defecto es un ObjectId (ya único y aproximadamente creciente). Si se necesita un entero secuencial, genérelo de forma atómica con $inc en una colección de contadores aparte.","danger":"safe"},"redis":{"command":"# Atomic counter\nINCR customer:id\n# or with a specific step\nINCRBY customer:id 10","note":"INCR/INCRBY son atómicos e ideales para generar ID únicos crecientes en entornos distribuidos. Si la clave no existe, empieza desde 0.","danger":"safe"},"elasticsearch":{"command":"# Not applicable: Elasticsearch does not offer a sequence object. Document IDs are assigned automatically;\n# the internal _seq_no and _primary_term fields are used for concurrency control.\nPOST logs/_doc\n{ \"message\": \"sample\" }","note":"Si hace POST sin ID, ES asigna un _id único aleatorio (no creciente). _seq_no es un número de secuencia interno que aumenta con cada escritura, pero sirve para la concurrencia optimista, no como contador de aplicación.","danger":"safe"}}},{"id":"add-constraint","title":"Añadir o deshabilitar una restricción (FK/CHECK/UNIQUE)","aliases":["constraint","constraint","foreign key","check","unique","foreign key","disable constraint"],"engines":{"oracle":{"command":"-- Add a foreign key\nALTER TABLE orders ADD CONSTRAINT fk_customer FOREIGN KEY (customer_id) REFERENCES customer(id);\n-- CHECK\nALTER TABLE product ADD CONSTRAINT chk_price CHECK (price >= 0);\n-- Disable / enable\nALTER TABLE orders DISABLE CONSTRAINT fk_customer;\nALTER TABLE orders ENABLE CONSTRAINT fk_customer;","note":"Las restricciones de Oracle se pueden desactivar temporalmente con DISABLE/ENABLE (útil para cargas masivas). ENABLE VALIDATE comprueba las filas existentes; NOVALIDATE no.","danger":"caution","priv":"ALTER sobre la tabla, REFERENCES sobre el destino"},"postgres":{"command":"-- Foreign key and CHECK\nALTER TABLE orders ADD CONSTRAINT fk_customer FOREIGN KEY (customer_id) REFERENCES customer(id);\nALTER TABLE product ADD CONSTRAINT chk_price CHECK (price >= 0);\n\n-- Instead of disabling the FK: NOT VALID + validate later\nALTER TABLE orders ADD CONSTRAINT fk2 FOREIGN KEY (c_id) REFERENCES customer(id) NOT VALID;\nALTER TABLE orders VALIDATE CONSTRAINT fk2;\nALTER TABLE orders DROP CONSTRAINT fk2;","note":"PostgreSQL no tiene DISABLE de restricciones. Para reducir el bloqueo en una tabla grande, añada la restricción con NOT VALID y haga VALIDATE más tarde. ALTER TABLE ... DISABLE TRIGGER ALL también deshabilita los triggers internos de la FK y requiere superuser; los datos insertados mientras están desactivados nunca se validan, y volver a habilitarlos no comprueba de forma retroactiva (riesgo de pérdida silenciosa de integridad). En su lugar, haga DROP de la restricción, vuelva a añadirla con NOT VALID y luego ejecute VALIDATE CONSTRAINT.","danger":"caution","priv":"propietario de la tabla, REFERENCES sobre el destino"},"mysql":{"command":"-- Foreign key and CHECK\nALTER TABLE orders ADD CONSTRAINT fk_customer FOREIGN KEY (customer_id) REFERENCES customer(id);\nALTER TABLE product ADD CONSTRAINT chk_price CHECK (price >= 0);\n\n-- Temporarily disable FK checks for the session\nSET FOREIGN_KEY_CHECKS = 0;\n-- ... bulk operation ...\nSET FOREIGN_KEY_CHECKS = 1;","note":"Requiere InnoDB. En lugar de deshabilitar una restricción concreta, se usa SET FOREIGN_KEY_CHECKS=0 a nivel de sesión. Las restricciones CHECK se aplican realmente a partir de MySQL 8.0.16 (antes se ignoraban).","danger":"caution","priv":"ALTER, REFERENCES sobre la tabla","minver":"8.0.16 para CHECK"},"mssql":{"command":"-- Foreign key and CHECK\nALTER TABLE orders ADD CONSTRAINT fk_customer FOREIGN KEY (customer_id) REFERENCES customer(id);\nALTER TABLE product ADD CONSTRAINT chk_price CHECK (price >= 0);\n\n-- Disable / re-enable (validate with WITH CHECK)\nALTER TABLE orders NOCHECK CONSTRAINT fk_customer;\nALTER TABLE orders WITH CHECK CHECK CONSTRAINT fk_customer;","note":"En SQL Server, las restricciones FK/CHECK se deshabilitan con NOCHECK CONSTRAINT. Si no se usa WITH CHECK CHECK CONSTRAINT al volver a habilitarlas, la restricción queda como 'not trusted' y el optimizador de consultas no puede usarla.","danger":"caution","priv":"ALTER sobre la tabla, REFERENCES sobre el destino"},"mongodb":{"command":"// Constraint-like checks with schema validation rules\ndb.runCommand({ collMod: \"product\",\n  validator: { $jsonSchema: { required: [\"price\"], properties: { price: { bsonType: \"number\", minimum: 0 } } } },\n  validationLevel: \"strict\"\n});\n// Unique index for uniqueness\ndb.customer.createIndex({ email: 1 }, { unique: true });","note":"No hay claves foráneas (se aplican en la capa de aplicación). El equivalente de UNIQUE es un índice único. En lugar de CHECK, use la validación $jsonSchema; validationAction: 'warn' logra el efecto de deshabilitarla.","danger":"caution"},"redis":{"command":"# Not applicable: Redis has no FK/CHECK/UNIQUE constraints. Uniqueness is enforced at the application level with SET or\n# SETNX/conditional writes.\nSET customer:email:sample@x.com 1 NX","note":"No existe el concepto de integridad relacional ni de restricciones. La unicidad se gestiona en la aplicación con SET ... NX (escribir solo si no existe) o comprobando un SET/HASH aparte.","danger":"safe"},"elasticsearch":{"command":"# Not applicable: Elasticsearch has no FK/CHECK. Field types are enforced with the mapping,\n# and uniqueness with a fixed _id.\nPUT product/_doc/ABC-123\n{ \"price\": 100 }","note":"El motor no tiene relaciones ni restricciones. La comprobación de tipos se hace con el mapping. Para un efecto UNIQUE, use la clave natural como _id (las escrituras sobre el mismo _id sobrescriben) o rechace los conflictos con op_type=create.","danger":"safe"}}},{"id":"create-view","title":"Crear / modificar una vista","aliases":["view","view","create view","alter view","or replace"],"engines":{"oracle":{"command":"CREATE OR REPLACE VIEW active_customer AS\nSELECT id, name, email FROM customer WHERE status = 'ACTIVE';\n\n-- Drop\nDROP VIEW active_customer;","note":"CREATE OR REPLACE VIEW sirve tanto para crear como para modificar. Para vistas actualizables bajo ciertas condiciones, se puede añadir WITH CHECK OPTION.","danger":"safe","readonly":true,"priv":"CREATE VIEW"},"postgres":{"command":"CREATE OR REPLACE VIEW active_customer AS\nSELECT id, name, email FROM customer WHERE status = 'ACTIVE';\n\nDROP VIEW active_customer;","note":"Con CREATE OR REPLACE solo se pueden añadir columnas nuevas al final; cambiar el tipo/orden de una columna requiere DROP primero. Las vistas simples son actualizables automáticamente.","danger":"safe","readonly":true,"priv":"CREATE sobre el esquema"},"mysql":{"command":"CREATE OR REPLACE VIEW active_customer AS\nSELECT id, name, email FROM customer WHERE status = 'ACTIVE';\n\n-- Modify\nALTER VIEW active_customer AS SELECT id, name FROM customer;\nDROP VIEW active_customer;","note":"La definición se actualiza con CREATE OR REPLACE VIEW o ALTER VIEW. El método de ejecución se puede establecer con ALGORITHM = MERGE/TEMPTABLE; WITH CHECK OPTION protege las vistas actualizables.","danger":"safe","readonly":true,"priv":"CREATE VIEW sobre la tabla"},"mssql":{"command":"CREATE OR ALTER VIEW active_customer AS\nSELECT id, name, email FROM customer WHERE status = 'ACTIVE';\n\nDROP VIEW active_customer;","note":"CREATE OR ALTER VIEW (2016 SP1+) crea/modifica en una sola sentencia. En versiones anteriores, use primero CREATE y luego ALTER VIEW. La sentencia de la vista debe ser la primera del lote.","danger":"safe","readonly":true,"priv":"CREATE VIEW","minver":"2016 SP1 para OR ALTER"},"mongodb":{"command":"// Read-only view (on top of an aggregation pipeline)\ndb.createView(\"active_customer\", \"customer\", [ { $match: { status: \"ACTIVE\" } }, { $project: { name: 1, email: 1 } } ]);\n\ndb.active_customer.drop();","note":"db.createView crea una vista de solo lectura sobre una colección de origen y un aggregation pipeline. No se puede escribir en las vistas; para cambiar la definición, elimínela y vuelva a crearla (o use collMod).","danger":"safe","readonly":true},"redis":{"command":"# Not applicable: Redis has no view concept; derived views are provided on the application\n# side or with RedisJSON/RediSearch indexes.","note":"No se admiten vistas definidas sobre consultas. Para una necesidad similar, mantenga claves precalculadas o cree un índice consultable con RediSearch.","danger":"safe"},"elasticsearch":{"command":"# Filtered logical view with an alias\nPOST _aliases\n{ \"actions\": [ { \"add\": { \"index\": \"customer\", \"alias\": \"active_customer\",\n  \"filter\": { \"term\": { \"status\": \"ACTIVE\" } } } } ] }","note":"No hay vista SQL clásica; un alias filtrado es el equivalente más cercano y aplica el filtro a las consultas automáticamente. Para seleccionar columnas, use el filtrado de _source en la consulta.","danger":"safe","readonly":true}}},{"id":"materialized-view","title":"Crear y refrescar una vista materializada","aliases":["materialized view","mview","refresh","refresh","cached view","materialize"],"engines":{"oracle":{"command":"-- FAST refresh first requires an MV log on the source table:\nCREATE MATERIALIZED VIEW LOG ON sales\n  WITH ROWID, SEQUENCE (region, amount) INCLUDING NEW VALUES;\n\nCREATE MATERIALIZED VIEW mv_revenue\nBUILD IMMEDIATE REFRESH FAST ON COMMIT\nAS SELECT region, SUM(amount) total, COUNT(*) cnt, COUNT(amount) amount_cnt\n   FROM sales GROUP BY region;\n\n-- Manual refresh\nEXEC DBMS_MVIEW.REFRESH('MV_REVENUE', 'C');","note":"REFRESH FAST requiere un materialized view log; COMPLETE ('C') recalcula por completo, FAST ('F') es incremental. ON COMMIT/ON DEMAND determina cuándo se produce el refresco.","danger":"caution","priv":"CREATE MATERIALIZED VIEW"},"postgres":{"command":"CREATE MATERIALIZED VIEW mv_revenue AS\nSELECT region, SUM(amount) AS total FROM sales GROUP BY region;\n\n-- Refresh (CONCURRENTLY requires a unique index)\nCREATE UNIQUE INDEX ON mv_revenue (region);\nREFRESH MATERIALIZED VIEW CONCURRENTLY mv_revenue;","note":"REFRESH lo recalcula todo (no es incremental). CONCURRENTLY no bloquea las lecturas, pero requiere un índice único y es más lento.","danger":"caution","priv":"propietario de la tabla","minver":"PG9.3 (CONCURRENTLY 9.4)"},"mysql":{"command":"-- MySQL/MariaDB has no built-in materialized views.\n-- Approach: summary table + refresh with a trigger or a scheduled event\nCREATE TABLE mv_revenue (region VARCHAR(50) PRIMARY KEY, total DECIMAL(18,2));\n\nCREATE EVENT ev_mv_revenue ON SCHEDULE EVERY 1 HOUR DO\n  REPLACE INTO mv_revenue SELECT region, SUM(amount) FROM sales GROUP BY region;","note":"No hay soporte nativo de vistas materializadas. Se mantiene una tabla resumen que se vuelve a poblar al instante con triggers o periódicamente con el EVENT SCHEDULER.","danger":"caution","priv":"CREATE, EVENT sobre el esquema"},"mssql":{"command":"-- Indexed view (materialized view equivalent)\nCREATE VIEW dbo.mv_revenue WITH SCHEMABINDING AS\nSELECT region, SUM(ISNULL(amount, 0)) AS total, COUNT_BIG(*) AS cnt\nFROM dbo.sales GROUP BY region;\nGO\nCREATE UNIQUE CLUSTERED INDEX ix_mv_revenue ON dbo.mv_revenue (region);","note":"SQL Server usa vistas indexadas: WITH SCHEMABINDING es obligatorio; una vez creado un índice clustered único, los datos se almacenan físicamente y se mantienen automáticamente (no hace falta un REFRESH aparte). Con GROUP BY se requiere COUNT_BIG(*); si la columna dentro de SUM admite nulos, escriba SUM(ISNULL(amount, 0)) (de lo contrario, error 8662). La vista debe crearse con QUOTED_IDENTIFIER y ANSI_NULLS ON. La coincidencia automática de vistas por parte del optimizador de consultas solo está disponible en la edición Enterprise (y Developer); en Standard, haga referencia a la vista con el hint WITH (NOEXPAND).","danger":"caution","priv":"CREATE VIEW, ALTER sobre el esquema"},"mongodb":{"command":"// On-demand materialized view: write the result to a collection with $merge\ndb.sales.aggregate([\n  { $group: { _id: \"$region\", total: { $sum: \"$amount\" } } },\n  { $merge: { into: \"mv_revenue\", whenMatched: \"replace\", whenNotMatched: \"insert\" } }\n]);","note":"Se obtiene una vista materializada bajo demanda escribiendo el resultado de la agregación en una colección persistente con $out o $merge. Para refrescarla, vuelva a ejecutar el pipeline (manualmente o con un planificador).","danger":"caution"},"redis":{"command":"# Not applicable: Redis has no materialized views. The computed result is written to a key\n# and cached with a TTL.\nSET mv:revenue:region1 12345 EX 3600","note":"No existe el concepto de vista materializada; lo más parecido es mantener valores precalculados en claves (String/Hash/Sorted Set) y cachearlos durante un tiempo limitado con EX/TTL.","danger":"safe"},"elasticsearch":{"command":"# Continuous/materialized summary index with a transform\nPUT _transform/mv_revenue\n{ \"source\": { \"index\": \"sales\" },\n  \"dest\": { \"index\": \"mv_revenue\" },\n  \"pivot\": { \"group_by\": { \"region\": { \"terms\": { \"field\": \"region\" } } },\n            \"aggregations\": { \"total\": { \"sum\": { \"field\": \"amount\" } } } } }\nPOST _transform/mv_revenue/_start","note":"Un transform pivota el índice de origen y lo materializa en un índice de destino resumido. Este ejemplo es un batch transform: se ejecuta una vez con _start y se detiene. Para un transform continuo que se actualice automáticamente al llegar nuevos datos, añada \"sync\": { \"time\": { \"field\": \"<time_field>\", \"delay\": \"60s\" } } con un campo de tiempo a la configuración (no existe una opción continuous: true).","danger":"caution"}}},{"id":"rename-object","title":"Renombrar una tabla / objeto","aliases":["rename","rename","change name","rename table","sp_rename"],"engines":{"oracle":{"command":"-- Table\nALTER TABLE old_name RENAME TO new_name;\n-- or\nRENAME old_name TO new_name;\n-- Column\nALTER TABLE customer RENAME COLUMN name TO full_name;","note":"Para tablas/vistas/secuencias se usa RENAME o ALTER ... RENAME TO. Los objetos dependientes (vistas, sinónimos) pueden quedar inválidos y tener que recompilarse.","danger":"caution","priv":"ALTER sobre el objeto"},"postgres":{"command":"ALTER TABLE old_name RENAME TO new_name;\nALTER TABLE customer RENAME COLUMN name TO full_name;\nALTER VIEW old_view RENAME TO new_view;","note":"ALTER ... RENAME TO funciona para la mayoría de tipos de objeto. Las referencias de claves foráneas e índices se conservan automáticamente aunque cambie el nombre (basado en OID).","danger":"caution","priv":"propietario de la tabla"},"mysql":{"command":"RENAME TABLE old_name TO new_name;\n-- or\nALTER TABLE old_name RENAME TO new_name;\n-- Column (8.0+)\nALTER TABLE customer RENAME COLUMN name TO full_name;","note":"RENAME TABLE es atómico y puede mover varias tablas en una sola sentencia. Para renombrar columnas use RENAME COLUMN en 8.0+; antes había que usar CHANGE e indicar también el tipo.","danger":"caution","priv":"ALTER, DROP sobre la antigua; CREATE, INSERT sobre la nueva"},"mssql":{"command":"-- Table\nEXEC sp_rename 'dbo.old_name', 'new_name';\n-- Column\nEXEC sp_rename 'dbo.customer.name', 'full_name', 'COLUMN';","note":"SQL Server no tiene una sentencia RENAME TABLE; se usa el procedimiento de sistema sp_rename. El prefijo de esquema se indica en el nombre de la tabla, pero no en el nombre nuevo. Las vistas/procedimientos dependientes deben actualizarse manualmente.","danger":"caution","priv":"ALTER sobre el objeto, CONTROL"},"mongodb":{"command":"// Rename a collection\ndb.old_name.renameCollection(\"new_name\");\n// or the admin command\ndb.adminCommand({ renameCollection: \"db.old_name\", to: \"db.new_name\" });","note":"renameCollection cambia el nombre de la colección. Para renombrar un campo, use el operador $rename con updateMany. Las colecciones con sharding se pueden renombrar en 5.0+; las versiones anteriores no lo admiten.","danger":"caution","priv":"renameCollectionSameDB / clusterManager"},"redis":{"command":"# Rename a key\nRENAME old_key new_key\n# Without overwriting if the target exists\nRENAMENX old_key new_key","note":"RENAME renombra la clave; si el destino ya existe, se sobrescribe. RENAMENX solo funciona si el destino no existe. Se devuelve un error si la clave de origen no existe.","danger":"caution"},"elasticsearch":{"command":"# An index cannot be renamed directly; reindex + alias approach\nPOST _reindex\n{ \"source\": { \"index\": \"old_name\" }, \"dest\": { \"index\": \"new_name\" } }\nDELETE old_name\n# or use an alias for a seamless switch\nPOST _aliases\n{ \"actions\": [ { \"add\": { \"index\": \"new_name\", \"alias\": \"customer\" } } ] }","note":"No hay comando para renombrar índices. Reindexe con el nuevo nombre mediante _reindex y elimine el antiguo. Se recomienda trabajar detrás de un alias estable para que la aplicación no dependa de los nombres de índice.","danger":"caution"}}}]},"rosetta/datamove.json":{"category":"Movimiento de datos","categoryId":"datamove","order":11,"concepts":[{"id":"bulk-import","title":"Carga masiva de datos (CSV / desde archivo)","aliases":["import","bulk load","load csv","bulk insert","load data","copy from","import data"],"engines":{"oracle":{"command":"-- SQL*Loader (from the shell)\nsqlldr app_user/password control=customer.ctl log=load.log\n\n-- Contents of customer.ctl:\n-- LOAD DATA INFILE 'customer.csv'\n-- INTO TABLE customer FIELDS TERMINATED BY ','\n-- (id, name, email)\n\n-- Alternative: with an external table\nCREATE TABLE ext_customer (id NUMBER, name VARCHAR2(100))\nORGANIZATION EXTERNAL (TYPE ORACLE_LOADER DEFAULT DIRECTORY dir_csv\n  ACCESS PARAMETERS (FIELDS TERMINATED BY ',') LOCATION ('customer.csv'));","note":"La herramienta más habitual es SQL*Loader (con un control file). Para leer el archivo como una consulta, use una external table. Para grandes volúmenes, la carga direct path (DIRECT=TRUE) es rápida.","danger":"caution","priv":"INSERT sobre la tabla, un objeto DIRECTORY"},"postgres":{"command":"-- Server side (file on the server; requires superuser/pg_read_server_files)\nCOPY customer (id, name, email) FROM '/data/customer.csv' WITH (FORMAT csv, HEADER true);\n\n-- Client side (psql, file on the client)\n\\copy customer (id, name, email) FROM 'customer.csv' WITH (FORMAT csv, HEADER true)","note":"COPY lee un archivo en el servidor y requiere privilegios; psql \\copy lee un archivo del cliente y no necesita privilegios adicionales. Es la forma más rápida de cargar CSV.","danger":"caution","priv":"INSERT sobre la tabla (pg_read_server_files para COPY)"},"mysql":{"command":"-- Server side\nLOAD DATA INFILE '/data/customer.csv' INTO TABLE customer\nFIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\\n' IGNORE 1 LINES;\n\n-- From a client file\nLOAD DATA LOCAL INFILE 'customer.csv' INTO TABLE customer\nFIELDS TERMINATED BY ',' IGNORE 1 LINES;","note":"LOAD DATA INFILE es la forma más rápida de carga masiva. LOCAL lee un archivo del cliente y debe estar habilitado tanto en el servidor (local_infile=ON) como en el cliente. secure_file_priv restringe la ruta del servidor.","danger":"caution","priv":"FILE (INSERT para LOCAL)"},"mssql":{"command":"-- T-SQL BULK INSERT\nBULK INSERT customer FROM 'C:\\data\\customer.csv'\nWITH (FORMAT='CSV', FIRSTROW=2, FIELDTERMINATOR=',', ROWTERMINATOR='0x0a');\n\n-- bcp from the shell\nbcp mydb.dbo.customer in customer.csv -c -t, -S server -T","note":"BULK INSERT lee el archivo desde una ruta accesible para la cuenta de servicio de SQL Server. bcp es una herramienta de línea de comandos; -c es el modo carácter, -t el terminador de campo. Para la máxima velocidad, busque un registro mínimo (minimal logging) con TABLOCK.","danger":"caution","priv":"INSERT, ADMINISTER BULK OPERATIONS"},"mongodb":{"command":"# Import CSV / JSON (from the shell)\nmongoimport --db=shop --collection=customer --type=csv --headerline --file=customer.csv\n\n# For a JSON array\nmongoimport --db=shop --collection=customer --file=customer.json --jsonArray","note":"mongoimport carga CSV/TSV/JSON; --headerline trata la primera línea como nombres de campo. Para actualizar documentos existentes, use --mode=upsert --upsertFields. Para grandes volúmenes, --numInsertionWorkers lo acelera.","danger":"caution","priv":"insert (readWrite)"},"redis":{"command":"# Fastest: feed commands in protocol format and pipe them\ncat commands.txt | redis-cli --pipe\n\n# Example lines in commands.txt:\n# SET customer:1 \"Alice\"\n# SET customer:2 \"Bob\"","note":"Para la carga masiva, use redis-cli --pipe (mass insert); es muy rápido porque envía los comandos en formato de protocolo RESP por una única conexión. Como alternativa, los comandos se agrupan con pipelining.","danger":"caution"},"elasticsearch":{"command":"# _bulk API (NDJSON: an action line + a source line per document)\ncurl -s -H 'Content-Type: application/x-ndjson' -XPOST 'localhost:9200/customer/_bulk' --data-binary @data.ndjson\n\n# Contents of data.ndjson:\n# { \"index\": {} }\n# { \"id\": 1, \"name\": \"Alice\" }\n# { \"index\": {} }\n# { \"id\": 2, \"name\": \"Bob\" }","note":"La API _bulk recibe NDJSON (una línea de acción + una línea de datos por documento; el archivo debe terminar con \\n). Para CSV, use Logstash, Filebeat o elasticdump. Desactivar refresh_interval durante la carga la acelera.","danger":"caution","priv":"privilegio index / create index"}}},{"id":"bulk-export","title":"Exportar datos (CSV / a archivo)","aliases":["export","export data","export csv","copy to","outfile","dump","extract data"],"engines":{"oracle":{"command":"-- CSV with SQL*Plus (spool)\nSET MARKUP CSV ON\nSPOOL customer.csv\nSELECT id, name, email FROM customer;\nSPOOL OFF\n\n-- Logical export with Data Pump (from the shell)\nexpdp app_user/password tables=customer directory=dir_dmp dumpfile=customer.dmp","note":"Para CSV, use SQL*Plus SET MARKUP CSV ON + SPOOL (12.2+). Para mover esquemas/tablas, Data Pump (expdp) genera un .dmp binario que se importa en otra base de datos Oracle con impdp.","danger":"safe","readonly":true,"priv":"SELECT sobre la tabla","minver":"12.2 para MARKUP CSV"},"postgres":{"command":"-- Server side\nCOPY (SELECT id, name, email FROM customer) TO '/data/customer.csv' WITH (FORMAT csv, HEADER true);\n\n-- Client side (psql)\n\\copy (SELECT id, name, email FROM customer) TO 'customer.csv' WITH (FORMAT csv, HEADER true)","note":"COPY ... TO escribe el resultado de una consulta en un archivo (en el servidor, requiere privilegios); psql \\copy escribe en un archivo del cliente. HEADER true añade una línea de cabecera.","danger":"safe","readonly":true,"priv":"SELECT sobre la tabla"},"mysql":{"command":"-- To a file on the server\nSELECT id, name, email FROM customer\nINTO OUTFILE '/data/customer.csv'\nFIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\\n';\n\n-- To the client: mysql -e ... > file (tab-separated)\n-- mysql -e \"SELECT * FROM customer\" shop > customer.tsv","note":"SELECT ... INTO OUTFILE crea el archivo en el servidor (requiere la ruta de secure_file_priv y el privilegio FILE; no puede sobrescribir). Para una salida en el cliente, redirija con mysql -e.","danger":"safe","readonly":true,"priv":"SELECT, FILE"},"mssql":{"command":"-- Export with bcp (from the shell)\nbcp \"SELECT id, name, email FROM shop.dbo.customer\" queryout customer.csv -c -t, -S server -T\n\n-- or the whole table\nbcp shop.dbo.customer out customer.dat -n -S server -T","note":"bcp out/queryout escribe datos en un archivo: -c modo carácter, -t, terminador de campo, -n formato binario nativo. Use queryout para el resultado de una consulta. 'Results to File' en SSMS también es una opción.","danger":"safe","readonly":true,"priv":"SELECT sobre la tabla"},"mongodb":{"command":"# Export CSV (fields must be specified)\nmongoexport --db=shop --collection=customer --type=csv --fields=id,name,email --out=customer.csv\n\n# As JSON\nmongoexport --db=shop --collection=customer --out=customer.json","note":"mongoexport genera CSV/JSON; para CSV, las columnas deben indicarse explícitamente con --fields. Use --query para filtrar. Para un backup binario completo, se prefiere mongodump.","danger":"safe","readonly":true,"priv":"find (read)"},"redis":{"command":"# Scan keys (SCAN instead of KEYS in production)\nredis-cli --scan --pattern 'customer:*' > keys.txt\n\n# For a full data backup, download an RDB snapshot to the client side (non-blocking)\nredis-cli --rdb ./dump.rdb","note":"La exportación estructurada a CSV no viene integrada. Use --scan (no bloqueante) para una lista de claves y un archivo RDB para los datos completos: redis-cli --rdb descarga a un archivo local un snapshot generado en segundo plano en el servidor; si quiere el archivo en el servidor, use BGSAVE. SAVE bloquea el hilo principal; no lo use en producción. Para obtener también los valores, lea el resultado del scan en un bucle.","danger":"safe","readonly":true},"elasticsearch":{"command":"# Export with elasticdump\nelasticdump --input=http://localhost:9200/customer --output=customer.json --type=data\n\n# For large data, paged reads with scroll / point-in-time\nGET customer/_search?scroll=1m\n{ \"size\": 1000, \"query\": { \"match_all\": {} } }","note":"No hay exportación directa a CSV. Para obtener todos los datos, use elasticdump o pagine _search con scroll/PIT (point-in-time). La conversión a CSV requiere la salida csv de Logstash o una herramienta externa.","danger":"safe","readonly":true,"priv":"read sobre el índice"}}},{"id":"copy-between","title":"Copiar datos entre tablas / entornos","aliases":["copy","copy","insert select","ctas","reindex","move data","between tables"],"engines":{"oracle":{"command":"-- Into an existing table\nINSERT INTO target_tbl (id, name) SELECT id, name FROM source_tbl WHERE status = 'ACTIVE';\n\n-- By creating a new table (CTAS)\nCREATE TABLE target_tbl AS SELECT * FROM source_tbl;\n\n-- From a remote database via a database link\nINSERT INTO target_tbl SELECT * FROM source_tbl@remote_db;","note":"Dentro de la misma base de datos, use INSERT ... SELECT o CREATE TABLE AS SELECT (CTAS). Para copias entre entornos se prefiere un database link (@remote_db) o Data Pump. El hint APPEND lo acelera con direct-path.","danger":"caution","priv":"SELECT sobre el origen, INSERT/CREATE sobre el destino"},"postgres":{"command":"-- Same database\nINSERT INTO target_tbl (id, name) SELECT id, name FROM source_tbl;\nCREATE TABLE target_tbl AS SELECT * FROM source_tbl;\n\n-- Between environments (psql pipe)\n-- pg_dump -t source_tbl sourcedb | psql targetdb\n-- or read the remote table with postgres_fdw and INSERT","note":"Dentro de la misma BD, use INSERT ... SELECT / CTAS. Entre servidores, use un pipeline pg_dump | psql o copie a través de una foreign table con postgres_fdw.","danger":"caution","priv":"SELECT sobre el origen, INSERT/CREATE sobre el destino"},"mysql":{"command":"-- Same database\nINSERT INTO target_tbl (id, name) SELECT id, name FROM source_tbl;\nCREATE TABLE target_tbl AS SELECT * FROM source_tbl;\n\n-- Between servers (mysqldump pipe)\n-- mysqldump sourcedb source_tbl | mysql -h targethost targetdb","note":"Dentro de la misma BD, INSERT ... SELECT o CREATE TABLE ... AS SELECT. Entre servidores distintos es habitual canalizar la salida de mysqldump hacia el cliente mysql. CTAS no copia índices ni restricciones.","danger":"caution","priv":"SELECT sobre el origen, INSERT/CREATE sobre el destino"},"mssql":{"command":"-- Into an existing table\nINSERT INTO target_tbl (id, name) SELECT id, name FROM source_tbl;\n\n-- By creating a new table\nSELECT * INTO target_tbl FROM source_tbl;\n\n-- Between servers: linked server\nINSERT INTO target_tbl SELECT * FROM [REMOTE_SRV].shop.dbo.source_tbl;","note":"En SQL Server, la copia a una tabla nueva se hace con SELECT ... INTO (el equivalente de CTAS). Entre servidores, use un linked server (nombre de cuatro partes), bcp out/in o SSIS.","danger":"caution","priv":"SELECT sobre el origen, INSERT / CREATE TABLE sobre el destino"},"mongodb":{"command":"// Write an aggregation result to another collection\ndb.source_tbl.aggregate([\n  { $match: { status: \"ACTIVE\" } },\n  { $out: \"target_tbl\" }\n]);\n\n// Merge into an existing collection (upsert)\ndb.source_tbl.aggregate([ { $merge: { into: \"target_tbl\", whenMatched: \"merge\", whenNotMatched: \"insert\" } } ]);","note":"$out reemplaza por completo la colección de destino; $merge fusiona con documentos existentes o los inserta (en la misma u otra BD). Para mover datos entre servidores, use mongodump | mongorestore o mongoexport/mongoimport.","danger":"caution","priv":"find sobre el origen, insert sobre el destino"},"redis":{"command":"# Move a single key to another server (deleted from the source)\nMIGRATE target_host 6379 customer:1 0 5000\n\n# To copy (without deleting): DUMP + RESTORE\nDUMP customer:1          # returns the serialized value\nRESTORE customer:1new 0 \"<dump-data>\"","note":"MIGRATE mueve la clave de forma atómica a la instancia de destino y la elimina del origen (con la opción COPY no la elimina). Para copiar dentro de la misma instancia, use el comando COPY (6.0+) o DUMP+RESTORE.","danger":"caution"},"elasticsearch":{"command":"# Within the same cluster\nPOST _reindex\n{ \"source\": { \"index\": \"source_tbl\" }, \"dest\": { \"index\": \"target_tbl\" } }\n\n# From a remote cluster (remote reindex; requires the whitelist setting)\nPOST _reindex\n{ \"source\": { \"remote\": { \"host\": \"http://remote:9200\" }, \"index\": \"source_tbl\" },\n  \"dest\": { \"index\": \"target_tbl\" } }","note":"_reindex copia entre índices; se puede filtrar con una consulta y transformar con un script. Entre clústeres, use remote reindex (reindex.remote.whitelist debe estar definido en el destino). Los trabajos grandes se paralelizan con slices.","danger":"caution","priv":"read sobre el origen, write sobre el destino"}}},{"id":"generate-insert","title":"Generar los datos como script INSERT","aliases":["generate insert","insert script","generate script","data dump","inserts","generate script"],"engines":{"oracle":{"command":"-- DBMS_METADATA for object DDL\nSELECT DBMS_METADATA.GET_DDL('TABLE','CUSTOMER') FROM dual;\n\n-- Generating row data as INSERTs is not built in;\n-- use SQL Developer 'Export > Insert' or SQLcl 'SET SQLFORMAT INSERT':\n-- SET SQLFORMAT INSERT\n-- SELECT * FROM customer;","note":"Oracle no ofrece ningún comando SQL que convierta los datos directamente en un script INSERT listo. Para DDL use DBMS_METADATA.GET_DDL; para INSERT de filas use SQLcl (SET SQLFORMAT INSERT) o el asistente Export de SQL Developer.","danger":"safe","readonly":true,"priv":"SELECT sobre la tabla"},"postgres":{"command":"-- Data only, as INSERT statements\npg_dump --data-only --inserts --table=customer shop > insert.sql\n\n-- With column names (more robust to schema differences)\npg_dump --data-only --column-inserts --table=customer shop > insert.sql","note":"pg_dump --inserts escribe cada fila como un INSERT independiente; --column-inserts añade también los nombres de columna (robusto ante diferencias de esquema). El valor por defecto (sin opción) genera formato COPY, que se carga mucho más rápido.","danger":"safe","readonly":true,"priv":"SELECT sobre la tabla"},"mysql":{"command":"-- Data only (INSERTs), without the table definition\nmysqldump --no-create-info shop customer > insert.sql\n\n-- One INSERT per row (more readable)\nmysqldump --no-create-info --skip-extended-insert shop customer > insert.sql","note":"mysqldump genera sentencias INSERT por defecto; --no-create-info omite CREATE TABLE y solo exporta datos. --skip-extended-insert hace que cada fila sea un INSERT independiente (por defecto combina muchas filas en una sentencia).","danger":"safe","readonly":true,"priv":"SELECT sobre la tabla"},"mssql":{"command":"-- SSMS: right-click > Tasks > Generate Scripts >\n--   Advanced > 'Types of data to script' = Data only (or Schema and data)\n\n-- Command-line alternative (sqlpackage / mssql-scripter tool)\n-- mssql-scripter -S server -d shop -t customer --data-only > insert.sql","note":"SQL Server no tiene un comando T-SQL integrado para esto. La forma más habitual es elegir Data only en el asistente 'Generate Scripts' de SSMS. En la línea de comandos, la herramienta mssql-scripter de Microsoft genera INSERT con --data-only.","danger":"safe","readonly":true,"priv":"SELECT sobre la tabla"},"mongodb":{"command":"# Produces a binary/BSON backup instead of INSERT SQL\nmongodump --db=shop --collection=customer --out=./backup\n\n# As JSON text (can be adapted to insertMany)\nmongoexport --db=shop --collection=customer --jsonArray --out=customer.json","note":"No existe el concepto de INSERT SQL. Para un backup portable use mongodump (BSON, se restaura con mongorestore). Para un insertMany incrustado en código, se puede usar la salida de mongoexport --jsonArray.","danger":"safe","readonly":true,"priv":"find (read)"},"redis":{"command":"# Portable serialization of a single key\nDUMP customer:1\n# The output is restored with RESTORE:\n# RESTORE customer:1 0 \"<dump-data>\"\n\n# RDB snapshot for all data\nredis-cli --rdb dump.rdb","note":"No se genera un script INSERT SQL. Use la serialización DUMP + RESTORE por clave y un archivo RDB (--rdb) para todos los datos. Si se necesita una lista de comandos, recorra con SCAN y construya los comandos SET/HSET adecuados para cada tipo.","danger":"safe","readonly":true},"elasticsearch":{"command":"# The INSERT equivalent is _bulk NDJSON; produce it with elasticdump\nelasticdump --input=http://localhost:9200/customer --output=customer_bulk.json --type=data\n\n# Manual: converting a _search result to _bulk NDJSON requires an external script","note":"No se genera INSERT SQL; en Elasticsearch la unidad de carga es _bulk NDJSON. Para extraer los datos existentes en un formato recargable, use elasticdump (type=data); la salida de _search + PIT se puede convertir a NDJSON con un script externo.","danger":"safe","readonly":true,"priv":"read sobre el índice"}}}]},"rosetta/monitoring.json":{"category":"Monitoreo y diagnóstico","categoryId":"monitoring","order":12,"concepts":[{"id":"current-locks","title":"Listar los bloqueos actuales","aliases":["locks","lock list","lock list","current locks","active locks"],"engines":{"oracle":{"command":"SELECT s.sid, s.serial#, l.type, DECODE(l.lmode,0,'None',1,'Null',2,'Row-S',3,'Row-X',4,'Share',5,'S/Row-X',6,'Exclusive') AS lmode, o.object_name, s.username, s.blocking_session\nFROM v$lock l\nJOIN v$session s ON s.sid = l.sid\nLEFT JOIN v$locked_object lo ON lo.session_id = l.sid\nLEFT JOIN dba_objects o ON o.object_id = lo.object_id\nORDER BY s.sid;","note":"Muestra los bloqueos activos y la sesión bloqueante (blocking_session) mediante v$lock y v$locked_object.","danger":"safe","readonly":true,"priv":"SELECT sobre v$lock, v$session"},"postgres":{"command":"SELECT l.locktype, l.mode, l.granted, a.pid, a.usename, a.state, a.wait_event_type, a.query\nFROM pg_locks l\nJOIN pg_stat_activity a ON a.pid = l.pid\nWHERE l.pid <> pg_backend_pid()\nORDER BY l.granted, a.pid;","note":"Hace join de pg_locks con pg_stat_activity; las filas con granted=false muestran bloqueos en espera (bloqueados).","danger":"safe","readonly":true,"priv":"rol pg_monitor (para ver todas las consultas)"},"mysql":{"command":"SELECT ENGINE, OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_MODE, LOCK_STATUS, THREAD_ID\nFROM performance_schema.data_locks;\n-- For waiters: SELECT * FROM performance_schema.data_lock_waits;","note":"performance_schema.data_locks lista los bloqueos InnoDB actuales y data_lock_waits lista las relaciones de espera.","danger":"safe","readonly":true,"priv":"SELECT sobre performance_schema","minver":"8.0"},"mssql":{"command":"SELECT tl.resource_type, tl.resource_database_id, tl.request_mode, tl.request_status, tl.request_session_id, es.login_name, er.blocking_session_id\nFROM sys.dm_tran_locks tl\nLEFT JOIN sys.dm_exec_sessions es ON es.session_id = tl.request_session_id\nLEFT JOIN sys.dm_exec_requests er ON er.session_id = tl.request_session_id\nORDER BY tl.request_session_id;","note":"sys.dm_tran_locks muestra las solicitudes de bloqueo actuales; las que tienen request_status = WAIT son solicitudes bloqueadas.","danger":"safe","readonly":true,"priv":"VIEW SERVER STATE"},"mongodb":{"command":"db.currentOp({ \"$or\": [ { \"waitingForLock\": true }, { \"locks\": { \"$exists\": true } } ] })","note":"db.currentOp muestra las operaciones que esperan un bloqueo (waitingForLock) y las que mantienen bloqueos; MongoDB bloquea a nivel de documento/colección, no de fila.","danger":"safe","priv":"rol inprog / clusterMonitor"},"redis":{"command":"# Not applicable: Redis has no row/table lock concept; commands run sequentially on a single thread. For blocking analysis, use the SLOWLOG and LATENCY commands.","note":"Como Redis es de un solo hilo, no hay una lista de bloqueos tradicional.","danger":"safe"},"elasticsearch":{"command":"GET _cluster/pending_tasks","note":"Elasticsearch no tiene bloqueos de fila; esto lista las tareas de cambio del clúster en cola en el master (pending tasks).","danger":"safe","readonly":true}}},{"id":"long-operations","title":"Ver el progreso de operaciones de larga duración","aliases":["progress","operation progress","long operations","long-running operations","longops"],"engines":{"oracle":{"command":"SELECT sid, serial#, opname, target, sofar, totalwork,\n       ROUND(sofar / NULLIF(totalwork,0) * 100, 2) AS pct_done,\n       time_remaining, elapsed_seconds\nFROM v$session_longops\nWHERE totalwork > 0 AND sofar < totalwork\nORDER BY start_time;","note":"v$session_longops muestra el porcentaje de progreso y el tiempo restante (time_remaining) de las operaciones que duran más de 6 segundos.","danger":"safe","readonly":true,"priv":"SELECT sobre v$session_longops"},"postgres":{"command":"SELECT pid, phase, heap_blks_total, heap_blks_scanned,\n       ROUND(heap_blks_scanned::numeric / NULLIF(heap_blks_total,0) * 100, 2) AS pct\nFROM pg_stat_progress_vacuum;\n-- Others: pg_stat_progress_create_index, _cluster, _analyze, _copy, _basebackup","note":"Las vistas pg_stat_progress_* informan del progreso de las operaciones VACUUM, CREATE INDEX, CLUSTER, ANALYZE y COPY.","danger":"safe","readonly":true,"minver":"9.6"},"mysql":{"command":"SELECT t.processlist_id, st.event_name, st.work_completed, st.work_estimated,\n       ROUND(st.work_completed / NULLIF(st.work_estimated,0) * 100, 2) AS pct\nFROM performance_schema.events_stages_current st\nJOIN performance_schema.threads t ON t.thread_id = st.thread_id\nWHERE st.work_estimated > 0;","note":"Con la instrumentación de stages habilitada, events_stages_current muestra el progreso mediante work_completed/work_estimated (p. ej., ALTER TABLE).","danger":"safe","readonly":true,"priv":"SELECT sobre performance_schema"},"mssql":{"command":"SELECT session_id, command, percent_complete,\n       DATEADD(ms, estimated_completion_time, GETDATE()) AS est_finish,\n       total_elapsed_time / 1000 AS elapsed_sec\nFROM sys.dm_exec_requests\nWHERE percent_complete > 0\nORDER BY percent_complete;","note":"sys.dm_exec_requests.percent_complete da el porcentaje para operaciones como BACKUP, RESTORE, DBCC y reconstrucciones de índices.","danger":"safe","readonly":true,"priv":"VIEW SERVER STATE"},"mongodb":{"command":"db.currentOp({ \"active\": true, \"secs_running\": { \"$gte\": 3 } })","note":"En la salida de db.currentOp, las operaciones de larga duración y, en las creaciones de índices, el campo 'progress' (done/total) muestran el progreso.","danger":"safe","priv":"rol inprog / clusterMonitor"},"redis":{"command":"INFO persistence\n# Keys: rdb_bgsave_in_progress, aof_rewrite_in_progress, rdb_last_bgsave_status, loading, loading_loaded_perc","note":"La sección INFO persistence indica si hay operaciones de guardado o rewrite de RDB/AOF en curso y el porcentaje de carga.","danger":"safe","readonly":true},"elasticsearch":{"command":"GET _tasks?detailed=true&actions=*reindex,*update/byquery,*delete/byquery\n# For recovery/shard relocation: GET _cat/recovery?v&active_only=true","note":"_tasks?detailed muestra el estado de las tareas de larga duración (reindex, update_by_query); _cat/recovery muestra el progreso de la recuperación de shards activa.","danger":"safe","readonly":true}}},{"id":"session-trace","title":"Trazar una sesión / consulta (habilitar trace / profiling)","aliases":["trace","enable tracing","profiling","session trace","query tracing"],"engines":{"oracle":{"command":"-- For a specific session (10046 event tracing):\nBEGIN\n  DBMS_MONITOR.SESSION_TRACE_ENABLE(session_id => 123, serial_num => 4567, waits => TRUE, binds => TRUE);\nEND;\n/\n-- To disable: DBMS_MONITOR.SESSION_TRACE_DISABLE(123, 4567);","note":"DBMS_MONITOR.SESSION_TRACE_ENABLE genera un archivo de trace 10046 para la sesión; deshabilítelo siempre al terminar (el tracing añade sobrecarga y uso de disco).","danger":"caution","priv":"EXECUTE sobre DBMS_MONITOR"},"postgres":{"command":"LOAD 'auto_explain';\nSET auto_explain.log_min_duration = 0;\nSET auto_explain.log_analyze = true;\n-- Simple alternative: SET log_min_duration_statement = 0;","note":"auto_explain escribe los planes de consulta, y log_min_duration_statement las consultas lentas/todas, en el archivo de log; un valor de 0 registra todas las consultas, así que vigile el volumen de log.","danger":"caution","priv":"SUPERUSER (para LOAD) o SET a nivel de sesión"},"mysql":{"command":"-- performance_schema approach (recommended):\nUPDATE performance_schema.setup_consumers SET ENABLED='YES' WHERE NAME LIKE 'events_statements%';\n-- Legacy/simple method (being deprecated):\nSET profiling = 1;\n-- run the query, then:\nSHOW PROFILES;","note":"El trazado de consultas se hace con events_statements de performance_schema; el método SET profiling es antiguo (obsoleto) pero ofrece una medición rápida por sesión.","danger":"caution","priv":"SELECT/UPDATE sobre performance_schema"},"mssql":{"command":"CREATE EVENT SESSION [trace_slow] ON SERVER\nADD EVENT sqlserver.sql_statement_completed(\n  WHERE duration > 1000000)\nADD TARGET package0.event_file(SET filename=N'trace_slow.xel');\nGO\nALTER EVENT SESSION [trace_slow] ON SERVER STATE = START;","note":"Extended Events (XEvents) es el método de trazado moderno; cree la sesión e iníciela con START, y deténgala con STOP/DROP al terminar.","danger":"caution","priv":"ALTER ANY EVENT SESSION"},"mongodb":{"command":"db.setProfilingLevel(2)\n// 1 = slow operations only (slowms threshold), 0 = off\n// Results: db.system.profile.find().sort({ ts: -1 }).limit(10)","note":"setProfilingLevel(2) registra todas las operaciones en la colección system.profile; en producción se prefiere 1 (solo lentas), ya que el nivel 2 añade sobrecarga.","danger":"caution","priv":"dbAdmin / enableProfiler"},"redis":{"command":"MONITOR\n# Streams all commands arriving at the server in real time. Exit: Ctrl-C","note":"MONITOR muestra todos los comandos en vivo, pero provoca una caída importante del rendimiento; úselo brevemente y con cuidado en producción. Alternativa: SLOWLOG GET.","danger":"caution"},"elasticsearch":{"command":"GET my-index/_search\n{\n  \"profile\": true,\n  \"query\": { \"match\": { \"title\": \"elastic\" } }\n}\n// Persistent slow query tracing: the index.search.slowlog.threshold.query.warn setting","note":"\"profile\": true da el desglose de tiempos de consulta/agregación de una sola búsqueda; para un monitoreo continuo, configure umbrales de slowlog.","danger":"safe","readonly":true}}},{"id":"top-wait-events","title":"Ver los principales wait events","aliases":["wait events","wait event list","top waits","waits"],"engines":{"oracle":{"command":"SELECT event, total_waits, time_waited, ROUND(time_waited/100,2) AS sec_waited\nFROM v$system_event\nWHERE wait_class <> 'Idle'\nORDER BY time_waited DESC\nFETCH FIRST 20 ROWS ONLY;\n-- For real-time analysis: v$active_session_history (ASH)","note":"v$system_event da los wait events acumulados; v$active_session_history da el muestreo de sesiones activas de los últimos minutos (requiere la licencia Diagnostics Pack).","danger":"safe","readonly":true,"priv":"SELECT sobre v$system_event"},"postgres":{"command":"SELECT wait_event_type, wait_event, count(*)\nFROM pg_stat_activity\nWHERE wait_event IS NOT NULL AND state = 'active'\nGROUP BY 1, 2\nORDER BY 3 DESC;","note":"pg_stat_activity muestra el estado de espera actual; muestrearlo repetidamente (polling) revela los wait events más frecuentes.","danger":"safe","readonly":true,"priv":"rol pg_monitor"},"mysql":{"command":"SELECT event_name, count_star, sum_timer_wait,\n       ROUND(sum_timer_wait/1000000000000, 2) AS sec_waited\nFROM performance_schema.events_waits_summary_global_by_event_name\nWHERE count_star > 0\nORDER BY sum_timer_wait DESC\nLIMIT 20;","note":"events_waits_summary_global_by_event_name da los tiempos de espera totales por evento (en picosegundos); los instrumentos de espera deben estar habilitados.","danger":"safe","readonly":true,"priv":"SELECT sobre performance_schema"},"mssql":{"command":"SELECT wait_type, waiting_tasks_count, wait_time_ms,\n       signal_wait_time_ms, wait_time_ms - signal_wait_time_ms AS resource_wait_ms\nFROM sys.dm_os_wait_stats\nWHERE wait_time_ms > 0\nORDER BY wait_time_ms DESC;","note":"sys.dm_os_wait_stats muestra los tipos de espera acumulados desde el arranque de la instancia; se puede reiniciar con DBCC SQLPERF.","danger":"safe","readonly":true,"priv":"VIEW SERVER STATE"},"mongodb":{"command":"db.serverStatus().wiredTiger.concurrentTransactions\n// For general waits/congestion:\n// db.serverStatus().globalLock  and  db.serverStatus().wiredTiger.cache","note":"WiredTiger no tiene wait events clásicos; el agotamiento de concurrentTransactions (tickets), globalLock.currentQueue y la presión de caché indican congestión.","danger":"safe","priv":"rol clusterMonitor"},"redis":{"command":"CONFIG SET latency-monitor-threshold 100\nLATENCY LATEST\nLATENCY HISTORY command\n# For the slowest commands: SLOWLOG GET 10","note":"Una vez configurado el umbral del latency monitor, LATENCY LATEST/HISTORY informa de los eventos que causan más latencia.","danger":"safe"},"elasticsearch":{"command":"GET _nodes/hot_threads","note":"_nodes/hot_threads revela cuellos de botella mostrando los stack traces de los hilos que más CPU consumen.","danger":"safe","readonly":true}}},{"id":"os-resource","title":"Ver el uso de recursos del servidor desde la BD (CPU/E/S)","aliases":["os resource","cpu io","resource usage","server resources","system metrics"],"engines":{"oracle":{"command":"SELECT stat_name, value FROM v$osstat\nWHERE stat_name IN ('NUM_CPUS','BUSY_TIME','IDLE_TIME','IOWAIT_TIME','PHYSICAL_MEMORY_BYTES');\n-- Real-time metrics: SELECT metric_name, value FROM v$sysmetric WHERE group_id = 2;","note":"v$osstat da los contadores de CPU/memoria del SO; v$sysmetric da métricas de la BD en tiempo real (CPU, IOPS) del último intervalo.","danger":"safe","readonly":true,"priv":"SELECT sobre v$osstat, v$sysmetric"},"postgres":{"command":"SELECT backend_type, object, context, reads, writes, hits, evictions\nFROM pg_stat_io\nORDER BY reads DESC;\n-- Note: CPU usage is not visible from inside the DB; an OS tool (top, sar) is required.","note":"pg_stat_io da contadores de lectura/escritura/aciertos de caché; PostgreSQL no expone el uso de CPU, por lo que se requieren herramientas del sistema operativo.","danger":"safe","readonly":true,"minver":"16"},"mysql":{"command":"SELECT * FROM sys.host_summary;\n-- For InnoDB I/O and thread details:\nSHOW ENGINE INNODB STATUS\\G","note":"Las vistas resumen del esquema sys (host_summary) dan un desglose de E/S/latencia; SHOW ENGINE INNODB STATUS muestra la actividad del buffer pool y de E/S.","danger":"safe","readonly":true,"priv":"SELECT sobre sys, PROCESS"},"mssql":{"command":"SELECT object_name, counter_name, cntr_value\nFROM sys.dm_os_performance_counters\nWHERE counter_name LIKE '%CPU%' OR counter_name LIKE '%Page life expectancy%';\n-- CPU history: sys.dm_os_ring_buffers (RING_BUFFER_SCHEDULER_MONITOR)","note":"sys.dm_os_performance_counters da los contadores de rendimiento de la BD; dm_os_ring_buffers da la proporción de CPU de SQL/otros procesos de los últimos ~256 minutos.","danger":"safe","readonly":true,"priv":"VIEW SERVER STATE"},"mongodb":{"command":"db.serverStatus().metrics\n// Relevant fields: .opcounters, .network, .wiredTiger.cache, .mem, .extra_info.page_faults","note":"serverStatus() da métricas de memoria, red, opcounters y caché de WiredTiger; para el detalle de CPU/E/S del SO use mongostat o herramientas del sistema operativo.","danger":"safe","priv":"rol clusterMonitor"},"redis":{"command":"INFO cpu\nINFO stats\n# used_cpu_sys, used_cpu_user, instantaneous_ops_per_sec, total_net_input_bytes","note":"INFO cpu informa del consumo de CPU del proceso Redis, e INFO stats de las operaciones por segundo y el tráfico de red.","danger":"safe","readonly":true},"elasticsearch":{"command":"GET _nodes/stats/os,process\n// Look at the os.cpu.percent, os.mem, process.cpu.percent, fs (disk) fields","note":"_nodes/stats/os,process da el porcentaje de CPU, la memoria y las métricas de proceso por nodo; use la sección fs para el disco.","danger":"safe","readonly":true}}}]},"rosetta/automation.json":{"category":"Automatización y auditoría","categoryId":"automation","order":13,"concepts":[{"id":"schedule-job","title":"Crear un job programado","aliases":["schedule job","scheduled job","cron","create job","scheduled task"],"engines":{"oracle":{"command":"BEGIN\n  DBMS_SCHEDULER.CREATE_JOB(\n    job_name        => 'NIGHTLY_MAINT',\n    job_type        => 'PLSQL_BLOCK',\n    job_action      => 'BEGIN my_maintenance_proc; END;',\n    start_date      => SYSTIMESTAMP,\n    repeat_interval => 'FREQ=DAILY; BYHOUR=3',\n    enabled         => TRUE);\nEND;\n/","note":"DBMS_SCHEDULER.CREATE_JOB define un job programado; repeat_interval establece la recurrencia con una expresión de calendario.","danger":"caution","priv":"CREATE JOB"},"postgres":{"command":"SELECT cron.schedule('nightly-vacuum', '0 3 * * *', 'VACUUM ANALYZE');\n-- Requires the pg_cron extension. Alternative: pgAgent.","note":"La extensión pg_cron define jobs con la sintaxis cron estándar mediante cron.schedule; si la extensión no está instalada, use pgAgent o el cron del sistema operativo.","danger":"caution","priv":"pg_cron instalado; normalmente superuser/propietario de la extensión"},"mysql":{"command":"SET GLOBAL event_scheduler = ON;\nCREATE EVENT nightly_cleanup\nON SCHEDULE EVERY 1 DAY STARTS '2026-01-01 03:00:00'\nDO\n  DELETE FROM logs WHERE created_at < NOW() - INTERVAL 30 DAY;","note":"CREATE EVENT se ejecuta mediante el event scheduler; la variable global event_scheduler debe estar en ON; de lo contrario, los eventos no se disparan.","danger":"caution","priv":"EVENT"},"mssql":{"command":"USE msdb;\nEXEC dbo.sp_add_job @job_name = N'NightlyMaint';\nEXEC dbo.sp_add_jobstep @job_name = N'NightlyMaint', @step_name = N'Step1',\n     @subsystem = N'TSQL', @command = N'EXEC dbo.usp_Maintenance;';\nEXEC dbo.sp_add_schedule @schedule_name = N'Daily3AM',\n     @freq_type = 4, @freq_interval = 1, @active_start_time = 030000;\nEXEC dbo.sp_attach_schedule @job_name = N'NightlyMaint', @schedule_name = N'Daily3AM';\nEXEC dbo.sp_add_jobserver @job_name = N'NightlyMaint';","note":"Los jobs de SQL Server Agent se crean con procedimientos de msdb: se definen el job, el step y el schedule, y se asocian al servidor. El servicio SQL Server Agent debe estar en ejecución.","danger":"caution","priv":"SQLAgentUserRole o sysadmin"},"mongodb":{"command":"# Not applicable: MongoDB has no built-in scheduler. Use external cron/systemd timers, an application scheduler or a MongoDB Atlas Scheduled Trigger.","note":"La edición Community no tiene jobs programados; se requiere un planificador externo o un Atlas Trigger.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no built-in job scheduler. Use EXPIRE/keyspace notifications for key expiry and external cron for periodic tasks.","note":"Redis no ejecuta jobs programados; esto se resuelve con TTL/keyspace notifications y un cron externo.","danger":"safe"},"elasticsearch":{"command":"PUT _slm/policy/daily-snapshot\n{\n  \"schedule\": \"0 30 1 * * ?\",\n  \"name\": \"<snap-{now/d}>\",\n  \"repository\": \"backup_repo\",\n  \"config\": { \"indices\": [\"*\"] },\n  \"retention\": { \"expire_after\": \"30d\" }\n}","note":"SLM (Snapshot Lifecycle Management) toma snapshots programados; ILM se usa para el ciclo de vida de los datos y Watcher para la automatización avanzada.","danger":"caution","priv":"privilegio de clúster manage_slm"}}},{"id":"list-jobs","title":"Listar jobs programados / ver su estado","aliases":["list jobs","show jobs","job status","scheduled jobs","job history"],"engines":{"oracle":{"command":"SELECT job_name, enabled, state, last_start_date, next_run_date\nFROM dba_scheduler_jobs\nORDER BY next_run_date;\n-- Run history: SELECT job_name, status, actual_start_date, run_duration\n--                 FROM dba_scheduler_job_run_details ORDER BY log_date DESC;","note":"dba_scheduler_jobs muestra los jobs definidos y sus estados; dba_scheduler_job_run_details muestra los resultados de éxito/fallo de las ejecuciones anteriores.","danger":"safe","readonly":true,"priv":"SELECT sobre dba_scheduler_jobs"},"postgres":{"command":"SELECT jobid, schedule, command, nodename, active FROM cron.job;\n-- Run history:\nSELECT jobid, status, return_message, start_time, end_time\nFROM cron.job_run_details ORDER BY start_time DESC LIMIT 20;","note":"Con pg_cron, cron.job lista los jobs definidos y cron.job_run_details da el estado y el mensaje de retorno de cada ejecución.","danger":"safe","readonly":true,"priv":"pg_cron instalado; acceso al esquema cron"},"mysql":{"command":"SELECT event_schema, event_name, status, interval_value, interval_field, last_executed\nFROM information_schema.events\nORDER BY last_executed DESC;","note":"information_schema.events lista los eventos definidos, su estado (ENABLED/DISABLED) y la hora de la última ejecución.","danger":"safe","readonly":true,"priv":"EVENT / SELECT"},"mssql":{"command":"SELECT j.name, j.enabled, h.run_status, h.run_date, h.run_time\nFROM msdb.dbo.sysjobs j\nLEFT JOIN msdb.dbo.sysjobhistory h ON h.job_id = j.job_id AND h.step_id = 0\nORDER BY h.run_date DESC, h.run_time DESC;","note":"msdb.dbo.sysjobs da los jobs de Agent definidos, y sysjobhistory el historial de ejecuciones y su resultado (run_status).","danger":"safe","readonly":true,"priv":"SQLAgentReaderRole / acceso a msdb"},"mongodb":{"command":"# Not applicable: there is no built-in scheduler, so there are no jobs to list. If Atlas Triggers are used, view them in the Atlas UI/API.","note":"MongoDB Community no tiene tabla de jobs; los Atlas Triggers se gestionan en la interfaz de Atlas.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no concept of scheduled jobs, so there are no jobs to list.","note":"Redis no incluye un planificador de jobs; no hay jobs que listar.","danger":"safe"},"elasticsearch":{"command":"GET _slm/policy\nGET _ilm/policy\n// For Watcher states: GET _watcher/stats  and  GET _watcher/_query/watches","note":"_slm/policy lista las políticas de snapshot, _ilm/policy las políticas de ciclo de vida y _watcher/stats muestra el estado de ejecución de Watcher.","danger":"safe","readonly":true,"priv":"privilegio read_slm / read_ilm"}}},{"id":"enable-audit","title":"Habilitar auditoría / registro","aliases":["audit","auditing","logging","enable audit","audit log"],"engines":{"oracle":{"command":"-- Unified Auditing (12c+):\nCREATE AUDIT POLICY table_audit\n  ACTIONS SELECT, INSERT, UPDATE, DELETE ON hr.employees;\nAUDIT POLICY table_audit;\n-- Monitoring: SELECT * FROM unified_audit_trail ORDER BY event_timestamp DESC;","note":"Con Unified Auditing, se crea una política y se habilita con AUDIT; los registros se guardan en la vista unified_audit_trail.","danger":"caution","priv":"AUDIT SYSTEM / rol AUDIT_ADMIN","minver":"12.1"},"postgres":{"command":"-- pgaudit extension (recommended; first shared_preload_libraries = 'pgaudit' + restart + CREATE EXTENSION pgaudit):\nALTER SYSTEM SET pgaudit.log = 'write, ddl';\nSELECT pg_reload_conf();\n-- Simple built-in method:\nALTER SYSTEM SET log_statement = 'ddl';\nSELECT pg_reload_conf();","note":"pgaudit proporciona un registro de auditoría detallado; configúrelo con ALTER SYSTEM para aplicarlo a todo el servidor (un SET a nivel de sesión solo audita esa sesión). Sin la extensión, el registro básico se habilita con log_statement ('ddl','mod','all'). Vigile el volumen de log.","danger":"caution","priv":"superuser (ALTER SYSTEM)"},"mysql":{"command":"-- Enterprise/MariaDB audit plugin:\nINSTALL PLUGIN audit_log SONAME 'audit_log.so';\nSET GLOBAL audit_log_policy = 'ALL';\n-- Simple alternative (all queries):\nSET GLOBAL general_log = 'ON';","note":"El plugin audit_log (Enterprise, o server_audit en MariaDB) mantiene una pista de auditoría detallada; general_log registra todas las consultas pero degrada el rendimiento.","danger":"caution","priv":"SUPER / SYSTEM_VARIABLES_ADMIN"},"mssql":{"command":"USE master;\nCREATE SERVER AUDIT Audit_A TO FILE (FILEPATH = 'C:\\\\audit\\\\');\nALTER SERVER AUDIT Audit_A WITH (STATE = ON);\nUSE mydb;\nCREATE DATABASE AUDIT SPECIFICATION db_spec\n  FOR SERVER AUDIT Audit_A\n  ADD (SELECT, INSERT, UPDATE, DELETE ON SCHEMA::dbo BY public)\n  WITH (STATE = ON);","note":"Primero cree y habilite un destino SERVER AUDIT (archivo/registro de eventos) y luego defina qué eventos rastrear con una AUDIT SPECIFICATION de servidor o de base de datos.","danger":"caution","priv":"ALTER ANY SERVER AUDIT / CONTROL SERVER"},"mongodb":{"command":"# MongoDB Enterprise / Atlas only. Configuration (mongod.conf):\n#   auditLog:\n#     destination: file\n#     format: JSON\n#     path: /var/log/mongodb/audit.json\n#     filter: '{ atype: { $in: [\"authCheck\",\"createCollection\",\"dropCollection\"] } }'\n# mongod --auditDestination file --auditPath /var/log/mongodb/audit.json","note":"La auditoría solo está disponible en Enterprise y Atlas; se habilita con la sección auditLog de la configuración de mongod. No existe en la edición Community.","danger":"caution"},"redis":{"command":"ACL LOG\n# Shows rejected authentication/command/key access attempts.\n# To reset: ACL LOG RESET\n# For general command auditing: an external proxy or MONITOR (with care).","note":"ACL LOG registra automáticamente los eventos de seguridad (autenticación fallida, comandos/claves no autorizados); la auditoría completa de comandos no viene integrada.","danger":"safe","priv":"permiso ACL (admin por defecto)"},"elasticsearch":{"command":"# elasticsearch.yml (on all nodes, restart required):\nxpack.security.audit.enabled: true\n# Optional event filter:\nxpack.security.audit.logfile.events.include: [ \"authentication_failed\", \"access_denied\" ]","note":"El registro de auditoría se habilita con xpack.security.audit.enabled; se configura en elasticsearch.yml y requiere reiniciar el nodo (no es dinámico). Requiere una licencia comercial.","danger":"caution"}}},{"id":"current-config-diff","title":"Listar la configuración no predeterminada","aliases":["config diff","changed settings","non-default settings","modified settings","modified config"],"engines":{"oracle":{"command":"SELECT name, value, isdefault\nFROM v$parameter\nWHERE isdefault = 'FALSE'\nORDER BY name;","note":"Las filas de v$parameter con isdefault = 'FALSE' muestran los parámetros que difieren del valor por defecto (establecidos explícitamente).","danger":"safe","readonly":true,"priv":"SELECT sobre v$parameter"},"postgres":{"command":"SELECT name, setting, unit, source\nFROM pg_settings\nWHERE source NOT IN ('default', 'override')\nORDER BY name;","note":"Si pg_settings.source es distinto de 'default' y 'override', la configuración se cambió mediante un archivo de configuración, la línea de comandos o la sesión.","danger":"safe","readonly":true},"mysql":{"command":"SHOW GLOBAL VARIABLES;\n-- MySQL does not automatically compare variables with defaults; with performance_schema\n-- you can see the source of each setting:\nSELECT VARIABLE_NAME, VARIABLE_SOURCE, VARIABLE_VALUE\nFROM performance_schema.variables_info\nWHERE VARIABLE_SOURCE <> 'COMPILED';","note":"SHOW GLOBAL VARIABLES devuelve toda la configuración; no muestra directamente la diferencia con los valores por defecto. La columna VARIABLE_SOURCE de performance_schema.variables_info muestra los cambios distintos del valor compilado por defecto (archivo, línea de comandos, dinámico).","danger":"safe","readonly":true,"minver":"8.0"},"mssql":{"command":"SELECT name, value, value_in_use, is_dynamic, is_advanced\nFROM sys.configurations\nWHERE value <> value_in_use\nORDER BY name;\n-- value <> value_in_use: changes not yet RECONFIGUREd or awaiting a restart (is_dynamic = 0).","note":"sys.configurations devuelve la configuración del servidor; si value y value_in_use difieren, hay un cambio no aplicado con RECONFIGURE (o pendiente de reinicio). sys.configurations no almacena el valor por defecto (minimum/maximum es solo el rango permitido); para ver las diferencias con los valores por defecto, compare los valores con los documentados.","danger":"safe","readonly":true,"priv":"VIEW SERVER STATE"},"mongodb":{"command":"db.adminCommand({ getCmdLineOpts: 1 })\n// The 'parsed' field shows settings explicitly provided (configuration file/command line) beyond the defaults.","note":"La sección 'parsed' del comando getCmdLineOpts solo lista la configuración especificada explícitamente (no predeterminada).","danger":"safe","priv":"privilegio clusterMonitor / hostInfo"},"redis":{"command":"CONFIG GET *\n# Redis lists all settings but does not compare them with defaults.\n# To see differences, compare the output with the default redis.conf values for your version.","note":"CONFIG GET * devuelve toda la configuración en ejecución; no tiene una comparación integrada con los valores por defecto, así que compare la salida con los valores por defecto de redis.conf de la versión correspondiente.","danger":"safe","readonly":true},"elasticsearch":{"command":"GET _cluster/settings\n// Returns only explicitly set persistent and transient settings.\n// For everything including defaults: GET _cluster/settings?include_defaults=true&flat_settings=true","note":"_cluster/settings devuelve la configuración del clúster no predeterminada (persistent/transient); con include_defaults=true también se muestran los valores por defecto para comparar.","danger":"safe","readonly":true}}}]},"rosetta/lifecycle.json":{"category":"Instalación y actualización","categoryId":"lifecycle","order":14,"concepts":[{"id":"check-version","title":"Mostrar la versión instalada y el nivel de parches","aliases":["version","patch level","server version","patch level check","release","build"],"engines":{"oracle":{"command":"SELECT * FROM v$version;\n-- Registered RU/patch history (SQL layer):\nSELECT patch_id, action, status, action_time\nFROM dba_registry_sqlpatch ORDER BY action_time;\n-- From the OS shell (patches installed in ORACLE_HOME):\n-- $ORACLE_HOME/OPatch/opatch lspatches","note":"El banner de v$version da la versión. dba_registry_sqlpatch muestra si los Release Updates (RU) y los parches one-off aplicados se han registrado en la capa SQL, mientras que 'opatch lspatches' muestra los parches binarios del home.","danger":"safe","readonly":true,"priv":"SELECT sobre v$version, dba_registry_sqlpatch"},"postgres":{"command":"SELECT version();\n-- Numeric version only:\nSHOW server_version;\nSELECT current_setting('server_version_num');","note":"version() devuelve el texto completo, incluida la información de build. PostgreSQL no tiene un número de parche aparte; la versión menor (p. ej., 16.3) representa el nivel de correcciones.","danger":"safe","readonly":true},"mysql":{"command":"SELECT VERSION();\n-- Detailed build information:\nSHOW VARIABLES LIKE 'version%';","note":"VERSION() devuelve, p. ej., '8.0.36'; el último componente es el nivel de parche (release). La variable version_comment indica si el build es Community/Enterprise o MariaDB.","danger":"safe","readonly":true},"mssql":{"command":"SELECT @@VERSION;\n-- Structured version fields:\nSELECT SERVERPROPERTY('ProductVersion')       AS version,\n       SERVERPROPERTY('ProductLevel')         AS service_pack,\n       SERVERPROPERTY('ProductUpdateLevel')   AS cu_level,\n       SERVERPROPERTY('ProductUpdateReference') AS kb_number,\n       SERVERPROPERTY('Edition')              AS edition;","note":"ProductUpdateLevel da el Cumulative Update aplicado (p. ej., CU12) y ProductUpdateReference el número de KB relacionado. ProductLevel muestra 'RTM' o, en versiones antiguas, 'SP'.","danger":"safe","readonly":true},"mongodb":{"command":"db.version()\n// Detailed build information:\ndb.serverBuildInfo()\n// or: db.adminCommand({ buildInfo: 1 })","note":"db.version() devuelve la cadena de versión (p. ej., '7.0.11'). En MongoDB, las correcciones se publican como una nueva versión de parche; no existe un concepto de 'nivel de parche' aparte.","danger":"safe"},"redis":{"command":"INFO server\n# redis-cli one-liner: redis-cli INFO server | grep redis_version","note":"El campo redis_version de la salida de INFO server da la versión instalada. En Redis, las correcciones llegan como una nueva versión de parche (p. ej., 7.2.5); el campo redis_git_sha1 muestra también el identificador del build.","danger":"safe"},"elasticsearch":{"command":"GET /\n# curl: curl -s http://localhost:9200/","note":"El endpoint raíz devuelve version.number (p. ej., '8.14.1'), lucene_version y build_flavor. Para ver las versiones de todos los nodos: GET _cat/nodes?v&h=name,version","danger":"safe","readonly":true}}},{"id":"check-upgrade-readiness","title":"Comprobación de compatibilidad/preparación previa a la actualización","aliases":["upgrade readiness","preupgrade","compatibility check","readiness","compatibility check","deprecation"],"engines":{"oracle":{"command":"-- Recommended method for 19c and later (AutoUpgrade analyze mode):\n-- $ java -jar autoupgrade.jar -config db.cfg -mode analyze\n-- Legacy/alternative: preupgrade script (produces a report only):\n-- $ $NEW_ORACLE_HOME/jdk/bin/java -jar $NEW_ORACLE_HOME/rdbms/admin/preupgrade.jar FILE TEXT DIR /tmp","note":"El modo 'analyze' de AutoUpgrade genera un informe previo a la actualización sin modificar datos; lista las advertencias que deben corregirse y los elementos que se resolverán automáticamente. Ejecútelo siempre antes de la actualización real.","danger":"safe"},"postgres":{"command":"# With the new version's binaries, compatibility check only (moves no data):\n$ pg_upgrade \\\n    --old-datadir=/var/lib/pgsql/16/data \\\n    --new-datadir=/var/lib/pgsql/17/data \\\n    --old-bindir=/usr/pgsql-16/bin \\\n    --new-bindir=/usr/pgsql-17/bin \\\n    --check","note":"--check verifica que la actualización es viable sin modificar ningún dato (informa de extensiones que faltan, tipos de datos incompatibles, etc.). --check también se puede ejecutar con el clúster antiguo en marcha; la actualización real se hace con ambos clústeres detenidos.","danger":"safe"},"mysql":{"command":"-- 8.0 and later: Upgrade Checker utility (MySQL Shell):\n-- $ mysqlsh -- util check-for-server-upgrade { --user=root --host=localhost }\n-- Table consistency for legacy 5.7->8.0:\n$ mysqlcheck --all-databases --check-upgrade -u root -p","note":"util.checkForServerUpgrade() de MySQL Shell informa de funciones eliminadas, conflictos con palabras reservadas y definiciones incompatibles. A partir de 8.0, el propio servidor realiza la actualización del diccionario al arrancar; ya no se necesita un mysql_upgrade aparte.","danger":"safe"},"mssql":{"command":"-- Run from the desktop with Data Migration Assistant (DMA):\n-- DmaCmd.exe /AssessmentName=... /AssessmentDatabases=...\n-- (Produces compatibility/deprecated feature and blocker reports.)","note":"Data Migration Assistant (DMA) sustituyó al antiguo Upgrade Advisor; informa por base de datos de funciones obsoletas, cambios incompatibles y bloqueantes. Ejecútelo antes de la actualización y resuelva los hallazgos.","danger":"safe"},"mongodb":{"command":"// Verify the current Feature Compatibility Version (FCV):\ndb.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })\n// Compatibility items (deprecations) in the release notes are reviewed manually.","note":"Antes de actualizar la versión binaria, el FCV debe ser igual a la versión actual; de lo contrario, la actualización se bloquea. MongoDB no tiene un 'escáner de obsolescencias' automático; las notas de compatibilidad de la versión se revisan manualmente.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no separate pre-upgrade readiness tool.\n# Checklist: read the target version's release notes for RDB/AOF persistence format,\n# changed configuration directives, and removed commands/arguments.","note":"Las actualizaciones de Redis suelen ser compatibles hacia adelante; como no hay un escáner de preparación oficial, revise manualmente en las release notes el formato de los archivos RDB/AOF, las directivas de configuración eliminadas y los cambios de comandos.","danger":"safe"},"elasticsearch":{"command":"GET _migration/deprecations\n# For a specific component: GET <index>/_migration/deprecations","note":"La Deprecation Info API lista como 'critical' / 'warning' la configuración de clúster, nodo, índice y ML que causará problemas al pasar a la siguiente versión mayor. Kibana Upgrade Assistant muestra los mismos datos de forma visual.","danger":"safe","readonly":true}}},{"id":"apply-patch","title":"Aplicar un parche","aliases":["apply patch","patching","patch","minor upgrade","hotfix","ru apply"],"engines":{"oracle":{"command":"# 1) Shut down the database, apply the binary patch with OPatch:\n$ $ORACLE_HOME/OPatch/opatch apply\n# 2) Open the database and load the SQL layer changes:\n$ $ORACLE_HOME/OPatch/datapatch -verbose","note":"opatch apply actualiza los binarios del Oracle Home; después datapatch aplica los cambios de diccionario/SQL en la base de datos abierta y los registra en dba_registry_sqlpatch. En RAC, los parches rolling-installable se pueden aplicar nodo por nodo.","danger":"caution","priv":"SO: propietario del ORACLE_HOME; SQL: SYSDBA"},"postgres":{"command":"# Minor version patch = replace binaries + restart (no dump needed):\n$ sudo systemctl stop postgresql-16\n$ sudo dnf update postgresql16-server   # or apt upgrade\n$ sudo systemctl start postgresql-16","note":"Los parches menores dentro de la misma serie de versión mayor (16.3 -> 16.4) no cambian el formato de datos: basta con actualizar el paquete y reiniciar el servicio; no se necesita pg_upgrade ni dump/restore.","danger":"caution"},"mysql":{"command":"# Minor/patch update: update the package and restart the service\n$ sudo systemctl stop mysqld\n$ sudo dnf update mysql-community-server   # or apt upgrade\n$ sudo systemctl start mysqld","note":"MySQL 8.0+ actualiza el diccionario de datos automáticamente al arrancar; no se necesita un paso mysql_upgrade aparte. En replicación, aplique el parche primero a las réplicas y luego al origen.","danger":"caution"},"mssql":{"command":"-- Run the Cumulative Update (CU) or GDR security update installer:\n-- SQLServer2022-KB<no>-x64.exe /quiet /IAcceptSQLServerLicenseTerms\n--   /Action=Patch /InstanceName=MSSQLSERVER\n-- The instance then restarts; verify the version with SERVERPROPERTY.","note":"Las actualizaciones CU (Cumulative Update) y GDR (General Distribution Release) se aplican con el instalador setup y requieren reiniciar la instancia. En un grupo Always On, aplique el parche primero a las réplicas secundarias y haga failover para minimizar el tiempo de inactividad.","danger":"caution"},"mongodb":{"command":"// 'Rolling' patch on a replica set: one node at a time\n// 1) Stop mongod, replace the binary with the new patch version, restart\n// 2) Secondaries first, the primary last\n// Step down the primary manually before patching it:\nrs.stepDown()","note":"Las actualizaciones de parche dentro de la misma versión mayor (7.0.x) se hacen de forma rolling, primero los secondaries. El primary se actualiza tras cederle el rol con rs.stepDown(), lo que minimiza la indisponibilidad de escritura.","danger":"caution"},"redis":{"command":"# Patch via rolling restart in a replicated deployment:\n# 1) Stop one replica node, install the new version, start it, wait for sync\n# 2) Repeat for the remaining replicas\n# 3) Finally, fail over the master and patch it:\nredis-cli -h <replica> REPLICAOF NO ONE   # promote the upgraded replica","note":"Las versiones de parche (7.2.x) se aplican con un rolling restart: primero se actualizan y resincronizan las réplicas, luego se promueve una réplica a master y se actualiza el antiguo master. Sentinel/Cluster automatiza el failover.","danger":"caution"},"elasticsearch":{"command":"# Rolling upgrade (patch within the same major version), per node:\nPUT _cluster/settings\n{ \"persistent\": { \"cluster.routing.allocation.enable\": \"primaries\" } }\n# -> stop the node, update the package, start the node, then:\nPUT _cluster/settings\n{ \"persistent\": { \"cluster.routing.allocation.enable\": null } }","note":"Las actualizaciones de parche/menores se hacen con un rolling upgrade: se restringe la asignación de shards, los nodos se actualizan y reinician de uno en uno, luego se vuelve a habilitar la asignación y se espera a que el clúster vuelva a green.","danger":"caution"}}},{"id":"upgrade-major","title":"Actualización de versión mayor","aliases":["major upgrade","major version","version upgrade","release upgrade","version upgrade"],"engines":{"oracle":{"command":"# Recommended: AutoUpgrade (analyze + fixups + deploy in a single flow):\n$ java -jar $NEW_ORACLE_HOME/rdbms/admin/autoupgrade.jar -config db.cfg -mode deploy\n# Graphical alternative: DBUA (Database Upgrade Assistant)\n$ $NEW_ORACLE_HOME/bin/dbua","note":"AutoUpgrade ejecuta el análisis, los pre-fixups, la actualización y los pasos posteriores con un solo comando; puede procesar varias bases de datos en secuencia para acortar la indisponibilidad. DBUA hace lo mismo con una interfaz guiada. Es obligatorio un backup completo previo.","danger":"danger","priv":"SYSDBA + SO: propietario del nuevo ORACLE_HOME"},"postgres":{"command":"# pg_upgrade to the new major version (with both clusters stopped):\n$ pg_upgrade \\\n    --old-datadir=/var/lib/pgsql/16/data \\\n    --new-datadir=/var/lib/pgsql/17/data \\\n    --old-bindir=/usr/pgsql-16/bin \\\n    --new-bindir=/usr/pgsql-17/bin \\\n    --link   # fast migration with hard links\n# Then gather statistics (analyze_new_cluster.sh was removed in PG14):\n$ vacuumdb --all --analyze-in-stages","note":"pg_upgrade realiza la migración de versión mayor (16 -> 17) in situ; el modo --link acorta el tiempo creando hard links en lugar de copiar archivos, pero invalida el clúster antiguo. Las estadísticas deben recopilarse de nuevo tras la migración.","danger":"danger"},"mysql":{"command":"# In-place upgrade (same datadir, new binaries):\n$ sudo systemctl stop mysqld\n$ sudo dnf install mysql-community-server-8.4   # replacing the old package\n$ sudo systemctl start mysqld   # the server upgrades the dictionary automatically","note":"A partir de MySQL 8.x se admiten actualizaciones mayores in situ: un servidor arrancado con los nuevos binarios convierte automáticamente el diccionario de datos y las tablas del sistema (se eliminó el antiguo paso mysql_upgrade aparte). Actualice una versión mayor cada vez.","danger":"danger"},"mssql":{"command":"-- In-place major version upgrade (with setup):\n-- setup.exe /q /ACTION=upgrade /INSTANCENAME=MSSQLSERVER\n--   /IAcceptSQLServerLicenseTerms\n-- Alternative: install a new instance side by side and move the databases (restore/attach).","note":"setup /ACTION=upgrade actualiza la instancia in situ a la nueva versión mayor. El método más seguro es una instalación en paralelo (side-by-side), mover después las bases de datos con backup/restore y subir el nivel de compatibilidad en un paso aparte.","danger":"danger"},"mongodb":{"command":"// Stepwise major upgrade (e.g. 6.0 -> 7.0), one major version at a time:\n// 1) Roll the binary to 7.0 on all nodes\n// 2) Once the cluster is stable, raise the FCV:\ndb.adminCommand({ setFeatureCompatibilityVersion: \"7.0\", confirm: true })","note":"Una actualización mayor en MongoDB tiene dos fases: primero se pasan los binarios de todos los nodos a la nueva versión y luego, con el clúster estable, se sube el FCV a la nueva versión. Solo se puede subir una versión mayor cada vez; el FCV se gestiona por separado para conservar la posibilidad de downgrade.","danger":"danger"},"redis":{"command":"# Major upgrade via a replica (no downtime):\n# 1) Create a replica on the new version, wait for full sync from the master\n# 2) Prepare the application; fail over the master:\nredis-cli -h <new-replica> REPLICAOF NO ONE   # make the new version the master\n# 3) Upgrade the old master, attach it as a replica of the new master","note":"Una migración de versión mayor de Redis (6.x -> 7.x) se hace montando una réplica en la nueva versión, dejando que se sincronice y promoviéndola después a master; así la indisponibilidad de escritura se reduce casi a cero. Los archivos RDB son compatibles con versiones posteriores.","danger":"danger"},"elasticsearch":{"command":"# Major versions cannot be skipped; one major at a time, via the last minor first (e.g. 6.8 -> 7.17 -> 8.x):\n# 1) Upgrade to the last minor (7.17), resolve the GET _migration/deprecations warnings\n# 2) Rolling upgrade, per node: set allocation to \"primaries\" -> update the node -> start -> wait for green\n# Alternative: full-cluster restart (stop all nodes -> upgrade -> start together); this also moves only one major","note":"Elasticsearch no admite saltarse versiones mayores con ningún método: tanto el rolling upgrade como el full-cluster restart solo pasan a la siguiente mayor (n -> n+1) y requieren que el origen esté en la última menor (p. ej., 7.17). Los índices creados dos versiones mayores antes (p. ej., en 6.x) no se pueden abrir en la nueva versión; deben reconstruirse con _reindex antes de la migración (8.x solo puede leerlos de forma limitada como archive indices de solo lectura). Tome un snapshot y resuelva las advertencias de obsolescencia antes de la migración.","danger":"danger"}}},{"id":"set-compatibility","title":"Establecer el nivel de compatibilidad / versión de funciones","aliases":["compatibility level","database compatibility level","feature version","fcv","compatible","compat level"],"engines":{"oracle":{"command":"-- Raise the COMPATIBLE initialization parameter (irreversible!):\nALTER SYSTEM SET COMPATIBLE = '19.0.0' SCOPE=SPFILE;\n-- Then restart the instance. To see the current value:\nSHOW PARAMETER compatible;","note":"El parámetro COMPATIBLE determina qué funciones y formatos de archivo de cada versión están habilitados. Una vez subido y reiniciada la instancia, NO SE PUEDE REVERTIR (el downgrade queda bloqueado); por eso súbalo de forma gradual hasta confirmar el éxito de la actualización.","danger":"danger","priv":"ALTER SYSTEM (SYSDBA)"},"postgres":{"command":"# Not applicable: PostgreSQL has no database compatibility level.\n# If legacy behavior is needed, set the relevant GUCs (e.g. SET standard_conforming_strings=off)\n# or manage the major migration with dump/restore.","note":"PostgreSQL no tiene un concepto de 'nivel de compatibilidad' como SQL Server/Oracle; el comportamiento de la versión está ligado directamente a la versión binaria. La migración entre versiones se hace con pg_dump/pg_restore o pg_upgrade.","danger":"safe"},"mysql":{"command":"# Not applicable: MySQL has no configurable database compatibility level.\n# Behavioral differences are managed with sql_mode:\nSET GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';","note":"MySQL no tiene un nivel de compatibilidad por versión; el comportamiento SQL depende sobre todo de la versión binaria y de la configuración de sql_mode. Ajuste sql_mode para conservar el comportamiento de aplicaciones heredadas.","danger":"caution"},"mssql":{"command":"-- Set the database compatibility level (e.g. SQL Server 2022 = 160):\nALTER DATABASE Sales SET COMPATIBILITY_LEVEL = 160;\n-- To see the current value:\nSELECT name, compatibility_level FROM sys.databases;","note":"COMPATIBILITY_LEVEL determina a qué versión se asemeja el comportamiento del optimizador de consultas y de las funciones del lenguaje (150=2019, 160=2022). Tras actualizar la instancia, las bases de datos pueden quedarse en el nivel antiguo; se recomienda subirlo de forma gradual y vigilar las regresiones de planes. Se puede volver a bajar.","danger":"caution"},"mongodb":{"command":"// Set the Feature Compatibility Version (FCV):\ndb.adminCommand({ setFeatureCompatibilityVersion: \"7.0\", confirm: true })\n// Read the current value:\ndb.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })","note":"El FCV controla cuándo se habilitan los cambios de formato de datos persistentes y las nuevas funciones que trae una versión. Súbalo tras la actualización binaria con el clúster estable; bajarlo solo es posible si la misma versión binaria sigue admitiendo el FCV inferior.","danger":"caution","priv":"rol clusterManager / root"},"redis":{"command":"# Not applicable: Redis has no compatibility level / feature version concept.\n# Behavior depends directly on the running binary version.","note":"Redis no tiene un nivel de compatibilidad configurable; el conjunto de funciones depende por completo de la versión en ejecución. La compatibilidad solo se mantiene a nivel del formato de archivo RDB/AOF, hacia adelante.","danger":"safe"},"elasticsearch":{"command":"# Not applicable: Elasticsearch has no configurable compatibility level.\n# An index is tied to the format of the version it was created in; very old indices\n# cannot be read in the new version and require a reindex.","note":"Elasticsearch no tiene un ajuste de nivel de compatibilidad; cada índice conserva el formato de la versión mayor en la que se creó. Los índices creados en la versión mayor anterior se pueden leer, mientras que los más antiguos deben pasarse a la nueva versión con _reindex.","danger":"safe"}}},{"id":"post-upgrade-tasks","title":"Tareas posteriores a la actualización (recompile/analyze)","aliases":["post upgrade","post-upgrade","recompile","analyze","refresh statistics","invalid objects"],"engines":{"oracle":{"command":"-- 1) Compile invalid objects in parallel:\n@?/rdbms/admin/utlrp.sql\n-- 2) Refresh dictionary/fixed object statistics:\nEXEC DBMS_STATS.GATHER_DICTIONARY_STATS;\nEXEC DBMS_STATS.GATHER_FIXED_OBJECTS_STATS;\n-- Check remaining invalid objects:\nSELECT owner, object_name, object_type FROM dba_objects WHERE status='INVALID';","note":"utlrp.sql recompila los paquetes/vistas/triggers que quedaron INVALID tras la actualización. Recopilar después las estadísticas del diccionario y de los fixed objects permite al optimizador generar planes correctos en la nueva versión.","danger":"caution","priv":"SYSDBA"},"postgres":{"command":"# 1) Regather statistics after pg_upgrade (mandatory):\n$ vacuumdb --all --analyze-in-stages\n-- 2) Bring extensions to the new version:\nALTER EXTENSION postgis UPDATE;\n-- 3) If the collation changed, rebuild text indexes (can take long):\nREINDEX DATABASE database_name;","note":"pg_upgrade (PG17 y anteriores) no traslada las estadísticas del planificador; analyze-in-stages hace que el sistema sea utilizable rápidamente recopilando primero estadísticas aproximadas y luego las completas. Las extensiones deben actualizarse con ALTER EXTENSION ... UPDATE, y los índices con REINDEX si cambió la collation.","danger":"caution"},"mysql":{"command":"-- 8.0+ dictionary upgrade happens automatically at startup; additionally:\nANALYZE TABLE customer;   -- refresh statistics of critical tables\n-- Legacy 5.7 -> if needed on first startup:\n-- $ mysql_upgrade -u root -p   (before 8.0.16)","note":"En MySQL 8.0.16 y posteriores, la función de mysql_upgrade se ha integrado en el servidor, por lo que no se ejecuta por separado. Tras la actualización, refrescar las estadísticas con ANALYZE TABLE en las tablas grandes/críticas preserva la calidad de los planes.","danger":"safe"},"mssql":{"command":"-- 1) Correct page/row usage counters:\nDBCC UPDATEUSAGE(Sales);\n-- 2) Refresh statistics (sp_updatestats uses sampling; FULLSCAN for critical tables):\nEXEC sp_updatestats;\n-- UPDATE STATISTICS dbo.CriticalTable WITH FULLSCAN;\n-- 3) Raise the compatibility level to the new version (while monitoring):\nALTER DATABASE Sales SET COMPATIBILITY_LEVEL = 160;","note":"Tras la actualización, DBCC UPDATEUSAGE corrige los contadores de espacio/filas inexactos heredados de versiones anteriores y sp_updatestats refresca las estadísticas. Al subir el nivel de compatibilidad, vigile las regresiones de planes que pueda provocar el nuevo Cardinality Estimator (CE).","danger":"caution"},"mongodb":{"command":"// 1) Verify the FCV upgrade:\ndb.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })\n// 2) Check indexes and compatibility warnings in the server logs:\ndb.customer.getIndexes()\n// If an index is incompatible: dropIndex + createIndex (rolling); do NOT use reIndex","note":"Tras una actualización mayor, verifique que el FCV ha alcanzado la versión de destino. Reconstruir los índices no es un paso rutinario de la actualización; si las release notes mencionan un índice o este genera una advertencia en el log, vuelva a crearlo con dropIndex + createIndex. En 6.0+, reIndex solo funciona en un standalone y no se permite en miembros de un replica set.","danger":"caution"},"redis":{"command":"# Usually no extra task is needed. Recommended: verify persistence and replication:\nredis-cli INFO replication   # is the master/replica link healthy\nredis-cli INFO persistence   # rdb_last_bgsave_status / aof_last_bgrewrite_status\nredis-cli DBSIZE             # is the key count as expected","note":"Tras actualizar Redis no hay tareas de compilación/análisis de esquema; se trata de verificar que la replicación se ha restablecido, que la persistencia RDB/AOF está sana y que se conserva el número de claves.","danger":"safe"},"elasticsearch":{"command":"# Verify cluster health:\nGET _cluster/health\n# Move indices created in an old major version that can no longer be read:\nPOST _reindex\n{ \"source\": { \"index\": \"old_index\" }, \"dest\": { \"index\": \"new_index\" } }","note":"Tras la actualización, verifique que el estado del clúster es 'green' y que todos los shards están asignados. Los índices con un formato anterior a la versión mayor previa no se pueden leer en la nueva versión, por lo que deben pasarse al formato actual con _reindex.","danger":"caution"}}}]},"rosetta/migration.json":{"category":"Migración y transferencia","categoryId":"migration","order":15,"concepts":[{"id":"compare-schema","title":"Comparar dos esquemas/entornos (schema diff)","aliases":["schema diff","diff","compare schemas","compare","schema difference","ddl diff"],"engines":{"oracle":{"command":"-- Extract the schema DDL from both environments and take a text diff\n-- (generate deterministic DDL with DBMS_METADATA)\nEXEC DBMS_METADATA.SET_TRANSFORM_PARAM(DBMS_METADATA.SESSION_TRANSFORM,'SQLTERMINATOR',TRUE);\nEXEC DBMS_METADATA.SET_TRANSFORM_PARAM(DBMS_METADATA.SESSION_TRANSFORM,'STORAGE',FALSE);\nSELECT DBMS_METADATA.GET_DDL('TABLE', table_name) FROM user_tables ORDER BY table_name;\n-- Spool the output to a file in both environments and compare with diff/fc.\n\n-- Export the schema as a changelog with SQLcl's Liquibase integration:\n-- SQL> lb generate-schema\n-- Commercial: the Oracle SQL Developer 'Database Diff' tool is also used.","note":"Oracle no tiene un schema diff integrado de un solo comando. El método habitual es generar DDL determinista desde ambos entornos con DBMS_METADATA.GET_DDL (con los atributos STORAGE/de segmento desactivados) y hacer un diff de texto. Para una comparación visual/automatizada, use el asistente Database Diff de SQL Developer o SQLcl.","danger":"safe","readonly":true,"priv":"SELECT_CATALOG_ROLE o acceso de lectura al esquema"},"postgres":{"command":"# Dump only the schema from both databases and diff them\npg_dump --schema-only --no-owner --no-privileges sourcedb > source_schema.sql\npg_dump --schema-only --no-owner --no-privileges targetdb  > target_schema.sql\ndiff -u source_schema.sql target_schema.sql\n\n# Tool that generates applicable ALTERs: apgdiff (Another PostgreSQL Diff Tool)\n# apgdiff source_schema.sql target_schema.sql > migration.sql\n# Alternatives: migra, Liquibase diffChangeLog","note":"La forma más sencilla es obtener la salida de pg_dump --schema-only en ambos entornos y compararla con diff. apgdiff y migra son herramientas que convierten la diferencia directamente en sentencias ALTER ejecutables. Añada --no-owner --no-privileges para una salida determinista.","danger":"safe","readonly":true,"priv":"SELECT / lectura del esquema (acceso a objetos para pg_dump)"},"mysql":{"command":"# Schema/object comparison with MySQL Utilities (5.x):\nmysqldbcompare --server1=root:password@host1 --server2=root:password@host2 \\\n  shop:shop --difftype=sql --run-all-tests\n\n# Built-in method: take CREATE statements from both environments and diff them\nmysqldump --no-data --skip-comments shop > source_schema.sql\n# SHOW CREATE TABLE customer;  (to see a single table definition)","note":"La herramienta mysqldbcompare de MySQL Utilities informa de las diferencias de objetos/esquema entre dos servidores (como sentencias aplicables con difftype=sql). El método sin herramientas es obtener la salida de mysqldump --no-data (solo esquema) o de SHOW CREATE TABLE en ambos entornos y comparar con diff. mysqldbcompare ya está obsoleto; para MariaDB/8.x se prefiere Liquibase o Skeema.","danger":"safe","readonly":true,"priv":"SELECT, SHOW VIEW sobre ambos esquemas"},"mssql":{"command":"-- Schema comparison/deploy report with SqlPackage (what will change before applying):\nsqlpackage /Action:DeployReport /SourceFile:source.dacpac \\\n  /TargetConnectionString:\"Server=target;Database=shop;Integrated Security=true\" \\\n  /OutputPath:diff_report.xml\n\n-- For data differences (row level), the tablediff utility:\ntablediff -sourceserver S1 -sourcedatabase shop -sourcetable customer \\\n  -destinationserver S2 -destinationdatabase shop -destinationtable customer","note":"Para diferencias de esquema, SqlPackage /Action:DeployReport devuelve como informe XML los cambios que se aplicarían al destino; /Action:Script genera un script de diferencias ejecutable. 'Schema Compare' de Visual Studio / Azure Data Studio usa el mismo motor. Para diferencias de datos a nivel de fila existe la utilidad de línea de comandos tablediff.","danger":"safe","readonly":true,"priv":"VIEW DEFINITION (acceso de lectura al destino para DeployReport)"},"mongodb":{"command":"// The schema is implicit; extract and compare the field structure of both environments.\n// Read the $jsonSchema validator definition (collection rules):\ndb.getCollectionInfos({ name: \"customer\" })[0].options.validator\n\n// External tools for inferred schema comparison by sampling:\n// mongodb-schema (npm) or the Compass 'Schema' tab\n// Take samples with mongoexport and diff the field/type distribution of both environments.","note":"MongoDB no tiene esquema; no hay DDL fijo. La comparación se hace de dos formas: comparar las reglas del validador $jsonSchema definidas en la colección (mediante getCollectionInfos), o generar un esquema inferido a partir de los datos con mongodb-schema / Compass y comparar las distribuciones de campos y tipos. Para los índices, haga un diff de la salida de db.collection.getIndexes().","danger":"safe","readonly":true,"priv":"read (getCollectionInfos, listIndexes)"},"redis":{"command":"# Not applicable: Redis is a schemaless key-value store; there are no\n# table/column definitions (DDL) to compare.\n# Closest equivalent: compare the key space (namespace) structure\n# redis-cli --scan --pattern 'customer:*' | sort > source_keys.txt","note":"No aplica: Redis no tiene concepto de esquema (tabla/columna/DDL), por lo que un schema diff clásico no tiene sentido. Si se necesita una comparación estructural, solo se puede listar con SCAN el patrón de nombres de claves (namespace) y compararlo.","danger":"safe","readonly":true},"elasticsearch":{"command":"# Fetch the index mapping from both clusters and compare\ncurl -s 'http://source:9200/customer/_mapping?pretty' > source_mapping.json\ncurl -s 'http://target:9200/customer/_mapping?pretty'  > target_mapping.json\ndiff -u source_mapping.json target_mapping.json\n\n# To compare settings as well:\n# GET customer/_settings   and   GET customer/_mapping","note":"En Elasticsearch, el equivalente de un esquema es el mapping del índice. El método estándar es obtener la salida de GET <index>/_mapping (y _settings si es necesario) de ambos clústeres y comparar con diff. Como los campos del mapping son mayoritariamente inmutables, una diferencia detectada puede requerir un índice nuevo + _reindex.","danger":"safe","readonly":true,"priv":"view_index_metadata / monitor"}}},{"id":"export-for-migration","title":"Exportación consistente para migración","aliases":["export","consistent export","consistent export","take a dump","migration export","snapshot"],"engines":{"oracle":{"command":"# Consistent logical export with Data Pump (at a single SCN)\nexpdp system/password schemas=shop directory=DIR_DMP \\\n  dumpfile=shop_%U.dmp logfile=shop_exp.log \\\n  flashback_scn=<scn> parallel=4\n\n# Current SCN for point-in-time consistency:\n# SELECT current_scn FROM v$database;\n# Alternative: FLASHBACK_TIME=SYSTIMESTAMP","note":"Data Pump (expdp) es la herramienta estándar de exportación lógica para migraciones. Para la consistencia de lectura, pase FLASHBACK_SCN=<scn> (o FLASHBACK_TIME) para que todas las tablas se exporten de forma consistente en un único SCN; esto es crítico en un sistema en vivo. PARALLEL lo acelera y %U genera varios archivos.","danger":"caution","readonly":true,"priv":"DATAPUMP_EXP_FULL_DATABASE, objeto DIRECTORY","minver":"10g (Data Pump)"},"postgres":{"command":"# Single compressed file in custom format (restorable in parallel)\npg_dump -Fc -Z6 -f shop.dump shop\n\n# Directory format (parallel dump + parallel restore):\npg_dump -Fd -j4 -f shop_dir shop\n\n# For the whole cluster + roles/tablespaces:\n# pg_dumpall --globals-only > roles.sql","note":"Para migraciones se prefiere el formato pg_dump -Fc (custom) o -Fd (directory); ambos permiten una carga selectiva/en paralelo con pg_restore. Como pg_dump se ejecuta dentro de una única transacción, su salida es automáticamente un snapshot consistente. Los objetos de todo el clúster, como roles y tablespaces, se vuelcan por separado con pg_dumpall --globals-only.","danger":"safe","readonly":true,"priv":"SELECT (sobre todos los objetos a volcar)"},"mysql":{"command":"# Consistent, lock-free export for InnoDB\nmysqldump --single-transaction --quick --routines --triggers --events \\\n  --source-data=2 shop > shop.sql\n\n# Note: older versions use --master-data=2 instead of --source-data.\n# Parallel, fast alternative: mydumper -B shop -o ./backup --threads 4","note":"En InnoDB, --single-transaction proporciona un único snapshot consistente y no bloquea las tablas. --source-data=2 (antes --master-data=2) escribe como comentario al principio de la salida la posición del binlog/GTID necesaria para iniciar la replicación/CDC; esto es esencial para un cutover sin indisponibilidad. Para grandes volúmenes, mydumper funciona en paralelo.","danger":"safe","readonly":true,"priv":"SELECT, LOCK TABLES, RELOAD, REPLICATION CLIENT (para --source-data)"},"mssql":{"command":"-- Full backup for physical migration (to the same/compatible version):\nBACKUP DATABASE shop TO DISK = 'C:\\backup\\shop.bak' WITH COMPRESSION, CHECKSUM;\n\n-- Portable/version-independent logical export (BACPAC = schema+data):\n-- sqlpackage /Action:Export /SourceConnectionString:\"...\" /TargetFile:shop.bacpac\n\n-- For a single table: bcp shop.dbo.customer out customer.dat -n -S server -T","note":"Al migrar a un SQL Server de la misma versión o más reciente, BACKUP ... RESTORE es la vía más fiable (la integridad se verifica con CHECKSUM). Para una migración independiente de versión/plataforma o un traslado a Azure SQL, se prefiere exportar esquema+datos a un único archivo .bacpac con SqlPackage /Action:Export. bcp se usa para tablas individuales.","danger":"safe","readonly":true,"priv":"BACKUP DATABASE (db_backupoperator) o acceso de lectura para Export"},"mongodb":{"command":"# Consistent binary (BSON) export; point-in-time consistency with the oplog\nmongodump --uri=\"mongodb://source:27017\" \\\n  --oplog --out=./backup --numParallelCollections=4\n\n# --oplog captures changes made during the dump (requires a replica set);\n# it works only with a FULL dump and cannot be combined with --db / --collection.\n# On restore, mongorestore --oplogReplay brings it to a single point in time.\n# If you need a single database: take it with --db=shop (without the oplog) or use --nsInclude='shop.*' on restore.","note":"mongodump es la exportación binaria (BSON) estándar para migraciones. En un replica set, la opción --oplog captura del oplog los cambios escritos mientras se ejecuta el dump, lo que proporciona un punto en el tiempo consistente (junto con mongorestore --oplogReplay); esta opción solo es válida para un dump completo. Para la migración de un clúster grande a nivel de clúster, mongosync/herramientas en vivo son más adecuadas.","danger":"safe","readonly":true,"priv":"read (read sobre la db local para el oplog / clusterMonitor)"},"redis":{"command":"# Create an RDB snapshot in the background (non-blocking)\nredis-cli BGSAVE\n# Check for completion:\nredis-cli INFO persistence | grep rdb_bgsave_in_progress\n\n# Move the resulting file (path: CONFIG GET dir + dbfilename)\n# e.g. /var/lib/redis/dump.rdb  ->  copy to the target server","note":"En Redis, la exportación consistente es el snapshot RDB. BGSAVE genera en segundo plano, mediante fork, un dump.rdb consistente sin bloquear el proceso principal (SAVE bloquea y no debe usarse). La ruta del archivo se obtiene con CONFIG GET dir y dbfilename; migre copiando este dump.rdb al directorio de datos del servidor de destino.","danger":"safe","readonly":true},"elasticsearch":{"command":"# 1) Register a snapshot repository (shared file system / S3)\nPUT _snapshot/migration_repo\n{ \"type\": \"fs\", \"settings\": { \"location\": \"/mnt/es_backup\" } }\n\n# 2) Take a consistent snapshot\nPUT _snapshot/migration_repo/migration_1?wait_for_completion=true\n{ \"indices\": \"customer,orders\", \"include_global_state\": false }","note":"En Elasticsearch, el método de exportación consistente para migraciones es el snapshot. Primero se registra un repositorio (fs, S3, GCS, etc.) y luego se toma el snapshot; los snapshots son incrementales y capturan un estado consistente del momento en que se toman. Para datos pequeños/un solo índice también se puede usar elasticdump, pero el estándar es el snapshot.","danger":"safe","readonly":true,"priv":"manage / create_snapshot"}}},{"id":"import-for-migration","title":"Importar en el destino","aliases":["import","import data","restore","load","load into target","load"],"engines":{"oracle":{"command":"# Import with Data Pump; remap schema and tablespace\nimpdp system/password directory=DIR_DMP dumpfile=shop_%U.dmp \\\n  logfile=shop_imp.log \\\n  remap_schema=shop:shop_new \\\n  remap_tablespace=USERS:DATA parallel=4\n\n# For existing objects: TABLE_EXISTS_ACTION=TRUNCATE|APPEND|REPLACE","note":"impdp carga en el destino el archivo .dmp obtenido con expdp. REMAP_SCHEMA traslada el origen a otro esquema y REMAP_TABLESPACE a otro tablespace; se usan a menudo al migrar entre entornos. El comportamiento ante tablas existentes se define con TABLE_EXISTS_ACTION, y la velocidad con PARALLEL.","danger":"caution","priv":"DATAPUMP_IMP_FULL_DATABASE, cuota en el tablespace de destino"},"postgres":{"command":"# Parallel restore (from custom/directory format)\npg_restore -j4 -d targetdb --no-owner --clean --if-exists shop.dump\n\n# A specific table only: pg_restore -d targetdb -t customer shop.dump\n# For a plain .sql dump:  psql -d targetdb -f shop.sql","note":"Con pg_restore -j, los dumps en formato custom/directory se cargan en paralelo (varios procesos); esto acelera mucho la carga en bases de datos grandes. --clean --if-exists elimina primero los objetos del destino y --no-owner se adapta a una estructura de roles distinta. Los dumps SQL planos se cargan con psql -f (no en paralelo).","danger":"caution","priv":"privilegios CREATE / de propietario sobre la base de datos de destino"},"mysql":{"command":"# Load a plain SQL dump\nmysql --binary-mode shop < shop.sql\n\n# Parallel/fast load (if taken with mydumper):\nmyloader --directory=./backup --database=shop --threads=4 --overwrite-tables\n\n# Tip: SET FOREIGN_KEY_CHECKS=0; unique_checks=0 before loading speeds it up.","note":"Un dump SQL de mysqldump se carga redirigiéndolo al cliente mysql (mysql shop < dump.sql). Para una carga en paralelo y mucho más rápida, un directorio obtenido con mydumper se restaura con myloader --threads. Desactivar FOREIGN_KEY_CHECKS y UNIQUE_CHECKS durante la carga mejora el rendimiento.","danger":"caution","priv":"INSERT, CREATE (y DROP, para --overwrite/--clean)"},"mssql":{"command":"-- Restore from a physical backup (move file paths to the new environment)\nRESTORE DATABASE shop FROM DISK = 'C:\\backup\\shop.bak'\nWITH MOVE 'shop_data' TO 'D:\\data\\shop.mdf',\n     MOVE 'shop_log'  TO 'D:\\log\\shop.ldf', REPLACE;\n\n-- Import from a BACPAC (schema+data):\n-- sqlpackage /Action:Import /TargetConnectionString:\"...\" /SourceFile:shop.bacpac","note":"Un backup .bak se carga con RESTORE DATABASE; al pasar a una distribución de discos distinta, WITH MOVE redirige los archivos de datos/log a nuevas rutas. Para un destino independiente de versión/Azure SQL, SqlPackage /Action:Import crea a partir del archivo .bacpac (esquema + datos) una nueva base de datos.","danger":"caution","priv":"dbcreator/sysadmin para RESTORE; CREATE DATABASE para Import"},"mongodb":{"command":"# Restore the BSON backup; replay the oplog to land on a single point in time\nmongorestore --uri=\"mongodb://target:27017\" --oplogReplay \\\n  --numParallelCollections=4 ./backup\n\n# Renaming: --nsFrom='shop.*' --nsTo='shop_new.*'\n# Drop existing data before loading: --drop","note":"mongorestore carga en el destino la salida BSON de mongodump. --oplogReplay vuelve a aplicar los cambios del oplog de un backup obtenido con --oplog, llevando el dump a un único punto en el tiempo consistente. Los nombres de base de datos/colección se cambian con --nsFrom/--nsTo, y los datos existentes se pueden borrar con --drop.","danger":"caution","priv":"readWrite (restore/clusterAdmin para oplogReplay)"},"redis":{"command":"# Stop the server, place dump.rdb in the data directory, start it again\n# 1) systemctl stop redis  (or SHUTDOWN NOSAVE)\n# 2) cp dump.rdb  <path found with CONFIG GET dir>/dump.rdb\n# 3) systemctl start redis   -> Redis loads the RDB into memory at startup\n\n# Alternative (live): feed a RESP command stream with redis-cli --pipe","note":"Redis solo carga el RDB al arrancar. Por eso se detiene el servidor de destino, se coloca el dump.rdb migrado en el directorio de datos (CONFIG GET dir) y se reinicia el servidor; al arrancar, la memoria se carga desde el RDB. Para cargar sin detener el servidor, envíe las claves en formato RESP con redis-cli --pipe.","danger":"caution"},"elasticsearch":{"command":"# Register the same repository on the target cluster, then restore the snapshot\nPUT _snapshot/migration_repo\n{ \"type\": \"fs\", \"settings\": { \"location\": \"/mnt/es_backup\" } }\n\nPOST _snapshot/migration_repo/migration_1/_restore\n{ \"indices\": \"customer,orders\",\n  \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_$1\" }","note":"En el clúster de destino, registre el mismo repositorio de snapshots que en el origen (los archivos deben ser accesibles) y luego restaure los índices con _snapshot/.../_restore. Los nombres de índice se pueden cambiar con rename_pattern/rename_replacement. Los índices existentes no se pueden restaurar mientras estén abiertos; ciérrelos o renómbrelos antes.","danger":"caution","priv":"manage (create_index, restore)"}}},{"id":"heterogeneous-migration","title":"Migrar a otra base de datos (heterogénea)","aliases":["heterogeneous","heterogeneous migration","cross-database","different database","convert database","convert"],"engines":{"oracle":{"command":"# Oracle -> PostgreSQL: Ora2Pg (schema + data + PL/SQL conversion)\nora2pg -c ora2pg.conf -t TABLE   -o schema.sql\nora2pg -c ora2pg.conf -t COPY    -o data.sql   # data in COPY format\nora2pg -t SHOW_REPORT --estimate_cost           # migration cost estimate\n\n# Oracle -> (multiple targets) managed/online: AWS DMS or Oracle GoldenGate","note":"Para la migración heterogénea desde Oracle (sobre todo a PostgreSQL), la herramienta open source más habitual es Ora2Pg: convierte tablas, restricciones, PL/SQL y datos al dialecto de destino; --estimate_cost estima el esfuerzo de migración. Para una migración en línea/con poca indisponibilidad, se usa AWS DMS u Oracle GoldenGate (con CDC).","danger":"caution","readonly":true,"priv":"SELECT sobre el esquema de origen (acceso de lectura a las vistas DBA)"},"postgres":{"command":"# MySQL -> PostgreSQL: pgloader (schema+data+type conversion in one command)\npgloader mysql://app_user:password@source/shop \\\n         postgresql://app_user:password@target/shop\n\n# Also from SQLite/MSSQL sources: with a pgloader ./migration.load script\n# For a managed/online PostgreSQL target: AWS DMS","note":"pgloader es la herramienta estándar para migrar a PostgreSQL desde otros motores (MySQL, SQLite, MS SQL): con cadenas de conexión crea el esquema en un solo comando, mapea los tipos y copia los datos rápidamente; las transformaciones/mapeos de tipos personalizados se definen en un script .load. Si el origen es Oracle, se prefiere Ora2Pg; para migraciones en línea, AWS DMS.","danger":"caution","priv":"CREATE sobre el destino; SELECT sobre el origen"},"mysql":{"command":"# PostgreSQL/Oracle/MSSQL -> MySQL: AWS DMS (managed) is the most common path\n# or adapt the source-specific export for MySQL:\n#   pg_dump --inserts ... | (type/syntax fixes) | mysql shop\n\n# MySQL <-> MariaDB (compatible): mysqldump output loads directly\nmysqldump --single-transaction shop | mysql -h target_mariadb shop","note":"No hay una única herramienta abierta dominante para migrar a MySQL desde otros motores; la solución más habitual es AWS DMS (gestionado, con soporte de CDC) o adaptar el dump de origen en sintaxis/tipos y cargarlo con el cliente mysql. MySQL y MariaDB son en gran medida compatibles; la salida de mysqldump suele migrar directamente.","danger":"caution","priv":"CREATE, INSERT sobre el destino"},"mssql":{"command":"# SQL Server -> PostgreSQL: with pgloader\npgloader mssql://app_user:password@source/shop \\\n         postgresql://app_user:password@target/shop\n\n# SQL Server -> PostgreSQL compatibility layer: Babelfish for Aurora PostgreSQL\n# (runs T-SQL and the TDS protocol on top of PostgreSQL)\n# To other targets: SSMA (SQL Server Migration Assistant) or AWS DMS","note":"pgloader (origen mssql://) se usa para migrar de SQL Server a PostgreSQL; si quiere conservar T-SQL sin modificar la aplicación, Babelfish for Aurora PostgreSQL ofrece una capa de compatibilidad. La herramienta SSMA de Microsoft automatiza la migración desde otros motores (Oracle, MySQL, DB2) a SQL Server. Para migraciones en línea, AWS DMS.","danger":"caution","priv":"SELECT sobre el origen; CREATE sobre el destino"},"mongodb":{"command":"# Relational <-> MongoDB conversion requires schema mapping (ETL):\n#   - Relational Migrator (official MongoDB tool): maps tables to the document model\n#   - or generate JSON from the source -> load with mongoimport\nmongoexport --db=shop --collection=customer --type=csv --fields=id,name > c.csv\n# (To an SQL target: mongoexport CSV -> relational COPY/LOAD DATA)","note":"La migración entre MongoDB y motores SQL requiere una transformación del modelo fila<->documento (ETL); no hay un comando único automático. De SQL a MongoDB, MongoDB Relational Migrator mapea las tablas a un esquema de documentos; en casos sencillos, genere CSV/JSON desde el origen y cárguelo con mongoimport. En sentido inverso, la salida de mongoexport se carga en la tabla relacional de destino con COPY/LOAD DATA.","danger":"caution","priv":"read sobre el origen, write sobre el destino"},"redis":{"command":"# With RIOT (Redis Input/Output Tools), to/from Redis from other sources:\n#   Relational -> Redis:\nriot db-import --jdbc-url \"jdbc:postgresql://host/shop\" \\\n  --sql \"SELECT * FROM customer\" hset --keyspace customer --keys id\n#   Redis -> another Redis / file:  riot replicate / riot file-export","note":"En una migración heterogénea, Redis suele ser la capa de caché como destino/origen; para cargar datos en Redis desde fuentes relacionales o archivos se usa la herramienta oficial RIOT de Redis (db-import mapea el resultado de una consulta SQL a estructuras HSET/SET). Para Redis a Redis o a un archivo existen los comandos riot replicate / file-export.","danger":"caution"},"elasticsearch":{"command":"# Relational/other source -> Elasticsearch: Logstash (JDBC input)\n# logstash.conf: input { jdbc { statement => \"SELECT * FROM customer\" } }\n#                output { elasticsearch { index => \"customer\" } }\n\n# ES -> ES (version/cluster upgrade): reindex from a remote source\nPOST _reindex\n{ \"source\": { \"remote\": { \"host\": \"http://old:9200\" }, \"index\": \"customer\" },\n  \"dest\": { \"index\": \"customer\" } }","note":"La herramienta más habitual para migrar desde otras bases de datos a Elasticsearch es Logstash (entrada JDBC/file -> salida elasticsearch); también se usan los connectors/Beats propios de Elastic. Al pasar de un clúster Elasticsearch antiguo a uno nuevo, es habitual el remote reindex (_reindex con un origen 'remote'); reindex.remote.whitelist debe estar definido en el destino.","danger":"caution","priv":"write/create_index sobre el destino"}}},{"id":"setup-cdc-replication","title":"Configurar replicación continua / CDC (cutover sin indisponibilidad)","aliases":["cdc","replication","replication","zero-downtime migration","live migration","change data capture"],"engines":{"oracle":{"command":"# CDC-based replication with Oracle GoldenGate (outline)\n# 1) Enable supplemental logging on the source:\nALTER DATABASE ADD SUPPLEMENTAL LOG DATA;\n# 2) Extract (capture changes from redo) and Replicat (apply to the target) processes:\n#   GGSCI> ADD EXTRACT ext1, INTEGRATED TRANLOG, BEGIN NOW\n#   GGSCI> ADD REPLICAT rep1, INTEGRATED\n# Alternative: initial load with Data Pump + incremental capture with GoldenGate","note":"En Oracle, la herramienta estándar para CDC heterogéneo/sin indisponibilidad es Oracle GoldenGate: el proceso Extract captura los cambios de los redo logs y Replicat los aplica en el destino; debe estar habilitado el supplemental logging. Migración típica: la carga inicial se hace con Data Pump y, a partir de ahí, GoldenGate envía los cambios al destino, lo que permite un cutover con una indisponibilidad casi nula.","danger":"caution","priv":"privilegios de administrador de GoldenGate, supplemental logging"},"postgres":{"command":"-- Source (publisher): create a publication (wal_level must be logical)\nCREATE PUBLICATION migration_pub FOR ALL TABLES;\n\n-- Target (subscriber): create a subscription (initial copy + continuous stream)\nCREATE SUBSCRIPTION migration_sub\n  CONNECTION 'host=source dbname=shop user=repl password=***'\n  PUBLICATION migration_pub;","note":"PostgreSQL ofrece CDC con la replicación lógica integrada: CREATE PUBLICATION en el origen, CREATE SUBSCRIPTION en el destino. Al iniciarse, la suscripción copia los datos existentes (copia inicial) y luego transmite continuamente los cambios desde el WAL; se requiere wal_level=logical. Para escenarios grandes/complejos se prefiere pglogical o Debezium.","danger":"caution","priv":"rol REPLICATION en el origen; superuser en el destino para CREATE SUBSCRIPTION (en PG16+ también basta el rol pg_create_subscription)","minver":"10 (replicación lógica)"},"mysql":{"command":"-- Source: binlog must be enabled and in ROW format (log_bin, binlog_format=ROW)\n-- Grant a replication user: GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';\n\n-- Define the source on the target (start from the binlog position of the initial load):\nCHANGE REPLICATION SOURCE TO\n  SOURCE_HOST='source', SOURCE_USER='repl', SOURCE_PASSWORD='***',\n  SOURCE_LOG_FILE='binlog.000001', SOURCE_LOG_POS=1547;\nSTART REPLICA;","note":"En MySQL, un cutover sin indisponibilidad se hace con replicación basada en binlog. La carga inicial se obtiene con mysqldump --source-data y se registra la posición del binlog/GTID; en el destino, la replicación se inicia desde esa posición con CHANGE REPLICATION SOURCE TO (sintaxis de 8.0.23+; antes CHANGE MASTER TO). START REPLICA aplica los cambios de forma continua. Una configuración basada en GTID es más segura.","danger":"caution","priv":"REPLICATION SLAVE (usuario repl), REPLICATION CLIENT","minver":"8.0.23 (CHANGE REPLICATION SOURCE)"},"mssql":{"command":"-- Option A: Transactional Replication (publisher->distributor->subscriber) — SSMS wizard\n\n-- Option B: Change Data Capture (track changes as tables)\nEXEC sys.sp_cdc_enable_db;              -- enable CDC at database level\nEXEC sys.sp_cdc_enable_table\n  @source_schema = N'dbo', @source_name = N'customer',\n  @role_name = NULL;                    -- enable CDC at table level\n-- Changes are read from the cdc.dbo_customer_CT table and moved to the target.","note":"SQL Server tiene dos vías principales para la replicación continua: si quiere un flujo directo hacia el destino, configure Transactional Replication (publisher/subscriber); si va a mover los cambios con su propio ETL, habilite Change Data Capture (sp_cdc_enable_db + sp_cdc_enable_table) y lea de las tablas _CT. Para destinos heterogéneos, CDC se combina con AWS DMS/Debezium.","danger":"caution","priv":"sysadmin (para habilitar CDC); SQL Server Agent debe estar en ejecución"},"mongodb":{"command":"// Watch changes live with Change Streams (requires a replica set/sharded cluster)\nconst cs = db.customer.watch([], { fullDocument: \"updateLookup\" });\nwhile (cs.hasNext()) { printjson(cs.next()); }  // apply to the target\n\n// Official tool for managed cluster-to-cluster replication/migration: mongosync\n// mongosync --cluster0 \"mongodb://source\" --cluster1 \"mongodb://target\"","note":"En MongoDB se usan Change Streams para CDC: db.collection.watch() transmite en vivo desde el oplog los eventos insert/update/delete para aplicarlos en el destino (se requiere un replica set). Para una migración/replicación continua completa de clúster a clúster, la herramienta oficial Cluster-to-Cluster Sync de MongoDB, mongosync (no exclusiva de Atlas; también funciona con clústeres autogestionados), gestiona la carga inicial + la sincronización continua.","danger":"caution","priv":"changeStream (read); clusterAdmin para mongosync"},"redis":{"command":"# Make the target instance a replica of the source (live synchronization)\n# On the target Redis:\nREPLICAOF source_host 6379\n# Monitor sync status:\nINFO replication   # master_link_status must be up\n\n# Once the cutover is complete, make the target standalone:\nREPLICAOF NO ONE","note":"En Redis, un cutover sin indisponibilidad se hace con la replicación nativa: ejecute REPLICAOF <source host port> (antes SLAVEOF) en el servidor de destino; el destino recibe primero el RDB completo y luego se mantiene sincronizado de forma continua mediante el flujo de comandos. Verifique master_link_status:up con INFO replication y, una vez completado el cutover, convierta el destino en standalone con REPLICAOF NO ONE.","danger":"caution"},"elasticsearch":{"command":"# Incremental reindex from the old cluster to the new one within the same version family\nPOST _reindex?wait_for_completion=false\n{ \"source\": { \"remote\": { \"host\": \"http://old:9200\" }, \"index\": \"customer\",\n    \"query\": { \"range\": { \"updated_at\": { \"gt\": \"<last-cursor>\" } } } },\n  \"dest\": { \"index\": \"customer\" } }\n\n# Licensed feature: Cross-Cluster Replication (CCR) — continuous following\n# On the target (follower) cluster: PUT /customer/_ccr/follow { \"remote_cluster\": \"old\", \"leader_index\": \"customer\" }","note":"La función real de replicación continua en Elasticsearch es Cross-Cluster Replication (CCR): el clúster de destino sigue al leader index del origen y extrae los cambios de forma continua (requiere una licencia comercial/Platinum). Sin licencia, se logra una sincronización casi continua con remote reindex periódico (incremental, basado en un campo de tiempo/secuencia).","danger":"caution","priv":"manage/read_ccr para CCR; whitelist para remote reindex"}}},{"id":"verify-migration","title":"Verificación posterior a la migración (número de filas/checksum)","aliases":["verify","verification","row count","checksum","row count","consistency check"],"engines":{"oracle":{"command":"-- Compare row counts on source and target\nSELECT 'customer' table_name, COUNT(*) row_count FROM customer;\n\n-- To find content differences at object level, DBMS_COMPARISON (via db link)\nBEGIN DBMS_COMPARISON.CREATE_COMPARISON(\n  comparison_name => 'COMPARE_CUSTOMER',\n  schema_name => 'SHOP', object_name => 'CUSTOMER',\n  dblink_name => 'TARGET_DB'); END;\n/\n-- SELECT DBMS_COMPARISON.COMPARE('COMPARE_CUSTOMER', scan_info) FROM dual;","note":"La comprobación más rápida es comparar COUNT(*) por tabla en ambos lados. Para encontrar diferencias reales de contenido a nivel de fila, use el paquete DBMS_COMPARISON: compara las tablas de origen y destino fila a fila a través de un database link y puede informar/converger las diferencias. Para checksums, se puede obtener un digest de columnas con ORA_HASH o STANDARD_HASH.","danger":"safe","readonly":true,"priv":"SELECT en ambos lados; EXECUTE sobre DBMS_COMPARISON"},"postgres":{"command":"-- Row count\nSELECT count(*) FROM customer;\n\n-- Ordered digest (checksum) of the whole table content — compare on both sides\nSELECT md5(string_agg(t.row_md5, '' ORDER BY t.row_md5)) AS digest\nFROM (SELECT md5(customer::text) AS row_md5 FROM customer) t;","note":"El primer paso es comparar count(*) en cada tabla. Para verificar el contenido, aplique md5 a la representación de texto de cada fila, concaténelas en orden y vuelva a calcular el hash; así se obtiene un checksum independiente del orden en ambos lados. Para grandes volúmenes, calcule el digest solo sobre las columnas mutables/clave; ordenar por PK es importante para la repetibilidad.","danger":"safe","readonly":true,"priv":"SELECT sobre la tabla"},"mysql":{"command":"-- Built-in table checksum (run on both servers and compare)\nCHECKSUM TABLE customer;\n\n-- Row count\nSELECT COUNT(*) FROM customer;\n\n-- Preferred tool for live/large environments (compares row chunks online):\n-- pt-table-checksum h=source,D=shop  (Percona Toolkit)","note":"En MySQL, CHECKSUM TABLE da un único checksum del contenido de la tabla; ejecútelo en origen y destino y compare (el engine/versión debe ser el mismo). Use COUNT(*) para el número de filas. En producción en vivo y para la consistencia de la replicación, el estándar del sector es pt-table-checksum de Percona Toolkit; compara por chunks, en línea y con bajo impacto.","danger":"safe","readonly":true,"priv":"SELECT sobre la tabla"},"mssql":{"command":"-- Full row-level comparison (official tool that produces differences)\ntablediff -sourceserver S1 -sourcedatabase shop -sourcetable customer \\\n  -destinationserver S2 -destinationdatabase shop -destinationtable customer\n\n-- Quick aggregate checksum (single query, compare on both sides):\nSELECT COUNT_BIG(*) row_count, CHECKSUM_AGG(BINARY_CHECKSUM(*)) digest FROM dbo.customer;","note":"En SQL Server, la herramienta oficial de línea de comandos que encuentra diferencias a nivel de fila es tablediff (informa qué filas se añadieron/cambiaron y, con la opción -f, escribe un script de corrección). Para una comprobación global rápida, CHECKSUM_AGG(BINARY_CHECKSUM(*)) da un único digest de la tabla; compare ambos lados junto con COUNT_BIG(*) (independiente del orden de las filas).","danger":"safe","readonly":true,"priv":"SELECT sobre ambas tablas"},"mongodb":{"command":"// Compare document counts (on source and target)\ndb.customer.countDocuments({});\n\n// For a content checksum, dbHash (md5 digest at collection/DB level):\ndb.runCommand({ dbHash: 1, collections: [\"customer\"] });\n// -> compare the md5 field across both environments.","note":"La primera comprobación es comparar el número de documentos en ambos lados con countDocuments() (estimatedDocumentCount es rápido pero se basa en metadatos; use countDocuments para un recuento exacto). Para verificar el contenido, el comando dbHash genera un digest md5 a nivel de colección/base de datos; compare los hashes de ambos entornos. Revise los índices por separado con getIndexes().","danger":"safe","readonly":true,"priv":"read (acceso para dbHash)"},"redis":{"command":"# Compare key counts (on source and target)\nredis-cli DBSIZE\n\n# For a memory/data summary and key distribution:\nredis-cli INFO keyspace     # db0:keys=... information\n# For full content verification: DUMP each key and compare with the target\n# (e.g. redis-cli --scan | xargs -I{} redis-cli DUMP {})","note":"En Redis, la verificación básica es comparar el número total de claves con DBSIZE; INFO keyspace muestra la distribución por base de datos. Redis no tiene un checksum de tabla integrado; si se necesita una verificación exacta del contenido, obtenga la serialización DUMP por clave y compare ambos lados (tenga en cuenta las diferencias de TTL). También se puede usar la herramienta riot compare.","danger":"safe","readonly":true},"elasticsearch":{"command":"# Compare document counts (on source and target clusters)\nGET customer/_count\n\n# Details: for health/segment/storage comparison\nGET _cat/indices/customer?v&h=index,docs.count,store.size\n# Content by sampling: compare GET customer/_doc/<id> for the same _ids","note":"En Elasticsearch, la verificación básica es comparar el número de documentos en ambos clústeres con GET <index>/_count (docs.count de _cat/indices también da un resumen rápido). Base la comparación en _count: docs.count de _cat/indices cuenta a nivel de Lucene (incluidos los subdocumentos nested; p. ej., un documento con dos elementos nested cuenta como 3), por lo que puede diferir de _count. Para verificar el contenido, compare tomando una muestra de documentos sobre el mismo conjunto de _id o mediante un campo hash.","danger":"safe","readonly":true,"priv":"read / monitor sobre el índice"}}}]},"rosetta/tuning.json":{"category":"Ajuste de rendimiento","categoryId":"tuning","order":16,"concepts":[{"id":"plan-baseline","title":"Fijar / forzar un plan de consulta (plan baseline / hint)","aliases":["plan baseline","force plan","pin plan","hint","plan stability","query store force"],"engines":{"oracle":{"command":"-- Load the good plan from the cursor cache as a baseline:\nDECLARE n NUMBER; BEGIN\n  n := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(sql_id => '&sql_id');\nEND;\n/","note":"SQL Plan Management fija el plan; es la protección más sólida frente a regresiones. También se pueden usar hints /*+ ... */.","danger":"caution","priv":"ADMINISTER SQL MANAGEMENT OBJECT"},"postgres":{"command":"-- There is no built-in plan pinning; the pg_hint_plan extension:\n/*+ IndexScan(customer customer_city_idx) */ SELECT ... ;","note":"Alternativa: orientar a nivel de sesión con parámetros como enable_seqscan; para una fijación persistente, use pg_hint_plan.","danger":"caution"},"mysql":{"command":"SELECT /*+ INDEX(customer customer_city_idx) */ * FROM customer WHERE city='Ankara';","note":"Los optimizer hints se incrustan en la consulta. Para una reescritura persistente, use el plugin query rewrite.","danger":"safe"},"mssql":{"command":"-- Force the good plan in Query Store:\nEXEC sp_query_store_force_plan @query_id = 42, @plan_id = 17;","note":"Query Store debe estar habilitado. Como alternativa, OPTION (USE PLAN N'...') o una plan guide.","danger":"caution","priv":"ALTER sobre la base de datos"},"mongodb":{"command":"// Force an index for the query:\ndb.customer.find({ city: \"Ankara\" }).hint({ city: 1 })\n// Persistent: db.customer.getPlanCache() / index filter","note":"hint fuerza un índice concreto; elimine un plan obsoleto con planCacheClear.","danger":"safe"},"redis":{"command":"# Not applicable: there is no query planner.","note":"El rendimiento depende de la estructura de datos y de la complejidad de los comandos.","danger":"safe"},"elasticsearch":{"command":"# There is no plan pinning; optimize the query/index and routing.\nGET <index>/_search { \"profile\": true, \"query\": {...} }","note":"Use profile para ver qué consulta es costosa; oriéntela con shards/routing.","danger":"safe"}}},{"id":"gather-stats-advanced","title":"Recopilar estadísticas con opciones avanzadas (histogramas, etc.)","aliases":["gather stats","histogram","statistics","analyze advanced","method_opt","estimate percent"],"engines":{"oracle":{"command":"EXEC DBMS_STATS.GATHER_TABLE_STATS(\n  ownname=>'HR', tabname=>'EMPLOYEE',\n  estimate_percent=>DBMS_STATS.AUTO_SAMPLE_SIZE,\n  method_opt=>'FOR ALL COLUMNS SIZE AUTO', degree=>4);","note":"method_opt controla los histogramas y degree el paralelismo. Use LOCK_TABLE_STATS para bloquear las estadísticas.","danger":"safe"},"postgres":{"command":"ALTER TABLE customer ALTER COLUMN city SET STATISTICS 500;\nANALYZE customer;","note":"Objetivo de muestreo por columna; default_statistics_target establece el valor global por defecto.","danger":"safe"},"mysql":{"command":"ANALYZE TABLE customer UPDATE HISTOGRAM ON city WITH 32 BUCKETS;","note":"Soporte de histogramas en MySQL 8.0; útil en columnas por las que se filtra pero que no son selectivas.","danger":"safe"},"mssql":{"command":"UPDATE STATISTICS dbo.customer WITH FULLSCAN;\n-- Filtered statistics: CREATE STATISTICS ... WHERE ...","note":"FULLSCAN es el más preciso; use SAMPLE en tablas grandes. AUTO_UPDATE_STATISTICS para actualizaciones automáticas.","danger":"safe"},"mongodb":{"command":"// There are no manual statistics; the planner measures at runtime.\ndb.customer.getPlanCache().clear()","note":"Aparte de limpiar una caché de planes obsoleta, no se necesitan estadísticas manuales.","danger":"safe"},"redis":{"command":"# Not applicable.","note":"No hay estadísticas/optimizador.","danger":"safe"},"elasticsearch":{"command":"# There is no statistics gathering; refresh searchability:\nPOST <index>/_refresh","note":"Field data y doc values se usan en tiempo de consulta; force merge reduce los segmentos.","danger":"safe"}}},{"id":"perf-report","title":"Generar un informe de rendimiento (AWR / ASH y equivalentes)","aliases":["awr","ash","performance report","performance report","profile","workload report"],"engines":{"oracle":{"command":"-- AWR report (SQL*Plus):\n@?/rdbms/admin/awrrpt.sql\n-- ASH report: @?/rdbms/admin/ashrpt.sql\n-- Live: SELECT * FROM v$active_session_history WHERE sample_time > SYSDATE-1/24;","note":"AWR/ASH requieren la licencia Diagnostics Pack. Statspack es el equivalente sin licencia.","danger":"safe","priv":"SELECT_CATALOG_ROLE"},"postgres":{"command":"-- pg_stat_statements summary + pg_profile/pgBadger:\nSELECT queryid, calls, total_exec_time, mean_exec_time, query\nFROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 20;","note":"Como equivalente de AWR, use la extensión pg_profile (snapshots periódicos) o el análisis de logs con pgBadger.","danger":"safe"},"mysql":{"command":"-- sys schema summaries:\nSELECT * FROM sys.statement_analysis LIMIT 20;\nSELECT * FROM sys.io_global_by_file_by_bytes LIMIT 10;","note":"El esquema sys convierte performance_schema en informes legibles; Enterprise Monitor es más completo.","danger":"safe"},"mssql":{"command":"-- Query Store reports (SSMS) or:\nSELECT TOP 20 * FROM sys.query_store_runtime_stats ORDER BY avg_duration DESC;","note":"Query Store conserva planes/rendimiento a lo largo del tiempo; sp_BlitzFirst (comunidad) ofrece un diagnóstico rápido.","danger":"safe"},"mongodb":{"command":"# Live from the shell: mongostat --rowcount 10 ; mongotop\n// or Atlas Performance Advisor / profiler:\ndb.system.profile.find().sort({millis:-1}).limit(20)","note":"mongostat/mongotop muestran la carga instantánea; el profiler registra las operaciones lentas.","danger":"safe"},"redis":{"command":"INFO stats\nredis-cli --latency-history\nSLOWLOG GET 20","note":"--latency y --latency-history dan el perfil de latencia; INFO da todos los contadores.","danger":"safe"},"elasticsearch":{"command":"GET _nodes/stats\nGET _cat/thread_pool?v&h=node_name,name,active,queue,rejected\nGET _nodes/hot_threads","note":"hot_threads muestra los hilos más costosos; _nodes/stats da métricas globales. Monitoring (Kibana) es histórico.","danger":"safe"}}},{"id":"optimizer-params","title":"Ver/cambiar la configuración del optimizador / paralelismo","aliases":["optimizer","parallelism","parallel","maxdop","optimizer_switch","cost","planner settings"],"engines":{"oracle":{"command":"SHOW PARAMETER optimizer;\n-- Session: ALTER SESSION SET optimizer_mode = FIRST_ROWS_10;\n-- Parallelism: ALTER SESSION FORCE PARALLEL QUERY PARALLEL 4;","note":"Parámetros como optimizer_index_cost_adj y parallel_degree_policy afectan a la elección del plan; cámbielos con cuidado.","danger":"caution"},"postgres":{"command":"SHOW random_page_cost;\nSHOW max_parallel_workers_per_gather;\n-- Session: SET enable_seqscan = off;","note":"En SSD se recomienda random_page_cost ~1.1. Los flags enable_* son para diagnóstico, no una solución permanente.","danger":"caution"},"mysql":{"command":"SELECT @@optimizer_switch;\nSET SESSION optimizer_switch='index_merge=off';","note":"optimizer_switch activa/desactiva optimizaciones individuales; pruebe a nivel de sesión.","danger":"caution"},"mssql":{"command":"-- Instance-level MAXDOP and cost threshold:\nEXEC sp_configure 'max degree of parallelism', 8; RECONFIGURE;\n-- Database level: ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP = 8;","note":"MAXDOP=0 es ilimitado; normalmente 4-8 para OLTP. El valor por defecto de 5 de 'cost threshold for parallelism' es demasiado bajo en la mayoría de los casos.","danger":"caution"},"mongodb":{"command":"# There are no optimizer parameters; indexes and query shape are decisive.\ndb.customer.find({...}).explain('allPlansExecution')","note":"allPlansExecution muestra todos los planes evaluados por el planificador.","danger":"safe"},"redis":{"command":"# Not applicable.","note":"No hay planificador; io-threads y la elección de la estructura de datos determinan el rendimiento.","danger":"safe"},"elasticsearch":{"command":"GET <index>/_settings?include_defaults=true\n# search limits such as search.max_buckets, index.max_result_window","note":"No hay optimizador; el número de shards, el routing y la estructura de la consulta determinan el rendimiento.","danger":"safe"}}},{"id":"sql-monitor","title":"Monitorear en tiempo real el SQL en ejecución/reciente","aliases":["sql monitor","live monitoring","real time sql","live query","current sql","monitor active queries"],"engines":{"oracle":{"command":"SET LONG 1000000 LONGCHUNKSIZE 1000000 LINESIZE 250 PAGESIZE 0\nSELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR(sql_id=>'&sql_id', type=>'TEXT') AS report FROM dual;\n-- or: SELECT sql_id, status, elapsed_time FROM v$sql_monitor WHERE status='EXECUTING';","note":"Real-Time SQL Monitoring muestra en vivo las consultas largas/paralelas (Tuning Pack).","danger":"safe"},"postgres":{"command":"SELECT pid, now()-query_start AS dur, state, wait_event, query\nFROM pg_stat_activity WHERE state='active' ORDER BY query_start;","note":"Con auto_explain, los planes de las consultas lentas se pueden registrar automáticamente.","danger":"safe"},"mysql":{"command":"SELECT thread_id, event_name, timer_wait/1e9 AS ms, sql_text\nFROM performance_schema.events_statements_current;","note":"events_statements_current muestra las sentencias en ejecución en este momento.","danger":"safe"},"mssql":{"command":"SELECT r.session_id, r.status, r.wait_type, r.percent_complete, t.text\nFROM sys.dm_exec_requests r JOIN sys.dm_exec_sessions s ON s.session_id = r.session_id CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t\nWHERE s.is_user_process = 1;","note":"'Live Query Statistics' en SSMS muestra en vivo el progreso del plan.","danger":"safe"},"mongodb":{"command":"db.currentOp({ active: true, secs_running: { $gt: 1 } })","note":"Muestra en vivo las operaciones de larga duración; se pueden terminar con killOp.","danger":"safe"},"redis":{"command":"MONITOR","note":"Transmite en vivo TODOS los comandos; afecta al rendimiento en producción, úselo solo brevemente. Es preferible SLOWLOG.","danger":"caution"},"elasticsearch":{"command":"GET _tasks?detailed=true&group_by=parents","note":"Muestra en vivo las tareas en ejecución; una tarea larga se cancela con POST _tasks/<id>/_cancel.","danger":"safe"}}}]},"rosetta/tuning-adv.json":{"category":"Ajuste avanzado","categoryId":"tuning-adv","order":17,"concepts":[{"id":"parallel-query","title":"Ejecutar una consulta en paralelo / establecer el grado de paralelismo","aliases":["parallel","parallelism","dop","degree of parallelism","parallel query"],"engines":{"oracle":{"command":"-- At query level with a hint:\nSELECT /*+ PARALLEL(t, 4) */ * FROM big_table t;\n-- Force at session level:\nALTER SESSION FORCE PARALLEL QUERY PARALLEL 4;","note":"El hint PARALLEL aplica un recorrido paralelo a la tabla; degree=DOP. Un DOP excesivo agota los recursos del servidor; elíjalo con cuidado. parallel_degree_policy=AUTO lo gestiona automáticamente.","danger":"caution"},"postgres":{"command":"-- Set the number of parallel workers for the session:\nSET max_parallel_workers_per_gather = 4;\n-- The planner adds a Gather/Gather Merge node:\nEXPLAIN (ANALYZE) SELECT count(*) FROM big_table;","note":"Si aparece Gather/Parallel Seq Scan en el plan, el paralelismo está activo. max_parallel_workers y min_parallel_table_scan_size también influyen.","danger":"safe"},"mysql":{"command":"-- Limited InnoDB parallelism (certain operations only):\nSET GLOBAL innodb_parallel_read_threads = 4;\n-- Effective for COUNT(*) and primary key scans.","note":"MySQL no ofrece ejecución de consultas en paralelo de forma general; innodb_parallel_read_threads solo paraleliza los recorridos del índice clustered (p. ej., COUNT(*)). Los JOIN complejos son de un solo hilo.","danger":"safe"},"mssql":{"command":"-- MAXDOP hint at query level:\nSELECT * FROM dbo.big_table OPTION (MAXDOP 4);\n-- Instance level: sp_configure 'max degree of parallelism', 4;","note":"MAXDOP limita el número de hilos paralelos; MAXDOP 1 desactiva el paralelismo. Una vez superado el 'cost threshold for parallelism', el plan pasa a ser paralelo.","danger":"safe"},"mongodb":{"command":"// Allow disk use for a large aggregation:\ndb.sale.aggregate([ { $group: { _id: \"$city\", t: { $sum: \"$amount\" } } } ], { allowDiskUse: true })\n// A sharded cluster processes each shard in parallel.","note":"En un clúster con sharding la consulta se ejecuta en paralelo en cada shard; en un solo nodo las etapas de agregación son mayoritariamente secuenciales. allowDiskUse permite operaciones que superan el límite de memoria.","danger":"safe"},"redis":{"command":"# Not applicable: there is no concept of query parallelism.","note":"La ejecución de comandos es de un solo hilo; escale con Cluster (sharding) o use io-threads para el paralelismo de E/S de red.","danger":"safe"},"elasticsearch":{"command":"# Shard count = unit of parallelism; a query runs in parallel across shards.\n# Parallelism by slicing within a single shard (with scroll/PIT):\nGET <index>/_search\n{ \"slice\": { \"id\": 0, \"max\": 4 }, \"query\": { \"match_all\": {} } }","note":"Una búsqueda se ejecuta de forma concurrente en todos los shards relevantes; el número de primary shards fija el nivel básico de paralelismo. slices dividen las operaciones grandes de scroll/reindex.","danger":"safe"}}},{"id":"bind-variables","title":"Gestionar variables bind / parameter sniffing","aliases":["bind","bind variable","parameter","parameter sniffing","cursor sharing","prepared statement"],"engines":{"oracle":{"command":"-- Use binds instead of literals (for plan sharing):\nSELECT * FROM customer WHERE city = :city;\n-- Share legacy code that uses literals:\nALTER SESSION SET cursor_sharing = FORCE;","note":"Las variables bind reducen los hard parses. Adaptive Cursor Sharing mitiga los problemas de sniffing manteniendo varios planes para valores bind con distribuciones sesgadas. Use cursor_sharing=FORCE con cuidado.","danger":"caution"},"postgres":{"command":"-- Parameterize with a prepared statement:\nPREPARE q (text) AS SELECT * FROM customer WHERE city = $1;\nEXECUTE q('Ankara');\n-- Set plan cache behavior:\nSET plan_cache_mode = force_custom_plan;","note":"plan_cache_mode=auto elige entre planes genéricos y personalizados; force_custom_plan genera un plan específico para los parámetros en cada ejecución (una solución para problemas similares al sniffing).","danger":"safe"},"mysql":{"command":"-- Server-side prepared statement:\nPREPARE stmt FROM 'SELECT * FROM customer WHERE city = ?';\nSET @city = 'Ankara';\nEXECUTE stmt USING @city;\nDEALLOCATE PREPARE stmt;","note":"Los prepared statements evitan la inyección SQL y reducen el costo de parsing. MySQL también admite la parametrización a nivel de driver.","danger":"safe"},"mssql":{"command":"-- Parameterized query (sp_executesql):\nEXEC sp_executesql N'SELECT * FROM dbo.customer WHERE city = @city', N'@city nvarchar(50)', @city = N'Ankara';\n-- Handle sniffing with a hint:\nSELECT ... OPTION (RECOMPILE);\n-- or OPTION (OPTIMIZE FOR (@city = 'Ankara'));","note":"OPTION(RECOMPILE) genera un plan específico para los parámetros en cada ejecución (costoso en CPU). OPTIMIZE FOR fija el plan a un valor concreto/típico; OPTIMIZE FOR UNKNOWN usa la selectividad media.","danger":"caution"},"mongodb":{"command":"// The query shape is the key of the plan cache:\ndb.customer.find({ city: \"Ankara\" }).explain(\"queryPlanner\")\n// Clear a stale plan:\ndb.customer.getPlanCache().clear()","note":"Las consultas con la misma forma (mismos campos/operadores) comparten el plan cacheado; valores literales distintos no generan un plan nuevo. Si la distribución de valores está muy sesgada, restablézcalo con planCacheClear.","danger":"safe"},"redis":{"command":"# Not applicable: there is no query planner or parameter sniffing.","note":"Los comandos se ejecutan directamente; el diseño de claves, no la parametrización, determina el rendimiento.","danger":"safe"},"elasticsearch":{"command":"# Reuse the query with a parameterized search template:\nPOST _scripts/customer_city\n{ \"script\": { \"lang\": \"mustache\", \"source\": { \"query\": { \"term\": { \"city\": \"{{city}}\" } } } } }\nGET <index>/_search/template\n{ \"id\": \"customer_city\", \"params\": { \"city\": \"Ankara\" } }","note":"Un search template (Mustache) parametriza el cuerpo de la consulta; la aplicación llama a una plantilla fija con valores distintos, lo que mejora la consistencia de las consultas y el uso de la caché.","danger":"safe"}}},{"id":"partition-pruning","title":"Verificar el partition pruning","aliases":["partition pruning","partition elimination","partition","pruning","pstart pstop"],"engines":{"oracle":{"command":"EXPLAIN PLAN FOR\nSELECT * FROM sale WHERE sale_date = DATE '2026-01-15';\nSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);\n-- Look for 'PARTITION RANGE ITERATOR/SINGLE' and the Pstart/Pstop columns in the plan.","note":"Pstart/Pstop muestran que solo se leen las particiones relevantes (lo ideal es un número en lugar de KEY). Aplicar una función/conversión implícita a la clave de partición impide el pruning.","danger":"safe"},"postgres":{"command":"SET enable_partition_pruning = on;\nEXPLAIN (ANALYZE)\nSELECT * FROM sale WHERE sale_date = DATE '2026-01-15';\n-- Verify in the plan that only the relevant partition (child table) is scanned.","note":"Con enable_partition_pruning activado, solo aparecen en el plan las particiones que coinciden; las demás se muestran como 'never executed'. Los valores constantes se podan en tiempo de planificación, los parámetros en tiempo de ejecución.","danger":"safe"},"mysql":{"command":"-- Show which partitions are scanned:\nEXPLAIN SELECT * FROM sale WHERE sale_date = '2026-01-15';\n-- 5.7 and earlier: EXPLAIN PARTITIONS SELECT ... (removed in 8.0)","note":"En MySQL 5.7+, la columna 'partitions' de la salida de EXPLAIN simple lista las particiones que se recorrerán (la sintaxis EXPLAIN PARTITIONS se eliminó en 8.0); solo deben aparecer las particiones relevantes. Si se listan todas, no se está produciendo pruning.","danger":"safe"},"mssql":{"command":"-- Find which partition a value falls into:\nSELECT $PARTITION.pf_sale_date('2026-01-15') AS partition_no;\n-- Examine 'Actual Partition Count' and 'Actual Partitions Accessed' in the actual plan.","note":"La función $PARTITION devuelve el número de partición de un valor. Si el número de particiones accedidas en el plan de ejecución real es cercano a 1, el pruning está activo; si se recorren todas las particiones, el predicado no coincide.","danger":"safe"},"mongodb":{"command":"// Targeted query using the shard key in a sharded cluster:\ndb.sale.find({ shop_id: 42 }).explain(\"executionStats\")\n// In explain, only the relevant shard should appear under 'shards'.","note":"Si la consulta contiene la shard key, mongos la dirige solo al shard correspondiente (targeted); de lo contrario, se distribuye a todos los shards (scatter-gather). Verifique los shards accedidos en la salida de explain.","danger":"safe"},"redis":{"command":"# Not applicable: there is no table/partition concept.","note":"Los datos se distribuyen mediante los hash slots de Cluster; en lugar de query pruning, el acceso por clave es directo.","danger":"safe"},"elasticsearch":{"command":"# Route to the relevant shard with routing:\nGET <index>/_search?routing=customer42\n{ \"query\": { \"term\": { \"customer_id\": 42 } } }\n# Query only the relevant indices with a time-based index pattern:\nGET sale-2026-01/_search","note":"El custom routing limita la consulta a un solo shard (similar al pruning). Para datos de series temporales, un nombre de índice con patrón de fecha (sale-2026-01) garantiza que solo se busque en los índices relevantes; use un patrón estrecho en lugar de un comodín.","danger":"safe"}}},{"id":"result-cache","title":"Usar una caché de resultados / consultas","aliases":["result cache","result caching","query cache","query result cache","request cache"],"engines":{"oracle":{"command":"-- Result cache at query level:\nSELECT /*+ RESULT_CACHE */ city, COUNT(*) FROM customer GROUP BY city;\n-- Global mode: ALTER SYSTEM SET result_cache_mode = FORCE;","note":"Server Result Cache mantiene el resultado en la SGA; se invalida automáticamente cuando cambian las tablas subyacentes. RESULT_CACHE_MODE=FORCE lo aplica a todas las consultas elegibles, MANUAL solo mediante el hint. Poco beneficio en tablas que cambian con frecuencia.","danger":"caution"},"postgres":{"command":"-- There is no built-in result cache.\n-- Precompute with a materialized view:\nCREATE MATERIALIZED VIEW mv_city_summary AS\n  SELECT city, COUNT(*) FROM customer GROUP BY city;\nREFRESH MATERIALIZED VIEW mv_city_summary;","note":"PostgreSQL no ofrece una caché de resultados de consultas; use una vista materializada (REFRESH manual/programado) o una caché a nivel de aplicación (Redis). Nota: el nodo Memoize proporciona caché a nivel de fila dentro de un nested loop, no caché de resultados de consultas.","danger":"safe"},"mysql":{"command":"-- Query Cache was REMOVED in MySQL 8.0.\n-- Alternative: a materialized summary table or an application cache:\nCREATE TABLE city_summary AS SELECT city, COUNT(*) c FROM customer GROUP BY city;","note":"El antiguo query_cache_type se eliminó por completo en MySQL 8.0 (por problemas de escalabilidad). Use en su lugar el buffer pool de InnoDB, tablas resumen o una caché externa (Redis/Memcached).","danger":"safe"},"mssql":{"command":"-- There is no built-in query result cache.\n-- Precompute with an indexed (materialized) view:\nCREATE VIEW dbo.vw_city_summary WITH SCHEMABINDING AS\n  SELECT city, COUNT_BIG(*) c FROM dbo.customer GROUP BY city;\nCREATE UNIQUE CLUSTERED INDEX ix ON dbo.vw_city_summary(city);","note":"SQL Server no mantiene una caché de resultados (mantiene una caché de planes). Para agregaciones repetidas, una vista indexada se mantiene automáticamente; la coincidencia automática en los planes solo existe en la edición Enterprise (y Developer), mientras que en Standard la vista debe consultarse con el hint WITH (NOEXPAND); además, el buffer pool mantiene en memoria las páginas leídas con frecuencia.","danger":"safe"},"mongodb":{"command":"// There is no built-in query result cache.\n// Pre-aggregate and write to a collection ($merge):\ndb.sale.aggregate([\n  { $group: { _id: \"$city\", t: { $sum: \"$amount\" } } },\n  { $merge: \"city_summary\" }\n])","note":"MongoDB no ofrece una caché de resultados; WiredTiger mantiene en memoria los datos de acceso frecuente. Para agregaciones costosas, use una colección resumen precalculada mediante $merge/$out o una caché de aplicación (Redis).","danger":"safe"},"redis":{"command":"# Redis is already a cache layer; cache the result manually:\nSET query:city_summary \"{...}\" EX 300\nGET query:city_summary\n# If already computed, return it with GET; otherwise compute it and write it with SET.","note":"El patrón cache-aside habitual: si la clave existe, devuélvala rápidamente con GET; de lo contrario, calcule desde el origen y haga SET con EX (TTL). Redis suele servir como caché de resultados para otras bases de datos.","danger":"safe"},"elasticsearch":{"command":"# Shard request cache (enabled by default for size:0 aggregations):\nGET <index>/_search?request_cache=true\n{ \"size\": 0, \"aggs\": { \"city\": { \"terms\": { \"field\": \"city\" } } } }\n# Setting: PUT <index>/_settings { \"index.requests.cache.enable\": true }","note":"La shard request cache almacena los resultados por shard de las solicitudes size:0 (solo agregación); se invalida cuando el índice hace refresh. Las consultas con size>0 (que devuelven hits) no se cachean por defecto.","danger":"safe"}}}]},"rosetta/advanced-features.json":{"category":"Funciones avanzadas","categoryId":"advanced-features","order":18,"concepts":[{"id":"full-text-search","title":"Búsqueda de texto completo","aliases":["full text","full text","fts","search","text search"],"engines":{"oracle":{"command":"-- Oracle Text: create a CONTEXT index\nCREATE INDEX article_idx ON article(content) INDEXTYPE IS CTXSYS.CONTEXT;\n-- Search with CONTAINS (label 1 ties CONTAINS to SCORE):\nSELECT id, SCORE(1) FROM article\nWHERE CONTAINS(content, 'database AND performance', 1) > 0\nORDER BY SCORE(1) DESC;","note":"Oracle Text (antes interMedia) proporciona búsqueda de texto completo sensible al idioma mediante un índice CONTEXT. El operador CONTAINS admite operadores de búsqueda como AND/OR/NEAR. El índice se mantiene con CTX_DDL.SYNC_INDEX o con SYNC automático.","danger":"safe"},"postgres":{"command":"-- Search with tsvector/tsquery, accelerated by a GIN index:\nCREATE INDEX article_fts_idx ON article\n  USING GIN (to_tsvector('english', content));\nSELECT id, ts_rank(to_tsvector('english', content), q) AS score\nFROM article, to_tsquery('english', 'database & performance') q\nWHERE to_tsvector('english', content) @@ q\nORDER BY score DESC;","note":"La búsqueda de texto completo integrada de PostgreSQL compara tsvector (documento) con tsquery (consulta) mediante el operador @@. Un índice GIN acelera las búsquedas sobre textos grandes. La configuración de idioma ('english') realiza el stemming y el tratamiento de stop words.","danger":"safe"},"mysql":{"command":"-- Create a FULLTEXT index (InnoDB/MyISAM):\nALTER TABLE article ADD FULLTEXT INDEX ft_content (content);\n-- Search in natural language / boolean mode:\nSELECT id, MATCH(content) AGAINST('database performance') AS score\nFROM article\nWHERE MATCH(content) AGAINST('+database +performance' IN BOOLEAN MODE);","note":"Un índice FULLTEXT de MySQL se consulta con la sintaxis MATCH ... AGAINST. IN NATURAL LANGUAGE MODE devuelve una puntuación de relevancia; IN BOOLEAN MODE da un control detallado con los operadores +/-/*. InnoDB admite la búsqueda de texto completo a partir de 5.6.","since":"MySQL 5.6","xnote":"La búsqueda de texto completo en InnoDB llegó con MySQL 5.6; antes solo existía en MyISAM.","danger":"safe"},"mssql":{"command":"-- Create a Full-Text Catalog and index:\nCREATE FULLTEXT CATALOG ft_catalog;\nCREATE FULLTEXT INDEX ON dbo.article(content)\n  KEY INDEX PK_article ON ft_catalog;\n-- Search with CONTAINS / FREETEXT:\nSELECT id FROM dbo.article\nWHERE CONTAINS(content, '\"database\" AND \"performance\"');","note":"Full-Text Search de SQL Server necesita primero un Full-Text Catalog y luego un Full-Text Index en la tabla (es obligatorio un KEY INDEX único). CONTAINS admite búsqueda exacta/por proximidad y operadores lógicos; FREETEXT admite búsqueda por significado (lingüística). El componente Full-Text Search debe estar instalado.","danger":"safe"},"mongodb":{"command":"// Create a text index:\ndb.article.createIndex({ content: \"text\" })\n// Search with $text and sort by relevance score:\ndb.article.find(\n  { $text: { $search: \"database performance\" } },\n  { score: { $meta: \"textScore\" } }\n).sort({ score: { $meta: \"textScore\" } })","note":"Un índice de texto de MongoDB proporciona búsqueda de texto completo mediante el operador $text; una colección solo puede tener un índice de texto. El metacampo textScore da la relevancia. Para búsquedas más avanzadas se usa Atlas Search ($search, basado en Lucene).","danger":"safe"},"redis":{"command":"# Requires the RediSearch module (Redis Stack).\n# Define the index:\nFT.CREATE article_idx ON HASH PREFIX 1 article: SCHEMA content TEXT\n# Full-text search:\nFT.SEARCH article_idx \"database performance\"","note":"Redis básico no ofrece búsqueda de texto completo; el módulo RediSearch (incluido en Redis Stack) proporciona la indexación con FT.CREATE y la búsqueda con FT.SEARCH. Se admiten stemming, búsqueda difusa, filtrado y ordenación. Sin el módulo instalado, la función no está disponible.","danger":"safe"},"elasticsearch":{"command":"# Elasticsearch is natively a full-text search engine.\n# match query (analyzed text search):\nGET article/_search\n{\n  \"query\": {\n    \"match\": { \"content\": \"database performance\" }\n  }\n}","note":"Elasticsearch es fundamentalmente un motor de búsqueda de texto completo construido sobre Lucene; los campos text se analizan en tiempo de indexación (tokenización, stemming). La consulta match devuelve resultados ordenados por puntuación de relevancia (_score). Se pueden asignar analizadores específicos por idioma (p. ej., 'english').","danger":"safe"}}},{"id":"json-query","title":"Consultar datos JSON","aliases":["json","json query","json query","jsonb","document query"],"engines":{"oracle":{"command":"-- Extract a scalar value with JSON_VALUE:\nSELECT JSON_VALUE(data, '$.customer.city') AS city FROM orders;\n-- Expand into rows with JSON_TABLE:\nSELECT t.product, t.quantity\nFROM orders s,\n     JSON_TABLE(s.data, '$.items[*]'\n       COLUMNS (product VARCHAR2(50) PATH '$.product',\n                quantity NUMBER PATH '$.quantity')) t;","note":"Oracle (12c+) consulta datos JSON con expresiones de ruta SQL/JSON. JSON_VALUE devuelve un único valor escalar, JSON_QUERY un fragmento de objeto/array y JSON_TABLE convierte JSON en una estructura relacional de filas y columnas. 21c+ admite un tipo de datos JSON nativo.","since":"Oracle 12c","xnote":"Las consultas SQL/JSON llegaron en Oracle 12c y el tipo de datos JSON nativo en 21c.","danger":"safe"},"postgres":{"command":"-- jsonb operators:\nSELECT data->'customer'->>'city' AS city FROM orders;\n-- Containment (@>) and a GIN index:\nCREATE INDEX ix ON orders USING GIN (data);\nSELECT * FROM orders\nWHERE data @> '{\"status\": \"approved\"}';","note":"El tipo jsonb de PostgreSQL ofrece operadores muy completos: -> (objeto/elemento json), ->> (valor de texto), #> (ruta), @> (contención), ? (existe la clave). Un índice GIN acelera las consultas con @> y ?. También son posibles las consultas de ruta SQL/JSON con jsonb_path_query.","since":"PostgreSQL 9.4","xnote":"El tipo json llegó en PostgreSQL 9.2 y el tipo jsonb en 9.4.","danger":"safe"},"mysql":{"command":"-- JSON_EXTRACT or shorthand operators:\nSELECT JSON_EXTRACT(data, '$.customer.city') AS city FROM orders;\n-- ->> returns unquoted text:\nSELECT data->>'$.customer.city' AS city FROM orders\nWHERE data->>'$.status' = 'approved';","note":"MySQL (5.7+) consulta el tipo JSON nativo con JSON_EXTRACT(); el operador -> es la abreviatura de JSON_EXTRACT y ->> de JSON_UNQUOTE(JSON_EXTRACT()). Las búsquedas en campos JSON se pueden acelerar con una generated column + índice.","since":"MySQL 5.7","xnote":"El tipo de datos JSON nativo llegó en MySQL 5.7.","danger":"safe"},"mssql":{"command":"-- Extract a scalar value with JSON_VALUE:\nSELECT JSON_VALUE(data, '$.customer.city') AS city FROM orders;\n-- Expand into rows with OPENJSON:\nSELECT k.product, k.quantity\nFROM orders s\nCROSS APPLY OPENJSON(s.data, '$.items')\n  WITH (product NVARCHAR(50) '$.product', quantity INT '$.quantity') k;","note":"SQL Server (2016+) almacena JSON en una columna NVARCHAR. JSON_VALUE devuelve un valor escalar, JSON_QUERY un objeto/array y OPENJSON convierte JSON a formato tabular. ISJSON() lo valida. Las búsquedas por rutas JSON se pueden acelerar con una computed column + índice.","since":"SQL Server 2016","xnote":"Las funciones JSON (JSON_VALUE/OPENJSON) llegaron en SQL Server 2016.","danger":"safe"},"mongodb":{"command":"// MongoDB is natively a document (BSON/JSON) database.\n// Access nested fields with dot notation:\ndb.orders.find({ \"customer.city\": \"Ankara\" })\n// Array element and projection:\ndb.orders.find(\n  { \"items.product\": \"pen\" },\n  { \"items.$\": 1 }\n)","note":"MongoDB almacena todos los datos como documentos (BSON, similar a JSON); consultarlos es nativo. La notación de punto llega a los campos anidados y a los elementos de los arrays. Las transformaciones complejas se hacen con Aggregation Pipeline, y los índices permiten búsquedas rápidas en campos anidados.","danger":"safe"},"redis":{"command":"# Requires the RedisJSON module (Redis Stack).\n# Write a JSON document:\nJSON.SET order:1 $ '{\"customer\":{\"city\":\"Ankara\"},\"status\":\"approved\"}'\n# Read a value with JSONPath:\nJSON.GET order:1 $.customer.city","note":"Redis básico no tiene tipo JSON; el módulo RedisJSON (Redis Stack) proporciona lecturas y escrituras basadas en JSONPath con JSON.SET/JSON.GET. Junto con RediSearch, los documentos JSON se pueden indexar y consultar. Sin el módulo, JSON se almacena como cadena y se analiza en la aplicación.","danger":"safe"},"elasticsearch":{"command":"# Documents are already JSON; query fields directly:\nGET orders/_search\n{ \"query\": { \"term\": { \"customer.city\": \"Ankara\" } } }\n# For nested arrays, use the nested type and a nested query:\nGET orders/_search\n{ \"query\": { \"nested\": { \"path\": \"items\",\n  \"query\": { \"match\": { \"items.product\": \"pen\" } } } } }","note":"Elasticsearch almacena e indexa los documentos de forma nativa como JSON. Para conservar los límites de los elementos en arrays de objetos anidados, use el tipo de campo 'nested' y una consulta nested; de lo contrario, los arrays de tipo object se aplanan (riesgo de coincidencias cruzadas). La distinción object vs. nested es crítica para la exactitud.","danger":"safe"}}},{"id":"spatial-geo","title":"Consulta geográfica / espacial","aliases":["spatial","geographic","geo","gis","location","map"],"engines":{"oracle":{"command":"-- Oracle Spatial: SDO_GEOMETRY column and spatial index\nCREATE INDEX location_sidx ON shop(location)\n  INDEXTYPE IS MDSYS.SPATIAL_INDEX;\n-- Shops within 5 km:\nSELECT name FROM shop\nWHERE SDO_WITHIN_DISTANCE(location,\n  SDO_GEOMETRY(2001, 4326,\n    SDO_POINT_TYPE(32.85, 39.92, NULL), NULL, NULL),\n  'distance=5 unit=KM') = 'TRUE';","note":"Oracle Spatial almacena los datos geográficos en el tipo SDO_GEOMETRY; se requieren una entrada en USER_SDO_GEOM_METADATA y un índice espacial. Operadores como SDO_WITHIN_DISTANCE, SDO_RELATE y SDO_NN resuelven consultas de distancia/intersección/vecino más cercano. SRID 4326 es el sistema WGS84 (latitud/longitud).","danger":"safe"},"postgres":{"command":"-- PostGIS extension:\nCREATE EXTENSION IF NOT EXISTS postgis;\nCREATE INDEX location_gix ON shop USING GIST (location);\n-- Shops within 5 km (geography, meters):\nSELECT name FROM shop\nWHERE ST_DWithin(location,\n  ST_SetSRID(ST_MakePoint(32.85, 39.92), 4326)::geography,\n  5000);","note":"PostGIS convierte PostgreSQL en una base de datos espacial completa. Proporciona los tipos geometry (plano) y geography (esférico) y cientos de funciones ST_ como ST_Distance/ST_DWithin/ST_Intersects/ST_Contains. Un índice GiST/SP-GiST acelera las consultas espaciales.","danger":"safe"},"mysql":{"command":"-- Spatial column (with SRID) and SPATIAL index:\nALTER TABLE shop MODIFY location POINT NOT NULL SRID 4326;\nALTER TABLE shop ADD SPATIAL INDEX (location);\n-- Compute distance (meters, geography):\nSELECT name,\n  ST_Distance(location,\n    ST_SRID(POINT(32.85, 39.92), 4326)) AS distance\nFROM shop\nORDER BY distance LIMIT 10;","note":"MySQL (5.7+) admite tipos de datos espaciales conformes con OGC (POINT, POLYGON, etc.) y funciones ST_. Un índice SPATIAL (R-tree) solo se puede crear en InnoDB/MyISAM y sobre columnas NOT NULL; para que el optimizador lo use, la columna debe definirse con un atributo SRID (SRID 4326). Con SRID 4326, ST_Distance devuelve la distancia geodésica en metros. Con POINT(x, y) + ST_SRID, x es la longitud e y la latitud; para entrada WKT (ST_GeomFromText) con SRID 4326 el orden es latitud-longitud.","danger":"safe"},"mssql":{"command":"-- geography type and spatial index:\nCREATE SPATIAL INDEX location_sidx ON dbo.shop(location);\n-- Shops within 5 km (STDistance returns meters):\nSELECT name FROM dbo.shop\nWHERE location.STDistance(\n  geography::Point(39.92, 32.85, 4326)) <= 5000;","note":"SQL Server ofrece dos tipos espaciales: geography (esférico, latitud/longitud) y geometry (plano). Métodos como STDistance, STIntersects y STContains resuelven las consultas. Un índice espacial (basado en cuadrícula) mejora el rendimiento. Tenga en cuenta el orden de los argumentos de geography::Point(latitude, longitude, SRID).","danger":"safe"},"mongodb":{"command":"// 2dsphere index (for GeoJSON):\ndb.shop.createIndex({ location: \"2dsphere\" })\n// Shops nearest to a given point ($near):\ndb.shop.find({\n  location: {\n    $near: {\n      $geometry: { type: \"Point\", coordinates: [32.85, 39.92] },\n      $maxDistance: 5000\n    }\n  }\n})","note":"MongoDB almacena los datos geográficos en formato GeoJSON y ejecuta consultas esféricas con un índice 2dsphere. Se admiten los operadores $near (ordenado por distancia), $geoWithin (dentro de un área) y $geoIntersects. Las coordenadas van en el orden [longitud, latitud]; $maxDistance está en metros.","danger":"safe"},"redis":{"command":"# Built-in geospatial commands (built on sorted sets):\nGEOADD shops 32.85 39.92 \"kizilay\"\nGEOADD shops 32.86 39.93 \"cankaya\"\n# Search within 5 km of a point:\nGEOSEARCH shops FROMLONLAT 32.85 39.92\n  BYRADIUS 5 km ASC","note":"Redis almacena ubicaciones geográficas en un sorted set con GEOADD (longitude latitude member). GEOSEARCH (Redis 6.2+, que sustituye al antiguo GEORADIUS) devuelve los miembros dentro de un radio o rectángulo, ordenados por distancia. GEODIST da la distancia entre dos puntos. Es una función básica; no se necesita ningún módulo.","danger":"safe"},"elasticsearch":{"command":"# Map the field as geo_point:\nPUT shop\n{ \"mappings\": { \"properties\": { \"location\": { \"type\": \"geo_point\" } } } }\n# Query within 5 km:\nGET shop/_search\n{ \"query\": { \"geo_distance\": {\n  \"distance\": \"5km\",\n  \"location\": { \"lat\": 39.92, \"lon\": 32.85 } } } }","note":"Elasticsearch admite los tipos de campo geo_point (punto) y geo_shape (polígono/línea). El filtrado se hace con las consultas geo_distance (radio), geo_bounding_box (rectángulo) y geo_polygon; la ordenación con geo_distance y la agregación con geohash_grid/geotile_grid. Las coordenadas se indican como lat/lon o como un array [lon, lat].","danger":"safe"}}},{"id":"window-functions-adv","title":"Funciones de ventana (ROW_NUMBER/RANK/LAG)","aliases":["window function","window function","row_number","rank","lag","over","analytic"],"engines":{"oracle":{"command":"-- Rank by amount within each city and fetch the previous value:\nSELECT city, name, amount,\n  ROW_NUMBER() OVER (PARTITION BY city ORDER BY amount DESC) AS row_num,\n  RANK()       OVER (PARTITION BY city ORDER BY amount DESC) AS rnk,\n  LAG(amount)   OVER (PARTITION BY city ORDER BY created_at)      AS prev_amount\nFROM sale;","note":"Oracle fue pionero en las funciones analíticas (de ventana); ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD, SUM/AVG OVER, NTILE y FIRST_VALUE están totalmente soportadas. Dentro de OVER, PARTITION BY define los grupos, ORDER BY el orden y el marco (ROWS/RANGE BETWEEN) la ventana deslizante.","since":"Oracle 8i","danger":"safe"},"postgres":{"command":"-- Window functions with OVER():\nSELECT city, name, amount,\n  ROW_NUMBER() OVER (PARTITION BY city ORDER BY amount DESC) AS row_num,\n  RANK()       OVER (PARTITION BY city ORDER BY amount DESC) AS rnk,\n  LAG(amount)   OVER (PARTITION BY city ORDER BY created_at)      AS prev_amount\nFROM sale;","note":"PostgreSQL admite plenamente las funciones de ventana con la sintaxis SQL estándar. Están disponibles ROW_NUMBER/RANK/DENSE_RANK, LAG/LEAD, agregados con OVER, NTILE y FIRST_VALUE/LAST_VALUE. Las definiciones de ventana repetidas se pueden nombrar y compartir con la cláusula WINDOW.","since":"PostgreSQL 8.4","danger":"safe"},"mysql":{"command":"-- MySQL 8.0+ window functions:\nSELECT city, name, amount,\n  ROW_NUMBER() OVER (PARTITION BY city ORDER BY amount DESC) AS row_num,\n  RANK()       OVER (PARTITION BY city ORDER BY amount DESC) AS rnk,\n  LAG(amount)   OVER (PARTITION BY city ORDER BY created_at)      AS prev_amount\nFROM sale;","note":"MySQL añadió las funciones de ventana en la versión 8.0 (MariaDB 10.2+ también las admite). Están disponibles ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD, NTILE y agregados con OVER. Las versiones anteriores a 8.0 carecen de estas funciones; antes se emulaban con variables de usuario.","since":"MySQL 8.0","xnote":"Antes de MySQL 8.0 no había funciones de ventana; se emulaban con variables de usuario.","danger":"safe"},"mssql":{"command":"-- Window functions with OVER():\nSELECT city, name, amount,\n  ROW_NUMBER() OVER (PARTITION BY city ORDER BY amount DESC) AS row_num,\n  RANK()       OVER (PARTITION BY city ORDER BY amount DESC) AS rnk,\n  LAG(amount)   OVER (PARTITION BY city ORDER BY created_at)      AS prev_amount\nFROM dbo.sale;","note":"SQL Server admite funciones de ventana desde 2005 (ROW_NUMBER/RANK) y 2012 (LAG/LEAD, marcos, agregados con OVER). PARTITION BY, ORDER BY y el marco ROWS/RANGE están plenamente disponibles. OFFSET/FETCH o ROW_NUMBER son habituales para la paginación.","since":"SQL Server 2005","xnote":"Las funciones de ranking llegaron en 2005; LAG/LEAD y los marcos, en 2012.","danger":"safe"},"mongodb":{"command":"// Window calculations with $setWindowFields:\ndb.sale.aggregate([\n  { $setWindowFields: {\n      partitionBy: \"$city\",\n      sortBy: { amount: -1 },\n      output: {\n        row_num: { $documentNumber: {} },\n        rnk: { $rank: {} },\n        prev_amount: { $shift: { output: \"$amount\", by: -1 } }\n      }\n  } }\n])","note":"La etapa de agregación $setWindowFields, introducida en MongoDB 5.0, proporciona funciones de ventana: $documentNumber (ROW_NUMBER), $rank/$denseRank, $shift (LAG/LEAD), funciones de agregación y ventanas deslizantes (window: { documents/range }). partitionBy y sortBy corresponden a PARTITION BY/ORDER BY en SQL.","since":"MongoDB 5.0","xnote":"La etapa $setWindowFields llegó en MongoDB 5.0.","danger":"safe"},"redis":{"command":"# Not applicable: there are no SQL-like window functions.","note":"Redis es un almacén clave-valor; no tiene funciones analíticas de ventana. Para resultados ordenados se puede usar un Sorted Set (acceso tipo ranking con ZADD/ZRANGE); sin embargo, las operaciones de ventana relacionales como PARTITION BY/LAG se hacen en la capa de aplicación.","danger":"safe"},"elasticsearch":{"command":"# Not applicable: there are no SQL-like window functions.\n# Closest equivalent: bucket aggregations.\nGET sale/_search\n{ \"size\": 0, \"aggs\": { \"by_city\": {\n  \"terms\": { \"field\": \"city\" },\n  \"aggs\": { \"total\": { \"sum\": { \"field\": \"amount\" } } } } } }","note":"Elasticsearch no ofrece funciones de ventana clásicas (ROW_NUMBER/RANK/LAG). La agrupación y la ordenación se hacen con el framework de agregaciones: una terms aggregation se aproxima a PARTITION BY, top_hits a los registros principales y derivative/serial_diff (pipeline aggregations) a diferencias sucesivas tipo LAG.","danger":"safe"}}},{"id":"columnstore-analytics","title":"Almacenamiento columnar / analítica","aliases":["columnstore","columnar","column store","analytics","olap","columnar"],"engines":{"oracle":{"command":"-- Database In-Memory: keep the table in memory in columnar format\nALTER TABLE sale INMEMORY;\n-- With priority and compression:\nALTER TABLE sale INMEMORY PRIORITY HIGH\n  MEMCOMPRESS FOR QUERY HIGH;","note":"Oracle Database In-Memory (12.1.0.2+) acelera las consultas analíticas manteniendo las tablas en memoria en formato columnar (In-Memory Column Store, IM) y conservando el formato de filas en disco (formato dual). INMEMORY es una opción con licencia aparte. El parámetro inmemory_size dimensiona el área IM.","since":"Oracle 12.1.0.2","xnote":"El column store de Database In-Memory llegó en Oracle 12.1.0.2 (licencia aparte).","danger":"caution"},"postgres":{"command":"-- Core PostgreSQL is row-based; columnar storage needs an extension:\n-- Citus columnar access method:\nCREATE EXTENSION IF NOT EXISTS citus;\nCREATE TABLE sale_columnar (...) USING columnar;\n-- Alternatives: the Hydra/cstore_fdw extensions.","note":"PostgreSQL básico solo ofrece almacenamiento por filas (heap). Para almacenamiento columnar se usan el access method 'columnar' de Citus (USING columnar), Hydra o la antigua extensión cstore_fdw; proporcionan compresión y velocidad de recorrido para cargas de analítica/data warehouse.","danger":"safe"},"mysql":{"command":"-- Not applicable: core InnoDB does not offer columnar storage.\n-- For analytics: MySQL HeatWave (cloud, in-memory columnar)\n-- or integration with external ClickHouse/Apache Doris.","note":"MySQL estándar (InnoDB) es por filas y no tiene un columnstore integrado. Para acelerar la analítica se usa MySQL HeatWave (un acelerador columnar en memoria en Oracle Cloud); las soluciones on-premises suelen usar motores columnares externos como ClickHouse o Apache Doris.","danger":"safe"},"mssql":{"command":"-- Clustered columnstore index (for analytic tables):\nCREATE CLUSTERED COLUMNSTORE INDEX cci_sale ON dbo.sale;\n-- Nonclustered columnstore on a rowstore table (HTAP):\nCREATE NONCLUSTERED COLUMNSTORE INDEX ncci_sale\n  ON dbo.sale (city, product, amount, created_at);","note":"Los índices columnstore de SQL Server (nonclustered de solo lectura en 2012; clustered actualizables en 2014; nonclustered actualizables en 2016) almacenan los datos por columnas con alta compresión; con batch mode aceleran mucho las consultas analíticas con mucha agregación/recorrido. Clustered es adecuado para data warehouses y nonclustered para HTAP.","since":"SQL Server 2012","xnote":"El índice columnstore llegó en 2012 (solo lectura), el clustered columnstore actualizable en 2014 y el nonclustered columnstore actualizable en 2016.","danger":"safe"},"mongodb":{"command":"-- Not applicable: MongoDB is document (row-like) based.\n-- For analytics: dedicated Analytics Nodes in Atlas\n-- or export to a columnar format ($out)/an external engine.","note":"MongoDB almacena los documentos como BSON orientado a filas; no hay almacenamiento columnar (columnstore) integrado. Para cargas analíticas se usan Analytics Nodes dedicados en un replica set de Atlas, Atlas Data Federation o la exportación de datos a un sistema columnar (p. ej., Parquet).","danger":"safe"},"redis":{"command":"-- Not applicable: there is no concept of columnar storage.","note":"Redis es un almacén clave-valor en memoria; no existe la distinción entre almacenamiento por filas o columnas. Las agregaciones analíticas se hacen con estructuras especiales como Sorted Set, HyperLogLog (recuento aproximado) o RedisTimeSeries; no es adecuado para OLAP a gran escala.","danger":"safe"},"elasticsearch":{"command":"# doc_values: fields are stored column-wise on disk (enabled by default).\n# Required for sorting/aggregation; active on non-text fields.\nPUT sale\n{ \"mappings\": { \"properties\":\n  { \"amount\": { \"type\": \"double\", \"doc_values\": true } } } }","note":"Para ordenaciones y agregaciones, Elasticsearch mantiene los campos en una estructura columnar en disco llamada doc_values (habilitada por defecto en campos numeric/keyword/date). Esta disposición columnar acelera las consultas analíticas/de agregación; está deshabilitada por defecto en los campos text.","danger":"safe"}}},{"id":"in-memory","title":"Tablas en memoria / aceleración","aliases":["in memory","in-memory","memory table","acceleration","in-memory oltp"],"engines":{"oracle":{"command":"-- Keep the table in memory with Database In-Memory:\nALTER TABLE orders INMEMORY;\n-- Memory size (init parameter):\nALTER SYSTEM SET inmemory_size = 4G SCOPE=SPFILE;","note":"Oracle Database In-Memory acelera la analítica manteniendo en memoria, en formato columnar, las tablas consultadas con frecuencia (licencia aparte). Para OLTP, el Buffer Cache y el KEEP pool también mantienen en memoria los bloques de acceso frecuente. La solución en memoria conserva la durabilidad gracias al formato de filas en disco.","danger":"caution"},"postgres":{"command":"-- Fast table without durability (writes no WAL):\nCREATE UNLOGGED TABLE temp_calc (id int, data text);\n-- Session-private temporary table (tends to stay in memory):\nCREATE TEMP TABLE session_data (id int) ON COMMIT DELETE ROWS;","note":"PostgreSQL no ofrece un motor de tablas en memoria dedicado; una tabla UNLOGGED omite las escrituras en WAL para acelerar la escritura (su contenido se pierde tras un crash; no es durable). Las tablas TEMP duran lo que la sesión y se mantienen en memoria cuando es posible mediante temp_buffers. Los datos calientes se cachean en shared_buffers.","danger":"caution"},"mysql":{"command":"-- Table using the MEMORY (HEAP) storage engine:\nCREATE TABLE cache_table (\n  id INT PRIMARY KEY,\n  data VARCHAR(100)\n) ENGINE=MEMORY;","note":"El storage engine MEMORY de MySQL (antes HEAP) mantiene los datos de la tabla íntegramente en RAM; es muy rápido, pero los datos se pierden al reiniciar el servidor (la estructura se conserva). No admite BLOB/TEXT y usa un tamaño de fila fijo. Para datos calientes durables se prefiere el buffer pool de InnoDB.","danger":"caution"},"mssql":{"command":"-- In-Memory OLTP: memory-optimized table\nCREATE TABLE dbo.session_state (\n  id INT NOT NULL PRIMARY KEY NONCLUSTERED,\n  data NVARCHAR(100)\n) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA);","note":"In-Memory OLTP de SQL Server (Hekaton) proporciona OLTP de alta concurrencia con tablas optimizadas para memoria (MEMORY_OPTIMIZED=ON) y estructuras sin latches. DURABILITY=SCHEMA_AND_DATA mantiene los datos durables; SCHEMA_ONLY conserva solo el esquema. Requiere un filegroup optimizado para memoria.","since":"SQL Server 2014","xnote":"In-Memory OLTP (Hekaton) llegó en SQL Server 2014.","danger":"caution"},"mongodb":{"command":"-- Hot data is kept in the WiredTiger cache automatically.\n-- Set the cache size (default ~50% of RAM):\n// mongod --wiredTigerCacheSizeGB 8\n// or in the configuration: storage.wiredTiger.engineConfig.cacheSizeGB","note":"MongoDB no tiene un tipo de tabla en memoria aparte (el antiguo In-Memory Storage Engine era exclusivo de Enterprise y está obsoleto). El motor WiredTiger por defecto mejora la velocidad de lectura manteniendo en su caché de memoria los datos e índices de acceso frecuente; la caché de archivos del SO también contribuye.","danger":"safe"},"redis":{"command":"# Redis already runs entirely in memory; nothing extra is needed.\n# Persistence and memory policy settings:\nCONFIG SET maxmemory 4gb\nCONFIG SET maxmemory-policy allkeys-lru","note":"Redis mantiene todos los datos en RAM; por diseño, esta es la fuente de su velocidad. La persistencia la proporcionan RDB (snapshots) y/o AOF (log de comandos). El límite maxmemory y la política de eviction (LRU/LFU/random) determinan el comportamiento cuando la memoria está llena.","danger":"safe"},"elasticsearch":{"command":"-- Not applicable: there is no fully in-memory table mode.\n# Place hot data on fast nodes with a hot/cold architecture:\nPUT sale/_settings\n{ \"index.routing.allocation.require.data\": \"hot\" }","note":"Elasticsearch almacena los datos en disco (segmentos de Lucene); no ofrece un motor de tablas en memoria dedicado. Para la velocidad depende de la caché de archivos del SO (page cache), de doc_values y de una arquitectura hot-warm-cold. Disponer de RAM suficiente y de una configuración de heap adecuada es crítico para el rendimiento.","danger":"safe"}}},{"id":"time-series","title":"Datos de series temporales","aliases":["time series","time series","timeseries","iot","metrics","sensor"],"engines":{"oracle":{"command":"-- Range partitioning by date:\nCREATE TABLE measurements (\n  ts TIMESTAMP, sensor_id NUMBER, value NUMBER)\nPARTITION BY RANGE (ts)\nINTERVAL (NUMTODSINTERVAL(1,'DAY'))\n( PARTITION p0 VALUES LESS THAN (TIMESTAMP '2026-01-01 00:00:00') );","note":"En Oracle, los datos de series temporales suelen gestionarse con particionamiento RANGE (INTERVAL); las particiones nuevas se crean automáticamente y las antiguas se eliminan con facilidad (el partition pruning acelera las consultas). También se usan la compresión (ILM/ADO) y vistas materializadas para la agregación.","danger":"safe"},"postgres":{"command":"-- Hypertable with the TimescaleDB extension:\nCREATE EXTENSION IF NOT EXISTS timescaledb;\nCREATE TABLE measurements (ts TIMESTAMPTZ, sensor_id INT, value DOUBLE PRECISION);\nSELECT create_hypertable('measurements', 'ts');\n-- Summarize with a continuous aggregate.","note":"PostgreSQL puede alojar series temporales con particionamiento nativo (PARTITION BY RANGE); la extensión TimescaleDB añade hypertables con chunking automático por tiempo, compresión, políticas de retención y continuous aggregates. Es una solución habitual para grandes cargas de IoT/métricas.","danger":"safe"},"mysql":{"command":"-- RANGE partitioning by date:\nCREATE TABLE measurements (\n  ts DATETIME, sensor_id INT, value DOUBLE,\n  PRIMARY KEY (ts, sensor_id))\nPARTITION BY RANGE (TO_DAYS(ts)) (\n  PARTITION p2026_01 VALUES LESS THAN (TO_DAYS('2026-02-01')),\n  PARTITION pmax VALUES LESS THAN MAXVALUE);","note":"MySQL no ofrece un motor dedicado a series temporales; el particionamiento RANGE (con TO_DAYS/UNIX_TIMESTAMP) permite eliminar rápidamente los datos antiguos y el partition pruning. Para series temporales de gran volumen se suelen preferir soluciones externas (TimescaleDB, InfluxDB) o tablas resumen.","danger":"safe"},"mssql":{"command":"-- Partitioned table (partition function + scheme) or\n-- system-versioned temporal table (history kept automatically):\nCREATE TABLE dbo.measurements (\n  id INT PRIMARY KEY, value DECIMAL(10,2),\n  valid_from DATETIME2 GENERATED ALWAYS AS ROW START,\n  valid_to DATETIME2 GENERATED ALWAYS AS ROW END,\n  PERIOD FOR SYSTEM_TIME (valid_from, valid_to))\nWITH (SYSTEM_VERSIONING = ON);","note":"SQL Server gestiona las series temporales particionando con una partition function/scheme; esto proporciona pruning y un archivado rápido para datos que abarcan amplios rangos de fechas. Además, las system-versioned temporal tables (SYSTEM_VERSIONING) conservan automáticamente el historial de cambios de las filas y permiten consultas point-in-time (FOR SYSTEM_TIME).","since":"SQL Server 2016","xnote":"Las system-versioned temporal tables llegaron en SQL Server 2016.","danger":"safe"},"mongodb":{"command":"// Time-series collection (MongoDB 5.0+):\ndb.createCollection(\"measurements\", {\n  timeseries: {\n    timeField: \"ts\",\n    metaField: \"sensor_id\",\n    granularity: \"seconds\"\n  }\n})","note":"Las time-series collections, introducidas en MongoDB 5.0, almacenan automáticamente los datos en un formato comprimido de tipo columnar (por buckets) según el tiempo y el campo meta; reducen el uso de disco e índices y aceleran las consultas por rango. timeField es obligatorio; metaField (origen/etiqueta) y granularity determinan el rendimiento.","since":"MongoDB 5.0","xnote":"Las time-series collections llegaron en MongoDB 5.0.","danger":"safe"},"redis":{"command":"# Requires the RedisTimeSeries module (Redis Stack).\n# Create a series with a retention period:\nTS.CREATE sensor:1 RETENTION 86400000 LABELS type temperature\n# Add a sample (with an automatic timestamp):\nTS.ADD sensor:1 * 23.5","note":"Redis básico no tiene un tipo de series temporales; el módulo RedisTimeSeries (Redis Stack) proporciona ingesta de muestras a alta velocidad con TS.CREATE/TS.ADD, retención automática (RETENTION), downsampling (TS.CREATERULE) y consultas por rango basadas en etiquetas con TS.RANGE/TS.MRANGE. Sin el módulo, se emula con un Sorted Set.","danger":"safe"},"elasticsearch":{"command":"# Data stream: automatically rolling indexes for time series\nPUT _index_template/measurements-tpl\n{ \"index_patterns\": [\"measurements*\"], \"data_stream\": {},\n  \"template\": {\n    \"settings\": { \"index.mode\": \"time_series\", \"index.routing_path\": [\"sensor_id\"] },\n    \"mappings\": { \"properties\": {\n      \"@timestamp\": { \"type\": \"date\" },\n      \"sensor_id\": { \"type\": \"keyword\", \"time_series_dimension\": true },\n      \"temperature\": { \"type\": \"double\", \"time_series_metric\": \"gauge\" } } } } }\nPUT _data_stream/measurements","note":"Elasticsearch gestiona los datos de series temporales con data streams; con el tiempo se crean automáticamente nuevos backing indexes (rollover) e ILM mueve los datos antiguos por los niveles hot/warm/cold y los elimina. TSDS (index.mode: time_series) proporciona optimizaciones de almacenamiento específicas para datos de métricas; son obligatorios al menos un campo time_series_dimension, un index.routing_path que apunte a él y un campo @timestamp (de lo contrario, la plantilla se rechaza).","danger":"safe"}}},{"id":"vector-search","title":"Búsqueda vectorial (IA/embeddings)","aliases":["vector search","vector search","embedding","ai","semantic search","knn"],"engines":{"oracle":{"command":"-- Oracle 23ai native VECTOR type:\nCREATE TABLE docs (id NUMBER, content CLOB, embedding VECTOR);\n-- Similarity search (cosine distance):\nSELECT id FROM docs\nORDER BY VECTOR_DISTANCE(embedding, :query_vector, COSINE)\nFETCH FIRST 5 ROWS ONLY;","note":"Oracle Database 23ai ofrece un tipo de datos VECTOR nativo y la función AI Vector Search. La función VECTOR_DISTANCE calcula la similitud con las métricas COSINE/EUCLIDEAN/DOT; se admiten índices vectoriales HNSW e IVF para búsqueda aproximada. Los embeddings se generan en la base de datos o con modelos externos.","since":"Oracle 23ai","xnote":"El tipo VECTOR nativo y AI Vector Search llegaron en Oracle 23ai.","danger":"safe"},"postgres":{"command":"-- pgvector extension:\nCREATE EXTENSION IF NOT EXISTS vector;\nCREATE TABLE docs (id int, embedding vector(1536));\nCREATE INDEX ON docs USING hnsw (embedding vector_cosine_ops);\n-- 5 nearest neighbors (<-> Euclidean, <=> cosine):\nSELECT id FROM docs ORDER BY embedding <=> :query_vec LIMIT 5;","note":"La extensión pgvector añade a PostgreSQL un tipo vector y operadores de similitud: <-> (L2/euclídea), <=> (coseno), <#> (producto interno). Los índices HNSW o IVFFlat aceleran la búsqueda aproximada de vecinos más cercanos (ANN). Muy usada en aplicaciones de RAG y búsqueda semántica.","xnote":"PostgreSQL básico no tiene tipo vector; se requiere la extensión pgvector.","danger":"safe"},"mysql":{"command":"-- MySQL 9.0+ native VECTOR type:\nCREATE TABLE docs (id INT, embedding VECTOR(1536));\n-- Cosine distance (DISTANCE is HeatWave-only):\nSELECT id, DISTANCE(embedding, :query_vec, 'COSINE') AS d\nFROM docs ORDER BY d LIMIT 5;","note":"MySQL 9.0 añadió el tipo de datos VECTOR nativo y las funciones STRING_TO_VECTOR/VECTOR_TO_STRING y VECTOR_DIM (también en Community). La función DISTANCE() y la búsqueda vectorial aproximada a escala solo están disponibles en MySQL HeatWave en Oracle Cloud; en Community/Enterprise, calcule las distancias en la aplicación o en un almacén vectorial externo. Las versiones on-premises anteriores requieren un almacén vectorial externo.","since":"MySQL 9.0","xnote":"El tipo de datos VECTOR nativo llegó en MySQL 9.0.","danger":"safe"},"mssql":{"command":"-- SQL Server 2025 native vector type:\nCREATE TABLE docs (id INT, embedding VECTOR(1536));\n-- Nearest neighbors by cosine distance:\nSELECT TOP 5 id,\n  VECTOR_DISTANCE('cosine', embedding, @query_vec) AS distance\nFROM dbo.docs ORDER BY distance;","note":"SQL Server 2025 (y Azure SQL) ofrecen un tipo de datos vector nativo y la función VECTOR_DISTANCE (cosine/euclidean/dot); los índices vectoriales basados en DiskANN para la búsqueda aproximada de vecinos más cercanos están en desarrollo. Los embeddings se generan con modelos externos como Azure OpenAI y se almacenan.","since":"SQL Server 2025","xnote":"El tipo de datos vector nativo llegó en SQL Server 2025.","danger":"safe"},"mongodb":{"command":"// Atlas Vector Search ($vectorSearch aggregation stage):\ndb.docs.aggregate([\n  { $vectorSearch: {\n      index: \"vector_idx\",\n      path: \"embedding\",\n      queryVector: [0.12, 0.87, /* ... */],\n      numCandidates: 100,\n      limit: 5\n  } }\n])","note":"MongoDB Atlas Vector Search consulta embeddings con la etapa de agregación $vectorSearch; internamente realiza una búsqueda aproximada de vecinos más cercanos basada en HNSW. El índice vectorial se define como un índice de Atlas Search. Esta función es específica del servicio gestionado Atlas (no está disponible en la edición Community on-premises).","danger":"safe"},"redis":{"command":"# Requires the RediSearch (Redis Stack) VECTOR field type.\n# Define an HNSW vector index:\nFT.CREATE docs_idx ON HASH PREFIX 1 docs: SCHEMA\n  embedding VECTOR HNSW 6 TYPE FLOAT32 DIM 1536 DISTANCE_METRIC COSINE\n# KNN search (with a query vector):\nFT.SEARCH docs_idx \"*=>[KNN 5 @embedding $vec]\" PARAMS 2 vec \"...\"","note":"Redis proporciona búsqueda vectorial mediante el módulo RediSearch (Redis Stack); el tipo de campo VECTOR admite índices FLAT (exacto) o HNSW (aproximado) y las métricas COSINE/L2/IP. Los vecinos más cercanos se obtienen con la sintaxis KNN dentro de FT.SEARCH. Se usa para búsqueda semántica de baja latencia y RAG.","danger":"safe"},"elasticsearch":{"command":"# dense_vector field and kNN search:\nPUT docs\n{ \"mappings\": { \"properties\": { \"embedding\":\n  { \"type\": \"dense_vector\", \"dims\": 1536, \"similarity\": \"cosine\" } } } }\nGET docs/_search\n{ \"knn\": { \"field\": \"embedding\", \"query_vector\": [0.12, 0.87],\n  \"k\": 5, \"num_candidates\": 100 } }","note":"Elasticsearch admite consultas aproximadas de vecinos más cercanos (HNSW) con el tipo de campo dense_vector y la búsqueda knn integrada; la similitud puede ser cosine/dot_product/l2_norm. La relevancia se puede mejorar combinando la búsqueda vectorial con una consulta de texto clásica (búsqueda híbrida / RRF).","since":"Elasticsearch 8.0","xnote":"La búsqueda kNN integrada con dense_vector llegó en Elasticsearch 8.x.","danger":"safe"}}},{"id":"cdc-change-tracking","title":"Seguimiento de cambios (CDC / change tracking)","aliases":["cdc","change data capture","change tracking","change tracking","replication feed","binlog"],"engines":{"oracle":{"command":"-- Read changes from redo logs with LogMiner:\n-- 1) Add the archived/redo log file to analyze:\nEXECUTE DBMS_LOGMNR.ADD_LOGFILE(\n  LOGFILENAME => '/u01/arch/1_1234_1122334455.arc',\n  OPTIONS => DBMS_LOGMNR.NEW);\n-- 2) Start the session:\nEXECUTE DBMS_LOGMNR.START_LOGMNR(\n  OPTIONS => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG);\nSELECT sql_redo, sql_undo FROM V$LOGMNR_CONTENTS\nWHERE seg_name = 'ORDERS';\n-- 3) When finished: EXECUTE DBMS_LOGMNR.END_LOGMNR;\n-- For enterprise CDC/replication, Oracle GoldenGate is used.","note":"Oracle hace el seguimiento de cambios basándose en el redo log. LogMiner traduce los redo/archived logs a SQL para inspeccionar DML pasado; los archivos de log que se analizarán se añaden con DBMS_LOGMNR.ADD_LOGFILE. Para obtener resultados significativos, debe estar habilitado al menos el supplemental logging mínimo (ALTER DATABASE ADD SUPPLEMENTAL LOG DATA). Para CDC continuo y de baja latencia y replicación heterogénea, el estándar es Oracle GoldenGate (un producto aparte); las antiguas funciones Streams/Change Data Capture están obsoletas.","danger":"caution"},"postgres":{"command":"-- Create a logical replication (logical decoding) slot:\nSELECT * FROM pg_create_logical_replication_slot(\n  'cdc_slot', 'wal2json');\n-- Consume changes as JSON:\nSELECT data FROM pg_logical_slot_get_changes('cdc_slot', NULL, NULL);","note":"El logical decoding de PostgreSQL convierte los registros del WAL en un flujo de cambios legible mediante un output plugin (wal2json, pgoutput, test_decoding); el replication slot garantiza que no se pierdan cambios. wal_level debe ser logical. Debezium usa este mecanismo para ofrecer CDC empresarial.","danger":"caution"},"mysql":{"command":"-- The row-based binlog is the CDC source (binlog_format=ROW):\nSHOW BINARY LOGS;\nSHOW BINLOG EVENTS IN 'binlog.000042';\n-- Usually Debezium/Maxwell reads the binlog and turns it into a stream.","note":"En MySQL, la fuente de CDC es el binary log. Con binlog_format=ROW se registra cada cambio de fila. Herramientas como Debezium o Maxwell leen el binlog como una réplica y generan un flujo de eventos hacia Kafka. Habilitar GTID facilita un seguimiento fiable de la posición.","danger":"caution"},"mssql":{"command":"-- Enable Change Data Capture on the database and the table:\nEXEC sys.sp_cdc_enable_db;\nEXEC sys.sp_cdc_enable_table\n  @source_schema = N'dbo', @source_name = N'orders',\n  @role_name = NULL;\n-- For lightweight tracking: ALTER DATABASE ... SET CHANGE_TRACKING = ON;","note":"SQL Server ofrece dos mecanismos: Change Data Capture (CDC) escribe todo el DML (con valores antiguos/nuevos) desde el log de transacciones en tablas de cambios y requiere SQL Server Agent. Change Tracking solo registra de forma ligera qué filas cambiaron (cambios netos) y no conserva un historial completo de los datos.","since":"SQL Server 2008","xnote":"CDC y Change Tracking llegaron en SQL Server 2008.","danger":"caution"},"mongodb":{"command":"// Watch changes in real time with Change Streams:\nconst stream = db.orders.watch([\n  { $match: { operationType: { $in: [\"insert\", \"update\"] } } }\n]);\nwhile (stream.hasNext()) { printjson(stream.next()); }","note":"MongoDB Change Streams, construidos sobre el oplog, entregan los eventos insert, update y delete a nivel de colección/base de datos/clúster como un flujo en tiempo real. Requiere un replica set o un sharded cluster. Con un resumeToken, un flujo puede reanudarse donde lo dejó tras una interrupción; es la base de las integraciones de CDC.","since":"MongoDB 3.6","xnote":"Change Streams llegaron en MongoDB 3.6.","danger":"safe"},"redis":{"command":"# Listen for key changes with keyspace notifications:\nCONFIG SET notify-keyspace-events KEA\n# Subscribe to changed keys via Pub/Sub:\nPSUBSCRIBE __keyevent@0__:set\nPSUBSCRIBE __keyevent@0__:expired","note":"Las keyspace notifications de Redis publican eventos sobre las claves (SET, DEL, EXPIRE, etc.) a través de canales Pub/Sub; se habilitan con la opción notify-keyspace-events (K=keyspace, E=keyevent, A=todos los eventos). Para un flujo de cambios fiable y durable, se prefieren Redis Streams (XADD/XREAD).","danger":"safe"},"elasticsearch":{"command":"-- Not applicable: there is no built-in CDC/change stream.\n# Closest equivalent: incremental query on a timestamp field:\nGET orders/_search\n{ \"query\": { \"range\": { \"updated_ts\": { \"gt\": \"2026-07-05T00:00:00\" } } },\n  \"sort\": [ { \"updated_ts\": \"asc\" } ] }","note":"Elasticsearch no ofrece un flujo de captura de cambios (CDC); no publica de forma nativa cuándo cambió un documento. El enfoque habitual es añadir un campo updated_ts a cada documento y ejecutar consultas incrementales por marca de tiempo (range + sort). Normalmente se alimenta Elasticsearch con el CDC de la base de datos de origen.","danger":"safe"}}}]},"rosetta/sql-syntax.json":{"category":"Sintaxis y palabras clave SQL","categoryId":"sql-syntax","order":19,"concepts":[{"id":"with-cte","title":"WITH (Common Table Expression / CTE)","aliases":["with","cte","common table expression","temporary query","temporary result set"],"engines":{"oracle":{"command":"WITH region AS (\n  SELECT city, COUNT(*) AS n\n  FROM customer\n  GROUP BY city\n)\nSELECT city, n FROM region WHERE n > 10;","note":"WITH define un conjunto de resultados temporal con nombre (CTE) dentro de una consulta; en lugar de escribir una subconsulta repetidamente, se define una vez y se referencia por su nombre. Divide las consultas complejas en partes legibles. Soportado desde Oracle 9i.","since":"Oracle 9i","danger":"safe"},"postgres":{"command":"WITH region AS (\n  SELECT city, COUNT(*) AS n\n  FROM customer\n  GROUP BY city\n)\nSELECT city, n FROM region WHERE n > 10;","note":"Una CTE divide las consultas complejas en bloques con nombre. Antes de PostgreSQL 12 una CTE era una barrera de optimización (siempre se materializaba); a partir de 12 puede integrarse en línea, y el comportamiento antiguo se puede forzar con WITH ... AS MATERIALIZED.","danger":"safe"},"mysql":{"command":"WITH region AS (\n  SELECT city, COUNT(*) AS n\n  FROM customer\n  GROUP BY city\n)\nSELECT city, n FROM region WHERE n > 10;","note":"Una CTE define un conjunto de resultados temporal con nombre dentro de una consulta y mejora la legibilidad. Soportado a partir de MySQL 8.0 (y MariaDB 10.2); las versiones anteriores requieren una derived table (una subconsulta en FROM).","since":"MySQL 8.0","xnote":"Antes de MySQL 8.0 no había CTE; lo mismo se hacía con una derived table (una subconsulta en FROM).","danger":"safe"},"mssql":{"command":"WITH region AS (\n  SELECT city, COUNT(*) AS n\n  FROM customer\n  GROUP BY city\n)\nSELECT city, n FROM region WHERE n > 10;","note":"Una CTE es un conjunto de resultados temporal con nombre. En SQL Server, la sentencia inmediatamente anterior a una CTE debe terminar con punto y coma, por eso WITH se escribe a menudo como ';WITH'. Es válida durante una sola sentencia (SELECT/INSERT/UPDATE/DELETE).","danger":"safe"},"mongodb":{"command":"// Closest equivalent: aggregation pipeline stages\ndb.customer.aggregate([\n  { $group: { _id: \"$city\", n: { $sum: 1 } } },\n  { $match: { n: { $gt: 10 } } }\n])","note":"MongoDB no tiene SQL/CTE; las etapas del aggregation pipeline cumplen la misma función. Cada etapa toma como entrada la salida de la anterior, lo que da un procesamiento paso a paso similar a las cadenas de CTE. También se pueden usar $facet o $lookup para resultados intermedios.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no concept of SQL or CTEs.","note":"Redis es un almacén de datos clave-valor sin lenguaje de consulta. Los resultados intermedios se combinan en el lado del cliente, o se usa un script Lua (EVAL) para la lógica en el servidor.","danger":"safe"},"elasticsearch":{"command":"# CTEs are not supported in Elasticsearch SQL.\nPOST _sql\n{ \"query\": \"SELECT city, COUNT(*) n FROM customer GROUP BY city HAVING COUNT(*) > 10\" }","note":"Elasticsearch SQL (la API _sql) no admite la sintaxis WITH/CTE; la agrupación y el filtrado se escriben en un único SELECT. Con el enfoque nativo, lo mismo se hace con una agregación 'terms' y una pipeline aggregation 'bucket_selector'.","danger":"safe"}}},{"id":"recursive-cte","title":"CTE recursiva (WITH RECURSIVE)","aliases":["recursive","recursion","with recursive","connect by","hierarchy","tree query"],"engines":{"oracle":{"command":"-- Hierarchy with CONNECT BY:\nSELECT id, name, manager_id, LEVEL\nFROM employee\nSTART WITH manager_id IS NULL\nCONNECT BY PRIOR id = manager_id;\n\n-- Alternative (11gR2+): standard recursive WITH (the RECURSIVE keyword is not written)\nWITH tree (id, name, manager_id) AS (\n  SELECT id, name, manager_id FROM employee WHERE manager_id IS NULL\n  UNION ALL\n  SELECT c.id, c.name, c.manager_id\n  FROM employee c JOIN tree a ON c.manager_id = a.id\n)\nSELECT * FROM tree;","note":"Se usa para recorrer datos jerárquicos (árboles): organigramas, listas de materiales y similares. La forma clásica de Oracle es CONNECT BY / PRIOR y la pseudocolumna LEVEL; START WITH establece la fila raíz. Desde 11gR2 también se admite la CTE recursiva estándar.","since":"Oracle 11gR2","xnote":"CONNECT BY es el método clásico; el WITH recursivo estándar (sin la palabra clave RECURSIVE) llegó con Oracle 11gR2.","danger":"safe"},"postgres":{"command":"WITH RECURSIVE tree (id, name, manager_id, level) AS (\n  SELECT id, name, manager_id, 1\n  FROM employee WHERE manager_id IS NULL\n  UNION ALL\n  SELECT c.id, c.name, c.manager_id, a.level + 1\n  FROM employee c JOIN tree a ON c.manager_id = a.id\n)\nSELECT * FROM tree ORDER BY level;","note":"WITH RECURSIVE combina una consulta ancla y una parte recursiva con UNION ALL; la parte recursiva se ejecuta repetidamente hasta que el resultado queda vacío. Ideal para jerarquías y recorridos de grafos. Protéjase de los bucles infinitos con UNION (en lugar de ALL) o con protección de ciclos.","since":"PostgreSQL 8.4","danger":"safe"},"mysql":{"command":"WITH RECURSIVE tree (id, name, manager_id, level) AS (\n  SELECT id, name, manager_id, 1\n  FROM employee WHERE manager_id IS NULL\n  UNION ALL\n  SELECT c.id, c.name, c.manager_id, a.level + 1\n  FROM employee c JOIN tree a ON c.manager_id = a.id\n)\nSELECT * FROM tree ORDER BY level;","note":"MySQL 8.0 (y MariaDB 10.2+) admiten la sintaxis WITH RECURSIVE conforme al estándar. La profundidad de recursión está limitada por la variable de sistema cte_max_recursion_depth (por defecto 1000). Las versiones anteriores requerían escribir un bucle en un procedimiento almacenado.","since":"MySQL 8.0","xnote":"Antes de MySQL 8.0 (MariaDB 10.2) no había consultas recursivas; se escribía un bucle en un procedimiento almacenado.","danger":"safe"},"mssql":{"command":"WITH tree (id, name, manager_id, level) AS (\n  SELECT id, name, manager_id, 1\n  FROM employee WHERE manager_id IS NULL\n  UNION ALL\n  SELECT c.id, c.name, c.manager_id, a.level + 1\n  FROM employee c JOIN tree a ON c.manager_id = a.id\n)\nSELECT * FROM tree ORDER BY level\nOPTION (MAXRECURSION 100);","note":"En SQL Server no se escribe la palabra clave RECURSIVE para una CTE recursiva; una CTE con UNION ALL que se referencia a sí misma se trata automáticamente como recursiva. El límite de recursión por defecto es 100; cámbielo con OPTION (MAXRECURSION n) (0 = ilimitado, úselo con cuidado).","since":"SQL Server 2005","danger":"safe"},"mongodb":{"command":"// Closest equivalent: the $graphLookup stage\ndb.employee.aggregate([\n  { $match: { manager_id: null } },\n  { $graphLookup: {\n      from: \"employee\",\n      startWith: \"$id\",\n      connectFromField: \"id\",\n      connectToField: \"manager_id\",\n      as: \"subordinates\"\n  } }\n])","note":"MongoDB no tiene CTE recursiva; para recorridos jerárquicos se usa la etapa de agregación $graphLookup. connectFromField/connectToField establecen los campos de enlace y maxDepth el límite de profundidad. Resuelve relaciones de árbol y de grafo en una sola consulta.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no recursive queries.","note":"Redis no ofrece un lenguaje de consulta. El recorrido de jerarquías/grafos se hace en el código de la aplicación o en el servidor con un script Lua; en Redis básico, el recorrido del árbol se escribe en el lado del cliente. (El soporte del módulo RedisGraph, usado anteriormente, terminó en 2023.)","danger":"safe"},"elasticsearch":{"command":"# Not applicable: recursive queries are not supported.\n# Approach: model with parent-child (join field) or nested,\n# deep hierarchies are traversed on the application side.","note":"Elasticsearch no ofrece consultas recursivas (no hay equivalente de WITH RECURSIVE / CONNECT BY). Las relaciones jerárquicas se modelan con el tipo de campo 'join' (parent/child) o almacenando la ruta del árbol (materialized path) en un campo; el recorrido de varios niveles suele hacerse en la aplicación.","danger":"safe"}}},{"id":"joins","title":"Tipos de JOIN (INNER/LEFT/RIGHT/FULL/CROSS)","aliases":["join","inner join","left join","right join","full join","cross join","combine tables","table join"],"engines":{"oracle":{"command":"-- INNER: matching rows\nSELECT m.name, s.amount\nFROM customer m JOIN orders s ON m.id = s.customer_id;\n-- LEFT: customers without orders are included too\nSELECT m.name, s.amount\nFROM customer m LEFT JOIN orders s ON m.id = s.customer_id;\n-- CROSS: Cartesian product\nSELECT r.name, u.name FROM color r CROSS JOIN product u;","note":"Un JOIN combina dos tablas según la condición indicada. INNER devuelve solo las coincidencias; LEFT/RIGHT devuelven todas las filas de un lado (NULL donde no hay coincidencia); FULL devuelve las filas de ambos lados; CROSS devuelve todas las combinaciones. Además de los JOIN ANSI estándar, Oracle también admite la antigua sintaxis de outer join (+).","danger":"safe"},"postgres":{"command":"SELECT m.name, s.amount\nFROM customer m INNER JOIN orders s ON m.id = s.customer_id;\n\nSELECT m.name, s.amount\nFROM customer m LEFT JOIN orders s ON m.id = s.customer_id;\n\nSELECT m.name, s.amount\nFROM customer m FULL OUTER JOIN orders s ON m.id = s.customer_id;","note":"PostgreSQL admite plenamente todos los tipos de JOIN estándar (INNER, LEFT/RIGHT/FULL OUTER, CROSS). USING(my_column) es una abreviatura para columnas con el mismo nombre, mientras que NATURAL JOIN une automáticamente por todas las columnas homónimas (úselo con cuidado).","danger":"safe"},"mysql":{"command":"SELECT m.name, s.amount\nFROM customer m INNER JOIN orders s ON m.id = s.customer_id;\n\nSELECT m.name, s.amount\nFROM customer m LEFT JOIN orders s ON m.id = s.customer_id;\n\n-- MySQL has no FULL OUTER JOIN:\nSELECT m.name, s.amount FROM customer m LEFT JOIN orders s ON m.id = s.customer_id\nUNION\nSELECT m.name, s.amount FROM customer m RIGHT JOIN orders s ON m.id = s.customer_id;","note":"MySQL/MariaDB admiten INNER, LEFT, RIGHT y CROSS JOIN, pero no ofrecen FULL OUTER JOIN directamente; se emula combinando los resultados de LEFT y RIGHT con UNION. El antiguo join con comas produce un producto cartesiano y debe restringirse con ON/WHERE.","danger":"safe"},"mssql":{"command":"SELECT m.name, s.amount\nFROM customer m INNER JOIN orders s ON m.id = s.customer_id;\n\nSELECT m.name, s.amount\nFROM customer m LEFT JOIN orders s ON m.id = s.customer_id;\n\nSELECT m.name, s.amount\nFROM customer m FULL OUTER JOIN orders s ON m.id = s.customer_id;","note":"SQL Server admite todos los tipos de JOIN estándar. Los antiguos operadores de outer join *= / =* se han eliminado; debe usarse la sintaxis JOIN ANSI. APPLY (CROSS/OUTER APPLY) es una forma de join adicional para subconsultas correlacionadas fila a fila.","danger":"safe"},"mongodb":{"command":"// Closest equivalent: $lookup (left outer join)\ndb.customer.aggregate([\n  { $lookup: {\n      from: \"orders\",\n      localField: \"id\",\n      foreignField: \"customer_id\",\n      as: \"orders\"\n  } }\n])","note":"MongoDB no tiene JOIN clásico; la etapa de agregación $lookup obtiene como array los documentos coincidentes de otra colección (similar a un left outer join). Para un resultado tipo INNER, filtre después los arrays vacíos con $match y expanda el array en filas con $unwind.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no tables or JOINs.","note":"Redis no ofrece joins relacionales; los datos suelen desnormalizarse, o las relaciones se construyen en el lado del cliente leyendo varias claves. RediSearch (FT.AGGREGATE) puede usarse para consultas limitadas de tipo join; el soporte del módulo RedisGraph terminó en 2023.","danger":"safe"},"elasticsearch":{"command":"# Not applicable: there is no general JOIN in a distributed design.\n# Limited options: nested, parent/child (join field),\n# terms lookup. Data is usually denormalized at indexing time.","note":"Por su arquitectura distribuida, Elasticsearch no ofrece JOIN arbitrarios entre tablas. Para relaciones limitadas existen el tipo de campo 'nested' dentro de un único índice y el tipo de campo 'join' (parent/child) en el mismo shard. La solución más habitual es desnormalizar los datos en el momento de la indexación.","danger":"safe"}}},{"id":"window-functions","title":"Funciones de ventana (OVER / PARTITION BY)","aliases":["window","window function","over","partition by","row_number","rank","lag","lead","analytic function"],"engines":{"oracle":{"command":"SELECT name, department, salary,\n  ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_no,\n  RANK()       OVER (PARTITION BY department ORDER BY salary DESC) AS rank_no,\n  LAG(salary)    OVER (PARTITION BY department ORDER BY salary)      AS prev_salary\nFROM employee;","note":"Las funciones de ventana calculan sobre una 'ventana' para cada fila sin agrupar las filas (sin reducir el número de filas). PARTITION BY divide la ventana y ORDER BY la ordena. Oracle fue pionero en estas funciones analíticas y ofrece un conjunto amplio que incluye ROW_NUMBER, RANK, DENSE_RANK, LAG/LEAD y SUM() OVER.","since":"Oracle 8i","danger":"safe"},"postgres":{"command":"SELECT name, department, salary,\n  ROW_NUMBER() OVER w AS row_no,\n  RANK()       OVER w AS rank_no,\n  LEAD(salary)   OVER w AS next_salary\nFROM employee\nWINDOW w AS (PARTITION BY department ORDER BY salary DESC);","note":"PostgreSQL admite todas las funciones de ventana estándar y permite nombrar una definición de ventana repetida con WINDOW ... AS. Con las definiciones de marco ROWS/RANGE/GROUPS se pueden calcular totales acumulados, medias móviles y similares.","since":"PostgreSQL 8.4","danger":"safe"},"mysql":{"command":"SELECT name, department, salary,\n  ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_no,\n  DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_no,\n  LAG(salary)    OVER (PARTITION BY department ORDER BY salary)      AS prev_salary\nFROM employee;","note":"MySQL 8.0 (y MariaDB 10.2+) admiten funciones de ventana; antes se emulaban con variables de usuario o subconsultas. Están disponibles ROW_NUMBER, RANK, DENSE_RANK, NTILE, LAG/LEAD, FIRST_VALUE/LAST_VALUE y la forma OVER de las funciones de agregación.","since":"MySQL 8.0","xnote":"Antes de MySQL 8.0 no había funciones de ventana; se emulaban con variables/self-joins.","danger":"safe"},"mssql":{"command":"SELECT name, department, salary,\n  ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_no,\n  RANK()       OVER (PARTITION BY department ORDER BY salary DESC) AS rank_no,\n  SUM(salary)    OVER (PARTITION BY department) AS dept_total\nFROM employee;","note":"SQL Server admite funciones de ventana de ranking desde 2005, y agregados con marco y funciones como LAG/LEAD desde 2012. ROW_NUMBER se usa habitualmente para la paginación (con OFFSET/FETCH) y para obtener las N primeras filas por grupo.","since":"SQL Server 2005","xnote":"Las funciones de ventana de ranking llegaron en 2005; los marcos y LAG/LEAD, en 2012.","danger":"safe"},"mongodb":{"command":"// Closest equivalent: $setWindowFields (MongoDB 5.0+)\ndb.employee.aggregate([\n  { $setWindowFields: {\n      partitionBy: \"$department\",\n      sortBy: { salary: -1 },\n      output: { row_no: { $rank: {} },\n                prev_salary: { $shift: { output: \"$salary\", by: -1 } } }\n  } }\n])","note":"La etapa $setWindowFields de MongoDB 5.0+ es el equivalente de las funciones de ventana de SQL: la ventana se define con partitionBy y sortBy y produce salidas como $rank/$denseRank/$documentNumber, $shift (LAG/LEAD) y totales acumulados. En versiones anteriores se resolvía con $group + código.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no window functions.","note":"Redis no ofrece funciones analíticas de ventana. Para resultados ordenados y rankings, use la estructura Sorted Set (ZSET): asigne puntuaciones con ZADD, lea la posición con ZRANK/ZREVRANK y los rangos con ZRANGE. La analítica compleja se hace en la aplicación.","danger":"safe"},"elasticsearch":{"command":"# Closest equivalent: top_hits and pipeline aggregations\nGET employee/_search\n{\n  \"size\": 0,\n  \"aggs\": { \"dept\": { \"terms\": { \"field\": \"department\" },\n    \"aggs\": { \"top_paid\": { \"top_hits\": {\n      \"sort\": [ { \"salary\": \"desc\" } ], \"size\": 3 } } } } }\n}","note":"Elasticsearch no tiene funciones de ventana directas. Para los N primeros registros por grupo use una subagregación 'top_hits'; para cálculos móviles/acumulados use pipeline aggregations como 'derivative', 'cumulative_sum' y 'moving_fn'. La ordenación y el particionamiento se hacen con una terms aggregation.","danger":"safe"}}},{"id":"group-rollup","title":"GROUP BY, HAVING, ROLLUP/CUBE/GROUPING SETS","aliases":["group by","having","rollup","cube","grouping sets","grouping","subtotal","subtotals"],"engines":{"oracle":{"command":"-- Basic grouping + HAVING\nSELECT city, COUNT(*) AS n\nFROM customer\nGROUP BY city\nHAVING COUNT(*) > 5;\n\n-- Subtotals and grand total with ROLLUP\nSELECT city, district, SUM(balance)\nFROM customer\nGROUP BY ROLLUP(city, district);","note":"GROUP BY agrupa las filas por las columnas indicadas y aplica funciones de agregación; HAVING filtra el resultado agrupado (WHERE filtra las filas antes de agrupar). ROLLUP genera subtotales jerárquicos + un total general, CUBE todas las combinaciones y GROUPING SETS las agrupaciones seleccionadas. Oracle admite las tres.","danger":"safe"},"postgres":{"command":"SELECT city, district, SUM(balance)\nFROM customer\nGROUP BY GROUPING SETS ((city, district), (city), ());\n\n-- CUBE produces all subset combinations\nSELECT city, district, SUM(balance)\nFROM customer\nGROUP BY CUBE(city, district);","note":"PostgreSQL 9.5+ admite ROLLUP, CUBE y GROUPING SETS conforme al estándar. La función GROUPING() indica si una columna está en una fila de total (NULL). HAVING filtra después de la agregación y WHERE antes.","since":"PostgreSQL 9.5","xnote":"ROLLUP, CUBE y GROUPING SETS llegaron con PostgreSQL 9.5.","danger":"safe"},"mysql":{"command":"SELECT city, COUNT(*) AS n\nFROM customer\nGROUP BY city\nHAVING n > 5;\n\n-- ROLLUP in MySQL:\nSELECT city, district, SUM(balance)\nFROM customer\nGROUP BY city, district WITH ROLLUP;","note":"MySQL/MariaDB generan subtotales/totales generales con la sintaxis GROUP BY ... WITH ROLLUP; CUBE y GROUPING SETS no están soportados. Los alias de SELECT se pueden usar en HAVING (esto en general no funciona en Oracle/SQL Server). En las filas de ROLLUP, la columna agrupada aparece como NULL.","danger":"safe"},"mssql":{"command":"SELECT city, COUNT(*) AS n\nFROM customer\nGROUP BY city\nHAVING COUNT(*) > 5;\n\n-- ROLLUP / CUBE / GROUPING SETS\nSELECT city, district, SUM(balance)\nFROM customer\nGROUP BY ROLLUP(city, district);","note":"Junto con GROUP BY, SQL Server admite ROLLUP(), CUBE() y GROUPING SETS (2008+) con sintaxis moderna; la antigua forma WITH ROLLUP/WITH CUBE está obsoleta. GROUPING() y GROUPING_ID() ayudan a distinguir las filas de total.","since":"SQL Server 2008","xnote":"La sintaxis moderna de GROUPING SETS llegó con SQL Server 2008.","danger":"safe"},"mongodb":{"command":"// Closest equivalent: $group + $sum\ndb.customer.aggregate([\n  { $group: { _id: \"$city\", n: { $sum: 1 } } },\n  { $match: { n: { $gt: 5 } } }\n])\n// $facet is used for ROLLUP-like multi-level totals","note":"En MongoDB, $group es el equivalente de GROUP BY en SQL; _id establece la clave de agrupación y los acumuladores ($sum, $avg, $max) definen los agregados. El filtrado posterior a la agrupación se hace con $match (el equivalente de HAVING). Los totales de varios niveles como ROLLUP/CUBE se obtienen ejecutando varios pipelines con $facet.","danger":"safe"},"redis":{"command":"# Not applicable: core Redis has no GROUP BY.\n# Aggregation with RediSearch:\nFT.AGGREGATE customer_idx \"*\" GROUPBY 1 @city REDUCE COUNT 0 AS n","note":"Redis básico no ofrece agrupación; el comando FT.AGGREGATE del módulo RediSearch realiza agregaciones tipo SQL con GROUPBY y REDUCE (COUNT, SUM, AVG). El paso FILTER filtra el resultado agrupado como HAVING. Sin el módulo, la agregación se hace en la aplicación.","danger":"safe"},"elasticsearch":{"command":"GET customer/_search\n{\n  \"size\": 0,\n  \"aggs\": {\n    \"by_city\": { \"terms\": { \"field\": \"city\" },\n      \"aggs\": { \"total\": { \"sum\": { \"field\": \"balance\" } } } }\n  }\n}","note":"En Elasticsearch, la agrupación se hace con la bucket aggregation 'terms' y los agregados con metric aggregations (sum, avg, cardinality). Para un filtrado tipo HAVING, use la pipeline aggregation 'bucket_selector'. Los subtotales de varios niveles se construyen con agregaciones anidadas.","danger":"safe"}}},{"id":"case-expr","title":"Expresión CASE (valor condicional)","aliases":["case","conditional","when","then","decode","if then","conditional value"],"engines":{"oracle":{"command":"-- Searched CASE\nSELECT name,\n  CASE WHEN salary >= 20000 THEN 'high'\n       WHEN salary >= 10000 THEN 'medium'\n       ELSE 'low' END AS tier\nFROM employee;\n\n-- Oracle-specific shortcut: DECODE (equality comparison)\nSELECT name, DECODE(status, 'A', 'Active', 'P', 'Passive', 'Unknown') FROM employee;","note":"CASE asigna a una expresión distintos valores según condiciones; tiene una forma simple (CASE my_column WHEN value) y una forma de búsqueda (CASE WHEN condition). DECODE de Oracle hace algo similar, pero solo compara por igualdad y trata los NULL como iguales; para la portabilidad, prefiera CASE.","danger":"safe"},"postgres":{"command":"SELECT name,\n  CASE WHEN salary >= 20000 THEN 'high'\n       WHEN salary >= 10000 THEN 'medium'\n       ELSE 'low' END AS tier\nFROM employee;","note":"PostgreSQL admite plenamente la expresión CASE estándar, que puede usarse en cualquier lugar donde se produzca un valor (SELECT, WHERE, ORDER BY, GROUP BY). No tiene DECODE al estilo de Oracle; use CASE en su lugar, o COALESCE/NULLIF para condiciones cortas.","danger":"safe"},"mysql":{"command":"SELECT name,\n  CASE WHEN salary >= 20000 THEN 'high'\n       WHEN salary >= 10000 THEN 'medium'\n       ELSE 'low' END AS tier\nFROM employee;\n\n-- IF() for a short condition:\nSELECT name, IF(active = 1, 'Yes', 'No') FROM employee;","note":"MySQL/MariaDB admiten la expresión CASE estándar; también existe la función IF(condition, if_true, if_false) para una condición rápida de dos ramas (similar a IIF de SQL Server). CASE se puede usar en cualquier contexto de expresión.","danger":"safe"},"mssql":{"command":"SELECT name,\n  CASE WHEN salary >= 20000 THEN 'high'\n       WHEN salary >= 10000 THEN 'medium'\n       ELSE 'low' END AS tier\nFROM employee;\n\n-- Shortcut: IIF() (2012+)\nSELECT name, IIF(active = 1, 'Yes', 'No') FROM employee;","note":"SQL Server admite el CASE estándar; desde 2012 existen el atajo IIF() para condiciones de dos ramas y CHOOSE() para múltiples coincidencias. IIF se traduce realmente a CASE. CASE también se puede usar en ORDER BY y en computed columns.","danger":"safe"},"mongodb":{"command":"// Closest equivalent: $switch or $cond\ndb.employee.aggregate([\n  { $project: { name: 1, tier: {\n      $switch: { branches: [\n        { case: { $gte: [\"$salary\", 20000] }, then: \"high\" },\n        { case: { $gte: [\"$salary\", 10000] }, then: \"medium\" } ],\n        default: \"low\" } } } }\n])","note":"En la agregación de MongoDB, los equivalentes de CASE son los operadores $switch (varias ramas) y $cond (if-then-else de tres argumentos). Se usan dentro de $project o $addFields para crear campos derivados condicionales.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no in-query conditional expression.","note":"Redis no tiene una expresión de consulta como CASE. La lógica condicional se escribe en el código de la aplicación o en un script Lua en el servidor (EVAL). Dentro de FT.AGGREGATE de RediSearch es posible una evaluación limitada de expresiones con APPLY.","danger":"safe"},"elasticsearch":{"command":"# Closest equivalent: a condition inside a runtime field or script\nGET employee/_search\n{\n  \"runtime_mappings\": { \"tier\": { \"type\": \"keyword\",\n    \"script\": \"if (doc['salary'].value >= 20000) emit('high'); else if (doc['salary'].value >= 10000) emit('medium'); else emit('low');\" } },\n  \"fields\": [\"tier\"]\n}","note":"En Elasticsearch, la lógica CASE dentro de la consulta se escribe como un script Painless (runtime field, script_fields o scripted metric). El flujo if/else se define en el lenguaje Painless. La agregación 'range' también puede generar buckets tipo CASE para categorizar.","danger":"safe"}}},{"id":"union-set","title":"UNION / UNION ALL / INTERSECT / EXCEPT (MINUS)","aliases":["union","union all","intersect","except","minus","set operations","combine","intersection","difference"],"engines":{"oracle":{"command":"-- Union without duplicates\nSELECT city FROM customer\nUNION\nSELECT city FROM supplier;\n\n-- Intersection and difference (MINUS instead of EXCEPT in Oracle)\nSELECT city FROM customer INTERSECT SELECT city FROM supplier;\nSELECT city FROM customer MINUS SELECT city FROM supplier;","note":"Los operadores de conjuntos combinan los conjuntos de resultados de dos consultas. UNION elimina duplicados (más lento), UNION ALL los conserva todos (más rápido), INTERSECT devuelve las filas comunes y el operador de diferencia devuelve las filas que solo están en el primer conjunto. Para la diferencia, Oracle usa MINUS en lugar del EXCEPT estándar (EXCEPT también existe desde 21c).","danger":"safe"},"postgres":{"command":"SELECT city FROM customer\nUNION\nSELECT city FROM supplier;\n\nSELECT city FROM customer INTERSECT SELECT city FROM supplier;\nSELECT city FROM customer EXCEPT SELECT city FROM supplier;","note":"PostgreSQL admite plenamente UNION, UNION ALL, INTERSECT y EXCEPT estándar (EXCEPT, no MINUS, para la diferencia). La forma ALL de cada operador (INTERSECT ALL, EXCEPT ALL) conserva el número de duplicados. El número y los tipos de columnas deben ser compatibles.","danger":"safe"},"mysql":{"command":"SELECT city FROM customer\nUNION\nSELECT city FROM supplier;\n\n-- INTERSECT/EXCEPT arrive with MySQL 8.0.31+ and MariaDB 10.3+\nSELECT city FROM customer INTERSECT SELECT city FROM supplier;","note":"Durante mucho tiempo MySQL solo admitió UNION / UNION ALL; INTERSECT y EXCEPT no se añadieron hasta MySQL 8.0.31 (MariaDB 10.3). En versiones anteriores use INNER JOIN/EXISTS para la intersección y LEFT JOIN ... IS NULL o NOT EXISTS para la diferencia.","since":"MySQL 8.0.31","xnote":"INTERSECT/EXCEPT llegaron con MySQL 8.0.31 (MariaDB 10.3); antes se resolvían con JOIN/EXISTS.","danger":"safe"},"mssql":{"command":"SELECT city FROM customer\nUNION\nSELECT city FROM supplier;\n\nSELECT city FROM customer INTERSECT SELECT city FROM supplier;\nSELECT city FROM customer EXCEPT SELECT city FROM supplier;","note":"SQL Server admite UNION, UNION ALL, INTERSECT y EXCEPT estándar (2005+); el operador de diferencia es EXCEPT (no MINUS). INTERSECT/EXCEPT comparan las filas tratando los valores NULL como iguales.","since":"SQL Server 2005","danger":"safe"},"mongodb":{"command":"// Closest equivalent: $unionWith (MongoDB 4.4+)\ndb.customer.aggregate([\n  { $project: { city: 1, _id: 0 } },\n  { $unionWith: { coll: \"supplier\",\n      pipeline: [ { $project: { city: 1, _id: 0 } } ] } },\n  { $group: { _id: \"$city\" } }  // UNION (distinct) effect\n])","note":"La etapa $unionWith de MongoDB 4.4+ combina los resultados de dos colecciones (como UNION ALL). Para eliminar duplicados, deduplique después con $group. INTERSECT/EXCEPT no tienen un equivalente directo; se modelan con $lookup u operadores de conjuntos ($setIntersection, $setDifference).","danger":"safe"},"redis":{"command":"# Set operations with the Set data type:\nSUNION city_customer city_supplier    # union\nSINTER city_customer city_supplier    # intersection\nSDIFF  city_customer city_supplier    # difference","note":"Redis no tiene SQL, pero el tipo de datos Set ofrece operaciones de conjuntos nativas: SUNION (unión), SINTER (intersección), SDIFF (diferencia) y sus variantes STORE, que guardan el resultado. Operan sobre miembros de conjuntos ya construidos, no sobre resultados de consultas.","danger":"safe"},"elasticsearch":{"command":"# There is no direct set operator.\n# For a union, search multiple indices in one query:\nGET customer,supplier/_search\n{ \"query\": { \"match_all\": {} } }","note":"Elasticsearch no ofrece UNION/INTERSECT/EXCEPT. El efecto de unión se logra consultando varios índices separados por comas; para la intersección y la diferencia use las cláusulas must/should/must_not de una bool query o lógica de conjuntos en la aplicación.","danger":"safe"}}},{"id":"exists-in","title":"EXISTS / IN / NOT EXISTS (subconsulta)","aliases":["exists","in","not exists","not in","subquery","sub-select","correlated subquery"],"engines":{"oracle":{"command":"-- Customers with orders (EXISTS)\nSELECT name FROM customer m\nWHERE EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);\n\n-- List membership with IN\nSELECT name FROM customer\nWHERE city IN ('Ankara', 'Izmir');\n\n-- Customers with no orders at all (NOT EXISTS)\nSELECT name FROM customer m\nWHERE NOT EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);","note":"EXISTS comprueba si una subconsulta correlacionada devuelve al menos una fila (se detiene en la primera coincidencia, por lo que es rápido). IN comprueba si un valor está en una lista/resultado de subconsulta. Atención: si la subconsulta puede producir NULL, NOT IN da resultados inesperados; NOT EXISTS es más seguro. El optimizador de Oracle a menudo convierte ambos en el mismo plan.","danger":"safe"},"postgres":{"command":"SELECT name FROM customer m\nWHERE EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);\n\nSELECT name FROM customer\nWHERE city IN (SELECT city FROM target_cities);\n\nSELECT name FROM customer m\nWHERE NOT EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);","note":"PostgreSQL admite plenamente EXISTS/IN/NOT EXISTS. Con NOT IN, si la subconsulta contiene NULL todo el resultado puede quedar vacío, por lo que se recomienda NOT EXISTS o LEFT JOIN ... IS NULL. El planificador trata IN y EXISTS de forma equivalente como un semi-join en la mayoría de los casos.","danger":"safe"},"mysql":{"command":"SELECT name FROM customer m\nWHERE EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);\n\nSELECT name FROM customer\nWHERE city IN ('Ankara', 'Izmir');\n\nSELECT name FROM customer m\nWHERE NOT EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);","note":"MySQL/MariaDB admiten EXISTS/IN/NOT EXISTS. Antes de MySQL 5.6 las subconsultas correlacionadas podían ser lentas; 5.6+ aceleró las subconsultas IN con las optimizaciones semi-join y materialization. La trampa de NOT IN + NULL también se aplica aquí.","danger":"safe"},"mssql":{"command":"SELECT name FROM customer m\nWHERE EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);\n\nSELECT name FROM customer\nWHERE city IN (SELECT city FROM target_cities);\n\nSELECT name FROM customer m\nWHERE NOT EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);","note":"SQL Server admite EXISTS/IN/NOT EXISTS, y el optimizador los procesa mayoritariamente como semi/anti-joins. Si una subconsulta NOT IN contiene NULL, el resultado puede quedar vacío de forma inesperada; NOT EXISTS es la opción segura. INTERSECT/EXCEPT también pueden usarse para una lógica de pertenencia similar.","danger":"safe"},"mongodb":{"command":"// IN equivalent: the $in operator\ndb.customer.find({ city: { $in: [\"Ankara\", \"Izmir\"] } })\n\n// EXISTS equivalent: match check with $lookup + $match\ndb.customer.aggregate([\n  { $lookup: { from: \"orders\", localField: \"id\",\n      foreignField: \"customer_id\", as: \"s\" } },\n  { $match: { \"s.0\": { $exists: true } } }  // at least one order\n])","note":"En MongoDB, la pertenencia a una lista de valores se hace con los operadores $in / $nin. El equivalente de un EXISTS con subconsulta es obtener con $lookup el array de documentos coincidentes y filtrar según si el array está vacío. Comprobar la existencia de un campo es un operador aparte, { field: { $exists: true } } (la presencia de un campo, no una relación entre filas).","danger":"safe"},"redis":{"command":"# Membership check with Set/Hash:\nSISMEMBER target_cities \"Ankara\"   # is it in the set? (IN-like)\nEXISTS customer:1                 # does the key exist?","note":"Redis no tiene subconsultas. Para la pertenencia de valores, use SISMEMBER sobre un Set (similar a IN). El comando EXISTS no es una subconsulta; devuelve si la clave indicada existe. Las comprobaciones de existencia relacionales se hacen en la aplicación leyendo varias claves.","danger":"safe"},"elasticsearch":{"command":"# IN equivalent: terms query\nGET customer/_search\n{ \"query\": { \"terms\": { \"city\": [\"Ankara\", \"Izmir\"] } } }\n\n# Field existence: exists query\nGET customer/_search\n{ \"query\": { \"exists\": { \"field\": \"phone\" } } }","note":"En Elasticsearch, el equivalente de IN es la consulta 'terms' (coincide con uno de varios valores), y NOT IN es terms dentro de bool must_not. La consulta 'exists' no corresponde a EXISTS de SQL; comprueba si un campo tiene valor en el documento. Un EXISTS correlacionado entre tablas suele requerir desnormalización.","danger":"safe"}}},{"id":"limit-paging","title":"Limitación de filas / paginación (LIMIT/OFFSET, FETCH, ROWNUM, TOP)","aliases":["limit","offset","fetch","rownum","top","paging","page through results","pagination","row limit"],"engines":{"oracle":{"command":"-- 12c+ standard: OFFSET/FETCH\nSELECT name, salary FROM employee\nORDER BY salary DESC\nOFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;\n\n-- Older versions: with ROWNUM\nSELECT * FROM (\n  SELECT name, salary FROM employee ORDER BY salary DESC\n) WHERE ROWNUM <= 10;","note":"Se usa para limitar/paginar un conjunto de resultados. Desde Oracle 12c se admite el estándar OFFSET n ROWS FETCH NEXT m ROWS ONLY. Antes se usaba la pseudocolumna ROWNUM; como ROWNUM se asigna antes de ordenar, es obligatorio envolver una subconsulta con ORDER BY.","since":"Oracle 12c","xnote":"Antes de Oracle 12c, la limitación de filas se hacía con ROWNUM.","danger":"safe"},"postgres":{"command":"-- LIMIT/OFFSET\nSELECT name, salary FROM employee\nORDER BY salary DESC\nLIMIT 10 OFFSET 20;\n\n-- Standard equivalent: FETCH\nSELECT name, salary FROM employee\nORDER BY salary DESC\nOFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;","note":"PostgreSQL admite tanto LIMIT n OFFSET m como la forma estándar OFFSET ... FETCH. Los valores grandes de OFFSET son lentos; para paginación profunda, la paginación basada en claves (keyset / 'WHERE id > last_id') es más eficiente. LIMIT sin ORDER BY no garantiza el orden del resultado.","danger":"safe"},"mysql":{"command":"-- LIMIT [offset,] row_count\nSELECT name, salary FROM employee\nORDER BY salary DESC\nLIMIT 10 OFFSET 20;\n\n-- Short form: LIMIT offset, row_count\nSELECT name, salary FROM employee ORDER BY salary DESC LIMIT 20, 10;","note":"MySQL/MariaDB usan la palabra clave LIMIT en dos formas: LIMIT row_count OFFSET offset o LIMIT offset, row_count. MySQL 8.0.19+ también acepta la sintaxis FETCH estándar. Se recomienda la paginación keyset para el rendimiento con OFFSET profundos.","danger":"safe"},"mssql":{"command":"-- First N with TOP\nSELECT TOP 10 name, salary FROM employee ORDER BY salary DESC;\n\n-- OFFSET/FETCH for paging (2012+), ORDER BY mandatory\nSELECT name, salary FROM employee\nORDER BY salary DESC\nOFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;","note":"SQL Server usa TOP n (con las variantes TOP (n) PERCENT / WITH TIES) para las primeras N filas y, desde 2012, OFFSET ... FETCH para la paginación. ORDER BY es obligatorio para que OFFSET/FETCH funcione. SQL Server no tiene la palabra clave LIMIT.","since":"SQL Server 2012","xnote":"La paginación con OFFSET/FETCH llegó con 2012; antes se hacía con TOP o ROW_NUMBER.","danger":"safe"},"mongodb":{"command":"// Paging with skip/limit\ndb.employee.find()\n  .sort({ salary: -1 })\n  .skip(20)\n  .limit(10)","note":"En MongoDB, limit() establece el número de documentos devueltos y skip() el número omitido; combinados con sort() proporcionan paginación. Los valores grandes de skip son costosos; para paginación profunda se prefiere una consulta por rango (keyset) basada en el último valor visto.","danger":"safe"},"redis":{"command":"# Range read on a sorted set (ZSET):\nZREVRANGE salary_sorted 20 29    # members ranked 21-30\n# Range on a list:\nLRANGE queue 0 9              # first 10 elements","note":"Redis no tiene LIMIT de SQL, pero tiene comandos de lectura por rango: ZRANGE/ZREVRANGE start stop (por índice) o ZRANGEBYSCORE ... LIMIT offset count para Sorted Sets; LRANGE para Lists. Estos comandos proporcionan un acceso similar a la paginación.","danger":"safe"},"elasticsearch":{"command":"GET employee/_search\n{\n  \"from\": 20,\n  \"size\": 10,\n  \"sort\": [ { \"salary\": \"desc\" } ]\n}","note":"En Elasticsearch, 'size' establece el número de hits devueltos y 'from' el número omitido (el equivalente de LIMIT/OFFSET). from+size puede ser como máximo index.max_result_window (por defecto 10,000); para paginación profunda use 'search_after' (keyset) o un PIT (point-in-time).","danger":"safe"}}},{"id":"merge-upsert","title":"MERGE / UPSERT (actualizar si existe; si no, insertar)","aliases":["merge","upsert","on conflict","on duplicate key","insert or update","update if exists"],"engines":{"oracle":{"command":"MERGE INTO customer m\nUSING (SELECT 1 AS id, 'Ali' AS name, 500 AS balance FROM dual) k\nON (m.id = k.id)\nWHEN MATCHED THEN\n  UPDATE SET m.name = k.name, m.balance = k.balance\nWHEN NOT MATCHED THEN\n  INSERT (id, name, balance) VALUES (k.id, k.name, k.balance);","note":"MERGE (upsert) compara un origen con un destino, actualiza las filas coincidentes e inserta las no coincidentes; ideal para la sincronización masiva de datos. Oracle tiene un soporte sólido de MERGE: también se pueden escribir WHEN MATCHED ... DELETE y actualizaciones condicionales. Para un origen de una sola fila se usa la tabla dual.","since":"Oracle 9i","danger":"caution"},"postgres":{"command":"-- The PostgreSQL way: INSERT ... ON CONFLICT\nINSERT INTO customer (id, name, balance)\nVALUES (1, 'Ali', 500)\nON CONFLICT (id) DO UPDATE\n  SET name = EXCLUDED.name, balance = EXCLUDED.balance;","note":"El upsert clásico de PostgreSQL es INSERT ... ON CONFLICT (columna única) DO UPDATE / DO NOTHING; a los nuevos valores de la fila en conflicto se accede mediante el alias EXCLUDED. Desde PostgreSQL 15 también se admite la sentencia MERGE estándar. ON CONFLICT es atómico sobre una sola tabla y muy usado.","since":"PostgreSQL 15","xnote":"La sentencia MERGE estándar llegó con PostgreSQL 15; antes los upserts se hacían con INSERT ... ON CONFLICT (9.5).","danger":"caution"},"mysql":{"command":"INSERT INTO customer (id, name, balance)\nVALUES (1, 'Ali', 500)\nON DUPLICATE KEY UPDATE\n  name = VALUES(name), balance = VALUES(balance);","note":"MySQL/MariaDB usan INSERT ... ON DUPLICATE KEY UPDATE para los upserts; si hay conflicto con una clave única o primaria, la fila se actualiza. A los nuevos valores se accede con VALUES(my_column) (o con el alias de fila 'new.my_column' en 8.0.19+). INSERT IGNORE omite silenciosamente la fila en caso de conflicto. MySQL no tiene MERGE estándar.","danger":"caution"},"mssql":{"command":"MERGE INTO customer AS m\nUSING (VALUES (1, 'Ali', 500)) AS k(id, name, balance)\nON m.id = k.id\nWHEN MATCHED THEN\n  UPDATE SET m.name = k.name, m.balance = k.balance\nWHEN NOT MATCHED THEN\n  INSERT (id, name, balance) VALUES (k.id, k.name, k.balance);","note":"SQL Server admite la sentencia MERGE desde 2008; ofrece ramas adicionales como WHEN NOT MATCHED BY SOURCE para eliminar las filas sobrantes en el destino. Una sentencia MERGE debe terminar con punto y coma. Nota: debido a algunos bugs en casos límite, para upserts sencillos puede preferirse escribir sentencias INSERT/UPDATE separadas.","since":"SQL Server 2008","danger":"caution"},"mongodb":{"command":"// Find-and-update or insert with upsert:true\ndb.customer.updateOne(\n  { id: 1 },\n  { $set: { name: \"Ali\", balance: 500 } },\n  { upsert: true }\n)","note":"En MongoDB el upsert es una opción nativa: si se añade { upsert: true } a updateOne/updateMany/findOneAndUpdate, se actualiza el documento coincidente o, si no existe, se crea uno nuevo. Para la sincronización masiva, la etapa de agregación $merge (escribir/actualizar en una colección de destino) se acerca más a MERGE de SQL.","danger":"caution"},"redis":{"command":"# SET is a natural upsert: it overwrites the key if present, otherwise creates it.\nSET customer:1 '{\"name\":\"Ali\",\"balance\":500}'\n# Update/add a hash field:\nHSET customer:1 name Ali balance 500","note":"En Redis los comandos de escritura son upserts por naturaleza: SET/HSET actualizan la clave o el campo si existe y, si no, lo crean. Use la opción SET ... NX para escribir solo si no existe, y XX solo si existe. La distinción 'actualizar si existe; si no, insertar' ya es el comportamiento por defecto de la mayoría de los comandos.","danger":"safe"},"elasticsearch":{"command":"# An index call with a document id acts like an upsert (replaces if present):\nPUT customer/_doc/1\n{ \"name\": \"Ali\", \"balance\": 500 }\n# Partial update + insert if absent:\nPOST customer/_update/1\n{ \"doc\": { \"balance\": 500 }, \"doc_as_upsert\": true }","note":"En Elasticsearch, una llamada index (PUT) con el mismo _id reemplaza por completo el documento si existe y, si no, lo crea (similar a un upsert). Para actualizaciones parciales con inserción si no existe, use 'doc_as_upsert': true o un bloque 'upsert' en la API _update. También son posibles los upserts con script.","danger":"caution"}}},{"id":"returning-output","title":"Devolver la fila modificada (RETURNING / OUTPUT)","aliases":["returning","output","inserted","deleted","returned value","modified row"],"engines":{"oracle":{"command":"-- Single row: RETURNING ... INTO (PL/SQL bind variable)\nUPDATE employee SET salary = salary * 1.1\nWHERE id = 5\nRETURNING salary INTO :new_salary;\n\n-- Get back the key generated by an INSERT\nINSERT INTO employee (name) VALUES ('Ayse')\nRETURNING id INTO :new_id;","note":"RETURNING devuelve los valores de las filas modificadas por INSERT/UPDATE/DELETE (p. ej., un ID autogenerado) sin un SELECT aparte. En Oracle, una sola fila se devuelve con RETURNING ... INTO en una variable bind, y varias filas con RETURNING ... BULK COLLECT INTO en una colección.","danger":"safe"},"postgres":{"command":"INSERT INTO employee (name, salary)\nVALUES ('Ayse', 15000)\nRETURNING id, name;\n\nUPDATE employee SET salary = salary * 1.1\nWHERE department = 'IT'\nRETURNING id, salary;","note":"La cláusula RETURNING de PostgreSQL es muy flexible: devuelve directamente como conjunto de resultados cualquier columna (incluidas expresiones calculadas) de las filas afectadas por las sentencias INSERT, UPDATE, DELETE y MERGE. Es la forma más limpia de obtener los valores serial/identity generados.","since":"PostgreSQL 8.2","danger":"safe"},"mysql":{"command":"-- MySQL has no RETURNING; the generated key is retrieved separately:\nINSERT INTO employee (name, salary) VALUES ('Ayse', 15000);\nSELECT LAST_INSERT_ID();\n\n-- MariaDB 10.5+ supports INSERT ... RETURNING:\nINSERT INTO employee (name) VALUES ('Ayse') RETURNING id;","note":"MySQL no admite el RETURNING estándar; la clave autoincremental generada por un INSERT se obtiene con LAST_INSERT_ID(), y los valores actualizados se leen con un SELECT aparte. MariaDB ofrece INSERT ... RETURNING desde 10.5 y DELETE ... RETURNING desde 10.0.","danger":"safe"},"mssql":{"command":"INSERT INTO employee (name, salary)\nOUTPUT INSERTED.id, INSERTED.name\nVALUES ('Ayse', 15000);\n\nUPDATE employee SET salary = salary * 1.1\nOUTPUT DELETED.salary AS old_salary, INSERTED.salary AS new_salary\nWHERE id = 5;","note":"El equivalente en SQL Server es la cláusula OUTPUT: accede a las pseudotablas INSERTED (valores nuevos) y DELETED (valores antiguos); ambas pueden usarse en un UPDATE. Con OUTPUT ... INTO los resultados se pueden escribir en una tabla/variable de tabla. Ideal para copiar los cambios en una tabla de auditoría.","since":"SQL Server 2005","xnote":"La cláusula OUTPUT llegó con SQL Server 2005.","danger":"safe"},"mongodb":{"command":"// findOneAndUpdate returns the modified document\ndb.employee.findOneAndUpdate(\n  { id: 5 },\n  { $mul: { salary: 1.1 } },\n  { returnDocument: \"after\" }   // return the updated version\n)","note":"En MongoDB, los comandos findOneAndUpdate / findOneAndDelete / findOneAndReplace devuelven el documento afectado en una única operación atómica. returnDocument: 'before' da la versión anterior al cambio y 'after' la posterior. insertOne devuelve el _id del documento insertado.","danger":"safe"},"redis":{"command":"# Some commands return the old/new value directly:\nSET counter 0 GET       # (6.2+) returns the old value and writes the new one (replaces GETSET)\nINCR visits          # increments and returns the new value","note":"Redis no tiene un RETURNING aparte, pero muchos comandos de escritura devuelven el resultado directamente: INCR/INCRBY devuelven el nuevo valor, SET ... GET el valor antiguo (GETSET está obsoleto desde 6.2) y LPOP/RPOP el elemento extraído. Para lectura-escritura atómica, este comportamiento sustituye a RETURNING.","danger":"safe"},"elasticsearch":{"command":"# The _update response can return the result:\nPOST employee/_update/5\n{\n  \"script\": { \"source\": \"ctx._source.salary *= 1.1\" },\n  \"_source\": true\n}\n# The response returns the updated document in get._source","note":"En Elasticsearch, las operaciones index/update devuelven metadatos del resultado (_id, _version, result). Para obtener el cuerpo del documento actualizado, añada '_source': true a la solicitud _update; el campo get de la respuesta contiene el _source actual. No se necesita una llamada GET aparte.","danger":"safe"}}},{"id":"lateral-apply","title":"LATERAL / CROSS APPLY (subconsulta fila a fila)","aliases":["lateral","cross apply","outer apply","row-by-row subquery","correlated table function"],"engines":{"oracle":{"command":"-- Each customer's 3 most recent orders (LATERAL, 12c+)\nSELECT m.name, s.created_at, s.amount\nFROM customer m,\n     LATERAL (SELECT created_at, amount FROM orders\n              WHERE customer_id = m.id\n              ORDER BY created_at DESC FETCH FIRST 3 ROWS ONLY) s;\n\n-- Alternative: CROSS APPLY (12c+)\nSELECT m.name, s.amount\nFROM customer m\nCROSS APPLY (SELECT amount FROM orders WHERE customer_id = m.id) s;","note":"LATERAL/APPLY permite que una subconsulta en FROM haga referencia a columnas de la tabla situada a su izquierda, de modo que se pueden escribir joins correlacionados como 'las N primeras filas para cada fila'. Oracle 12c admite tanto la sintaxis ANSI LATERAL como CROSS/OUTER APPLY.","since":"Oracle 12c","xnote":"LATERAL y CROSS/OUTER APPLY llegaron con Oracle 12c.","danger":"safe"},"postgres":{"command":"SELECT m.name, s.created_at, s.amount\nFROM customer m\nCROSS JOIN LATERAL (\n  SELECT created_at, amount FROM orders\n  WHERE customer_id = m.id\n  ORDER BY created_at DESC LIMIT 3\n) s;","note":"En PostgreSQL, las subconsultas que dependen de la fila de la izquierda se escriben con la palabra clave LATERAL (JOIN LATERAL / CROSS JOIN LATERAL / LEFT JOIN LATERAL). Muy útil para 'top N por grupo' y para expandir filas con funciones que devuelven conjuntos. No hay sintaxis CROSS APPLY; se usa LATERAL.","since":"PostgreSQL 9.3","xnote":"LATERAL llegó con PostgreSQL 9.3.","danger":"safe"},"mysql":{"command":"-- MySQL 8.0.14+ supports LATERAL\nSELECT m.name, s.amount\nFROM customer m\nJOIN LATERAL (\n  SELECT amount FROM orders\n  WHERE customer_id = m.id\n  ORDER BY created_at DESC LIMIT 3\n) s ON true;","note":"Desde MySQL 8.0.14 se admiten las LATERAL derived tables; la subconsulta puede hacer referencia a columnas de la tabla externa. Antes, y para CROSS APPLY, no había equivalente; 'top N por grupo' se resolvía con funciones de ventana o subconsultas correlacionadas. MariaDB todavía no tiene LATERAL.","since":"MySQL 8.0.14","xnote":"Las LATERAL derived tables llegaron con MySQL 8.0.14; MariaDB no las tiene.","danger":"safe"},"mssql":{"command":"SELECT m.name, s.created_at, s.amount\nFROM customer m\nCROSS APPLY (\n  SELECT TOP 3 created_at, amount FROM orders\n  WHERE customer_id = m.id\n  ORDER BY created_at DESC\n) s;\n-- Use OUTER APPLY to return non-matching rows too","note":"El equivalente de LATERAL en SQL Server es el operador APPLY: CROSS APPLY (como INNER JOIN LATERAL, descarta las no coincidencias) y OUTER APPLY (como LEFT JOIN LATERAL, NULL para las no coincidencias). Habitual para llamar a funciones con valores de tabla para cada fila y en consultas de top N por grupo.","since":"SQL Server 2005","xnote":"El operador CROSS/OUTER APPLY llegó con SQL Server 2005.","danger":"safe"},"mongodb":{"command":"// Closest equivalent: $lookup with a sub-pipeline\ndb.customer.aggregate([\n  { $lookup: {\n      from: \"orders\",\n      let: { mid: \"$id\" },\n      pipeline: [\n        { $match: { $expr: { $eq: [\"$customer_id\", \"$$mid\"] } } },\n        { $sort: { created_at: -1 } }, { $limit: 3 }\n      ],\n      as: \"last3\"\n  } }\n])","note":"En MongoDB, el equivalente de LATERAL/APPLY es la forma de $lookup con un sub-pipeline y variables let: los campos del documento externo se pasan al sub-pipeline mediante let como $$variable, donde se hacen la coincidencia/ordenación/limitación. Esto resuelve el patrón 'top N relacionados para cada documento'.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no subqueries / LATERAL.","note":"Redis no ofrece subconsultas correlacionadas fila a fila. El patrón 'top N relacionados para cada elemento' se resuelve con un bucle en la aplicación (p. ej., un ZRANGE aparte por clave) o con un script Lua en el servidor.","danger":"safe"},"elasticsearch":{"command":"# Closest equivalent: terms + top_hits sub-aggregation\nGET orders/_search\n{\n  \"size\": 0,\n  \"aggs\": { \"by_customer\": { \"terms\": { \"field\": \"customer_id\" },\n    \"aggs\": { \"last3\": { \"top_hits\": {\n      \"sort\": [ { \"created_at\": \"desc\" } ], \"size\": 3 } } } } }\n}","note":"Elasticsearch no tiene LATERAL/APPLY; el patrón 'top N registros por grupo' se logra con una subagregación top_hits anidada dentro de una terms aggregation. En lugar de una subconsulta correlacionada, se trata de agrupación más muestreo dentro del grupo sobre un único índice.","danger":"safe"}}},{"id":"pivot","title":"PIVOT / UNPIVOT (filas↔columnas)","aliases":["pivot","unpivot","crosstab","cross tab","rows to columns","transpose"],"engines":{"oracle":{"command":"-- Turn sales by month into columns\nSELECT * FROM (\n  SELECT month, amount FROM sale\n)\nPIVOT (SUM(amount) FOR month IN ('Jan' AS jan, 'Feb' AS feb, 'Mar' AS mar));\n\n-- UNPIVOT: turn columns back into rows\nSELECT product, month, amount\nFROM sale_wide\nUNPIVOT (amount FOR month IN (jan AS 'Jan', feb AS 'Feb'));","note":"PIVOT convierte los valores distintos de las filas en columnas (una tabla cruzada) y UNPIVOT vuelve a convertir las columnas en filas; se usan en tablas resumen/de informes. Oracle ofrece los operadores PIVOT/UNPIVOT integrados desde 11g; la lista de columnas debe ser estática (las columnas dinámicas requieren PIVOT XML o código).","since":"Oracle 11g","xnote":"Los operadores PIVOT/UNPIVOT integrados llegaron con Oracle 11g.","danger":"safe"},"postgres":{"command":"-- The tablefunc extension for crosstab\nCREATE EXTENSION IF NOT EXISTS tablefunc;\nSELECT * FROM crosstab(\n  'SELECT product, month, amount FROM sale ORDER BY 1,2'\n) AS ct(product text, jan numeric, feb numeric, mar numeric);\n\n-- Without an extension: conditional aggregation\nSELECT product,\n  SUM(amount) FILTER (WHERE month = 'Jan') AS jan,\n  SUM(amount) FILTER (WHERE month = 'Feb') AS feb\nFROM sale GROUP BY product;","note":"PostgreSQL no tiene un operador PIVOT integrado. Hay dos vías: la función crosstab() de la extensión tablefunc, o la agregación condicional portable (SUM(...) FILTER (WHERE ...) o con CASE). La cláusula FILTER es una técnica de pivot elegante y específica de PostgreSQL.","danger":"safe"},"mysql":{"command":"-- There is no PIVOT; conditional aggregation with manual CASE\nSELECT product,\n  SUM(CASE WHEN month = 'Jan' THEN amount ELSE 0 END) AS jan,\n  SUM(CASE WHEN month = 'Feb' THEN amount ELSE 0 END) AS feb,\n  SUM(CASE WHEN month = 'Mar' THEN amount ELSE 0 END) AS mar\nFROM sale\nGROUP BY product;","note":"MySQL/MariaDB no tienen operador PIVOT/UNPIVOT; una tabla cruzada se construye a mano con GROUP BY más un SUM(CASE WHEN ... THEN ... END) por cada columna de destino. Si las columnas son dinámicas, la consulta se genera como cadena con un prepared statement.","danger":"safe"},"mssql":{"command":"SELECT * FROM (\n  SELECT product, month, amount FROM sale\n) src\nPIVOT (\n  SUM(amount) FOR month IN ([Jan], [Feb], [Mar])\n) pvt;\n\n-- Converting back with UNPIVOT is also supported","note":"SQL Server admite de forma nativa los operadores PIVOT y UNPIVOT desde 2005. La lista de columnas de PIVOT se indica como valores estáticos entre corchetes; para columnas dinámicas la consulta se construye con SQL dinámico. Para resúmenes pequeños, la agregación condicional (CASE) es una alternativa.","since":"SQL Server 2005","xnote":"Los operadores PIVOT/UNPIVOT llegaron con SQL Server 2005.","danger":"safe"},"mongodb":{"command":"// PIVOT equivalent: conditional aggregation inside $group\ndb.sale.aggregate([\n  { $group: {\n      _id: \"$product\",\n      jan: { $sum: { $cond: [ { $eq: [\"$month\", \"Jan\"] }, \"$amount\", 0 ] } },\n      feb: { $sum: { $cond: [ { $eq: [\"$month\", \"Feb\"] }, \"$amount\", 0 ] } }\n  } }\n])","note":"MongoDB no tiene operador PIVOT; una tabla cruzada se construye en la etapa $group con $sum + $cond (agregación condicional) por cada columna de destino. Para el sentido inverso (UNPIVOT), los campos se expanden en filas con $objectToArray y $unwind.","danger":"safe"},"redis":{"command":"# Not applicable: Redis has no pivot/cross tab.","note":"Redis no tiene el concepto de rotar filas y columnas. Los resúmenes/tablas cruzadas se calculan en la aplicación; los resúmenes solicitados con frecuencia se pueden precalcular y almacenar en campos de un Hash (un campo por columna). FT.AGGREGATE de RediSearch puede hacer agregaciones agrupadas, pero no es un pivot real.","danger":"safe"},"elasticsearch":{"command":"# Closest equivalent: nested terms aggregations\nGET sale/_search\n{\n  \"size\": 0,\n  \"aggs\": { \"product\": { \"terms\": { \"field\": \"product\" },\n    \"aggs\": { \"month\": { \"terms\": { \"field\": \"month\" },\n      \"aggs\": { \"total\": { \"sum\": { \"field\": \"amount\" } } } } } }\n}","note":"Elasticsearch no tiene operador PIVOT; un resumen cruzado se construye con terms aggregations anidadas (clave externa / clave interna); el resultado vuelve como buckets anidados en lugar de columnas y se convierte en tabla en el cliente. Un transform job puede generar un índice de destino agrupado, similar a un pivot.","danger":"safe"}}},{"id":"coalesce-null","title":"Tratamiento de NULL (COALESCE/NVL/NULLIF/IS NULL)","aliases":["coalesce","nvl","nullif","is null","ifnull","isnull","null handling","empty value"],"engines":{"oracle":{"command":"-- COALESCE: returns the first non-NULL value (standard)\nSELECT name, COALESCE(phone, mobile, 'none') AS contact FROM customer;\n\n-- Oracle-specific NVL / NVL2\nSELECT NVL(balance, 0) FROM customer;\nSELECT NVL2(discount, 'yes', 'no') FROM orders;\n\n-- NULLIF and IS NULL\nSELECT NULLIF(a, b) FROM t;   -- NULL if a=b\nSELECT name FROM customer WHERE phone IS NULL;","note":"Para tratar NULL (valor desconocido): COALESCE devuelve el primer valor no NULL de la lista (estándar, varios argumentos). NVL(a,b) de Oracle es el equivalente de dos argumentos; NVL2 devuelve valores distintos según el valor sea NULL o no. NULLIF(a,b) devuelve NULL si son iguales. Compruebe siempre NULL con IS NULL / IS NOT NULL (= NULL no funciona).","danger":"safe"},"postgres":{"command":"SELECT name, COALESCE(phone, mobile, 'none') AS contact FROM customer;\nSELECT NULLIF(divisor, 0) FROM t;   -- prevent division by zero\nSELECT name FROM customer WHERE phone IS NULL;\n-- The equivalent of Oracle NVL is COALESCE","note":"PostgreSQL ofrece COALESCE, NULLIF e IS NULL/IS NOT NULL estándar; use COALESCE en lugar de NVL de Oracle. Use IS, no =, para las comparaciones con NULL; si dos NULL deben tratarse como iguales, el operador 'IS NOT DISTINCT FROM' cumple esa función.","danger":"safe"},"mysql":{"command":"SELECT name, COALESCE(phone, mobile, 'none') AS contact FROM customer;\n\n-- MySQL shortcut: IFNULL (two arguments)\nSELECT IFNULL(balance, 0) FROM customer;\nSELECT NULLIF(a, b) FROM t;\nSELECT name FROM customer WHERE phone IS NULL;","note":"MySQL/MariaDB admiten COALESCE y NULLIF estándar; IFNULL(a,b) es un atajo de dos argumentos (similar a NVL de Oracle). El operador <=> proporciona igualdad segura frente a NULL (trata dos NULL como iguales). NULL se comprueba con IS NULL / IS NOT NULL.","danger":"safe"},"mssql":{"command":"SELECT name, COALESCE(phone, mobile, 'none') AS contact FROM customer;\n\n-- SQL Server shortcut: ISNULL (two arguments)\nSELECT ISNULL(balance, 0) FROM customer;\nSELECT NULLIF(a, b) FROM t;\nSELECT name FROM customer WHERE phone IS NULL;","note":"SQL Server ofrece COALESCE estándar (varios argumentos, con promoción de tipos de datos) e ISNULL(a,b) de dos argumentos; ambos difieren en aspectos sutiles (COALESCE es estándar y más flexible con la precedencia de tipos). También se admiten NULLIF e IS NULL. ISNULL es el equivalente de NVL de Oracle (los nombres son parecidos).","danger":"safe"},"mongodb":{"command":"// COALESCE equivalent: $ifNull\ndb.customer.aggregate([\n  { $project: { name: 1,\n      contact: { $ifNull: [\"$phone\", \"$mobile\", \"none\"] } } }\n])\n// NULL / missing field check:\ndb.customer.find({ phone: { $in: [null] } })","note":"En la agregación de MongoDB, el equivalente de COALESCE es el operador $ifNull (varios argumentos, devuelve el primero que no sea null/ausente). En una consulta, { field: null } coincide tanto con los documentos cuyo valor es null como con los que carecen por completo del campo; haga explícita la distinción con { field: { $type: 'null' } } o $exists.","danger":"safe"},"redis":{"command":"# Instead of NULL, Redis has the concept of 'no such key/field':\nEXISTS customer:1              # does the key exist?\nHGET customer:1 phone        # returns nil if the field is absent\nGET missing_key                  # (nil)","note":"Redis no tiene NULL en el sentido de SQL; el equivalente es una clave inexistente o un campo de Hash inexistente, y en ese caso los comandos devuelven nil. La lógica de valor por defecto (como COALESCE) se aplica en la aplicación comprobando nil. La existencia de un campo se comprueba con HEXISTS.","danger":"safe"},"elasticsearch":{"command":"# Default assignment when a field is missing (missing bucket / null_value)\n# Find documents without the field in a query:\nGET customer/_search\n{ \"query\": { \"bool\": { \"must_not\": { \"exists\": { \"field\": \"phone\" } } } } }\n# Map empty values to a specific value with null_value in the mapping","note":"En Elasticsearch, los campos null o ausentes se comprueban con la consulta 'exists' (must_not exists = documentos sin el campo). Para un valor por defecto tipo COALESCE, defina 'null_value' en el mapping, o asigne un valor a las entradas ausentes con el parámetro 'missing' en las agregaciones. También se pueden generar valores por defecto con runtime fields/scripts.","danger":"safe"}}}]},"rosetta/overlay-elasticsearch.json":{"engineId":"elasticsearch","byConcept":{"active-sessions":{"command":"GET _tasks?detailed=true&actions=*search*\n# To see node pressure: GET _nodes/hot_threads","note":"La Task API devuelve las tareas en ejecución (search, reindex, bulk). Para la carga de CPU momentánea, hot_threads muestra los hilos más activos.","danger":"safe"},"blocking-sessions":{"command":"# Not applicable: Elasticsearch has no concept of row/document locks or blocking.\n# Closest diagnosis: find long-running tasks\nGET _tasks?detailed=true","note":"En lugar de bloqueos, la lentitud suele deberse a tareas de larga duración (búsquedas grandes, reindex). Evalúe la duración con el campo running_time_in_nanos.","danger":"safe"},"kill-session":{"command":"POST _tasks/<task_id>/_cancel\n# task_id format: nodeId:taskNumber (e.g. oTUltX4IQMOUUVeiohTt8A:12345)","note":"Tome el valor task_id de la salida de GET _tasks. Solo se pueden detener las tareas cancelables (cancellable:true).","danger":"danger"},"connection-count":{"command":"GET _nodes/stats/http","note":"current_open da las conexiones HTTP abiertas actualmente y total_opened el total de conexiones desde el arranque del nodo. El límite superior de conexiones HTTP lo determinan los límites del sistema operativo/sockets, no http.max_content_length.","danger":"safe"},"database-size":{"command":"GET _cluster/stats\n# For a simple summary: GET _cat/indices?v&h=index,store.size","note":"_cluster/stats da el uso total de disco de todo el clúster mediante indices.store.size_in_bytes. _cat/indices lista el tamaño por índice en formato legible.","danger":"safe"},"largest-tables":{"command":"GET _cat/indices?v&s=store.size:desc&h=index,docs.count,store.size","note":"En Elasticsearch, el equivalente de una \"tabla\" es un índice. s=store.size:desc coloca arriba los índices más grandes.","danger":"safe"},"free-space":{"command":"GET _cat/allocation?v","note":"Las columnas disk.used, disk.avail y disk.percent dan el uso de disco por nodo. Cuando se superan los umbrales de watermark (85%/90%/95%), la asignación de shards se detiene.","danger":"safe"},"row-count":{"command":"GET <index>/_count\n# Shortcut: GET _cat/count/<index>?v","note":"_count devuelve rápidamente el número de documentos. Para un recuento filtrado, pase una consulta en el cuerpo: GET <index>/_count {\"query\":{\"term\":{\"status\":\"active\"}}}","danger":"safe"},"top-slow-queries":{"command":"PUT <index>/_settings\n{\n  \"index.search.slowlog.threshold.query.warn\": \"1s\",\n  \"index.search.slowlog.threshold.query.info\": \"500ms\",\n  \"index.search.slowlog.threshold.fetch.warn\": \"1s\"\n}\n# Queries exceeding the threshold are written to the <cluster>_index_search_slowlog.json log file","note":"Elasticsearch no tiene una tabla central de consultas lentas; el search slowlog escribe las consultas que superan el umbral en los archivos de log del nodo. Lea los logs allí.","danger":"caution"},"explain-plan":{"command":"GET <index>/_search\n{\n  \"profile\": true,\n  \"query\": { \"match\": { \"title\": \"test\" } }\n}\n# To check query validity/parsing:\n# GET <index>/_validate/query?explain=true","note":"profile:true desglosa el tiempo de cada componente de la consulta y de cada paso de agregación. _validate/query?explain muestra el equivalente Lucene de la consulta y por qué fue rechazada.","danger":"safe"},"unused-indexes":{"command":"# Not applicable: secondary indexes in ES differ from relational databases.\n# Closest: assess rarely used/unaccessed indices by size and search count\nGET _cat/indices?v&h=index,docs.count,store.size\nGET <index>/_stats/search","note":"En lugar de índices de campos no utilizados, identifique mediante _stats/search los índices con un número bajo de búsquedas (search.query_total) o que ya no se consultan, y ciérrelos/elimínelos.","danger":"safe"},"cache-hit-ratio":{"command":"GET _nodes/stats/indices/query_cache\n# Ratio = hit_count / (hit_count + miss_count)","note":"query_cache cachea los resultados en filter context. Examine request_cache por separado con GET _nodes/stats/indices/request_cache.","danger":"safe"},"logical-dump":{"command":"# First define a repository (once): PUT _snapshot/<repo> {\"type\":\"fs\",\"settings\":{\"location\":\"/mnt/backups\"}}\nPUT _snapshot/<repo>/<snapshot_1>?wait_for_completion=true\n{\n  \"indices\": \"*\",\n  \"include_global_state\": true\n}","note":"En Elasticsearch, el equivalente de un dump lógico es la API de snapshots. wait_for_completion=true espera a que termine la operación; para snapshots grandes déjelo en false y consulte el estado por separado.","danger":"safe"},"restore-dump":{"command":"POST _snapshot/<repo>/<snapshot_1>/_restore\n{\n  \"indices\": \"*\",\n  \"rename_pattern\": \"(.+)\",\n  \"rename_replacement\": \"restored_$1\"\n}","note":"Para restaurar un índice existente, ciérrelo primero (POST <index>/_close) o restáurelo con otro nombre mediante rename_pattern. No se puede restaurar sobre un índice abierto.","danger":"danger"},"last-backup-status":{"command":"GET _snapshot/<repo>/_all\n# For in-progress/latest snapshot details:\n# GET _snapshot/<repo>/<snapshot_1>/_status","note":"El campo state toma los valores SUCCESS / IN_PROGRESS / PARTIAL / FAILED. _status da el progreso detallado por shard.","danger":"safe"},"list-users":{"command":"GET _security/user","note":"X-Pack Security debe estar habilitado. Para un solo usuario: GET _security/user/<user>. Los usuarios integrados también aparecen en la lista.","danger":"safe"},"create-user-grant":{"command":"# 1) Create a role\nPOST _security/role/<role>\n{\n  \"cluster\": [\"monitor\"],\n  \"indices\": [ { \"names\": [\"log-*\"], \"privileges\": [\"read\"] } ]\n}\n# 2) Create the user and assign the role\nPOST _security/user/<user>\n{\n  \"password\": \"StrongPassword123\",\n  \"roles\": [\"<role>\"]\n}","note":"La autorización se basa en roles: primero defina el rol que contiene los privilegios de índice/clúster y luego asígnelo al usuario. La contraseña debe tener al menos 6 caracteres.","danger":"caution"},"show-privileges":{"command":"GET _security/user/<user>\nGET _security/role/<role>\n# Check whether a specific privilege is held:\n# POST _security/user/<user>/_has_privileges { \"index\": [ { \"names\": [\"log-*\"], \"privileges\": [\"read\"] } ] }","note":"Vea los roles de un usuario con _security/user y los privilegios que contiene un rol con _security/role. _has_privileges verifica si una acción concreta está permitida.","danger":"safe"},"change-password":{"command":"POST _security/user/<user>/_password\n{\n  \"password\": \"NewStrongPassword123\"\n}","note":"Para cambiar su propia contraseña, basta con POST _security/user/_password (sin nombre de usuario). Para el usuario integrado elastic también se puede usar la herramienta bin/elasticsearch-reset-password.","danger":"caution"},"update-statistics":{"command":"# Not applicable: Elasticsearch has no cost-based optimizer or table statistics gathering.\n# Closest: make newly written documents searchable\nPOST <index>/_refresh","note":"Lucene se basa en segmentos; no se necesita un paso aparte de actualización de estadísticas. _refresh hace que los documentos aún no consultables lo sean de inmediato (por defecto ocurre automáticamente cada 1s).","danger":"safe"},"rebuild-index":{"command":"POST _reindex\n{\n  \"source\": { \"index\": \"old_index\" },\n  \"dest\":   { \"index\": \"new_index\" }\n}\n# For large data: run in the background with POST _reindex?wait_for_completion=false","note":"Reindex se usa para cambios de mapping o para reconstruir un índice dañado. Para un cambio sin interrupciones, apunte un alias al nuevo índice.","danger":"caution"},"reclaim-space":{"command":"POST <index>/_forcemerge?only_expunge_deletes=true\n# Full merge on read-only/old indices: POST <index>/_forcemerge?max_num_segments=1","note":"Los documentos eliminados/actualizados permanecen en los segmentos como tombstones; forcemerge recupera ese espacio. Es costoso en índices con escritura activa; ejecútelo fuera de horas pico.","danger":"caution"},"version-uptime":{"command":"GET /\n# Uptime (JVM uptime):\n# GET _nodes/stats/jvm\n# Short summary: GET _cat/nodes?v&h=name,uptime,version","note":"El endpoint raíz GET / devuelve la versión, la versión de Lucene y el nombre del clúster. jvm.uptime_in_millis es el tiempo de actividad del nodo.","danger":"safe"},"show-config-param":{"command":"GET _cluster/settings?include_defaults=true&flat_settings=true","note":"include_defaults=true muestra también los valores por defecto. La configuración persistent y transient se devuelve en secciones separadas; para la configuración a nivel de nodo use GET _nodes/settings.","danger":"safe"},"list-tables":{"command":"GET _cat/indices?v","note":"En Elasticsearch, el equivalente de una tabla relacional es un índice. Da un resumen con las columnas health, status, docs.count y store.size. La salida por defecto lista los índices abiertos y cerrados, pero no los ocultos (p. ej., backing indices .ds-, índices del sistema); añada &expand_wildcards=all para incluirlos, o &expand_wildcards=open para solo los índices abiertos.","danger":"safe"},"describe-table":{"command":"GET <index>/_mapping","note":"El mapping da los tipos de campo y la configuración de análisis; es el equivalente de DESCRIBE TABLE. Para un solo campo: GET <index>/_mapping/field/<field>.","danger":"safe"},"list-indexes":{"command":"GET <index>/_mapping","note":"En ES los campos se indexan por defecto; no hay una tabla de índices secundarios aparte. Los campos con \"index\": false en el mapping no son consultables. Para los índices (tablas) de todo el clúster: GET _cat/indices?v.","danger":"safe"},"create-index":{"command":"PUT <index>\n{\n  \"settings\": { \"number_of_shards\": 1, \"number_of_replicas\": 1 },\n  \"mappings\": {\n    \"properties\": {\n      \"title\": { \"type\": \"text\" },\n      \"created_at\": { \"type\": \"date\" }\n    }\n  }\n}\n# To add fields later: PUT <index>/_mapping {\"properties\":{...}}","note":"Una vez creado un índice, el número de primary shards no se puede cambiar in situ (el número de réplicas es dinámico); si es necesario, genere un índice nuevo con las API _split / _shrink o con _reindex. El tipo de un campo existente no se puede cambiar; se pueden añadir campos nuevos.","danger":"caution"},"show-ddl":{"command":"GET <index>","note":"Una sola llamada devuelve settings, mappings y aliases; juntos, estos tres son el equivalente del \"DDL\" del índice. Puede reutilizar la misma salida como cuerpo de un nuevo PUT <index>.","danger":"safe"},"replication-status":{"command":"GET _cluster/health\n# For ongoing shard copy/recovery: GET _cat/recovery?v&active_only=true\n# For cross-cluster replication (CCR): GET <index>/_ccr/stats","note":"status resume el estado de los shards como green/yellow/red; yellow = réplica sin asignar. active_shards_percent da el porcentaje de completitud.","danger":"safe"},"list-replicas":{"command":"GET _cat/shards?v&h=index,shard,prirep,state,node\n# For node roles: GET _cat/nodes?v","note":"La columna prirep toma el valor p (primary) o r (replica); muestra cómo se distribuyen los shards entre los nodos y su estado (STARTED/RELOCATING).","danger":"safe"},"promote-failover":{"command":"# Master election is automatic; no manual promote is needed.\n# To convert a CCR follower into an independent (writable) index:\nPOST <index>/_ccr/pause_follow\nPOST <index>/_close\nPOST <index>/_ccr/unfollow\nPOST <index>/_open","note":"Ante el fallo de un nodo, Elasticsearch vuelve a elegir automáticamente el master y reasigna los primary shards. La toma de control manual solo es necesaria para desvincular un follower index de CCR de su leader y hacerlo escribible.","danger":"danger"},"open-transactions":{"command":"# Not applicable: Elasticsearch has no multi-document transactions (BEGIN/COMMIT).\n# Operations are atomic at the document level; the closest diagnosis is running tasks:\nGET _tasks?detailed=true","note":"Una escritura en un único documento es atómica y versionada (concurrencia optimista: if_seq_no / if_primary_term). No existe el concepto de seguimiento de transacciones abiertas.","danger":"safe"},"connect-cli":{"command":"curl -u user:password -k https://host:9200\n# Kibana Dev Tools console (recommended): Kibana > Management > Dev Tools\n# With an API key: curl -H \"Authorization: ApiKey <base64key>\" https://host:9200","note":"Está basado en REST; en lugar de un cliente CLI aparte, se usa curl o Kibana Dev Tools. -k omite la verificación de certificados TLS autofirmados (use una CA en producción).","danger":"safe"},"list-databases":{"command":"GET _cat/indices?v\n# For aliases (logical views): GET _aliases","note":"Elasticsearch no tiene una capa de \"base de datos\" aparte; los conceptos más cercanos son los índices y los alias que los agrupan. Para data streams: GET _data_stream.","danger":"safe"},"switch-database":{"command":"# Not applicable: the target index is specified directly in the request path; there is no session context to change.\nGET <index>/_search\n# In Kibana, a data view (index pattern) is selected as the default view","note":"No hay un comando del tipo USE <db>; el índice/alias de destino se indica en la ruta de cada solicitud. Para buscar en varios índices, sepárelos con comas: GET log-2024,log-2025/_search.","danger":"safe"}}},"errors/oracle.json":{"engineId":"oracle","engine":"Oracle","order":1,"errors":[{"code":"ORA-00001","title":"Unique constraint violated (violación de restricción única)","severity":"caution","meaning":"Una fila que se inserta o actualiza viola una restricción UNIQUE o PRIMARY KEY. Es decir, el mismo valor ya existe.","cause":"Volver a insertar una fila con el mismo valor de clave, introducir datos duplicados en una columna que debe ser única o procesar el mismo registro dos veces durante la replicación o reejecuciones.","fix":"Identifique la columna afectada a partir del nombre de la restricción en el mensaje de error: SELECT column_name FROM all_cons_columns WHERE constraint_name = 'CONSTRAINT_NAME'. Use GROUP BY ... HAVING COUNT(*) > 1 para encontrar datos duplicados. En la aplicación, use UPDATE o MERGE en lugar de INSERT para registros existentes. Si el valor de la secuencia se ha quedado atrás, avánzelo con ALTER SEQUENCE.","tags":["unique","uniqueness","constraint","unique constraint","primary key","duplicate","duplicate key","insert"]},{"code":"ORA-00054","title":"Resource busy and acquire with NOWAIT specified or timeout expired (recurso ocupado; NOWAIT especificado o tiempo de espera agotado)","severity":"caution","meaning":"El recurso (fila o tabla) que se quiere bloquear está bloqueado por otra sesión y no se pudo obtener de inmediato porque se especificó NOWAIT o expiró DDL_LOCK_TIMEOUT.","cause":"Normalmente, un DDL (ALTER TABLE, DROP, CREATE INDEX) intentado mientras otra transacción bloquea la tabla; o una fila bloqueada encontrada con SELECT ... FOR UPDATE NOWAIT.","fix":"Encuentre la sesión que mantiene el bloqueo: SELECT * FROM v$locked_object. Combine v$session y v$lock para identificar la sesión bloqueante. Espere a que termine la transacción o, si es necesario, termínela con ALTER SYSTEM KILL SESSION. Para DDL puede definir un tiempo de espera: ALTER SESSION SET DDL_LOCK_TIMEOUT = 60; Ejecute los DDL en ventanas de mantenimiento con poca actividad.","tags":["lock","locked","ddl","nowait","busy","resource busy","timeout"]},{"code":"ORA-00060","title":"Deadlock detected while waiting for resource (deadlock detectado al esperar un recurso)","severity":"danger","meaning":"Dos o más sesiones están en deadlock, cada una esperando un recurso que mantiene la otra. Oracle detecta el ciclo, elige una víctima y revierte la sentencia de esa sesión.","cause":"Bloqueos de fila adquiridos en distinto orden, las mismas filas actualizadas de forma cruzada por transacciones diferentes, bloqueos de tabla en actualizaciones de la tabla padre causados por claves foráneas sin índice, o DML concurrente sobre un índice bitmap.","fix":"Examine el archivo de traza (grafo) del deadlock referenciado en el alert log para encontrar las sentencias SQL y filas en conflicto. En la aplicación, acceda siempre a tablas/filas en el mismo orden. Indexe las columnas de claves foráneas (las FK sin índice son una causa clásica de deadlock). Mantenga las transacciones cortas y revise la frecuencia de commit. Capture la transacción en deadlock en la aplicación y reinténtela.","tags":["deadlock","deadlock detected","lock","locking","tx","transaction"]},{"code":"ORA-01017","title":"Invalid username/password; logon denied (usuario/contraseña no válidos; inicio de sesión denegado)","severity":"caution","meaning":"Se denegó el inicio de sesión porque el nombre de usuario o la contraseña proporcionados al conectar a la base de datos son incorrectos.","cause":"Contraseña incorrecta, nombre de usuario incorrecto, la configuración de contraseña sensible a mayúsculas (SEC_CASE_SENSITIVE_LOGON) distinta de la esperada, una contraseña cambiada o credenciales obsoletas en la cadena de conexión de la aplicación.","fix":"Verifique el nombre de usuario y la contraseña. Si es necesario, restablezca la contraseña como DBA: ALTER USER app_user IDENTIFIED BY new_password. Compruebe que el usuario existe y no está bloqueado: SELECT username, account_status FROM dba_users. Con las versiones de hash de contraseña de 12c+, los clientes 11g pueden tener problemas para conectarse; revise el parámetro SQLNET.ALLOWED_LOGON_VERSION_SERVER.","tags":["auth","credentials","password","password","logon","login","privilege"]},{"code":"ORA-01555","title":"Snapshot too old: rollback segment number too small (snapshot demasiado antiguo)","severity":"danger","meaning":"Una consulta de larga duración no pudo acceder a los datos de undo antiguos necesarios para construir la imagen de lectura consistente a la hora de su inicio, porque esa información de undo fue sobrescrita.","cause":"Un tablespace de undo pequeño, un UNDO_RETENTION corto, consultas de larga duración con DML concurrente muy intenso, o un bucle que hace commit sobre la misma tabla dentro de una consulta (fetch across commit).","fix":"Aumente UNDO_RETENTION y amplíe el tablespace de undo; cuando sea posible, déjelo en gestión automática (AUTOEXTEND activado). Divida las consultas largas en partes más pequeñas o ejecútelas fuera de horas pico. Considere RETENTION GUARANTEE: ALTER TABLESPACE undotbs1 RETENTION GUARANTEE. Corrija la lógica de la aplicación que hace commit dentro de un bucle.","tags":["undo","snapshot","rollback","read consistency","consistency","long query"]},{"code":"ORA-01652","title":"Unable to extend temp segment by N in tablespace (no se puede extender el segmento temporal en el tablespace)","severity":"danger","meaning":"Oracle no encontró suficiente espacio libre en el tablespace para extender un segmento temporal (temp). Suele ocurrir durante ordenaciones, hash joins u operaciones con tablas temporales globales.","cause":"El tablespace TEMP está lleno; operaciones grandes de ORDER BY, GROUP BY, DISTINCT, hash join o creación de índices no caben en la memoria PGA y se vuelcan a disco; los datafiles tienen AUTOEXTEND desactivado o se agota el espacio en disco.","fix":"Supervise el uso de TEMP: SELECT * FROM v$sort_usage / v$tempseg_usage. Añada un archivo o amplíe el tablespace TEMP: ALTER TABLESPACE temp ADD TEMPFILE '...' SIZE 2G AUTOEXTEND ON. Si un tablespace permanente está lleno, añada un datafile. Reduzca las ordenaciones en disco optimizando la consulta (índice adecuado, PGA_AGGREGATE_TARGET mayor).","tags":["temp","tablespace","free space","space","sort","sorting","extend"]},{"code":"ORA-04031","title":"Unable to allocate N bytes of shared memory (no se pueden asignar N bytes de memoria compartida)","severity":"danger","meaning":"Oracle no pudo asignar memoria contigua del tamaño solicitado en un pool de memoria compartida (normalmente el shared pool o el large pool). Un componente de la SGA está bajo presión de memoria.","cause":"Fragmentación del shared pool por muchas sentencias SQL únicas sin variables bind (llenas de literales), SHARED_POOL_SIZE insuficiente, demasiadas conexiones concurrentes, paquetes PL/SQL grandes o un dimensionamiento ASMM/AMM incorrecto.","fix":"Compruebe a qué pool se refiere el mensaje de error (shared pool, large pool, etc.). A corto plazo: ALTER SYSTEM FLUSH SHARED_POOL (precaución: en producción obliga a volver a analizar todo el SQL, es decir, una tormenta temporal de hard parse, y no corrige la fragmentación de forma permanente). Solución permanente: imponga el uso de variables bind (incluido CURSOR_SHARING), aumente SGA_TARGET / SHARED_POOL_SIZE / LARGE_POOL_SIZE. Fije en memoria los paquetes de uso frecuente con DBMS_SHARED_POOL.KEEP. Examine la distribución de memoria con V$SGASTAT.","tags":["memory","shared memory","shared pool","sga","ora-4031","bind"]},{"code":"ORA-12154","title":"TNS: could not resolve the connect identifier specified (no se pudo resolver el identificador de conexión)","severity":"caution","meaning":"No se pudo resolver el nombre de servicio (connect identifier / alias TNS) usado en la conexión. Oracle Net no puede asociar el nombre indicado a la información de servidor/servicio.","cause":"Falta el alias en tnsnames.ora, la variable de entorno TNS_ADMIN apunta al directorio equivocado, el nombre del alias está mal escrito, falta la configuración de nombres EZConnect/LDAP o el orden de NAMES.DIRECTORY_PATH en sqlnet.ora es incorrecto.","fix":"Verifique que el alias que usa esté definido en tnsnames.ora. Pruebe la conexión: tnsping service_name. Asegúrese de que TNS_ADMIN apunte a la carpeta correcta. Como alternativa, pruebe EZConnect: app_user/password@host:1521/service_name. Revise la línea NAMES.DIRECTORY_PATH = (TNSNAMES, EZCONNECT) en sqlnet.ora.","tags":["tns","connect","connection","network","networking","tnsnames","service"]},{"code":"ORA-12514","title":"TNS: listener does not currently know of service requested in connect descriptor (el listener no conoce el servicio solicitado)","severity":"caution","meaning":"El listener está en ejecución pero no reconoce el nombre de servicio solicitado; es decir, el servicio no está registrado en el listener.","cause":"La instancia de base de datos no está completamente abierta, el servicio aún no se ha registrado en el listener (LREG en 12c+, retraso del registro de PMON en 11g y anteriores), se usa un SERVICE_NAME incorrecto o se confunden SID y nombre de servicio.","fix":"Vea los servicios que conoce el listener: lsnrctl status y lsnrctl services. Verifique que la base de datos esté OPEN. Fuerce el registro del servicio: ALTER SYSTEM REGISTER en SQL. Revise el parámetro LOCAL_LISTENER y el uso correcto de SERVICE_NAME. En una pluggable database, asegúrese de que la PDB esté OPEN.","tags":["tns","listener","service","service_name","connection","network","networking"]},{"code":"ORA-00942","title":"Table or view does not exist (la tabla o vista no existe)","severity":"caution","meaning":"La tabla o vista referenciada en la consulta no existe o no es visible para el usuario que accede.","cause":"El objeto realmente no existe, está en otro esquema y no se indicó el prefijo de esquema, el usuario carece de privilegios sobre el objeto (como SELECT), falta un sinónimo o el nombre del objeto está mal escrito.","fix":"Compruebe que el objeto existe y quién es su propietario: SELECT owner, object_type FROM all_objects WHERE object_name = 'TABLE_NAME'. Si está en otro esquema, califíquelo completamente como schema.table u otorgue GRANT SELECT ON app_schema.my_table TO app_user. Si es necesario, cree un sinónimo público/privado: CREATE SYNONYM my_table FOR app_schema.my_table. Tenga en cuenta las mayúsculas/minúsculas y los identificadores entre comillas.","tags":["table","missing table","view","missing view","grant","privilege","synonym","alias"]},{"code":"ORA-01031","title":"Insufficient privileges (privilegios insuficientes)","severity":"caution","meaning":"El usuario no tiene el privilegio (de sistema o de objeto) necesario para la operación que intenta realizar.","cause":"No se ha otorgado el privilegio de sistema o de objeto necesario, el privilegio llega solo a través de un rol (los roles no son efectivos dentro de bloques PL/SQL con derechos del definidor), se intentó una operación administrativa sin SYSDBA/SYSOPER o falta un grant directo para procedimientos con derechos del definidor.","fix":"Otorgue el privilegio que falta, por ejemplo: GRANT CREATE TABLE TO app_user o GRANT SELECT ON app_schema.my_table TO app_user. Recuerde que, para objetos PL/SQL con nombre, los privilegios deben otorgarse directamente, no a través de un rol. Para operaciones que requieren SYSDBA, conéctese con las credenciales correctas. Compruebe los privilegios actuales: SELECT * FROM session_privs / user_sys_privs.","tags":["privilege","privileges","grant","permission","auth","role grant","role"]},{"code":"ORA-00257","title":"Archiver error. Connect internal only, until freed (error del archiver)","severity":"danger","meaning":"El proceso archiver (ARCH) no puede archivar los redo logs; normalmente el destino de archivado (FRA o destino de logs archivados) está lleno. La base de datos suspende las nuevas transacciones.","cause":"La Flash Recovery Area (DB_RECOVERY_FILE_DEST) o el destino de logs archivados está lleno, se ha agotado el espacio en disco, los backups de RMAN no eliminan los logs archivados o se ha alcanzado el límite DB_RECOVERY_FILE_DEST_SIZE.","fix":"Libere espacio: haga backup de los logs archivados antiguos con RMAN a un destino fuera de la FRA y elimínelos: RMAN> BACKUP ARCHIVELOG ALL FORMAT '/backup/arc_%U' DELETE INPUT (escribir el backup en la FRA mientras está llena falla). Con Data Guard, no elimine archivos aún no enviados o aplicados en el standby; de lo contrario se forma un gap en el standby (CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON ALL STANDBY). Vea el uso de la FRA: SELECT * FROM v$recovery_file_dest / v$flash_recovery_area_usage. Aumente temporalmente DB_RECOVERY_FILE_DEST_SIZE o mueva el destino a un disco más grande. Compruebe el espacio libre a nivel de disco. Establezca una política regular de limpieza de archivos.","tags":["archive","archivelog","archiver","fra","disk","space","redo"]},{"code":"ORA-01000","title":"Maximum open cursors exceeded (se superó el máximo de cursores abiertos)","severity":"caution","meaning":"Una sesión abrió más cursores de los permitidos por el parámetro OPEN_CURSORS. No se puede abrir ningún cursor nuevo.","cause":"La aplicación no cierra los objetos Statement/ResultSet que abre (fuga de cursores), OPEN_CURSORS es demasiado bajo para la carga de trabajo o se preparan continuamente nuevos prepared statements en un bucle que nunca se cierran.","fix":"Supervise los cursores abiertos: SELECT * FROM v$open_cursor WHERE sid = ...  y vea el recuento por sesión mediante v$sesstat. La solución real es cerrar cada Statement/ResultSet en un bloque finally en la aplicación (eliminando la fuga de cursores). Como solución temporal, aumente OPEN_CURSORS: ALTER SYSTEM SET OPEN_CURSORS = 1000. Revise el tamaño de la caché de statements de JDBC.","tags":["cursor","open cursor","open_cursors","leak","cursor leak","jdbc"]},{"code":"ORA-00018","title":"Maximum number of sessions exceeded (se superó el número máximo de sesiones)","severity":"danger","meaning":"La base de datos alcanzó el número máximo de sesiones permitido por el parámetro SESSIONS; no se puede abrir ninguna sesión nueva.","cause":"SESSIONS/PROCESSES configurados por debajo de lo que necesita la carga de trabajo, un pool de conexiones sobredimensionado, una fuga de conexiones o acumulación de sesiones que nunca se cierran.","fix":"Vea el número actual de sesiones: SELECT COUNT(*) FROM v$session. Compruebe los límites: SELECT * FROM v$resource_limit WHERE resource_name IN ('sessions','processes'). Aumente PROCESSES y, de forma indirecta, SESSIONS (vía SPFILE, requiere reinicio): ALTER SYSTEM SET PROCESSES = 500 SCOPE=SPFILE. Revise los tamaños de los pools de conexiones y las fugas en la aplicación.","tags":["session","session limit","sessions","processes","connection","connections","limit"]},{"code":"ORA-28000","title":"The account is locked (la cuenta está bloqueada)","severity":"caution","meaning":"El inicio de sesión falla porque la cuenta de usuario de la base de datos a la que se conecta está bloqueada.","cause":"Se superó el límite FAILED_LOGIN_ATTEMPTS del perfil (contraseñas incorrectas repetidas) o un DBA bloqueó la cuenta manualmente (ALTER USER ... ACCOUNT LOCK). Un bloqueo por inicios de sesión fallidos se libera automáticamente tras PASSWORD_LOCK_TIME. Nota: la caducidad por PASSWORD_LIFE_TIME no bloquea la cuenta; la cuenta pasa a EXPIRED / EXPIRED(GRACE) y devuelve ORA-28001/ORA-28002.","fix":"Compruebe el estado de la cuenta: SELECT username, account_status, lock_date FROM dba_users WHERE username = 'APP_USER'. Desbloquéela: ALTER USER app_user ACCOUNT UNLOCK. Para evitar que se repita, encuentre el origen de los inicios de sesión fallidos y, si es necesario, revise FAILED_LOGIN_ATTEMPTS en el perfil. Si la contraseña ha caducado, asigne una nueva.","tags":["account","user account","locked","account locked","auth","password","profile"]},{"code":"ORA-01578","title":"Oracle data block corrupted (file #, block #) (bloque de datos de Oracle corrupto)","severity":"danger","meaning":"Un bloque de datos del datafile indicado está corrupto; Oracle informa de que el bloque no superó su comprobación de consistencia (checksum). Se trata de corrupción física.","cause":"Fallo de disco/hardware, errores en la capa de almacenamiento, problemas del sistema operativo o de la controladora, bloques escritos parcialmente tras un corte de energía, o un backup/restore corrupto.","fix":"Confirme la corrupción y su alcance: RMAN> VALIDATE DATABASE / VALIDATE CHECK LOGICAL DATAFILE n; ejecute también DBVERIFY (dbv) y revise v$database_block_corruption. Si existe un backup, recupere a nivel de bloque: RMAN> RECOVER DATAFILE n BLOCK m; o, para todos los bloques listados, RMAN> RECOVER CORRUPTION LIST; (el antiguo comando BLOCKRECOVER está obsoleto desde 11g). Si es un bloque de índice, reconstruya el índice. Compruebe siempre el estado del hardware/disco; la causa raíz suele ser el almacenamiento.","tags":["corruption","block corruption","block","corrupt block","datafile","rman","disk"]},{"code":"ORA-01722","title":"Invalid number (número no válido)","severity":"safe","meaning":"Se usó una cadena de caracteres que no puede convertirse en número donde se requiere un contexto numérico. Falló una conversión implícita o explícita.","cause":"Comparar una columna numérica con texto (p. ej., WHERE num_col = 'ABC'), insertar un valor con letras en un campo numérico, un formato inadecuado para TO_NUMBER o una discrepancia de separador decimal/de miles (coma frente a punto).","fix":"Encuentre la expresión con la conversión fallida; normalmente una condición WHERE o un valor de INSERT. Asegure la compatibilidad de tipos en las comparaciones texto/número y use conversión explícita cuando sea necesario: TO_NUMBER(my_column) o TO_CHAR para el lado de texto. Impida la entrada de valores no numéricos en columnas numéricas en la capa de aplicación. Tenga en cuenta el parámetro NLS_NUMERIC_CHARACTERS.","tags":["number","numeric","conversion","type conversion","to_number","datatype","data type"]},{"code":"ORA-02049","title":"Timeout: distributed transaction waiting for lock (timeout de bloqueo en transacción distribuida)","severity":"danger","meaning":"Una transacción distribuida superó DISTRIBUTED_LOCK_TIMEOUT mientras esperaba un bloqueo en la base de datos remota. La transacción agotó el tiempo de espera.","cause":"Una transacción que se ejecuta a través de un database link espera una fila bloqueada en el lado remoto; transacciones largas en la sesión remota, latencia de red o bloqueos en conflicto mantenidos en ambos extremos.","fix":"Encuentre y resuelva el bloqueo y la sesión bloqueante en la base de datos remota (v$lock, v$session). Mantenga las transacciones distribuidas cortas y adquiera los bloqueos necesarios lo más tarde posible. Si es necesario, aumente DISTRIBUTED_LOCK_TIMEOUT según la carga de trabajo (SPFILE, requiere reinicio). Para transacciones distribuidas in-doubt, examine la vista DBA_2PC_PENDING y resuélvalas manualmente si es necesario.","tags":["distributed","distributed transaction","dblink","lock","lock wait","timeout","2pc"]},{"code":"enq: TX - row lock contention","title":"Espera: sesión bloqueada por un bloqueo de fila que mantiene otra transacción","severity":"caution","meaning":"Una sesión espera a que se libere un bloqueo de fila que mantiene otra transacción. Es el wait event clásico de bloqueo a nivel de aplicación.","cause":"Transacciones concurrentes que intentan actualizar la misma fila, transacciones sin commit durante mucho tiempo, bloqueos amplios causados por claves foráneas sin índice o concentración en una clave secuencial (hot block).","fix":"Encuentre las sesiones bloqueantes y bloqueadas: SELECT * FROM v$session WHERE blocking_session IS NOT NULL; y correlacione con v$lock. Si la causa raíz son transacciones largas, revise la estrategia de commit. Indexe las columnas de claves foráneas. Replantee el diseño (sequence, hash) para filas calientes. Termine la sesión bloqueante cuando sea necesario.","tags":["wait","wait event","enq","row lock","row lock wait","tx","contention","lock"]},{"code":"log file sync","title":"Espera: sesión esperando a que LGWR escriba el redo en disco al hacer commit","severity":"caution","meaning":"Cuando una sesión hace COMMIT, espera a que LGWR escriba (flush) los registros de redo en los archivos de redo log. Si esta espera es alta, hay latencia de commit.","cause":"Discos de redo log lentos (alta latencia de E/S), una aplicación que hace commit con demasiada frecuencia (commit fila a fila), presión de CPU sobre el proceso LGWR o un volumen de redo muy alto.","fix":"Mida el tiempo medio de espera con AWR/Statspack. Mueva los archivos de redo log a discos rápidos de baja latencia (SSD). Reduzca la frecuencia innecesaria de commit en la aplicación y agrupe las operaciones por lotes. Si es necesario, evalúe las opciones COMMIT_LOGGING/COMMIT_WAIT (commit_write) frente a los requisitos de negocio; ADVERTENCIA: NOWAIT/BATCH reducen la durabilidad y las transacciones que parecen confirmadas pueden perderse en una caída. Úselas solo a nivel de sesión o de sentencia para trabajos en los que la pérdida sea tolerable; no las active en todo el sistema. Examine conjuntamente la latencia de LGWR y del almacenamiento.","tags":["wait","wait event","log file sync","commit","lgwr","redo","latency"]},{"code":"db file sequential read","title":"Espera: lectura física de un solo bloque, normalmente un acceso a índice o tabla","severity":"caution","meaning":"Una sesión espera a que se lea un único bloque desde disco. Suele verse con acceso por índice o lecturas de un bloque por ROWID; normalmente es un evento esperado.","cause":"Bloques que no están en el buffer cache leídos desde disco, uso de índices poco selectivos o incorrectos, un buffer cache insuficiente o una capa de almacenamiento lenta.","fix":"Si es alto, encuentre las sentencias SQL que más leen (AWR Top SQL, v$sql). Mejore las consultas con índices adecuados y rutas de acceso correctas, y reduzca los nested loops innecesarios. Evalúe el tamaño del buffer cache (DB_CACHE_SIZE). Compruebe la latencia de E/S del almacenamiento. Este evento suele ser normal; si es anormalmente alto, céntrese en el ajuste de SQL.","tags":["wait","wait event","db file sequential read","io","disk io","index","index access","single block"]},{"code":"db file scattered read","title":"Espera: lectura física multibloque, normalmente un full table scan o index fast full scan","severity":"caution","meaning":"Una sesión espera a que se lean varios bloques (multiblock) desde disco. Suele ocurrir durante un full table scan o un index fast full scan.","cause":"Full table scans por falta de un índice adecuado, un plan incorrecto por estadísticas obsoletas, recorridos de tablas muy grandes o la ruta de acceso elegida que recurre a un recorrido de tabla.","fix":"Identifique las sentencias SQL que hacen recorridos completos y añada los índices necesarios o asegúrese de que se use el índice existente. Actualice las estadísticas: DBMS_STATS.GATHER_TABLE_STATS. Si realmente se necesita un recorrido grande, considere parallel query o particionamiento. Revise DB_FILE_MULTIBLOCK_READ_COUNT y el throughput del almacenamiento.","tags":["wait","wait event","db file scattered read","full scan","full table scan","io","disk io","multiblock"]},{"code":"buffer busy waits","title":"Espera: contención por un buffer que otra sesión ya está leyendo o modificando","severity":"caution","meaning":"Una sesión espera para acceder a un bloque de buffer que otra sesión está leyendo o modificando al mismo tiempo. Es contención de bloques en memoria.","cause":"Concentración en hot blocks, contención en el extremo derecho de índices con claves monótonamente crecientes, freelists insuficientes/segmentos no ASSM o DML concurrente intenso sobre el mismo bloque.","fix":"Averigüe qué segmento y qué clase de bloque están afectados (v$session, v$segment_statistics, ASH). Para bloques de índice calientes, considere un índice reverse key o particionado por hash. Use ASSM (Automatic Segment Space Management). Con alta concurrencia, aumente INITRANS/PCTFREE. Distribuya el patrón de acceso de la aplicación.","tags":["wait","wait event","buffer busy","hot block","hot blocks","contention","block contention"]},{"code":"gc buffer busy","title":"Espera (RAC): contención por un bloque de cache fusion que se transfiere entre instancias","severity":"caution","meaning":"En un entorno RAC, una sesión espera para acceder a un bloque global que se está transfiriendo mediante cache fusion desde/hacia otra instancia o que esa instancia mantiene ocupado.","cause":"Los mismos hot blocks compartidos entre nodos RAC (hot block entre instancias), latencia/cuello de botella del interconnect, contención entre nodos en índices con claves secuenciales o una distribución desequilibrada de la carga de trabajo.","fix":"Encuentre los segmentos afectados con v$segment_statistics y las vistas GV$. Compruebe el estado y la latencia del interconnect (red privada y rápida). Use reverse key o particionamiento por hash para índices calientes. Reduzca la contención entre nodos sobre los mismos bloques separando la carga de trabajo entre nodos por servicio.","tags":["wait","wait event","rac","gc buffer busy","cache fusion","interconnect","global"]},{"code":"library cache lock","title":"Espera: contención en el handle de un objeto del library cache (serialización de parse/DDL)","severity":"caution","meaning":"Una sesión espera un bloqueo sobre un objeto del library cache (SQL, PL/SQL, definición de tabla). Suele deberse a la serialización de operaciones de parse y DDL.","cause":"DDL (ALTER/CREATE) sobre objetos muy utilizados, objetos PL/SQL recompilados con frecuencia (invalidación), hard parsing excesivo, invalidación de objetos dependientes o tormentas intensas de conexiones concurrentes.","fix":"Encuentre la sesión bloqueante y el objeto (v$session, ASH, diagnósticos de library cache lock si es necesario). Evite ejecutar DDL sobre objetos muy utilizados en horas pico. Reduzca las cadenas de dependencias y evite recompilaciones innecesarias. Disminuya el hard parsing usando variables bind. Suavice las tormentas de conexiones con un pool de conexiones.","tags":["wait","wait event","library cache lock","parse","parsing","ddl","hard parse"]}]},"errors/postgres.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"errors":[{"code":"23505","title":"unique_violation","severity":"caution","meaning":"La fila insertada o actualizada viola una restricción UNIQUE o la PRIMARY KEY; el mismo valor ya existe en la tabla.","cause":"Volver a insertar con la misma clave única, introducir datos duplicados en una columna única, el valor de la secuencia que se queda atrás por inserciones manuales o dos peticiones concurrentes que insertan la misma clave.","fix":"El nombre de la restricción y el valor de la clave en el detalle del error muestran qué fila entra en conflicto. Para actualizar la fila existente, use INSERT ... ON CONFLICT (my_column) DO UPDATE o DO NOTHING. Para encontrar duplicados: SELECT my_column, COUNT(*) FROM my_table GROUP BY my_column HAVING COUNT(*) > 1. Si la secuencia se quedó atrás: SELECT setval('seq_name', (SELECT MAX(id) FROM my_table)).","tags":["unique","unique","constraint","constraint","duplicate","duplicate","23505","primary key"]},{"code":"23503","title":"foreign_key_violation","severity":"caution","meaning":"Se violó una restricción FOREIGN KEY. La fila padre referenciada por la fila insertada/actualizada no existe, o la fila padre que se elimina aún tiene filas hijas dependientes.","cause":"Un INSERT/UPDATE que referencia una clave padre inexistente, un intento de eliminar/actualizar una fila padre que aún está referenciada o datos cargados en el orden incorrecto (primero el hijo, luego el padre).","fix":"Inserte primero la fila padre y luego la fila hija. Si se necesita un comportamiento en cascada al eliminar, defina la restricción con ON DELETE CASCADE u ON DELETE SET NULL. En cargas masivas, cargue los datos en orden padre-hijo. Encuentre las filas que hacen referencia: SELECT * FROM child WHERE fk_column = value.","tags":["foreign key","foreign key","fk","constraint","constraint","reference","23503"]},{"code":"23502","title":"not_null_violation","severity":"caution","meaning":"Se intentó insertar NULL en una columna con restricción NOT NULL, o asignar NULL a esa columna.","cause":"Omitir una columna obligatoria en el INSERT, asignar NULL explícitamente, que la columna no tenga DEFAULT o un valor vacío/ausente procedente de la aplicación.","fix":"Proporcione un valor válido para la columna o defina en ella un DEFAULT razonable: ALTER TABLE my_table ALTER COLUMN my_column SET DEFAULT value. Si la columna realmente no necesita ser obligatoria, elimine la restricción: ALTER TABLE my_table ALTER COLUMN my_column DROP NOT NULL. Añada validación de datos ausentes en la capa de aplicación.","tags":["not null","null","constraint","constraint","required","23502","insert"]},{"code":"40P01","title":"deadlock_detected","severity":"danger","meaning":"Dos o más transacciones entraron en deadlock al esperar cada una bloqueos que mantiene la otra. PostgreSQL detecta el ciclo y cancela una de las transacciones.","cause":"Bloqueos adquiridos en distinto orden, actualizaciones cruzadas de las mismas filas, transacciones largas o bloqueos en conflicto tomados durante las comprobaciones de claves foráneas.","fix":"El detalle del deadlock en el log del servidor muestra las consultas en conflicto. Acceda a tablas/filas en el mismo orden en todas las transacciones. Mantenga las transacciones cortas. Capture la transacción cancelada en la aplicación y reinténtela. Combine pg_locks con pg_stat_activity para examinar los bloqueos. Si es necesario, haga explícito el orden de bloqueo con SELECT ... FOR UPDATE.","tags":["deadlock","deadlock","lock","lock","40P01","tx","transaction"]},{"code":"40001","title":"serialization_failure","severity":"caution","meaning":"Con el nivel de aislamiento SERIALIZABLE o REPEATABLE READ, la transacción no pudo serializarse porque entró en conflicto con cambios concurrentes y fue cancelada. Es un comportamiento esperado que protege la consistencia de los datos.","cause":"Transacciones que acceden a los mismos datos de forma concurrente y no pueden ordenarse de forma serializable, conflictos de escritura con alta concurrencia o dependencias peligrosas detectadas por SSI (Serializable Snapshot Isolation).","fix":"No es un bug sino una señal esperada: reintente la transacción tal cual. Añada lógica de reintento automático en la aplicación. Para reducir los conflictos, acorte las transacciones y use un nivel de aislamiento más bajo (READ COMMITTED) si realmente no se necesita. Diseñe para reducir la contención en filas calientes.","tags":["serialization","serialization","isolation","isolation","retry","retry","40001","mvcc"]},{"code":"53300","title":"too_many_connections","severity":"danger","meaning":"El servidor alcanzó el número máximo de conexiones concurrentes permitido por max_connections; no se pueden aceptar conexiones nuevas.","cause":"Ausencia de pool de conexiones o uno mal dimensionado, fugas de conexiones, aplicaciones que abren muchas conexiones de corta duración o max_connections configurado por debajo de la carga de trabajo.","fix":"Identifique los orígenes de las conexiones: SELECT usename, state, COUNT(*) FROM pg_stat_activity GROUP BY 1,2. Termine de forma selectiva las conexiones de cliente inactivas durante mucho tiempo (una terminación masiva sin filtro afecta a todas las aplicaciones y pools): SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle' AND backend_type = 'client backend' AND usename = 'app_user' AND state_change < now() - interval '10 minutes' AND pid <> pg_backend_pid(). La solución real es un pool de conexiones como PgBouncer. Aumentar max_connections consume memoria; prefiera el pooling. Las conexiones reservadas para superusuarios mantienen disponible el acceso administrativo.","tags":["connection","connection","max_connections","too many","53300","pool","pool","pgbouncer"]},{"code":"53100","title":"disk_full","severity":"danger","meaning":"El servidor no pudo escribir en un archivo porque el disco está lleno. No queda espacio en el disco que contiene el directorio de datos, el WAL o los archivos temporales.","cause":"Crecimiento de datos, acumulación descontrolada de WAL (archivado o un replication slot que se queda atrás), archivos temporales grandes (ordenaciones enormes), archivos de log inflados o bloat de tablas/índices.","fix":"Inspeccione el uso de disco y elimine los archivos innecesarios. Compruebe la acumulación de WAL: pg_stat_replication y pg_replication_slots (elimine los slots no usados; los rezagados retienen WAL). No ejecute VACUUM FULL ni REINDEX con el disco lleno: como reescriben la tabla, necesitan espacio adicional igual al tamaño de la tabla y pueden empeorar la situación; además, el VACUUM normal (casi) nunca devuelve espacio al sistema operativo. NUNCA elimine a mano archivos del directorio pg_wal. Primero libere espacio resolviendo los orígenes de WAL/slots, logs y temporales; revise la ubicación de los logs y los archivos temporales. Como solución permanente, aumente la capacidad de disco y configure supervisión/alertas.","tags":["disk","space","space","full","full","wal","53100","vacuum"]},{"code":"57014","title":"query_canceled","severity":"safe","meaning":"La consulta fue cancelada. Suele deberse a que expiró statement_timeout o a una cancelación del usuario o del cliente (pg_cancel_backend, cancelación del driver).","cause":"Se superó statement_timeout, una cancelación con pg_cancel_backend o una solicitud de cancelación enviada por la aplicación/cliente. Situaciones similares tienen códigos distintos: la expiración de lock_timeout devuelve 55P03, idle_in_transaction_session_timeout 25P03 (FATAL), y una cancelación causada por un conflicto de replicación en un hot standby 40001 (40P01 en algunos casos).","fix":"Si la consulta realmente tarda mucho, ajústela (EXPLAIN ANALYZE, índices, plan). Si el timeout es demasiado bajo, ajuste statement_timeout para la sesión/rol correspondiente. Las cancelaciones deliberadas no requieren acción. Para cancelaciones causadas por bloqueos y conflictos en el standby, consulte las entradas 55P03 y 40001.","tags":["cancel","cancel","timeout","timeout","statement_timeout","57014","standby"]},{"code":"42P01","title":"undefined_table","severity":"caution","meaning":"La tabla (o vista) referenciada en la consulta no existe o no se encuentra en el search_path.","cause":"La tabla realmente no existe, está en otro esquema que no figura en el search_path, un error tipográfico o de mayúsculas en el nombre de la tabla, una discrepancia de mayúsculas/minúsculas con identificadores entre comillas, o el objeto aún no se ha creado o fue eliminado.","fix":"Verifique que la tabla existe y su esquema: SELECT schemaname, tablename FROM pg_tables WHERE tablename = 'my_table'. Si está en otro esquema, califíquela como schema.table o configure el search_path: SET search_path TO app_schema, public. Recuerde que los identificadores sin comillas se convierten a minúsculas; si se usaron comillas al crearla, haga referencia a ella de la misma forma.","tags":["table","table","undefined","42P01","search_path","schema","schema"]},{"code":"42703","title":"undefined_column","severity":"caution","meaning":"La columna referenciada en la consulta no existe en la tabla ni en el ámbito actual.","cause":"Un error tipográfico en el nombre de la columna, la columna eliminada/renombrada, una referencia a la tabla equivocada, una discrepancia de mayúsculas/minúsculas con identificadores entre comillas o una calificación ambigua/incorrecta en una consulta con varias tablas.","fix":"Liste las columnas: \\d my_table o SELECT column_name FROM information_schema.columns WHERE table_name = 'my_table'. Corrija el nombre y la tabla, y califique las columnas en los JOIN como table.column. Si se usaron comillas al crearla, haga referencia a ella con las mismas mayúsculas/minúsculas.","tags":["column","column","undefined","42703","typo","typo","schema"]},{"code":"42501","title":"insufficient_privilege","severity":"caution","meaning":"El rol no tiene el privilegio necesario para la operación que intenta realizar.","cause":"No se otorgó el GRANT necesario sobre la tabla/esquema/base de datos, falta USAGE sobre el esquema, el objeto pertenece a otro rol o las políticas de seguridad a nivel de fila (RLS) bloquean el acceso.","fix":"Otorgue el privilegio que falta: GRANT SELECT, INSERT ON my_table TO app_role; para el esquema GRANT USAGE ON SCHEMA app_schema TO app_role. Para grants masivos use GRANT ... ON ALL TABLES IN SCHEMA, y ALTER DEFAULT PRIVILEGES para objetos futuros. Vea los privilegios actuales: \\dp my_table. Si RLS está activo, revise las políticas correspondientes.","tags":["privilege","privilege","permission","grant","42501","permission","rls"]},{"code":"28P01","title":"invalid_password","severity":"caution","meaning":"La contraseña proporcionada durante la autenticación es incorrecta; se rechazó el inicio de sesión del rol.","cause":"Una contraseña incorrecta, una contraseña cambiada, credenciales obsoletas en la cadena de conexión de la aplicación o una discrepancia con el método de autenticación esperado en pg_hba.conf (scram-sha-256, md5).","fix":"Verifique la contraseña y restablézcala si es necesario: ALTER ROLE app_role WITH PASSWORD 'new_password'. Asegúrese de que el método en pg_hba.conf sea compatible con el cliente (los clientes antiguos pueden tener problemas con scram-sha-256). Tras los cambios, recargue la configuración con SELECT pg_reload_conf().","tags":["auth","identity","password","password","28P01","scram","md5","hba"]},{"code":"08006","title":"connection_failure","severity":"danger","meaning":"No se pudo establecer la conexión entre cliente y servidor, o se interrumpió de forma inesperada.","cause":"El servidor no está en ejecución o se está reiniciando, problemas de red/firewall, host/puerto incorrectos, un valor de listen_addresses que bloquea el acceso, una conexión inactiva cortada por la red o un servidor sobrecargado.","fix":"Compruebe que el servidor está activo y escuchando en el host/puerto correctos (pg_isready host port). Verifique listen_addresses en postgresql.conf y las reglas de acceso en pg_hba.conf. Revise la red/firewall y la configuración DNS. Revise la configuración de TCP keepalive y de timeouts en el pool de conexiones. Busque rastros de caídas/reinicios en el log del servidor.","tags":["connection","connection","network","network","08006","failure","disconnect"]},{"code":"55P03","title":"lock_not_available","severity":"caution","meaning":"No se pudo adquirir de inmediato el bloqueo solicitado. Suele ocurrir cuando se solicita un bloqueo con NOWAIT mientras otro lo mantiene, o cuando expira lock_timeout.","cause":"La fila/tabla está bloqueada al usar SELECT ... FOR UPDATE NOWAIT o LOCK TABLE ... NOWAIT, o expira lock_timeout. La mayoría de las veces, una transacción larga mantiene el bloqueo.","fix":"Encuentre la sesión bloqueante: combine pg_locks y pg_stat_activity (relación de espera/bloqueo). Finalice la transacción larga o, si es necesario, termínela con pg_terminate_backend(pid). En lugar de NOWAIT, espere con un lock_timeout razonable y añada lógica de reintento. Acorte las transacciones para reducir el tiempo de retención de los bloqueos.","tags":["lock","lock","nowait","lock_timeout","55P03","not available","busy"]},{"code":"54000","title":"program_limit_exceeded","severity":"caution","meaning":"Se superó un límite de implementación/formato de PostgreSQL; por ejemplo, el tamaño máximo de una fila o campo, el tamaño de fila de índice o el límite del número de parámetros/columnas de una consulta.","cause":"Intentar almacenar un valor muy grande (p. ej., un campo de más de 1 GB), un valor indexado que supera el límite de página de B-tree (unos 2704 bytes) o usar demasiadas columnas o parámetros de consulta.","fix":"Identifique el límite superado (el mensaje suele indicarlo). Revise o divida la estructura para textos/datos muy grandes. Indexe las columnas de texto largas con un índice hash o un índice de expresión/parcial en lugar de un B-tree. Reduzca el número de parámetros/columnas de la consulta o divida el lote.","tags":["limit","limit","program_limit","54000","index size","size","row size"]},{"code":"22001","title":"string_data_right_truncation","severity":"caution","meaning":"El valor se habría truncado porque la longitud del texto supera la longitud definida de la columna de destino (p. ej., VARCHAR(n)); PostgreSQL lo rechaza y genera un error.","cause":"Insertar o actualizar un texto de más de n caracteres en una columna VARCHAR(n)/CHAR(n), datos más largos de lo esperado procedentes de la aplicación o una longitud de columna definida demasiado corta para la necesidad.","fix":"Acorte el valor para que quepa en la columna o aumente la longitud de la columna: ALTER TABLE my_table ALTER COLUMN my_column TYPE VARCHAR(500). Si se necesita texto ilimitado, use el tipo TEXT. Valide la longitud en la aplicación. Si desea un truncamiento deliberado, aplique LEFT(value, n) explícitamente.","tags":["truncation","truncation","varchar","string","text","22001","length","length"]},{"code":"25P02","title":"in_failed_sql_transaction","severity":"caution","meaning":"Ya se produjo un error anteriormente en la transacción; no pueden ejecutarse más comandos en ella. La transacción está en estado abortado y solo se acepta ROLLBACK.","cause":"Una sentencia anterior de un bloque de transacción falló (p. ej., una violación de restricción) y aun así se siguen enviando comandos nuevos en la misma transacción. La aplicación no captura el error y continúa con la transacción.","fix":"Revierta la transacción: ROLLBACK, y vuelva a iniciarla. Si quiere aislar el error de un bloque y continuar, use SAVEPOINT y vuelva a ese punto con ROLLBACK TO SAVEPOINT. En la aplicación, capture el error de cada sentencia y gestione la transacción correctamente (haga explícita la decisión de commit/rollback).","tags":["transaction","transaction","aborted","aborted","25P02","rollback","savepoint"]},{"code":"53200","title":"out_of_memory","severity":"danger","meaning":"El servidor no pudo asignar la memoria necesaria para una operación. Un proceso backend o todo el servidor está bajo presión de memoria.","cause":"work_mem/maintenance_work_mem demasiado altos y multiplicados por una alta concurrencia, demasiadas operaciones hash/sort en una sola consulta, un número excesivo de conexiones, una fuga de memoria o presión del OOM killer del sistema operativo.","fix":"Revise el contexto de memoria en el log del servidor y los rastros del OOM killer. Configure work_mem de forma razonable, teniendo en cuenta el número de consultas y la concurrencia (se asigna por separado para cada sort/hash). Limite el número de conexiones con pooling. Revise la configuración de memoria/overcommit del sistema operativo. Optimice las consultas pesadas para reducir su necesidad de memoria.","tags":["memory","memory","out of memory","53200","work_mem","oom"]}]},"errors/mysql.json":{"engineId":"mysql","engine":"MySQL","order":3,"errors":[{"code":"1045","title":"Access denied for user (using password: YES/NO) (acceso denegado para el usuario)","severity":"caution","meaning":"Falló la autenticación para el nombre de usuario, la contraseña o el host desde el que se conecta; se rechazó el inicio de sesión.","cause":"Contraseña incorrecta, el usuario no tiene privilegios desde el host desde el que se conecta (user@host), la cuenta no existe, una discrepancia de plugin de autenticación (caching_sha2_password frente a mysql_native_password) o credenciales obsoletas en la cadena de conexión.","fix":"Compruebe la correspondencia entre usuario y host: SELECT user, host, plugin FROM mysql.user. Otorgue privilegios para el host correcto (p. ej., 'app_user'@'%'). Restablezca la contraseña: ALTER USER 'app_user'@'host' IDENTIFIED BY 'new_password'; (ALTER USER/GRANT surten efecto de inmediato; FLUSH PRIVILEGES solo es necesario si las tablas de grants se modificaron directamente con DML). Si ve 'using password: NO', el cliente no está enviando ninguna contraseña. Para discrepancias de plugin, revise el plugin de autenticación: en MySQL 8.4 el plugin mysql_native_password está deshabilitado por defecto (solo se carga con --mysql-native-password=ON); las cuentas antiguas que usan este plugin y los clientes antiguos no pueden conectarse. Migre la cuenta con ALTER USER ... IDENTIFIED WITH caching_sha2_password BY '...'; y actualice el driver del cliente.","tags":["auth","authentication","access denied","access","password","password","1045","privilege"]},{"code":"1062","title":"Duplicate entry for key (entrada duplicada para la clave)","severity":"caution","meaning":"La fila insertada o actualizada viola un índice UNIQUE o la PRIMARY KEY; el mismo valor de clave ya existe.","cause":"Un INSERT repetido con la misma clave única, entrada de datos duplicados, un contador AUTO_INCREMENT que se ha quedado atrás o filas duplicadas existentes al añadir un índice único a una tabla existente.","fix":"El mensaje de error muestra el valor de clave en conflicto y el nombre del índice. Para actualizar la fila existente, considere INSERT ... ON DUPLICATE KEY UPDATE o INSERT IGNORE / REPLACE. Encuentre los duplicados: SELECT my_column, COUNT(*) FROM my_table GROUP BY my_column HAVING COUNT(*) > 1. Si no puede añadir un índice único, limpie primero los duplicados.","tags":["duplicate","duplicate entry","unique","unique key","1062","primary key","insert"]},{"code":"1213","title":"Deadlock found when trying to get lock; try restarting transaction (deadlock al obtener el bloqueo; reinicie la transacción)","severity":"danger","meaning":"InnoDB detectó un deadlock en el que dos o más transacciones esperan los bloqueos de la otra y rompió el ciclo revirtiendo una de las transacciones.","cause":"Bloqueos de fila adquiridos en distinto orden, actualizaciones cruzadas de las mismas filas, rangos amplios de filas bloqueados (gap locks) por falta de índices o conflictos durante las comprobaciones de claves foráneas.","fix":"Vea los detalles del último deadlock: SHOW ENGINE INNODB STATUS (sección LATEST DETECTED DEADLOCK). Acceda a filas/tablas en el mismo orden en todas las transacciones y mantenga las transacciones cortas. Reduzca el rango bloqueado añadiendo índices adecuados. Capture la transacción revertida en la aplicación y reinténtela (lógica de reintento).","tags":["deadlock","deadlock detected","lock","row lock","innodb","1213","tx","transaction"]},{"code":"1205","title":"Lock wait timeout exceeded; try restarting transaction (se superó el tiempo de espera de bloqueo; reinicie la transacción)","severity":"danger","meaning":"Una transacción esperó un bloqueo de fila necesario durante innodb_lock_wait_timeout segundos, no pudo adquirirlo y agotó el tiempo de espera.","cause":"Otra transacción que mantiene el bloqueo durante mucho tiempo sin commit ni rollback; transacciones abiertas inactivas, actualizaciones masivas de larga duración o filas calientes en contención.","fix":"Encuentre la transacción bloqueante: combine performance_schema.data_lock_waits con data_locks (information_schema.innodb_lock_waits/innodb_trx en versiones antiguas). Haga commit/rollback de la transacción bloqueante o termínela con KILL <trx_mysql_thread_id> si es necesario. Mantenga las transacciones cortas y no deje transacciones inactivas (autocommit/lógica de la aplicación). Si es necesario, ajuste innodb_lock_wait_timeout a la carga de trabajo.","tags":["lock","lock wait","timeout","timed out","innodb","1205","wait","waiting"]},{"code":"1040","title":"Too many connections (demasiadas conexiones)","severity":"danger","meaning":"El servidor alcanzó el número máximo de conexiones concurrentes permitido por max_connections; se están rechazando las conexiones nuevas.","cause":"Un pool de conexiones mal dimensionado, fugas de conexiones, muchas conexiones de corta duración, conexiones inactivas durante mucho tiempo (Sleep) o un valor de max_connections demasiado bajo para la carga de trabajo.","fix":"Inspeccione las conexiones: SHOW PROCESSLIST o SELECT * FROM performance_schema.processlist. Cierre las conexiones inactivas innecesarias con KILL. Aumente max_connections: SET GLOBAL max_connections = 500 (en my.cnf para que persista). La solución de fondo es usar un pool de conexiones y cerrar las conexiones inactivas con wait_timeout. Se reserva una conexión de administración de emergencia adicional para SUPER/CONNECTION_ADMIN.","tags":["connection","connections","too many","max_connections","1040","pool","connection pool"]},{"code":"1064","title":"You have an error in your SQL syntax (error de sintaxis SQL)","severity":"safe","meaning":"La sentencia SQL tiene un error de sintaxis; el servidor no pudo analizarla. El mensaje muestra el texto cercano al error.","cause":"Un error tipográfico, una coma o paréntesis que falta o sobra, una palabra reservada usada sin comillas como nombre de columna/tabla, sintaxis no soportada por la versión de MySQL o errores de comillas/escape.","fix":"La parte 'near ...' del mensaje muestra dónde empieza el error; revise justo lo que hay antes. Ponga las palabras reservadas entre backticks: `order`, `group`. Equilibre paréntesis y comas. Verifique que usa sintaxis soportada por su versión. Si genera SQL dinámico, registre e inspeccione la consulta completa resultante.","tags":["syntax","syntax error","1064","parse","parsing","reserved word","reserved keyword"]},{"code":"1146","title":"Table doesn't exist (la tabla no existe)","severity":"caution","meaning":"La tabla referenciada en la consulta no existe o no se encuentra en la base de datos actual.","cause":"La tabla realmente no existe, está seleccionada la base de datos equivocada, un error tipográfico en el nombre de la tabla, la distinción de mayúsculas/minúsculas en nombres de tabla en Linux (lower_case_table_names) o un archivo de tabla InnoDB (.ibd) ausente/corrupto (en 5.7 y anteriores también el archivo de definición .frm; en 8.0 las definiciones residen en el diccionario de datos).","fix":"Verifique la tabla: SHOW TABLES; y seleccione la base de datos correcta: USE mydb. Tenga cuidado con la distinción de mayúsculas/minúsculas (sobre todo en Linux). Compruebe el nombre de la tabla y el esquema con SELECT ... FROM information_schema.tables. Si hay un problema con el archivo InnoDB, puede ser necesario restaurar/reparar.","tags":["table","table missing","doesn't exist","not found","1146","schema","database"]},{"code":"1054","title":"Unknown column in field list (columna desconocida en la lista de campos)","severity":"caution","meaning":"La columna referenciada en la consulta no existe en la tabla ni en el ámbito actual.","cause":"Un error tipográfico en el nombre de la columna, la columna se eliminó/renombró, una referencia a la tabla equivocada, una calificación ambigua/incorrecta en un JOIN o un valor de cadena escrito sin comillas e interpretado como columna (WHERE name = value en lugar de WHERE name = 'value').","fix":"Liste las columnas: SHOW COLUMNS FROM my_table o DESCRIBE my_table. Corrija el nombre de la columna y califíquela como my_table.my_column en los JOIN. Encierre los literales de cadena entre comillas simples; los backticks son solo para identificadores. Puede comprobarlo con information_schema.columns.","tags":["column","column name","unknown","unknown column","1054","typo","misspelling"]},{"code":"2002","title":"Can't connect to local MySQL server through socket (no se puede conectar al servidor MySQL local por el socket)","severity":"danger","meaning":"El cliente no pudo conectarse al servidor MySQL local a través del socket Unix. Normalmente el servidor no está en ejecución o la ruta del socket es incorrecta.","cause":"El servicio MySQL/MariaDB no está en ejecución o se ha caído, la ruta del archivo de socket (p. ej., /var/run/mysqld/mysqld.sock) difiere entre cliente y servidor, problemas de permisos o el servidor no ha arrancado.","fix":"Compruebe el estado del servicio e inícielo (systemctl status mysql / start mysql). Verifique la ruta correcta del socket: SHOW VARIABLES LIKE 'socket' y el parámetro socket en my.cnf. Si la ruta difiere, indíquela explícitamente con --socket o use TCP: mysql -h 127.0.0.1 -P 3306. Revise los logs del servidor en busca de errores de arranque/caída.","tags":["connection","connect","socket","unix socket","2002","server","local","localhost"]},{"code":"2003","title":"Can't connect to MySQL server (TCP/IP) (no se puede conectar al servidor MySQL por TCP/IP)","severity":"danger","meaning":"El cliente no pudo conectarse al servidor MySQL por TCP/IP en el host y puerto indicados.","cause":"El servidor no está en ejecución, host/puerto incorrectos, un firewall que bloquea el puerto 3306, el servidor enlazado solo a localhost (bind-address = 127.0.0.1) o skip-networking habilitado.","fix":"Compruebe que el servidor está activo y escuchando en el puerto correcto. Para acceso remoto, configure bind-address = 0.0.0.0 (o la IP adecuada) en my.cnf y asegúrese de que skip-networking esté desactivado. Abra el puerto 3306 en el firewall. Verifique host/puerto: mysql -h host -P 3306 -u app_user -p. Asegúrese de que el usuario tenga privilegios desde el host remoto.","tags":["connection","connect","tcp","port","2003","network","firewall","bind-address"]},{"code":"2006","title":"MySQL server has gone away (se perdió la conexión con el servidor MySQL)","severity":"caution","meaning":"Se perdió la conexión entre cliente y servidor durante un comando. El servidor puede haber cerrado la conexión o rechazado el paquete.","cause":"Una conexión inactiva cerrada porque expiró wait_timeout/interactive_timeout, una consulta/datos mayores que max_allowed_packet, un reinicio o caída del servidor o una interrupción de la red.","fix":"Para problemas de conexiones inactivas, use validación en el pool de conexiones y revise wait_timeout. Si envía datos grandes, aumente max_allowed_packet (p. ej., SET GLOBAL max_allowed_packet = 64*1024*1024, en my.cnf para que persista). Revise los logs del servidor en busca de reinicios/caídas y rastros de OOM. Añada lógica de reconexión en la aplicación para las conexiones perdidas.","tags":["connection","connection lost","gone away","disconnect","2006","timeout","max_allowed_packet"]},{"code":"1114","title":"The table is full (la tabla está llena)","severity":"danger","meaning":"No se pueden escribir más datos en la tabla porque se alcanzó un límite de almacenamiento o el final del espacio disponible.","cause":"Disco lleno, se alcanzó el límite max_heap_table_size en tablas MEMORY, se superó tmp_table_size en tablas temporales, se llenó un tablespace de tamaño fijo definido por innodb_data_file_path o límites de tamaño del sistema de archivos o específicos de la tabla.","fix":"Primero compruebe y libere espacio en disco. Para tablas MEMORY/temporales, aumente max_heap_table_size y tmp_table_size. Para InnoDB, asegúrese de que innodb_data_file_path sea autoextend o añada espacio. Optimice las consultas que vuelcan resultados temporales grandes a disco. Como solución permanente, aumente la capacidad de almacenamiento.","tags":["table full","table is full","disk","space","1114","space","memory","innodb"]},{"code":"1215","title":"Cannot add foreign key constraint (no se puede añadir la restricción de clave foránea)","severity":"caution","meaning":"No se puede crear la restricción de clave foránea que se está definiendo; MySQL no puede añadir la restricción.","cause":"Discrepancia de tipo de dato/longitud/signo (signed-unsigned) o de conjunto de caracteres/collation entre la columna hija y la referenciada, ausencia de un índice/PRIMARY KEY adecuado en la columna referenciada, tablas con distintos motores de almacenamiento (una es MyISAM) o la tabla referenciada aún no existe.","fix":"Asegúrese de que los tipos de columna de ambos lados sean EXACTAMENTE iguales (p. ej., ambos INT UNSIGNED). Verifique que la columna referenciada esté respaldada por un índice (normalmente PRIMARY KEY o UNIQUE). Asegúrese de que ambas tablas sean InnoDB. Use el mismo charset/collation para claves de cadena. Para más detalles: SHOW ENGINE INNODB STATUS (LATEST FOREIGN KEY ERROR).","tags":["foreign key","foreign key constraint","fk","1215","constraint","constraint error","innodb","type"]},{"code":"1451","title":"Cannot delete or update a parent row: a foreign key constraint fails (no se puede eliminar/actualizar la fila padre: falla la restricción de clave foránea)","severity":"caution","meaning":"Intentó eliminar una fila padre o cambiar su clave, pero la restricción de clave foránea no lo permite porque existen filas hijas dependientes.","cause":"La fila padre referenciada aún tiene filas hijas y la restricción usa RESTRICT/NO ACTION para ON DELETE/UPDATE.","fix":"Primero elimine las filas hijas dependientes o muévalas a otro padre y luego elimine la fila padre. Si desea un comportamiento automático, redefina la restricción con ON DELETE CASCADE u ON DELETE SET NULL. Encuentre las filas dependientes: SELECT * FROM child_table WHERE fk_column = value.","tags":["foreign key","foreign key constraint","fk","1451","parent","parent row","delete","reference"]},{"code":"1452","title":"Cannot add or update a child row: a foreign key constraint fails (no se puede añadir/actualizar la fila hija: falla la restricción de clave foránea)","severity":"caution","meaning":"Está insertando o actualizando una fila hija, pero el valor de la clave padre al que apunta no existe en la tabla referenciada; la restricción de clave foránea lo rechaza.","cause":"Un INSERT/UPDATE que referencia una clave padre inexistente, datos cargados en el orden incorrecto (primero el hijo) o una fila padre eliminada.","fix":"Inserte primero la fila padre y luego la fila hija. Verifique que el valor referenciado exista: SELECT * FROM parent_table WHERE key_column = value. Respete el orden padre-hijo en las cargas masivas. Use SET FOREIGN_KEY_CHECKS=0 solo en operaciones de mantenimiento controladas y vuelva a ponerlo en 1 al terminar.","tags":["foreign key","foreign key constraint","fk","1452","child","child row","reference","insert"]},{"code":"1366","title":"Incorrect string/integer value for column (valor de cadena/entero incorrecto para la columna)","severity":"caution","meaning":"Se insertó en una columna un valor incompatible con su tipo o conjunto de caracteres. Suele deberse a una discrepancia de codificación de caracteres o de tipo.","cause":"Escribir un emoji/carácter de 4 bytes (utf8mb4) en una columna latin1, introducir texto no numérico en una columna numérica, un charset de conexión incorrecto o enviar un valor de un tipo distinto del esperado.","fix":"Para problemas de caracteres, convierta la columna/tabla a utf8mb4: ALTER TABLE my_table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci y configure el charset de la conexión en utf8mb4. Valide los tipos en la aplicación para los campos numéricos. Recuerde que el servidor funciona en modo SQL estricto (STRICT); la causa raíz es el tipo/codificación de los datos.","tags":["charset","character","incorrect value","1366","utf8mb4","encoding","type","collation"]},{"code":"1044","title":"Access denied for user to database (acceso denegado del usuario a la base de datos)","severity":"caution","meaning":"Aunque el usuario se autenticó correctamente, no tiene privilegios para operar en la base de datos indicada.","cause":"No hay GRANT sobre la base de datos para el usuario, un nombre de base de datos incorrecto, el privilegio definido para otro host (user@host) o una base de datos inexistente.","fix":"Vea los privilegios del usuario: SHOW GRANTS FOR 'app_user'@'host'. Otorgue el privilegio necesario: GRANT ALL PRIVILEGES ON mydb.* TO 'app_user'@'host'; (GRANT surte efecto de inmediato, no se necesita FLUSH PRIVILEGES). Verifique la correspondencia correcta del host y el nombre de la base de datos. Siguiendo el principio de mínimo privilegio, otorgue solo los derechos necesarios.","tags":["access denied","access","database","database access","1044","grant","privilege","permission"]},{"code":"1264","title":"Out of range value for column (valor fuera de rango para la columna)","severity":"caution","meaning":"Se insertó en una columna numérica o de fecha un valor fuera del rango permitido por el tipo. Este error se genera en modo SQL estricto.","cause":"Superar el límite de tipos como TINYINT/INT (p. ej., 200 en TINYINT o un valor negativo en una columna UNSIGNED), desbordamiento de fecha/número o enviar un valor mayor del esperado.","fix":"Amplíe el tipo de la columna según sea necesario (p. ej., INT en lugar de TINYINT, BIGINT en lugar de INT) o corrija el signo (SIGNED/UNSIGNED). Valide los rangos de valores en la aplicación. Aunque quiera un recorte silencioso (no recomendado), haga que los datos sean compatibles con el tipo en lugar de quitar STRICT de sql_mode.","tags":["out of range","value out of range","1264","value","int","tinyint","unsigned","overflow"]}]},"errors/mssql.json":{"engineId":"mssql","engine":"SQL Server","order":4,"errors":[{"code":"1205","title":"Transaction (Process ID) was deadlocked on lock resources with another process and has been chosen as the deadlock victim (la transacción quedó en deadlock y fue elegida como víctima)","severity":"danger","meaning":"Dos o más transacciones esperan cada una bloqueos que mantiene la otra (un deadlock). Para romper el ciclo, SQL Server elige una transacción como víctima y la revierte.","cause":"Las causas típicas son transacciones que bloquean tablas/filas en distinto orden, transacciones de larga duración, recorridos amplios por falta de índices, lock escalation y alta concurrencia.","fix":"Capture el error 1205 en la aplicación y reintente la transacción automáticamente. Acceda a los objetos en el mismo orden en todas las transacciones y mantenga las transacciones cortas. Examine el grafo de deadlock: use Extended Events (la sesión system_health) o el trace flag 1222 para más detalles. Añada índices adecuados para reducir recorridos y bloqueos. Si es necesario, evalúe el nivel de aislamiento READ COMMITTED SNAPSHOT (RCSI).","tags":["deadlock","lock","locking","victim","rollback","1205","retry"]},{"code":"2627","title":"Violation of PRIMARY KEY / UNIQUE KEY constraint. Cannot insert duplicate key in object (violación de restricción PRIMARY KEY / UNIQUE KEY; clave duplicada)","severity":"caution","meaning":"Intentó insertar o actualizar un valor que ya existe en columnas protegidas por una restricción PRIMARY KEY o UNIQUE. No se aceptan claves duplicadas.","cause":"Insertar el mismo valor único por segunda vez, un UPDATE que colisiona con un valor existente, la ausencia de una comprobación de unicidad o dos peticiones concurrentes que generan la misma fila.","fix":"Compruebe si la fila existe antes de insertarla o use MERGE / IF NOT EXISTS. Para inserciones idempotentes, aplique el patrón \"INSERT ... WHERE NOT EXISTS\". Capture el error y devuelva un mensaje significativo. Si realmente necesita datos duplicados, revise la restricción. Use IDENTITY o SEQUENCE para evitar colisiones al generar claves.","tags":["unique","primary key","uniqueness","duplicate","constraint","constraint violation","2627"]},{"code":"2601","title":"Cannot insert duplicate key row in object with unique index. The duplicate key value is ... (fila con clave duplicada en un índice único)","severity":"caution","meaning":"Un UNIQUE INDEX rechazó una fila por un valor de clave duplicado. 2627 se genera para restricciones, 2601 para índices únicos.","cause":"Volver a escribir un valor existente en columnas protegidas por un UNIQUE INDEX, inserciones concurrentes o una colisión entre filas cubiertas por un índice único filtrado.","fix":"Elimine o actualice el valor duplicado. Si deben tolerarse duplicados, considere la opción IGNORE_DUP_KEY = ON (los duplicados se omiten silenciosamente). Añada una comprobación NOT EXISTS antes de insertar. Si hay un índice filtrado, verifique su cláusula WHERE y la colisión.","tags":["unique index","duplicate key","unique idx","duplicate","2601","index"]},{"code":"547","title":"The INSERT/UPDATE/DELETE statement conflicted with the FOREIGN KEY / CHECK constraint (la sentencia entró en conflicto con la restricción FOREIGN KEY / CHECK)","severity":"caution","meaning":"La sentencia violó una restricción FOREIGN KEY o CHECK. O bien referenció una fila padre que no existe, escribió un valor que incumple una regla CHECK o intentó eliminar una fila padre que aún está referenciada.","cause":"FK: la fila padre referenciada por la fila hija insertada no existe. DELETE/UPDATE: hay filas hijas que dependen de la fila padre que se elimina/modifica. CHECK: el valor de la columna no cumple la condición definida.","fix":"Inserte primero la fila padre y luego la fila hija. Para eliminaciones, elimine primero las filas dependientes o defina ON DELETE CASCADE donde corresponda. Para una violación de CHECK, haga que el valor cumpla la regla. Identifique la restricción violada a partir del nombre de la restricción en el texto del error.","tags":["foreign key","fk","check","constraint","constraint violation","referential","547"]},{"code":"18456","title":"Login failed for user (error de inicio de sesión del usuario)","severity":"danger","meaning":"Se rechazó un intento de inicio de sesión durante la autenticación. El nombre de usuario/contraseña puede ser incorrecto, la cuenta puede estar deshabilitada o el usuario puede no tener acceso a la base de datos.","cause":"Contraseña incorrecta, login inexistente, cuenta deshabilitada/bloqueada, uso de un login de SQL cuando solo está habilitada la autenticación de Windows o sin acceso a la base de datos predeterminada. El código State del texto del error identifica la causa real (State 5: el login no existe, State 7: login deshabilitado y además contraseña incorrecta, State 8: contraseña incorrecta (lo más común), State 18: la contraseña debe cambiarse, State 38: no se puede abrir la base de datos indicada, State 58: intento de login de SQL con el servidor en modo de autenticación solo Windows).","fix":"Encuentre la causa raíz a partir del valor State en el error log de SQL Server (por seguridad, los detalles no se envían al cliente). Verifique el nombre de usuario/contraseña. Compruebe que el servidor funcione en 'SQL Server and Windows Authentication Mode' (modo mixto); si lo cambió, reinicie el servicio. Verifique el mapeo de usuario del login y el permiso de acceso en la base de datos de destino.","tags":["login","session","authentication","authentication failed","password","18456","state"]},{"code":"8152","title":"String or binary data would be truncated (los datos de cadena o binarios se truncarían)","severity":"caution","meaning":"Intentó escribir datos más largos que la longitud definida de una columna. SQL Server detiene la sentencia para evitar la pérdida de datos (2628 es la versión más reciente del mismo error que indica la columna/valor).","cause":"Por ejemplo, escribir 15 caracteres en una columna VARCHAR(10), una columna de destino más estrecha que el origen, una discrepancia de tipo/longitud de datos o un tamaño de campo insuficiente en ETL/transferencias.","fix":"Amplíe la columna para que quepan los datos (ALTER TABLE ... ALTER COLUMN) o acorte los datos. En SQL Server 2019+, use el error 2628, que muestra la tabla/columna/valor que desbordó; en versiones anteriores, revise las columnas una a una con LEN() para encontrar la fila problemática. En las transferencias, alinee las longitudes de los esquemas de origen y destino.","tags":["truncation","truncate","overflow","varchar","length","column length","8152","2628"]},{"code":"2628","title":"String or binary data would be truncated in table, column ...; truncated value (los datos se truncarían en la tabla y columna indicadas)","severity":"caution","meaning":"La versión mejorada de 8152 (SQL Server 2019 y posteriores). Indica explícitamente qué tabla, qué columna y qué valor se truncarían.","cause":"Escribir un valor de texto/binario más largo que la longitud definida de la columna de destino. Normalmente, una discrepancia de esquema o datos de entrada de longitud inesperada.","fix":"Mire el nombre de la tabla y de la columna en el texto del error; amplíe la columna o acorte el valor de origen. En SQL Server 2019, 2628 se genera por defecto solo con nivel de compatibilidad 150+; en niveles inferiores, habilítelo con ALTER DATABASE SCOPED CONFIGURATION SET VERBOSE_TRUNCATION_WARNINGS = ON o con el trace flag 460. Desde SQL Server 2016 SP2 CU6 y 2017 CU12, el trace flag 460 ofrece el mismo mensaje detallado. Alinee las longitudes de los campos con el destino en la capa de transferencia.","tags":["truncation","truncate","overflow","column","column name","length","2628","460"]},{"code":"1222","title":"Lock request time out period exceeded (se superó el tiempo de espera de la solicitud de bloqueo)","severity":"caution","meaning":"El timeout de una consulta expiró mientras esperaba adquirir un bloqueo. La solicitud se canceló porque otra transacción mantuvo bloqueado el recurso necesario demasiado tiempo.","cause":"Superar el tiempo de espera establecido con SET LOCK_TIMEOUT, transacciones bloqueantes de larga duración, transacciones abiertas sin commit, recorridos largos y conflictos de bloqueo.","fix":"Encuentre al bloqueador: examine sys.dm_exec_requests, sys.dm_tran_locks y la columna blocking_session_id, o use sp_who2 / sp_whoisactive. Acorte las transacciones largas y confírmelas. Ajuste LOCK_TIMEOUT si es necesario. En sistemas con muchas lecturas, reduzca los conflictos lector-escritor con RCSI/snapshot isolation.","tags":["lock timeout","lock wait timeout","blocking","blocked session","1222","lock_timeout"]},{"code":"9002","title":"The transaction log for database is full due to ... (el log de transacciones de la base de datos está lleno)","severity":"danger","meaning":"El archivo de log de transacciones de la base de datos está lleno y no se pueden registrar nuevas transacciones. La base de datos rechaza las escrituras y puede comportarse como si fuera de solo lectura.","cause":"Sin backups de log en el modelo de recuperación FULL, una transacción abierta durante mucho tiempo, retraso de replicación/AlwaysOn o el log incapaz de crecer (autogrow deshabilitado o disco lleno). log_reuse_wait_desc muestra el motivo.","fix":"Compruebe log_reuse_wait_desc en sys.databases. Si es LOG_BACKUP en el modelo FULL, haga un backup de log (BACKUP LOG). Si es ACTIVE_TRANSACTION, encuentre la transacción abierta y haga commit/rollback (DBCC OPENTRAN). Libere espacio en disco o corrija la configuración de autogrow del archivo de log. Si el log realmente ha crecido, redúzcalo con DBCC SHRINKFILE después del backup. Si no necesita la cadena de logs, revise el modelo de recuperación.","tags":["transaction log","log is full","log full","9002","backup log","recovery model"]},{"code":"1105","title":"Could not allocate space for object in database because the filegroup is full (no se puede asignar espacio: el filegroup está lleno)","severity":"danger","meaning":"El filegroup que contiene el archivo de datos está lleno y no se puede asignar espacio para datos nuevos. Las inserciones/actualizaciones fallan.","cause":"El archivo de datos no puede crecer (autogrowth deshabilitado o MAXSIZE alcanzado), no hay espacio libre en disco, el filegroup PRIMARY está agotado o un límite de tamaño de archivo.","fix":"Libere espacio en disco o añada un disco/archivo nuevo. Habilite autogrowth en el archivo de datos o amplíelo (ALTER DATABASE ... MODIFY FILE). Añada un archivo nuevo al filegroup. Archive y purgue los datos innecesarios/antiguos. Para tempdb, revise los archivos de tempdb y su disco.","tags":["filegroup full","file full","allocate space","disk","autogrow","1105"]},{"code":"208","title":"Invalid object name (nombre de objeto no válido)","severity":"safe","meaning":"No se encontró la tabla, vista u objeto referenciado en la consulta. SQL Server no pudo resolver el nombre indicado.","cause":"Un nombre de objeto mal escrito, contexto de base de datos/esquema incorrecto (un esquema distinto de dbo), el objeto realmente no existe o aún no se ha creado, o falta el nombre de dos partes (schema.table).","fix":"Escriba correctamente el nombre del objeto y el esquema; use el nombre calificado (p. ej., dbo.Customers). Asegúrese de estar conectado a la base de datos correcta (USE). Compruebe que el objeto existe: SELECT * FROM sys.objects WHERE name = 'YourObjectName'. Asegúrese de que las tablas temporales se crean en la misma sesión.","tags":["invalid object","invalid object name","table not found","table does not exist","schema","app schema","208"]},{"code":"229","title":"The SELECT/EXECUTE permission was denied on the object (se denegó el permiso SELECT/EXECUTE sobre el objeto)","severity":"caution","meaning":"El usuario no tiene permiso para realizar la operación solicitada (SELECT, INSERT, EXECUTE, etc.) sobre el objeto. Se denegó el acceso por motivos de seguridad.","cause":"No se otorgó el permiso necesario al usuario/rol, propiedad de esquema incorrecta (ownership chaining roto), un DENY explícito sobre el objeto o el usuario no es miembro de un rol suficiente.","fix":"Otorgue el permiso necesario: GRANT SELECT ON dbo.MyTable TO UserName (o GRANT EXECUTE ON dbo.MyProcedure TO ...). Añada el usuario a un rol adecuado (p. ej., db_datareader). Elimine cualquier DENY explícito. Organice los permisos según el principio de mínimo privilegio. Audite los permisos existentes con sys.database_permissions.","tags":["permission denied","access denied","grant","privilege","security","security permissions","229"]},{"code":"4060","title":"Cannot open database requested by the login. The login failed (no se puede abrir la base de datos solicitada; falló el inicio de sesión)","severity":"danger","meaning":"La autenticación puede tener éxito, pero la conexión se rechaza porque no se puede abrir la base de datos solicitada. Suele verse junto con 18456.","cause":"La base de datos no existe, está OFFLINE/SINGLE_USER/RESTORING/SUSPECT, el login no tiene mapeo de usuario en esa base de datos o la base de datos predeterminada de la cadena de conexión es inaccesible.","fix":"Verifique que la base de datos existe y está ONLINE: SELECT name, state_desc FROM sys.databases. Cree un usuario para el login en la base de datos y otorgue permisos. Compruebe el valor Initial Catalog/Database en la cadena de conexión. Si la base de datos está RESTORING/SUSPECT, siga los pasos de reparación/restauración.","tags":["cannot open database","database cannot be opened","4060","login","state","offline"]},{"code":"233","title":"A connection was successfully established with the server, but then an error occurred (no process is on the other end of the pipe) (conexión establecida, pero luego se produjo un error)","severity":"danger","meaning":"El cliente llegó al servidor, pero la conexión se cortó durante el handshake. Suele deberse a la configuración de Named Pipes/TCP, a la configuración de protocolos o a un rechazo del lado del servidor. Relacionado con 10054 (conexión cerrada a la fuerza).","cause":"Protocolos Shared Memory/Named Pipes/TCP deshabilitados, conexiones remotas no permitidas, la cuenta no puede iniciar sesión (ver 18456), un firewall/corte de red o el servidor que cierra la conexión a la fuerza (10054).","fix":"Habilite TCP/IP y, si es necesario, Named Pipes en SQL Server Configuration Manager y reinicie el servicio. Verifique que se permiten las conexiones remotas. Abra el puerto de SQL (por defecto 1433) en el firewall. Para problemas de autenticación, examine el código State de 18456 en el error log. Compruebe la estabilidad de la red y la configuración de TLS/cifrado.","tags":["connection","connection error","pipe","10054","tcp","named pipes","233","network"]},{"code":"823","title":"The operating system returned error to SQL Server during a read/write at offset in file (I/O error) (error de E/S del sistema operativo al leer/escribir un archivo)","severity":"danger","meaning":"SQL Server recibió un error de E/S a nivel del sistema operativo al leer o escribir un archivo de datos/log. Es una advertencia grave de integridad que apunta a un problema del hardware de disco o del subsistema de almacenamiento.","cause":"Sectores de disco defectuosos, una controladora/driver defectuoso, problemas de almacenamiento/SAN, errores del sistema de archivos o una página que no se puede leer en absoluto (error de E/S grave). Normalmente relacionado con el hardware.","fix":"Haga de inmediato un backup válido (si es posible). Examine el registro de eventos de Windows y los logs del almacenamiento, y pruebe el estado del hardware/disco. Ejecute DBCC CHECKDB para comprobar la integridad. Revise la tabla suspect_pages (msdb.dbo.suspect_pages). Si hay corrupción, restaure desde un backup válido o realice una restauración a nivel de página. Sustituya/repare el hardware.","tags":["io error","i/o error","disk","corruption","data corruption","823","checkdb"]},{"code":"824","title":"SQL Server detected a logical consistency-based I/O error (page corruption) (error de E/S de consistencia lógica (corrupción de página))","severity":"danger","meaning":"SQL Server leyó una página, pero su contenido es lógicamente inconsistente (checksum incorrecto, torn page o ID de página incorrecto). La página de datos está corrupta.","cause":"Errores del subsistema de almacenamiento, cortes de energía repentinos/torn pages, problemas de controladora o driver, corrupción silenciosa detectada por PAGE_VERIFY CHECKSUM. Como en 823, casi siempre relacionado con el hardware/almacenamiento.","fix":"Ejecute DBCC CHECKDB para determinar el alcance de la corrupción. Restaure desde el último backup válido o repare las páginas afectadas con una restauración a nivel de página. Examine msdb.dbo.suspect_pages. Asegúrese de que PAGE_VERIFY = CHECKSUM esté habilitado. Inspeccione el hardware/almacenamiento. REPAIR_ALLOW_DATA_LOSS debe ser el último recurso y provoca pérdida de datos.","tags":["page corruption","corrupt page","consistency","824","checkdb","checksum","torn page"]},{"code":"701","title":"There is insufficient system memory in resource pool to run this query (memoria del sistema insuficiente en el resource pool)","severity":"danger","meaning":"SQL Server no pudo asignar desde el resource pool correspondiente la memoria necesaria para ejecutar la consulta. La operación falló por presión de memoria.","cause":"Memoria del servidor insuficiente, un valor bajo de max server memory, consultas que consumen mucha memoria (sorts/hashes grandes), una fuga de memoria, muchas solicitudes concurrentes o límites de pools de Resource Governor.","fix":"Revise el parámetro max server memory y proporcione suficiente RAM física. Optimice las consultas que consumen mucha memoria (índices adecuados, menos sorts/hashes). Supervise la memoria: sys.dm_os_memory_clerks, DBCC MEMORYSTATUS, contadores de memoria de Perfmon. Ajuste los límites de los pools de Resource Governor. Reduzca la concurrencia si es necesario.","tags":["memory","memory pressure","insufficient memory","resource pool","701","oom"]},{"code":"845","title":"Time-out occurred while waiting for buffer latch (timeout al esperar un buffer latch)","severity":"danger","meaning":"Se produjo un timeout al esperar un buffer latch sobre una página de datos. SQL Server no pudo adquirir en un tiempo razonable el bloqueo interno de corta duración (latch) necesario para acceder a una página; suele ser señal de E/S intensa o presión de memoria.","cause":"Un subsistema de disco lento/sobrecargado, páginas leídas constantemente desde disco por presión de memoria, problemas de hardware, paginación excesiva o un cuello de botella de almacenamiento.","fix":"Mida la latencia de E/S de disco (sys.dm_io_virtual_file_stats, latencia de disco en Perfmon). Compruebe el rendimiento del almacenamiento y el estado del hardware. Revise si la memoria es suficiente y el parámetro max server memory. Examine los tipos de espera (sys.dm_os_wait_stats, esperas PAGEIOLATCH). Elimine el cuello de botella de almacenamiento; proporcione discos más rápidos/más RAM si es necesario.","tags":["buffer latch","latch timeout","845","io","pageiolatch","memory","memory pressure"]},{"code":"3201","title":"Cannot open backup device. Operating system error (backup device error) (no se puede abrir el dispositivo de backup)","severity":"caution","meaning":"No se pudo abrir el dispositivo/archivo de backup de destino durante el backup o la restauración. SQL Server no pudo acceder a la ruta indicada.","cause":"Una ruta de carpeta incorrecta/inexistente, la cuenta de servicio de SQL Server sin permiso de escritura en la carpeta de destino, sin acceso a un recurso compartido de red (UNC), un disco lleno o el archivo bloqueado por otro proceso.","fix":"Verifique que la ruta del backup existe y está bien escrita. Otorgue a la cuenta de servicio de SQL Server permiso de escritura en la carpeta de destino. Para recursos compartidos UNC, compruebe el acceso de la cuenta de servicio. Asegúrese de que haya suficiente espacio libre en disco. Para una ruta local, verifique que la letra de unidad sea visible desde el servidor (no una unidad del cliente).","tags":["backup device","backup file","backup","restore","3201","permission denied","permission","os error"]}]},"errors/mongodb.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"errors":[{"code":"E11000","title":"E11000 duplicate key error collection (error de clave duplicada)","severity":"caution","meaning":"Una inserción o actualización intentó escribir un valor que ya existe en la colección en campos protegidos por un índice único. MongoDB rechaza la clave duplicada.","cause":"Una colisión de _id, escribir el mismo valor por segunda vez en un campo con índice único, una condición de carrera durante un upsert o valores null que colisionan en más de un documento en un índice único (índice no sparse).","fix":"Compruebe si el documento existe antes de escribir o use un upsert: db.collection.updateOne(filter, {$set:{...}}, {upsert:true}). Mire el nombre del índice y el valor dup key en el mensaje de error. Si la unicidad no es realmente necesaria, elimine el índice; si el problema son los null, use un índice partial/sparse. En la aplicación, capture el error y reintente o infórmelo al usuario.","tags":["duplicate key","duplicate key","unique index","unique","e11000","upsert","_id"]},{"code":"11000","title":"DuplicateKey (código de error 11000)","severity":"caution","meaning":"El código numérico del error de clave duplicada (se devuelve junto con el texto E11000). Una operación de escritura violó una restricción de índice único.","cause":"Volver a escribir el mismo campo único, upserts concurrentes que generan la misma clave o documentos duplicados en una escritura masiva.","fix":"Capture el código de error 11000 en el código de la aplicación e implemente un comportamiento idempotente (actualizar si existe, insertar en caso contrario). En operaciones masivas, use ordered:false para permitir que se escriban los documentos distintos de los duplicados. Revise la regla de unicidad y la definición del índice.","tags":["duplicatekey","11000","duplicate","unique","unique","bulk write","write error"]},{"code":"13","title":"Unauthorized (código de error 13)","severity":"caution","meaning":"El usuario autenticado no tiene privilegios para la operación o el recurso que intenta usar. El usuario ha iniciado sesión, pero sus roles no cubren esta acción.","cause":"No se otorgó al usuario el rol necesario (p. ej., readWrite, dbAdmin), la operación se ejecuta contra la base de datos equivocada o los privilegios son insuficientes para un comando administrativo. Configuración de RBAC incompleta.","fix":"Compruebe los roles del usuario: db.getUser(\"app_user\"). Otorgue el rol necesario: db.grantRolesToUser(\"app_user\", [{role:\"readWrite\", db:\"mydb\"}]). Asegúrese de que la operación se ejecuta en la base de datos correcta. Asigne un rol integrado o personalizado adecuado siguiendo el principio de mínimo privilegio. Verifique el valor authSource en la cadena de conexión.","tags":["unauthorized","unauthorized","authorization","role","role","rbac","13","permission"]},{"code":"18","title":"AuthenticationFailed (código de error 18)","severity":"caution","meaning":"Falló la autenticación. El nombre de usuario, la contraseña o las credenciales proporcionadas no son válidos; el usuario no pudo iniciar sesión.","cause":"Nombre de usuario/contraseña incorrectos, base de datos authSource incorrecta, mecanismo de autenticación no coincidente (SCRAM-SHA-1/SCRAM-SHA-256) o el usuario no está definido en la base de datos indicada.","fix":"Verifique el nombre de usuario y la contraseña. Indique el authSource correcto en la cadena de conexión (la base de datos donde se creó el usuario, normalmente admin): ...?authSource=admin. Compruebe que el mecanismo de autenticación es compatible con el servidor. Asegúrese de que el usuario se creó en la base de datos correcta. Codifique la contraseña en URL si contiene caracteres especiales.","tags":["authentication failed","authentication","password","password","authsource","18","scram"]},{"code":"11600","title":"InterruptedAtShutdown (código de error 11600)","severity":"danger","meaning":"La operación se interrumpió porque el servidor comenzó a apagarse. La operación en curso no pudo completarse porque el proceso mongod se estaba deteniendo.","cause":"Un reinicio/apagado de mongod, un apagado durante un stepdown del primario del replica set, mantenimiento planificado, una caída o el proceso terminado por el OOM killer.","fix":"Espere a que el servidor vuelva a estar activo y reintente la operación. En la aplicación, capture los errores transitorios y reintente automáticamente; habilite retryable writes. Para apagados inesperados, revise los logs de mongod y los logs del sistema (OOM, caída). Use un replica set para alta disponibilidad.","tags":["interrupted","shutdown","shutdown","downtime","11600","retry","replica set"]},{"code":"50","title":"MaxTimeMSExpired (código de error 50)","severity":"caution","meaning":"El servidor canceló la operación porque superó el límite de tiempo establecido con maxTimeMS. La consulta no terminó en el tiempo asignado.","cause":"Consultas de larga duración, recorridos de colección (COLLSCAN) por falta de índices, grandes volúmenes de datos, operaciones de agregación pesadas o alta carga del servidor.","fix":"Optimice la consulta: inspeccione el plan con explain() y añada un índice adecuado. Si realmente se necesita más tiempo, aumente maxTimeMS. Simplifique las agregaciones pesadas o filtre pronto con $match. Divida los resultados con paginación. Compruebe la carga del servidor y la capacidad de recursos.","tags":["maxtimems","timeout","timeout","50","slow query","slow query","index"]},{"code":"251","title":"NoSuchTransaction (código de error 251)","severity":"caution","meaning":"No se encontró la transacción multidocumento referenciada. La transacción ya terminó, agotó el tiempo o se usó un número de transacción no válido.","cause":"La transacción superó transactionLifetimeLimitSeconds y se abortó automáticamente, la sesión terminó, un txnNumber incorrecto/obsoleto o se intentó continuar una transacción ya abortada tras un error.","fix":"Reinicie la transacción desde el principio (con una sesión nueva). Mantenga las transacciones cortas; diséñelas para que terminen dentro de la vida útil predeterminada (60 s). Para errores con la etiqueta TransientTransactionError, reintente toda la transacción de forma segura. Si se necesitan transacciones largas, revise el parámetro transactionLifetimeLimitSeconds.","tags":["nosuchtransaction","transaction","transaction","251","session","abort","retry"]},{"code":"112","title":"WriteConflict (código de error 112)","severity":"caution","meaning":"Un conflicto de escritura que se produce cuando dos operaciones concurrentes intentan modificar los mismos documentos. Por el control de concurrencia optimista, WiredTiger revierte una de las operaciones.","cause":"Escrituras con alta concurrencia sobre los mismos documentos, actualizaciones en conflicto durante transacciones de larga duración, contención intensa sobre un documento caliente.","fix":"Reintente la operación; WriteConflict suele ser transitorio y se resuelve con un reintento (lleva la etiqueta TransientTransactionError). Mantenga las transacciones cortas y reduzca el número de documentos afectados. Revise el modelo de datos para reducir la contención sobre el mismo documento. Implemente reintentos con backoff exponencial en la aplicación.","tags":["writeconflict","write conflict","112","concurrency","concurrency","retry","wiredtiger"]},{"code":"133","title":"FailedToSatisfyReadPreference (código de error 133)","severity":"danger","meaning":"No se encontró ningún miembro adecuado del replica set que satisfaga la read preference indicada (p. ej., primary, secondary). El driver no pudo seleccionar un nodo al que enviar la solicitud.","cause":"Los miembros del replica set no son accesibles/están caídos, no hay primario mientras hay una elección en curso, una partición de red, ningún miembro cumple los criterios del tag set o no se puede alcanzar la mayoría (quórum).","fix":"Compruebe el estado del replica set: rs.status(). Verifique el estado de los nodos y la conectividad de red. Espere a que termine la elección del primario y reintente. Revise su configuración de read preference; si es necesario, use una preferencia más flexible (p. ej., primaryPreferred). Si usa tag sets, asegúrese de que exista un miembro que coincida.","tags":["read preference","readpreference","133","replica set","election","secondary","primary"]},{"code":"6","title":"HostUnreachable (código de error 6)","severity":"danger","meaning":"El driver/cliente no pudo alcanzar el servidor MongoDB de destino (host). No se pudo establecer ninguna conexión a nivel de red.","cause":"El servidor está caído, host/puerto incorrectos, un firewall que bloquea, un problema de resolución DNS, un corte de red o una configuración de bindIp que excluye al cliente.","fix":"Compruebe que el nombre de host y el puerto son correctos (por defecto 27017). Verifique que el servicio mongod está en ejecución. Pruebe el firewall y el acceso de red (ping/telnet). Asegúrese de que el parámetro bindIp de mongod.conf permite el acceso del cliente. Verifique los registros DNS/SRV (para conexiones mongodb+srv).","tags":["hostunreachable","host","connection","network","network","6","firewall","bindip"]},{"code":"89","title":"NetworkTimeout (código de error 89)","severity":"danger","meaning":"Una operación de red durante la comunicación con el servidor no terminó en el tiempo asignado y agotó el tiempo de espera. La conexión se estableció, pero la respuesta no llegó a tiempo.","cause":"Una red lenta o inestable, un servidor sobrecargado, una operación de larga duración que retrasa la respuesta, valores de timeout de socket/conexión demasiado bajos o congestión de red.","fix":"Mida la latencia y la estabilidad de la red. Compruebe la carga y los recursos del servidor. Configure connectTimeoutMS y socketTimeoutMS en el driver con valores realistas. Capture los errores transitorios y reintente. Optimice las consultas lentas para acortar los tiempos de respuesta.","tags":["networktimeout","network timeout","89","timeout","socket","network","retry"]},{"code":"262","title":"ExceededTimeLimit (código de error 262)","severity":"caution","meaning":"La operación superó el límite de tiempo permitido en el lado del servidor y se terminó. Superar maxTimeMS o límites de tiempo internos puede causar este error.","cause":"Una consulta/comando de larga duración, ejecución lenta por falta de un índice, agregación pesada, recorrido de un conjunto de datos grande o alta carga del servidor.","fix":"Inspeccione la consulta con explain() y añada un índice adecuado. Divida la operación en fragmentos más pequeños. Configure maxTimeMS según la necesidad real. Reduzca los datos al principio del pipeline de agregación con $match/$project. Verifique que los recursos del servidor sean suficientes.","tags":["exceededtimelimit","time limit","262","timeout","maxtimems","slow","index"]},{"code":"292","title":"QueryExceededMemoryLimitNoDiskUseAllowed (código de error 292)","severity":"caution","meaning":"Una consulta o etapa de agregación superó el límite de memoria permitido (por defecto 100 MB) y falló porque no se permitía escribir en disco. Suele verse en etapas de sort o group.","cause":"Ordenación en memoria sobre datos grandes sin índice, etapas de agregación $group/$sort grandes, conjuntos de resultados intermedios muy grandes, la opción allowDiskUse:false.","fix":"Permita el volcado a disco en la agregación con la opción allowDiskUse:true: db.collection.aggregate(pipeline, {allowDiskUse:true}). Reduzca el uso de memoria indexando los campos ordenados. Filtre pronto el pipeline con $match para reducir los datos procesados. Pagine los resultados si es necesario. En MongoDB 6.0+, el uso de disco está habilitado por defecto en la mayoría de los casos.","tags":["memory limit","memory limit","292","allowdiskuse","sort","aggregation","group"]},{"code":"26","title":"NamespaceNotFound (código de error 26)","severity":"safe","meaning":"No se encontró el namespace referenciado (database.collection). La colección o el índice indicado no existe.","cause":"Un nombre de colección mal escrito, una colección aún no creada, el contexto de base de datos equivocado o un intento de drop/operación de índice sobre una colección inexistente.","fix":"Verifique el nombre de la colección y la base de datos: show collections. Recuerde que los nombres distinguen mayúsculas de minúsculas. MongoDB crea una colección automáticamente con la primera escritura; si es necesario, créela explícitamente con db.createCollection(). Para operaciones como drop, compruebe antes que la colección existe.","tags":["namespacenotfound","namespace","collection missing","26","collection","not found","not found"]},{"code":"43","title":"CursorNotFound (código de error 43)","severity":"caution","meaning":"El cursor del lado del servidor ya no existe. Cuando el cliente solicita el siguiente lote (getMore), se devuelve el error porque no se encuentra el cursor.","cause":"El cursor agotó el tiempo (por defecto 10 minutos de inactividad), una espera demasiado larga entre lotes, un reinicio del servidor o el cursor se cerró explícitamente.","fix":"Procese los resultados del cursor más rápido; no espere mucho entre lotes. Para operaciones de larga duración, use con cuidado la opción noCursorTimeout (mongosh: cursor.noCursorTimeout(); PyMongo: no_cursor_timeout=True) y cierre siempre el cursor; un cursor dentro de una sesión se cierra al expirar la sesión (localLogicalSessionTimeoutMinutes, por defecto 30 min) incluso con esta opción. Para conjuntos de resultados grandes, itere con un batchSize menor o use paginación basada en rangos. Reinicie la operación y vuelva a consultar.","tags":["cursornotfound","cursor","cursor","43","getmore","timeout","batch","pagination"]},{"code":"211","title":"KeyNotFound (código de error 211)","severity":"caution","meaning":"No se encontró una clave/valor esperado. Suele indicar una inconsistencia relacionada con la gestión interna de claves, las claves de sesión o la colección oplog/keys.","cause":"Claves de firma ausentes/eliminadas en la colección admin.system.keys, problemas de sincronización de reloj en el replica set/sharded cluster, problemas de keyfile/rotación de claves o estado interno corrupto.","fix":"Verifique la sincronización de reloj (NTP) entre los miembros del replica set. Compruebe el contenido de admin.system.keys y la generación de claves. Revise el estado del clúster (rs.status(), sh.status()). Si persiste, analice los logs de mongod y revise la configuración de keyfile/identidad. Asegúrese de que el primario funcione correctamente para que puedan regenerarse las claves.","tags":["keynotfound","key","211","keys","session","ntp","cluster","cluster"]}]},"errors/redis.json":{"engineId":"redis","engine":"Redis","order":6,"errors":[{"code":"WRONGTYPE","title":"WRONGTYPE Operation against a key holding the wrong kind of value (operación contra una clave con un tipo de valor incorrecto)","severity":"caution","meaning":"Se ejecutó un comando contra una clave que no corresponde al tipo de datos de esa clave. Por ejemplo, aplicar un comando de lista (LPUSH) o de hash (HSET) a una clave que contiene un string.","cause":"La clave es de un tipo distinto del esperado, el mismo nombre de clave se usa con distintos tipos de datos, confusión de tipos en la lógica de la aplicación o una clave antigua/residual que se sobrescribe.","fix":"Compruebe el tipo de la clave: TYPE mykey. Elija el comando según el tipo real de la clave. Distinga los tipos en la nomenclatura de claves (p. ej., un set para user:1:tags, un string para user:1:name). Si es necesario, elimine la clave antigua (DEL) y vuelva a crearla con el tipo correcto. Imponga la coherencia de tipos en la capa de aplicación.","tags":["wrongtype","wrong type","type","data type","key","key"]},{"code":"OOM","title":"OOM command not allowed when used memory > 'maxmemory' (comando no permitido: memoria usada > maxmemory)","severity":"danger","meaning":"La memoria usada por Redis ha alcanzado el límite maxmemory y los comandos de escritura se rechazan porque la política de eviction no puede liberar espacio para datos nuevos. La memoria está llena; no es posible escribir.","cause":"Se supera el límite maxmemory, está seleccionada la política noeviction (nunca se elimina ninguna clave), datos sin TTL que siguen creciendo, una fuga de memoria o RAM insuficiente para el límite.","fix":"Inspeccione el uso de memoria: INFO memory y MEMORY USAGE. Elija una política de eviction adecuada (p. ej., allkeys-lru o volatile-lru): CONFIG SET maxmemory-policy allkeys-lru. Defina TTL en las claves para que se limpien automáticamente. Aumente maxmemory/RAM. Encuentre y reduzca las claves grandes (redis-cli --bigkeys). Purgue los datos innecesarios.","tags":["oom","out of memory","memory","maxmemory","eviction","memory","danger"]},{"code":"NOAUTH","title":"NOAUTH Authentication required (se requiere autenticación)","severity":"caution","meaning":"Redis requiere autenticación, pero el cliente intentó ejecutar un comando antes de autenticarse. Primero debe iniciar sesión con AUTH.","cause":"Hay una contraseña requirepass/ACL configurada en el servidor, el cliente no envía el comando AUTH, falta la contraseña en la cadena de conexión o no se repite AUTH tras restablecerse la conexión.","fix":"Autentíquese antes de ejecutar comandos: AUTH password (con ACL, AUTH app_user password). Añada la contraseña al cliente/cadena de conexión. Si usa un pool de conexiones, asegúrese de que se ejecute AUTH en cada conexión nueva. Almacene la contraseña de forma segura (variable de entorno/gestor de secretos).","tags":["noauth","authentication","authentication","auth","password","requirepass","acl"]},{"code":"WRONGPASS","title":"WRONGPASS invalid username-password pair or user is disabled (par usuario-contraseña no válido o usuario deshabilitado)","severity":"caution","meaning":"El par usuario/contraseña indicado en el comando AUTH no es válido o el usuario de ACL está deshabilitado. Se rechazó la autenticación.","cause":"Contraseña incorrecta, nombre de usuario de ACL incorrecto, el usuario está deshabilitado en la ACL, el valor de requirepass ha cambiado o un problema de escape/codificación en la contraseña.","fix":"Introduzca el nombre de usuario y la contraseña correctos. Inspeccione el usuario de ACL: ACL LIST y ACL GETUSER app_user. Si el usuario está deshabilitado, habilítelo (ACL SETUSER app_user on ...). Verifique la contraseña configurada con requirepass (CONFIG GET requirepass). Al restablecer la contraseña, asegúrese de actualizar todos los clientes.","tags":["wrongpass","password","invalid password","auth","acl","user","invalid"]},{"code":"READONLY","title":"READONLY You can't write against a read only replica (no se puede escribir en una réplica de solo lectura)","severity":"caution","meaning":"Se envió un comando de escritura a un nodo réplica (slave). Las réplicas son de solo lectura por defecto; las escrituras solo pueden realizarse en el master.","cause":"El cliente se conecta a una réplica en lugar de al master, el antiguo master pasó a ser réplica tras un failover, un enrutamiento erróneo al nodo equivocado en Sentinel/Cluster o un balanceador de carga que reenvía escrituras a una réplica.","fix":"Dirija las solicitudes de escritura al nodo master. Si usa Sentinel, consulte el master actual a Sentinel (SENTINEL get-master-addr-by-name). En Cluster, escriba en el master del shard correcto. Configure la separación de lectura/escritura en la biblioteca cliente. Tras un failover, actualice las conexiones para que apunten al nuevo master.","tags":["readonly","read only","replica","slave","write","write","master","failover"]},{"code":"MASTERDOWN","title":"MASTERDOWN Link with MASTER is down and replica-serve-stale-data is set to 'no' (enlace con el master caído)","severity":"danger","meaning":"El enlace entre la réplica y el master está caído y, como replica-serve-stale-data está en 'no', la réplica se niega a responder solicitudes con datos obsoletos.","cause":"El nodo master se cayó o no es accesible, un corte/partición de red, el enlace de replicación master-réplica se interrumpió o el master no puede responder por una carga elevada.","fix":"Compruebe el estado del nodo master y la conectividad de red. Inspeccione el estado de la replicación: INFO replication. Vuelva a levantar el master o deje que se produzca un failover mediante Sentinel/Cluster. Si es aceptable servir datos obsoletos, considere replica-serve-stale-data yes (tenga en cuenta el riesgo de consistencia). Use Sentinel/Cluster para alta disponibilidad.","tags":["masterdown","master down","replication","replication","stale","failover","sentinel","danger"]},{"code":"CLUSTERDOWN","title":"CLUSTERDOWN The cluster is down / Hash slot not served (clúster caído / hash slot no atendido)","severity":"danger","meaning":"Redis Cluster no está completamente operativo. Algunos hash slots no los sirve ningún nodo, por lo que el clúster rechaza las solicitudes.","cause":"No hay suficientes nodos master accesibles, uno o más slots sin asignar/sin cubrir, pérdida de la mayoría (quórum), caídas de nodos o algunos slots inaccesibles mientras cluster-require-full-coverage es yes.","fix":"Compruebe el estado del clúster: CLUSTER INFO y CLUSTER NODES. Vuelva a levantar los masters caídos o deje que se promuevan las réplicas. Reasigne los slots sin asignar (redis-cli --cluster fix o CLUSTER ADDSLOTS). Verifique que todos los slots estén cubiertos. Si es aceptable el funcionamiento parcial, considere cluster-require-full-coverage no.","tags":["clusterdown","cluster","cluster","hash slot","slot","quorum","coverage","danger"]},{"code":"LOADING","title":"LOADING Redis is loading the dataset in memory (Redis está cargando el conjunto de datos en memoria)","severity":"caution","meaning":"Redis está arrancando y cargando el conjunto de datos (RDB/AOF) desde disco a memoria. La mayoría de los comandos se rechazan temporalmente hasta que termina la carga.","cause":"El servidor acaba de arrancar, se está cargando un archivo RDB/AOF grande, una réplica está haciendo una sincronización completa desde el master o un proceso de recuperación tras un reinicio.","fix":"Espere a que termine la carga; la duración depende del tamaño de los datos. Supervise el estado: INFO persistence (espere mientras loading:1; listo cuando sea 0). En la aplicación, capture este error transitorio y reintente a intervalos cortos. Para acortar el tiempo de carga, revise el conjunto de datos/la estrategia de persistencia (RDB frente a AOF). Proporcione suficiente RAM y discos rápidos.","tags":["loading","loading","startup","startup","rdb","aof","sync","persistence"]},{"code":"BUSY","title":"BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE (Redis está ocupado ejecutando un script)","severity":"caution","meaning":"Se está ejecutando un script Lua o una function de larga duración que ha superado busy-reply-threshold (llamado lua-time-limit antes de Redis 7; por defecto 5000 ms). Como Redis es de un solo hilo, no puede procesar otros comandos durante ese tiempo; solo se acepta SCRIPT KILL (FUNCTION KILL para functions) o SHUTDOWN NOSAVE.","cause":"Un bucle infinito o un script Lua/function de muy larga duración, cálculo pesado, un script que recorre datos grandes o una llamada EVAL mal escrita.","fix":"Si el script no ha modificado datos, deténgalo: SCRIPT KILL. Si el script ya ha realizado escrituras, SCRIPT KILL no funciona; como último recurso para preservar la consistencia de los datos, puede ser necesario SHUTDOWN NOSAVE (los datos no guardados se pierden; tenga cuidado). Escriba scripts cortos y deterministas. Divida las operaciones largas en fragmentos. Revise el parámetro busy-reply-threshold (antes lua-time-limit).","tags":["busy","busy server","lua","script","script kill","eval","time limit"]},{"code":"MISCONF","title":"MISCONF Redis is configured to save RDB snapshots, but it's currently unable to persist to disk (Redis no puede persistir los snapshots RDB en disco)","severity":"danger","meaning":"Redis está configurado para tomar snapshots RDB, pero no puede escribir en disco. En este estado, si stop-writes-on-bgsave-error está habilitado, Redis rechaza todos los comandos de escritura.","cause":"Un disco lleno, problemas de permisos del sistema de archivos, un directorio sin permiso de escritura, errores del guardado en segundo plano (BGSAVE), fallo de fork por memoria insuficiente o un problema de hardware del disco.","fix":"Libere espacio en disco y compruebe el permiso de escritura en el directorio RDB (dir). Inspeccione el último error de BGSAVE: INFO persistence y los logs de Redis. Para permitir temporalmente las escrituras hasta resolver el problema, puede configurarse stop-writes-on-bgsave-error no (tenga en cuenta el riesgo para la durabilidad). Asegure memoria suficiente para el fork y el parámetro vm.overcommit_memory=1. Verifique la configuración de persistencia (save/appendonly).","tags":["misconf","persist","durability","rdb","bgsave","disk","snapshot","danger"]},{"code":"CROSSSLOT","title":"CROSSSLOT Keys in request don't hash to the same slot (las claves de la solicitud no van al mismo slot)","severity":"caution","meaning":"En Redis Cluster se usó un comando multiclave (p. ej., MGET, MSET, SUNION), pero las claves caen en hash slots distintos. Las operaciones multiclave requieren que todas las claves estén en el mismo slot.","cause":"En modo clúster, se llaman comandos que acceden a varias claves a la vez con claves repartidas en slots distintos. Las claves se distribuyen en nodos distintos porque no usan un hash tag.","fix":"Use un hash tag para colocar claves relacionadas en el mismo slot: fije parte del nombre de la clave con llaves (p. ej., {user1}:profile y {user1}:settings van al mismo slot). Use comandos multiclave solo con claves que compartan el mismo hash tag. Como alternativa, divida las operaciones en comandos de una sola clave.","tags":["crossslot","cross slot","cluster","cluster","hash tag","slot","mget","mset"]},{"code":"MOVED","title":"MOVED slot ip:port (redirección del clúster)","severity":"safe","meaning":"En Redis Cluster, el hash slot que contiene la clave solicitada pertenece a otro nodo. El servidor redirige permanentemente al cliente al nodo correcto. Es una redirección más que un error.","cause":"Un cliente que no es consciente del clúster envía una solicitud al nodo equivocado, los slots se han redistribuido entre nodos (resharding) o el mapa de slots almacenado en caché por el cliente está obsoleto.","fix":"Use una biblioteca cliente compatible con clúster; estas siguen automáticamente la respuesta MOVED, redirigen al nodo correcto y actualizan el mapa de slots. Use la opción -c al conectarse con redis-cli. Asegúrese de que su cliente actualice periódicamente la topología de slots (CLUSTER SLOTS/SHARDS).","tags":["moved","cluster","cluster","redirect","redirect","slot","resharding"]},{"code":"ASK","title":"ASK slot ip:port (redirección del clúster)","severity":"safe","meaning":"En Redis Cluster, un slot se está migrando a otro nodo y la clave solicitada puede estar temporalmente en el nodo de destino. El servidor redirige al cliente al nodo de destino solo para esta solicitud (redirección temporal).","cause":"Acceder a una clave que ya se ha transferido dentro de un slot en migración durante un resharding. A diferencia de MOVED, no es permanente; solo aplica en ese momento.","fix":"Los clientes compatibles con clúster gestionan automáticamente las redirecciones ASK: envían el comando ASKING al nodo de destino y reintentan la solicitud, pero no actualizan el mapa de slots de forma permanente. Use una biblioteca cliente adecuada y la opción -c en redis-cli. Cuando termina el resharding, las redirecciones pasan a ser MOVED y permanentes.","tags":["ask","asking","cluster","cluster","migration","migration","redirect","redirect"]},{"code":"NOSCRIPT","title":"NOSCRIPT No matching script. Please use EVAL (no hay script coincidente; use EVAL)","severity":"caution","meaning":"El digest SHA1 del script Lua llamado con EVALSHA no se encontró en la caché de scripts del servidor. Redis no conoce ningún script correspondiente a este hash.","cause":"El servidor se reinició o la caché de scripts se vació con SCRIPT FLUSH, el script nunca se cargó o la caché está vacía en el nuevo nodo tras un failover.","fix":"Ejecute el script con su cuerpo completo mediante EVAL (Redis lo almacena en caché automáticamente), o cárguelo primero con SCRIPT LOAD y use el SHA devuelto en EVALSHA. En la aplicación, capture el error NOSCRIPT e implemente una lógica de respaldo de EVALSHA a EVAL. Muchas bibliotecas cliente de Redis lo hacen automáticamente.","tags":["noscript","script","evalsha","eval","lua","sha","script load","cache"]},{"code":"EXECABORT","title":"EXECABORT Transaction discarded because of previous errors (transacción descartada por errores previos)","severity":"caution","meaning":"Se abortó una transacción de un bloque MULTI/EXEC por errores producidos durante la fase de encolado. EXEC rechaza la transacción sin ejecutar ningún comando.","cause":"Un comando entre MULTI y EXEC contiene un error de sintaxis, o errores detectados durante el encolado, como un nombre de comando inexistente o un número incorrecto de argumentos. Estos errores invalidan toda la transacción.","fix":"Corrija la sintaxis y los argumentos de los comandos del bloque MULTI. Compruebe las respuestas al encolar los comandos; si algún comando devuelve un error en lugar de QUEUED, la transacción se aborta en EXEC. Reconstruya la transacción con comandos válidos. Nota: las transacciones de Redis no hacen rollback; solo los errores de encolado provocan EXECABORT, y los errores en tiempo de ejecución no detienen los demás comandos.","tags":["execabort","transaction","transaction","multi","exec","discard","queue","queue"]}]},"errors/elasticsearch.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"errors":[{"code":"cluster_block_exception","title":"ClusterBlockException[blocked by: [TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark, index has read-only-allow-delete block]] (antes de 7.x: FORBIDDEN/12/index read-only / allow delete (api))","severity":"danger","meaning":"Como el uso de disco superó el flood stage watermark (por defecto 95 %; en discos grandes lo determina max_headroom), Elasticsearch aplicó un bloqueo read_only_allow_delete a los índices que tienen un shard en el nodo afectado, para evitar que el disco se llene por completo. Por este bloqueo no es posible escribir en el índice (index/update/delete de documentos); solo se permite eliminar el propio índice. ES 8.x devuelve este error con HTTP 429.","cause":"El uso de disco de al menos un nodo superó el umbral cluster.routing.allocation.disk.watermark.flood_stage. Aunque el bloqueo se elimine manualmente, Elasticsearch vuelve a aplicarlo mientras el disco siga lleno. Índices grandes sin rotación de logs, snapshots sin limpiar o capacidad de disco insuficiente son causas típicas.","fix":"Primero libere espacio en disco: elimine índices antiguos/innecesarios (DELETE /old-index), haga un snapshot y elimine índices, o amplíe el disco. En 7.4+, el bloqueo se elimina automáticamente cuando el uso de disco baja del high watermark; para quitarlo de inmediato sin esperar, elimínelo manualmente: PUT /_all/_settings {\"index.blocks.read_only_allow_delete\": null}. Como solución permanente, elimine/archive automáticamente los datos antiguos con ILM (Index Lifecycle Management) y revise los parámetros de watermark (disk.watermark.low/high/flood_stage).","tags":["disk","read-only","watermark","flood-stage","cluster-block","danger"]},{"code":"index_read_only_allow_delete","title":"TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark, index has read-only-allow-delete block (legacy: FORBIDDEN/12/index read-only / allow delete (api)) (uso de disco por encima del flood stage watermark; índice bloqueado)","severity":"danger","meaning":"El índice es de solo lectura porque index.blocks.read_only_allow_delete es true; no se pueden escribir ni actualizar documentos nuevos, solo se puede eliminar el índice. Elasticsearch suele configurarlo automáticamente cuando se supera el flood stage watermark del disco; ES 8.x devuelve HTTP 429 a las solicitudes de escritura.","cause":"El bloqueo se aplicó automáticamente porque el uso de disco superó el umbral flood stage, o el parámetro se puso manualmente en true. Si se elimina el bloqueo sin liberar espacio en disco, Elasticsearch vuelve a aplicarlo.","fix":"Libere suficiente espacio en disco. En 7.4+, un bloqueo aplicado automáticamente se elimina solo cuando el uso de disco baja del high watermark; para un bloqueo aplicado manualmente, o para quitarlo sin esperar, elimine el bloqueo: PUT /_all/_settings {\"index.blocks.read_only_allow_delete\": null}. Para un solo índice: PUT /index-name/_settings {\"index.blocks.read_only_allow_delete\": null}. Si se repite continuamente, aumente la capacidad de disco, configure una política ILM y supervise con _cat/allocation qué nodo se está llenando.","tags":["read-only","disk","forbidden","watermark","block","danger"]},{"code":"high_disk_watermark_exceeded","title":"high disk watermark [90%] exceeded / flood stage disk watermark [95%] exceeded (high/flood stage disk watermark superado)","severity":"danger","meaning":"El uso de disco de un nodo superó el umbral high watermark (por defecto 90 %) o flood stage (por defecto 95 %). Al superarse el high watermark, Elasticsearch intenta mover shards de ese nodo a otros nodos; al superarse flood stage, pone los índices en solo lectura.","cause":"El disco se está llenando: índices de logs/métricas que crecen continuamente, índices antiguos que nunca se eliminan, un repositorio de snapshots sin limpiar, un disco infradimensionado o un único nodo que se llena en exceso por una distribución desequilibrada de shards.","fix":"Inspeccione el uso de disco: GET _cat/allocation?v. Elimine índices antiguos o haga un snapshot y elimínelos, y amplíe el disco. Puede elevar temporalmente los umbrales (con cuidado; en 8.x configure los tres a la vez por la validación de max_headroom): PUT _cluster/settings {\"persistent\":{\"cluster.routing.allocation.disk.watermark.low\":\"88%\",\"cluster.routing.allocation.disk.watermark.high\":\"93%\",\"cluster.routing.allocation.disk.watermark.flood_stage\":\"96%\"}}. Una vez resuelto, vuelva a los valores predeterminados poniendo esos mismos parámetros en null (los parámetros transient están obsoletos; no los use). Solución permanente: gestión del ciclo de vida con ILM, limpieza regular de índices y planificación de capacidad. Si los índices pasaron a solo lectura por flood stage, elimine el bloqueo por separado.","tags":["disk","watermark","flood-stage","allocation","capacity","danger"]},{"code":"circuit_breaking_exception","title":"CircuitBreakingException[[parent] Data too large, data for [<...>] would be [...], which is larger than the limit of [...]] (datos demasiado grandes; límite del circuit breaker)","severity":"danger","meaning":"Se rechazó una solicitud porque superaría el límite del circuit breaker de memoria (heap). Elasticsearch detiene la solicitud de antemano para evitar que el nodo se caiga con un OutOfMemoryError. El breaker [parent] protege el uso total de memoria; el breaker [fielddata] protege la memoria de fielddata en campos text.","cause":"Heap de JVM insuficiente, agregaciones muy grandes/complejas, conjuntos de resultados grandes, solicitudes bulk grandes, fielddata cargado en campos text o muchas consultas pesadas concurrentes que llenan el heap. Suele ser un síntoma de presión sobre el heap.","fix":"Reduzca las consultas: disminuya el tamaño de las agregaciones, el tamaño de los lotes bulk y los campos devueltos. Agregue sobre el subcampo keyword (.keyword) en lugar de usar fielddata en campos text. Aumente el heap de JVM (~50 % de la RAM del servidor, como máximo ~31GB). Supervise la presión del heap: GET _cat/nodes?v&h=name,heap.percent. Corrija la causa raíz (memoria/consulta) en lugar de elevar temporalmente los límites de los breakers. Compruebe qué breaker saltó con GET _nodes/stats/breaker.","tags":["circuit-breaker","data-too-large","heap","memory","fielddata","oom","danger"]},{"code":"mapper_parsing_exception","title":"mapper_parsing_exception: failed to parse field [...] of type [...] (no se pudo analizar el campo)","severity":"caution","meaning":"Al indexar un documento, el valor enviado para un campo no coincidía con el tipo definido para ese campo en el mapping y no pudo analizarse. Por ejemplo, texto enviado a un campo definido como integer o un formato de fecha no válido enviado a un campo definido como date.","cause":"Discrepancia de tipo de campo (un string para un campo numérico, un formato mal formado para un campo de fecha), una fecha que no coincide con el formato de fecha esperado, JSON mal formado/incompleto, campos con tipo dinámico erróneo o un valor escalar enviado donde se espera un objeto.","fix":"Compruebe el nombre del campo y el tipo esperado en el mensaje de error: GET /index-name/_mapping. Haga que el valor del documento coincida con el tipo del campo o redefina el mapping con el tipo correcto. Para campos de fecha, haga coincidir el parámetro format con el formato del documento. El tipo de un campo existente no puede cambiarse; si es necesario, cree un índice nuevo y mueva los datos con _reindex. Valide los datos en la capa de aplicación.","tags":["mapping","parse","mapper","field-type","indexing","caution"]},{"code":"strict_dynamic_mapping_exception","title":"strict_dynamic_mapping_exception: mapping set to strict, dynamic introduction of [...] within [...] is not allowed (mapping strict: no se permite añadir campos dinámicamente)","severity":"caution","meaning":"Como el mapping del índice o del objeto está configurado con \"dynamic\": \"strict\", se rechazó un documento que contenía un campo nuevo no definido en el mapping. El modo strict impide que se añadan silenciosamente campos ajenos al esquema.","cause":"Se envió en el documento un campo que no está en el mapping (error tipográfico, campo nuevo, campo inesperado) mientras el mapping es dynamic:strict. Suele ser una salvaguarda deliberada para mantener un control estricto del esquema.","fix":"Corrija o elimine el campo sobrante/mal escrito del documento. Si el campo es realmente necesario, añádalo primero al mapping: PUT /index-name/_mapping {\"properties\": {\"new_field\": {\"type\": \"...\"}}}. Si quiere que los campos nuevos se añadan automáticamente, configure dynamic en \"true\", o en \"false\" si deben almacenarse pero no indexarse. Use el modo strict de forma deliberada para mantener la disciplina del esquema.","tags":["mapping","strict","dynamic","schema","field","caution"]},{"code":"illegal_argument_exception_fielddata","title":"illegal_argument_exception: Fielddata is disabled on text fields by default. Set fielddata=true on [your_field] in order to load fielddata in memory (fielddata deshabilitado en campos text)","severity":"caution","meaning":"Se intentó una agregación, ordenación o script sobre un campo text analizado. Estas operaciones están deshabilitadas por defecto en campos text porque requieren fielddata, que es costoso en memoria.","cause":"Se aplicó una agregación/ordenación directamente a un campo text. En Elasticsearch, text se usa para búsqueda de texto completo y keyword para agregación/ordenación por valor exacto; mezclar ambos produce este error.","fix":"Use el subcampo keyword del campo para agregación/ordenación (p. ej., \"my_field.keyword\"). Si el campo es solo text, añada un subcampo keyword al mapping (multi-field) y ejecute _reindex. Habilitar fielddata en un campo text (fielddata=true) es posible, pero no se recomienda por el alto consumo de memoria y el riesgo de circuit breaker; úselo solo cuando sea inevitable.","tags":["fielddata","text","keyword","aggregation","sorting","mapping","caution"]},{"code":"search_phase_execution_exception","title":"search_phase_execution_exception: all shards failed / Failed to execute phase [query] (fallaron todos los shards en la fase de búsqueda)","severity":"danger","meaning":"Una solicitud de búsqueda falló en uno o en todos los shards necesarios. Es un error de búsqueda genérico que envuelve la causa subyacente real; la sección \"caused by\" indica el problema real (error de análisis, breaker, tipo de campo inadecuado, shard inaccesible, etc.).","cause":"Sintaxis de consulta no válida, agregación sobre un tipo de campo incorrecto, too_many_clauses, un salto del circuit breaker, un shard sin asignar/inaccesible, un timeout de consulta o la caída de un nodo. La causa raíz está en la excepción interna de la respuesta.","fix":"Encuentre la excepción real en los detalles \"caused by\" / \"failed_shards\" de la respuesta y corrija en consecuencia (corregir la consulta, usar un campo keyword, configuración de breaker/heap, asignación de shards). Compruebe el estado del clúster: GET _cluster/health y GET _cat/shards?v. Si se permiten resultados parciales y algunos shards fallaron, revise el comportamiento de allow_partial_search_results.","tags":["search","shard","search","all-shards-failed","query","danger"]},{"code":"too_many_clauses","title":"too_many_clauses: maxClauseCount is set to 1024 (query contains too many clauses) (demasiadas cláusulas en la consulta)","severity":"caution","meaning":"Una consulta superó el número máximo permitido de cláusulas de Lucene. En ES 8.x este límite no es fijo; se calcula automáticamente a partir del tamaño del heap del nodo y del número de hilos de búsqueda (puede bajar a 1024 en nodos con heap pequeño), y el mensaje de error muestra el valor actual. Lo provocan las consultas bool muy grandes y las consultas que se expanden a muchos términos.","cause":"Consultas bool generadas por programa con miles de cláusulas should/must; consultas wildcard/prefix/fuzzy/query_string que se expanden a muchos términos; estructuras bool profundamente anidadas. Nota: la consulta terms (una lista de valores sobre un solo campo) no está sujeta a este límite; su tope es index.max_terms_count (por defecto 65,536).","fix":"Simplifique la consulta: convierta muchas cláusulas term sobre un solo campo en una sola consulta terms (o un terms lookup para listas grandes). Use tipos de consulta más específicos o campos index_prefixes/keyword en lugar de wildcard/prefix. En ES 8.x el parámetro indices.query.bool.max_clause_count está obsoleto y se ignora; la forma de elevar el límite es aumentar el heap del nodo. Revise la lógica de generación de consultas.","tags":["too-many-clauses","max-clause-count","bool","terms","query","caution"]},{"code":"version_conflict_engine_exception","title":"version_conflict_engine_exception: [_doc][id]: version conflict, current version [x] is different than the one provided [y] (HTTP 409) (conflicto de versión del documento)","severity":"caution","meaning":"El control de concurrencia optimista rechazó una solicitud de actualización/eliminación de un documento. La versión del documento (o _seq_no/_primary_term) no coincide con la de la solicitud; es decir, otro modificó el documento después de que usted lo leyera. Devuelve HTTP 409.","cause":"Actualizaciones concurrentes del mismo documento, una escritura intentada con una version/seq_no obsoleta, un intento de crear (op_type=create) un documento que ya existe o una condición de carrera durante _bulk.","fix":"Vuelva a leer el documento y reintente la solicitud con los _seq_no y _primary_term (o version) actuales (bucle read-modify-write). Para reintentos automáticos, use el parámetro retry_on_conflict en la solicitud _update; en _update_by_query/_delete_by_query pase conflicts=proceed para omitir los documentos en conflicto y continuar. Si la intención es solo crear, el conflicto es normal; capture el 409 en la aplicación y gestiónelo adecuadamente (reintentar u omitir).","tags":["version-conflict","409","concurrency","optimistic","seq-no","caution"]},{"code":"document_missing_exception","title":"document_missing_exception: [_doc][id]: document missing (documento inexistente)","severity":"caution","meaning":"No se encontró en el índice el documento que se quiere actualizar (_update). Una actualización requiere un documento existente; si el documento no existe, se produce esta excepción.","cause":"El documento se eliminó antes, nunca se creó, se usó un _id incorrecto, la solicitud fue al índice equivocado o se accedió sin routing a un documento escrito con routing personalizado. (GET y _update son en tiempo real; el retraso de refresh no causa este error.)","fix":"Verifique que el _id y el índice de destino son correctos: GET /index-name/_doc/id. Si quiere un comportamiento de actualizar si existe/crear si falta, use upsert (o doc_as_upsert:true) en la solicitud _update. Cree el documento directamente con index (PUT). En la aplicación, capture esta excepción y recurra a la lógica de upsert.","tags":["document-missing","update","upsert","document","not-found","caution"]},{"code":"index_not_found_exception","title":"index_not_found_exception: no such index [index_name] (HTTP 404) (el índice no existe)","severity":"caution","meaning":"El índice (o alias) solicitado no existe. La operación se rechazó con HTTP 404 porque no se encontró ningún índice con el nombre indicado.","cause":"El índice nunca se creó, se eliminó, el nombre del índice/alias tiene un error tipográfico, el cliente está conectado al entorno (clúster) equivocado o se intentó escribir en un índice inexistente con action.auto_create_index deshabilitado.","fix":"Liste los índices existentes: GET _cat/indices?v. Verifique el nombre y el alias: GET _alias. Cree el índice: PUT /index-name (con los mappings/settings necesarios), o configure la creación automática con un index template. Si se desea la creación automática, revise el parámetro action.auto_create_index. Verifique que no está conectado al clúster equivocado.","tags":["index-not-found","404","index","alias","not-found","caution"]},{"code":"resource_already_exists_exception","title":"resource_already_exists_exception: index [index_name/uuid] already exists (HTTP 400) (el índice ya existe)","severity":"safe","meaning":"El índice (o recurso) que se quiere crear ya existe. La solicitud de creación se rechazó porque existe un índice con el mismo nombre. En versiones antiguas también aparece como index_already_exists_exception.","cause":"Intentar crear el mismo índice dos veces, scripts de configuración no idempotentes, solicitudes de creación concurrentes o un intento de volver a crear el mismo nombre con otra configuración.","fix":"Si el índice ya existe, no es necesario crearlo; continúe. Si debe recrearse, elimínelo primero (DELETE /index-name) y luego créelo (precaución: pérdida de datos). Haga idempotentes los scripts de configuración: compruebe antes su existencia con GET _cat/indices. Evite conflictos usando index templates o ILM/rollover para la gestión del esquema.","tags":["already-exists","resource-already-exists","index","create","idempotent","safe"]},{"code":"cluster_health_red","title":"cluster health status: RED / unassigned shards (estado del clúster RED / shards sin asignar)","severity":"danger","meaning":"El estado RED del clúster significa que al menos un shard primario no está asignado y no está disponible. Es un estado crítico en el que algunos datos no pueden leerse/escribirse, con riesgo de pérdida parcial de datos. (YELLOW significa que solo hay réplicas sin asignar; los primarios están intactos.)","cause":"Un nodo se cayó/salió y los shards primarios que tenía quedaron sin copia, el disk watermark bloquea la asignación, un shard corrupto, un error durante la restauración de un snapshot o parámetros de asignación que impiden asignar shards.","fix":"Vea por qué los shards no están asignados: GET _cluster/allocation/explain o GET _cat/shards?v&h=index,shard,prirep,state,unassigned.reason. Recupere los nodos caídos; si es un problema de disco, libere espacio (watermark). Si la asignación se bloqueó temporalmente, fuerce la reasignación: POST _cluster/reroute?retry_failed=true. Si se perdió por completo un primario, restaure desde un snapshot. En estado RED, céntrese primero en recuperar los primarios.","tags":["cluster-health","red","unassigned","shard","primary","danger"]},{"code":"no_shard_available_action_exception","title":"no_shard_available_action_exception: No shard available for [...] (no hay shard disponible)","severity":"danger","meaning":"No se encontró ninguna copia activa del shard capaz de atender la solicitud. La solicitud no puede cumplirse porque el shard correspondiente no está asignado, se está recuperando o el nodo que lo aloja no es accesible.","cause":"La caída de un nodo/partición de red, shards sin asignar, una recuperación de shard en curso, un bloqueo de asignación por el disk watermark o el clúster en estado RED/YELLOW.","fix":"Inspeccione el estado del clúster y de los shards: GET _cluster/health y GET _cat/shards?v. Vea por qué los shards no están asignados: GET _cluster/allocation/explain. Recupere los nodos caídos, corrija el problema de disco y espere a que termine la recuperación: GET _cat/recovery?v&active_only=true. Si es necesario, reintente la asignación con POST _cluster/reroute?retry_failed=true.","tags":["no-shard-available","shard","unassigned","recovery","cluster","danger"]},{"code":"es_rejected_execution_exception","title":"es_rejected_execution_exception: rejected execution of ... on ... (queue capacity ...) (HTTP 429) (ejecución rechazada: cola llena)","severity":"danger","meaning":"Se rechazó una operación porque la capacidad de la cola del thread pool correspondiente (search, write/bulk, etc.) está llena. En sobrecarga, Elasticsearch se protege rechazando solicitudes (backpressure) y devuelve HTTP 429 (Too Many Requests).","cause":"Un volumen muy alto de solicitudes concurrentes, solicitudes bulk muy grandes o muy frecuentes, carga intensa de agregaciones/búsquedas, recursos de nodo/CPU insuficientes o una cola de thread pool demasiado pequeña para la carga de trabajo.","fix":"Reintente en el lado del cliente con backoff exponencial; el 429 normalmente es transitorio. Reduzca el tamaño de los lotes bulk y la tasa de envío, y disminuya la concurrencia. Supervise las operaciones rechazadas: GET _cat/thread_pool?v&h=node_name,name,active,queue,rejected. Para cargas sostenidas, añada nodos/recursos (escalado horizontal). Aumentar el tamaño de la cola es el último recurso; la causa raíz es la capacidad.","tags":["rejected-execution","429","thread-pool","queue","bulk","backpressure","danger"]},{"code":"master_not_discovered_exception","title":"master_not_discovered_exception / MasterNotDiscoveredException (no se descubrió ningún master)","severity":"danger","meaning":"El clúster no pudo elegir/encontrar un nodo master activo. Sin master no pueden realizarse asignaciones de shards, cambios de mapping ni actualizaciones del estado del clúster; el clúster queda incapaz de procesar operaciones.","cause":"La mayoría (quórum) de los nodos master-eligible no es accesible, una partición de red (prevención de split brain), configuración incorrecta de discovery.seed_hosts / cluster.initial_master_nodes, sin bootstrap en una instalación nueva o muy pocos nodos master-eligible.","fix":"Verifique que los nodos master-eligible están en ejecución y pueden verse entre sí: GET _cat/nodes?v y GET _cluster/state. Revise los parámetros discovery.seed_hosts y cluster.initial_master_nodes (en la primera configuración). Verifique la red/firewall y la conectividad entre nodos (transport 9300). Mantenga en ejecución suficientes nodos master-eligible (preferiblemente 3). Inspeccione los logs en busca de mensajes de elección de master.","tags":["master","discovery","quorum","split-brain","cluster","danger"]},{"code":"security_exception","title":"security_exception: unable to authenticate / action [...] is unauthorized for user [...] (HTTP 401/403) (no se puede autenticar / acción no autorizada)","severity":"danger","meaning":"Falló la autenticación (401 Unauthorized) o la autorización (403 Forbidden). 401 indica credenciales ausentes/incorrectas; 403 indica que el usuario está autenticado pero carece del rol/privilegio necesario para esta acción/índice.","cause":"Nombre de usuario/contraseña o API key incorrectos, falta la cabecera Authorization, un token caducado/no válido, el usuario carece del privilegio de índice o de clúster correspondiente, o una asignación de roles ausente/incorrecta.","fix":"Para 401: envíe las credenciales/API key correctas y revise la cabecera Authorization. Para 403: inspeccione los roles y privilegios del usuario (GET _security/user/app_user, GET _security/role/app_role) y añada los privilegios de índice/clúster necesarios. Siga el principio de mínimo privilegio. Si usa una API key, verifique su caducidad y sus permisos.","tags":["security","401","403","authentication","authorization","security","danger"]},{"code":"validation_exception","title":"validation_exception / illegal_argument_exception: Validation Failed: ... (validación fallida)","severity":"caution","meaning":"Los parámetros o la configuración de una solicitud no superaron la validación. Elasticsearch rechazó sin ejecutarla la solicitud que contenía valores no válidos o incoherentes (p. ej., número de shards no válido, combinación de parámetros incompatible, falta un campo obligatorio).","cause":"Un parámetro de índice no válido (p. ej., number_of_shards negativo/cero), un intento de actualizar un parámetro no modificable en un índice existente, una combinación de parámetros incompatible, falta un campo obligatorio o un valor fuera de rango.","fix":"Lea el detalle de validación en el mensaje de error; indica claramente qué parámetro no es válido y por qué. Lleve el valor a un rango/formato válido. Indique los parámetros no modificables (static) al crear un índice nuevo, no en un índice existente (_reindex si es necesario). Proporcione los campos obligatorios que espera la API. Verifique los valores válidos documentados del parámetro.","tags":["validation","validation","settings","illegal-argument","invalid","caution"]},{"code":"task_cancelled_exception","title":"task_cancelled_exception: Task was cancelled / TaskCancelledException (tarea cancelada)","severity":"caution","meaning":"Se canceló una tarea en ejecución (search, _reindex, _update_by_query, etc.). La cancelación puede producirse por una solicitud del usuario a _tasks/_cancel, porque el cliente cierra la conexión HTTP (timeout/abort del cliente; Elasticsearch cancela automáticamente la búsqueda asociada) o por la cancelación de una tarea padre o una async search caducada.","cause":"El usuario canceló la tarea con POST _tasks/<task_id>/_cancel (X-Opaque-Id solo identifica la tarea y ayuda a encontrarla; no la cancela), un timeout del cliente cortó la conexión, consultas costosas de larga duración o la gestión adaptativa de recursos detuvo una tarea pesada.","fix":"Si la tarea se canceló de forma inesperada, revise la configuración de timeout y de conexión del cliente; optimice la consulta pesada (menos datos, filtros más específicos). Para operaciones largas, ejecute _reindex/_update_by_query de forma asíncrona (wait_for_completion=false) y supervise con GET _tasks. Vea las tareas activas: GET _tasks?detailed=true. Si es necesario, divida las consultas costosas o use scroll/PIT.","tags":["task-cancelled","task","timeout","reindex","cancel","caution"]}]},"errors/oracle-more.json":{"engineId":"oracle","engine":"Oracle","order":1,"errors":[{"code":"ORA-00604","title":"error occurred at recursive SQL level (error en el nivel de SQL recursivo)","severity":"danger","meaning":"Se produjo un error durante el SQL recursivo que Oracle ejecuta internamente en segundo plano. Es un error envoltorio (externo) que oculta el error real; la causa real está en el segundo código de error que aparece justo debajo.","cause":"Acceso al diccionario de datos, un trigger DDL que falla, un tablespace SYSTEM lleno, espacio temporal insuficiente, un objeto del diccionario de datos corrupto o un trigger LOGON que falla al iniciar sesión.","fix":"Lea la segunda línea que sigue a ORA-00604 en la pila de errores; esa es la causa real, y corríjala. Por ejemplo, si ve ORA-01653, amplíe el tablespace. Si sospecha de un trigger DDL/LOGON, deshabilítelo temporalmente: ALTER TRIGGER trigger_name DISABLE; Compruebe el uso de los tablespaces SYSTEM y TEMP con DBA_FREE_SPACE.","tags":["recursive","recursion","dictionary","data dictionary","trigger","wrapper"]},{"code":"ORA-01438","title":"value larger than specified precision allowed for this column (valor mayor que la precisión permitida para la columna)","severity":"caution","meaning":"Se escribió en una columna NUMBER un número que supera los límites de precisión y escala definidos. Por ejemplo, un valor con parte entera de 3 dígitos no cabe en una columna NUMBER(4,2).","cause":"La definición NUMBER(p,s) de la columna es demasiado estrecha para los datos; el resultado de un cálculo es mayor de lo esperado; la escala decimal reduce los dígitos disponibles para la parte entera (NUMBER(5,3) permite solo 2 dígitos enteros).","fix":"Amplíe la capacidad de la columna según el rango real de los datos: ALTER TABLE my_table MODIFY (my_column NUMBER(12,2)); Redondee el valor en la aplicación o ajústelo a la escala con ROUND/TRUNC. Para encontrar la fila que desborda, acote su consulta durante las pruebas y añada validación de entrada.","tags":["number","precision","precision","scale","scale","overflow","numeric"]},{"code":"ORA-01400","title":"cannot insert NULL into (column) (no se puede insertar NULL en la columna)","severity":"caution","meaning":"Se insertó o asignó un valor NULL en una columna con restricción NOT NULL. Un campo obligatorio no puede quedar vacío.","cause":"No se proporcionó valor para una columna obligatoria en un INSERT, un UPDATE que asigna NULL a la columna, no hay DEFAULT definido para la columna o un valor vacío procedente de una subconsulta/aplicación.","fix":"Proporcione un valor válido para la columna indicada en el mensaje de error. Defina un valor predeterminado razonable para la columna: ALTER TABLE my_table MODIFY (my_column DEFAULT 0); (DEFAULT solo se aplica cuando la columna se omite por completo en el INSERT; para valores NULL enviados explícitamente use DEFAULT ON NULL de 12c+: ALTER TABLE my_table MODIFY (my_column DEFAULT ON NULL 0);) Si la regla no es realmente obligatoria, elimine la restricción: ALTER TABLE my_table MODIFY (my_column NULL); Valide los valores vacíos en la aplicación antes de enviarlos.","tags":["not null","null","empty","insert","constraint","constraint","mandatory"]},{"code":"ORA-02291","title":"integrity constraint violated - parent key not found (violación de integridad: clave padre no encontrada)","severity":"danger","meaning":"Se violó una restricción de clave foránea. No se encontró ninguna fila padre correspondiente al valor insertado en la tabla hija; es decir, el registro referenciado no existe.","cause":"Un INSERT/UPDATE que referencia un registro padre inexistente, la fila de la tabla padre aún no insertada, un orden de carga incorrecto (hijo antes que padre) o la coincidencia falla por diferencias de mayúsculas o espacios en blanco.","fix":"Inserte primero el registro correspondiente en la tabla padre y luego en la tabla hija. Verifique que la referencia existe: SELECT * FROM parent_table WHERE key_col = value; En cargas masivas, procese las tablas en el orden correcto (primero el padre). Para encontrar la fila infractora, compare el valor de la tabla hija con la tabla padre usando NOT IN / un anti-join.","tags":["fk","foreign key","foreign key","constraint","constraint","parent","reference"]},{"code":"ORA-02292","title":"integrity constraint violated - child record found (violación de integridad: se encontró un registro hijo)","severity":"danger","meaning":"Se eliminó una fila padre o se cambió su clave, pero la restricción de clave foránea no lo permitió porque existen registros hijos dependientes.","cause":"Intentar hacer DELETE de una fila padre que tiene registros en la tabla hija; la FK está definida sin ON DELETE CASCADE; no se limpiaron antes los registros dependientes.","fix":"Primero elimine los registros hijos dependientes o muévalos a otro registro padre y luego elimine la fila padre. Si la regla de negocio lo permite, vuelva a crear la clave foránea con ON DELETE CASCADE: ALTER TABLE child DROP CONSTRAINT fk_name; ALTER TABLE child ADD CONSTRAINT fk_name FOREIGN KEY (my_column) REFERENCES parent(key_col) ON DELETE CASCADE; Para encontrar las filas dependientes, consulte la tabla hija por la clave padre.","tags":["fk","foreign key","foreign key","child","child record","delete","cascade"]},{"code":"ORA-00979","title":"not a GROUP BY expression (no es una expresión GROUP BY)","severity":"caution","meaning":"Se usó una columna no agregada en la lista SELECT junto con una función de agregación (SUM, COUNT, etc.), pero la columna no se añadió a la cláusula GROUP BY. Oracle no sabe cómo agrupar esa columna.","cause":"Una columna presente en el SELECT pero no en el GROUP BY; expresiones fuera de funciones de agregación que faltan en la lista GROUP BY; incoherencia en HAVING o en las expresiones del select.","fix":"Añada todas las columnas no agregadas a la lista GROUP BY: SELECT department, COUNT(*) FROM employee GROUP BY department; Como alternativa, envuelva también esa columna en una función de agregación (MAX, MIN) o rediseñe la agrupación con una función analítica (OVER()).","tags":["group by","grouping","aggregate","aggregation","sum","count","query"]},{"code":"ORA-00918","title":"column ambiguously defined (columna definida de forma ambigua)","severity":"caution","meaning":"Existe una columna con el mismo nombre en más de una tabla de la consulta y, como se usa sin prefijo de tabla/alias, Oracle no puede determinar a qué tabla pertenece.","cause":"Ambas tablas combinadas tienen una columna con el mismo nombre (p. ej., ID en ambas); la columna se usa sin calificar (sin prefijo) en SELECT, WHERE u ORDER BY.","fix":"Califique cada columna ambigua con el nombre o alias de la tabla: SELECT a.id, b.id FROM tableA a JOIN tableB b ON a.id = b.a_id; Asigne alias cortos a las tablas y anteponga el prefijo a todas las columnas para mejorar la legibilidad y la claridad.","tags":["ambiguous","ambiguous column","column","column","join","alias","query"]},{"code":"ORA-06550","title":"línea/columna PLS-... error de compilación PL/SQL","severity":"caution","meaning":"Se encontró un error de sintaxis o semántico al compilar un bloque PL/SQL, un procedimiento o un bloque anónimo. El código falló en la fase de compilación antes de poder ejecutarse.","cause":"Errores tipográficos, una referencia a una variable u objeto no declarado, una llamada con un número/tipo incorrecto de parámetros, un punto y coma que falta, una llamada a un procedimiento/paquete inexistente o un privilegio que falta.","fix":"Vaya al número de línea y columna del mensaje de error; lea el subcódigo PLS-XXXX. Para ver todos los errores de compilación: SHOW ERRORS; o SELECT * FROM user_errors WHERE name = 'OBJECT_NAME'; Verifique los nombres de objetos, las firmas de los parámetros y los GRANT necesarios, y vuelva a compilar.","tags":["plsql","compilation","compile","pls","syntax","procedure","code"]},{"code":"ORA-04068","title":"existing state of packages has been discarded (se descartó el estado existente de los paquetes)","severity":"caution","meaning":"Mientras una sesión en ejecución usaba el estado de un paquete, el paquete se recompiló, por lo que ese estado quedó invalidado. La instancia del paquete que mantiene la sesión ya no es válida.","cause":"Se recompiló el cuerpo o la especificación de un paquete con ALTER/CREATE OR REPLACE mientras había sesiones activas que dependían de él; suele ocurrir durante el despliegue de aplicaciones en un sistema en producción. Se ve junto con ORA-04061/04065.","fix":"Volver a ejecutar la llamada (reintento) suele resolverlo; en el segundo intento el paquete se carga en su nueva forma. Capture ORA-04068 en la aplicación y reintente automáticamente. Aplique los cambios de paquetes en ventanas de mantenimiento con poca actividad de usuarios. Un diseño de paquetes sin estado reduce este problema.","tags":["package","package","state","package state","recompile","compile","retry"]},{"code":"ORA-01427","title":"single-row subquery returns more than one row (una subconsulta de una sola fila devuelve más de una fila)","severity":"caution","meaning":"Una subconsulta de la que se esperaba una sola fila devolvió más de una fila. Por ejemplo, la subconsulta del lado derecho de una comparación de igualdad (=) produjo varios valores.","cause":"Una subconsulta que devuelve varias filas donde se espera = o una asignación; una condición WHERE insuficientemente acotada; una coincidencia no única en la correlación; una subconsulta escalar en la lista SELECT que devuelve varias filas.","fix":"Acote la subconsulta para que devuelva una sola fila (WHERE adicional, clave única). Si realmente se esperan varios valores, use IN en lugar de =: WHERE id IN (SELECT id FROM ...). O añada una agregación a la subconsulta (MAX, MIN), o redúzcala a una fila con ROWNUM = 1. Ejecute primero la subconsulta por separado y compruebe cuántas filas devuelve.","tags":["subquery","subquery","single row","single row","in","scalar","query"]},{"code":"ORA-12899","title":"value too large for column (valor demasiado grande para la columna)","severity":"caution","meaning":"Se escribió en una columna de caracteres/datos un valor más largo que la longitud máxima definida. Por ejemplo, un texto de 15 caracteres no cabe en una columna VARCHAR2(10).","cause":"El ancho de la columna es insuficiente para los datos; en un conjunto de caracteres multibyte (UTF8) el número de caracteres supera el límite de bytes (semántica BYTE); un conjunto de caracteres distinto durante la migración de datos; el resultado de una concatenación que crece en longitud.","fix":"Amplíe la columna: ALTER TABLE my_table MODIFY (my_column VARCHAR2(100)); Para contenido multibyte use semántica CHAR: my_column VARCHAR2(100 CHAR). Si hay que acortar valores largos, trúnquelos con SUBSTR. El mensaje de error indica la longitud real y la permitida; encuentre el registro que desborda con LENGTH/LENGTHB.","tags":["varchar2","length","length","too large","column","column","charset"]},{"code":"ORA-30926","title":"unable to get a stable set of rows in the source tables (no se puede obtener un conjunto estable de filas en las tablas de origen)","severity":"danger","meaning":"En una sentencia MERGE (o un join actualizable), el lado de origen produjo más de una fila coincidente para la misma fila de destino. Oracle detuvo la operación porque no puede decidir con qué valor de origen actualizar.","cause":"El origen de MERGE ... USING contiene coincidencias duplicadas para la condición ON; la consulta de origen no es única sobre la clave de destino; varias filas de origen corresponden a la misma fila de destino.","fix":"Haga que la consulta de origen sea única sobre la clave de destino. Encuentre las coincidencias duplicadas: SELECT key_col, COUNT(*) FROM (source_query) GROUP BY key_col HAVING COUNT(*) > 1; Elimine los duplicados del origen con DISTINCT o una función de agregación/analítica (ROW_NUMBER) y vuelva a ejecutar el MERGE. Asegúrese de que la condición ON sea suficientemente selectiva.","tags":["merge","merge","duplicate","duplicate rows","source","source","stable"]},{"code":"ORA-08103","title":"object no longer exists (el objeto ya no existe)","severity":"danger","meaning":"Un objeto (tabla, índice, segmento LOB) al que accede una consulta en ejecución ya no existe o su estructura física cambió. Normalmente se produjo un DDL/TRUNCATE sobre el objeto mientras la consulta estaba en curso.","cause":"Se hizo TRUNCATE/DROP/SHRINK del mismo objeto mientras la consulta estaba abierta; un movimiento (MOVE) o mantenimiento de particiones; un índice/segmento corrupto; la desaparición de la estructura subyacente mientras se accede a un segmento LOB; un conflicto con un DDL concurrente.","fix":"Verifique que el objeto sigue existiendo y es válido: SELECT status FROM user_objects WHERE object_name = 'NAME'; Reconstruya el índice/objeto correspondiente (REBUILD) o vuelva a ejecutar la consulta. Evite TRUNCATE/DROP sobre objetos mientras los usan consultas de larga duración. Si sospecha corrupción, compruébelo con ANALYZE ... VALIDATE STRUCTURE.","tags":["object","object","truncate","drop","ddl","corruption","segment"]},{"code":"ORA-01843","title":"not a valid month (mes no válido)","severity":"caution","meaning":"Al convertir texto a DATE, la parte del mes no pudo interpretarse como un mes válido. Normalmente el formato de fecha no coincide con el formato de los datos.","cause":"No se pasó máscara de formato a TO_DATE y el NLS_DATE_FORMAT de la sesión no coincide con los datos; día y mes intercambiados (confusión DD/MM frente a MM/DD); una diferencia de idioma/configuración regional (en los nombres de los meses); texto de fecha corrupto o inesperado.","fix":"Pase siempre un formato explícito en las llamadas a TO_DATE: TO_DATE('2026-07-04', 'YYYY-MM-DD'). Para texto con nombres de meses, indique el idioma: TO_DATE(str, 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=ENGLISH'). Configure el formato de la sesión si es necesario: ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'. Valide el texto de fecha no válido antes de cargarlo.","tags":["date","date","to_date","nls","format","month","month"]},{"code":"ORA-14400","title":"inserted partition key does not map to any partition (la clave de partición insertada no corresponde a ninguna partición)","severity":"danger","meaning":"El valor de la clave de partición de la fila insertada no corresponde a ninguna partición definida en la tabla. No existe ninguna partición de rango o de lista que cubra el valor.","cause":"En particionamiento RANGE, se insertó una fecha/valor por encima del rango más alto y no hay partición MAXVALUE; en particionamiento LIST, un valor nuevo que no está definido; la nueva partición no se creó de antemano en el límite de mes/año.","fix":"Añada la partición que falta: ALTER TABLE my_table ADD PARTITION p_2026_08 VALUES LESS THAN (TO_DATE('2026-09-01','YYYY-MM-DD')); Para RANGE, añada una partición MAXVALUE como red de seguridad. Defina una partición DEFAULT para LIST para capturar valores inesperados. Use particionamiento automático (INTERVAL partitioning) para que las nuevas particiones se creen automáticamente.","tags":["partition","partition","partition key","range","list","maxvalue","insert"]},{"code":"ORA-01536","title":"space quota exceeded for tablespace (cuota de espacio superada en el tablespace)","severity":"danger","meaning":"Se agotó la cuota de espacio del usuario en el tablespace indicado. Aunque el tablespace tenga espacio libre, no se puede asignar espacio nuevo porque se superó el límite concedido al usuario.","cause":"El usuario tiene una cuota limitada (no UNLIMITED); la cuota se llenó con el crecimiento de los datos; una carga excesiva en una tabla/índice; escritura en el tablespace equivocado; segmentos que crecen más rápido de lo esperado.","fix":"Aumente la cuota del usuario: ALTER USER app_user QUOTA UNLIMITED ON tablespace_name; o asigne un valor concreto: QUOTA 500M ON users. Vea el uso actual: SELECT * FROM dba_ts_quotas WHERE username = 'APP_USER'. Archive/purgue segmentos innecesariamente grandes o mueva los objetos a un tablespace adecuado.","tags":["quota","quota","tablespace","space","space","user","user"]},{"code":"direct path read","title":"Espera: sesión que lee bloques directamente en la PGA sin pasar por el buffer cache","severity":"caution","meaning":"La sesión pasa tiempo en este wait event mientras lee bloques directamente en la memoria PGA en lugar de a través del buffer cache de la base de datos. Suele asociarse a full table scans grandes y consultas paralelas.","cause":"Muchos bloques leídos desde disco por recorridos completos de tablas grandes, consultas paralelas, operaciones sort/hash que se vuelcan al tablespace temporal, la decisión de serial direct path read en recorridos completos en serie y consultas que no pueden usar un índice.","fix":"Identifique con AWR/ASH el SQL que realiza recorridos completos intensos y añada índices adecuados. Reduzca los recorridos innecesarios de tablas grandes mediante el ajuste de consultas. Investigue la latencia del almacenamiento (E/S) y pase a discos más rápidos si es necesario. Revise el grado de paralelismo. Para operaciones que se vuelcan a TEMP, evalúe el tamaño de PGA_AGGREGATE_TARGET.","tags":["wait","wait","direct path read","full scan","full table scan","io","pga"]},{"code":"latch: cache buffers chains","title":"Espera: contención en el latch cache buffers chains","severity":"danger","meaning":"Hay contención en el latch cache buffers chains (CBC), que protege los bloques del buffer cache. Esta espera aumenta cuando muchas sesiones intentan acceder a los mismos bloques de acceso frecuente (calientes).","cause":"Hot blocks leídos con frecuencia; SQL ineficiente que genera muchas lecturas lógicas; acceso repetido a los mismos bloques raíz de índice en nested loop joins; acceso concurrente intenso distribuido de forma desigual; tablas pequeñas pero muy leídas.","fix":"Encuentre el SQL que genera más lecturas lógicas (V$SQL, ASH) y reduzca el acceso a buffers mejorando los índices/el plan de acceso. Identifique los hot blocks (X$BH / vistas relacionadas). Considere convertir los nested loops innecesarios en hash joins. Para tablas de referencia pequeñas y muy leídas, distribuya el patrón de acceso o almacénelas en caché.","tags":["latch","cache buffers chains","cbc","contention","contention","hot block","wait","wait"]}]},"errors/postgres-more.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"errors":[{"code":"42883","title":"undefined_function","severity":"caution","meaning":"No se encontró la función (u operador) llamada en la consulta con este nombre y estos tipos de argumentos. PostgreSQL resuelve las funciones por nombre y por tipos de argumentos; si la firma no coincide, se genera este error.","cause":"Un nombre de función mal escrito; tipos de argumentos que no coinciden con la firma definida (p. ej., un número en lugar de texto); falta un cast explícito; el esquema de la función no está en search_path; la extensión necesaria no está instalada.","fix":"Verifique que la función existe y compruebe su firma: \\df function_name. Convierta explícitamente los argumentos: SELECT fn(value::text). Añada el prefijo de esquema o configure search_path. Si se requiere una extensión, instálela: CREATE EXTENSION extension_name; Para errores de operador, haga coincidir los tipos de ambos lados.","tags":["function","function not found","undefined","operator","cast","type conversion","42883"]},{"code":"42804","title":"datatype_mismatch","severity":"caution","meaning":"El tipo de datos esperado y el proporcionado no coinciden. Por ejemplo, asignar a una columna un valor del tipo incorrecto, ramas CASE que devuelven tipos distintos o ramas UNION cuyos tipos no coinciden.","cause":"Una asignación incompatible con el tipo de la columna; ramas CASE/COALESCE que producen tipos distintos; columnas correspondientes de un UNION con tipos distintos; un valor devuelto con RETURN que no coincide con el tipo de retorno de la función; un valor no booleano donde se espera un boolean.","fix":"Convierta la expresión al tipo correcto: value::integer o CAST(value AS numeric). Haga que todas las ramas CASE devuelvan el mismo tipo. En un UNION, alinee los tipos de las columnas correspondientes de ambos lados. Asegúrese de que la condición WHERE produzca un boolean.","tags":["type","data type","mismatch","type mismatch","cast","union","42804"]},{"code":"22P02","title":"invalid_text_representation","severity":"caution","meaning":"Un valor de texto no era una representación válida para el tipo de datos de destino durante la conversión. Por ejemplo, intentar convertir el texto 'abc' a integer genera este error.","cause":"Texto no numérico enviado a una columna/parámetro numérico; una cadena vacía ('') convertida a número/fecha; texto no válido para un UUID o enum; JSON mal formado; datos de entrada que no tienen el formato esperado.","fix":"Verifique que el valor convertido sea realmente válido. Valide la entrada en la aplicación antes de la conversión de tipo. Convierta las cadenas vacías en NULL: NULLIF(value, ''). Para una conversión segura en PostgreSQL 16+, pruebe el valor de antemano con pg_input_is_valid('abc', 'integer') y use pg_input_error_info() para los detalles del error; en versiones anteriores, considere una comprobación de validación (regex). Acote qué fila es la incorrecta.","tags":["cast","type conversion","invalid text","invalid input","integer","uuid","22P02"]},{"code":"22003","title":"numeric_value_out_of_range","severity":"caution","meaning":"Un valor numérico está fuera del rango permitido por el tipo de destino. Por ejemplo, escribir un valor superior a 2 mil millones en una columna integer, o un número demasiado grande para una columna numeric(5,2).","cause":"Superar los límites de smallint/integer/bigint; superar la precisión/escala de numeric(p,s); desbordamiento aritmético; una suma/producto que supera la capacidad del tipo; una secuencia que alcanza el límite superior de su tipo.","fix":"Cambie la columna/parámetro a un tipo más amplio (bigint en lugar de integer, aumente la precisión de numeric): ALTER TABLE my_table ALTER COLUMN my_column TYPE bigint; Redondee (ROUND) o valide el valor según la regla de negocio. Si desborda un cálculo intermedio, calcule la expresión como numeric. Para el desbordamiento de una secuencia, amplíe el tipo.","tags":["numeric","number","out of range","overflow","numeric overflow","integer","22003"]},{"code":"23514","title":"check_violation","severity":"caution","meaning":"Se violó una restricción CHECK. La fila insertada o actualizada no cumple una condición lógica definida en la tabla (p. ej., price > 0).","cause":"Datos que incumplen una regla de negocio (precio negativo, código de estado no válido, violación de rango de fechas); sin validación en el lado de la aplicación; una restricción demasiado estricta para los datos; registros que incumplen la regla durante una carga masiva.","fix":"Use el nombre de la restricción del mensaje de error para averiguar qué condición se violó; vea la definición: \\d table_name. Consulte las filas infractoras invirtiendo la condición. Corrija los datos o, si la regla realmente ha cambiado, actualice la restricción: ALTER TABLE my_table DROP CONSTRAINT name; ALTER TABLE my_table ADD CONSTRAINT name CHECK (new_condition);","tags":["check","constraint","check constraint","violation","constraint violation","validation","23514"]},{"code":"22012","title":"division_by_zero","severity":"caution","meaning":"Una expresión aritmética intentó dividir (o calcular el módulo) por cero. PostgreSQL no permite la división por cero y aborta la consulta.","cause":"La columna/expresión divisora vale 0; un denominador cero en un cálculo de proporción/porcentaje; dividir por un COUNT o SUM que es cero; valores cero inesperados en datos sin filtrar.","fix":"Use NULLIF para devolver NULL cuando el denominador sea cero: numerator / NULLIF(denominator, 0). Si lo desea, convierta el resultado en un valor predeterminado con COALESCE: COALESCE(numerator / NULLIF(denominator,0), 0). O use CASE WHEN denominator = 0 THEN NULL ELSE numerator/denominator END. Filtre de antemano los casos con cero en los datos.","tags":["division","divide","zero","divide by zero","nullif","arithmetic","22012"]},{"code":"42601","title":"syntax_error","severity":"caution","meaning":"Se encontró un error de sintaxis en la sentencia SQL. PostgreSQL no pudo analizar la consulta e indica la ubicación aproximada (position) del error.","cause":"Una coma o paréntesis que falta o sobra; una palabra clave reservada (como user, order) usada como nombre de columna sin comillas; un punto y coma que falta; un comando mal escrito; un literal de cadena sin cerrar; uso de sintaxis específica de una versión.","fix":"Use el valor 'position' del mensaje de error para localizar el problema; inspeccione la parte inmediatamente anterior. Ponga las palabras reservadas entre comillas dobles: \"order\". Compruebe que paréntesis y comillas estén equilibrados. Pruebe una consulta compleja dividiéndola en partes. Consulte la documentación de su versión para la sintaxis correcta.","tags":["syntax","syntax error","parse","parser","keyword","position","42601"]},{"code":"42P07","title":"duplicate_table","severity":"caution","meaning":"La tabla (u objeto similar) que se intenta crear ya existe. CREATE falló porque existe un objeto con el mismo nombre.","cause":"Volver a crear una tabla existente con CREATE TABLE; ejecutar el script dos veces; volver a aplicar migraciones; un conflicto de nombre con una vista/secuencia/índice del mismo nombre.","fix":"Use IF NOT EXISTS para omitir la creación cuando el objeto ya existe: CREATE TABLE IF NOT EXISTS my_table (...). Si realmente quiere volver a crearla, elimínela primero: DROP TABLE IF EXISTS my_table; Haga que las migraciones puedan volver a ejecutarse de forma segura (idempotentes). Compruebe el objeto existente con \\dt.","tags":["duplicate","duplicate object","table","relation","create","already exists","42P07"]},{"code":"42701","title":"duplicate_column","severity":"caution","meaning":"Se usó el mismo nombre de columna más de una vez en una definición de tabla u operación ALTER, o se volvió a añadir una columna que ya existía.","cause":"Escribir el mismo nombre de columna dos veces en CREATE TABLE; añadir una columna ya existente con ALTER TABLE ADD COLUMN; un error al copiar y pegar; una migración que se vuelve a ejecutar.","fix":"Elimine o renombre el nombre de columna duplicado en la definición de la tabla. Compruebe primero para no volver a añadir una columna existente: \\d table_name. Cuando la versión lo admita, añádala de forma segura: ALTER TABLE my_table ADD COLUMN IF NOT EXISTS my_column type; Asegúrese de que las migraciones sean idempotentes.","tags":["duplicate","duplicate column","column","column exists","alter","add column","42701"]},{"code":"3D000","title":"invalid_catalog_name","severity":"danger","meaning":"La base de datos a la que intenta conectarse o acceder no existe. No se encontró ninguna base de datos con el nombre indicado.","cause":"Un nombre de base de datos mal escrito en la cadena de conexión; la base de datos aún no creada o eliminada; distinción de mayúsculas/minúsculas; conexión al servidor equivocado; un nombre incorrecto en la configuración del entorno (env).","fix":"Liste las bases de datos existentes: psql -l o SELECT datname FROM pg_database. Corrija el nombre de la base de datos en la cadena de conexión. Créela si es necesario: CREATE DATABASE database_name; Verifique que se conecta al servidor/puerto correcto. Compruebe el nombre de la BD en la configuración de la aplicación.","tags":["database","database not found","catalog","does not exist","missing","connection","3D000"]},{"code":"3F000","title":"invalid_schema_name","severity":"caution","meaning":"El esquema referenciado no existe o no se encuentra en search_path. No hay ningún esquema con el nombre indicado.","cause":"Un nombre de esquema mal escrito; el esquema nunca se creó; search_path no incluye el esquema; una diferencia de mayúsculas/minúsculas; un prefijo incorrecto al acceder a un objeto con prefijo de esquema.","fix":"Liste los esquemas existentes: \\dn o SELECT schema_name FROM information_schema.schemata. Corrija el nombre del esquema. Créelo si es necesario: CREATE SCHEMA schema_name; Configure search_path: SET search_path TO schema_name, public; Acceda a los objetos con el prefijo de esquema correcto.","tags":["schema","schema not found","search_path","does not exist","missing","invalid","3F000"]},{"code":"0A000","title":"feature_not_supported","severity":"caution","meaning":"La funcionalidad que intenta usar no está soportada en este contexto o en esta versión de PostgreSQL. Aunque la operación solicitada pueda ser válida, no puede aplicarse en la situación actual.","cause":"Sintaxis/funcionalidad aún no disponible en la versión; un ALTER no soportado para un tipo concreto de columna/tabla; una operación no soportada por un foreign data wrapper (FDW); restricciones en combinaciones de replicación lógica/restricciones; conversiones no permitidas para ciertos tipos de datos.","fix":"Lea el mensaje de error completo; normalmente indica claramente qué no está soportado. Use un enfoque alternativo (p. ej., columna nueva + copia + renombrado en lugar de cambiar directamente el tipo de la columna). Si la funcionalidad llegó en una versión más reciente, considere actualizar. Si se topa con límites de FDW/extensiones, consulte la documentación.","tags":["not supported","unsupported","feature","unsupported feature","version","upgrade","0A000"]},{"code":"57P03","title":"cannot_connect_now","severity":"danger","meaning":"El servidor no puede aceptar conexiones en este momento; normalmente la base de datos aún está arrancando o realizando una recuperación. Podrán abrirse conexiones cuando el sistema esté listo.","cause":"PostgreSQL acaba de iniciarse y aún está en su fase de arranque/recuperación; recuperación tras una caída; durante una restauración desde backup; un standby que aún no está listo para aceptar consultas; un apagado en curso.","fix":"Espere un momento y vuelva a conectarse; el servidor acepta conexiones cuando termina el arranque. Supervise el progreso de la recuperación en el log del servidor. Si tarda mucho, puede haber un replay de WAL grande o un problema de recuperación. Añada lógica de reintento a la aplicación.","tags":["connection","connection refused","starting up","startup","recovery","crash recovery","57P03"]},{"code":"2BP01","title":"dependent_objects_still_exist","severity":"caution","meaning":"Se bloqueó la eliminación porque otros objetos dependen del objeto que se quiere eliminar. DROP falla mientras existan objetos dependientes.","cause":"Intentar eliminar una tabla de la que dependen vistas/claves foráneas/funciones; objetos que dependen de un tipo o esquema; DROP sin CASCADE; una columna/secuencia referenciada por otros objetos.","fix":"Elimine primero los objetos dependientes o elimine con CASCADE: DROP TABLE my_table CASCADE; (recuerde que CASCADE también elimina los objetos dependientes; tenga cuidado). Para ver las dependencias, lea el detalle del error (línea DETAIL) o inspeccione pg_depend/los catálogos correspondientes. Planifique correctamente el orden de eliminación.","tags":["dependency","dependent objects","cascade","drop","objects","object","2BP01"]},{"code":"55006","title":"object_in_use","severity":"danger","meaning":"La operación no puede realizarse sobre el objeto de destino (normalmente una base de datos) porque otras sesiones lo están usando. Por ejemplo, no se puede eliminar una base de datos con conexiones activas.","cause":"Conexiones abiertas a la base de datos durante DROP DATABASE; una operación que requiere un bloqueo exclusivo sobre un objeto en uso; otra sesión que mantiene el objeto.","fix":"Encuentre y termine las conexiones abiertas a la base de datos de destino: SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'db_name' AND pid <> pg_backend_pid(); Bloquee las conexiones nuevas (si es necesario). En PostgreSQL 13+, elimínela cerrando las conexiones a la fuerza: DROP DATABASE db_name WITH (FORCE);","tags":["in use","being accessed","object","object in use","drop database","connection","55006"]},{"code":"22007","title":"invalid_datetime_format","severity":"caution","meaning":"Un valor de texto no tenía un formato válido al convertirse a un tipo de fecha/hora (date, time, timestamp). El valor no coincide con el formato de fecha-hora esperado.","cause":"Texto de fecha en un formato inesperado (confusión en el orden día/mes/año); valores de día/hora no válidos; convertir una cadena vacía ('') en fecha; componentes que faltan o sobran; discrepancia de configuración regional/formato; una máscara de formato incorrecta en to_timestamp/to_date.","fix":"Convierta el texto de fecha con el formato correcto: to_date('2026-07-04', 'YYYY-MM-DD') o to_timestamp(str, 'YYYY-MM-DD HH24:MI:SS'). Prefiera el formato ISO 8601 (YYYY-MM-DD). Convierta los valores vacíos en NULL con NULLIF(value,''). Valide los datos de entrada antes de cargarlos; acote la fila incorrecta.","tags":["date","date format","datetime","format","timestamp","to_date","22007"]}]},"errors/mysql-more.json":{"engineId":"mysql","engine":"MySQL","order":3,"errors":[{"code":"1052","title":"Column 'x' in field list is ambiguous (columna ambigua en la lista de campos)","severity":"caution","meaning":"Existe una columna con el mismo nombre en más de una tabla de la consulta y, como se usa sin prefijo de tabla/alias, MySQL no puede saber a qué tabla pertenece.","cause":"Ambas tablas combinadas tienen una columna con el mismo nombre (p. ej., id en ambas); la columna se escribe sin calificar (sin prefijo) en SELECT, WHERE, ON, GROUP BY u ORDER BY.","fix":"Califique cada columna ambigua con el nombre o alias de la tabla: SELECT a.id, b.id FROM a JOIN b ON a.id = b.a_id; Asigne alias cortos a las tablas y anteponga el prefijo a todas las columnas en conflicto. Reduzca la ambigüedad en joins sobre columnas comunes con USING(my_column).","tags":["ambiguous","ambiguous column","column","column name","join","alias","1052"]},{"code":"1136","title":"Column count doesn't match value count (el número de columnas no coincide con el de valores)","severity":"caution","meaning":"El número de columnas de la sentencia INSERT no coincide con el número de valores de la lista VALUES. Cada columna debe recibir exactamente un valor.","cause":"La lista de columnas y la lista de valores de un INSERT tienen longitudes distintas; en un INSERT sin lista de columnas, los valores no coinciden con el número de columnas de la tabla; una coma que falta o sobra; la estructura de la tabla cambió pero el script no se actualizó.","fix":"Iguale el número de columnas y de valores. Use siempre una lista de columnas explícita: INSERT INTO my_table (a, b, c) VALUES (1, 2, 3); Compruebe la estructura de la tabla: DESCRIBE my_table; Haga corresponder los valores uno a uno con las columnas y corrija las comas que sobran/faltan.","tags":["column count","column count mismatch","insert","values","mismatch","value count","1136"]},{"code":"1048","title":"Column 'x' cannot be null (la columna no puede ser nula)","severity":"caution","meaning":"Se intentó insertar NULL en una columna definida como NOT NULL, o asignar NULL a esa columna. Un campo obligatorio no puede quedar vacío.","cause":"No se indicó valor para una columna obligatoria en el INSERT; un UPDATE que asigna NULL a la columna; la columna no tiene DEFAULT; un valor vacío procedente de la aplicación/una subconsulta; el modo SQL estricto no rellena silenciosamente el valor vacío.","fix":"Proporcione un valor válido para la columna obligatoria. Defina un valor predeterminado razonable para la columna: ALTER TABLE my_table MODIFY my_column INT NOT NULL DEFAULT 0; Si la regla no es necesaria, permita NULL: ALTER TABLE my_table MODIFY my_column INT NULL; Valide los valores vacíos en la aplicación antes de enviarlos.","tags":["not null","null","empty value","insert","cannot be null","mandatory","1048"]},{"code":"1292","title":"Incorrect datetime value / truncated incorrect value (valor de fecha/hora incorrecto / valor truncado)","severity":"caution","meaning":"Un valor era no válido o estaba mal formado al convertirse al tipo de la columna de destino (sobre todo fecha/hora o numérico). En modo estricto, MySQL lo rechaza como error.","cause":"Fechas o valores no válidos como '0000-00-00'; un formato de fecha incorrecto; texto no numérico en una columna numérica; modo SQL estricto (STRICT_TRANS_TABLES) activo; problemas de conversión causados por un conjunto de caracteres/collation distinto.","fix":"Lleve el valor a un formato válido para el tipo de la columna ('YYYY-MM-DD' para fechas). Use NULL en lugar de fechas vacías/no válidas. Si es necesario, indique un formato explícito con STR_TO_DATE: STR_TO_DATE(str, '%Y-%m-%d'). Si se requiere tolerancia temporal, revise sql_mode, pero preferiblemente corrija los datos. Acote la búsqueda para encontrar la fila problemática.","tags":["datetime","date","truncated","incorrect value","strict mode","format","1292"]},{"code":"1406","title":"Data too long for column (datos demasiado largos para la columna)","severity":"caution","meaning":"Se escribió un valor más largo que la longitud máxima definida de la columna. Por ejemplo, una cadena de 20 caracteres no cabe en una columna VARCHAR(10).","cause":"Ancho de columna demasiado pequeño para los datos; caracteres de un conjunto multibyte (utf8mb4) que ocupan más espacio del esperado; el resultado de una concatenación que crece en longitud; modo SQL estricto activo (de lo contrario el valor se trunca silenciosamente).","fix":"Amplíe la columna: ALTER TABLE my_table MODIFY my_column VARCHAR(255); Para contenido muy largo, considere el tipo TEXT. Si hay que acortar el valor, recórtelo en la aplicación o con SUBSTRING. Encuentre la fila que desborda: SELECT * FROM my_table WHERE CHAR_LENGTH(my_column) > 10; Añada validación de entrada.","tags":["data too long","length","varchar","column","column length","truncate","1406"]},{"code":"1071","title":"Specified key was too long; max key length is 767 bytes (clave demasiado larga; máximo 767 bytes)","severity":"caution","meaning":"El índice (o clave) que se está creando superó la longitud máxima permitida en bytes. Sobre todo con conjuntos de caracteres multibyte, los índices VARCHAR alcanzan rápidamente este límite.","cause":"Un índice sobre una columna VARCHAR ancha con utf8mb4 (4 bytes por carácter); un formato de fila InnoDB antiguo (REDUNDANT/COMPACT; en 5.7 y anteriores también innodb_large_prefix deshabilitado); la longitud total de un índice compuesto de varias columnas que supera el límite; una clave primaria grande.","fix":"Acorte la longitud de la columna indexada o use un índice de prefijo: CREATE INDEX idx ON my_table (my_column(191)); Pase al límite de 3072 bytes con el formato de fila DYNAMIC/COMPRESSED (DYNAMIC es el predeterminado en MySQL 8.0; innodb_large_prefix se eliminó en 8.0 y solo es necesario en 5.7 y anteriores). Si es necesario, revise el conjunto de caracteres/longitud de la columna.","tags":["index","index length","key too long","key","prefix","utf8mb4","1071"]},{"code":"1050","title":"Table already exists (la tabla ya existe)","severity":"caution","meaning":"La tabla que se intenta crear ya existe. CREATE TABLE falló porque existe una tabla con el mismo nombre.","cause":"Volver a crear una tabla existente; un script ejecutado dos veces; migraciones aplicadas de nuevo; otro objeto (una vista) que usa el mismo nombre.","fix":"Use IF NOT EXISTS para crear la tabla solo si no existe: CREATE TABLE IF NOT EXISTS my_table (...); Si realmente quiere volver a crearla, elimínela primero: DROP TABLE IF EXISTS my_table; Liste las tablas existentes: SHOW TABLES; Haga que las migraciones puedan volver a ejecutarse de forma segura (idempotentes).","tags":["table exists","table already exists","duplicate","duplicate table","create table","1050"]},{"code":"1051","title":"Unknown table 'x' (tabla desconocida)","severity":"caution","meaning":"La tabla referenciada no existe. No se encontró ninguna tabla con el nombre indicado durante una eliminación, modificación o consulta.","cause":"Un nombre de tabla mal escrito; la tabla nunca se creó o ya se eliminó; está seleccionada la base de datos (esquema) equivocada; distinción de mayúsculas/minúsculas (los nombres de tabla distinguen mayúsculas en Linux); DROP TABLE sobre una tabla inexistente.","fix":"Liste las tablas existentes: SHOW TABLES; y seleccione la base de datos correcta: USE mydb; Corrija el nombre de la tabla y sus mayúsculas/minúsculas. Para evitar el error al eliminar: DROP TABLE IF EXISTS my_table; Si está en otro esquema, califíquela como schema.table.","tags":["unknown table","table not found","does not exist","drop","not exists","1051"]},{"code":"1091","title":"Can't DROP 'x'; check that column/key exists (no se puede hacer DROP; compruebe que existe la columna/clave)","severity":"caution","meaning":"La columna, el índice o la clave foránea que se quiere eliminar con ALTER TABLE no existe. No se puede eliminar un objeto inexistente.","cause":"Indicar un nombre de objeto inexistente en DROP COLUMN/INDEX/FOREIGN KEY; un nombre mal escrito; el objeto ya eliminado; confundir el nombre de la clave foránea con el del índice; una migración ejecutada por segunda vez.","fix":"Verifique el nombre real del objeto: SHOW CREATE TABLE my_table; o SHOW INDEX FROM my_table; Elimínelo usando el nombre correcto. Para una clave foránea use su nombre de símbolo: ALTER TABLE my_table DROP FOREIGN KEY fk_name; MySQL no tiene la sintaxis DROP COLUMN IF EXISTS (solo MariaDB); en migraciones idempotentes, compruebe primero que la columna existe mediante information_schema.columns: SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'my_table' AND column_name = 'my_column';","tags":["cant drop","cannot drop","column","index","foreign key","alter","1091"]},{"code":"1093","title":"You can't specify target table 'x' for update in FROM clause (no se puede usar la tabla de destino del update en FROM)","severity":"caution","meaning":"En una sentencia UPDATE o DELETE, la propia tabla que se actualiza/elimina no puede usarse directamente como origen en una subconsulta (en FROM). MySQL no permite que la misma tabla sea a la vez destino y origen de esta forma.","cause":"Una subconsulta que lee directamente la misma tabla de destino, como en UPDATE my_table ... WHERE id IN (SELECT ... FROM my_table ...); una subconsulta que referencia la misma tabla en un DELETE.","fix":"Envuelva la subconsulta en una tabla derivada y asígnele un alias nuevo para que MySQL la trate como una copia separada: UPDATE my_table SET c = 1 WHERE id IN (SELECT id FROM (SELECT id FROM my_table WHERE condition) AS t); Como alternativa, escríbala como un self-join: UPDATE my_table a JOIN my_table b ON ... SET a.c = ...;","tags":["update","delete","target table","same table","subquery","derived table","1093"]},{"code":"1305","title":"PROCEDURE does not exist (el procedimiento no existe)","severity":"caution","meaning":"No se encontró en la base de datos indicada el procedimiento almacenado o la función llamada. No hay ninguna rutina definida con este nombre.","cause":"Un nombre de procedimiento mal escrito; el procedimiento está en otra base de datos o nunca se creó; se seleccionó el esquema equivocado con USE; sin privilegio EXECUTE para llamar al procedimiento; distinción de mayúsculas/minúsculas.","fix":"Verifique que el procedimiento existe y en qué esquema: SHOW PROCEDURE STATUS WHERE Db = 'mydb'; Seleccione la base de datos correcta: USE mydb; o llámelo con el nombre completo: CALL mydb.my_procedure(...); Otorgue los privilegios necesarios: GRANT EXECUTE ON PROCEDURE mydb.my_procedure TO app_user;","tags":["procedure","routine","does not exist","not found","stored procedure","call","1305"]},{"code":"1227","title":"Access denied; you need (at least one of) the PRIVILEGE (acceso denegado; se necesita el privilegio)","severity":"danger","meaning":"El usuario carece del privilegio necesario para la operación que intenta realizar. La operación requiere un derecho que va más allá de los permisos concedidos a la cuenta.","cause":"Falta un privilegio administrativo como SUPER, PROCESS, RELOAD o CREATE USER; no se concedió el permiso necesario para leer una vista/tabla global; una operación concreta (SET GLOBAL, acceso al binlog) que requiere privilegios elevados.","fix":"Vea los privilegios actuales: SHOW GRANTS FOR CURRENT_USER(); Otorgue el permiso necesario con una cuenta privilegiada: GRANT PROCESS ON *.* TO 'app_user'@'host'; (GRANT surte efecto de inmediato; no se necesita FLUSH PRIVILEGES). Cuando sea posible, use privilegios dinámicos (p. ej., SYSTEM_VARIABLES_ADMIN) en lugar de SUPER. Siga el principio de mínimo privilegio.","tags":["access denied","privilege","missing privilege","permission","grant","access","1227"]},{"code":"3730","title":"Cannot drop table referenced by a foreign key constraint (no se puede eliminar una tabla referenciada por una clave foránea)","severity":"danger","meaning":"No se puede eliminar la tabla porque la clave foránea de otra tabla la referencia. Mientras exista la referencia, DROP está bloqueado.","cause":"Intentar eliminar una tabla padre mientras aún existen tablas hijas vinculadas a ella por una FK; no respetar el orden de dependencias; no eliminar primero las claves foráneas.","fix":"Elimine primero la clave foránea que hace referencia: ALTER TABLE child_table DROP FOREIGN KEY fk_name; y luego elimine la tabla. Encuentre las tablas/restricciones dependientes: SELECT * FROM information_schema.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_NAME = 'my_table'; En operaciones masivas puede usarse temporalmente SET FOREIGN_KEY_CHECKS = 0;, pero tenga cuidado y vuelva a ponerlo en 1 después.","tags":["foreign key","fk","drop table","referenced","reference","constraint","3730"]},{"code":"2013","title":"Lost connection to MySQL server during query (se perdió la conexión con el servidor MySQL durante la consulta)","severity":"danger","meaning":"La conexión entre el cliente y el servidor MySQL se cortó mientras se ejecutaba la consulta. Se interrumpió antes de que el servidor pudiera responder o devolver resultados.","cause":"Una consulta de larga duración que supera net_read_timeout/net_write_timeout; el servidor termina la consulta/conexión por falta de memoria (OOM); un corte de red; se supera max_allowed_packet; el servidor se cae o se reinicia; un conjunto de resultados muy grande.","fix":"Compruebe la estabilidad de la red y el error log del servidor; vea si hubo un OOM o una caída. Aumente los timeouts: SET GLOBAL net_read_timeout = 120; Divida las consultas grandes en lotes. Aumente max_allowed_packet. Añada lógica de reintento de conexión en la aplicación. Optimice las consultas largas.","tags":["lost connection","connection dropped","timeout","network","network outage","oom","2013"]},{"code":"1153","title":"Got a packet bigger than 'max_allowed_packet' bytes (paquete mayor que max_allowed_packet)","severity":"danger","meaning":"El cliente o el servidor intentó enviar un paquete de datos mayor que el tamaño máximo de paquete permitido (max_allowed_packet). Suele ocurrir con valores BLOB/TEXT grandes o INSERT masivos muy grandes.","cause":"Insertar datos BLOB/TEXT muy grandes; un INSERT único enorme o un INSERT masivo de varias filas; una consulta que devuelve un resultado grande; un valor pequeño de max_allowed_packet; importar filas grandes con mysqldump.","fix":"Aumente max_allowed_packet (en servidor y cliente): SET GLOBAL max_allowed_packet = 67108864; (64MB) y vuelva a conectarse. Para hacerlo permanente, añada max_allowed_packet = 64M bajo [mysqld] en my.cnf. Divida las operaciones masivas grandes en lotes más pequeños. Evalúe si realmente necesita almacenar valores muy grandes.","tags":["max_allowed_packet","packet","packet size","blob","large data","insert","1153"]},{"code":"1969","title":"MariaDB: Query execution was interrupted (max_statement_time exceeded) (ejecución de la consulta interrumpida)","severity":"caution","meaning":"Específico de MariaDB (ER_STATEMENT_TIMEOUT): la consulta se interrumpió porque superó el tiempo máximo de ejecución definido por max_statement_time. MySQL no tiene este código; el equivalente en MySQL es el error 3024 (ER_QUERY_TIMEOUT, max_execution_time).","cause":"En MariaDB, superar el límite max_statement_time de sesión/global o un límite fijado con SET STATEMENT max_statement_time=N FOR ...; un plan ineficiente, un índice que falta, un recorrido de datos grande o esperas de bloqueo.","fix":"Analice la consulta con EXPLAIN y acelérela añadiendo índices y mejorando el plan. Si la consulta realmente necesita ejecutarse durante mucho tiempo, eleve el límite en MariaDB: SET SESSION max_statement_time = 60; (segundos) o SET STATEMENT max_statement_time = 60 FOR SELECT ...; Si usa MySQL, consulte la entrada 3024 (max_execution_time, milisegundos; hint MAX_EXECUTION_TIME). Divida las operaciones grandes.","tags":["timeout","statement timeout","interrupted","query interrupted","max_execution_time","query","1969"]}]},"errors/mssql-more.json":{"engineId":"mssql","engine":"SQL Server","order":4,"errors":[{"code":"8134","title":"Divide by zero error encountered (error de división por cero)","severity":"caution","meaning":"Se produjo un error de división por cero porque el divisor de una expresión aritmética es cero. SQL Server no puede completar la división y detiene la consulta.","cause":"Una columna o expresión usada como divisor vale 0, un denominador cero en cálculos de promedio/proporción, división sin NULLIF o valores cero inesperados en los datos.","fix":"Proteja el denominador con NULLIF (p. ej., value / NULLIF(denominator, 0)); el resultado pasa a ser NULL y puede proporcionarse un valor predeterminado con ISNULL/COALESCE. Use CASE WHEN denominator = 0 THEN NULL ELSE value/denominator END. Filtre los valores cero con WHERE antes de dividir. Si es necesario, evalúe el comportamiento de SET ARITHABORT/ANSI_WARNINGS.","tags":["math","arithmetic","divide by zero","division by zero","8134"]},{"code":"245","title":"Conversion failed when converting the value to data type (falló la conversión del valor al tipo de datos)","severity":"caution","meaning":"No se pudo convertir un valor al tipo de datos de destino. Por ejemplo, una conversión implícita/explícita falló por contenido inadecuado al convertir un valor de cadena a int o date.","cause":"Convertir texto no numérico a int/decimal, un formato de fecha no válido, conversión implícita de tipos en comparaciones de columnas, tipos incompatibles en una condición WHERE o JOIN, o datos de entrada mal formados.","fix":"Use las variantes de conversión segura: TRY_CAST o TRY_CONVERT (devuelven NULL si fallan). Identifique las filas incorrectas con ISNUMERIC/TRY_CONVERT. Alinee los tipos de las columnas y elimine la conversión implícita. Valide la entrada y use un formato explícito para las fechas (p. ej., un estilo de CONVERT).","tags":["conversion","type conversion","cast","convert","data type","245"]},{"code":"515","title":"Cannot insert the value NULL into column (no se puede insertar NULL en la columna)","severity":"caution","meaning":"Se intentó insertar o actualizar un valor NULL en una columna definida como NOT NULL. La operación se rechazó porque la columna no admite nulos.","cause":"Una columna obligatoria omitida en la sentencia INSERT, campos ausentes/vacíos en los datos de origen, una columna NOT NULL sin valor predeterminado o NULL enviado por la aplicación.","fix":"Proporcione un valor válido para las columnas obligatorias. Añada restricciones DEFAULT a las columnas adecuadas. Valide los datos de origen en busca de campos ausentes y asigne valores predeterminados con ISNULL/COALESCE. Si la columna realmente no necesita ser obligatoria, revise el esquema para permitir NULL.","tags":["null","not null","insert","constraint","constraint violation","515"]},{"code":"8115","title":"Arithmetic overflow error converting expression to data type (desbordamiento aritmético al convertir la expresión)","severity":"caution","meaning":"El valor producido por una operación aritmética o una conversión superó el rango que puede contener el tipo de datos de destino. Se produjo un desbordamiento porque el número es demasiado grande.","cause":"Una suma/producto (SUM) que supera el rango de int, un valor grande asignado a una columna de tipo pequeño, precisión/escala decimal insuficiente o un valor IDENTITY que alcanza el límite del tipo.","fix":"Use un tipo más amplio para los cálculos intermedios (p. ej., SUM con CAST(my_column AS bigint)). Aumente la precisión/escala decimal. Amplíe el tipo de la columna para que quepan los datos. Convierta las expresiones con riesgo de desbordamiento antes de calcular y limite los rangos de entrada.","tags":["overflow","arithmetic overflow","arithmetic","math","int","bigint","8115"]},{"code":"512","title":"Subquery returned more than 1 value (la subconsulta devolvió más de un valor)","severity":"caution","meaning":"Una subconsulta usada en un contexto que espera un único valor (=, <, >, comparación o asignación) devolvió más de una fila. SQL Server esperaba un único valor escalar pero encontró varios valores.","cause":"Una subconsulta que se suponía que devolvía un único valor para la comparación devuelve varias filas, una condición de coincidencia no única, un filtro que falta o una consulta correlacionada mal construida.","fix":"Reduzca la subconsulta a una sola fila (p. ej., TOP 1, MAX/MIN o un GROUP BY adecuado). Si se esperan varios valores, use IN o EXISTS en lugar de =. Reescríbala con un JOIN. Investigue por qué la subconsulta devuelve varias filas y corrija las condiciones de filtro/clave.","tags":["subquery","scalar subquery","more than one","512","in","exists"]},{"code":"207","title":"Invalid column name (nombre de columna no válido)","severity":"caution","meaning":"No se encontró en la tabla/vista el nombre de columna referenciado en la consulta. No existe ninguna columna con ese nombre o está fuera de ámbito.","cause":"Un error ortográfico o de mayúsculas/minúsculas, el alias de tabla equivocado, una columna aún no añadida o renombrada, el contexto de esquema/tabla equivocado u omitir los corchetes en lugar de comillas.","fix":"Verifique el nombre de la columna y la tabla (consulte sys.columns u Object Explorer). Use el alias y el esquema correctos. Para columnas recién añadidas, asegúrese de que el objeto esté actualizado; recompile el procedimiento almacenado si es necesario. Use corchetes para nombres que contengan espacios/caracteres especiales.","tags":["column","column name","invalid column","invalid column name","alias","207"]},{"code":"156","title":"Incorrect syntax near the keyword (sintaxis incorrecta cerca de la palabra clave)","severity":"caution","meaning":"Hay un error de sintaxis cerca de una palabra clave. SQL Server no pudo analizar la sentencia porque se usa una palabra reservada en una posición inesperada.","cause":"Usar una palabra reservada (p. ej., ORDER, USER, TABLE) como nombre de columna/objeto sin corchetes, una coma/paréntesis que falta, cláusulas en orden incorrecto o una cláusula mal ubicada (WHERE, GROUP BY).","fix":"Inspeccione la zona alrededor de la palabra clave que señala el error. Si usa palabras reservadas como nombres de objetos, enciérrelas entre corchetes (p. ej., [User]). Corrija las comas, los paréntesis y el orden de las cláusulas que falten. Pruebe la sintaxis dividiendo la consulta en partes.","tags":["syntax","syntax error","keyword","reserved word","156"]},{"code":"2812","title":"Could not find stored procedure (no se encontró el procedimiento almacenado)","severity":"caution","meaning":"No se encontró el procedimiento almacenado llamado con EXEC en el ámbito actual de base de datos/esquema. No existe ningún procedimiento con ese nombre.","cause":"Un error tipográfico en el nombre del procedimiento, un prefijo de esquema ausente o incorrecto (p. ej., se omitió dbo.), el procedimiento está en otra base de datos, aún no se ha creado o no es visible por permisos.","fix":"Verifique el nombre del procedimiento y el esquema; use el nombre completo en la llamada (p. ej., EXEC dbo.ProcedureName). Asegúrese de estar en la base de datos correcta (USE DatabaseName). Compruebe que el procedimiento existe (sys.procedures / OBJECT_ID). Otorgue el permiso EXECUTE necesario y verifique que se ejecutó el script de despliegue.","tags":["stored procedure","procedure","sp","not found","procedure not found","2812"]},{"code":"8623","title":"The query processor ran out of internal resources and could not produce a query plan (el procesador de consultas no pudo generar un plan)","severity":"danger","meaning":"El procesador de consultas agotó sus recursos internos con una consulta excesivamente compleja y no pudo generar un plan de ejecución. La consulta no puede optimizarse porque es demasiado grande/compleja.","cause":"Consultas con listas IN muy largas o muchas cláusulas OR/UNION, un número excesivo de JOIN, consultas enormes generadas automáticamente, consultas con demasiados parámetros o árboles de expresiones excesivamente profundos.","fix":"Simplifique la consulta: mueva las listas IN largas a una tabla temporal/table-valued parameter, reduzca las condiciones OR con UNION ALL o una reescritura, y divida la consulta compleja en partes. Reduzca el número de JOIN. Procese por lotes las consultas generadas automáticamente. Revise los índices y el diseño de la consulta cuando sea necesario.","tags":["query processor","internal resources","plan","complex query","8623","danger"]},{"code":"4104","title":"The multi-part identifier could not be bound (no se pudo vincular el identificador de varias partes)","severity":"caution","meaning":"No se pudo resolver un identificador de varias partes separado por puntos (p. ej., table.column o alias.column). SQL Server no pudo vincular este nombre a una tabla/columna de la consulta.","cause":"Un alias de tabla incorrecto o no definido, una referencia a una tabla que no está en FROM/JOIN, una columna atribuida a la tabla equivocada, usar el nombre real de la tabla tras definir un alias o un error tipográfico.","fix":"Verifique que los alias usados estén definidos en la cláusula FROM/JOIN. Califique la columna con la tabla/alias correcto. Si asignó un alias a una tabla, use el alias en todas partes (no el nombre real). Añada la tabla que falta al JOIN y compruebe los nombres.","tags":["identifier","multi-part","multi-part identifier","bind","alias","4104"]},{"code":"2714","title":"There is already an object named in the database (ya existe un objeto con ese nombre en la base de datos)","severity":"caution","meaning":"Ya existe en la base de datos un objeto con el mismo nombre que el que se está creando (tabla, vista, procedimiento, etc.). La sentencia CREATE falló por un conflicto de nombres.","cause":"Ejecutar el mismo script CREATE por segunda vez, no eliminar antes el objeto existente, una suposición de esquema distinta, scripts de despliegue/migración no idempotentes o un conflicto de nombres.","fix":"Compruebe primero si el objeto existe: créelo de forma condicional con IF OBJECT_ID('dbo.Name') IS NULL, o use CREATE OR ALTER (para procedimientos/vistas/funciones). Si es necesario, haga DROP del objeto existente y vuelva a crearlo. Escriba scripts de migración idempotentes. Verifique que trabaja en el esquema/base de datos correctos.","tags":["already exists","object exists","create","object","duplicate object","2714"]},{"code":"601","title":"Could not continue scan with NOLOCK due to data movement (no se pudo continuar el recorrido con NOLOCK por movimiento de datos)","severity":"caution","meaning":"Durante un recorrido con NOLOCK (READ UNCOMMITTED), el recorrido no pudo continuar por un movimiento de datos concurrente (page splits/movimientos). La lectura sin bloqueos llegó a un estado inconsistente.","cause":"Páginas recorridas que se reubican por actualizaciones/eliminaciones/page splits concurrentes mientras se lee con el hint NOLOCK o el nivel de aislamiento READ UNCOMMITTED.","fix":"Vuelva a ejecutar la consulta (suele ser transitorio). Evite el hint NOLOCK cuando sea posible; use el aislamiento READ COMMITTED SNAPSHOT (RCSI) para lecturas no bloqueantes y a la vez consistentes. En tablas con muchas escrituras, prefiera snapshot isolation a las lecturas sucias.","tags":["nolock","scan","data movement","601","isolation","read uncommitted"]},{"code":"50000","title":"Error personalizado generado con RAISERROR","severity":"caution","meaning":"Es el número predeterminado (50000) para errores personalizados (definidos por el usuario) generados con RAISERROR sin indicar un ID de mensaje. La lógica de la aplicación/procedimiento generó un error de forma deliberada.","cause":"Un error personalizado generado con RAISERROR o THROW en un procedimiento almacenado o script ante una violación de regla de negocio, un fallo de validación, control de flujo deliberado o mensajes de excepción personalizados.","fix":"Lea el texto del mensaje de RAISERROR/THROW para entender la violación real de la regla de negocio. Capture este error en el lado de la aplicación con TRY...CATCH y obtenga los detalles con ERROR_MESSAGE()/ERROR_NUMBER(). Defina mensajes con sp_addmessage para dar números significativos a sus errores personalizados. Corrija en la lógica de negocio la condición que lo provoca.","tags":["raiserror","custom error","user-defined error","throw","50000","try catch"]},{"code":"3902","title":"The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION (COMMIT TRANSACTION sin BEGIN TRANSACTION correspondiente)","severity":"caution","meaning":"Se ejecutó un COMMIT TRANSACTION, pero no hay ningún BEGIN TRANSACTION abierto correspondiente. No se encontró ninguna transacción que confirmar porque el contador de transacciones (@@TRANCOUNT) es cero.","cause":"Uso desequilibrado de BEGIN/COMMIT/ROLLBACK, la transacción ya revertida tras un error, gestión incorrecta de los contadores de transacciones anidadas o llamar de nuevo a COMMIT tras un ROLLBACK en un bloque CATCH.","fix":"Equilibre el número de sentencias BEGIN y COMMIT. Compruebe @@TRANCOUNT antes de confirmar (IF @@TRANCOUNT > 0 COMMIT). En TRY...CATCH, ante un error haga ROLLBACK según XACT_STATE()/@@TRANCOUNT y evite un COMMIT innecesario. Revise la lógica de transacciones anidadas.","tags":["transaction","commit","begin","trancount","transaction count","3902"]}]},"errors/mongodb-more.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"errors":[{"code":"121","title":"DocumentValidationFailure","severity":"caution","meaning":"El documento que se inserta o actualiza se rechazó porque no cumple las reglas de validación JSON Schema (validator) definidas en la colección.","cause":"El documento no se ajusta al esquema: falta un campo obligatorio, un tipo BSON incorrecto, un valor fuera del rango/patrón permitido, campos adicionales prohibidos o una violación de regla mientras validationAction es 'error'.","fix":"Inspeccione los detalles del error (errInfo) para ver qué regla se violó. Haga que el documento se ajuste al esquema: añada los campos obligatorios y corrija los tipos. Revise el validator de la colección con collMod. validationLevel/validationAction pueden relajarse temporalmente, pero es preferible mantener las reglas por la calidad de los datos.","tags":["validation","validation","schema","schema","121","document"]},{"code":"40","title":"ConflictingUpdateOperators","severity":"caution","meaning":"Una actualización aplicó más de un operador de actualización en conflicto sobre el mismo campo (path). MongoDB no puede modificar el mismo path dos veces en una sola operación.","cause":"Operadores en conflicto que apuntan al mismo campo en un documento de actualización (p. ej., $set y $inc a la vez, o modificar al mismo tiempo un path padre y uno hijo), una actualización mal construida o un conflicto de paths anidados.","fix":"Fusione en un solo operador los operadores en conflicto sobre el mismo campo, o divida la actualización en operaciones separadas. No modifique al mismo tiempo un path padre y uno hijo (p. ej., a y a.b). Revise el documento de actualización y asegúrese de que cada campo se modifique una sola vez.","tags":["update","update","operator","conflict","conflict","40"]},{"code":"66","title":"ImmutableField (cannot update _id) (no se puede actualizar _id)","severity":"caution","meaning":"Se intentó actualizar un campo inmutable, normalmente el campo _id del documento. _id no puede cambiarse después de crear el documento.","cause":"Una actualización que intenta cambiar el campo _id, actualizar un campo de shard key de una forma no permitida o asignar un nuevo valor a un campo tratado como inmutable.","fix":"No actualice el valor de _id; si se necesita otro _id, inserte el documento con el nuevo _id y elimine el antiguo (insert + delete). Siga las reglas de MongoDB para actualizar la shard key. Elimine los campos inmutables del documento de actualización y asigne solo campos modificables.","tags":["immutable","_id","immutable","update","shard key","66"]},{"code":"2","title":"BadValue","severity":"caution","meaning":"Se pasó un valor no válido o inesperado a un comando o consulta. MongoDB no pudo interpretar el argumento como un valor aceptable.","cause":"Un argumento de operador no válido, un número fuera de rango, un valor de opción no soportado, una especificación de ordenación mal formada, una expresión de consulta mal formada o una combinación inadecuada de parámetros.","fix":"Corrija el argumento según la pista de campo/valor del mensaje de error. Valide las opciones de comandos y consultas con la documentación. Compruebe el formato de las expresiones sort/projection/aggregate. Lleve los rangos y tipos de valores al rango válido.","tags":["badvalue","invalid value","argument","2","query"]},{"code":"14","title":"TypeMismatch","severity":"caution","meaning":"El tipo del valor proporcionado no coincide con el tipo BSON esperado. Se esperaba un tipo de datos distinto para un campo o argumento.","cause":"Texto proporcionado donde se espera un número, un string usado en lugar de una fecha, un valor único enviado donde se espera un array, un argumento del tipo incorrecto pasado a un operador de agregación/consulta o incoherencia de tipos en el esquema.","fix":"Proporcione el tipo BSON correcto para el campo/argumento. Convierta los tipos en la capa de aplicación antes de guardar los datos (p. ej., escriba los números como valores Number reales). Verifique los tipos que esperan los operadores de consulta y agregación. Imponga la coherencia de tipos con un validador de esquema.","tags":["typemismatch","type mismatch","bson","type","14"]},{"code":"10334","title":"BSONObjectTooLarge","severity":"danger","meaning":"Un documento BSON superó el tamaño máximo permitido (16 MB). MongoDB no puede almacenar ni devolver un único documento que supere este límite.","cause":"Superar el límite de 16 MB al añadir continuamente datos/elementos de array a un documento, arrays embebidos muy grandes, documentos enormes producidos por una agregación o datos binarios grandes almacenados directamente en el documento.","fix":"Revise el modelo de datos: divida los arrays grandes en colecciones separadas y use un modelo de referencias. Use GridFS para archivos binarios grandes. Acote los arrays sin límite (p. ej., el patrón capped/bucket). Mantenga bajo control el tamaño de los documentos en las etapas de agregación.","tags":["bson","16mb","too large","large document","gridfs","10334","danger"]},{"code":"46","title":"LockBusy","severity":"caution","meaning":"No se pudo adquirir de inmediato el bloqueo o recurso solicitado porque lo mantenía otra operación, y la operación se rechazó en lugar de esperar. Las esperas de bloqueo que terminan en timeout tienen un código distinto (24 LockTimeout).","cause":"Operaciones administrativas/DDL concurrentes sobre la misma colección o base de datos (p. ej., dos construcciones de índices a la vez, collMod, drop o rename), un conflicto con una operación de mantenimiento en curso o una operación interna que no puede esperar ni siquiera brevemente un bloqueo y encuentra un recurso ocupado.","fix":"Encuentre la operación en conflicto: use db.currentOp() para inspeccionar las operaciones DDL o de mantenimiento que se ejecutan en el mismo namespace. Reintente el comando cuando termine la operación en conflicto. Serialice las operaciones administrativas/DDL y no las ejecute en paralelo sobre la misma colección. Si persiste, investigue en los logs de mongod qué operación mantiene el recurso.","tags":["lock","lock","lockbusy","busy","ddl","46"]},{"code":"96","title":"OperationFailed","severity":"danger","meaning":"Un fallo genérico de operación; se produjo un error inesperado mientras se ejecutaba el comando y la operación no pudo completarse. Es el error genérico que se usa cuando no existe un código más específico.","cause":"Un error interno del lado del servidor, agotamiento de recursos, un estado inconsistente, un fallo de un subsistema (almacenamiento, replicación) o diversas condiciones de fallo propias de un comando concreto.","fix":"Determine la causa raíz examinando el texto completo del mensaje de error y los logs del servidor (mongod.log). Reintente la operación; la condición puede ser transitoria. Compruebe los recursos (disco, memoria) y el estado del replica set. Si el problema se repite, revise el comando y sus datos de entrada.","tags":["operationfailed","operation failed","generic error","96","danger"]},{"code":"72","title":"InvalidOptions","severity":"caution","meaning":"Se pasaron a un comando opciones no válidas o incompatibles entre sí. La combinación de opciones indicada no está soportada para este comando.","cause":"Un nombre de opción no soportado, opciones mutuamente excluyentes usadas juntas, una opción no válida para la versión en ejecución, una combinación no válida de write concern/read concern o un conflicto de opciones de índice/collation.","fix":"Verifique en la documentación las opciones que acepta el comando y elimine las incompatibles. Asegúrese de que su versión admite la opción. Revise la configuración de write/read concern y collation. Use solo combinaciones de opciones válidas.","tags":["invalidoptions","invalid option","options","72","command"]},{"code":"9","title":"FailedToParse","severity":"caution","meaning":"El servidor no pudo analizar el comando, la consulta o la expresión enviada. La estructura es sintácticamente no válida o no tiene el formato esperado.","cause":"Una estructura JSON/BSON mal formada, campos que faltan o sobran, uso incorrecto de operadores, sintaxis no válida del pipeline de agregación o un documento de comando que no coincide con el formato esperado.","fix":"Corrija la estructura del comando/consulta/pipeline según la documentación. Valide la estructura JSON (llaves/comas que faltan, nombres de campo incorrectos). Compruebe la sintaxis de las etapas de agregación. Asegúrese de que los operadores se usan correctamente y de que los argumentos se proporcionan en el formato esperado.","tags":["failedtoparse","parse","parsing","syntax","9","query"]},{"code":"134","title":"ReadConcernMajorityNotAvailableYet","severity":"caution","meaning":"Se solicitó una lectura con read concern majority, pero aún no hay disponible un snapshot confirmado por la mayoría. El servidor no puede ofrecer actualmente este nivel de consistencia.","cause":"El replica set acaba de arrancar o es inestable, las escrituras majority aún no se han confirmado, retraso de replicación, no hay suficientes miembros accesibles o el clúster aún está en su fase de arranque.","fix":"Espere un momento y reintente; la lectura tendrá éxito cuando el snapshot majority esté disponible. Compruebe el estado del replica set y el retraso de replicación (rs.status()). Asegúrese de que haya suficientes miembros activos. Si no es imprescindible, considere un read concern menor (p. ej., local).","tags":["readconcern","majority","majority","replica","134","consistency"]},{"code":"202","title":"NetworkInterfaceExceededTimeLimit","severity":"danger","meaning":"Una solicitud de red (p. ej., comunicación entre miembros o una conexión de cliente) superó su límite de tiempo. La operación no pudo completarse por un timeout de red.","cause":"Latencia/corte de red, un miembro de destino lento o inaccesible, carga excesiva, valores de timeout mal configurados, problemas de DNS/conexión o miembros geográficamente distantes.","fix":"Compruebe la conectividad y la latencia de la red; verifique la accesibilidad entre miembros. Inspeccione el estado y la carga del miembro de destino. Configure los timeouts (connect/socket timeout) según su entorno. Añada lógica de reintento. Si persiste, revise la configuración de infraestructura/red.","tags":["network","network","timeout","timeout","202","danger"]}]},"errors/redis-more.json":{"engineId":"redis","engine":"Redis","order":6,"errors":[{"code":"NOPERM","title":"NOPERM this user has no permissions to run the command or access one of the keys (el usuario no tiene permisos para el comando o la clave)","severity":"caution","meaning":"El usuario de ACL autenticado no está autorizado para el comando que quiere ejecutar o la clave a la que accede. Redis rechaza la operación según las reglas de ACL.","cause":"El usuario de ACL carece del permiso de comando/categoría, el patrón de clave no está en su lista de permitidos, se aplica una restricción de canal (pub/sub) o se dieron al usuario privilegios de alcance limitado.","fix":"Inspeccione los permisos del usuario: ACL GETUSER app_user. Añada los comandos/categorías y patrones de clave necesarios (p. ej., ACL SETUSER app_user +get +set ~app:*). Siguiendo el mínimo privilegio, otorgue solo los permisos necesarios. Verifique que está conectado con el usuario correcto.","tags":["noperm","acl","privilege","permission denied","permission","user"]},{"code":"ERR value is not an integer or out of range","title":"ERR value is not an integer or out of range (el valor no es un entero o está fuera de rango)","severity":"caution","meaning":"Un comando que espera un entero (p. ej., INCR, EXPIRE, LPOP count) recibió un valor no numérico o fuera de rango. Redis no pudo interpretar el valor como entero.","cause":"La clave a la que apunta INCR/DECR contiene un valor de texto (no entero), un argumento dado como texto en lugar de número, un número demasiado grande/pequeño para el rango o un valor negativo donde no se permite.","fix":"Compruebe el valor de la clave: GET mykey y TYPE mykey. Para operaciones numéricas, asegúrese de que la clave contiene un valor entero. Verifique que los argumentos son enteros válidos dentro del rango correcto. Si es necesario, restablezca el valor (SET mykey 0) o vuelva a crearlo con el tipo correcto.","tags":["err","integer","not an integer","range","incr","value"]},{"code":"ERR value is not a valid float","title":"ERR value is not a valid float (el valor no es un float válido)","severity":"caution","meaning":"Un comando que espera un número decimal (float) (p. ej., INCRBYFLOAT, un score de ZADD) recibió un valor que no es un número de coma flotante válido. Redis no pudo analizar el valor como float.","cause":"La clave a la que apunta INCRBYFLOAT no contiene un float válido, un argumento no numérico, el separador decimal incorrecto (se requiere punto, no coma) o un número mal formado.","fix":"Compruebe el valor de la clave con GET y asegúrese de que contiene un float válido. Use el punto como separador decimal (p. ej., 3.14). Verifique que el argumento es numérico y tiene el formato correcto. Si es necesario, restablezca el valor a un float válido.","tags":["err","float","decimal","incrbyfloat","zadd","value"]},{"code":"ERR increment or decrement would overflow","title":"ERR increment or decrement would overflow (el incremento o decremento desbordaría)","severity":"caution","meaning":"Se rechazó un INCR/DECR (o INCRBY/DECRBY) porque el resultado desbordaría el rango de enteros con signo de 64 bits. El valor superaría el límite superior o inferior.","cause":"El valor de la clave está muy cerca del límite de int64 (unos 9,2 trillones) y el incremento/decremento desborda, un argumento de INCRBY muy grande o un contador que crece sin control.","fix":"Diseñe los valores de los contadores para que permanezcan dentro de los límites de int64. Para recuentos grandes, aplique escalado o reinicios periódicos. Mantenga el incremento (el argumento de INCRBY) en un rango razonable. Si se requieren valores muy grandes, use otra estrategia de conteo en la capa de aplicación.","tags":["err","overflow","integer overflow","incr","decr","int64"]},{"code":"ERR max number of clients reached","title":"ERR max number of clients reached (se alcanzó el número máximo de clientes)","severity":"danger","meaning":"El número de clientes conectados alcanzó el límite maxclients y no se pueden aceptar conexiones nuevas. Redis rechaza más conexiones de clientes.","cause":"Una fuga de conexiones (conexiones que nunca se cierran), un valor de maxclients insuficiente, un pool de conexiones mal configurado, un aumento repentino de clientes o un límite bajo de descriptores de archivo del sistema operativo.","fix":"Inspeccione las conexiones actuales: CLIENT LIST e INFO clients. Corrija las fugas de conexiones; dimensione correctamente el pool del cliente y reutilice las conexiones. Si es necesario, eleve maxclients (CONFIG SET maxclients) y el límite ulimit (descriptores de archivo) del SO. Configure un timeout para las conexiones inactivas.","tags":["err","maxclients","connection","client","limit","danger"]},{"code":"BUSYKEY","title":"BUSYKEY Target key name already exists (la clave de destino ya existe)","severity":"caution","meaning":"RESTORE (o un comando similar) falló porque la clave de destino ya existe. Por defecto, Redis no sobrescribe una clave existente.","cause":"El nombre de clave que se restaura con RESTORE ya existe en la base de datos, no se limpiaron los datos anteriores o hay varios intentos de restauración sobre la misma clave.","fix":"Si quiere sobrescribir la clave existente, añada la opción REPLACE a RESTORE. Como alternativa, elimine primero la clave de destino (DEL mykey) o use otro nombre de clave. Compruebe antes su existencia con EXISTS mykey.","tags":["busykey","restore","already exists","target key","replace","key"]},{"code":"ERR no such key","title":"ERR no such key (la clave no existe)","severity":"caution","meaning":"No se encontró en la base de datos la clave sobre la que operar. Los comandos que requieren una clave existente, como RENAME, DUMP y MOVE, no funcionan sobre una clave inexistente.","cause":"La clave nunca se creó, caducó y se eliminó por su TTL, un nombre/ortografía de clave incorrecto, el índice de base de datos (SELECT) equivocado o la clave ya se eliminó con DEL.","fix":"Compruebe que la clave existe: EXISTS mykey. Asegúrese de usar el nombre de clave correcto y la base de datos correcta (SELECT db). RENAME requiere que exista la clave de origen. Revise la lógica de las claves que caducan por TTL. Vuelva a crear la clave si es necesario.","tags":["err","no such key","key not found","rename","dump","key"]},{"code":"ERR DUMP payload version or checksum are wrong","title":"ERR DUMP payload version or checksum are wrong (versión o checksum del payload DUMP incorrectos)","severity":"caution","meaning":"Los datos serializados (salida de DUMP) pasados a RESTORE no son válidos; la versión es incompatible o falló la verificación del checksum. Redis no puede restaurar los datos de forma segura.","cause":"Los datos de DUMP están corruptos/modificados, se mueven entre versiones de Redis distintas e incompatibles, corrupción durante la codificación/decodificación (p. ej., manejo incorrecto de bytes) o un payload ausente/truncado.","fix":"Asegúrese de que los datos de DUMP se transportan íntegros (binary-safe); transfiéralos como bytes sin procesar y no mediante una codificación de texto. Verifique que las versiones de Redis de origen y destino sean compatibles. Haga un DUMP nuevo y vuelva a transferirlo. Para migraciones, considere el comando MIGRATE en lugar de RESTORE.","tags":["err","dump","restore","checksum","checksum mismatch","version"]},{"code":"NOREPLICAS","title":"NOREPLICAS Not enough good replicas to write (no hay suficientes réplicas válidas para escribir)","severity":"danger","meaning":"Por el parámetro min-replicas-to-write, las escrituras se rechazan porque no hay suficientes réplicas sanas. Redis bloquea las escrituras por la seguridad de los datos.","cause":"No hay suficientes réplicas conectadas/al día, caídas de réplicas, retraso de replicación que supera el límite min-replicas-max-lag, problemas de red o réplicas ausentes tras un failover.","fix":"Compruebe el estado de las réplicas: INFO replication. Vuelva a levantar las réplicas caídas y reduzca el retraso de replicación. Corrija la conectividad de red. Revise los parámetros min-replicas-to-write / min-replicas-max-lag para su entorno (el equilibrio entre durabilidad y disponibilidad). Proporcione suficientes réplicas.","tags":["noreplicas","replica","write","min-replicas","replication","danger"]},{"code":"ERR This instance has cluster support disabled","title":"ERR This instance has cluster support disabled (soporte de clúster deshabilitado en esta instancia)","severity":"caution","meaning":"Se ejecutó un comando de clúster (CLUSTER ...), pero esta instancia de Redis no se inició en modo clúster. El comando se rechazó porque el soporte de clúster está deshabilitado.","cause":"No está configurado cluster-enabled yes, la instancia funciona en modo standalone, se envían comandos de clúster a un servidor standalone o se conecta al servidor equivocado.","fix":"Si quiere usar las funcionalidades de clúster, inicie la instancia en modo clúster: configure cluster-enabled yes, reinicie y forme el clúster con los nodos. Si se queda en modo standalone, no use comandos de clúster. Verifique que está conectado al servidor (de clúster) correcto.","tags":["err","cluster","cluster mode","disabled","standalone","config"]},{"code":"ERR unknown subcommand","title":"ERR unknown subcommand or wrong number of arguments (subcomando desconocido o número de argumentos incorrecto)","severity":"caution","meaning":"Un comando contenedor (p. ej., CONFIG, CLIENT, ACL, XINFO) recibió un subcomando no válido, o se enviaron al subcomando un número incorrecto de argumentos. Redis no reconoció el subcomando.","cause":"Un error tipográfico en el nombre del subcomando, un subcomando que no existe en la versión de Redis en uso, un número de argumentos incorrecto/insuficiente o una sintaxis de subcomando incorrecta.","fix":"Use el nombre de subcomando y los argumentos correctos; consulte el subcomando HELP del comando (p. ej., CONFIG HELP, CLIENT HELP). Verifique que su versión de Redis admite ese subcomando. Corrija el número de argumentos y la sintaxis según la documentación.","tags":["err","subcommand","unknown subcommand","argument","config","client"]},{"code":"ERR Client sent AUTH, but no password is set","title":"ERR AUTH <password> called without any password configured for the default user (Redis 6+) / ERR Client sent AUTH, but no password is set (Redis <6) (AUTH sin contraseña configurada)","severity":"caution","meaning":"El cliente intentó autenticarse con AUTH, pero en el servidor no hay definida ninguna contraseña (requirepass) ni contraseña de ACL. AUTH se rechazó porque no hay credencial que verificar. Redis 6+ devuelve en este caso \"ERR AUTH <password> called without any password configured for the default user. Are you sure your configuration is correct?\"; \"Client sent AUTH, but no password is set\" es el mensaje anterior a Redis 6.","cause":"requirepass vacío en el servidor, el cliente envía AUTH innecesariamente, una contraseña definida en la cadena de conexión cuando el servidor no tiene ninguna o una discrepancia de configuración.","fix":"Si el servidor no requiere contraseña, elimine el comando AUTH/la configuración de contraseña del cliente. Si quiere protección por contraseña, configure requirepass en el servidor (CONFIG SET requirepass password) o defina una contraseña para el usuario de ACL. Haga coherente la configuración de cliente y servidor.","tags":["err","auth","password","requirepass","authentication","acl"]}]},"errors/elasticsearch-more.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"errors":[{"code":"action_request_validation_exception","title":"action_request_validation_exception","severity":"caution","meaning":"La solicitud no superó la validación antes de ejecutarse. Falta un campo obligatorio o hay campos no válidos, por lo que Elasticsearch rechaza la operación.","cause":"Faltan parámetros obligatorios (p. ej., no se indicó el nombre del índice), un cuerpo vacío, una combinación de campos no válida o información obligatoria no proporcionada en una solicitud bulk/index.","fix":"Revise la lista de campos ausentes/no válidos del mensaje de error y proporcione los campos obligatorios. Complete el cuerpo y los parámetros de la solicitud según la documentación de la API. En solicitudes bulk, verifique que cada línea de acción tenga metadatos y un cuerpo correctos.","tags":["validation","validation error","request","request error","action"]},{"code":"index_closed_exception","title":"index_closed_exception","severity":"caution","meaning":"El índice de destino está cerrado. Un índice cerrado no atiende solicitudes de lectura/escritura, por lo que no se pudo realizar la operación.","cause":"El índice se cerró previamente con la API close, se cerró durante operaciones de mantenimiento/ILM o se desactivó para ahorrar recursos.","fix":"Vuelva a abrir el índice: POST /index_name/_open. Tras abrirlo, espere a que se asignen los shards (green/yellow). Investigue por qué se cerró el índice (política ILM, acción manual). Revise la estrategia de mantener cerrados los índices a los que se accede poco.","tags":["index closed","closed index","open","index","closed"]},{"code":"unavailable_shards_exception","title":"unavailable_shards_exception","severity":"danger","meaning":"Uno o más shards necesarios para la operación no están disponibles, por lo que la solicitud (normalmente una escritura) no pudo completarse. No se encontraron suficientes copias activas del shard.","cause":"Shards sin asignar por la caída/salida de un nodo, superación de un disk watermark, réplicas ausentes, una condición wait_for_active_shards no cumplida o estado red del clúster.","fix":"Compruebe el estado del clúster y de los shards: GET _cluster/health y GET _cat/shards?v. Investigue por qué los shards no están asignados: GET _cluster/allocation/explain. Vuelva a levantar los nodos caídos, libere espacio en disco (watermark) y revise la configuración de réplicas. Si es necesario, ajuste wait_for_active_shards de forma adecuada.","tags":["shard","unavailable","inaccessible","cluster","allocation","danger"]},{"code":"query_shard_exception","title":"query_shard_exception","severity":"caution","meaning":"Una consulta falló al ejecutarse en un shard concreto. Suele ocurrir porque la consulta es incompatible con el mapping o tiene una estructura no válida.","cause":"Consultar un campo inexistente o de tipo incorrecto, intentar agregación/ordenación sobre un campo text (fielddata deshabilitado), sintaxis de consulta no válida, análisis incorrecto o un tipo de campo incompatible.","fix":"Revise el campo/motivo en los detalles del error. Compruebe el mapping del campo consultado: GET /index/_mapping. Para ordenación/agregación, use el subcampo keyword (field.keyword) en lugar de text. Corrija la sintaxis de la consulta y asegúrese de que los tipos de campo sean compatibles con la consulta.","tags":["query","query error","shard","mapping","fielddata","query shard"]},{"code":"script_exception","title":"script_exception","severity":"caution","meaning":"Se produjo un error al compilar o ejecutar un script (normalmente Painless). El script está mal escrito o lanzó una excepción en tiempo de ejecución.","cause":"Error de sintaxis de Painless, acceso a un valor null, discrepancia de tipos, uso de un campo/variable inexistente, superación del límite de compilación de scripts o una llamada a una API no permitida.","fix":"Revise los detalles de compilación/ejecución del mensaje de error. Corrija la sintaxis del script y el acceso a campos; añada comprobaciones de null (p. ej., doc['field'].size() > 0). Parametrice los scripts de uso frecuente (stored/params) para no superar el límite de compilación. Use nombres y tipos de campo correctos.","tags":["script","painless","compilation","runtime","script exception"]},{"code":"number_format_exception","title":"number_format_exception","severity":"caution","meaning":"Un valor que debería ser numérico no pudo convertirse a número. Se proporcionó una cadena no válida/no numérica donde un campo o parámetro espera un número.","cause":"Texto no numérico enviado a un campo numérico (integer/long/double), un intento de convertir una cadena vacía en número, un formato decimal incorrecto o datos de entrada incompatibles con el mapping.","fix":"Envíe valores numéricos válidos al campo. Valide/transforme los datos de origen antes de indexarlos. Use null para los valores vacíos u omita el campo. Revise el parámetro coerce y el tipo de campo en el mapping. Use el punto como separador decimal.","tags":["number format","number","conversion","numeric","format"]},{"code":"connect_transport_exception","title":"connect_transport_exception","severity":"danger","meaning":"No se pudo conectar a un nodo a través de la capa de transporte (9300). No pudo establecerse la comunicación entre nodos o entre cliente y clúster, por lo que no se pudo realizar la operación.","cause":"Nodo de destino caído/inaccesible, problemas de red/firewall, dirección/puerto de transporte incorrectos, discrepancia en la configuración TLS, un problema de DNS o una discrepancia de nombre/versión del clúster.","fix":"Verifique que el nodo de destino está en ejecución y que el puerto de transporte (por defecto 9300) es accesible por la red. Compruebe las reglas de firewall/red y la configuración de discovery. Asegúrese de que la configuración de TLS/certificados sea coherente en todos los nodos. Revise los logs de los nodos y verifique la compatibilidad de cluster.name/versión.","tags":["transport","connection","connect","network","node","danger"]},{"code":"repository_missing_exception","title":"repository_missing_exception","severity":"caution","meaning":"No se encontró el repositorio de snapshots referenciado en una operación de snapshot/restore. No hay ningún repositorio registrado con el nombre indicado.","cause":"El repositorio nunca se registró, un error tipográfico en el nombre, el repositorio se eliminó, está definido en otro clúster o no se volvió a registrar tras restablecerse el estado del clúster.","fix":"Liste los repositorios registrados: GET _snapshot/_all. Vuelva a registrar el repositorio con el nombre y la configuración correctos: PUT _snapshot/repo_name (con type y settings). Asegúrese de que el nombre del repositorio esté bien escrito. Verifique que el acceso al repositorio compartido (fs/s3, etc.) funcione en todos los nodos.","tags":["repository","snapshot repository","snapshot","missing","backup","restore"]},{"code":"concurrent_snapshot_execution_exception","title":"concurrent_snapshot_execution_exception","severity":"caution","meaning":"Se rechazó una nueva solicitud de snapshot/restore porque se está ejecutando al mismo tiempo una operación de snapshot en conflicto. Otra operación de snapshot sigue en curso sobre el repositorio o el índice.","cause":"Un snapshot anterior aún en curso, un conflicto de snapshot/restore concurrente que afecta al mismo índice, una política SLM (snapshot lifecycle) que coincide con un snapshot manual o disparadores a intervalos cortos.","fix":"Compruebe los snapshots en ejecución: GET _snapshot/repo/_current y GET _snapshot/_status. Espere a que termine la operación actual o cancélela si es necesario: DELETE _snapshot/repo/snapshot_name. Programe los snapshots para que no se solapen. Coordine las operaciones manuales con SLM.","tags":["snapshot","concurrent","conflict","simultaneous","slm","backup"]},{"code":"invalid_index_name_exception","title":"invalid_index_name_exception","severity":"caution","meaning":"El nombre de índice indicado no cumple las reglas de nomenclatura de Elasticsearch. El nombre contiene caracteres no válidos o incumple reglas de formato.","cause":"Letras mayúsculas, caracteres no permitidos (\\ / * ? \" < > | espacio , #), un nombre que empieza por _, - o +, un nombre demasiado largo (más de 255 bytes) o nombres reservados como . / ... (Los nombres que empiezan por punto no están prohibidos, pero están reservados para índices ocultos/de sistema y generan una advertencia.)","fix":"Haga que el nombre del índice cumpla las reglas: use solo minúsculas; evite los caracteres prohibidos; no empiece el nombre por _, - o +, y no use nombres que empiecen por punto salvo para índices de sistema/ocultos. Mantenga el nombre por debajo de 255 bytes. Imponga estas reglas en la aplicación al generar nombres de índices.","tags":["index name","index naming","invalid","invalid name","naming"]},{"code":"resource_not_found_exception","title":"resource_not_found_exception","severity":"caution","meaning":"No se encontró el recurso solicitado (documento, índice, tarea, pipeline, modelo, etc.). No existe ningún recurso con el ID/nombre indicado.","cause":"Un ID de documento inexistente, un índice/pipeline eliminado o que nunca se creó, un nombre mal escrito, una tarea aún no completada o ya limpiada, o una solicitud enviada al clúster equivocado.","fix":"Verifique el nombre/ID del recurso y que existe (p. ej., GET /index/_doc/id, GET _ingest/pipeline/name). Asegúrese de enviar la solicitud al índice y clúster correctos. Revise el orden de las operaciones de eliminación/creación. Para tareas, compruebe el estado con la API _tasks.","tags":["not found","resource not found","resource","missing resource","404"]},{"code":"process_cluster_event_timeout_exception","title":"process_cluster_event_timeout_exception","severity":"danger","meaning":"Una actualización del estado del clúster (p. ej., creación de índice, cambio de mapping) no terminó en el tiempo asignado y agotó el tiempo de espera. El nodo master no pudo procesar la actualización a tiempo.","cause":"Nodo master con carga elevada, un estado del clúster muy grande/complejo, cambios frecuentes e intensivos de mapping/template, un master lento o problemas de elección del master.","fix":"Compruebe la carga y el estado del nodo master: GET _cluster/health, GET _cat/master y GET _cluster/pending_tasks para las tareas pendientes. Reduzca el estado del clúster (disminuya índices/mappings/templates innecesarios, evite la explosión de dynamic mapping). Ejecute las operaciones pesadas de tipo DDL por lotes en periodos de poco tráfico. Aumente los recursos del master si es necesario.","tags":["cluster event","timeout","timed out","master","cluster state","danger"]}]},"errors/oracle-more2.json":{"engineId":"oracle","engine":"Oracle","order":1,"errors":[{"code":"ORA-00955","title":"name is already used by an existing object (el nombre ya lo usa un objeto existente)","severity":"caution","meaning":"El nombre del objeto que intenta crear (tabla, índice, vista, secuencia, sinónimo, etc.) ya lo usa otro objeto del mismo esquema. En Oracle, los nombres de objeto son únicos dentro de un esquema, e incluso tipos distintos como tablas, vistas y secuencias comparten el mismo espacio de nombres.","cause":"El nombre elegido en la sentencia CREATE entra en conflicto con un objeto existente. Una trampa habitual: intentar crear una secuencia o vista con el mismo nombre que una tabla; o un nombre de índice creado automáticamente (p. ej., el índice de la clave primaria) que coincide con el nombre que indicó.","fix":"Elija otro nombre o elimine el objeto en conflicto. Para averiguar qué objeto usa el nombre: SELECT object_type, object_name FROM user_objects WHERE object_name = 'YOUR_NAME'; Si realmente quiere sustituirlo, haga DROP del objeto antiguo (haga antes un backup de sus datos). CREATE OR REPLACE está disponible para vistas, sinónimos, tipos y objetos PL/SQL (procedure, function, package, trigger); no existe para tablas, índices y secuencias.","tags":["ddl","create","name","conflict","duplicate","object"]},{"code":"ORA-02449","title":"unique/primary keys in table referenced by foreign keys (claves únicas/primarias referenciadas por claves foráneas)","severity":"caution","meaning":"La clave primaria o única de la tabla que quiere eliminar está referenciada por claves foráneas de otras tablas. Oracle no permite eliminar la tabla sin quitar antes estas dependencias.","cause":"Ejecutó DROP TABLE sin CASCADE CONSTRAINTS mientras otras tablas referencian esta tabla mediante FK. Para preservar la integridad referencial, Oracle impide que queden claves foráneas huérfanas.","fix":"Para eliminarla junto con las claves foráneas dependientes: DROP TABLE table_name CASCADE CONSTRAINTS; Esto también elimina las restricciones FK relacionadas de las otras tablas (no elimina los datos de esas tablas). Para ver qué restricciones dependen de ella: SELECT table_name, constraint_name FROM all_constraints WHERE r_constraint_name IN (SELECT constraint_name FROM user_constraints WHERE table_name = 'TABLE_NAME');","tags":["drop","foreign key","foreign key reference","constraint","cascade","reference"]},{"code":"ORA-01950","title":"no privileges on tablespace 'name' (sin privilegios sobre el tablespace)","severity":"danger","meaning":"El usuario no tiene cuota en el tablespace en el que intenta escribir. En Oracle, un usuario debe tener cuota en el tablespace para asignar espacio a un objeto.","cause":"No se ha concedido al usuario cuota en el tablespace; normalmente, un usuario nuevo sin el privilegio de sistema UNLIMITED TABLESPACE intenta crear una tabla/índice o insertar datos en su tablespace predeterminado. Aparece durante INSERT, CREATE TABLE o el crecimiento de índices.","fix":"Como DBA, conceda una cuota al usuario: ALTER USER app_user QUOTA UNLIMITED ON tablespace_name; o un límite concreto: ALTER USER app_user QUOTA 500M ON users; Como alternativa (úselo con cuidado) puede concederse GRANT UNLIMITED TABLESPACE TO app_user;, pero esto da derechos ilimitados sobre todos los tablespaces y en general no se recomienda.","tags":["tablespace","quota","tablespace quota","privilege","privilege","insert"]},{"code":"ORA-01110","title":"data file N: 'path'","severity":"danger","meaning":"Mensaje complementario (secundario) que indica un problema con el datafile del número y la ruta indicados. Nunca aparece solo; lo precede otro error ORA que informa del problema real, y esta línea solo indica qué datafile está afectado.","cause":"Suele acompañar a un error principal, como un datafile offline (ORA-00376), que necesita media recovery (ORA-01113), corrupción de bloques (ORA-01578) o un archivo ilegible. Puede desencadenarlo un fallo de disco, un borrado accidental, un archivo movido o una restauración incompleta desde backup.","fix":"Lea el error ORA real que precede a esta línea y resuélvalo. Revise el archivo afectado: SELECT file#, name, status FROM v$datafile; Si se necesita recuperación: RECOVER DATAFILE 'path'; luego ALTER DATABASE DATAFILE 'path' ONLINE; Si el archivo se ha perdido físicamente, restáurelo y recupérelo con RMAN: RESTORE DATAFILE N; RECOVER DATAFILE N;","tags":["datafile","data file","recovery","media recovery","offline","rman"]},{"code":"ORA-16038","title":"log N sequence# M cannot be archived","severity":"danger","meaning":"Oracle no puede archivar un online redo log y, por lo tanto, no puede reutilizar ese grupo de log. Cuando el archivado se bloquea en modo ARCHIVELOG, la base de datos puede dejar de aceptar nuevas transacciones y quedarse colgada.","cause":"Normalmente el destino de los logs archivados o la Fast Recovery Area (FRA) está lleno; se ha agotado el espacio en disco; el destino de archivado no es accesible; o hay un problema de permisos/montaje. Suele verse junto con ORA-19809 (limit exceeded for recovery files) u ORA-00312.","fix":"Libere espacio en el destino de archivado/FRA: haga backup con RMAN de los logs archivados antiguos a un destino fuera de la FRA y elimínelos (DELETE ARCHIVELOG ALL COMPLETED BEFORE ...; con Data Guard, no elimine logs archivados que aún no se hayan enviado/aplicado en el standby), o aumente db_recovery_file_dest_size. Revise el estado del destino: ARCHIVE LOG LIST; y SELECT dest_name, status, error FROM v$archive_dest WHERE status != 'INACTIVE'; Una vez corregido el problema, el archivado se reanuda donde se quedó.","tags":["archivelog","archive","redo","fra","recovery area","archiver stuck"]},{"code":"ORA-19809","title":"limit exceeded for recovery files","severity":"danger","meaning":"El espacio asignado a la Fast Recovery Area (FRA) (db_recovery_file_dest_size) está lleno. Sin espacio para logs archivados, flashback logs y backups, Oracle no puede crear nuevos archivos de recuperación y la base de datos puede quedarse colgada.","cause":"Los logs archivados se acumularon en la FRA porque no se respaldaban ni purgaban con regularidad; creció el uso de flashback logs; o el tamaño asignado es demasiado pequeño para la carga real. Se ve junto con errores de archivado como ORA-16038.","fix":"Revise el uso de la FRA: SELECT * FROM v$recovery_file_dest; y SELECT * FROM v$flash_recovery_area_usage; Elimine con RMAN los logs archivados innecesarios: DELETE ARCHIVELOG ALL BACKED UP 1 TIMES TO DISK; (con Data Guard, verifique primero que se aplicaron en el standby; se recomienda CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON ALL STANDBY) o, como solución permanente, aumente el tamaño: ALTER SYSTEM SET db_recovery_file_dest_size = 50G SCOPE=BOTH; Configure un job de RMAN que haga backup de los logs archivados con regularidad.","tags":["fra","recovery area","db_recovery_file_dest_size","archivelog","space","rman"]},{"code":"ORA-00376","title":"file N cannot be read at this time","severity":"danger","meaning":"El datafile con el número indicado no se puede leer en este momento porque está offline o inaccesible. Las consultas y operaciones que acceden a datos del tablespace afectado fallan con este error.","cause":"El datafile se puso offline automáticamente tras un error o manualmente; está pendiente de media recovery; el disco quedó inaccesible; o se dejó offline un archivo que no se puede recuperar en modo NOARCHIVELOG. Suele ir acompañado de una línea ORA-01110 que identifica el archivo.","fix":"Vea el estado de los archivos: SELECT file#, name, status FROM v$datafile WHERE status != 'ONLINE'; Recupérelo y póngalo online: RECOVER DATAFILE N; luego ALTER DATABASE DATAFILE N ONLINE; Si hay corrupción/pérdida física, restaure y recupere con RMAN: RESTORE DATAFILE N; RECOVER DATAFILE N; Corrija también el problema subyacente de disco/hardware.","tags":["datafile","data file","offline","recovery","media recovery","read"]},{"code":"ORA-12520","title":"TNS:listener could not find available handler for requested type of server (el listener no encontró un handler disponible para el tipo de servidor solicitado)","severity":"danger","meaning":"El listener no encontró un proceso servidor (handler) adecuado para el tipo de conexión solicitado (dedicada o compartida). El cliente puede llegar al listener, pero la conexión no puede traspasarse a un proceso servidor.","cause":"La instancia no está registrada en el listener o su registro está roto; se alcanzó el límite PROCESSES/SESSIONS y no se puede crear un nuevo proceso dedicado; no hay dispatchers o son insuficientes en una configuración de shared server; o la instancia acaba de arrancar y aún no se ha registrado en el listener.","fix":"Revise los servicios que ve el listener: lsnrctl services (número de handlers y sus estados). Vuelva a registrar la instancia en el listener: ALTER SYSTEM REGISTER; Revise el límite PROCESSES: SELECT resource_name, current_utilization, max_utilization, limit_value FROM v$resource_limit WHERE resource_name IN ('processes','sessions'); Si es necesario, auméntelo con ALTER SYSTEM SET processes=... SCOPE=SPFILE; y reinicie la instancia.","tags":["listener","tns","connection","handler","processes","dispatcher"]},{"code":"ORA-28001","title":"the password has expired","severity":"caution","meaning":"La contraseña del usuario de base de datos con el que se conecta ha caducado porque transcurrió el PASSWORD_LIFE_TIME de su perfil. No se puede abrir ninguna sesión hasta cambiar la contraseña.","cause":"La vigencia de la contraseña definida en el perfil del usuario terminó y también transcurrió el periodo de gracia. En Oracle 11g y posteriores, las contraseñas del perfil DEFAULT caducan cada 180 días; esto provoca interrupciones inesperadas, sobre todo en cuentas de servicio de aplicaciones de larga duración.","fix":"Cambie la contraseña: ALTER USER app_user IDENTIFIED BY new_password; Para evitar problemas recurrentes con cuentas de aplicación, use un perfil sin caducidad de contraseña: CREATE PROFILE app_profile LIMIT PASSWORD_LIFE_TIME UNLIMITED; luego ALTER USER app_user PROFILE app_profile; También es útil un monitoreo que avise antes de la caducidad. Revise el estado con ACCOUNT_STATUS en DBA_USERS.","tags":["password","password expired","expired","profile","account","login"]},{"code":"ORA-38029","title":"object statistics are locked","severity":"caution","meaning":"Se rechazó la recopilación/eliminación de estadísticas porque las estadísticas del optimizador de una tabla (o esquema) están bloqueadas. Las estadísticas bloqueadas están protegidas contra su recálculo con DBMS_STATS.","cause":"Se llamó antes a DBMS_STATS.LOCK_TABLE_STATS o LOCK_SCHEMA_STATS (p. ej., para mantener fijas las estadísticas), o una importación de Data Pump (sobre todo CONTENT=METADATA_ONLY) dejó las estadísticas bloqueadas sin intención. Ejecutar GATHER_TABLE_STATS mientras están bloqueadas genera este error.","fix":"Desbloquee las estadísticas: EXEC DBMS_STATS.UNLOCK_TABLE_STATS('APP_SCHEMA','MY_TABLE'); o a nivel de esquema DBMS_STATS.UNLOCK_SCHEMA_STATS('APP_SCHEMA'); Si no quiere quitar el bloqueo pero sí recopilar, use FORCE=>TRUE: EXEC DBMS_STATS.GATHER_TABLE_STATS('APP_SCHEMA','MY_TABLE', force=>TRUE); Para ver el estado del bloqueo: SELECT table_name, stattype_locked FROM dba_tab_statistics WHERE stattype_locked IS NOT NULL;","tags":["statistics","optimizer statistics","dbms_stats","locked","lock","optimizer"]}]},"errors/postgres-more2.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"errors":[{"code":"54001","title":"statement_too_complex","severity":"caution","meaning":"La sentencia es más compleja de lo que PostgreSQL puede procesar; normalmente, expresiones anidadas muy profundas o muchos operadores anidados hacen que el árbol de expresiones supere el límite de pila del parser/planner. Pertenece a la Class 54 (program_limit_exceeded).","cause":"Paréntesis/subconsultas anidadas excesivamente profundas o cadenas OR/AND; enormes listas WHERE ... IN (...) generadas automáticamente; o árboles de expresiones muy grandes derivados de vistas recursivas. Se alcanza el límite max_stack_depth.","fix":"Simplifique la consulta: mueva las listas IN largas a una tabla temporal o a un conjunto VALUES y combínela con un JOIN; divida las expresiones anidadas; revise las consultas enormes generadas por el ORM. Si es realmente necesario y el límite de pila del SO lo permite, aumente max_stack_depth con cuidado (p. ej., SET max_stack_depth = '4MB';) pero manténgalo por debajo del ulimit del SO.","tags":["program_limit","complex","stack","nested","query","max_stack_depth"]},{"code":"58030","title":"io_error","severity":"danger","meaning":"PostgreSQL encontró un error de entrada/salida (I/O) a nivel del sistema operativo al leer o escribir un archivo. Indica un problema físico o del sistema de archivos al acceder a los archivos de la base de datos. Pertenece a la Class 58 (system_error).","cause":"Fallo de disco o sectores defectuosos; disco lleno; errores del sistema de archivos; una caída del almacenamiento en red (NFS/SAN); problemas de permisos; o límites de descriptores de archivo/inodos del SO agotados.","fix":"Revise el estado del disco y el espacio libre (df -h, dmesg, datos SMART). Revise en los logs de PostgreSQL qué archivo está afectado. Corrija los errores del sistema de archivos; sustituya el hardware si es necesario. Si se sospecha corrupción de datos, planifique una restauración desde el último backup válido y mantenga los checksums habilitados en adelante (initdb --data-checksums).","tags":["system_error","io","disk","hardware","file","corruption"]},{"code":"22004","title":"null_value_not_allowed","severity":"caution","meaning":"Se pasó un NULL donde no se acepta NULL. Es distinto de una restricción NOT NULL de columna (23502); suele deberse a pasar NULL a un parámetro u operación que no acepta NULL en el contexto de una función o procedimiento. Pertenece a la Class 22 (data_exception).","cause":"Asignar NULL a una variable PL/pgSQL declarada NOT NULL; pasar una cadena de consulta NULL a EXECUTE; o pasar NULL a argumentos de funciones integradas que no lo aceptan. Nota: una función marcada STRICT no genera error con una entrada NULL — devuelve NULL sin ejecutarse.","fix":"Localice la línea que falla (CONTEXT) y proteja el valor que llega a ella con una comprobación de NULL, o proporcione un valor predeterminado con COALESCE: COALESCE(value, default_value). En PL/pgSQL, valide los valores antes de asignarlos a variables NOT NULL; en SQL dinámico asegúrese de que la cadena pasada a EXECUTE no sea NULL. Evite en la capa de aplicación que los campos obligatorios se envíen como NULL.","tags":["data_exception","null","function","strict","parameter","coalesce"]},{"code":"42P18","title":"indeterminate_datatype","severity":"caution","meaning":"PostgreSQL no pudo inferir por el contexto el tipo de datos de una expresión o parámetro. Se ve sobre todo cuando se usa un marcador sin tipo (como $1) o un NULL sin más, sin ninguna indicación de tipo alrededor.","cause":"Parámetros sin tipo en sentencias preparadas; comparaciones en las que ambos lados son indeterminados, como \"WHERE $1 = $2\"; o uso sin contexto de literales NULL/de array. El planner no puede decidir qué tipo usar.","fix":"Añada un cast explícito a la expresión indeterminada: $1::text, $1::integer, NULL::date, etc. Por ejemplo: SELECT $1::int + 1; Especifique los tipos de los parámetros del driver/ORM. En comparaciones de columnas, asegúrese de que al menos un lado tenga un tipo conocido.","tags":["syntax","type","indeterminate","cast","parameter","prepared"]},{"code":"42P02","title":"undefined_parameter","severity":"caution","meaning":"La consulta hace referencia a un parámetro que no existe. PostgreSQL informa de que el parámetro posicional referenciado (p. ej., $2) no está definido. Pertenece a la Class 42 (syntax_error_or_access_rule_violation).","cause":"Referenciar más parámetros de los suministrados en una sentencia preparada con PREPARE (p. ej., enviar dos parámetros y usar $3); numeración de parámetros con saltos; o índices $N incorrectos en SQL concatenado dinámicamente.","fix":"Asegúrese de que los números de parámetro coincidan con la cantidad de valores que suministra; verifique que empiecen en $1 y continúen sin huecos. Defina completamente los tipos de parámetros en la sentencia PREPARE: PREPARE p(int, text) AS SELECT ... WHERE a=$1 AND b=$2; Si usa un ORM/driver, compare el número de parámetros vinculados con los marcadores de la consulta.","tags":["syntax","parameter","prepared","undefined","placeholder","sql"]},{"code":"2F003","title":"prohibited_sql_statement_attempted","severity":"caution","meaning":"Se intentó, dentro de una función/rutina SQL, una sentencia prohibida en ese contexto. Pertenece a la Class 2F (sql_routine_exception). Por ejemplo, una función que se espera de solo lectura intenta modificar datos.","cause":"El núcleo de PostgreSQL rara vez genera este código; suele provenir de código de extensiones o de lenguajes procedurales. Nota: una función marcada IMMUTABLE/STABLE que ejecuta INSERT/UPDATE/DELETE no genera 2F003; genera '... is not allowed in a non-volatile function' con el código 0A000 (feature_not_supported).","fix":"Lea en el mensaje de error qué sentencia se prohibió y consulte la documentación de la rutina/extensión implicada. Si obtiene el error de función no volátil (0A000), defina la función que modifica datos como VOLATILE (es el valor predeterminado): CREATE FUNCTION ... LANGUAGE plpgsql VOLATILE. Elimine las llamadas que modifican datos de los lugares que deben ser de solo lectura (expresiones de índice, comprobaciones de restricciones).","tags":["sql_routine","function","volatile","prohibited","immutable","side effect"]},{"code":"40003","title":"statement_completion_unknown","severity":"danger","meaning":"El resultado de la transacción es desconocido; la conexión con el servidor se cortó antes de saber si la transacción se completó (sobre todo durante COMMIT). El cliente no puede saber con certeza si los cambios son duraderos. Pertenece a la Class 40 (transaction_rollback).","cause":"La red se cayó, el servidor falló o se produjo un failover después de enviar COMMIT pero antes de recibir la respuesta. Puede que la transacción se haya completado en el servidor o que se haya revertido.","fix":"No reintente la transacción a ciegas; verifique primero si realmente se aplicó (p. ej., consultando la fila esperada). Diseñe las transacciones para que sean idempotentes; use claves de negocio únicas/claves de idempotencia. Implemente en el pool de conexiones una lógica robusta de reconexión y validación.","tags":["transaction","commit","unknown","connection","idempotent","failover"]},{"code":"08003","title":"connection_does_not_exist","severity":"danger","meaning":"Se intentó una operación sobre una conexión de base de datos que no existe (ya cerrada). La aplicación intentó enviar una consulta usando un handle de conexión no válido. Pertenece a la Class 08 (connection_exception).","cause":"Timeout de conexión, cierre por parte del servidor, una caída de red o una conexión tomada del pool que en realidad está muerta; o la reutilización de una conexión a la que se aplicó close() en el código de la aplicación.","fix":"Habilite comprobaciones de actividad en el pool de conexiones (validation query / keepalive); configure los ajustes que expulsan del pool las conexiones muertas (p. ej., connectionTestQuery/maxLifetime en HikariCP). En la aplicación, asegúrese de que la conexión esté abierta antes de usarla y reconecte automáticamente cuando se caiga. Revise los valores de TCP keepalive e idle_in_transaction_session_timeout.","tags":["connection","connection","closed","pool","pool","timeout"]},{"code":"25006","title":"read_only_sql_transaction","severity":"caution","meaning":"Se intentó un comando que modifica datos (INSERT/UPDATE/DELETE/DDL) dentro de una transacción de solo lectura. La transacción está configurada solo para lecturas. Pertenece a la Class 25 (invalid_transaction_state).","cause":"SET TRANSACTION READ ONLY o el ajuste default_transaction_read_only=on; o, lo más habitual, un intento de escritura en una réplica de lectura (hot standby / réplica de streaming). Los servidores standby son de solo lectura por definición.","fix":"Dirija las escrituras al servidor primario; revise el enrutamiento de conexiones que separa lecturas y escrituras. Si se configuró por error: SET TRANSACTION READ WRITE; o, a nivel de servidor, default_transaction_read_only=off. Asegúrese de que su aplicación envíe el tráfico de lectura-escritura al nodo correcto.","tags":["transaction","read only","read only","replica","standby","primary"]},{"code":"21000","title":"cardinality_violation","severity":"caution","meaning":"Se devolvió más de una fila donde se esperaba una sola. Una subconsulta escalar devolvió varias filas, o MERGE/ON CONFLICT DO UPDATE intentó afectar por segunda vez a la misma fila de destino. Pertenece a la Class 21.","cause":"Una subconsulta usada en la lista SELECT o en la forma WHERE = (subquery) que devuelve varias filas; la misma clave de conflicto propuesta más de una vez en un único INSERT ... ON CONFLICT DO UPDATE ('cannot affect row a second time'); o varias filas de origen que coinciden con una fila de destino en MERGE. UPDATE ... FROM no genera error con coincidencias múltiples; la fila de destino se actualiza solo a partir de una de las filas de origen, de forma impredecible.","fix":"Asegúrese de que la subconsulta devuelva una sola fila: añada LIMIT 1, use un agregado (MAX/MIN) o proporcione una condición que haga única la coincidencia. En los patrones MERGE y ON CONFLICT, elimine duplicados en el lado de origen con DISTINCT ON/agregado; haga lo mismo con UPDATE ... FROM para evitar resultados erróneos silenciosos. Para encontrar coincidencias múltiples inesperadas, ejecute la subconsulta por separado y revise el número de filas.","tags":["cardinality","subquery","subquery","single row","join","scalar"]}]},"errors/mysql-more2.json":{"engineId":"mysql","engine":"MySQL","order":3,"errors":[{"code":"1927","title":"MariaDB: ER_CONNECTION_KILLED: Connection was killed (la conexión fue terminada)","severity":"danger","meaning":"Específico de MariaDB: el servidor terminó la conexión/hilo en ejecución, la consulta en curso se interrumpió y la conexión del cliente se cortó. Este código no existe en MySQL; allí la misma situación aparece como 1317 (ER_QUERY_INTERRUPTED) cuando se interrumpe la consulta, y como 2013 (Lost connection) en el lado del cliente cuando se corta la conexión.","cause":"Un administrador o una herramienta de monitoreo que envía un comando KILL; el servidor entrando en apagado; límites de conexiones inactivas de MariaDB como idle_transaction_timeout/wait_timeout; o el servidor cortando la conexión bajo presión de recursos. También se ve durante el mantenimiento en entornos cloud/proxy.","fix":"Investigue por qué se terminó la consulta: SHOW PROCESSLIST y el slow query log para consultas de larga duración; calendarios de mantenimiento/reinicio del servidor; políticas de terminación de conexiones. Capture este error en la aplicación y reintente de forma segura (si es idempotente). Optimice y divida las consultas largas; use reconexión automática en el pool de conexiones.","tags":["connection","connection killed","kill","thread","shutdown","retry"]},{"code":"1290","title":"ER_OPTION_PREVENTS_STATEMENT: running with the --read-only option (se ejecuta con la opción --read-only)","severity":"caution","meaning":"El servidor MySQL rechazó una sentencia que modifica datos (INSERT/UPDATE/DELETE/DDL) porque se ejecuta con la opción --read-only o --super-read-only. Con read_only, los usuarios con CONNECTION_ADMIN/SUPER todavía pueden escribir; con super_read_only habilitado tampoco pueden, y el mensaje dice '--super-read-only option'.","cause":"read_only=ON o super_read_only=ON; lo más habitual, un intento accidental de escritura en una réplica. Las réplicas y los secundarios de Group Replication se mantienen en solo lectura por consistencia de datos; la automatización de failover pone el antiguo primario en super_read_only. Escribir en el nodo equivocado durante un failover/switchover también lo provoca.","fix":"Dirija las escrituras al servidor primario (source). Si este servidor realmente debe convertirse en el nuevo primario: SET GLOBAL read_only = OFF; (desactivar read_only desactiva automáticamente también super_read_only; activar super_read_only también activa read_only). No lo cambie manualmente en nodos gestionados por su herramienta de HA (Orchestrator/Group Replication/InnoDB Cluster). Para comprobar el estado: SELECT @@read_only, @@super_read_only;","tags":["read only","super_read_only","read-only mode","replica","primary","write","failover","1290"]},{"code":"1836","title":"ER_READ_ONLY_MODE: Running in read-only mode (se ejecuta en modo de solo lectura)","severity":"caution","meaning":"InnoDB se ejecuta en modo de solo lectura (innodb_read_only=ON) o el directorio de datos/medio es de solo lectura; se rechazan las escrituras y el DDL sobre tablas InnoDB. Nota: un intento de escritura con read_only/super_read_only habilitado devuelve 1290 (ER_OPTION_PREVENTS_STATEMENT), no este código.","cause":"El servidor se inició con innodb_read_only=ON (p. ej., medio de solo lectura, un directorio de datos compartido, una copia para análisis); en este modo no se pueden realizar cambios como INSERT/UPDATE/DELETE y CREATE/DROP/ALTER TABLE en tablas InnoDB.","fix":"Si se necesitan escrituras, inicie el servidor sin innodb_read_only: elimine la línea innodb_read_only=ON de my.cnf y reinicie (esta variable no es dinámica y requiere reinicio). Para comprobar el estado: SELECT @@innodb_read_only; Verifique que el directorio de datos y el sistema de archivos admitan escritura. Si está escribiendo en una réplica/secundario de HA (1290), dirija las escrituras al primario.","tags":["innodb_read_only","read only","read-only mode","innodb","1836"]},{"code":"3819","title":"ER_CHECK_CONSTRAINT_VIOLATED: Check constraint is violated (se infringe la restricción CHECK)","severity":"caution","meaning":"La fila que inserta o actualiza infringe una restricción CHECK definida en la tabla. En MySQL 8.0.16 y posteriores, las restricciones CHECK se aplican realmente (las versiones anteriores las ignoraban).","cause":"El valor de la columna no cumple la condición de la expresión CHECK (p. ej., introducir un precio negativo cuando existe CHECK (price >= 0)). Tras actualizar desde versiones antiguas de MySQL a 8.0.16+, puede aparecer de forma inesperada porque ahora se aplican restricciones que antes se ignoraban.","fix":"Asegúrese de que el valor cumpla la condición de la restricción; añada validación en el lado de la aplicación. Para ver la definición de la restricción: SHOW CREATE TABLE my_table; Si la restricción es realmente innecesaria: ALTER TABLE my_table DROP CHECK constraint_name; o temporalmente ALTER TABLE my_table ALTER CHECK constraint_name NOT ENFORCED; Corrija también el origen de los datos erróneos.","tags":["check","constraint","check constraint","validation","8.0","violation"]},{"code":"1118","title":"ER_TOO_BIG_ROWSIZE: Row size too large","severity":"caution","meaning":"El tamaño de fila de la tabla que define supera el tamaño máximo de fila permitido por el motor de almacenamiento. El tamaño máximo de fila para columnas que no son BLOB/TEXT es de 65535 bytes; InnoDB tiene límites adicionales que dependen del tamaño de página.","cause":"Definir muchas columnas anchas (sobre todo VARCHAR/CHAR largos); cada carácter ocupa hasta 4 bytes en juegos de caracteres multibyte como utf8mb4; o columnas grandes almacenadas inline en el formato de fila COMPACT/REDUNDANT de InnoDB.","fix":"Convierta algunas columnas grandes a TEXT/BLOB (pueden almacenarse fuera de página); reduzca las columnas innecesarias o muy anchas, o divida la tabla en dos. En InnoDB, usar ROW_FORMAT=DYNAMIC o COMPRESSED ayuda moviendo las columnas grandes fuera de página: ALTER TABLE my_table ROW_FORMAT=DYNAMIC; Tenga en cuenta también innodb_page_size.","tags":["row size","row size too large","innodb","varchar","row_format","limit"]},{"code":"1170","title":"ER_BLOB_KEY_WITHOUT_LENGTH: BLOB/TEXT column used in key specification without a key length (columna BLOB/TEXT usada en una clave sin longitud de clave)","severity":"caution","meaning":"Usó una columna BLOB o TEXT en una definición de índice/clave sin especificar una longitud de prefijo. Estos tipos no se pueden indexar completos; solo se pueden indexar los primeros N caracteres/bytes.","cause":"Definir un INDEX/UNIQUE/PRIMARY KEY sobre una columna TEXT/BLOB sin longitud en CREATE TABLE o CREATE INDEX. MySQL exige una longitud de prefijo porque no puede indexar completos estos valores potencialmente muy grandes.","fix":"Especifique una longitud de prefijo en el índice, como INDEX (description(255)). Ejemplo: ALTER TABLE my_table ADD INDEX idx_description (description(191)); (191 se usa habitualmente con utf8mb4 por el límite de prefijo de clave de InnoDB). Si se necesita búsqueda de texto completo, considere un índice FULLTEXT. Para campos que deben ser cortos y únicos, puede ser más adecuado convertir la columna a VARCHAR.","tags":["index","key","blob","text","prefix","prefix length"]},{"code":"1592","title":"ER_BINLOG_UNSAFE_STATEMENT: Unsafe statement written to the binary log (sentencia no segura escrita en el binary log)","severity":"caution","meaning":"Con el formato de binlog basado en STATEMENT, se ejecutó una sentencia de resultado no determinista (que podría producir un resultado distinto en la réplica). MySQL emite una advertencia al escribirla en el binlog porque hay riesgo de inconsistencia de datos entre el source y la réplica.","cause":"Con binlog_format=STATEMENT: funciones no deterministas como UUID(), SYSDATE() o USER() (NOW() se replica de forma segura con STATEMENT), UPDATE/DELETE con LIMIT pero sin ORDER BY, INSERT combinados con AUTO_INCREMENT o ciertas funciones definidas por el usuario/del sistema. Estas sentencias pueden no producir el mismo resultado en todos los servidores.","fix":"Cambie el formato de binlog a basado en filas (recomendado; ROW es el valor predeterminado desde MySQL 5.7.7): binlog_format=ROW en my.cnf o SET PERSIST binlog_format = ROW; (la variable binlog_format está obsoleta desde 8.0.34; el cambio surte efecto en las sesiones nuevas). El formato ROW replica de forma segura las sentencias no deterministas. Si debe seguir con STATEMENT, sustituya las funciones/expresiones no deterministas por valores constantes y añada un ORDER BY determinista al DML con LIMIT.","tags":["binlog","replication","unsafe","statement","row","nondeterministic"]},{"code":"3140","title":"ER_INVALID_JSON_TEXT: Invalid JSON text","severity":"caution","meaning":"Se pasó un texto JSON no válido (sintácticamente roto) a una columna JSON o a una función JSON. MySQL rechaza la operación porque no puede analizar el valor como JSON.","cause":"Una comilla/corchete sin cerrar, una coma final, comillas simples, errores en caracteres de escape o texto plano enviado a una columna JSON. Aparece cuando se pasa texto mal formado a funciones como JSON_SET, JSON_INSERT o a una columna de tipo JSON.","fix":"Corrija el texto JSON para que tenga una sintaxis válida: use comillas dobles, elimine las comas finales, verifique los escapes. Compruebe si el texto es válido: SELECT JSON_VALID('{...}'); (devuelve 1 si es válido). Use una biblioteca fiable para serializar JSON en la capa de aplicación y valídelo antes de enviarlo al servidor.","tags":["json","invalid","syntax","json_valid","parse","data"]},{"code":"1526","title":"ER_NO_PARTITION_FOR_GIVEN_VALUE: Table has no partition for value (la tabla no tiene ninguna partición para el valor)","severity":"caution","meaning":"El valor de la clave de particionamiento de la fila que inserta no encaja en ninguna partición definida. En el particionamiento RANGE o LIST, la inserción falla porque ninguna partición cubre el valor entrante.","cause":"En el particionamiento RANGE, insertar un valor por encima del límite superior sin una partición MAXVALUE definida (p. ej., una fecha futura); en el particionamiento LIST, la llegada de un valor no listado. Habitual en tablas particionadas por tiempo cuando la partición del nuevo periodo no se añade a tiempo.","fix":"Añada una partición que cubra el valor. Una nueva partición superior para RANGE: ALTER TABLE my_table ADD PARTITION (PARTITION p2026 VALUES LESS THAN (2027)); Para una partición comodín, defina MAXVALUE en RANGE y los valores correspondientes en LIST. En tablas basadas en tiempo, configure un job de mantenimiento (event/scheduler) que añada particiones automáticamente.","tags":["partition","partitioning","range","list","maxvalue","insert"]}]},"errors/mssql-more2.json":{"engineId":"mssql","engine":"SQL Server","order":4,"errors":[{"code":"1101","title":"Could not allocate a new page because of insufficient disk space in filegroup (no se pudo asignar una nueva página por falta de espacio en disco en el filegroup)","severity":"danger","meaning":"SQL Server intentó asignar una nueva página para un objeto, pero el filegroup no tiene suficiente espacio en disco. Los archivos de datos no pueden crecer ni encontrar una página libre, por lo que la escritura falla.","cause":"El disco que contiene el archivo de datos está lleno; el autogrow está deshabilitado en el archivo o se alcanzó MAXSIZE; o todos los archivos del filegroup llegaron a su tamaño máximo. Habitual durante INSERT grandes/creación de índices.","fix":"Libere espacio en el disco o añada espacio al filegroup. Aumente el archivo o habilite el autogrow: ALTER DATABASE db MODIFY FILE (NAME = logical_name, SIZE = 20GB, MAXSIZE = UNLIMITED, FILEGROWTH = 512MB); Como alternativa, añada un nuevo archivo al filegroup (en otro disco). Para ver el uso de los archivos, consulte sys.database_files y el espacio libre con DBCC SQLPERF.","tags":["disk","filegroup","file group","autogrow","space","allocation"]},{"code":"3729","title":"Cannot DROP because it is being referenced by object (no se puede eliminar porque otro objeto lo referencia)","severity":"caution","meaning":"El objeto que quiere eliminar (tabla, tipo, función, etc.) está referenciado por otro objeto, y esa dependencia impide eliminarlo. Los objetos con schema binding, en particular, están protegidos de esta forma.","cause":"Intentar eliminar una tabla/objeto del que depende una vista o función creada WITH SCHEMABINDING; eliminar una tabla referenciada por una foreign key; o quitar un tipo definido por el usuario mientras una tabla/parámetro todavía lo usa.","fix":"Elimine primero los objetos que lo referencian o quite la dependencia. Para encontrar dependencias: SELECT referencing_schema_name, referencing_entity_name FROM sys.dm_sql_referencing_entities('app_schema.object', 'OBJECT'); En vistas con schema binding, quite primero SCHEMABINDING con ALTER VIEW o elimine la vista, y después elimine la tabla.","tags":["drop","reference","schemabinding","view","dependencies","dependency"]},{"code":"3961","title":"Snapshot isolation transaction failed because the object was modified by a DDL statement (falló la transacción snapshot porque una sentencia DDL modificó el objeto)","severity":"caution","meaning":"Una transacción con aislamiento snapshot falló porque un objeto al que accedía fue modificado por una sentencia DDL (cambio de esquema) en otra transacción concurrente. Los metadatos no tienen versiones, por lo que la transacción snapshot no puede continuar.","cause":"Mientras se ejecuta una transacción iniciada con aislamiento SNAPSHOT (SET TRANSACTION ISOLATION LEVEL SNAPSHOT), otra sesión ejecuta DDL como ALTER TABLE/CREATE INDEX sobre una tabla a la que accede. Las transacciones snapshot pueden ver versiones de filas, pero no versiones del esquema. Este error no se produce con READ COMMITTED SNAPSHOT (RCSI); RCSI toma un nuevo snapshot para cada sentencia.","fix":"Reintente la transacción; normalmente funciona una vez completado el DDL. Como solución permanente, realice los cambios de esquema (DDL) en ventanas de mantenimiento sin carga snapshot intensa. Capture este error en la aplicación e implemente una lógica de reintento automático. Acorte las transacciones snapshot de larga duración.","tags":["snapshot","isolation level","ddl","concurrency","retry","isolation"]},{"code":"1785","title":"Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths (la restricción FOREIGN KEY puede provocar ciclos o varias rutas de cascada)","severity":"caution","meaning":"La foreign key que añade podría crear un ciclo o varias rutas de eliminación/actualización en cascada hacia la misma tabla en operaciones en cascada (ON DELETE/UPDATE CASCADE). SQL Server la rechaza para evitar un comportamiento ambiguo de la cascada.","cause":"Una FK con acción en cascada que crea una referencia circular entre tablas (A->B->A) o varias rutas de cascada hacia una tabla. SQL Server no lo permite para evitar un orden de cascada ambiguo y bucles infinitos.","fix":"Defina la foreign key como ON DELETE NO ACTION / ON UPDATE NO ACTION (quite la cascada): ALTER TABLE child ADD CONSTRAINT fk ... FOREIGN KEY (...) REFERENCES parent(...) ON DELETE NO ACTION; Implemente la eliminación en cascada necesaria con un trigger INSTEAD OF DELETE: el trigger elimina primero las filas hijas y después la fila padre (p. ej., DELETE c FROM child c JOIN deleted d ON c.parent_id = d.id; DELETE e FROM parent e JOIN deleted d ON e.id = d.id;). Un trigger AFTER DELETE no funciona; la comprobación de la FK se ejecuta antes del trigger, por lo que la eliminación se rechaza con el error 547. Revise las dependencias circulares del modelo de datos.","tags":["foreign key","fk","cascade","circular reference","cycle","constraint"]},{"code":"9421","title":"XML parsing: illegal xml character","severity":"caution","meaning":"SQL Server encontró un carácter no permitido (no válido) al analizar texto como XML. La conversión al tipo XML o la asignación a una columna/variable XML falló por este carácter.","cause":"Caracteres de control prohibidos en XML 1.0 (p. ej., algunos caracteres del rango 0x00-0x1F), una codificación incorrecta (encoding no coincidente) o datos corruptos/sin sanear procedentes de otra fuente. Suele aparecer durante CAST(... AS XML) o un INSERT en una columna XML.","fix":"Elimine los caracteres no válidos del texto antes de convertirlo a XML. Haga compatible la codificación de origen (UTF-8/UTF-16) con la de destino; declare la codificación correcta en la declaración XML. Elimine los caracteres de control con REPLACE/NCHAR, o genere y valide el XML con una biblioteca estándar en la capa de aplicación. Si debe almacenar el texto sin procesar, considere NVARCHAR(MAX).","tags":["xml","parse","character","encoding","invalid","cast"]},{"code":"15151","title":"Cannot find the object/principal because it does not exist or you do not have permission (no se encuentra el objeto/principal porque no existe o no tiene permiso)","severity":"caution","meaning":"El objeto o principal de seguridad (usuario, rol, login) que referenció no existe o usted no tiene permiso para acceder a él/actuar sobre él. Por motivos de seguridad, SQL Server combina ambos casos en un único mensaje.","cause":"Usar un nombre de usuario, rol, esquema u objeto inexistente en un comando como GRANT/ALTER/DROP; ejecutar en el contexto de base de datos equivocado (el objeto está en otra BD); o carecer del privilegio para la operación. Las erratas, las mayúsculas/minúsculas y los prefijos de esquema son causas frecuentes.","fix":"Verifique que el nombre del objeto/principal esté bien escrito y que esté en la base de datos correcta. Para usuarios: SELECT name, type_desc FROM sys.database_principals; para objetos, consulte sys.objects. Asegúrese de que la cuenta que ejecuta tenga los permisos necesarios (p. ej., ALTER ANY USER, CONTROL). Use el prefijo de esquema correcto (como dbo.object).","tags":["permission","principal","privilege","not found","grant","security"]},{"code":"4834","title":"You do not have permission to use the bulk load statement (no tiene permiso para usar la sentencia de carga masiva)","severity":"caution","meaning":"Carece del permiso necesario para ejecutar una carga masiva con BULK INSERT (u OPENROWSET BULK). La carga masiva accede directamente al sistema de archivos y a los recursos del servidor, por lo que requiere un privilegio aparte.","cause":"El login carece del permiso de nivel de servidor ADMINISTER BULK OPERATIONS o no es miembro del rol de servidor bulkadmin (Azure SQL Database requiere en su lugar ADMINISTER DATABASE BULK OPERATIONS a nivel de base de datos). También se requiere permiso INSERT sobre la tabla de destino y acceso de lectura al archivo de origen para la cuenta de servicio de SQL Server.","fix":"Conceda el permiso necesario: GRANT ADMINISTER BULK OPERATIONS TO login_name; (nivel de servidor) o añada el usuario al rol correspondiente. Azure SQL DB usa ADMINISTER DATABASE BULK OPERATIONS. Asegúrese también de que la cuenta de servicio de SQL Server tenga permiso de lectura sobre el archivo/recurso compartido de destino. Siguiendo el principio de mínimo privilegio, concédalo solo a las cuentas que lo necesiten.","tags":["bulk insert","privilege","permission","bulk load","bulkadmin","file"]},{"code":"10928","title":"Resource limit for the database has been reached (Azure SQL) (se alcanzó el límite de recursos de la base de datos)","severity":"danger","meaning":"En Azure SQL Database, la base de datos alcanzó un límite de recursos (como solicitudes/workers concurrentes o sesiones) y se rechazó la nueva solicitud. Indica que se alcanzó el techo del nivel de servicio seleccionado (DTU o vCore).","cause":"Superar el máximo de solicitudes concurrentes (workers) o sesiones que permite el nivel seleccionado; un pico repentino de carga; consultas ineficientes/de larga duración que consumen worker threads; o un pool de conexiones sobredimensionado.","fix":"Reduzca la concurrencia: optimice las consultas para acortar su duración, cierre las transacciones abiertas innecesarias, mantenga un tamaño razonable del pool de conexiones. Ante una carga sostenida, escale el nivel de servicio (más DTU/vCore). Supervise el uso de recursos con sys.dm_db_resource_stats. Para errores reintentables, implemente en la aplicación reintentos con backoff exponencial.","tags":["azure","limit","resource","dtu","vcore","concurrency"]},{"code":"40544","title":"The database has reached its size quota (Azure SQL) (la base de datos alcanzó su cuota de tamaño)","severity":"danger","meaning":"Azure SQL Database alcanzó la cuota máxima de tamaño de datos establecida para el nivel de servicio. La base de datos no puede crecer más, por lo que las nuevas escrituras fallan.","cause":"Datos que crecen hasta el límite del nivel; índices/log grandes que consumen espacio; o un MAXSIZE pequeño permitido por el nivel seleccionado (p. ej., Basic/serie S de Standard). Los datos históricos sin archivar también llenan rápidamente la cuota.","fix":"Libere espacio: elimine/archive los datos innecesarios, elimine los índices no utilizados, comprima tablas (data compression). Como solución permanente, aumente MAXSIZE o pase a un nivel de servicio superior: ALTER DATABASE db MODIFY (MAXSIZE = 250 GB); Supervise el uso de tamaño con sys.database_files y las DMV relacionadas. Planifique el escalado con antelación según la tendencia de crecimiento.","tags":["azure","size","quota","maxsize","size quota","scaling"]},{"code":"40613","title":"Database is not currently available (Azure SQL) (la base de datos no está disponible en este momento)","severity":"danger","meaning":"En Azure SQL, la base de datos de destino no es accesible en este momento. Suele ser transitorio; aparece brevemente mientras la plataforma mueve, reconfigura o recupera la base de datos.","cause":"Escalado/failover automático, aplicación de parches, balanceo de carga o reubicación de recursos en el lado de Azure; rara vez, un evento de plataforma más prolongado. Las conexiones se rechazan hasta que la base de datos vuelve a estar online.","fix":"Espere un momento y reintente; es un error transitorio. Implemente en la aplicación reintentos automáticos con backoff exponencial; los SDK/bibliotecas de reintento de Azure reconocen este código de error como transitorio. Si el problema persiste, revise Azure Service Health y los incidentes regionales. Para alta disponibilidad, considere geo-replicación/un failover group.","tags":["azure","temporary","transient","availability","retry","failover"]}]},"errors/mongodb-more2.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"errors":[{"code":"31 RoleNotFound","title":"Role not found","severity":"caution","meaning":"El rol referenciado no está definido en MongoDB. Se usó un nombre de rol inexistente al conceder un rol a un usuario, actualizar un rol o consultar los privilegios de un rol.","cause":"Un nombre de rol mal escrito o nunca creado en grantRolesToUser/createUser; buscar el rol en la base de datos equivocada (los roles están ligados a una BD concreta); o un rol personalizado (no integrado) que aún no se ha definido con createRole.","fix":"Verifique el nombre del rol y la base de datos a la que pertenece. Liste los roles existentes: db.getRoles({showBuiltinRoles: false}) o la consulta de administración equivalente en todas las BD. Si el rol personalizado no existe, créelo primero: db.createRole({role:'reader', privileges:[...], roles:[]}). Especifique la db correcta al conceder: db.grantRolesToUser('app_user', [{role:'reader', db:'app'}]).","tags":["role","roles","authorization","access control","createrole","user"]},{"code":"263 OperationNotSupportedInTransaction","title":"Operation not supported inside a multi-document transaction (operación no admitida dentro de una transacción multidocumento)","severity":"caution","meaning":"Se intentó, dentro de una transacción multidocumento, una operación no admitida en ese contexto. Algunos comandos (en particular, ciertas operaciones DDL y de metadatos) no pueden ejecutarse dentro de una transacción.","cause":"Operaciones no admitidas dentro de la transacción, como crear colecciones/índices (según la versión); ciertos comandos de tipo count/list; o comandos administrativos no válidos en una sesión de transacción. Las versiones antiguas de MongoDB no admiten DDL dentro de transacciones (una parte llegó con 4.4+).","fix":"Saque la operación no admitida de la transacción (p. ej., cree las colecciones/índices necesarios antes de que empiece la transacción). Revise su versión de MongoDB; 4.4+ admite parte del DDL dentro de transacciones. Ejecute la operación en una sesión aparte en el driver. Limite la transacción a operaciones CRUD admitidas.","tags":["transaction","multi-document transaction","ddl","unsupported","session","crud"]},{"code":"20 IllegalOperation","title":"Illegal operation","severity":"caution","meaning":"La operación solicitada no es válida/está prohibida para el estado actual del servidor o el tipo del objeto de destino. MongoDB devuelve este error genérico cuando rechaza una acción no permitida lógicamente.","cause":"Intentar una operación exclusiva de colecciones sobre una vista; un renombrado no válido entre dos bases de datos; un cambio prohibido en colecciones del sistema; o una acción administrativa no permitida por la configuración del replica set/sharding. Varía según el contexto.","fix":"Lea los detalles del mensaje de error; normalmente indica explícitamente qué operación se prohibió y por qué. Adapte la operación al tipo del objeto de destino (p. ej., trabaje sobre la colección subyacente en lugar de la vista). Para operaciones de renombrado/movimiento, revise las restricciones de MongoDB (como renameCollection dentro de la misma BD). Revise el estado del clúster (replica set/sharding).","tags":["illegal","invalid","operation","view","rename","system"]},{"code":"48 NamespaceExists","title":"Namespace (collection) already exists","severity":"safe","meaning":"La colección (namespace) que intenta crear ya existe. MongoDB informa de que está presente, por lo que no es necesario crear una nueva colección con el mismo nombre y opciones.","cause":"Intentar volver a crear una colección existente con createCollection (posiblemente con opciones distintas); scripts de esquema/instalación ejecutados más de una vez; o intentar redefinir un nombre existente como capped collection o con opciones especiales.","fix":"Normalmente se puede ignorar sin riesgo; significa que la colección ya existe. Haga idempotentes sus scripts de instalación: compruebe la existencia antes de crear (db.getCollectionNames().includes('name')). Para cambiar las opciones debe eliminar y volver a crear la colección (haga antes un backup de los datos): db.my_collection.drop() y luego createCollection.","tags":["namespace","collection","collection exists","createcollection","already exists","idempotent"]},{"code":"11601 Interrupted","title":"Operation was interrupted","severity":"caution","meaning":"La operación en ejecución se interrumpió. MongoDB la terminó antes de completarse por una intervención externa o un cambio en el estado del servidor.","cause":"Un administrador/herramienta que termina la operación con db.killOp(); el servidor entrando en apagado; un cambio de primario en el replica set (stepdown/failover). (Superar maxTimeMS devuelve 50 MaxTimeMSExpired, no este error.) Las consultas de larga duración pueden detenerse de esta forma.","fix":"Investigue por qué se interrumpió la operación: historial de currentOp/killOp, eventos de failover, mantenimiento del servidor. Si la operación es idempotente, reinténtela de forma segura. Optimice y acorte las consultas largas; añada índices adecuados. Use en el driver una lógica de reintento ante errores transitorios; garantice la reconexión automática ante failovers del replica set.","tags":["interrupted","interruption","killop","failover","stepdown","retry"]},{"code":"85 IndexOptionsConflict","title":"Index already exists with different options / different name (el índice ya existe con otras opciones / otro nombre)","severity":"caution","meaning":"El índice que crea entra en conflicto con uno existente: un índice con el mismo nombre tiene opciones distintas, o ya existe con otro nombre un índice equivalente con la misma clave y opciones. MongoDB no permite volver a crear el mismo índice con una definición en conflicto.","cause":"Crear un índice con el mismo nombre y clave pero opciones distintas (p. ej., expireAfterSeconds, partialFilterExpression, collation); o redefinir con otro nombre el equivalente de un índice existente ('Index already exists with a different name'). La causa típica es que las definiciones de índices se desvían con el tiempo en los scripts de aplicación/migración. El mismo nombre con una clave distinta da en su lugar 86 IndexKeySpecsConflict.","fix":"Vea el índice existente: db.my_collection.getIndexes(). Si realmente quiere cambiar las opciones, elimine el índice antiguo y cree el nuevo: db.my_collection.dropIndex('index_name') y luego createIndex(...). En caso contrario, haga que la definición coincida exactamente con el índice existente o dé al nuevo índice otro nombre. Haga idempotentes los scripts de migración.","tags":["index","indexes","options","conflict","unique","createindex"]},{"code":"67 CannotCreateIndex","title":"Cannot create index","severity":"caution","meaning":"La creación del índice falló por una restricción o un límite. MongoDB no pudo crear el índice solicitado debido a los datos existentes o a la configuración.","cause":"Superar el número máximo de índices permitido por colección (64); una clave demasiado grande; o combinaciones de opciones incompatibles (p. ej., un tipo de campo inadecuado para un índice TTL). Nota: crear un índice único cuando los datos existentes contienen duplicados genera E11000 DuplicateKey, no este código.","fix":"Si alcanza el límite de número de índices, elimine los índices no utilizados (analice su uso con $indexStats). Revise la compatibilidad de tamaño/tipo de la clave y la combinación de opciones. En colecciones grandes, cree el índice en periodos de baja carga (desde 4.2 la creación de índices no bloquea las escrituras durante mucho tiempo; la opción background se ignora).","tags":["index","indexes","unique","duplicate","limit","ttl"]},{"code":"27 IndexNotFound","title":"Index not found","severity":"safe","meaning":"No se encontró en la colección el índice referenciado. Suele aparecer al intentar eliminar o renombrar un índice inexistente.","cause":"Indicar un nombre de índice o una especificación de clave incorrectos en un comando como dropIndex/hideIndex; que el índice se haya eliminado antes; o que el nombre de índice generado automáticamente difiera del esperado (MongoDB genera nombres como field_1_field2_-1).","fix":"Liste los índices existentes y sus nombres exactos: db.my_collection.getIndexes(). Repita la operación con el nombre o el documento de clave correctos: db.my_collection.dropIndex({field: 1}) o db.my_collection.dropIndex('field_1'). Escriba los scripts de eliminación para que no fallen cuando el índice no existe (con una comprobación de existencia).","tags":["index","indexes","dropindex","not found","getindexes","name"]},{"code":"79 UnknownReplWriteConcern","title":"Unknown replica set write concern","severity":"caution","meaning":"No se reconoce el write concern especificado. El valor w usado en la operación (un nombre de write concern personalizado/con etiquetas o un valor numérico) no está definido en la configuración del replica set.","cause":"Usar un nombre de write concern personalizado no definido en getLastErrorModes (p. ej., una errata en lugar de 'majority' o un nombre de etiqueta personalizado). La configuración y la solicitud no coinciden. Nota: solicitar un w numérico superior al número de miembros con voto es un error distinto (100 UnsatisfiableWriteConcern).","fix":"Asegúrese de que el nombre de write concern que usa esté definido en la configuración del replica set. Vea la configuración actual: rs.conf() (settings.getLastErrorModes). Si usa un modo personalizado, defínalo primero o cambie a valores estándar ('majority' o un w numérico válido). Revise el ajuste writeConcern en el driver/la cadena de conexión de la aplicación.","tags":["write concern","write acknowledgement","replica set","majority","getlasterrormodes","config"]}]},"errors/redis-more2.json":{"engineId":"redis","engine":"Redis","order":6,"errors":[{"code":"ERR DEBUG command not allowed","title":"DEBUG command not allowed","severity":"caution","meaning":"Intentó ejecutar el comando DEBUG, pero el servidor no lo permite. DEBUG es un comando de diagnóstico de bajo nivel y peligroso; por motivos de seguridad está deshabilitado o restringido de forma predeterminada en las versiones recientes de Redis.","cause":"En Redis 7+, los subcomandos DEBUG no se ejecutan sin el ajuste enable-debug-command; los servicios gestionados de Redis en la nube suelen deshabilitar el comando por completo; o una ACL deniega el comando. Los scripts de prueba/diagnóstico (p. ej., DEBUG SLEEP, DEBUG OBJECT) suelen encontrarse con este error.","fix":"Evite DEBUG en producción. Si es realmente necesario en un entorno local/de pruebas, habilítelo en la configuración: enable-debug-command yes (o 'local') en redis.conf. En servicios en la nube normalmente no se puede habilitar el comando; use comandos seguros como MEMORY USAGE, OBJECT ENCODING e INFO para obtener información equivalente. Si se aplica una restricción de ACL, revise los permisos.","tags":["debug","command","prohibited","security","acl","config"]},{"code":"ERR Can't save in background: fork failed","title":"Background save failed: fork failed","severity":"danger","meaning":"Redis no pudo crear un proceso hijo (fork) para un snapshot RDB en segundo plano o una reescritura de AOF. Como la persistencia no puede ejecutarse, los datos no se pueden escribir en disco.","cause":"El sistema operativo no puede asignar memoria suficiente para el fork; normalmente overcommit_memory está deshabilitado y el sistema lo rechaza cuando Redis intenta reservar aproximadamente una copia de la memoria que usa. También puede deberse a memoria física/virtual insuficiente o a límites de número de procesos.","fix":"Habilite el overcommit de memoria en Linux: sysctl vm.overcommit_memory=1 (añádalo a /etc/sysctl.conf para que sea permanente). Añada memoria al servidor o reduzca el uso de memoria de Redis (maxmemory). Proporcione suficiente espacio de swap. Revise los límites de procesos/descriptores de archivo (ulimit). Si la persistencia es crítica, no pase a producción sin estos ajustes.","tags":["fork","rdb","aof","persistence","overcommit","memory"]},{"code":"NOPROTO unsupported protocol version","title":"Unsupported RESP protocol version","severity":"caution","meaning":"El cliente solicitó una versión del protocolo RESP que el servidor no admite. Suele devolverse cuando se solicita RESP3 (versión 3) con el comando HELLO y el servidor solo admite RESP2.","cause":"Se envió HELLO 3, pero el servidor ejecuta una versión antigua de Redis (anterior a 6.0) o no admite RESP3; la biblioteca cliente solicita RESP3 de forma predeterminada pero el servidor es antiguo. Una incompatibilidad de versiones.","fix":"Configure su cliente para usar RESP2, o solicite una versión válida con HELLO (HELLO 2). Actualice el servidor a una versión que admita RESP3 (Redis 6+). Revise el ajuste de versión de protocolo de su biblioteca cliente; la mayoría de las bibliotecas pueden volver a RESP2 automáticamente. Verifique la versión del servidor: INFO server (redis_version).","tags":["resp","protocol","hello","resp3","version","compatibility"]},{"code":"ERR Unknown option or number of arguments for CONFIG SET","title":"Unknown option or number of arguments for CONFIG SET (older versions: Unsupported CONFIG parameter) (opción o número de argumentos desconocidos para CONFIG SET)","severity":"caution","meaning":"El parámetro de configuración que intentó cambiar con CONFIG SET no se reconoce en esta versión de Redis, o el número de argumentos es incorrecto. En Redis 7 el mensaje completo es \"ERR Unknown option or number of arguments for CONFIG SET - '<name>'\"; las versiones antiguas devuelven \"ERR Unsupported CONFIG parameter\". CONFIG GET no genera error ante un parámetro desconocido; devuelve una lista vacía.","cause":"Un nombre de parámetro mal escrito; usar un parámetro de otra versión de Redis (los parámetros pueden añadirse y eliminarse entre versiones); o que el servicio gestionado en la nube no permita cambiar ciertos parámetros. La causa típica es ejecutar scripts antiguos en versiones más nuevas/antiguas.","fix":"Verifique el nombre de parámetro válido. Liste los ajustes actuales con un patrón, como CONFIG GET 'maxmemory*'. Revise la lista de parámetros en la documentación de su versión de Redis; vea la versión con INFO server. Si hay restricciones en un servicio en la nube, use el conjunto de parámetros que permite el proveedor. Actualice sus scripts para la versión de destino.","tags":["config","parameter","unsupported","version","config set","config get"]},{"code":"ERR MULTI calls can not be nested","title":"MULTI calls can not be nested","severity":"caution","meaning":"Se envió MULTI de nuevo mientras ya había un bloque MULTI (transacción) abierto. Redis no admite bloques de transacción anidados; solo puede haber un MULTI activo a la vez.","cause":"La lógica de la aplicación inicia un nuevo MULTI sin cerrar el anterior con EXEC/DISCARD; se reutiliza la misma conexión de un pool de conexiones sin restablecer correctamente el estado de la transacción; o un wrapper de biblioteca llama a MULTI dos veces sin saberlo.","fix":"Cierre cada bloque MULTI con un único EXEC (o DISCARD para cancelar) y abra un nuevo bloque solo después de terminar el anterior. Separe claramente el inicio y el fin de la transacción en el flujo del código. Asegúrese de que no quede ninguna transacción abierta antes de devolver una conexión al pool. Si se necesita lógica anidada, considere realizar la operación atómica con un script Lua (EVAL).","tags":["multi","transaction","nested","nested","exec","discard"]},{"code":"ERR Lua script attempted to access nonexistent global variable","title":"Lua script attempted to access a nonexistent global variable (el script Lua intentó acceder a una variable global inexistente)","severity":"caution","meaning":"Un script Lua ejecutado con EVAL/EVALSHA intentó acceder a una variable global no definida. Para proteger el entorno de scripting, Redis restringe la creación/lectura de variables globales y devuelve ese acceso como error.","cause":"Usar por accidente una variable global en lugar de una local en el script (olvidar la palabra clave local); acceder a un nombre no definido por una errata; o intentar mantener estado en una variable global. El entorno Lua aislado (sandbox) de Redis no lo permite.","fix":"Declare todas las variables del script con local: local x = ... Corrija las erratas; asegúrese de que cada variable esté definida antes de usarla. Los scripts deben ser sin estado; para un estado persistente use claves de Redis (KEYS/redis.call), no variables globales de Lua. Mantenga el script corto y determinista.","tags":["lua","eval","script","global","variable","local"]},{"code":"ERR unknown command","title":"Unknown command","severity":"caution","meaning":"Redis no reconoce el comando que envió. Puede que el nombre del comando sea incorrecto, no esté disponible en esta versión o esté deshabilitado/renombrado.","cause":"Un nombre de comando mal escrito; el comando se añadió en una versión más reciente de Redis (no existe en el servidor antiguo); llamar a un comando que requiere un módulo (p. ej., RedisJSON, RediSearch) sin el módulo cargado; o el comando se deshabilitó con rename-command/ACL. A veces una división incorrecta de argumentos por parte del cliente también hace que el nombre del comando parezca corrupto.","fix":"Verifique el nombre y la sintaxis del comando (COMMAND DOCS command o la documentación). Revise la versión del servidor: INFO server. Para un comando de módulo, verifique que el módulo esté cargado: MODULE LIST. Si el comando se ocultó con rename-command, revise la configuración. Asegúrese de que su biblioteca cliente codifique el comando correctamente.","tags":["command","unknown","unknown","version","module","acl"]},{"code":"TRYAGAIN Multiple keys request during rehashing of slot","title":"Multiple keys request during rehashing of slot (cluster) (solicitud de varias claves durante el rehashing del slot)","severity":"caution","meaning":"En Redis Cluster, mientras se movía un hash slot entre nodos (rehashing/resharding), se hizo una solicitud con varias claves del mismo slot, y algunas claves ya se habían movido y otras no. El servidor le pide que reintente la operación en breve.","cause":"Ejecutar un comando multiclave (MGET, MSET, Lua multiclave, transacción) mientras hay una migración de slot en curso. En el slot que se migra, algunas claves están en el nodo de origen y otras en el de destino, por lo que el comando no puede ejecutarse de forma atómica.","fix":"Capture este error en el cliente y reintente tras una breve espera (TRYAGAIN es transitorio; desaparece cuando termina el resharding). Para operaciones multiclave, garantice que todas las claves estén en el mismo slot con un hash tag, como {app_user123}.profile, {app_user123}.settings. Realice el rebalanceo del clúster (resharding) en periodos de poco tráfico.","tags":["cluster","slot","rehash","resharding","tryagain","hash tag"]},{"code":"ERR DISCARD without MULTI","title":"DISCARD without MULTI","severity":"caution","meaning":"Se envió DISCARD sin ningún bloque MULTI abierto. DISCARD solo se usa para cancelar una transacción en curso; devuelve este error cuando no hay nada que cancelar.","cause":"Llamar a DISCARD sin MULTI, o de nuevo después de que la transacción ya se cerrara con EXEC/DISCARD; una llamada incondicional a DISCARD en el código de manejo de errores; o un estado de la conexión distinto del esperado (no hay MULTI abierto en la conexión tomada del pool).","fix":"Llame a DISCARD solo cuando haya realmente un MULTI abierto; lleve el control del estado de la transacción en el código. En los manejadores de errores, compruebe si hay una transacción abierta antes de llamar a DISCARD. Use el wrapper de transacciones de su biblioteca para que gestione el estado automáticamente. Garantice un estado limpio en el pool de conexiones.","tags":["discard","multi","transaction","cancel","state","error handling"]}]},"errors/elasticsearch-more2.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"errors":[{"code":"search_context_missing_exception","title":"No search context found for id (scroll expired) (no se encontró el contexto de búsqueda para el id; el scroll caducó)","severity":"caution","meaning":"El contexto de búsqueda que usaba una consulta scroll ya no existe en el servidor. Suele aparecer al continuar con un scroll_id guardado después de que el contexto de scroll haya caducado o se haya eliminado.","cause":"Caducó el keep-alive especificado para el scroll (pausa demasiado larga entre solicitudes); el contexto se eliminó manualmente (clear scroll); o el contexto se perdió al reiniciarse un nodo. Los contextos de scroll se mantienen en la memoria del nodo, por lo que su vida útil es limitada.","fix":"Pase un keep-alive suficiente en cada solicitud de scroll (p. ej., scroll=5m) y acorte el retraso entre páginas. En Elasticsearch moderno, use search_after + Point in Time (PIT) en lugar de scroll para la paginación profunda; es más escalable y el método recomendado. Libere los contextos al terminar (clear scroll) para no desperdiciar memoria.","tags":["scroll","search_after","pit","context","timeout","pagination"]},{"code":"aliases_not_found_exception","title":"Aliases missing","severity":"safe","meaning":"El alias sobre el que intenta operar no existe. Suele devolverse al eliminar un alias inexistente o actuar sobre él.","cause":"El nombre del alias está mal escrito; el alias se eliminó antes; o una acción remove de la API _aliases nombra un alias que nunca se asoció. También aparece cuando ningún alias coincide con un patrón comodín.","fix":"Liste los alias existentes: GET _alias, o para un índice concreto GET my-index/_alias. Reintente la operación con el nombre correcto. Escriba los scripts de eliminación para que no fallen cuando el alias no existe; gestione las acciones de forma condicional en las operaciones masivas de _aliases. Para reasignar alias de forma atómica, use add y remove en una única solicitud _aliases.","tags":["alias","alias name","not found","aliases","index","routing"]},{"code":"snapshot_missing_exception","title":"Snapshot is missing","severity":"caution","meaning":"No se encontró el snapshot referenciado en el repositorio especificado. Indica que se usó un nombre de snapshot inexistente en una solicitud de restauración, eliminación o estado.","cause":"El nombre del snapshot está mal escrito; el snapshot se eliminó antes; se consultó el repositorio equivocado; o el backup nunca se completó. Se obtiene un resultado similar cuando la definición del repositorio ha cambiado o no es accesible.","fix":"Liste los snapshots del repositorio: GET _snapshot/repo_name/_all (o para un nombre concreto GET _snapshot/repo_name/snapshot_name). Reintente con el nombre correcto de repositorio y de snapshot. Revise la definición del repositorio: GET _snapshot/repo_name. Verifique que su política de backup (SLM) se ejecute correctamente y genere snapshots.","tags":["snapshot","backup","repository","repo","restore","not found"]},{"code":"index_template_missing_exception","title":"Index template is missing","severity":"safe","meaning":"No se encontró la plantilla de índice referenciada. Se usó un nombre de plantilla inexistente al obtener, eliminar o realizar una operación basada en una plantilla.","cause":"El nombre de la plantilla está mal escrito; la plantilla se eliminó antes; se confundió la API de plantillas legacy con la API de composable index templates; o la plantilla nunca se creó. También aparece cuando un script de instalación no se completó.","fix":"Liste las plantillas existentes. Para plantillas composable: GET _index_template o GET _index_template/name. Para plantillas legacy: GET _template. Reintente con la API y el nombre correctos. Si falta la plantilla, créela: PUT _index_template/name {...}. Haga idempotentes los scripts de eliminación/actualización para que no fallen cuando la plantilla no exista.","tags":["index template","template","composable","legacy","not found","mapping"]},{"code":"settings_exception","title":"Invalid settings","severity":"caution","meaning":"La operación se rechazó porque el ajuste indicado no es válido. Al aplicar ajustes de índice o de clúster, hay un problema con el nombre del ajuste, su valor o si puede cambiarse en ese contexto.","cause":"Un nombre de ajuste desconocido/mal escrito; un valor de tipo incorrecto o fuera del rango permitido; intentar cambiar en un índice activo un ajuste estático que solo puede fijarse al crear el índice (p. ej., number_of_shards); o usar un ajuste eliminado/renombrado.","fix":"Valide el nombre y el valor del ajuste con la documentación. Actualice los ajustes dinámicos en el índice activo: PUT my-index/_settings {\"index.number_of_replicas\": 2}. Los ajustes estáticos (p. ej., number_of_shards) solo se fijan en la creación; cambiarlos requiere un reindex. Vea los ajustes actuales: GET my-index/_settings.","tags":["settings","setting","invalid","static","dynamic","reindex"]},{"code":"shard_not_found_exception","title":"Shard not found","severity":"danger","meaning":"No se encontró el shard de destino. Elasticsearch no pudo completar la solicitud porque no pudo llegar al shard correspondiente del índice especificado.","cause":"El shard aún no está asignado (unassigned) o está en recuperación; el shard falta temporalmente porque un nodo abandonó el clúster; o una solicitud de bajo nivel dirigida directamente a un shard lleva información de routing que ya no es válida. Aparece cuando la salud del clúster está degradada.","fix":"Revise la salud del clúster y de los shards: GET _cluster/health y GET _cat/shards?v. Vea por qué los shards no están asignados: GET _cluster/allocation/explain. Si se perdió un nodo, recupérelo; revise los ajustes de disco/asignación (watermark). Si hay una recuperación en curso, espere a que termine. Mantenga un número adecuado de réplicas para que los datos sigan disponibles si se pierde un nodo.","tags":["shard","shard","unassigned","unassigned shard","recovery","cluster health"]},{"code":"not_x_content_exception","title":"Compressor detection error / body is not valid content (error de detección del compresor / el cuerpo no es contenido válido)","severity":"caution","meaning":"Elasticsearch no pudo reconocer el cuerpo de la solicitud como contenido estructurado válido (XContent como JSON). Los datos enviados no tienen el formato esperado o están corruptos.","cause":"El cuerpo de la solicitud es JSON no válido; una cabecera Content-Type incorrecta o ausente (no se envió application/json); el cuerpo se envió por accidente comprimido/en binario; o se envió JSON normal a endpoints como _bulk que esperan JSON delimitado por saltos de línea (NDJSON). Los errores de escape/codificación también lo provocan.","fix":"Verifique que el cuerpo de la solicitud sea JSON válido y envíe la cabecera Content-Type: application/json. Para el endpoint _bulk use NDJSON, donde cada línea es un objeto JSON independiente y la última línea termina con un salto de línea (Content-Type: application/x-ndjson). Asegúrese de que su biblioteca cliente serialice el cuerpo correctamente. Si usa compresión, envíe la cabecera Content-Encoding correspondiente.","tags":["json","xcontent","content-type","bulk","ndjson","request body"]},{"code":"document_source_missing_exception","title":"Document source is missing","severity":"caution","meaning":"La operación necesita el campo _source del documento, pero no está almacenado. Se intentó una operación que requiere _source (p. ej., actualización parcial, obtención del source) en un índice con el source deshabilitado.","cause":"El mapping del índice establece _source: {enabled: false}; como no se almacena el JSON original del documento, las actualizaciones parciales, el reindex y las consultas que devuelven _source no pueden funcionar. Las configuraciones que deshabilitan el source para ahorrar espacio causan este problema.","fix":"Las operaciones que necesitan _source requieren que el source esté habilitado en el mapping. Al crear un índice nuevo, deje _source habilitado (lo está de forma predeterminada). Si _source está deshabilitado en un índice existente, vuelva a indexar el documento completo en lugar de actualizarlo (sobrescríbalo con la index API). Para ahorrar espacio, considere el filtrado de _source (includes/excludes) o el _source sintético en lugar de deshabilitar el source por completo.","tags":["_source","source","update","mapping","reindex","document source"]}]},"errors/oracle-more3.json":{"engineId":"oracle","engine":"Oracle","order":1,"errors":[{"code":"ORA-00845","title":"MEMORY_TARGET not supported on this system (MEMORY_TARGET no es compatible con este sistema)","severity":"danger","meaning":"La instancia no puede arrancar porque la gestión automática de memoria solicitada mediante MEMORY_TARGET / MEMORY_MAX_TARGET no es compatible con la configuración de memoria compartida del sistema operativo. En Linux suele deberse a que /dev/shm es más pequeño que el objetivo de memoria solicitado.","cause":"MEMORY_TARGET está configurado con un valor mayor que /dev/shm (montado como tmpfs); o se usa MEMORY_TARGET (AMM) con HugePages habilitado; AMM no es compatible con HugePages, mientras que ASMM (SGA_TARGET) sí lo es. La gestión automática de memoria de Oracle funciona creando archivos en /dev/shm y, si este espacio es insuficiente, la instancia no arranca.","fix":"Haga /dev/shm más grande que MEMORY_TARGET: vuelva a montar tmpfs (mount -o remount,size=8G /dev/shm) y hágalo persistente en /etc/fstab. Como alternativa, use ajustes separados en lugar de AMM: establezca MEMORY_TARGET y MEMORY_MAX_TARGET en 0 y defina SGA_TARGET y PGA_AGGREGATE_TARGET. Si usa HugePages, deshabilite AMM y prefiera ASMM (SGA_TARGET).","tags":["memory_target","memory","amm","dev/shm","hugepages","startup"]},{"code":"ORA-27102","title":"out of memory","severity":"danger","meaning":"Durante el arranque, la instancia de Oracle no pudo asignar el segmento de memoria compartida (SGA) que solicitó al sistema operativo. Suele verse junto con ORA-27102 y un código de error OSD/Linux de nivel inferior (p. ej., Linux-x86_64 Error: 28: No space left on device).","cause":"La memoria solicitada para la SGA supera los límites de memoria compartida del kernel (shmmax, shmall); el servidor no tiene suficiente RAM física/swap; o la configuración de HugePages falta o es insuficiente, por lo que no se puede asignar una SGA grande. Habitual tras aumentar SGA_MAX_SIZE.","fix":"Aumente los parámetros de memoria compartida del kernel: establezca kernel.shmmax y kernel.shmall en /etc/sysctl.conf para cubrir la SGA y ejecute sysctl -p. Si usa HugePages, calcule y establezca vm.nr_hugepages según la SGA. Revise la RAM total y el swap del servidor; si es necesario, reduzca SGA_MAX_SIZE / SGA_TARGET.","tags":["sga","memory","shmmax","hugepages","startup","osd"]},{"code":"ORA-04030","title":"out of process memory when trying to allocate N bytes (memoria de proceso agotada al intentar asignar N bytes)","severity":"danger","meaning":"Un proceso servidor no pudo asignar espacio en su PGA (Program Global Area), es decir, en la memoria privada del proceso. A diferencia de la memoria compartida de la SGA, se trata del agotamiento de la memoria propia de una sola sesión/proceso.","cause":"Operaciones de sort/hash muy grandes, colecciones PL/SQL sobredimensionadas, variables que se acumulan en bucles sin control o límites de memoria de procesos del sistema operativo (ulimit -v/-d, vm.max_map_count, espacio de direcciones) han agotado la memoria del proceso. En instancias de 32 bits, el límite del espacio de direcciones también puede provocarlo.","fix":"Averigüe qué consume la memoria examinando la categoría de asignación de la segunda línea del error (p. ej., 'pga heap', 'koh-kghu sessi'). Revise PGA_AGGREGATE_LIMIT / PGA_AGGREGATE_TARGET. Revise los límites del sistema operativo (ulimit -a; memlock, as, stack para el usuario oracle). Optimice la consulta/el PL/SQL y reduzca el tamaño de los lotes de procesamiento masivo. Si es necesario, contacte con Oracle Support con el archivo de trace.","tags":["pga","process memory","memory","ulimit","sort","plsql"]},{"code":"ORA-12537","title":"TNS:connection closed","severity":"danger","meaning":"La conexión TNS entre cliente y servidor terminó porque el canal de comunicación se cerró de forma inesperada. La sesión se cae mientras se establece la conexión o inmediatamente después.","cause":"El listener traspasó el cliente a un proceso servidor, pero el proceso no pudo crearse o terminó de inmediato: caída de la instancia, límite PROCESSES alcanzado, escasez de recursos del sistema operativo, un firewall/capa intermedia que corta la conexión, o una regla TCP.VALIDNODE_CHECKING/TCP.INVITED_NODES en sqlnet.ora que rechaza al cliente. A veces un firewall entre cliente y servidor corta la conexión inactiva.","fix":"Examine listener.log y sqlnet.log. Revise los límites PROCESSES/SESSIONS: SELECT * FROM v$resource_limit WHERE resource_name IN ('processes','sessions'); Verifique que las listas TCP.INVITED_NODES/TCP.EXCLUDED_NODES del sqlnet.ora del servidor no rechacen la IP del cliente (una incompatibilidad de versión de logon genera ORA-28040, no este error). Aumente el timeout de inactividad en el firewall o envíe keepalives con SQLNET.EXPIRE_TIME. Busque en el alert.log registros de caída de la instancia.","tags":["tns","connection","listener","processes","firewall","sqlnet"]},{"code":"ORA-03135","title":"connection lost contact","severity":"danger","meaning":"Una sesión ya establecida y en funcionamiento perdió el contacto con el servidor. Una consulta de larga duración o una conexión inactiva se cortó porque ambos lados ya no podían comunicarse.","cause":"El proceso servidor terminó de forma inesperada (ORA-00600/07445, kill, límite de recursos); un perfil RESOURCE_LIMIT terminó la sesión; un firewall/balanceador de carga cortó una conexión inactiva; o hubo una caída de red/pérdida de paquetes. Ocurre típicamente con consultas que no responden durante mucho tiempo o con conexiones inactivas.","fix":"Busque en el alert.log y en los archivos de trace relacionados por qué murió el proceso (¿ORA-00600/ORA-07445?). En el lado de la red, aumente el timeout de inactividad del firewall y habilite la detección de conexiones muertas con SQLNET.EXPIRE_TIME en sqlnet.ora. Revise los límites CONNECT_TIME/IDLE_TIME del perfil del usuario. Examine la presión de recursos del servidor (memoria, CPU, PROCESSES).","tags":["connection","sqlnet","idle","firewall","dead connection","network"]},{"code":"ORA-01033","title":"ORACLE initialization or shutdown in progress (inicialización o apagado de ORACLE en curso)","severity":"danger","meaning":"La instancia de base de datos a la que se conecta está en pleno arranque (mount/open) o apagado. En este estado intermedio no se aceptan conexiones normales de usuario.","cause":"La instancia está detenida en NOMOUNT/MOUNT (p. ej., pendiente de media recovery), hay un SHUTDOWN en curso, o la base de datos está colgada antes de llegar a OPEN por un error. Conectarse a una instancia recién arrancada antes de que termine el arranque también devuelve este error.","fix":"Conéctese como SYSDBA y revise el estado: sqlplus / as sysdba; luego SELECT status FROM v$instance; y SELECT open_mode FROM v$database; Intente abrir la instancia: ALTER DATABASE OPEN; Si se requiere recuperación, ejecute RECOVER DATABASE; y después ábrala. Busque en el alert.log el error subyacente que impide la apertura (datafile ausente/corrupto, redo). Si el apagado está colgado, pruebe SHUTDOWN ABORT seguido de STARTUP.","tags":["startup","shutdown","mount","open","recovery","instance"]},{"code":"ORA-00600","title":"internal error code, arguments: [...]","severity":"danger","meaning":"Falló una comprobación de consistencia interna del kernel de Oracle. Es un error interno genérico que indica que el código del kernel encontró una condición inesperada; los argumentos entre corchetes identifican qué ruta de código se activó.","cause":"Puede provocarlo la corrupción de bloques/datos, un bug de software de Oracle no corregido, niveles de parches inconsistentes, problemas de memoria/hardware o casos límite inesperados. El primer argumento (p. ej., [ktbdchk1]) indica con qué capa está relacionado el problema y es clave para el análisis de soporte.","fix":"Este error debe tratarse casi siempre con Oracle Support. Conserve el archivo de trace generado y la entrada del alert.log; busque el primer argumento en My Oracle Support con el ORA-600 Troubleshooter y la 'ORA-600 Lookup Tool'. Aplique el patch/PSU/RU correspondiente. Si se sospecha corrupción, verifique la integridad de los bloques con DBVERIFY/RMAN VALIDATE. En entornos críticos, no experimente en producción sin abrir un SR.","tags":["internal","ora-600","kernel","bug","trace","support"]},{"code":"ORA-07445","title":"exception encountered: core dump","severity":"danger","meaning":"Un proceso servidor de Oracle falló de forma inesperada tras recibir una excepción a nivel del sistema operativo (p. ej., SIGSEGV segmentation fault, access violation) y generó un core dump. A diferencia de ORA-00600, el error lo desencadena una señal del sistema operativo.","cause":"Normalmente es un bug de software de Oracle, corrupción de memoria, bloques de datos/índice corruptos, bibliotecas incompatibles o un fallo de hardware/memoria. El nombre de la función y el tipo de señal (p. ej., SIGSEGV) del mensaje identifican la ruta de código que falló.","fix":"Conserve los archivos de trace e incident generados y el alert.log, y busque en My Oracle Support el nombre de la función + la señal; normalmente se requiere un patch. Ejecute pruebas de hardware/memoria y alinee los niveles de parches del sistema operativo y de Oracle. Si se sospecha corrupción, ejecute RMAN VALIDATE / DBVERIFY. Si se repite, abra un SR y envíe el core dump a Support.","tags":["ora-7445","core dump","sigsegv","kernel","bug","trace"]},{"code":"ORA-01102","title":"cannot mount database in EXCLUSIVE mode","severity":"danger","meaning":"La base de datos no puede montarse en modo EXCLUSIVE porque otra instancia (o archivos residuales de una instancia antigua) parece mantener todavía un bloqueo sobre ella. La instancia se detiene en la fase MOUNT.","cause":"La instancia anterior no se apagó limpiamente y quedan archivos de bloqueo (lk<SID> en $ORACLE_HOME/dbs, o memoria compartida/semáforos residuales); otra instancia ya está asociada a la misma base de datos; o se inició una segunda instancia en una instalación no RAC. Las causas más comunes son procesos/memoria compartida residuales de una instancia anterior o un archivo lk<SID> obsoleto.","fix":"Verifique que no haya realmente otra instancia en ejecución: ps -ef | grep pmon e ipcs -m (segmentos de memoria compartida). Si quedan restos, haga ABORT de la instancia y limpie; si es necesario, elimine los segmentos/semáforos residuales con ipcrm. Elimine el archivo de bloqueo lk<SID> en $ORACLE_HOME/dbs (solo si la instancia está realmente detenida). Después pruebe STARTUP MOUNT. En RAC, revise CLUSTER_DATABASE y el estado de los nodos.","tags":["mount","exclusive","lock","startup","pmon","instance"]},{"code":"ORA-29913","title":"error in executing ODCIEXTTABLEOPEN callout (error al ejecutar el callout ODCIEXTTABLEOPEN)","severity":"caution","meaning":"Se produjo un error al leer/analizar el archivo de origen al acceder a una tabla externa. Oracle falló en la rutina del access driver (ORACLE_LOADER / ORACLE_DATAPUMP) a la que llama para obtener los datos de la tabla externa.","cause":"Normalmente el archivo no está en la ruta a la que apunta el objeto DIRECTORY, el usuario del SO de Oracle no tiene permiso de lectura sobre el archivo/directorio, no hay permiso de escritura para los archivos log/bad, o los parámetros de acceso (delimitador de campo, formato de registro) son incorrectos. Suele ir acompañado de suberrores como ORA-29400 y KUP-04xxx.","fix":"Lea los suberrores (KUP-xxxx) para determinar la causa raíz. Verifique la ruta del DIRECTORY y que el archivo exista: SELECT directory_name, directory_path FROM all_directories; Conceda al usuario del SO de Oracle permiso de lectura sobre el archivo y el directorio, y permiso de escritura sobre el directorio de log/bad. Haga coincidir la sección ACCESS PARAMETERS de la definición de la tabla externa con el formato del archivo de origen. Si es necesario, revise los grants READ/WRITE sobre el DIRECTORY.","tags":["external table","external table","directory","oracle_loader","permission","kup"]}]},"errors/postgres-more3.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"errors":[{"code":"08001","title":"sqlclient_unable_to_establish_sqlconnection","severity":"danger","meaning":"El cliente no pudo establecer una conexión con el servidor PostgreSQL. La conexión falló a nivel de red/dirección, antes de convertirse en una sesión.","cause":"Host/puerto incorrectos; una dirección en la que el servidor no escucha (conexión remota con listen_addresses = 'localhost'); el servidor está caído/inaccesible; un firewall bloquea el puerto (5432 por defecto); o un fallo de resolución DNS. Un pool de conexiones mal configurado también puede provocarlo.","fix":"Verifique que el servidor esté activo y escuchando en la dirección correcta: revise listen_addresses = '*' (o la IP correspondiente) y el ajuste port en postgresql.conf, y reinicie el servidor (listen_addresses y port requieren reinicio; un reload no basta). Pruebe el acceso desde el cliente: psql -h host -p 5432 -U app_user. Abra 5432/tcp en el firewall. Revise los valores de host/puerto/DNS y la cadena de conexión.","tags":["connection","connection","network","listen_addresses","port","firewall"]},{"code":"08004","title":"sqlserver_rejected_establishment_of_sqlconnection","severity":"danger","meaning":"Se llegó al servidor, pero este se negó a establecer la conexión. Hay acceso de red, pero el servidor no aceptó esta conexión por política.","cause":"El servidor PostgreSQL rara vez genera este código; lo notifica sobre todo el driver del cliente o el pool de conexiones. Los códigos reales del servidor para rechazos similares son otros: un rechazo de pg_hba.conf ('no pg_hba.conf entry for host', el método reject) devuelve 28000 (invalid_authorization_specification), el agotamiento de max_connections devuelve 53300 (too_many_connections), y una conexión rechazada durante el arranque/apagado devuelve 57P03.","fix":"Lea en el log del servidor el SQLSTATE real y el motivo del rechazo. Para un rechazo de pg_hba (28000), añada una línea que coincida con la IP del cliente, el usuario y la base de datos (p. ej., host all all 10.0.0.0/24 scram-sha-256) y recargue con SELECT pg_reload_conf();. Ante la saturación de conexiones (53300), compare las conexiones de clientes con max_connections usando SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'client backend';","tags":["connection","pg_hba","rejected","authentication","max_connections","reject"]},{"code":"57P01","title":"admin_shutdown","severity":"danger","meaning":"Su conexión activa se terminó por un comando del administrador. El cliente recibe este error cuando el servidor se está apagando o la sesión se termina manualmente ('terminating connection due to administrator command').","cause":"Un DBA terminó la sesión con pg_terminate_backend(); el servidor se está apagando en modo fast/immediate (el smart shutdown no termina las sesiones, espera a que acaben); o se cortaron las conexiones existentes por un cambio de configuración/mantenimiento planificado. En algunos escenarios de failover, el antiguo primario también corta las conexiones de esta forma.","fix":"Normalmente no es un error permanente; la aplicación debe reconectarse (reintentar). Si es inesperado, investigue en el log del servidor el motivo del apagado/terminación. Para el mantenimiento planificado, implemente en la aplicación la reconexión automática y comprobaciones de salud del pool de conexiones. El log del servidor no muestra quién terminó una sesión; para rastrear las llamadas a pg_terminate_backend, mantenga un registro de auditoría con log_statement (o pgaudit).","tags":["shutdown","terminate","connection","maintenance","failover","retry"]},{"code":"53400","title":"configuration_limit_exceeded","severity":"danger","meaning":"La operación falló porque se superó un límite de configuración. El servidor rechazó la solicitud por un límite definido (p. ej., temp_file_limit, el número de replication slots o de WAL senders).","cause":"Ejemplos típicos: el uso de archivos temporales de una consulta supera temp_file_limit ('temporary file size exceeds temp_file_limit'); todos los replication slots están en uso ('all replication slots are in use', max_replication_slots); se supera el límite max_wal_senders. Nota: el agotamiento de max_prepared_transactions devuelve 53200, y las transacciones preparadas deshabilitadas (valor 0) devuelven 55000.","fix":"El mensaje de error indica qué límite se superó. Para temp_file_limit, optimice la consulta (use EXPLAIN ANALYZE para encontrar los pasos sort/hash que se vuelcan a disco), aumente work_mem a nivel de sesión si es necesario o suba el límite de forma deliberada. Si se agotaron los slots, limpie los slots no utilizados tras verificar sus propietarios: SELECT slot_name, slot_type, active FROM pg_replication_slots; o aumente max_replication_slots / max_wal_senders y reinicie el servidor.","tags":["limit","temp_file_limit","replication slot","max_wal_senders","configuration","resource"]},{"code":"XX001","title":"data_corrupted","severity":"danger","meaning":"PostgreSQL detectó corrupción de datos en una página de tabla/heap. La consulta no puede completarse de forma segura porque el contenido del bloque leído es inconsistente ('invalid page in block').","cause":"Fallo de disco/almacenamiento, corrupción del sistema de archivos, garantías de fsync no respetadas durante un corte de energía repentino, hardware defectuoso (RAM/controladora) o, rara vez, un bug de software. Con los checksums habilitados, la corrupción se detecta antes.","fix":"Verifique primero el hardware/almacenamiento y, si es posible, planifique de inmediato volver a un backup físico (PITR). Si existe un backup válido, restáurelo. Para la recuperación, CON RIESGO PARA LOS DATOS y solo como último recurso, puede poner a cero las páginas dañadas con zero_damaged_pages = on y exportar los datos legibles (esto provoca pérdida de datos). Habilite data_checksums (en un clúster existente con PG12+, pg_checksums --enable con el servidor detenido), ejecute pruebas de RAM y haga backups PITR periódicos.","tags":["corruption","corruption","checksum","page","hardware","pitr"]},{"code":"XX002","title":"index_corrupted","severity":"danger","meaning":"Se detectó corrupción en una estructura de índice. Como los datos a los que apunta el índice son inconsistentes con las filas reales del heap, las consultas que usan el índice pueden devolver resultados erróneos o fallar.","cause":"Fallo de hardware/almacenamiento, orden del B-tree roto por un cambio de collation (regla de ordenación) tras una actualización de glibc/ICU del sistema operativo, o una caída repentina. Las actualizaciones de collation del sistema operativo, en particular, provocan corrupción silenciosa en los índices de texto.","fix":"Reconstruya el índice corrupto: REINDEX INDEX idx_name; o REINDEX TABLE my_table; En tablas grandes use REINDEX INDEX CONCURRENTLY para reducir los bloqueos. Si la causa es la collation, reconstruya todos los índices de texto afectados y verifíquelos con la extensión amcheck (bt_index_check). Si la causa raíz es el hardware, sustituya el almacenamiento.","tags":["index","index","corruption","reindex","collation","amcheck"]},{"code":"25001","title":"active_sql_transaction","severity":"caution","meaning":"Se llamó, con una transacción activa, a un comando que solo puede ejecutarse fuera de una transacción o al inicio de ella. Algunos comandos no se ejecutan dentro de un bloque de transacción.","cause":"Ajustes como SET TRANSACTION ISOLATION LEVEL deben emitirse antes de la primera consulta del bloque ('SET TRANSACTION ISOLATION LEVEL must be called before any query'). De forma similar, algunos comandos se rechazan dentro de un BEGIN/transacción abierta. Suele deberse a que la aplicación desactiva el autocommit e inicia una transacción de forma silenciosa.","fix":"Emita los ajustes de nivel de transacción al principio de la transacción, antes de ejecutar cualquier consulta: BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; ... Compruebe si la biblioteca cliente abre una transacción implícita (ajuste de autocommit). Para los comandos que no pueden ejecutarse dentro de un bloque de transacción, cierre primero la transacción actual con COMMIT/ROLLBACK.","tags":["transaction","transaction","isolation","set transaction","autocommit","begin"]},{"code":"42P16","title":"invalid_table_definition","severity":"caution","meaning":"El comando DDL se rechazó porque la definición de la tabla no es válida. La estructura de tabla/columnas solicitada infringe las reglas de PostgreSQL.","cause":"Situaciones como definir más de una clave primaria ('multiple primary keys for table are not allowed'), definiciones que entran en conflicto con la herencia, restricciones de columnas generated/identity o un cambio a una estructura que ALTER TABLE no puede producir. Habitual en scripts de migración generados automáticamente.","fix":"Lea el motivo concreto en el mensaje de error. Defina una sola PRIMARY KEY; si necesita una clave de varias columnas, use una clave compuesta de la forma PRIMARY KEY (a, b). Revise las reglas de las columnas identity/generated. Revise la definición actual de la tabla con \\d my_table y corrija el script de migración.","tags":["ddl","table","primary key","definition","alter table","migration"]},{"code":"P0001","title":"raise_exception","severity":"caution","meaning":"Una función o trigger PL/pgSQL generó deliberadamente un error con RAISE EXCEPTION. Este error no lo produce el motor de base de datos, sino código de reglas de negocio escrito por usted (o por una biblioteca).","cause":"Cuando la lógica de negocio no supera una validación, el desarrollador llamó a RAISE EXCEPTION 'message' USING ERRCODE = ...;. Por ejemplo, un trigger genera un error personalizado para bloquear una actualización no permitida o para imponer una regla de consistencia.","fix":"El mensaje de error y los campos HINT/DETAIL, si los hay, explican qué rechazó la función; cumpla la regla de negocio en consecuencia. Para encontrar el origen, inspeccione el código de la función/trigger correspondiente (las sentencias RAISE). Si se necesita un SQLSTATE concreto, asigne un código significativo con RAISE ... USING ERRCODE en lugar del genérico P0001, para que la aplicación pueda distinguirlo.","tags":["plpgsql","raise","trigger","function","business rule","exception"]}]},"errors/mysql-more3.json":{"engineId":"mysql","engine":"MySQL","order":3,"errors":[{"code":"1049","title":"Unknown database 'name'","severity":"caution","meaning":"La base de datos (esquema) a la que intenta conectarse o que intenta usar no existe en el servidor. La sentencia USE o el nombre de base de datos predeterminado de la cadena de conexión apuntan a un esquema inexistente.","cause":"La base de datos nunca se creó, su nombre está mal escrito (la distinción entre mayúsculas y minúsculas puede variar según el sistema operativo), se eliminó, o la aplicación se conecta al entorno equivocado (dev/prod). Otra causa habitual es que no se migraron los esquemas al pasar a un nuevo servidor.","fix":"Liste las bases de datos existentes: SHOW DATABASES; Asegúrese de que el nombre esté bien escrito y que coincidan las mayúsculas/minúsculas (el ajuste lower_case_table_names es relevante). Si no existe, créela: CREATE DATABASE database_name CHARACTER SET utf8mb4; o restáurela desde un backup. Verifique el entorno de destino en la configuración de conexión de la aplicación.","tags":["database","database","schema","use","name","connection"]},{"code":"1364","title":"Field 'name' doesn't have a default value (el campo no tiene valor predeterminado)","severity":"caution","meaning":"Se insertó una fila sin valor para una columna definida como NOT NULL sin DEFAULT. En modo SQL estricto, MySQL genera un error porque no puede rellenar automáticamente la columna obligatoria que falta.","cause":"Se omitió en el INSERT una columna obligatoria (NOT NULL, sin DEFAULT) y STRICT_TRANS_TABLES/STRICT_ALL_TABLES está habilitado en sql_mode. Una aplicación escrita para el antiguo modo permisivo encuentra este error al trasladarse a un servidor con el modo estricto habilitado.","fix":"Añada al INSERT un valor para la columna que falta, o defina un DEFAULT razonable en la columna: ALTER TABLE my_table MODIFY my_column ... NOT NULL DEFAULT 'value'; Si la columna realmente es opcional, permita NULL. sql_mode puede relajarse como solución temporal/de compatibilidad, pero se recomienda mantener el modo estricto por la calidad de los datos.","tags":["insert","not null","default","strict mode","sql_mode","column"]},{"code":"1206","title":"The total number of locks exceeds the lock table size (el número total de bloqueos supera el tamaño de la tabla de bloqueos)","severity":"danger","meaning":"InnoDB agotó la memoria asignada a los bloqueos que mantiene (las estructuras de bloqueo dentro del buffer pool). Ocurre cuando una sola transacción acumula demasiados bloqueos de fila/gap, y la transacción se revierte.","cause":"Transacciones únicas muy grandes: UPDATE/DELETE de millones de filas a la vez, una cláusula WHERE sin índice que bloquea toda la tabla, o un innodb_buffer_pool_size demasiado pequeño para la carga. Los grandes trabajos por lotes son el desencadenante clásico.","fix":"Divida el DML grande en lotes pequeños (p. ej., elimine/actualice 10,000 filas cada vez con LIMIT en un bucle y COMMIT tras cada lote). Indexe las columnas del WHERE para reducir el número de filas bloqueadas. Como solución permanente, aumente innodb_buffer_pool_size (las estructuras de bloqueo se asignan desde ahí). Evite transacciones innecesariamente largas.","tags":["innodb","lock","lock","buffer pool","batch","memory"]},{"code":"1236","title":"Got fatal error 1236 from master when reading data from binary log (error fatal 1236 del master al leer el binary log)","severity":"danger","meaning":"Un servidor réplica (slave) recibió un error fatal al leer datos del binary log del source (master), y la replicación se detuvo. La réplica no encuentra en el binlog del source la posición que espera.","cause":"Se eliminaron en el source los archivos de binary log necesarios (binlog_expire_logs_seconds — expire_logs_days antes de 8.0, eliminado en 8.4 — o PURGE BINARY LOGS); un hueco/discrepancia de GTID; una posición de binlog corrupta tras una caída del source; o la réplica se retrasó tanto que solicita un log que ya no existe. 'Could not find first log file name in binary log index file' es el submensaje típico.","fix":"Inspeccione el estado de la replicación: SHOW REPLICA STATUS\\G (8.0.22+; SHOW SLAVE STATUS en versiones anteriores, la sintaxis antigua se eliminó en 8.4). Liste los binlogs disponibles en el source: SHOW BINARY LOGS; Si el log que falta se purgó, debe reconstruir la réplica desde un nuevo backup/posición GTID (mysqldump --source-data — --master-data antes de 8.0.26, un alias obsoleto en 8.0.26+ — o el plugin CLONE). Aumente la retención de binlogs en el source según la carga; si usa GTID, verifique los huecos de GTID.","tags":["replication","replication","binlog","master","gtid","slave"]},{"code":"1129","title":"Host 'host' is blocked because of many connection errors (host bloqueado por demasiados errores de conexión)","severity":"danger","meaning":"MySQL bloqueó un host cliente después de que los errores de conexión consecutivos desde él superaran el umbral (max_connect_errors). El host se incluyó en la lista negra por los handshakes fallidos hasta ese momento.","cause":"Un balanceador de carga/herramienta de monitoreo envía handshakes incompletos o mal formados al puerto TCP; problemas de red cortan las conexiones a mitad; o un cliente mal configurado sigue intentando conexiones fallidas. Cuando el contador de errores llega a max_connect_errors, el host se bloquea.","fix":"Elimine el bloqueo de inmediato: en SQL, TRUNCATE TABLE performance_schema.host_cache; (8.0.23+; FLUSH HOSTS quedó obsoleto en 8.0.23 y se eliminó en 8.4) o desde la línea de comandos, mysqladmin flush-hosts. Para ver los hosts bloqueados: SELECT host, ip, sum_connect_errors FROM performance_schema.host_cache; Corrija la causa raíz: configure la herramienta de health check/monitoreo para que realice un handshake MySQL completo y revise la estabilidad de la red. En entornos sanos puede subir max_connect_errors para aumentar la tolerancia, pero lo importante es resolver los errores de conexión reales.","tags":["connection","host blocked","max_connect_errors","flush hosts","handshake","network"]},{"code":"1194","title":"Table 'name' is marked as crashed and should be repaired (tabla marcada como caída; debe repararse)","severity":"danger","meaning":"Una tabla, normalmente almacenada con el motor MyISAM (también Aria en MariaDB), está marcada como corrupta/caída y no puede usarse hasta repararla. Las consultas que acceden a la tabla fallan con este error.","cause":"La tabla MyISAM no se cerró limpiamente durante una caída repentina del servidor/corte de energía, el disco se llenó, hubo un fallo de hardware o se terminó el proceso mysqld. Como MyISAM no ofrece transacciones ni seguridad ante caídas, esto ocurre con mucha más frecuencia que con InnoDB.","fix":"Repare la tabla: REPAIR TABLE table_name; u offline con myisamchk -r. Haga siempre backup de los archivos de datos antes de reparar. Como solución permanente, pase las tablas a InnoDB: ALTER TABLE my_table ENGINE=InnoDB; lo que las hace seguras ante caídas. Revise el estado del disco y el ajuste myisam_recover_options.","tags":["myisam","crashed","repair","corruption","corruption","innodb"]},{"code":"1615","title":"Prepared statement needs to be re-prepared (la sentencia preparada debe volver a prepararse)","severity":"caution","meaning":"Se rechazó la ejecución de una sentencia preparada del lado del servidor porque dejó de ser válida tras cambiar los metadatos de los que depende (definición de tabla/vista). La sentencia debe prepararse de nuevo.","cause":"DDL (ALTER TABLE) sobre la tabla subyacente después de preparar la sentencia, la tabla recreada, la caché de metadatos llena que expulsa la entrada, o límites de table_definition_cache/table_open_cache demasiado pequeños. Habitual en sistemas con mucho DDL y en algunos escenarios de ORM/pool.","fix":"La mayoría de las bibliotecas cliente lo capturan y vuelven a preparar la sentencia automáticamente; en la aplicación, capture este error y reintente prepare + execute. Si se repite con frecuencia, aumente table_definition_cache y table_open_cache. Evite DDL innecesario durante el tráfico de la aplicación.","tags":["prepared statement","re-prepare","ddl","metadata","cache","retry"]},{"code":"1553","title":"Cannot drop index needed in a foreign key constraint (no se puede eliminar un índice que necesita una restricción de foreign key)","severity":"caution","meaning":"Intenta eliminar un índice que necesita una restricción de foreign key. InnoDB no permite quitar el índice en el que se apoya una FK.","cause":"En InnoDB, toda foreign key requiere un índice en su(s) columna(s) de referencia. Eliminar ese índice con DROP INDEX se rechaza porque dejaría la restricción FK sin soporte. Suele surgir al reorganizar una tabla (limpieza de índices).","fix":"Elimine primero la restricción de foreign key y después el índice: ALTER TABLE my_table DROP FOREIGN KEY fk_name; luego DROP INDEX idx_name ON my_table; Si es necesario, vuelva a crear la FK para que se apoye en otro índice adecuado. Para listar las FK existentes: SELECT constraint_name, table_name FROM information_schema.key_column_usage WHERE referenced_table_name IS NOT NULL;","tags":["foreign key","foreign key","index","drop index","innodb","constraint"]},{"code":"3948","title":"Loading local data is disabled; this must be enabled on both the client and server sides (la carga de datos locales está deshabilitada; debe habilitarse en el cliente y en el servidor)","severity":"caution","meaning":"Intentó cargar datos desde un archivo del lado del cliente con LOAD DATA LOCAL INFILE, pero la función está deshabilitada en el lado del cliente y/o del servidor. Por motivos de seguridad, LOCAL está deshabilitado de forma predeterminada en MySQL 8.","cause":"La variable de sistema local_infile está en OFF en el servidor y/o el driver del cliente no ha habilitado LOCAL INFILE. La función está restringida de forma predeterminada porque puede permitir que un servidor malicioso lea archivos del cliente.","fix":"Si es realmente necesario, habilítelo en ambos lados: en el servidor SET GLOBAL local_infile = 1; en el cliente, habilite el indicador LOCAL INFILE del driver (p. ej., la opción de conexión allowLoadLocalInfile / --local-infile=1). Desde el punto de vista de la seguridad, lo preferible es colocar el archivo en el servidor y usar LOAD DATA INFILE sin LOCAL, o usar INSERT masivos/herramientas de importación. Vuelva a deshabilitar local_infile cuando termine.","tags":["load data","local infile","local_infile","security","import","loading"]},{"code":"1075","title":"Incorrect table definition; there can be only one auto column and it must be defined as a key (definición de tabla incorrecta; solo puede haber una columna automática y debe definirse como clave)","severity":"caution","meaning":"La definición de la tabla no es válida porque se infringieron las reglas de AUTO_INCREMENT: una tabla solo puede tener una columna autoincremental, y esa columna debe formar parte de una clave (PRIMARY KEY o UNIQUE/índice).","cause":"Definir más de una columna AUTO_INCREMENT, o dejar la columna AUTO_INCREMENT fuera de todas las claves (índices). Ocurre durante CREATE TABLE o ALTER TABLE.","fix":"Mantenga una sola columna AUTO_INCREMENT y haga que forme parte de una clave: CREATE TABLE t (id INT AUTO_INCREMENT PRIMARY KEY, ...); Para corregir una tabla existente, añada primero la columna a una clave: ALTER TABLE t ADD PRIMARY KEY (id); y después defina AUTO_INCREMENT. Si necesita más de una columna automática, replantee el diseño; MySQL no lo admite.","tags":["auto_increment","table","primary key","key","ddl","definition"]}]},"errors/mssql-more3.json":{"engineId":"mssql","engine":"SQL Server","order":4,"errors":[{"code":"102","title":"Incorrect syntax near 'token'","severity":"caution","meaning":"El parser de Transact-SQL encontró una sintaxis no válida cerca del token indicado y no pudo compilar la sentencia. La consulta se rechaza antes de ejecutarse.","cause":"Una errata, una coma/paréntesis que falta, una palabra clave reservada (p. ej., USER, ORDER) usada sin corchetes, un literal de cadena sin cerrar o sintaxis incompatible copiada de otro dialecto SQL. Los errores de concatenación en SQL dinámico también son un desencadenante frecuente.","fix":"Inspeccione con atención la zona alrededor del token indicado; el error suele estar en la línea anterior. Ponga las palabras reservadas entre corchetes ([Order]) y compruebe que los paréntesis y las comas estén equilibrados. Si construye SQL dinámico, haga PRINT @sql para ver el texto generado y ejecútelo. Verifique que la sintaxis sea compatible con la versión/nivel de compatibilidad.","tags":["syntax","syntax error","parse","tsql","dynamic sql","keyword"]},{"code":"8114","title":"Error converting data type X to Y","severity":"caution","meaning":"No se pudo convertir un valor de un tipo de datos a otro porque los tipos son incompatibles. A diferencia del error 245, suele tratarse de una conversión de tipo fallida a nivel de parámetro/expresión (p. ej., de varchar a numeric).","cause":"Enviar texto no numérico o mal formado a una columna/parámetro numérico; desbordamiento de escala/precisión; intentar convertir una cadena vacía ('') a numérico; o pasar un argumento del tipo equivocado en una consulta parametrizada. Habitual con las conversiones implícitas que generan los ORM.","fix":"Verifique que los valores convertidos encajen en el tipo de destino. Use TRY_CONVERT / TRY_CAST para una conversión segura; devuelven NULL si fallan, de modo que puede filtrar esas filas con WHERE. Haga coincidir los tipos de los parámetros con los de las columnas. Analice los datos con ISNUMERIC/TRY_CAST para encontrar las filas problemáticas y limpie las cadenas vacías.","tags":["convert","cast","data type","conversion","numeric","try_convert"]},{"code":"1204","title":"The instance of the SQL Server Database Engine cannot obtain a LOCK resource (la instancia de SQL Server Database Engine no puede obtener un recurso LOCK)","severity":"danger","meaning":"SQL Server no pudo asignar la memoria que necesita el lock manager y no puede crear más bloqueos. La operación falla porque el pool de memoria de bloqueos está agotado.","cause":"Demasiados bloqueos de grano fino (fila/página) mantenidos a la vez: DML grande, sin índices o masivo; lock escalation impedida; presión general de memoria en el servidor; o transacciones abiertas de larga duración. En ocasiones el desencadenante es la presión de memoria externa.","fix":"Divida las operaciones grandes en lotes más pequeños para reducir el número de bloqueos concurrentes. Use índices adecuados para reducir el número de filas recorridas/bloqueadas. Revise las configuraciones que deshabilitan la lock escalation: trace flags 1211/1224 y ALTER TABLE ... SET (LOCK_ESCALATION = DISABLE); compruébelo con SELECT name, lock_escalation_desc FROM sys.tables; y DBCC TRACESTATUS(-1); Revise la memoria del servidor y el ajuste max server memory, y alivie la presión de memoria.","tags":["lock","lock memory","memory","lock escalation","batch","resource"]},{"code":"3906","title":"Failed to update database because the database is read-only (no se pudo actualizar la base de datos porque es de solo lectura)","severity":"caution","meaning":"Intentó modificar datos (INSERT/UPDATE/DELETE/DDL) en una base de datos marcada como de solo lectura. SQL Server rechazó la escritura.","cause":"La base de datos se configuró como READ_ONLY; la conexión fue a una réplica secundaria de un Availability Group (destinada a lecturas); la base de datos quedó en solo lectura tras un snapshot o una restauración; o el sistema de archivos es de solo lectura. La causa típica es una aplicación dirigida por error a una réplica secundaria.","fix":"Si realmente debe admitir escritura: ALTER DATABASE mydb SET READ_WRITE; En un Availability Group, dirija la aplicación a la réplica primaria (revise el enrutamiento de conexiones ReadWrite / ApplicationIntent). En un snapshot de solo lectura no se puede escribir; use como destino la base de datos de origen. Verifique también los permisos de los archivos y el sistema de archivos.","tags":["read only","read-only database","availability group","replica","read_write","write"]},{"code":"15023","title":"User already exists in the current database (el usuario ya existe en la base de datos actual)","severity":"caution","meaning":"Al añadir un nuevo usuario a la base de datos, se encontró que ya existe un usuario con el mismo nombre en esa base de datos. También aparece al asignar un login a un usuario existente (usuarios huérfanos).","cause":"Normalmente, cuando una base de datos se restaura desde un backup en otro servidor, los SID de los logins no coinciden con los usuarios de la base de datos (usuarios huérfanos). Intentar añadir el mismo nombre de nuevo con CREATE USER, o intentar configurar manualmente la asignación login-usuario, genera este error.","fix":"Vuelva a asignar el usuario huérfano al login existente: ALTER USER app_user WITH LOGIN = login_name; (recomendado frente al sp_change_users_login legacy). Si el usuario es realmente redundante: DROP USER app_user; Para encontrar huérfanos: SELECT dp.name FROM sys.database_principals dp LEFT JOIN sys.server_principals sp ON dp.sid = sp.sid WHERE sp.sid IS NULL AND dp.type IN ('S','U');","tags":["user","database user","login","orphaned","restore","orphaned user"]},{"code":"18452","title":"Login failed. The login is from an untrusted domain and cannot be used with Windows authentication (error de login; el login procede de un dominio no confiable y no puede usarse con autenticación de Windows)","severity":"danger","meaning":"Se rechazó un intento de login con autenticación de Windows porque procedía de un dominio no confiable (o se usó autenticación de Windows donde se requiere autenticación de SQL). El servidor no confía en esta identidad.","cause":"El cliente se conecta con una identidad de Windows de un dominio/grupo de trabajo en el que SQL Server no confía; los equipos están en dominios distintos/no confiables; la configuración de Kerberos/SPN es incorrecta; o la cadena de conexión usa Integrated Security cuando se espera autenticación de SQL. Un intento de login de SQL con el modo mixto deshabilitado muestra un síntoma similar.","fix":"Si se requiere autenticación de SQL, cambie el servidor a Mixed Mode y use un nombre de usuario/contraseña en la cadena de conexión (Integrated Security=false). Si se requiere autenticación de Windows, verifique la confianza entre dominios y la configuración de DNS/SPN de Kerberos. Asegúrese de que el cliente y el servidor estén en el mismo dominio o en uno de confianza. Acote la causa con el valor 'state' del error log.","tags":["login","authentication","windows","domain","kerberos","mixed mode"]},{"code":"605","title":"Attempt to fetch logical page belongs to a different allocation unit (la página lógica solicitada pertenece a otra allocation unit)","severity":"danger","meaning":"Al leer una página, SQL Server encontró una página perteneciente a otro objeto en lugar del objeto/allocation unit esperado. Es un indicador sólido de corrupción física/lógica grave de la base de datos.","cause":"Enlaces de páginas rotos por un fallo de almacenamiento/disco, un bug de controladora o driver, corrupción de la caché de escritura durante un corte de energía repentino o daños en el sistema de archivos. Un 'transient 605' puede deberse en ocasiones a problemas temporales de I/O, pero los persistentes son corrupción real.","fix":"Ejecute de inmediato DBCC CHECKDB ('mydb') WITH NO_INFOMSGS, ALL_ERRORMSGS para determinar el alcance del daño. Si es posible, restaure desde un backup válido conocido (es preferible un PITR que minimice la pérdida de datos). Pruebe el almacenamiento/hardware y verifique el ajuste PAGE_VERIFY CHECKSUM. Las opciones REPAIR pueden provocar pérdida de datos, así que úselas solo como último recurso y tras hacer un backup; en entornos críticos, recurra al soporte de Microsoft.","tags":["corruption","database corruption","page","allocation unit","dbcc checkdb","storage"]},{"code":"4922","title":"ALTER TABLE ALTER COLUMN failed because one or more objects access this column (falló ALTER TABLE ALTER COLUMN porque uno o más objetos acceden a esta columna)","severity":"caution","meaning":"Intentó cambiar el tipo/propiedad de una columna, pero los objetos que dependen de ella (índice, restricción, columna calculada, estadísticas, vista con schema binding, etc.) lo impidieron. La columna no puede modificarse hasta quitar las dependencias.","cause":"La columna se usa en un índice, una restricción PRIMARY KEY/UNIQUE/CHECK/DEFAULT, una expresión de columna calculada, una vista/función definida WITH SCHEMABINDING o un objeto de estadísticas. Un cambio de tipo/tamaño invalidaría esos objetos, por lo que se rechaza el ALTER.","fix":"Busque primero los objetos dependientes y elimínelos o deshabilítelos temporalmente, modifique la columna y vuelva a crearlos. Consulte sys.sql_expression_dependencies y sys.indexes/sys.default_constraints para ver las dependencias. Orden típico: DROP CONSTRAINT/INDEX -> ALTER COLUMN -> CREATE de nuevo. En vistas con schema binding, quite temporalmente SCHEMABINDING y vuelva a añadirlo.","tags":["alter column","dependency","index","constraint","schemabinding","ddl"]},{"code":"7202","title":"Could not find server 'name' in sys.servers (no se encontró el servidor en sys.servers)","severity":"caution","meaning":"El nombre de servidor referenciado en la consulta mediante un linked server o un nombre de cuatro partes (server.database.schema.object) no se encontró entre los linked servers configurados. SQL Server no pudo resolver el nombre.","cause":"El linked server nunca se definió; el nombre está mal escrito; un linked server que existe en otro entorno (dev/prod) falta en producción; u OPENQUERY/el nombre de cuatro partes apunta a un nombre de servidor incorrecto. Habitual cuando se mueven scripts entre servidores.","fix":"Liste los linked servers definidos: SELECT name FROM sys.servers; o EXEC sp_linkedservers. Añádalo si es necesario: EXEC sp_addlinkedserver @server='SERVER', ...; y configure la asignación de logins con sp_addlinkedsrvlogin. Verifique que el nombre de cuatro partes de la consulta use el nombre correcto del linked server; si solo accede a un objeto local, quite el prefijo de servidor.","tags":["linked server","remote server","sys.servers","openquery","four-part name","configuration"]},{"code":"40501","title":"The service is currently busy. Retry the request after 10 seconds (el servicio está ocupado; reintente la solicitud en 10 segundos)","severity":"danger","meaning":"En Azure SQL Database, el servicio rechazó temporalmente la solicitud por presión de recursos (throttling del motor). El sistema está ocupado y se espera que el cliente reintente en breve.","cause":"Un recurso como la CPU, las conexiones, la velocidad de escritura del log de transacciones o el número de workers alcanza el límite del nivel de servicio seleccionado (DTU/vCore); un pico repentino de carga; o consultas de larga duración/ineficientes que consumen el recurso. El 'reason code' que acompaña al error muestra qué recurso se está limitando.","fix":"Implemente en la aplicación una lógica de reintento con backoff exponencial; es el comportamiento esperado ante errores transitorios. Revise el uso de recursos: sys.dm_db_resource_stats y las métricas del portal de Azure. Optimice las consultas/índices y reduzca la concurrencia innecesaria. Si la saturación es persistente, escale el nivel de servicio (DTU/vCore).","tags":["azure","throttling","busy","retry","resource","dtu"]}]},"errors/mongodb-more3.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"errors":[{"code":"10107 NotWritablePrimary","title":"not master / not writable primary","severity":"danger","meaning":"Se envió una escritura (u otra operación que requiere el primario) a un miembro del replica set que no es el primario en ese momento. Solo el primario acepta escrituras, por lo que la operación se rechazó.","cause":"La aplicación escribe en un secundario; durante un failover (elección), el antiguo primario pasó a secundario y el cliente sigue conectado a él; o la preferencia de lectura/escritura y la información de topología del driver están desactualizadas. La causa típica es que falte el parámetro replicaSet en la cadena de conexión.","fix":"Configure el driver con todos los miembros del replica set y su nombre (replicaSet=... y todos los hosts en la cadena de conexión) para que el driver descubra el primario automáticamente y redirija ante un failover. Use la preferencia de lectura/escritura predeterminada, que dirige las escrituras al primario. Como es un error transitorio, habilite las retryable writes en la aplicación. Verifique qué miembro es PRIMARY con rs.status().","tags":["replica set","primary","failover","write","election","retryable"]},{"code":"91 ShutdownInProgress","title":"The server is in the process of shutting down (el servidor se está apagando)","severity":"danger","meaning":"La operación no pudo completarse porque el servidor (mongod/mongos) se está apagando. Un nodo que se está apagando rechaza las nuevas solicitudes y termina las operaciones en curso.","cause":"Un reinicio/mantenimiento planificado, un apagado previo a un stepdown del primario en un replica set, una señal de terminación enviada por el contenedor/orquestador o la detención del proceso por agotamiento de recursos. Es esperable durante las rolling upgrades.","fix":"Suele ser transitorio; use lógica de reintento en la aplicación y una conexión de replica set para que las solicitudes se dirijan a otro miembro sano. Si es inesperado, revise en los logs de mongod el motivo del apagado (señal, OOM killer, recursos). Durante el mantenimiento escalonado, haga un stepdown limpio del primario antes de apagarlo (rs.stepDown()).","tags":["shutdown","shutting down","maintenance","stepdown","replica set","retry"]},{"code":"189 PrimarySteppedDown","title":"Primary stepped down while operation was in progress (el primario dejó de serlo durante la operación)","severity":"danger","meaning":"El primario dejó de serlo (stepdown) mientras se ejecutaba la operación y ya no acepta escrituras. Por eso se abortó el comando en curso.","cause":"Se desencadenó una elección en el replica set: el primario se degradó manualmente por mantenimiento, perdió el contacto con la mayoría, hubo un cambio de prioridad o una partición de red. Las operaciones en curso en el momento del failover reciben este error.","fix":"Suele ser un error reintentable; con las retryable writes/reads habilitadas en el driver, la operación puede dirigirse automáticamente al nuevo primario. Capture este error en la aplicación y reintente. Si ocurre con frecuencia, investigue la salud del replica set: revise los estados de los miembros, la latencia de red y el retraso del oplog con rs.status(), y reduzca las elecciones innecesarias (ajustes de heartbeat/timeout).","tags":["stepdown","election","failover","primary","replica set","retry"]},{"code":"24 LockTimeout","title":"Operation exceeded time limit waiting for a lock (la operación superó el límite de tiempo esperando un bloqueo)","severity":"caution","meaning":"Una operación superó su límite de tiempo mientras esperaba un bloqueo necesario (p. ej., a nivel de base de datos/colección) y se abortó. No se pudo obtener el bloqueo a tiempo.","cause":"Una operación de larga duración o un comando DDL (p. ej., operaciones administrativas en conflicto sobre una colección) mantiene el bloqueo necesario; escrituras concurrentes intensas; o una operación limitada por maxTimeMS caduca mientras espera un bloqueo. Se ve con operaciones de índices/colecciones en conflicto.","fix":"Busque la operación que mantiene el bloqueo: inspeccione las operaciones activas y en espera con db.currentOp(); termínela con db.killOp() si es necesario. Mueva los comandos administrativos en conflicto (creación de índices, collMod, compact) a horas valle. Mantenga las operaciones cortas y evite transacciones innecesariamente largas. Si es necesario, ajuste maxTimeMS a la carga.","tags":["lock","lock wait","timeout","currentop","ddl","concurrency"]},{"code":"7 HostNotFound","title":"Could not find host","severity":"danger","meaning":"MongoDB no pudo resolver/encontrar un nombre de host al que intentó conectarse. El driver o un miembro del replica set no puede llegar al host especificado, por lo que no se puede establecer la conexión.","cause":"Fallo de resolución DNS, un nombre de host incorrecto/obsoleto en la cadena de conexión, nombres de host de los miembros del replica set (rs.conf) que el cliente no puede resolver, o un problema de red/acceso. En entornos de contenedores, la causa típica son nombres de host internos que no pueden resolverse desde fuera.","fix":"Verifique que el nombre de host sea correcto y resoluble (pruebe el DNS con ping/nslookup). Actualice los hosts de la cadena de conexión. Asegúrese de que los nombres de los miembros del replica set sean resolubles tanto por los clientes como por los miembros; si es necesario, actualice los nombres de host en rs.conf() a FQDN/IP accesibles. Si usa SRV (mongodb+srv), revise los registros DNS SRV.","tags":["host","dns","connection","replica set","network","srv"]},{"code":"175 QueryPlanKilled","title":"Query plan killed during execution","severity":"caution","meaning":"El plan de una consulta en ejecución dejó de ser válido porque cambiaron los datos/la colección subyacentes, y se terminó. La consulta se abortó porque no podía continuar de forma segura.","cause":"Mientras se ejecutaba la consulta (sobre todo con un cursor abierto), se eliminó la colección, se eliminó un índice que usaba o se produjo un renombrado/reconfiguración. El desencadenante típico son cambios subyacentes durante llamadas getMore de larga duración.","fix":"Reinicie la consulta/el cursor (reintente). Asegúrese de que no se ejecute DDL destructivo (drop, dropIndex, rename) sobre la colección o el índice mientras hay consultas en ejecución; programe ese mantenimiento fuera del tráfico de consultas. Acorte las lecturas de larga duración y asegúrese de que los índices necesarios sean permanentes.","tags":["query","cursor","getmore","drop","index","plan"]},{"code":"11602 InterruptedDueToReplStateChange","title":"Operation interrupted due to a replication state change (operación interrumpida por un cambio de estado de la replicación)","severity":"danger","meaning":"La operación se interrumpió porque cambió el estado del replica set (p. ej., cambió el primario). El comando en curso se terminó porque no podía completarse de forma segura en la nueva topología.","cause":"Las operaciones en curso se interrumpen durante una elección, un stepdown del primario, cambios de rol de los miembros o un cambio de configuración. Tiene la misma raíz que 189 PrimarySteppedDown y suele aparecer en el momento del failover.","fix":"Es un error reintentable; con las retryable writes/reads habilitadas en el driver, la operación puede reintentarse contra el nuevo primario. Capture este error en la aplicación y reintente. Si se repite con frecuencia, investigue la causa de las elecciones: inestabilidad de red, timeouts de heartbeat, sobrecarga o retraso del oplog. Supervise la salud del replica set con rs.status().","tags":["replica set","election","failover","interrupted","retry","state change"]},{"code":"136 CappedPositionLost","title":"Capped collection position lost","severity":"caution","meaning":"Un cursor que lee una capped collection (de tamaño fijo), normalmente un tailable cursor, perdió su posición. El documento en el que estaba posicionado ya no existe porque la colección lo sobrescribió cíclicamente.","cause":"Cuando una capped collection está llena, los documentos más antiguos se eliminan y sobrescriben automáticamente; un tailable cursor que sigue una colección en la que se escribe más rápido de lo que puede consumir se queda atrás y su posición se sobrescribe. Se ve en consumidores que siguen estructuras capped como el oplog.","fix":"Reposicione el cursor (abra un nuevo tailable cursor) y reinicie el seguimiento; capture este error en la aplicación y renueve la lectura. Si la causa raíz es un consumidor que no puede seguir el ritmo de escritura: aumente el tamaño de la capped collection/del oplog o acelere el consumidor. Para seguir los cambios de forma fiable, use change streams en lugar de tailable cursors siempre que sea posible.","tags":["capped","tailable cursor","oplog","cursor","position","change stream"]},{"code":"28 PathNotViable","title":"Cannot use the part of the path to traverse the element (no se puede usar esa parte de la ruta para recorrer el elemento)","severity":"caution","meaning":"Una actualización intentó recorrer una ruta de campo con puntos (p. ej., a.b.c), pero una parte de la ruta encontró un tipo que no puede contener subcampos (un valor escalar o un elemento del tipo equivocado). Por eso no se pudo recorrer la ruta.","cause":"Escribir en una ruta anidada como a.b.c cuando el campo 'a' del documento es un escalar (p. ej., número/cadena) en lugar de un objeto/array; o discrepancias de tipo como encontrar un objeto donde se espera un índice de array. Habitual en documentos con un esquema inconsistente (al actualizar rutas profundas con $set).","fix":"Antes de actualizar, asegúrese de que la ruta sea del tipo esperado (objeto/array). Corrija los documentos incompatibles o haga que la actualización dependa de la estructura del documento. Si es necesario, convierta primero el campo al tipo correcto (p. ej., sustituya el valor escalar por un objeto) y después escriba el campo anidado. Considere la validación de esquema para mantener la consistencia del esquema.","tags":["update","path","dotted","type","set","schema"]}]},"errors/redis-more3.json":{"engineId":"redis","engine":"Redis","order":6,"errors":[{"code":"BUSYGROUP Consumer Group name already exists","title":"Consumer group already exists","severity":"caution","meaning":"Intentó crear un consumer group en un stream con XGROUP CREATE, pero ya existe un grupo con el mismo nombre en ese stream. Redis no sobrescribe el grupo existente.","cause":"La aplicación intenta crear el grupo de forma incondicional al arrancar y el grupo quedó de una ejecución anterior; o varias instancias intentan crear el mismo grupo en paralelo. Típico de una lógica de arranque no idempotente.","fix":"Haga idempotente la creación del grupo: capture e ignore el error (un grupo existente no es un problema). Use XGROUP CREATE stream group $ MKSTREAM para crear el stream automáticamente si aún no existe. Ejecute XINFO GROUPS stream para ver los grupos existentes. Si realmente quiere restablecerlo, elimínelo primero con XGROUP DESTROY stream group.","tags":["stream","consumer group","xgroup","busygroup","idempotent","mkstream"]},{"code":"NOGROUP No such key or consumer group","title":"No such key or consumer group in stream","severity":"caution","meaning":"Ejecutó un comando de consumo de streams (XREADGROUP, XACK, XCLAIM, etc.), pero la clave (stream) o el consumer group especificados no existen. No se encontró el grupo/stream sobre el que operar.","cause":"El grupo nunca se creó (se omitió XGROUP CREATE); la clave del stream se eliminó o nunca se escribió; el nombre del grupo/la clave están mal escritos; o el grupo se eliminó con XGROUP DESTROY. Habitual cuando los consumidores arrancan antes de que se cree el grupo.","fix":"Asegúrese primero de que el stream y el grupo existan: XINFO STREAM stream y XINFO GROUPS stream. Si falta el grupo, créelo: XGROUP CREATE stream group $ MKSTREAM (también crea el stream si es necesario). Añada al arranque de la aplicación un paso que cree el grupo (ignorando BUSYGROUP). Verifique que los nombres de la clave y del grupo estén bien escritos.","tags":["stream","consumer group","nogroup","xreadgroup","xgroup","key"]},{"code":"NOTBUSY No scripts in execution right now","title":"No scripts in execution right now","severity":"safe","meaning":"Envió SCRIPT KILL o FUNCTION KILL, pero no hay ningún script/función Lua en ejecución (que pueda terminarse). El comando se rechazó porque no hay nada que terminar.","cause":"El script ya había terminado cuando intentó detenerlo; SCRIPT KILL se envió al nodo equivocado (mientras el script se ejecuta en otra instancia); o la condición BUSY se resolvió por sí sola antes de SCRIPT KILL. Suele ser un problema de sincronización temporal.","fix":"Si realmente no hay ningún script bloqueado, no hay nada que hacer; es una respuesta segura/informativa. Si recibe un error BUSY (un script superó busy-reply-threshold, llamado lua-time-limit antes de Redis 7), pruebe SCRIPT KILL (FUNCTION KILL para funciones) en el nodo correcto donde se ejecuta el script. Si el script ha modificado datos, SCRIPT KILL no funciona; como último recurso puede ser necesario SHUTDOWN NOSAVE. Divida los scripts largos en partes y respete el límite de tiempo.","tags":["lua","script kill","notbusy","busy","script","function"]},{"code":"DENIED Redis is running in protected mode","title":"Redis is running in protected mode","severity":"danger","meaning":"Redis se ejecuta en modo protegido y rechazó una conexión sin contraseña desde el exterior (una dirección que no es loopback). Por seguridad, se bloquea el acceso remoto sin autenticación.","cause":"Redis, enlazado a todas las interfaces (o con un ajuste bind laxo), recibió una conexión de un cliente externo sin contraseña (requirepass) configurada. protected-mode está activado de forma predeterminada y en esta situación bloquea las solicitudes que no son de loopback. Indica un Redis no protegido expuesto por accidente a internet.","fix":"La solución correcta es protegerlo: establezca una contraseña robusta con requirepass (o usuarios ACL) y escuche solo en interfaces de confianza con bind. Si se requiere acceso remoto, protéjalo con contraseña/TLS y restrinja el puerto 6379 con un firewall. protected-mode no solo puede establecerse temporalmente en una red cerrada/segura, pero NUNCA se recomienda en instalaciones expuestas a internet.","tags":["protected mode","security","denied","requirepass","bind","auth"]},{"code":"ERR Can't execute '<cmd>': only (P|S)SUBSCRIBE / (P|S)UNSUBSCRIBE / PING / QUIT / RESET are allowed in this context","title":"Command not allowed in subscribe context (comando no permitido en el contexto de suscripción)","severity":"caution","meaning":"Después de que la conexión entrara en el modo de suscripción Pub/Sub, intentó ejecutar un comando normal (p. ej., GET/SET) que no está permitido en este modo. Una conexión en modo de suscripción solo acepta un conjunto limitado de comandos. En versiones antiguas el mensaje era \"ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context\".","cause":"Enviar comandos de datos por una conexión que ha pasado al modo de suscripción con SUBSCRIBE/PSUBSCRIBE/SSUBSCRIBE. En el protocolo RESP2, una conexión suscrita solo permite (P|S)SUBSCRIBE, (P|S)UNSUBSCRIBE, PING, QUIT y RESET. El error típico es compartir una sola conexión para pub/sub y para el trabajo normal.","fix":"Use una conexión dedicada para Pub/Sub y abra otra para los comandos normales (la mayoría de las bibliotecas cliente lo hacen automáticamente). Envíe UNSUBSCRIBE/PUNSUBSCRIBE para salir del modo de suscripción; después, los comandos normales funcionan. En el protocolo RESP3 (HELLO 3) también se permiten otros comandos en una conexión suscrita; cambie a RESP3 si su driver lo admite.","tags":["pubsub","subscribe","context","resp","connection","hello"]},{"code":"ERR SELECT is not allowed in cluster mode","title":"SELECT is not allowed in cluster mode","severity":"caution","meaning":"En el modo Redis Cluster intentó cambiar a otra base de datos lógica (índice de DB) con SELECT. El modo clúster solo admite la base de datos 0; no se pueden usar varias DB.","cause":"Una aplicación que cambia entre las DB 0-15 con SELECT en Redis standalone sigue enviando el mismo comando tras pasar a Redis Cluster. Como la arquitectura de clúster distribuye los datos por slot, el concepto de varias DB no está soportado.","fix":"Elimine las llamadas a SELECT en modo clúster; solo se usa la DB 0. Si necesita separación lógica, use prefijos de clave (namespaces) (p. ej., app1:key). Si ciertas claves deben procesarse juntas, use un hash tag ({user123}) para que caigan en el mismo slot. Haga que la configuración de la aplicación sea compatible con el clúster.","tags":["cluster","select","database","namespace","hash tag","slot"]},{"code":"ERR wrong number of arguments for command","title":"Wrong number of arguments for command","severity":"caution","meaning":"Se envió un comando de Redis con menos o más argumentos de los esperados. El comando se rechazó sin procesarse porque no cumplía su sintaxis.","cause":"Parámetros que faltan/sobran (p. ej., solo SET key en lugar de SET key value); variables pasadas como vacías/undefined; el comando llamado con un orden de argumentos incorrecto; o una expansión de array/argumentos (spread) defectuosa en la biblioteca cliente. Habitual con comandos construidos dinámicamente.","fix":"Revise la firma correcta del comando (p. ej., SET key value [options]); consulte la referencia oficial de comandos. En el código cliente, verifique que los argumentos (sobre todo los que vienen de arrays) no estén vacíos/ausentes. Si construye comandos a partir de la entrada del usuario, valide antes el número de argumentos. Pruebe el comando manualmente en redis-cli para confirmar el número de argumentos esperado.","tags":["syntax","arguments","argument","command","syntax","cli"]},{"code":"ERR Background save already in progress","title":"Background save already in progress","severity":"caution","meaning":"Intentó iniciar un nuevo guardado en segundo plano (BGSAVE) o una reescritura de AOF, pero ya hay un guardado en segundo plano en curso. Redis no permite un segundo proceso de guardado al mismo tiempo.","cause":"Un nuevo BGSAVE/BGREWRITEAOF lanzado antes de que terminara el anterior; scripts de monitoreo o la aplicación que llaman repetidamente a BGSAVE mientras el fork/guardado tarda mucho en un dataset grande. Un disco lento o un archivo RDB grande alargan el proceso.","fix":"Espere a que termine el guardado en curso; revise su estado con los campos rdb_bgsave_in_progress y aof_rewrite_in_progress de INFO persistence y la marca de tiempo de LASTSAVE. Haga condicional la lógica de la aplicación/automatización para que no lance un nuevo BGSAVE antes de que termine el guardado anterior. Si los guardados tardan demasiado, investigue la velocidad del disco y el tamaño del RDB.","tags":["bgsave","persistence","rdb","aof","background save","fork"]},{"code":"ERR bit offset is not an integer or out of range","title":"Bit offset is not an integer or out of range (el offset de bit no es un entero o está fuera de rango)","severity":"caution","meaning":"El offset de bit indicado a una operación de bits como SETBIT/GETBIT/BITCOUNT no es válido: no es un entero o está fuera del rango permitido. Redis no puede realizar una operación de bits en ese offset.","cause":"Pasar un valor no numérico como offset; usar un offset negativo; o indicar un offset muy grande que supera el límite superior permitido (una cadena de bits puede tener hasta 512 MB, es decir, unos 2^32 bits). Surge de errores de cálculo en estructuras de contadores/indicadores basadas en bitmaps.","fix":"Asegúrese de que el offset sea un entero no negativo dentro del rango permitido (de 0 a ~2^32-1; las cadenas tienen como máximo 512 MB). Valide los cálculos de offset (p. ej., los derivados de IDs de usuario) frente a desbordamientos/valores negativos. Si se necesitan offsets muy grandes, diseñe los datos para repartirlos entre varias claves (sharding).","tags":["bitmap","setbit","offset","integer","range","bit"]}]},"errors/elasticsearch-more3.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"errors":[{"code":"node_not_connected_exception","title":"Node is not connected","severity":"danger","meaning":"Un nodo no está conectado en este momento a otro nodo con el que necesita comunicarse. La solicitud no pudo reenviarse al nodo de destino porque se perdió la conexión interna del clúster (capa de transporte).","cause":"Caída de red o pérdida de paquetes, el nodo de destino que falla/se reinicia, un bloqueo de firewall/puerto (puerto de transporte 9300 por defecto), el nodo que deja de responder temporalmente por pausas de GC, o un problema de resolución DNS/de host. Puede aparecer de forma transitoria durante los reinicios escalonados.","fix":"Inspeccione la salud del clúster y de los nodos: use GET _cluster/health y GET _cat/nodes?v para ver qué nodo falta. Verifique el acceso de red entre nodos y las reglas de firewall para el puerto de transporte (9300). Revise en los logs del nodo afectado caídas, pausas de GC u OOM. Revise la estabilidad de la red y la configuración de discovery/seed hosts.","tags":["transport","node","connection","network","9300","cluster"]},{"code":"receive_timeout_transport_exception","title":"Request timed out waiting for a response from another node (timeout esperando la respuesta de otro nodo)","severity":"danger","meaning":"Un nodo envió una solicitud a otro nodo, pero la respuesta no llegó en el tiempo configurado y la solicitud excedió el timeout. La llamada entre nodos (transport) no pudo completarse.","cause":"El nodo de destino con carga elevada, pausas largas de GC, disco lento/presión de CPU, latencia de red/pérdida de paquetes, o el nodo de destino demasiado ocupado para responder. Habitual durante consultas pesadas, operaciones bulk grandes y el rebalanceo de shards.","fix":"Inspeccione el uso de recursos en los nodos afectados: revise el heap de la JVM, el GC y la saturación de thread pools con GET _nodes/stats. Revise las tareas rechazadas/en cola con GET _cat/thread_pool?v. Mida la latencia de red. Reduzca la carga (tamaños de consulta/bulk más pequeños) y revise el heap y los recursos de hardware. Si persiste, añada nodos para repartir la carga.","tags":["transport","timeout","gc","load","network","thread pool"]},{"code":"routing_missing_exception","title":"Routing value is required for this document (este documento requiere un valor de routing)","severity":"caution","meaning":"Se requiere un valor de routing para acceder al documento/indexarlo, pero no se proporcionó. Sin routing, Elasticsearch no pudo determinar a qué shard pertenece el documento.","cause":"El mapping del índice define _routing required=true; se usa una relación parent-join (padre/hijo) y los documentos hijos requieren routing; o se omitió el parámetro routing en una solicitud index/get/update/delete. La causa típica es leer documentos escritos con routing personalizado sin indicar el routing.","fix":"Especifique explícitamente el valor de routing en la solicitud (p. ej., ?routing=value en las llamadas index/get/update/delete). Con parent-join, pase el id del padre como routing de los documentos hijos. Use para lecturas/actualizaciones/eliminaciones el mismo routing que se usó al escribir el documento. Revise el requisito de _routing (required) en el mapping.","tags":["routing","shard","parent-child","join","mapping","index"]},{"code":"too_many_buckets_exception","title":"Trying to create too many buckets","severity":"caution","meaning":"Una consulta de agregación intentó producir más buckets que el límite superior permitido (search.max_buckets, 65,536 por defecto). La consulta se rechazó para proteger la memoria.","cause":"Agregaciones como terms/date_histogram ejecutadas sobre campos de alta cardinalidad con un size muy grande o un intervalo muy fino; agregaciones anidadas que multiplican el número de buckets. El límite se aplica porque crear buckets en exceso puede agotar la memoria del nodo.","fix":"Reduzca el número de buckets producidos: baje el size de las agregaciones terms, amplíe el intervalo de date_histogram, elimine las agregaciones anidadas innecesarias. Si se necesitan muchos resultados, recórralos por páginas con una composite aggregation. Subir el límite debe ser el último recurso y conlleva riesgo de memoria: search.max_buckets puede cambiarse con PUT _cluster/settings, pero hágalo con cuidado.","tags":["aggregation","bucket","max_buckets","terms","composite","memory"]},{"code":"shard_lock_obtain_failed_exception","title":"Obtaining shard lock timed out","severity":"danger","meaning":"Elasticsearch no pudo obtener dentro del timeout el bloqueo de directorio local (shard lock) necesario para operar sobre un shard. El shard no pudo iniciarse ni reasignarse.","cause":"Una copia anterior del mismo shard (tras un cierre/reubicación) aún no ha liberado el bloqueo; I/O lenta/colgada; problemas de disco; o estado obsoleto del shard que no se limpió tras un reinicio rápido del nodo. Suele ser transitorio, pero si persiste el shard queda UNASSIGNED.","fix":"Revise por qué el shard no está asignado: GET _cluster/allocation/explain. En la mayoría de los casos el bloqueo se libera en breve y el clúster reintenta por sí solo; si es necesario, fuerce la reasignación con POST _cluster/reroute?retry_failed=true. Si persiste, examine los logs del nodo afectado en busca de errores de I/O/disco y verifique el estado del disco. Un reinicio limpio del nodo problemático puede liberar el bloqueo.","tags":["shard","lock","unassigned","allocation","io","reroute"]},{"code":"parsing_exception","title":"Failed to parse the search request / query DSL (no se pudo analizar la solicitud de búsqueda / el query DSL)","severity":"caution","meaning":"No se pudo analizar el cuerpo de la consulta enviado (query DSL). La consulta no pudo ejecutarse porque la estructura JSON de la solicitud o la sintaxis de la consulta no coinciden con el formato que espera Elasticsearch.","cause":"JSON no válido, claves de consulta mal colocadas/desconocidas, un tipo de consulta (p. ej., bool, range) usado con una estructura incorrecta, un nombre de campo eliminado/cambiado entre versiones, o un corchete de más/de menos. Habitual con consultas escritas a mano o construidas dinámicamente.","fix":"Revise el cuerpo de la consulta usando la indicación de línea/posición del mensaje de error; verifique que el JSON sea válido. Compare la estructura del query DSL con la referencia oficial (ubicación de bool/must/filter, etc.). Actualice la sintaxis eliminada en las actualizaciones de versión. Simplifique la consulta paso a paso para aislar la parte defectuosa; use el query builder de la biblioteca cliente siempre que sea posible.","tags":["query dsl","parse","json","syntax","search","bool"]},{"code":"max_bytes_length_exceeded_exception","title":"Document contains at least one immense term that is too long (el documento contiene al menos un término enorme demasiado largo)","severity":"caution","meaning":"En un documento que se está indexando, un término (sobre todo el valor de un campo keyword/no analizado) superó la longitud máxima permitida en bytes (32766 bytes). Lucene no puede indexar un término tan largo.","cause":"Un valor de texto muy largo escrito (completo) en un campo keyword; p. ej., todo el cuerpo de un documento, datos base64 o una URL/identificador largo colocado en un campo keyword. Como los campos keyword no se analizan, el valor se indexa como un único término y supera el límite.","fix":"Use un campo text (analizado) para textos largos; keyword es solo para valores cortos que requieren coincidencia exacta. Para limitar el valor, use ignore_above en el mapping (los valores más largos no se indexan, pero siguen almacenándose). Si no se necesita, establezca index:false en los campos muy largos. Reevalúe el diseño del campo (text frente a keyword) según el tipo de datos.","tags":["keyword","term","ignore_above","lucene","mapping","length"]},{"code":"illegal_argument_exception_total_fields","title":"Limit of total fields has been exceeded","severity":"caution","meaning":"El número total de campos del mapping de un índice superó el límite superior permitido (index.mapping.total_fields.limit, 1000 por defecto). La indexación falla porque no se pueden añadir campos nuevos.","cause":"Normalmente es una explosión de mapping: con el dynamic mapping habilitado, los documentos JSON no estructurados/con claves variables (p. ej., objetos cuyas claves proceden de datos de usuario) añaden cada campo nuevo al mapping. Con el tiempo el número de campos crece rápidamente y alcanza el límite.","fix":"Para corregir la causa raíz, modele los datos con claves variables con un campo flattened o como un array de pares clave-valor en lugar de objetos; restrinja el dynamic mapping (dynamic: false o strict). Si realmente necesita más campos, suba el límite con cuidado: PUT idx/_settings {\"index.mapping.total_fields.limit\": 2000}. Vigile la explosión de mapping revisando el número de campos con GET idx/_mapping.","tags":["mapping","total_fields","dynamic mapping","flattened","limit","field"]},{"code":"verification_exception","title":"verification_exception: Found N problem(s) — Unknown column / Unknown index (ES|QL, SQL, EQL) (fallo de verificación: columna o índice desconocidos)","severity":"caution","meaning":"La consulta ES|QL (_query), SQL (_sql) o EQL se analizó sintácticamente, pero no superó la verificación semántica: no se encontró el índice, campo (columna) o función referenciados, o los tipos son incompatibles. El texto del error indica la posición de línea/columna problemática (p. ej., \"line 1:18: Unknown column [field]\"). Un fallo de verificación de un repositorio de snapshots no es este código; es repository_verification_exception.","cause":"Un nombre de campo mal escrito o que no está en el mapping; un patrón de índice que no existe o no coincide con ningún índice; una operación no admitida sobre un campo text (p. ej., agrupar por text en lugar de por su subcampo keyword); una comparación entre tipos incompatibles o una función desconocida.","fix":"Corrija la consulta usando la información línea:columna del mensaje de error. Verifique los nombres y tipos de campo: GET idx/_mapping (para SQL, DESCRIBE idx / SHOW COLUMNS). Revise el patrón de índice con GET _cat/indices. Use el subcampo keyword (field.keyword) para agrupar/ordenar; ante discrepancias de tipo, aplique una conversión explícita (ES|QL: TO_STRING, TO_INTEGER, etc.).","tags":["esql","sql","eql","verification","unknown column","query"]}]},"errors/oracle-more4.json":{"engineId":"oracle","engine":"Oracle","order":1,"errors":[{"code":"ORA-19804","title":"cannot reclaim N bytes disk space from M bytes limit (no se pueden recuperar N bytes de espacio en disco del límite de M bytes)","severity":"danger","meaning":"Oracle intentó hacer sitio en la Fast Recovery Area (FRA) para un nuevo archivo (log archivado, backup, flashback log), pero no pudo recuperar el espacio necesario porque se alcanzó el límite lógico definido por DB_RECOVERY_FILE_DEST_SIZE.","cause":"El tamaño de la FRA (DB_RECOVERY_FILE_DEST_SIZE) es un límite superior independiente del disco físico, y los logs archivados, los backups de RMAN o los flashback logs lo han llenado. La causa raíz suele ser que los logs archivados no se respaldan y eliminan con regularidad, o que no se purgan los backups obsoletos.","fix":"Revise el uso de la FRA: SELECT * FROM v$recovery_area_usage; y SELECT * FROM v$recovery_file_dest; Haga backup de los logs archivados a un destino fuera de la FRA y elimínelos (RMAN: BACKUP ARCHIVELOG ALL FORMAT '/backup/arc_%U' DELETE INPUT;) o purgue los backups que ya no se necesitan (RMAN: DELETE OBSOLETE;). Con Data Guard, no elimine logs archivados que aún no se hayan aplicado en el standby. Como solución permanente, aumente DB_RECOVERY_FILE_DEST_SIZE si hay espacio en disco. Si no se necesita Flashback Database, considere deshabilitarlo.","tags":["fra","recovery area","archived log","rman","db_recovery_file_dest_size","disk"]},{"code":"ORA-00494","title":"enqueue held for too long by 'inst N, osid M' (enqueue retenido demasiado tiempo)","severity":"danger","meaning":"Un proceso mantuvo un enqueue de un recurso crítico (p. ej., CF - control file enqueue) mucho más tiempo del permitido, y Oracle terminó ese proceso para evitar que el sistema se colgara. Aparece en el alert.log junto con una entrada 'killing enqueue blocker'.","cause":"Un proceso en segundo plano o de usuario mantuvo un enqueue importante más allá del tiempo permitido por un cuello de botella de I/O, una fuerte presión de CPU o un bloqueo del SO (swapping, discos lentos); para el enqueue del control file (CF) este tiempo lo fija _controlfile_enqueue_timeout (900 s por defecto). Los parámetros ocultos (con guion bajo) solo deben cambiarse bajo la guía de Oracle Support. Suele desencadenarse por la latencia del almacenamiento o por procesos lentos como LGWR/CKPT.","fix":"Revise el alert.log y el archivo de trace del proceso terminado para determinar qué enqueue (normalmente el del control file) y qué proceso en segundo plano son responsables. La causa real suele ser la latencia de almacenamiento/I/O: revise los tiempos de respuesta del disco, el multipath y el estado de la controladora. Reduzca la presión de CPU/memoria y el uso de swap. Si se repite, comparta el análisis del trace con Oracle Support.","tags":["enqueue","control file","i-o","storage","hang","background process"]},{"code":"ORA-04021","title":"timeout occurred while waiting to lock object (timeout al esperar el bloqueo de un objeto)","severity":"caution","meaning":"Se produjo un timeout al esperar para obtener un library cache lock/pin sobre un objeto (package, procedimiento, tipo, etc.). Suele ocurrir al intentar compilar o modificar un objeto PL/SQL mientras otra sesión lo está ejecutando o bloqueando.","cause":"El objeto que se quiere modificar/compilar se está ejecutando activamente en otra sesión o está bloqueado por otro DDL, por lo que no se puede obtener el library cache lock exclusivo. Una llamada a un procedimiento de larga duración o una sesión colgada pueden estar manteniendo el bloqueo.","fix":"Busque la sesión que bloquea el objeto: SELECT * FROM dba_kgllock WHERE kgllkhdl IN (SELECT p1raw FROM v$session WHERE event LIKE 'library cache%'); o identifique la sesión que usa el objeto con v$access y v$session. Reintente el DDL/la compilación en una ventana de mantenimiento con poca carga. Si es necesario, termine la sesión de forma segura. Detener brevemente la aplicación facilita obtener el bloqueo.","tags":["library cache","lock","plsql","ddl","compile","timeout"]},{"code":"ORA-04061","title":"existing state of package/procedure has been invalidated (se invalidó el estado existente del package/procedimiento)","severity":"caution","meaning":"El estado existente de un objeto PL/SQL llamado (package, procedimiento, función) se invalidó porque cambió un objeto del que depende. Es una variante de la condición de pérdida de estado que acompaña a ORA-04068; el estado del package que mantiene la sesión ya no es válido.","cause":"Mientras una sesión usaba un package, se aplicó DDL a ese package o a sus dependencias (cambio de estructura de tabla, recompilación, cambio de grants). Como resultado, se invalidó el estado del package existente en la sesión. Se ve en entornos con despliegues frecuentes cuando se hacen cambios de esquema con la aplicación en ejecución.","fix":"La misma llamada suele funcionar en el segundo intento (ya que el estado se ha restablecido); capture este error en la aplicación y reintente de forma segura. Haga los cambios de esquema con poco tráfico de aplicación/en una ventana de mantenimiento. Compile en bloque los objetos no válidos: EXEC DBMS_UTILITY.COMPILE_SCHEMA('APP_SCHEMA'); Los diseños que minimizan el estado de los packages (variables globales) reducen este efecto.","tags":["plsql","package","invalidate","ddl","state","recompile"]},{"code":"ORA-01466","title":"unable to read data - table definition has changed (no se pueden leer los datos; la definición de la tabla ha cambiado)","severity":"caution","meaning":"Una consulta que lee datos de un momento pasado (Flashback Query, AS OF o lectura consistente) falló porque la definición de la tabla (DDL) cambió entre el momento pasado consultado y ahora. Oracle no puede leer de forma segura los datos de ese momento con la definición actual.","cause":"Se aplicó un DDL (añadir/eliminar una columna, cambio de tipo) a la tabla después de la marca de tiempo a la que apunta la consulta Flashback/AS OF. Además, las diferencias de reloj entre cliente y servidor o las consultas de un pasado muy reciente (unos segundos) pueden generar este error por la resolución SCN/tiempo.","fix":"Ejecute la consulta flashback contra un momento/SCN anterior al cambio DDL de la tabla. Siempre que sea posible, use un SCN exacto en lugar de una marca de tiempo (AS OF SCN). Para consultas de un pasado muy reciente, retroceda unos segundos. Si se necesita acceso histórico permanente, considere un diseño de versionado o una tabla de archivo en lugar de DDL.","tags":["flashback","as of","ddl","consistent read","scn","query"]},{"code":"ORA-08176","title":"consistent read failure; rollback data not available (fallo de lectura consistente; datos de rollback no disponibles)","severity":"danger","meaning":"Al construir una imagen de lectura consistente, una consulta encontró bloques modificados por una operación que no genera undo; como no existen datos de undo/rollback para esos bloques, no se pudo construir la imagen consistente. A diferencia de ORA-01555, el undo nunca se generó, en lugar de haberse sobrescrito. (El error de aislamiento serializable es un código distinto: ORA-08177.)","cause":"Una operación que no genera undo se ejecuta sobre el mismo objeto mientras la consulta está en curso: cargas direct-path (INSERT /*+ APPEND */, SQL*Loader DIRECT=TRUE), CREATE INDEX / reconstrucción de índices o transacciones discretas.","fix":"Vuelva a ejecutar la consulta cuando haya terminado la carga direct-path o la operación de índice (reintente). No programe informes largos y trabajos de carga direct-path / mantenimiento de índices sobre el mismo objeto en la misma franja horaria. Si se repite, identifique el trabajo en conflicto con el alert log y ASH y separe las programaciones.","tags":["undo","consistent read","direct path","append","create index","retry"]},{"code":"ORA-00020","title":"maximum number of processes (N) exceeded (se superó el número máximo de procesos (N))","severity":"danger","meaning":"El número de procesos de la instancia alcanzó el límite superior definido por el parámetro de inicialización PROCESSES y no se puede crear ninguna conexión/proceso nuevo. Las nuevas sesiones no pueden conectarse; incluso puede que un administrador no logre conectarse.","cause":"Un pool de conexiones de la aplicación sobredimensionado, conexiones que nunca se cierran (fugas), más usuarios concurrentes de lo previsto o un parámetro PROCESSES demasiado bajo provocan que se supere el límite. A veces también consumen procesos jobs en segundo plano colgados o procesos de parallel query.","fix":"Puede conectarse con sqlplus -prelim / as sysdba, pero esta conexión no puede ejecutar SQL (no se puede consultar v$session); solo sirve para oradebug (p. ej., hanganalyze) y SHUTDOWN ABORT. Para liberar huecos, termine con cuidado a nivel de SO los procesos servidor inactivos de la aplicación (ps -ef | grep 'LOCAL=NO'); después, con una conexión SYSDBA normal, localice las sesiones con fugas mediante v$session/v$process y ciérrelas con ALTER SYSTEM KILL SESSION. Como solución permanente, aumente PROCESSES y reinicie la instancia (si SESSIONS está establecido explícitamente en el spfile, no se recalcula; actualícelo también). En el lado de la aplicación, corrija el máximo del pool de conexiones y la limpieza de conexiones inactivas.","tags":["processes","process","connection","limit","pool","init parameter"]},{"code":"ORA-01113","title":"file N needs media recovery","severity":"danger","meaning":"Se está poniendo online un datafile o abriendo la base de datos, pero el archivo es inconsistente; debe aplicarse primero media recovery antes de continuar. El archivo está en un SCN más antiguo/inconsistente del que espera el control file.","cause":"El datafile se restauró desde un backup antiguo, no se aplicó redo mientras estaba offline, o el archivo no está al día tras una caída de la instancia/del almacenamiento. Normalmente se debe a omitir el paso de recover tras una restauración o a que un archivo permaneció offline mucho tiempo.","fix":"Recupere el archivo o la base de datos: RECOVER DATAFILE N; o RECOVER DATABASE; luego ALTER DATABASE DATAFILE N ONLINE; o ALTER DATABASE OPEN; Asegúrese de que los logs archivados necesarios estén accesibles. Con RMAN: tras un RESTORE si es necesario, ejecute RECOVER DATAFILE N;. Siga el alert.log para ver qué logs se solicitan.","tags":["media recovery","datafile","redo","restore","recover","scn"]},{"code":"ORA-02393","title":"exceeded call limit on CPU usage","severity":"caution","meaning":"Se superó el tiempo de CPU permitido para una sola llamada (CPU_PER_CALL), definido en el perfil de recursos asignado a la sesión, y Oracle terminó esa llamada. Es una salvaguarda de limitación de recursos.","cause":"Hay un límite CPU_PER_CALL definido en el perfil del usuario y RESOURCE_LIMIT=TRUE; una sola llamada SQL/PLSQL superó ese umbral de CPU. Suele desencadenarlo una consulta ineficiente/sin control que consume mucha más CPU de lo esperado.","fix":"Optimice la consulta (índice adecuado, plan correcto, eliminación de full table scans innecesarios) para que el consumo de CPU quede por debajo del límite. Si el requisito de negocio es legítimo, aumente CPU_PER_CALL en el perfil del usuario correspondiente o establézcalo en UNLIMITED: ALTER PROFILE ... LIMIT CPU_PER_CALL ...; Considere gestionar los recursos de forma más flexible con Database Resource Manager en lugar de perfiles.","tags":["profile","cpu_per_call","resource_limit","resource","query","optimization"]},{"code":"ORA-12801","title":"error signaled in parallel query server Pnnn (error en el servidor de parallel query Pnnn)","severity":"danger","meaning":"En una consulta ejecutada en paralelo, uno de los servidores de ejecución paralela (parallel query slaves) encontró un error, y este se transmitió al query coordinator. ORA-12801 es un envoltorio; el error real está en la segunda línea que le sigue.","cause":"Un proceso slave encontró un error real durante una operación en paralelo: tablespace TEMP insuficiente (ORA-01652), agotamiento de memoria PGA (ORA-04030), corrupción de datos, límites de procesos paralelos u otro error SQL subyacente. ORA-12801 solo indica que ocurrió en un contexto paralelo.","fix":"Céntrese en el código de error real que sigue inmediatamente a ORA-12801 y resuélvalo (p. ej., ampliar TEMP, ajuste de la PGA, reparar un bloque corrupto). Si es necesario, aísle el error real ejecutando la consulta en serie, quitando los hints PARALLEL o reduciendo el grado de paralelismo. Revise PARALLEL_MAX_SERVERS y el uso de recursos.","tags":["parallel","parallel query","slave","temp","pga","wrapper"]}]},"errors/postgres-more4.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"errors":[{"code":"23P01","title":"exclusion_violation","severity":"danger","meaning":"Se infringió una restricción EXCLUDE (de exclusión). Este tipo de restricción impide que dos filas entren en conflicto según operadores concretos (p. ej., solapamiento de rangos &&); la fila insertada/actualizada se rechazó porque entra en conflicto con una fila existente.","cause":"Normalmente hay definida una restricción EXCLUDE basada en GiST (p. ej., que prohíbe solapamientos temporales con tstzrange && en reservas de citas/salas) y la nueva fila tiene un rango/valor que se solapa con una fila existente. También puede ocurrir cuando dos transacciones concurrentes insertan rangos solapados.","fix":"Compruebe el solapamiento con una consulta antes de insertar, o capture el error en la aplicación y muestre al usuario un mensaje significativo. Consulte el rango/condición correspondiente para encontrar la fila existente en conflicto. Si la regla de negocio permite solapamientos, relaje la restricción; en caso contrario, corrija la entrada. En situaciones de concurrencia, reintente la transacción de forma segura para serializarla.","tags":["exclude","constraint violation","gist","range","overlap","constraint"]},{"code":"23001","title":"restrict_violation","severity":"danger","meaning":"Código restrict_violation del estándar SQL. Importante: PostgreSQL no informa la infracción de una foreign key definida ON DELETE/ON UPDATE RESTRICT o NO ACTION con este código, sino con 23503 (foreign_key_violation); en la práctica no verá este código en el núcleo de PostgreSQL.","cause":"Cuando se elimina una fila padre con filas hijas dependientes o se cambia su clave, PostgreSQL devuelve 23503. 23001 suele aparecer en código de aplicación portado desde otra base de datos o como error personalizado de una extensión. A diferencia de NO ACTION, RESTRICT no puede diferir la comprobación hasta el final de la transacción y falla de inmediato.","fix":"Elimine/actualice primero las filas hijas dependientes o muévalas a otro padre; después procese la fila padre. Si la regla de negocio lo permite, considere redefinir la foreign key con ON DELETE CASCADE o SET NULL. En eliminaciones masivas, respete el orden de eliminación (primero el hijo, después el padre).","tags":["foreign key","restrict","fk","delete","reference","constraint"]},{"code":"42P17","title":"invalid_object_definition","severity":"danger","meaning":"La definición de un objeto (función, collation, trigger, regla, etc.) no es válida. PostgreSQL indica que la definición dada es internamente inconsistente o inaceptable; el objeto no puede crearse/modificarse tal como está escrito.","cause":"Causas comunes: combinaciones de atributos contradictorias en una función (p. ej., marcada IMMUTABLE pero con comportamiento volátil), una definición de collation/juego de caracteres no válida o una definición de trigger/regla inadecuada. Las opciones inconsistentes en row-level security (RLS) y en algunos DDL también pueden provocarlo.","fix":"Lea los detalles del error (DETAIL/HINT) para determinar qué parte de la definición se rechazó. Alinee las etiquetas de volatilidad de la función con su comportamiento real (VOLATILE/STABLE/IMMUTABLE). Verifique que el nombre de la collation exista en pg_collation. Corrija la definición según la sintaxis/reglas documentadas y vuelva a ejecutarla.","tags":["function","collation","definition","volatility","ddl","object"]},{"code":"42710","title":"duplicate_object","severity":"caution","meaning":"El objeto que se está creando (rol, tipo, restricción, extensión, esquema, nombre de índice, etc.) ya existe. PostgreSQL no permite un segundo objeto con el mismo nombre/definición.","cause":"Un comando como CREATE ROLE, CREATE TYPE, CREATE EXTENSION o ADD CONSTRAINT se ejecutó cuando ya existía un objeto con el mismo nombre. Suele deberse a un script ejecutado dos veces, migraciones no idempotentes o dos sesiones concurrentes que intentan crear el mismo objeto.","fix":"Compruebe si el objeto existe (pg_roles para roles, pg_type para tipos, pg_extension para extensiones). Escriba las migraciones de forma idempotente: use CREATE ... IF NOT EXISTS donde esté disponible, o ejecute primero DROP ... IF EXISTS. Ante un conflicto de restricciones, renombre la restricción existente o elimínela y vuelva a crearla.","tags":["duplicate","role","type","extension","constraint","migration"]},{"code":"22008","title":"datetime_field_overflow","severity":"caution","meaning":"Al calcular o analizar un valor de fecha/hora, uno de los campos quedó fuera de su rango válido. Por ejemplo, se produjo un componente imposible como el mes 13 o el día 32, o un resultado fuera del rango de tiempo admitido.","cause":"Una entrada de fecha no válida (p. ej., '2026-02-30'), un componente fuera de rango o una suma de INTERVAL que supera el rango de fechas admitido. La causa típica es analizar con un formato/locale incorrecto o insertar entradas de usuario sin validar.","fix":"Valide las entradas de fecha en la aplicación antes de insertarlas y envíelas en el formato correcto (preferiblemente ISO 8601: YYYY-MM-DD). Al analizar con TO_DATE/TO_TIMESTAMP, haga coincidir la máscara de formato con la entrada. Mantenga los resultados aritméticos dentro del rango de fechas admitido; someta los valores sospechosos a comprobaciones de límites.","tags":["date","time","overflow","interval","validation","format"]},{"code":"22026","title":"string_data_length_mismatch","severity":"caution","meaning":"La longitud de una cadena de bits no coincide con la longitud fija que exige la operación. En PostgreSQL aparece sobre todo cuando se escribe una cadena de bits de otra longitud en un tipo bit(n) de longitud fija, o cuando se aplica AND/OR/XOR bit a bit a cadenas de bits de distinta longitud.","cause":"Escribir una cadena de bits más corta/larga que n en una columna bit(n) (p. ej., B'101' en una columna bit(4): 'bit string length 3 does not match type bit(4)'), o aplicar &, |, # a cadenas de bits de distinta longitud ('cannot AND bit strings of different sizes'). No se debe al texto ni a la codificación de caracteres; un valor demasiado largo para char(n)/varchar(n) genera en su lugar 22001.","fix":"Lleve el valor a la longitud de destino: use un cast explícito (B'101'::bit(4) rellena con ceros a la derecha y trunca el exceso), o defina la columna como bit varying(n) si la longitud puede variar. Convierta los operandos a la misma longitud antes de las operaciones bit a bit; revise la longitud con length().","tags":["bit","length","bit string","varbit","conversion","validation"]},{"code":"2201B","title":"invalid_regular_expression","severity":"caution","meaning":"El patrón indicado a una operación de expresión regular (~, ~*, SIMILAR TO, regexp_matches, substring, etc.) no es válido. PostgreSQL no pudo compilar el patrón porque su sintaxis es incorrecta.","cause":"Un patrón regex con paréntesis desequilibrados, clases de caracteres no válidas, secuencias de escape incorrectas o una construcción no admitida. Una causa común es usar la entrada del usuario directamente como patrón sin escapar sus caracteres especiales.","fix":"Corrija el patrón regex según la sintaxis POSIX/ARE; revise los paréntesis, corchetes y secuencias de escape. Si busca la entrada del usuario como texto literal y no como patrón: PostgreSQL no tiene una función regexp_quote integrada, así que escape los caracteres especiales anteponiéndoles una barra invertida mediante regexp_replace, o evite por completo las regex y use strpos()/position() o LIKE ... ESCAPE. Valide el patrón probándolo en una herramienta aparte.","tags":["regex","regular expression","pattern","similar to","escape","syntax"]},{"code":"44000","title":"with_check_option_violation","severity":"danger","meaning":"Se rechazó un INSERT/UPDATE a través de una vista definida WITH CHECK OPTION porque la fila resultante ya no cumple la condición WHERE de la vista. La vista impide escribir filas que quedarían fuera de su propio filtro.","cause":"Una vista actualizable está definida WITH CHECK OPTION y la escritura deja la fila fuera de la condición que define la vista (p. ej., poner una fila como 'inactive' mientras la vista filtra status='active'). Una comprobación similar puede activarse en las políticas de row-level security (RLS).","fix":"Asegúrese de que los valores escritos cumplan la condición WHERE de la vista, o realice la operación directamente sobre la tabla base en lugar de la vista (si los privilegios lo permiten). Si la regla de negocio permite que la fila salga del filtro, redefina la vista sin WITH CHECK OPTION. En RLS, revise la expresión WITH CHECK de la política.","tags":["view","check option","updatable view","rls","insert","update"]},{"code":"40002","title":"transaction_integrity_constraint_violation","severity":"danger","meaning":"Código del estándar SQL para la infracción en COMMIT de una restricción de integridad comprobada al final de la transacción. Importante: PostgreSQL no produce este código en la práctica; devuelve las infracciones de restricciones diferidas (DEFERRED) en COMMIT con el código propio de la restricción: 23503 para foreign keys, 23505 para unicidad.","cause":"Una foreign key o restricción única definida DEFERRABLE INITIALLY DEFERRED se rompió temporalmente en pasos intermedios de la transacción y no se corrigió antes del COMMIT; en PostgreSQL se informa como 23503/23505. Por ejemplo, insertar filas que se referencian mutuamente en el orden incorrecto sin corregirlas provoca esto.","fix":"Corrija la lógica de la transacción para que todas las restricciones diferidas se cumplan antes del COMMIT (añada las referencias que faltan, resuelva los conflictos). Si es necesario, compruebe la restricción antes dentro de la transacción con SET CONSTRAINTS ... IMMEDIATE para detectar antes el paso problemático. Reordene las inserciones/actualizaciones en caso de referencias mutuas.","tags":["deferred","constraint violation","commit","foreign key","transaction","constraint"]}]},"errors/mysql-more4.json":{"engineId":"mysql","engine":"MySQL","order":3,"errors":[{"code":"1055","title":"Expression not in GROUP BY (only_full_group_by) (expresión que no está en GROUP BY)","severity":"caution","meaning":"Se usó en la lista SELECT o en HAVING/ORDER BY una columna que no está en GROUP BY ni envuelta en una función de agregación. Como el modo SQL ONLY_FULL_GROUP_BY está habilitado, MySQL rechaza esta expresión no determinista.","cause":"La consulta selecciona directamente una columna distinta de las agrupadas, pero esa columna puede no tener un único valor por grupo. ONLY_FULL_GROUP_BY está habilitado de forma predeterminada en MySQL 5.7+; las consultas que dependen del antiguo comportamiento permisivo de GROUP BY generan este error.","fix":"Añada la columna a la lista GROUP BY o envuélvala en una función de agregación (MAX(), MIN(), ANY_VALUE()). Si sabe que el valor es realmente único dentro del grupo, ANY_VALUE() suprime la ambigüedad de forma deliberada. Deshabilitar el modo (quitar ONLY_FULL_GROUP_BY de sql_mode) en lugar de corregir la consulta reintroduce el riesgo de resultados erróneos; evítelo.","tags":["group by","only_full_group_by","sql_mode","any_value","aggregate","query"]},{"code":"1267","title":"Illegal mix of collations","severity":"caution","meaning":"En una comparación u operación de combinación (JOIN, WHERE, UNION, CONCAT), se combinaron dos cadenas con collations distintas y MySQL no pudo determinar qué reglas de collation aplicar.","cause":"Normalmente se comparan columnas con juegos de caracteres/collations distintos (p. ej., utf8mb4_general_ci frente a utf8mb4_unicode_ci, o latin1 frente a utf8mb4), o hay una discrepancia entre una columna y la collation de la conexión. Habitual cuando las tablas/bases de datos se crearon con valores predeterminados distintos.","fix":"Lleve ambos lados de la comparación a la misma collation: especifique una collation explícita con COLLATE (p. ej., col1 = col2 COLLATE utf8mb4_unicode_ci) o use CONVERT(... USING ...). Como solución permanente, convierta las columnas/tablas afectadas a un único juego de caracteres y collation coherentes. Alinee también la collation de la conexión (collation_connection).","tags":["collation","collation mismatch","character set","join","utf8mb4","convert"]},{"code":"1030","title":"Got error N from storage engine","severity":"danger","meaning":"El motor de almacenamiento (InnoDB/MyISAM) no pudo completar una operación y devolvió un código de error de bajo nivel. Es un aviso genérico de un problema de disco, sistema de archivos o interno del motor por debajo de la capa SQL. El subcódigo entre paréntesis/en el mensaje muestra la causa real.","cause":"Causas comunes: disco/espacio temporal lleno (p. ej., errno 28 - No space left on device), problemas de permisos de archivos, archivos de tabla corruptos, problemas del tablespace de InnoDB o errores de I/O a nivel de SO. Las consultas de ordenación/agrupación que generan tablas temporales grandes también pueden provocarlo.","fix":"Descifre el subcódigo de error (en Linux, perror N muestra su significado, p. ej., perror 28). Revise el uso del disco y el espacio de tmpdir; libere espacio. Verifique la propiedad/los permisos de los archivos. En InnoDB, revise los detalles en el error log; si se sospecha corrupción, ejecute CHECK TABLE y recupere si es necesario. Optimice las consultas que generan resultados temporales enormes.","tags":["storage engine","innodb","disk","errno","tmpdir","i-o"]},{"code":"1197","title":"Multi-statement transaction required more than 'max_binlog_cache_size' (la transacción de varias sentencias requirió más que 'max_binlog_cache_size')","severity":"danger","meaning":"Los datos de binlog generados por una sola transacción superaron el límite de memoria/caché definido por max_binlog_cache_size. La transacción no cupo en el binary log, por lo que falló y se revirtió.","cause":"Una transacción muy grande (un único INSERT/UPDATE/DELETE de millones de filas o una carga masiva enorme) llenó la caché del binlog. Con el binary logging habilitado (replicación/recuperación a un punto en el tiempo), las transacciones grandes pueden superar fácilmente este límite.","fix":"Divida la transacción grande en lotes más pequeños; haga COMMIT tras cada lote. Si las transacciones realmente grandes son inevitables, aumente max_binlog_cache_size (y binlog_cache_size si es necesario). En ETL/cargas masivas, una carga por fragmentos con COMMIT intermedios evita tanto este error como los bloqueos mantenidos durante mucho tiempo.","tags":["binlog","max_binlog_cache_size","transaction","batch","replication","bulk"]},{"code":"1785","title":"Statement violates GTID consistency","severity":"danger","meaning":"Con la replicación basada en GTID (Global Transaction Identifier) habilitada, la sentencia ejecutada infringe las reglas de consistencia de GTID. El modo GTID no permite ciertos tipos de sentencias que no pueden asignarse de forma segura a un único GTID.","cause":"1785 (ER_GTID_UNSAFE_NON_TRANSACTIONAL_TABLE) se produce concretamente cuando se actualizan tablas no transaccionales (p. ej., MyISAM) en la misma transacción que tablas transaccionales (InnoDB), o en una transacción de varias sentencias sin autocommit. En versiones antiguas, CREATE TABLE ... SELECT (antes de 8.0.21; permitido en 8.0.21+ para motores que admiten DDL atómico) y CREATE/DROP TEMPORARY TABLE dentro de una transacción (antes de 8.0.13; permitido en 8.0.13+ cuando binlog_format es ROW/MIXED) también infringen la consistencia de GTID (se informan con códigos de error distintos). Se rechazan cuando enforce_gtid_consistency=ON.","fix":"No mezcle tablas de motores no transaccionales con tablas transaccionales en la misma transacción; actualice las tablas MyISAM con sentencias únicas en autocommit o pase todas las tablas a InnoDB: ALTER TABLE my_table ENGINE=InnoDB; En versiones antiguas (antes de 8.0.21), divida CREATE TABLE ... SELECT en dos pasos (CREATE TABLE y después INSERT ... SELECT); antes de 8.0.13, cree y elimine las tablas temporales fuera de una transacción explícita.","tags":["gtid","replication","consistency","temporary table","myisam","innodb"]},{"code":"3024","title":"Query execution was interrupted, maximum statement execution time exceeded (ejecución de la consulta interrumpida; se superó el tiempo máximo de ejecución)","severity":"caution","meaning":"MySQL interrumpió una consulta SELECT porque superó el tiempo máximo de ejecución definido para ella (max_execution_time / hint MAX_EXECUTION_TIME). Es una salvaguarda de timeout que impide que las consultas de larga duración acaparen recursos.","cause":"La consulta se ejecutó durante más tiempo que el valor de max_execution_time de sesión/global o que el hint del optimizador MAX_EXECUTION_TIME(N) del SELECT. Suele deberse a un plan ineficiente, un índice que falta, un recorrido grande o ralentizaciones con mucha concurrencia. (Solo se aplica a SELECT de solo lectura.)","fix":"Analice la consulta con EXPLAIN y optimícela (índices adecuados, predicados más selectivos, menos full table scans). Si la consulta necesita legítimamente ejecutarse durante mucho tiempo, aumente el hint MAX_EXECUTION_TIME de ese SELECT o el max_execution_time de la sesión. Ejecute las consultas analíticas pesadas fuera de línea/en una réplica.","tags":["timeout","max_execution_time","query","explain","index","select"]},{"code":"1298","title":"Unknown or incorrect time zone","severity":"caution","meaning":"MySQL no reconoció el nombre de zona horaria especificado. Una llamada a CONVERT_TZ(), el ajuste time_zone o una operación que hace referencia a una zona horaria con nombre (p. ej., 'Europe/Istanbul') falló porque esa zona horaria no está definida en el servidor.","cause":"Para que MySQL use zonas horarias con nombre, deben cargarse las tablas de zonas horarias del esquema mysql (time_zone, time_zone_name, etc.). Si estas tablas están vacías, solo funcionan los desplazamientos numéricos ('+03:00'); las zonas con nombre son desconocidas y se genera este error.","fix":"Cargue las tablas de zonas horarias. En Linux, importe los datos zoneinfo del sistema con mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql; después reinicie el servidor o ejecute FLUSH TABLES. Si no es posible cargar las zonas con nombre, use un desplazamiento numérico ('+03:00'). Verifique la ortografía del nombre (mayúsculas/minúsculas, 'Europe/Istanbul').","tags":["time zone","timezone","convert_tz","zoneinfo","mysql_tzinfo_to_sql","offset"]},{"code":"1418","title":"Function has none of DETERMINISTIC, NO SQL, or READS SQL DATA (binary logging enabled) (la función no tiene DETERMINISTIC, NO SQL ni READS SQL DATA)","severity":"caution","meaning":"Se estaba creando una función almacenada, pero el binary logging está habilitado y la función no se declaró con ninguna de las características de determinismo/acceso a datos. MySQL rechaza una función así porque es ambigua desde el punto de vista de la seguridad de la replicación.","cause":"La función se definió sin las características DETERMINISTIC, NO SQL o READS SQL DATA con log_bin en ON y log_bin_trust_function_creators=OFF. Sin estas características, MySQL no puede estar seguro de que la función produzca el mismo resultado en la réplica.","fix":"Declare el comportamiento de la función con las características correctas: añada DETERMINISTIC si es realmente determinista, NO SQL si no accede a datos, READS SQL DATA si solo lee. Si está seguro de que las funciones son seguras, puede establecer log_bin_trust_function_creators=1 como política central; sin embargo, declarar correctamente la característica es el enfoque más seguro. Asegúrese de que la característica coincida con el comportamiento real de la función.","tags":["stored function","deterministic","binary log","replication","log_bin_trust_function_creators","reads sql data"]},{"code":"1231","title":"Variable can't be set to the value of ... (no se puede asignar ese valor a la variable)","severity":"caution","meaning":"El valor que se asigna a una variable de sistema/sesión (con SET) no es válido. El valor se rechazó porque está fuera del conjunto permitido, es del tipo equivocado o tiene un formato inadecuado.","cause":"Ejemplos comunes: asignar a sql_mode un nombre de modo no válido/mal escrito, indicar una opción no definida a una variable de tipo enum o asignar NULL a una variable que no acepta NULL. Los valores numéricos en una unidad/rango incorrectos también pueden provocarlo.","fix":"Verifique los valores y el tipo válidos que acepta la variable; en variables de tipo ENUM/SET (p. ej., sql_mode, que es de tipo SET), escriba exactamente los nombres de valores/modos y separe varios valores con comas. Revise el rango de valores válidos en la documentación. Para listas como sql_mode, lea el valor actual (SELECT @@sql_mode) y añada/quite elementos de forma segura sobre él.","tags":["set","variable","sql_mode","enum","value","invalid"]}]},"errors/mssql-more4.json":{"engineId":"mssql","engine":"SQL Server","order":4,"errors":[{"code":"8645","title":"A time out occurred while waiting for memory resources to execute the query (timeout esperando recursos de memoria para ejecutar la consulta)","severity":"danger","meaning":"Una consulta excedió el timeout mientras esperaba en el resource semaphore el memory grant que necesita para ejecutarse. Como la presión de memoria impidió asignar un grant, se canceló la operación.","cause":"Consultas concurrentes con uso intensivo de memoria (sorts grandes, hash joins, hash aggregates) consumieron todo el pool de memoria de consultas; las nuevas consultas se pusieron en cola en el semaphore y excedieron el timeout. La causa raíz son estimaciones de memory grant sobredimensionadas (estadísticas obsoletas, malos planes) o memoria insuficiente en el servidor.","fix":"Identifique las consultas que esperan o retienen memoria con sys.dm_exec_query_memory_grants y las DMV del resource semaphore. Actualice las estadísticas y corrija los planes que provocan memory grants inflados (índices adecuados, mejores estimaciones). Reduzca la concurrencia de las consultas con uso intensivo de memoria; si es necesario, limite los memory grants con Resource Governor. Revise la configuración de memoria del servidor (max server memory).","tags":["memory grant","resource semaphore","memory","timeout","hash join","statistics"]},{"code":"8651","title":"Could not perform the operation because the requested memory grant was not available (no se pudo realizar la operación porque el memory grant solicitado no estaba disponible)","severity":"danger","meaning":"El memory grant que requiere la consulta no estaba disponible y no se pudo realizar la operación. A diferencia de 8645, no se trata de una espera, sino de la imposibilidad directa de proporcionar el gran bloque de memoria solicitado; suele indicar una presión de memoria grave.","cause":"La memoria de consultas disponible en el servidor no puede satisfacer el grant solicitado por un único operador (p. ej., un sort/hash muy grande). Pueden ser la causa estimaciones de memoria excesivamente altas, un max server memory bajo, presión de memoria externa o un límite bajo de Resource Governor, REQUEST_MAX_MEMORY_GRANT_PERCENT (workload group) o MAX_MEMORY_PERCENT (resource pool).","fix":"Optimice las consultas que inflan los memory grants (actualizar estadísticas, indexación adecuada, dividir la consulta). Supervise la memoria total y la disponible; revise el ajuste max server memory y la presión de memoria de aplicaciones externas. Si se usa Resource Governor, revise el REQUEST_MAX_MEMORY_GRANT_PERCENT del workload group y el MAX_MEMORY_PERCENT del pool. Mueva los trabajos por lotes con uso intensivo de memoria a horas valle.","tags":["memory grant","memory","resource governor","statistics","hash","pressure"]},{"code":"5061","title":"ALTER DATABASE failed because a lock could not be placed on database","severity":"danger","meaning":"Una operación ALTER DATABASE (p. ej., pasar a modo de usuario único, cambiar un archivo, establecer una opción) no pudo obtener el bloqueo necesario sobre la base de datos porque otras conexiones/sesiones activas la están usando.","cause":"Se intentó la operación mientras la base de datos estaba en uso; el bloqueo exclusivo necesario para SET SINGLE_USER, RESTRICTED_USER o cambios de opciones similares no pudo obtenerse por las conexiones de usuario activas. Habitual durante despliegues/mantenimientos frecuentes con conexiones de la aplicación abiertas.","fix":"Identifique las demás sesiones conectadas a la base de datos (sys.dm_exec_sessions, sp_who2) y ciérrelas de forma segura. Para el mantenimiento, cambie el modo revirtiendo las transacciones abiertas con la opción ROLLBACK: ALTER DATABASE [db] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; Realice la operación con poco tráfico de la aplicación o en una ventana de mantenimiento. Recuerde volver a MULTI_USER al terminar.","tags":["alter database","lock","single_user","connection","maintenance","rollback"]},{"code":"3702","title":"Cannot drop database because it is currently in use (no se puede eliminar la base de datos porque está en uso)","severity":"caution","meaning":"Se intentó eliminar o renombrar una base de datos, pero se rechazó porque la base de datos sigue en uso por conexiones activas. SQL Server no puede eliminar una base de datos que está en uso.","cause":"Otras sesiones (un pool de la aplicación, una pestaña de SSMS abierta, jobs en segundo plano) están conectadas a la base de datos que se elimina. Incluso que su propia sesión tenga esa base de datos como contexto (USE) puede provocarlo. Las conexiones abiertas de herramientas de monitoreo/backup también son una causa habitual.","fix":"Cambie a otra base de datos antes de eliminarla (USE master;). Busque y cierre las sesiones conectadas, o ponga primero la base de datos en modo de usuario único: ALTER DATABASE [db] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; y después ejecute DROP DATABASE [db]; Detenga los pools de conexiones de la aplicación y asegúrese de que las herramientas de monitoreo no abran conexiones.","tags":["drop database","connection","in use","single_user","use","maintenance"]},{"code":"15138","title":"The database principal owns a schema in the database, and cannot be dropped (el principal es propietario de un esquema de la base de datos y no puede eliminarse)","severity":"caution","meaning":"Se intentó eliminar un usuario/rol de base de datos (database principal), pero no pudo eliminarse porque el principal es propietario de uno o más esquemas de la base de datos. No puede eliminarse hasta transferir a otro principal los esquemas que posee.","cause":"El usuario que se elimina es OWNER de al menos un esquema (p. ej., un esquema creado de forma predeterminada con su propio nombre o uno asignado mediante AUTHORIZATION). SQL Server bloquea la eliminación para no dejar esquemas huérfanos.","fix":"Busque los esquemas que posee el usuario: SELECT name FROM sys.schemas WHERE principal_id = USER_ID('app_user'); Transfiera la propiedad de estos esquemas a otro principal (p. ej., dbo): ALTER AUTHORIZATION ON SCHEMA::[app_schema] TO dbo; Si el esquema ya no se necesita (y está vacío), DROP SCHEMA. Una vez transferida la propiedad, vuelva a ejecutar DROP USER.","tags":["schema","principal","user","owner","alter authorization","drop user"]},{"code":"1934","title":"INSERT/UPDATE failed because the following SET options have incorrect settings (falló INSERT/UPDATE porque estas opciones SET tienen valores incorrectos)","severity":"danger","meaning":"Una operación DML (INSERT/UPDATE/DELETE) falló porque las opciones SET necesarias estaban configuradas de forma incorrecta. Suele ocurrir cuando no se cumplen los ajustes SET obligatorios para vistas indexadas, índices sobre columnas calculadas, índices filtrados o índices espaciales/XML.","cause":"La tabla/vista afectada tiene un objeto que requiere opciones SET concretas (sobre todo QUOTED_IDENTIFIER y ANSI_NULLS en ON, NUMERIC_ROUNDABORT en OFF, etc.), pero las opciones SET actuales de la sesión no cumplen ese requisito. Lo provocan drivers de cliente antiguos o scripts que establecen explícitamente opciones SET incorrectas.","fix":"Establezca las opciones necesarias en la conexión/sesión: SET QUOTED_IDENTIFIER ON; SET ANSI_NULLS ON; SET ANSI_PADDING ON; SET ANSI_WARNINGS ON; SET CONCAT_NULL_YIELDS_NULL ON; SET ARITHABORT ON; SET NUMERIC_ROUNDABORT OFF; Asegúrese de que el driver de la aplicación envíe correctamente estas opciones al conectarse. El mensaje de error nombra las opciones incorrectas; corríjalas.","tags":["set options","indexed view","computed column","quoted_identifier","ansi_nulls","dml"]},{"code":"468","title":"Cannot resolve the collation conflict","severity":"caution","meaning":"En una operación (JOIN, UNION, comparación en WHERE o igualdad), se encontraron dos expresiones de cadena con collations distintas y SQL Server no pudo resolver qué collation aplicar. La operación se canceló por la ambigüedad.","cause":"Las columnas comparadas están definidas con collations distintas; se ve sobre todo cuando la collation de la base de datos difiere de la del servidor/tempdb, o cuando se combinan tablas de bases de datos con collations distintas. Combinar tablas temporales (collation de tempdb) con tablas permanentes es un desencadenante clásico.","fix":"Especifique una collation explícita en la comparación: ... WHERE a.col = b.col COLLATE DATABASE_DEFAULT (o un nombre de collation concreto). Use COLLATE DATABASE_DEFAULT al crear columnas de tablas temporales. Como solución permanente, alinee las columnas/bases de datos afectadas con una única collation coherente. Si las discrepancias con tempdb son frecuentes, DATABASE_DEFAULT es la solución más práctica.","tags":["collation","collation","join","tempdb","database_default","conflict"]},{"code":"4864","title":"Bulk load data conversion error (type mismatch or invalid character) (error de conversión de datos en la carga masiva (tipo no coincidente o carácter no válido))","severity":"caution","meaning":"Durante una carga masiva (BULK INSERT, bcp u OPENROWSET(BULK...)), un campo del archivo de origen no pudo convertirse al tipo de datos de la columna de destino. Los datos de la fila/columna indicada no coinciden con el tipo esperado.","cause":"Un valor del archivo de datos de origen no encaja en el tipo de la columna de destino: texto en una columna numérica, formato de fecha incorrecto, separador decimal/de miles inesperado, codificación de caracteres incorrecta (UTF-8/UTF-16) o columnas desplazadas por terminadores de campo/fila incorrectos. No omitir la fila de encabezado también es una causa habitual.","fix":"Vaya a la fila y la columna que indica el error e inspeccione los datos de origen. Haga coincidir los terminadores de campo (FIELDTERMINATOR) y de fila (ROWTERMINATOR) con el archivo; use FIRSTROW=2 para omitir la fila de encabezado. Establezca CODEPAGE o DATAFILETYPE para la página de códigos/codificación correcta. Haga compatibles los formatos de fecha/número con el tipo de destino. Usar un format file hace fiable la correspondencia.","tags":["bulk insert","bcp","conversion","fieldterminator","encoding","format file"]},{"code":"916","title":"The server principal is not able to access the database under the current security context (el principal de servidor no puede acceder a la base de datos en el contexto de seguridad actual)","severity":"caution","meaning":"Un login intentó acceder a una base de datos concreta, pero no tiene acceso a ella en el contexto de seguridad actual. El login puede conectarse al servidor, pero no tiene un usuario/permiso correspondiente en la base de datos de destino.","cause":"El login no tiene un usuario asignado en la base de datos (huérfano o nunca creado), el acceso a la base de datos está restringido o la cuenta guest está deshabilitada. Un caso habitual es el Object Explorer de SSMS intentando leer metadatos de una base de datos a la que el login no tiene acceso.","fix":"Cree un usuario para el login en la base de datos de destino y conceda los permisos necesarios: USE [db]; CREATE USER [login] FOR LOGIN [login]; y asigne los roles/permisos adecuados. Si existe un usuario huérfano, asígnelo con ALTER USER ... WITH LOGIN = ... Si el acceso es realmente necesario, añádalo al rol de base de datos adecuado; si no, el error es el comportamiento esperado (sin acceso a esa base de datos).","tags":["login","user","access","privilege","security context","orphaned user"]}]},"errors/mongodb-more4.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"errors":[{"code":"216 ElectionInProgress","title":"ElectionInProgress","severity":"caution","meaning":"Hay una elección de primario en curso en el replica set. Hasta que termine la elección el primario no está determinado, por lo que temporalmente no se aceptan escrituras (ni algunas lecturas).","cause":"El clúster está eligiendo un nuevo primario tras la caída del primario actual, una partición de red, un stepdown por mantenimiento o el reinicio de un nodo. Una elección suele durar unos segundos; las operaciones que llegan en esa ventana se encuentran con esta condición.","fix":"Es transitorio; habilite la función de reintento de su driver (retryable writes/reads) para que la operación se reintente automáticamente cuando termine la elección. Si las elecciones son frecuentes o largas, investigue la causa raíz: estabilidad de la red, salud de los nodos, configuración de votos y ajustes de timeout de elección (settings.electionTimeoutMillis). Inspeccione el estado del clúster con rs.status().","tags":["replica set","election","primary","retry","step-down","high availability"]},{"code":"225 TransactionTooOld","title":"TransactionTooOld","severity":"caution","meaning":"Se ejecutó un comando dentro de una transacción multidocumento, pero su número de transacción es más antiguo que el último número de transacción que el servidor ha visto para esa sesión. Es decir, se ha iniciado una transacción más reciente, por lo que esta antigua ya no es válida.","cause":"Se inició una nueva transacción en la misma sesión antes de que terminara la anterior, o los números de transacción avanzaron desordenadamente por la concurrencia. Suele ocurrir cuando un objeto de sesión se comparte de forma incorrecta o varios hilos usan la misma sesión.","fix":"Gestione correctamente el ciclo de vida de la sesión para cada transacción lógica; no comparta una sesión de forma concurrente entre varios hilos. Ejecute las transacciones con la API withTransaction/callback que recomienda el driver (gestiona los reintentos y la numeración). Cierre la transacción anterior con commit/abort antes de iniciar una nueva.","tags":["transaction","session","session misuse","concurrency","withtransaction","multi-document transaction"]},{"code":"246 SnapshotUnavailable","title":"SnapshotUnavailable","severity":"caution","meaning":"No se pudo realizar una lectura con el read concern snapshot solicitado (readConcern: snapshot) porque el servidor no puede proporcionar un snapshot consistente para ese momento. Suele estar relacionado con una marca de tiempo de lectura que no ha avanzado lo suficiente o que se ha quedado atrás.","cause":"Se usa readConcern 'snapshot' (sobre todo en transacciones multidocumento), pero aún no hay disponible una vista consistente para la marca de tiempo atLeast/at solicitada; p. ej., una colección/índice recién creados, un cambio de catálogo o retraso de replicación. En este caso el servidor rechaza la lectura.","fix":"Suele ser transitorio; reintente la operación tras un breve retraso (el mecanismo de reintento de errores transitorios de transacciones del driver cubre este caso). Si acaba de ejecutar DDL (creación de colecciones/índices), inicie la transacción después. Revise el retraso de replicación y la salud de los nodos. Si no se necesita realmente una lectura snapshot, elija un readConcern más adecuado.","tags":["snapshot","readconcern","transaction","timestamp","retry","replication"]},{"code":"257 TransactionTooLarge","title":"TransactionTooLarge","severity":"danger","meaning":"Una transacción multidocumento superó los límites de tamaño/número de operaciones permitidos. La transacción se rechazó porque los cambios acumulados en ella (entrada de oplog o uso de memoria) superaron el límite superior.","cause":"Modificar/insertar un gran número de documentos en una sola transacción elevó demasiado el total de datos producidos por la transacción (p. ej., el volumen de cambios que deben mantenerse en la caché de WiredTiger, o límites del servidor como transactionLimitBytes). Desde 4.2 una transacción puede repartirse en varias entradas de oplog, por lo que el límite de 16 MB por entrada de oplog no es decisivo por sí solo. Usar transacciones para migraciones masivas de datos/ETL alcanza fácilmente este límite.","fix":"Divida la transacción en partes más pequeñas; confirme cada lote en una transacción aparte. Las transacciones multidocumento están pensadas para ser cortas y pequeñas; es preferible hacer las actualizaciones masivas grandes sin transacciones (de forma idempotente, fragmento a fragmento) o dividirlas con bulk write. Evite modificar miles de documentos en una sola transacción.","tags":["transaction","size","oplog","batch","bulk","bulk write"]},{"code":"276 IndexBuildAborted","title":"IndexBuildAborted","severity":"danger","meaning":"Una creación de índice en curso se abortó/detuvo antes de completarse. El índice de destino no se creó correctamente.","cause":"La creación del índice puede haberse abortado por disco/memoria insuficientes en el nodo, un aborto explícito mediante dropIndexes, un stepdown del primario/cambio de estado de la réplica, una infracción de unicidad (documentos en conflicto al crear un índice único) o el reinicio de un nodo.","fix":"Busque en los logs de mongod el motivo del aborto (espacio en disco, memoria, conflicto de unicidad, etc.) y resuélvalo. Si está creando un índice único, limpie primero los documentos en conflicto. Proporcione suficiente disco/memoria y asegúrese de que el estado de la réplica sea estable. Tras corregir el problema, vuelva a crear el índice con createIndex; en colecciones grandes elija un periodo de poca carga.","tags":["index","index build","aborted","unique","disk","replica set"]},{"code":"86 IndexKeySpecsConflict","title":"IndexKeySpecsConflict","severity":"caution","meaning":"El índice que se crea tiene la misma especificación de clave (mismos campos/orden) que un índice existente, pero opciones o un nombre distintos. MongoDB no permite dos definiciones de índice en conflicto con la misma clave.","cause":"Se intentó crear un segundo índice sobre el mismo campo o campos con otro nombre u otras opciones (p. ej., distinta collation, partialFilterExpression, ajuste unique). Suele ocurrir cuando se cambia una definición de índice en una migración/en el código y se vuelve a ejecutar.","fix":"Inspeccione los índices existentes: db.collection.getIndexes() Busque el índice en conflicto con la misma clave. Para pasar a las opciones que desea, elimine primero el índice antiguo (dropIndex) y vuelva a crearlo con la nueva definición. Si solo difiere el nombre, siga usando el índice existente; no cree una segunda definición innecesaria.","tags":["index","getindexes","collation","unique","conflict","createindex"]},{"code":"166 CommandNotSupportedOnView","title":"CommandNotSupportedOnView","severity":"caution","meaning":"El comando no se admite sobre una vista. Las vistas de MongoDB son de solo lectura y solo aceptan ciertas operaciones de lectura; las escrituras o los comandos no admitidos se rechazan.","cause":"Se intentaron sobre una vista operaciones de escritura como insert/update/delete, o ciertos comandos no admitidos en vistas (p. ej., algunas operaciones de texto/geo, la creación directa de índices). Se ve cuando la aplicación intenta escribir en una vista creyendo que es una colección.","fix":"Realice las escrituras en la colección subyacente (de origen) en la que se basa la vista; las vistas son solo para lecturas. Verifique si el comando se admite en vistas y use operaciones de lectura/agregación admitidas. Si necesita una estructura en la que se pueda escribir, diseñe una colección materializada ($merge/$out) en lugar de una vista.","tags":["view","read-only view","read only","aggregation","write","collection"]},{"code":"168 InvalidPipelineOperator","title":"InvalidPipelineOperator","severity":"caution","meaning":"Se usó un operador/etapa no reconocido o no válido en un pipeline de agregación. MongoDB no pudo resolver el nombre del operador especificado y rechazó el pipeline.","cause":"Un nombre de operador mal escrito (p. ej., '$groups' en lugar de '$group'), un operador nuevo no disponible en la versión de MongoDB en uso o una expresión usada en el contexto equivocado. También lo provoca la falta del prefijo $ o un operador usado en una posición no válida.","fix":"Verifique la ortografía de los nombres de operadores/etapas y el prefijo $. Asegúrese de que la versión de MongoDB del servidor admita el operador (los operadores nuevos no existen en versiones antiguas). Compare el pipeline con la sintaxis documentada; use la expresión dentro de la etapa correcta (p. ej., $expr, $project). Aísle la etapa y pruébela con una muestra pequeña.","tags":["aggregation","pipeline","operator","syntax","version","expr"]},{"code":"118 NamespaceNotSharded","title":"NamespaceNotSharded","severity":"caution","meaning":"Se ejecutó una operación que solo tiene sentido en colecciones con sharding contra una colección/namespace que aún no tiene sharding. El comando no pudo aplicarse porque la colección de destino no tiene sharding.","cause":"Se llamó a moveChunk, split o ciertos comandos administrativos basados en la shard key sobre una colección sin sharding habilitado. Suele ocurrir porque la colección no se particionó con shardCollection o se apuntó al namespace equivocado.","fix":"Revise el estado de sharding de la colección: sh.status() o config.collections. Si la operación realmente requiere sharding, habilite primero el sharding en la base de datos (sh.enableSharding) y particione la colección (sh.shardCollection con una shard key adecuada). En caso contrario, estos comandos administrativos no son necesarios en colecciones sin sharding; verifique que apunta al namespace correcto.","tags":["sharding","shard","namespace","shardcollection","movechunk","cluster"]}]},"errors/redis-more4.json":{"engineId":"redis","engine":"Redis","order":6,"errors":[{"code":"ERR Protocol error: invalid multibulk length","title":"Protocol error: invalid multibulk length (error de protocolo: longitud multibulk no válida)","severity":"danger","meaning":"El servidor Redis no pudo analizar el flujo de bytes del cliente según RESP (Redis Serialization Protocol). Como el número de elementos declarado por un comando multibulk no era válido, la conexión se cerró con un error de protocolo.","cause":"Suele provocarlo un cliente que no es de Redis (p. ej., una solicitud HTTP, una aplicación conectada al puerto equivocado) que envía datos sin procesar al puerto de Redis, una biblioteca cliente rota/incompleta, un framing defectuoso en un pipeline o solicitudes enormes que superan los límites del protocolo (proto-max-bulk-len).","fix":"Verifique que solo un cliente Redis real se conecte al puerto de Redis (sin tráfico HTTP/de otros protocolos ajeno). Actualice la biblioteca cliente y evite la generación de comandos personalizada/manual que rompa el framing de RESP. Si envía valores muy grandes, revise el límite proto-max-bulk-len y el tamaño de las solicitudes. Asegúrese de que ningún proxy intermedio de la red corrompa los datos.","tags":["protocol","resp","multibulk","client","pipeline","connection"]},{"code":"ERR Number of keys can't be greater than number of args","title":"EVAL/EVALSHA: numkeys exceeds argument count (numkeys supera el número de argumentos)","severity":"caution","meaning":"Al ejecutar un script Lua (EVAL/EVALSHA/FCALL), el número de claves especificado (numkeys) es mayor que el número de argumentos realmente pasados al comando. Redis rechaza el comando porque no puede separar los arrays KEYS y ARGV.","cause":"La sintaxis de EVAL es 'EVAL script numkeys key [key ...] arg [arg ...]'. El valor numkeys se calculó mal (demasiado alto) o las claves esperadas nunca se añadieron al comando. Un error de programación habitual.","fix":"Haga que numkeys sea exactamente igual al número de claves que realmente se pasan al comando. Use el wrapper de EVAL de su biblioteca cliente (que normalmente recibe KEYS y ARGV como listas separadas) para que numkeys se calcule automáticamente. Al llamar al script, separe correctamente las claves en KEYS y los demás parámetros en ARGV.","tags":["lua","eval","evalsha","numkeys","keys","script"]},{"code":"ERR WATCH inside MULTI is not allowed","title":"WATCH inside MULTI is not allowed","severity":"caution","meaning":"Se envió WATCH dentro de un bloque MULTI abierto. Para el bloqueo optimista, WATCH debe poner las claves bajo vigilancia ANTES de que empiece la transacción; si se llama después de MULTI, se rechaza. El error no aborta la transacción; solo falla WATCH y no se vigila nada.","cause":"WATCH y MULTI escritos en orden inverso en el código (primero MULTI y luego WATCH); llamar a WATCH dentro del wrapper de pipeline/transacción de la biblioteca cliente; o usar una conexión del pool que quedó en estado MULTI. En este caso EXEC se ejecuta aunque hayan cambiado las claves que debían vigilarse; la protección contra conflictos esperada no actúa.","fix":"Orden correcto: WATCH key -> (leer valores) -> MULTI -> comandos -> EXEC. Si EXEC devuelve nil, cambió una clave vigilada; reintente la operación desde el principio. Use la API de WATCH/transacciones de la biblioteca cliente (p. ej., helpers de watch + pipeline). Nota: los comandos pub/sub como SUBSCRIBE no se rechazan dentro de MULTI; se ponen en cola y la conexión pasa al modo de suscripción tras EXEC; esto tampoco se recomienda, use una conexión dedicada para pub/sub.","tags":["multi","watch","transaction","optimistic locking","exec","connection"]},{"code":"ERR WAIT cannot be used with replica instances","title":"WAIT cannot be used with replica instances (WAIT no puede usarse en instancias réplica)","severity":"caution","meaning":"Se ejecutó WAIT en una instancia réplica (replica/slave). WAIT, que espera a que una escritura llegue a un número determinado de réplicas, solo tiene sentido en el primario (master); no puede usarse en una réplica.","cause":"La aplicación envió WAIT por error a través de una conexión a una réplica. Suele ocurrir porque la conexión se dirige a una réplica de lectura, o porque el cliente sigue conectado a una réplica tras un cambio de rol (failover).","fix":"Envíe WAIT solo al nodo primario. Asegúrese de que la configuración del cliente/proxy dirija las escrituras y las llamadas WAIT al master. Detecte los cambios de rol tras un failover y redirija las conexiones al nuevo primario (puede verificar el rol con el comando ROLE).","tags":["wait","replica","master","write","failover","replication"]},{"code":"ERR Background append only file rewriting already in progress","title":"AOF rewrite already in progress","severity":"caution","meaning":"Se intentó iniciar una reescritura de AOF (Append Only File) (BGREWRITEAOF), pero ya hay una reescritura de AOF en curso. No pueden ejecutarse dos reescrituras de AOF al mismo tiempo.","cause":"Llegó una nueva solicitud de reescritura mientras seguía ejecutándose un BGREWRITEAOF anterior (manual o automático por el disparador auto-aof-rewrite-percentage). En datasets grandes, una reescritura de AOF puede tardar mucho y provocar este conflicto.","fix":"Espere a que termine la reescritura en curso; vigile el campo aof_rewrite_in_progress en la salida de INFO persistence. Si la operación se está ejecutando legítimamente, no envíe otra solicitud. Si los conflictos son frecuentes, revise los umbrales de reescritura automática (auto-aof-rewrite-percentage/min-size) y el rendimiento de I/O del disco.","tags":["aof","bgrewriteaof","persistence","rewrite","info","disk"]},{"code":"IOERR error or timeout reading from target instance","title":"MIGRATE: I/O error or timeout to target","severity":"danger","meaning":"Un comando MIGRATE (normalmente al mover slots/claves en Redis Cluster) encontró un error de I/O o un timeout al conectarse a la instancia de destino o al leer su respuesta. La migración de la clave no pudo completarse de forma segura.","cause":"Latencia/caída de red entre los nodos de origen y destino, el nodo de destino sobrecargado o sin responder, un timeout de MIGRATE demasiado corto o una clave grande que no puede transferirse en el tiempo indicado. Habitual durante el rebalanceo del clúster (resharding).","fix":"Revise la conectividad y la latencia de red entre los nodos de origen y destino. Especifique un timeout más largo para MIGRATE (sobre todo con claves grandes). Verifique la salud/carga y el estado de memoria del nodo de destino. Al mover claves grandes durante el resharding, elija horas de poca carga; reintente la migración si es necesario.","tags":["migrate","cluster","resharding","timeout","network","i-o"]},{"code":"ERR Error compiling script (new function)","title":"Error compiling Lua script","severity":"caution","meaning":"El intérprete Lua integrado de Redis no pudo compilar el script Lua enviado con EVAL. Se rechazó antes de empezar a ejecutarse porque el script contiene un error de sintaxis.","cause":"Errores de sintaxis en el script Lua: paréntesis/comillas sin cerrar, palabras clave mal escritas, un 'end' que falta, operadores no válidos o una construcción de Lua no admitida. Una concatenación/escape incorrectos del script en el lado del cliente también pueden provocarlo.","fix":"Corrija la sintaxis de Lua usando la información de línea/carácter del mensaje de error (revise que coincidan los paréntesis, las comillas y los 'end'). Valide el script compilándolo en un entorno Lua aparte. Asegúrese de que el cliente construya el script correctamente (sin errores de escape/concatenación). Precargue los scripts validados con SCRIPT LOAD y llámelos con EVALSHA.","tags":["lua","eval","compilation","syntax","script load","script"]},{"code":"ERR FAILOVER requires connected replicas.","title":"FAILOVER requires connected replicas","severity":"caution","meaning":"Se ejecutó FAILOVER para iniciar un failover coordinado (ordenado), pero se rechazó porque no hay ninguna réplica adecuada conectada al primario que pueda asumir el rol. El failover no puede producirse sin un destino al que traspasar el rol.","cause":"El primario no tiene réplicas conectadas en este momento (las réplicas están caídas, desconectadas de la red o no sincronizadas), o la réplica de destino especificada no es apta/no está conectada. Cuando se llama al FAILOVER manual (Redis 6.2+), la topología de replicación no cumple la condición necesaria. Nota: FAILOVER solo funciona con la replicación standalone (master/replica); en Redis Cluster se usa CLUSTER FAILOVER en la réplica.","fix":"Asegúrese de que exista al menos una réplica sana, conectada y actualizada (revise connected_slaves y el estado de sincronización con INFO replication). Corrija los problemas de red/acceso de las réplicas para que se conecten al primario. Para hacer failover a un destino concreto, use FAILOVER TO host port y verifique que esa réplica sea apta.","tags":["failover","replica","master","replication","info replication","high availability"]},{"code":"ERR invalid expire time in 'set' command","title":"invalid expire time in SET","severity":"caution","meaning":"Se indicó a SET un tiempo de expiración no válido (con las opciones EX/PX/EXAT/PXAT). Redis rechazó el comando porque no acepta un tiempo de vida sin sentido, como cero o un valor negativo.","cause":"Enviar 0 o un valor negativo con EX/PX, o que la aplicación calcule mal la expiración y produzca un número no válido. Comandos como SETEX/GETEX devuelven este error de forma similar con duraciones no positivas. Algunos bugs de cliente se deben a confundir segundos y milisegundos.","fix":"Envíe siempre la expiración como un entero estrictamente positivo; EX espera segundos y PX milisegundos. Valide el cálculo de la duración en la aplicación (no debe producir resultados negativos/cero) y no confunda las unidades (segundos frente a milisegundos). Para eliminar una clave de inmediato, use DEL/UNLINK en lugar de establecer una expiración. Para un tiempo absoluto, pase a EXAT/PXAT una marca de tiempo Unix futura válida.","tags":["set","expire","ttl","ex","px","expiration"]}]},"errors/elasticsearch-more4.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"errors":[{"code":"bind_transport_exception","title":"bind_transport_exception (Address already in use) (la dirección ya está en uso)","severity":"danger","meaning":"Al arrancar, un nodo de Elasticsearch no pudo enlazar un socket para escuchar en el puerto de transporte (comunicación entre nodos) o HTTP. Suele ir acompañado del suberror 'Address already in use', y el nodo no puede arrancar.","cause":"El puerto solicitado (por defecto, transporte 9300, HTTP 9200) ya lo usa otro proceso; lo más habitual es que una instancia anterior del mismo nodo no se apagara limpiamente, que haya un conflicto con otra instancia/servicio de ES en el mismo host o que network.host/port esté mal configurado. Las restricciones de permisos/acceso también pueden impedir el enlace.","fix":"Averigüe quién ocupa el puerto (Linux: ss -ltnp | grep 9300 o lsof -i:9300) y termine el proceso en conflicto, o asigne al nodo otro puerto (transport.port / http.port). Verifique que el proceso de ES anterior haya terminado realmente. Asegúrese de que network.host y los ajustes del rango de puertos sean correctos y enlazables. Después reinicie el nodo.","tags":["transport","port","9300","bind","network.host","startup"]},{"code":"translog_corrupted_exception","title":"translog_corrupted_exception","severity":"danger","meaning":"No se pudo leer el log de transacciones (translog) de un shard porque está corrupto. Durante la recuperación, Elasticsearch detectó que el translog no superó su comprobación de integridad, y el shard no pudo arrancar.","cause":"Los archivos de translog suelen corromperse por un corte de energía repentino, un disco lleno, un error del sistema de archivos, un fallo de hardware/disco u operaciones externas sobre el directorio de datos (copia, intervención manual). Las escrituras parcialmente completadas durante un apagado forzado (kill -9) también pueden provocarlo.","fix":"Intente primero recuperar los datos desde una réplica o un snapshot; si existe una copia sana, reconstruir el shard corrupto a partir de ella es el camino más seguro. Si no existe otra copia y acepta la pérdida de datos, detenga el nodo y limpie el translog/los datos corruptos con bin/elasticsearch-shard remove-corrupted-data --index <index> --shard-id <n> (esto pierde de forma permanente los datos aún no confirmados; la antigua herramienta elasticsearch-translog se eliminó en 7.0). La herramienta imprime el comando POST _cluster/reroute (allocate_stale_primary, accept_data_loss: true) necesario para asignar el shard tras reiniciar el nodo. Haga backup del directorio de datos antes de la operación. Revise el estado del disco y el espacio libre.","tags":["translog","shard","recovery","corruption","snapshot","disk"]},{"code":"recovery_engine_exception","title":"recovery_engine_exception","severity":"danger","meaning":"Se produjo un error a nivel del motor durante la recuperación de un shard, y el shard no pudo abrirse correctamente. La recuperación es el paso que se realiza durante el reinicio de un nodo, la asignación de una réplica o la restauración de un snapshot.","cause":"Un problema subyacente durante la recuperación: segmentos de Lucene o translog corruptos, espacio en disco insuficiente, permisos de archivos, una interrupción de red entre los nodos de origen y destino, o problemas de versión/compatibilidad. Suele envolver un suberror más específico (corrupción, I/O).","fix":"Inspeccione el error subyacente (caused_by) para identificar el problema real (corrupción, disco, red, etc.). Verifique el espacio en disco y los permisos de los archivos. Si falla la asignación de réplicas, vea el motivo con _cluster/allocation/explain y fuerce la reasignación si es necesario. Si hay corrupción, recupere desde una réplica/snapshot sanos. Si está relacionado con la red, revise la conectividad entre nodos y los ajustes de recuperación.","tags":["recovery","shard","engine","allocation","corruption","disk"]},{"code":"snapshot_in_progress_exception","title":"snapshot_in_progress_exception","severity":"caution","meaning":"La operación solicitada se rechazó porque entra en conflicto con un snapshot en curso. Por ejemplo, se bloquea un intento de eliminar/cerrar un índice o de modificar el repositorio mientras se toma un snapshot.","cause":"Mientras un snapshot seguía ejecutándose, se intentó eliminar/cerrar índices incluidos en ese snapshot, quitar/modificar el repositorio o iniciar otra operación de snapshot/restauración en conflicto. Elasticsearch no permite estas operaciones en conflicto para preservar la consistencia.","fix":"Espere a que termine el snapshot en curso: supervise el progreso con GET _snapshot/_status. Si realmente hay que cancelar la operación, deténgala eliminando el snapshot: DELETE _snapshot/repo/snapshot_name (esto aborta el snapshot en curso). Cuando el snapshot termine o se cancele, vuelva a ejecutar su operación original (eliminación de índice, cambio de repositorio).","tags":["snapshot","repository","restore","index deletion","conflict","backup"]},{"code":"repository_verification_exception","title":"repository_verification_exception","severity":"danger","meaning":"Al registrar o verificar un repositorio de snapshots, falló la comprobación de que todos los nodos pueden acceder al repositorio. Como la verificación del repositorio no se superó, las operaciones de snapshot/restauración no pueden realizarse de forma fiable.","cause":"Un repositorio de sistema de archivos compartido (fs) no está montado/accesible en la misma ruta en todos los nodos, falta path.repo en la configuración, los permisos de archivos son insuficientes, o los almacenes en la nube/de objetos (S3, GCS, Azure) tienen credenciales/acceso de red incorrectos. La causa típica es que algunos nodos no pueden escribir en el repositorio.","fix":"Para un repositorio fs compartido, asegúrese de que path.repo esté definido en elasticsearch.yml en todos los nodos y de que la misma ruta esté montada con lectura/escritura en cada nodo. Corrija los permisos de los archivos (usuario elasticsearch). Para repositorios en la nube, verifique las credenciales (keystore), el nombre del bucket y los permisos de red/acceso. Vuelva a verificar el repositorio con POST _snapshot/repo/_verify.","tags":["repository","snapshot","path.repo","permission","s3","verification"]},{"code":"reduce_search_phase_exception","title":"reduce_search_phase_exception","severity":"danger","meaning":"Se produjo un error en la fase de reduce de una búsqueda. El nodo coordinador encontró un problema al combinar (reduce) los resultados parciales/agregaciones de distintos shards y no pudo producir el resultado final.","cause":"Normalmente se supera un límite de memoria/recursos al combinar resultados (p. ej., agregaciones muy grandes, un circuit breaker que salta), una estructura de agregación incompatible/no válida, o no se puede combinar un resultado parcial devuelto por un shard. Habitual con agregaciones de cardinalidad muy alta.","fix":"Lea el detalle caused_by para identificar la causa raíz. Aligere las agregaciones pesadas: reduzca el size en las agregaciones terms de alta cardinalidad, pagine con una composite aggregation y reduzca las subagregaciones innecesarias. Supervise la presión de memoria y divida la consulta en partes más pequeñas si es necesario. Si salta un circuit breaker, reduzca el costo de la consulta.","tags":["search","reduce","aggregation","coordinating node","memory","cardinality"]},{"code":"aggregation_execution_exception","title":"aggregation_execution_exception","severity":"caution","meaning":"Se produjo un error durante la ejecución de una agregación. Aunque la definición de la agregación se analizó, surgió un problema en tiempo de ejecución al procesar los campos/valores de destino.","cause":"La agregación es incompatible con el tipo de campo (p. ej., agregar un campo text sin fielddata), valores no válidos durante la ordenación/el cálculo, una pipeline aggregation que referencia una ruta inexistente (bucket path) o combinaciones de tipos de campo no admitidas. Los errores en tiempo de ejecución en agregaciones basadas en scripts también lo provocan.","fix":"Inspeccione el nombre del campo/agregación en el detalle del error. Asegúrese de que los campos agregados sean de un tipo adecuado (keyword/numérico en lugar de text; use campos con doc_values habilitado si es necesario). En las pipeline aggregations, verifique que buckets_path coincida con los nombres reales de los buckets. Añada comprobaciones de null/tipo en las agregaciones con scripts. Corrija el mapping con tipos de campo adecuados.","tags":["aggregation","execution","fielddata","keyword","pipeline","mapping"]},{"code":"failed_node_exception","title":"failed_node_exception","severity":"danger","meaning":"Durante una operación a nivel de clúster (p. ej., búsqueda, estadísticas, comando de administración), un nodo concreto del clúster no pudo procesar la solicitud ni responder a ella. El nodo coordinador envuelve el fallo de ese nodo en esta excepción.","cause":"El nodo falló, se desconectó de la red, no pudo responder por sobrecarga/pausas de GC, problemas de disco/recursos o un error de shard en ese nodo. También puede aparecer de forma transitoria durante la reconfiguración del clúster (nodos que salen/entran).","fix":"Use caused_by para determinar en qué nodo y por qué se produjo el fallo. Revise la salud del nodo: GET _cat/nodes, GET _nodes/stats (CPU, memoria, GC, disco). Si el nodo está bajo presión de recursos, revise la carga/los ajustes de heap; verifique la conectividad de red. Si el nodo abandonó el clúster, espere a que se reincorpore y a que se recupere la asignación de shards (GET _cluster/health).","tags":["node","cluster","network","gc","shard","resources"]},{"code":"elasticsearch_parse_exception","title":"elasticsearch_parse_exception","severity":"caution","meaning":"Elasticsearch encontró un error de análisis genérico al analizar el cuerpo de la solicitud (consulta JSON, ajustes o configuración). La solicitud no coincide estructuralmente con el formato esperado.","cause":"JSON mal formado/no válido (falta una coma, un corchete sin cerrar), campos mal colocados, tipos de valor inesperados (p. ej., texto donde se espera un número) o una expresión de tiempo/rango no válida. Un anidamiento incorrecto de una consulta DSL también produce esta excepción.","fix":"Pase el cuerpo de la solicitud por un validador JSON; revise corchetes, comillas y comas. Compruebe con la documentación del DSL de Elasticsearch que los campos estén en el lugar correcto y sean del tipo correcto. Use la indicación de posición/campo del mensaje de error para corregir la parte problemática. Si usa una biblioteca cliente, asegúrese de que la serialización produzca JSON válido.","tags":["parse","json","dsl","query","parsing","request"]}]},"health/oracle.json":{"engineId":"oracle","engine":"Oracle","order":1,"checks":[{"id":"version-uptime","group":"General","title":"Versión y tiempo de actividad","command":"SELECT i.instance_name, i.version_full, i.status, i.startup_time, ROUND(SYSDATE - i.startup_time, 2) AS uptime_days FROM v$instance i;","expect":"El estado de la instancia debe ser OPEN. Si startup_time es inesperadamente reciente (arranque reciente), puede haberse producido un reinicio no planificado; investíguelo. La columna version_full es de 18c+; en 12c y anteriores use la columna version.","severity":"safe"},{"id":"alertlog-errors","group":"General","title":"Errores críticos recientes en el alert log (ORA-)","command":"SELECT originating_timestamp, message_text FROM v$diag_alert_ext WHERE message_text LIKE '%ORA-%' AND originating_timestamp > SYSTIMESTAMP - INTERVAL '1' DAY ORDER BY originating_timestamp DESC FETCH FIRST 50 ROWS ONLY;","expect":"No debería devolverse ningún error ORA- de las últimas 24 horas. Entradas como ORA-00600, ORA-07445 y ORA-01578 (corrupción de bloques) requieren una acción inmediata.","severity":"danger"},{"id":"invalid-objects","group":"General","title":"Objetos INVALID","command":"SELECT owner, object_type, COUNT(*) AS invalid_count FROM dba_objects WHERE status = 'INVALID' GROUP BY owner, object_type ORDER BY invalid_count DESC;","expect":"Lo ideal es que no haya objetos INVALID. Si los hay, recompílelos con EXEC DBMS_UTILITY.COMPILE_SCHEMA o utlrp.sql; los objetos inválidos de SYS/SYSTEM tienen prioridad.","severity":"caution"},{"id":"tablespace-usage","group":"Capacidad","title":"Uso de tablespaces","command":"SELECT df.tablespace_name, ROUND(df.total_mb, 1) AS total_mb, ROUND(df.total_mb - NVL(fs.free_mb, 0), 1) AS used_mb, ROUND((df.total_mb - NVL(fs.free_mb, 0)) / df.total_mb * 100, 1) AS pct_used FROM (SELECT tablespace_name, SUM(bytes)/1024/1024 total_mb FROM dba_data_files GROUP BY tablespace_name) df LEFT JOIN (SELECT tablespace_name, SUM(bytes)/1024/1024 free_mb FROM dba_free_space GROUP BY tablespace_name) fs ON df.tablespace_name = fs.tablespace_name ORDER BY pct_used DESC;","expect":"Un pct_used superior al 85% es el umbral de advertencia, y superior al 90% crítico. Los tablespaces con autoextend desactivado tienen más riesgo de llenarse; añada un datafile o habilite autoextend.","severity":"danger"},{"id":"largest-segments","group":"Capacidad","title":"Segmentos más grandes (tablas/índices)","command":"SELECT owner, segment_name, segment_type, ROUND(bytes/1024/1024/1024, 2) AS size_gb FROM dba_segments ORDER BY bytes DESC FETCH FIRST 20 ROWS ONLY;","expect":"Muestra los segmentos que más espacio consumen. Un crecimiento inesperado puede indicar la necesidad de archivado/purga o de particionamiento.","severity":"safe"},{"id":"undo-temp-usage","group":"Capacidad","title":"Uso de los tablespaces UNDO y TEMP","command":"SELECT ts.tablespace_name, ts.contents, ROUND(SUM(ss.used_blocks) * ts.block_size / 1024 / 1024, 1) AS used_mb FROM v$sort_segment ss JOIN (SELECT tablespace_name, contents, block_size FROM dba_tablespaces WHERE contents = 'TEMPORARY') ts ON ss.tablespace_name = ts.tablespace_name GROUP BY ts.tablespace_name, ts.contents, ts.block_size UNION ALL SELECT tablespace_name, 'UNDO', ROUND(SUM(bytes)/1024/1024, 1) FROM dba_undo_extents WHERE status = 'ACTIVE' GROUP BY tablespace_name;","expect":"Si el uso de TEMP llega constantemente al techo, hay operaciones grandes de sort/hash. Si el espacio UNDO activo no puede satisfacer la retención, hay riesgo de ORA-01555 (snapshot too old).","severity":"caution"},{"id":"sessions-vs-limit","group":"Capacidad","title":"Número de sesiones frente al límite","command":"SELECT resource_name, current_utilization, max_utilization, limit_value FROM v$resource_limit WHERE resource_name IN ('sessions','processes','transactions');","expect":"Si current_utilization se acerca a limit_value (sobre todo por encima del 85%), pueden rechazarse conexiones nuevas. max_utilization muestra el pico histórico; revise el parámetro processes.","severity":"caution"},{"id":"fra-usage","group":"Capacidad","title":"Uso del Flash Recovery Area (FRA)","command":"SELECT name, ROUND(space_limit/1024/1024/1024, 2) AS limit_gb, ROUND(space_used/1024/1024/1024, 2) AS used_gb, ROUND(space_used/space_limit*100, 1) AS pct_used FROM v$recovery_file_dest;","expect":"Cuando el FRA llega al 85% se escribe una advertencia en el alert log; al 100% se detiene la escritura de archive logs y la base de datos se cuelga (archiver hung, ORA-00257). Limpie backups/archivos de RMAN o aumente db_recovery_file_dest_size.","severity":"danger"},{"id":"buffer-cache-hit","group":"Rendimiento","title":"Tasa de aciertos del buffer cache","command":"SELECT ROUND((1 - (phy.value - dir.value) / (con.value + cur.value)) * 100, 2) AS buffer_hit_ratio FROM v$sysstat con, v$sysstat cur, v$sysstat phy, v$sysstat dir WHERE con.name = 'consistent gets' AND cur.name = 'db block gets' AND phy.name = 'physical reads' AND dir.name = 'physical reads direct';","expect":"En sistemas OLTP se espera normalmente un valor superior al 90%. Valores persistentemente bajos pueden indicar una SGA/buffer cache infradimensionada o una indexación deficiente; interprételo junto con los wait events, no de forma aislada.","severity":"safe"},{"id":"top-sql","group":"Rendimiento","title":"Sentencias SQL más costosas (tiempo transcurrido)","command":"SELECT sql_id, ROUND(elapsed_time/1000000, 1) AS elapsed_sec, executions, ROUND(elapsed_time/1000000/GREATEST(executions,1), 3) AS sec_per_exec, SUBSTR(sql_text, 1, 100) AS sql_text FROM v$sqlarea ORDER BY elapsed_time DESC FETCH FIRST 15 ROWS ONLY;","expect":"Consultas con el mayor tiempo total transcurrido. Las que tienen un sec_per_exec alto o un número de executions muy alto son candidatas a ajuste; examínelas con AWR/SQL Tuning Advisor.","severity":"safe"},{"id":"long-active-sessions","group":"Rendimiento","title":"Sesiones activas de larga duración","command":"SELECT s.sid, s.serial#, s.username, s.status, s.last_call_et AS active_seconds, s.event, SUBSTR(q.sql_text, 1, 80) AS sql_text FROM v$session s LEFT JOIN v$sqlarea q ON s.sql_id = q.sql_id WHERE s.status = 'ACTIVE' AND s.type = 'USER' AND s.last_call_et > 60 ORDER BY s.last_call_et DESC;","expect":"Las sesiones activas con un last_call_et (segundos) muy alto pueden estar atascadas o ejecutando una consulta pesada. La columna event muestra el recurso que se espera.","severity":"caution"},{"id":"blocking-sessions","group":"Rendimiento","title":"Sesiones bloqueantes y en espera (bloqueos)","command":"SELECT bs.sid AS blocker_sid, bs.username AS blocker_user, ws.sid AS waiter_sid, ws.username AS waiter_user, ws.seconds_in_wait, ws.event FROM v$session ws JOIN v$session bs ON ws.blocking_session = bs.sid WHERE ws.blocking_session IS NOT NULL ORDER BY ws.seconds_in_wait DESC;","expect":"No debería devolverse ninguna fila. Las sesiones bloqueantes provocan latencia/deadlocks en la aplicación; si blocker_sid mantiene un bloqueo durante mucho tiempo (seconds_in_wait grande), evalúelo con el equipo de aplicación y, si es necesario, ejecute ALTER SYSTEM KILL SESSION.","severity":"danger"},{"id":"rman-last-backup","group":"Backup y recuperación","title":"Estado del último backup RMAN correcto","command":"SELECT input_type, status, start_time, end_time, ROUND(output_bytes/1024/1024/1024, 2) AS output_gb FROM v$rman_backup_job_details ORDER BY start_time DESC FETCH FIRST 10 ROWS ONLY;","expect":"El último backup debe tener status = COMPLETED y un start_time lo bastante reciente para su política de RPO (p. ej., dentro de las últimas 24 horas). Las entradas FAILED o 'COMPLETED WITH WARNINGS' deben investigarse.","severity":"danger"},{"id":"archivelog-mode","group":"Backup y recuperación","title":"Modo archivelog y estado del archiver","command":"SELECT log_mode, force_logging, (SELECT COUNT(*) FROM v$archived_log WHERE first_time > SYSDATE - 1) AS logs_last_24h FROM v$database;","expect":"Una base de datos de producción debe tener log_mode = ARCHIVELOG (de lo contrario, la recuperación point-in-time no es posible). Si no se generaron archivos en las últimas 24 horas, el archiver puede estar atascado.","severity":"danger"},{"id":"locked-expired-accounts","group":"Seguridad","title":"Cuentas bloqueadas o caducadas","command":"SELECT username, account_status, lock_date, expiry_date, profile FROM dba_users WHERE account_status != 'OPEN' ORDER BY username;","expect":"Las cuentas críticas de aplicación en estado LOCKED/EXPIRED provocan interrupciones inesperadas. Que las cuentas por defecto (SCOTT, HR, etc.) estén bloqueadas/deshabilitadas es positivo. EXPIRED(GRACE) indica una caducidad de contraseña próxima.","severity":"caution"},{"id":"privileged-users","group":"Seguridad","title":"Usuarios con privilegios elevados (rol DBA)","command":"SELECT grantee, granted_role, admin_option FROM dba_role_privs WHERE granted_role IN ('DBA','PDB_DBA') AND grantee NOT IN ('SYS','SYSTEM') ORDER BY grantee;","expect":"El rol DBA solo deben tenerlo cuentas necesarias y conocidas. Los grantees inesperados o los que tienen admin_option = YES suponen un riesgo de escalada de privilegios; revíselos según el principio de mínimo privilegio. Para las cuentas con privilegios SYSDBA/SYSOPER, compruebe también: SELECT username, sysdba, sysoper FROM v$pwfile_users;","severity":"caution"},{"id":"default-password-users","group":"Seguridad","title":"Cuentas abiertas con contraseñas por defecto sin cambiar","command":"SELECT du.username, du.account_status, du.profile, CASE WHEN dp.username IS NOT NULL THEN 'YES' ELSE 'NO' END AS has_default_password FROM dba_users du LEFT JOIN (SELECT username FROM dba_users_with_defpwd) dp ON du.username = dp.username WHERE du.account_status = 'OPEN' ORDER BY du.username;","expect":"Las cuentas con has_default_password = YES deben cambiar la contraseña de inmediato o bloquearse (ALTER USER ... ACCOUNT LOCK). Para la política de vigencia de contraseñas, revise también PASSWORD_LIFE_TIME en DBA_PROFILES.","severity":"caution"},{"id":"dataguard-lag","group":"Replicación","title":"Retraso del standby de Data Guard","command":"SELECT name, value, unit, time_computed FROM v$dataguard_stats WHERE name IN ('apply lag','transport lag');","expect":"apply lag y transport lag deben estar lo más cerca posible de 0. Un retraso del orden de minutos/horas indica un problema de red o de MRP (Managed Recovery Process); el destino de failover está en riesgo.","severity":"danger"},{"id":"dataguard-mrp","group":"Replicación","title":"Estado del proceso de aplicación del standby (MRP)","command":"SELECT name, role, action, thread#, sequence#, block# FROM v$dataguard_process WHERE name LIKE 'MRP%' OR role LIKE 'RFS%' ORDER BY name;","expect":"En un physical standby, el proceso MRP0 debe aparecer con action APPLYING_LOG o WAIT_FOR_LOG. Si no hay MRP, la aplicación se ha detenido; si no hay RFS, no llegan logs desde el primary. v$dataguard_process es de 12.2+; en 12.1 y anteriores use v$managed_standby (ahora obsoleta; columnas process, status).","severity":"danger"}]},"health/postgres.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"checks":[{"id":"version-uptime","group":"General","title":"Versión y tiempo de actividad","command":"SELECT version() AS pg_version, pg_postmaster_start_time() AS started_at, date_trunc('second', now() - pg_postmaster_start_time()) AS uptime;","expect":"Muestra la versión y la hora de arranque. Si el tiempo de actividad es inesperadamente corto (arranque reciente), puede haberse producido un reinicio no planificado o un crash; revise los logs.","severity":"safe"},{"id":"recent-errors","group":"General","title":"Estadísticas de checkpoint","command":"-- PG17+:\nSELECT num_timed, num_requested, buffers_written, stats_reset FROM pg_stat_checkpointer;\n-- PG16 and earlier:\nSELECT checkpoints_timed, checkpoints_req, buffers_checkpoint, buffers_backend, stats_reset FROM pg_stat_bgwriter;","expect":"Si el número de checkpoints solicitados (num_requested / checkpoints_req) es alto en relación con los programados (num_timed / checkpoints_timed), el WAL se está llenando demasiado rápido; aumente max_wal_size. En PG17 los contadores pasaron de pg_stat_bgwriter a pg_stat_checkpointer.","severity":"caution"},{"id":"table-bloat","group":"General","title":"Tablas con bloat y proporción de dead tuples","command":"SELECT schemaname, relname, n_live_tup, n_dead_tup, CASE WHEN n_live_tup > 0 THEN round(n_dead_tup * 100.0 / n_live_tup, 1) ELSE 0 END AS dead_pct, last_autovacuum FROM pg_stat_user_tables WHERE n_dead_tup > 1000 ORDER BY n_dead_tup DESC LIMIT 20;","expect":"Las tablas con un dead_pct alto (p. ej., superior al 20%) tienen bloat; esto afecta al rendimiento de VACUUM y al uso de disco. Si last_autovacuum es muy antiguo, revise la configuración de autovacuum o ejecute un VACUUM (ANALYZE) manual.","severity":"caution"},{"id":"database-size","group":"Capacidad","title":"Tamaños de las bases de datos","command":"SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size, pg_database_size(datname) AS size_bytes FROM pg_database WHERE datistemplate = false ORDER BY pg_database_size(datname) DESC;","expect":"Muestra los tamaños de las bases de datos. Evalúe un crecimiento inesperado junto con la capacidad del disco (df -h); PostgreSQL detiene las operaciones de escritura cuando el disco se llena.","severity":"danger"},{"id":"largest-tables","group":"Capacidad","title":"Tablas más grandes (incluidos los índices)","command":"SELECT schemaname, relname, pg_size_pretty(pg_total_relation_size(relid)) AS total_size, pg_size_pretty(pg_relation_size(relid)) AS table_size, pg_size_pretty(pg_total_relation_size(relid) - pg_relation_size(relid)) AS index_toast_size FROM pg_catalog.pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 20;","expect":"Muestra las tablas que más espacio ocupan. Si index_toast_size es mucho mayor que el tamaño de la tabla, puede haber índices excesivos/no utilizados; considere el archivado o el particionamiento.","severity":"safe"},{"id":"temp-files","group":"Capacidad","title":"Uso de archivos temporales","command":"SELECT datname, temp_files, pg_size_pretty(temp_bytes) AS temp_size, stats_reset FROM pg_stat_database WHERE temp_files > 0 ORDER BY temp_bytes DESC;","expect":"Valores altos de temp_files/temp_bytes indican que work_mem es insuficiente y que las operaciones de sort/hash se vuelcan a disco. Considere aumentar work_mem u optimizar las consultas.","severity":"caution"},{"id":"connections-vs-limit","group":"Capacidad","title":"Número de conexiones frente a max_connections","command":"SELECT (SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'client backend') AS current_connections, (SELECT setting::int FROM pg_settings WHERE name = 'max_connections') AS max_connections, round((SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'client backend') * 100.0 / (SELECT setting::int FROM pg_settings WHERE name = 'max_connections'), 1) AS pct_used;","expect":"Solo se cuentan las conexiones de cliente (el filtro backend_type = 'client backend' es necesario porque pg_stat_activity incluye también procesos en segundo plano). Por encima del 80% de pct_used, las conexiones nuevas corren riesgo de ser rechazadas (sobre todo al alcanzar superuser_reserved_connections). Se recomienda un connection pooler (PgBouncer).","severity":"caution"},{"id":"wal-directory-size","group":"Capacidad","title":"Tamaño del directorio WAL y retención de replication slots","command":"SELECT (SELECT count(*) FROM pg_ls_waldir()) AS wal_file_count, pg_size_pretty((SELECT sum(size) FROM pg_ls_waldir())) AS wal_total_size, slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal FROM pg_replication_slots ORDER BY slot_name;","expect":"Los replication slots inactivos (active = false) hacen que el WAL se acumule y que el directorio pg_wal se llene. Si retained_wal es grande, verifique a quién pertenece el slot (slot_type, database, plugin): active = false también puede indicar un standby desconectado temporalmente o un slot de un suscriptor lógico/CDC (p. ej., Debezium). Si realmente no se usa, elimínelo con pg_drop_replication_slot; un standby que dependía del slot eliminado debe reconstruirse, y el lado del suscriptor/CDC debe resincronizarse. Para una protección permanente, fije un límite superior con max_slot_wal_keep_size (PG13+).","severity":"danger"},{"id":"cache-hit-ratio","group":"Rendimiento","title":"Tasa de aciertos del buffer cache","command":"SELECT sum(heap_blks_read) AS heap_read, sum(heap_blks_hit) AS heap_hit, round(sum(heap_blks_hit) * 100.0 / NULLIF(sum(heap_blks_hit) + sum(heap_blks_read), 0), 2) AS cache_hit_ratio FROM pg_statio_user_tables;","expect":"Para cargas OLTP, el objetivo suele ser superior al 99%. Un valor constantemente bajo puede indicar que shared_buffers es demasiado pequeño o que el conjunto de trabajo no cabe en memoria.","severity":"safe"},{"id":"top-queries","group":"Rendimiento","title":"Consultas más costosas (pg_stat_statements)","command":"SELECT round(total_exec_time::numeric, 1) AS total_ms, calls, round(mean_exec_time::numeric, 2) AS mean_ms, round((100 * total_exec_time / sum(total_exec_time) OVER ())::numeric, 1) AS pct, left(query, 100) AS query FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 15;","expect":"pg_stat_statements debe estar en shared_preload_libraries (requiere reinicio) e instalado en la base de datos (CREATE EXTENSION pg_stat_statements). Los nombres total_exec_time/mean_exec_time son de PG13+; PG12 usa total_time/mean_time. Las consultas que consumen más tiempo total son candidatas a ajuste; ejecute EXPLAIN ANALYZE en las que tienen un mean_ms alto.","severity":"safe"},{"id":"long-running-queries","group":"Rendimiento","title":"Consultas activas de larga duración","command":"SELECT pid, usename, state, now() - query_start AS duration, wait_event_type, wait_event, left(query, 80) AS query FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > interval '1 minute' AND backend_type = 'client backend' ORDER BY duration DESC;","expect":"Las consultas activas que llevan más de 1 minuto pueden estar atascadas o ser pesadas. wait_event muestra lo que se está esperando. Esta consulta solo lista las consultas activas; revise por separado las sesiones 'idle in transaction' (que impiden que VACUUM elimine las dead tuples) con state = 'idle in transaction' y xact_start.","severity":"caution"},{"id":"blocking-locks","group":"Rendimiento","title":"Sesiones bloqueantes y bloqueos en espera","command":"SELECT blocked.pid AS blocked_pid, blocked.usename AS blocked_user, blocking.pid AS blocking_pid, blocking.usename AS blocking_user, now() - blocked.query_start AS blocked_duration, left(blocked.query, 60) AS blocked_query FROM pg_stat_activity blocked JOIN pg_stat_activity blocking ON blocking.pid = ANY(pg_blocking_pids(blocked.pid)) WHERE cardinality(pg_blocking_pids(blocked.pid)) > 0;","expect":"No debería devolverse ninguna fila. Las sesiones bloqueantes (blocking_pid) provocan retrasos y timeouts en la aplicación; investigue una transacción que bloquea durante mucho tiempo y, si es necesario, termínela con pg_terminate_backend.","severity":"danger"},{"id":"archiver-status","group":"Backup y recuperación","title":"Salud del archivado de WAL (pg_stat_archiver)","command":"SELECT archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, now() - last_archived_time AS since_last_archive FROM pg_stat_archiver;","expect":"failed_count debe ser 0 y last_failed_time debe ser anterior a last_archived_time. Si since_last_archive es muy grande, el archivado se ha detenido (fallo de archive_command); la cadena de backups para PITR está rota.","severity":"danger"},{"id":"last-basebackup","group":"Backup y recuperación","title":"Antigüedad del último backup y del archivo de WAL","command":"-- Base backup time is not stored in the database; read it from the backup tool: pgbackrest info / barman list-backup <server>\nSELECT pg_is_in_recovery() AS in_recovery, last_archived_wal, last_archived_time, now() - last_archived_time AS since_last_archive, failed_count, last_failed_time FROM pg_stat_archiver;","expect":"Verifique la hora del último base backup en la herramienta de backup (logs de jobs de pgBackRest / Barman / pg_basebackup); debe ser lo bastante reciente para su política de RPO. Para PITR, el archivo de WAL también debe ser continuo: si since_last_archive es grande o failed_count aumenta, la cadena de archivos puede estar rota.","severity":"danger"},{"id":"locked-expired-roles","group":"Seguridad","title":"Roles de login caducados o a punto de caducar","command":"SELECT rolname, rolcanlogin, rolvaliduntil, rolconnlimit FROM pg_roles WHERE rolcanlogin = true AND (rolvaliduntil IS NOT NULL AND rolvaliduntil < now() + interval '7 days') ORDER BY rolvaliduntil;","expect":"Los roles de aplicación cuyo rolvaliduntil ha pasado no pueden iniciar sesión, lo que provoca indisponibilidad. Planifique con antelación la renovación de las contraseñas que caducan en los próximos 7 días.","severity":"caution"},{"id":"superuser-roles","group":"Seguridad","title":"Roles con privilegios elevados (SUPERUSER / privilegios de creación)","command":"SELECT rolname, rolsuper, rolcreaterole, rolcreatedb, rolbypassrls FROM pg_roles WHERE rolsuper = true OR rolcreaterole = true OR rolbypassrls = true ORDER BY rolsuper DESC, rolname;","expect":"SUPERUSER solo debe existir en cuentas necesarias y conocidas. Los roles con rolbypassrls = true omiten la seguridad a nivel de fila (RLS); los privilegios elevados inesperados deben revocarse según el principio de mínimo privilegio.","severity":"caution"},{"id":"hba-trust-auth","group":"Seguridad","title":"Reglas de autenticación débil (trust)","command":"SELECT type, database, user_name, address, auth_method FROM pg_hba_file_rules WHERE auth_method IN ('trust','password') ORDER BY line_number;","expect":"auth_method = trust permite el acceso sin contraseña y es peligroso en producción; 'password' envía la contraseña en texto plano. Sustitúyalas por scram-sha-256. (Requiere PG 10+.)","severity":"danger"},{"id":"replication-lag","group":"Replicación","title":"Retraso de la streaming replication (lado primary)","command":"SELECT client_addr, state, sync_state, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn)) AS sent_lag, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS replay_lag, write_lag, flush_lag, replay_lag AS replay_time_lag FROM pg_stat_replication ORDER BY client_addr;","expect":"Ejecútelo en el primary. state debe ser streaming y replay_lag (bytes/tiempo) debe ser pequeño. Si el standby no aparece en absoluto, la conexión se ha caído; si sync_state = async, la replicación síncrona no funciona como se espera.","severity":"danger"},{"id":"replica-lag-standby","group":"Replicación","title":"Retraso de la replicación (lado standby)","command":"SELECT pg_is_in_recovery() AS in_recovery, CASE WHEN pg_is_in_recovery() THEN date_trunc('second', now() - pg_last_xact_replay_timestamp()) ELSE NULL END AS replication_delay, pg_last_wal_receive_lsn() AS receive_lsn, pg_last_wal_replay_lsn() AS replay_lsn;","expect":"Ejecútelo en el servidor standby. in_recovery debe ser true. Si replication_delay está en el rango de minutos/horas, puede haber retraso de aplicación; sin embargo, este valor también crece cuando no hay escrituras en el primary (falsa alarma), así que interprételo junto con la diferencia de LSN; si la distancia entre receive_lsn y replay_lsn es grande, la aplicación del WAL no da abasto.","severity":"danger"}]},"health/mysql.json":{"engineId":"mysql","engine":"MySQL","order":3,"checks":[{"id":"version-uptime","group":"General","title":"Versión y tiempo de actividad","command":"SELECT VERSION() AS mysql_version, VARIABLE_VALUE AS uptime_seconds, ROUND(VARIABLE_VALUE/86400, 2) AS uptime_days FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Uptime';","expect":"Muestra la versión y el tiempo de actividad en segundos. Si uptime_seconds es inesperadamente pequeño, puede haberse producido un reinicio no planificado o un crash; revise el error log.","severity":"safe"},{"id":"error-log-status","group":"General","title":"Entradas recientes del error log (performance_schema)","command":"SELECT logged, prio, error_code, subsystem, LEFT(data, 120) AS message FROM performance_schema.error_log WHERE prio IN ('Error','Warning') ORDER BY logged DESC LIMIT 30;","expect":"Muestra los errores y advertencias recientes (requiere MySQL 8.0+). Investigue las entradas con prio = 'Error' (sobre todo errores de InnoDB, de bloqueos y de disco). En versiones anteriores, revise el archivo log_error del servidor.","severity":"danger"},{"id":"crashed-tables","group":"General","title":"Tablas dañadas / tablas que conviene comprobar","command":"SELECT table_schema, table_name, engine, table_rows, update_time FROM information_schema.tables WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','sys') AND engine IS NOT NULL ORDER BY table_schema, table_name;","expect":"Lista el engine y la hora de la última actualización de cada tabla. Las tablas MyISAM pueden corromperse; ante la duda, verifique la integridad con CHECK TABLE ... o mysqlcheck. Se recomienda InnoDB para datos críticos.","severity":"caution"},{"id":"schema-sizes","group":"Capacidad","title":"Tamaños de los esquemas (bases de datos)","command":"SELECT table_schema, ROUND(SUM(data_length + index_length)/1024/1024, 1) AS size_mb, ROUND(SUM(data_free)/1024/1024, 1) AS free_mb FROM information_schema.tables GROUP BY table_schema ORDER BY SUM(data_length + index_length) DESC;","expect":"Muestra los tamaños de los esquemas y el espacio recuperable data_free. Evalúelo junto con la capacidad del disco; si free_mb es alto, se puede recuperar espacio con OPTIMIZE TABLE.","severity":"danger"},{"id":"largest-tables","group":"Capacidad","title":"Tablas más grandes (incluidos data_free / fragmentación)","command":"SELECT table_schema, table_name, engine, ROUND((data_length + index_length)/1024/1024, 1) AS total_mb, ROUND(data_free/1024/1024, 1) AS data_free_mb, table_rows FROM information_schema.tables WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','sys') ORDER BY (data_length + index_length) DESC LIMIT 20;","expect":"Muestra las tablas más grandes. Las tablas con un data_free_mb alto están fragmentadas; se puede recuperar espacio con OPTIMIZE TABLE. Considere el particionamiento para tablas muy grandes.","severity":"safe"},{"id":"innodb-tablespace","group":"Capacidad","title":"Uso de tablespaces InnoDB y del espacio temporal","command":"SELECT FILE_NAME, TABLESPACE_NAME, ROUND(TOTAL_EXTENTS * EXTENT_SIZE / 1024 / 1024, 1) AS total_mb, ROUND((TOTAL_EXTENTS - FREE_EXTENTS) * EXTENT_SIZE / 1024 / 1024, 1) AS used_mb FROM information_schema.files WHERE FILE_TYPE = 'TABLESPACE' ORDER BY TOTAL_EXTENTS DESC LIMIT 20;","expect":"Muestra el grado de ocupación de los datafiles de InnoDB. Si innodb_file_per_table está desactivado, ibdata1 sigue creciendo y nunca se reduce. Para el crecimiento del temp tablespace, revise también la salida de SHOW ENGINE INNODB STATUS.","severity":"caution"},{"id":"connections-vs-limit","group":"Capacidad","title":"Número de conexiones y límite max_connections","command":"SELECT (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Threads_connected') AS connected, (SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'max_connections') AS max_connections, (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Max_used_connections') AS max_used, (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Aborted_connects') AS aborted_connects;","expect":"Si max_used se acerca a max_connections (por encima del 85%), hay riesgo de errores 'Too many connections'. Un aborted_connects alto apunta a problemas de autenticación/red; use un pool de conexiones.","severity":"caution"},{"id":"binlog-size","group":"Capacidad","title":"Archivos de binary log y tamaño total","command":"SHOW BINARY LOGS;","expect":"Lista los archivos binlog y sus tamaños. Si el tamaño total presiona el espacio en disco, revise binlog_expire_logs_seconds (expire_logs_days antes de 8.0; esa variable quedó obsoleta en 8.0 y se eliminó en 8.4); si se usa replicación, no purgue antes de que las réplicas los hayan leído.","severity":"caution"},{"id":"buffer-pool-hit","group":"Rendimiento","title":"Tasa de aciertos del buffer pool de InnoDB","command":"SELECT (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests') AS read_requests, (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') AS disk_reads, ROUND((1 - (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') / (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests')) * 100, 3) AS hit_ratio_pct;","expect":"El objetivo de hit_ratio_pct suele ser superior al 99%. Si se mantiene bajo, innodb_buffer_pool_size es insuficiente; considere aumentarlo hasta ~70-80% de la RAM en un servidor dedicado (menos en un servidor compartido).","severity":"safe"},{"id":"top-queries","group":"Rendimiento","title":"Consultas más costosas (sys.statement_analysis)","command":"SELECT query, db, exec_count, total_latency, avg_latency, rows_sent_avg, rows_examined_avg FROM sys.statement_analysis ORDER BY total_latency DESC LIMIT 15;","expect":"El esquema sys y performance_schema deben estar habilitados. Las consultas que consumen más total_latency son candidatas a ajuste. Si rows_examined_avg es mucho mayor que rows_sent_avg, falta un índice; examínelo con EXPLAIN.","severity":"safe"},{"id":"long-running-queries","group":"Rendimiento","title":"Consultas activas de larga duración","command":"SELECT id, user, host, db, command, time AS seconds, state, LEFT(info, 80) AS query FROM information_schema.processlist WHERE command != 'Sleep' AND time > 60 ORDER BY time DESC;","expect":"Las consultas que llevan más de 60 segundos pueden estar atascadas o ser pesadas. La columna state muestra lo que se está esperando. Las sesiones largas en estado 'Sleep' también agotan el pool de conexiones; use KILL <id> si es necesario.","severity":"caution"},{"id":"blocking-locks","group":"Rendimiento","title":"Sesiones bloqueantes y bloqueos en espera","command":"SELECT r.trx_id AS waiting_trx, r.trx_mysql_thread_id AS waiting_thread, TIMESTAMPDIFF(SECOND, r.trx_wait_started, NOW()) AS wait_seconds, LEFT(r.trx_query, 60) AS waiting_query, b.trx_mysql_thread_id AS blocking_thread, LEFT(b.trx_query, 60) AS blocking_query FROM performance_schema.data_lock_waits w JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_engine_transaction_id JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_engine_transaction_id ORDER BY wait_seconds DESC;","expect":"No debería devolver filas. Una transacción bloqueante (blocking_thread) provoca latencia en la aplicación y errores de lock wait timeout. Investigue una transacción que bloquea durante mucho tiempo y termínela con KILL si es necesario. (Sintaxis de MySQL 8.0.)","severity":"danger"},{"id":"binlog-enabled","group":"Backup y recuperación","title":"Configuración del binary log y de recuperación","command":"SELECT (SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'log_bin') AS log_bin, (SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'binlog_format') AS binlog_format, (SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'sync_binlog') AS sync_binlog, (SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'innodb_flush_log_at_trx_commit') AS flush_log_commit;","expect":"log_bin debe estar en ON para la recuperación point-in-time. Para la durabilidad se recomiendan sync_binlog = 1 e innodb_flush_log_at_trx_commit = 1 (de lo contrario, pueden perderse transacciones en un crash). binlog_format = ROW es la opción más segura para la replicación.","severity":"danger"},{"id":"last-backup-note","group":"Backup y recuperación","title":"Hora del último backup (binlog / indicador de la herramienta)","command":"SELECT (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Uptime') AS uptime_seconds, (SELECT MAX(update_time) FROM information_schema.tables WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','sys')) AS last_data_change;","expect":"MySQL no registra por sí mismo el estado de los backups; verifique la hora del último backup correcto en los logs de la herramienta mysqldump / MySQL Shell dump / Percona XtraBackup (mysqlpump quedó obsoleto en 8.0.34 y se eliminó en 8.4). El backup debe ser tan reciente como exija su política de RPO (p. ej., dentro de las últimas 24 horas).","severity":"danger"},{"id":"locked-expired-accounts","group":"Seguridad","title":"Cuentas bloqueadas o con contraseñas caducadas","command":"SELECT user, host, account_locked, password_expired, password_lifetime FROM mysql.user ORDER BY user, host;","expect":"Las cuentas críticas de aplicación con account_locked = 'Y' o password_expired = 'Y' provocan interrupciones. Si password_lifetime es NULL y la caducidad global está desactivada, la contraseña nunca caduca; esto puede ser una laguna de la política.","severity":"caution"},{"id":"anonymous-nopassword-users","group":"Seguridad","title":"Usuarios anónimos y usuarios sin contraseña","command":"SELECT user, host, plugin, IF(authentication_string = '', 'EMPTY_PASSWORD', 'set') AS password_status FROM mysql.user WHERE user = '' OR authentication_string = '' ORDER BY host;","expect":"No debería devolver filas. Los usuarios anónimos (user = '') y las cuentas sin contraseña son un grave agujero de seguridad; elimínelos con mysql_secure_installation o asígneles contraseñas robustas.","severity":"danger"},{"id":"super-privilege-users","group":"Seguridad","title":"Usuarios con privilegios elevados (SUPER / GRANT)","command":"SELECT grantee, privilege_type FROM information_schema.user_privileges WHERE privilege_type IN ('SUPER','GRANT OPTION','SHUTDOWN','FILE','CREATE USER') ORDER BY grantee, privilege_type;","expect":"Privilegios como SUPER, FILE y GRANT OPTION solo deben existir en cuentas necesarias y conocidas. Los grantees inesperados suponen un riesgo de escalada de privilegios; preste especial atención a las cuentas con privilegios elevados definidas con % (cualquier host).","severity":"caution"},{"id":"replica-status","group":"Replicación","title":"Estado y retraso de la réplica (standby)","command":"SHOW REPLICA STATUS;","expect":"Ejecútelo en la réplica (MySQL 8.0.22+). Replica_IO_Running y Replica_SQL_Running deben ser ambos 'Yes'. Seconds_Behind_Source debe ser pequeño; si es NULL, la replicación se ha detenido. Revise el campo Last_Error. (SHOW SLAVE STATUS en versiones anteriores.)","severity":"danger"},{"id":"replication-threads","group":"Replicación","title":"Estado de la conexión y del applier de replicación (performance_schema)","command":"SELECT c.channel_name, c.service_state AS io_state, a.service_state AS sql_state, c.last_error_message AS io_error, w.last_error_message AS sql_error FROM performance_schema.replication_connection_status c JOIN performance_schema.replication_applier_status a ON c.channel_name = a.channel_name LEFT JOIN performance_schema.replication_applier_status_by_coordinator w ON w.channel_name = a.channel_name;","expect":"io_state y sql_state deben ser 'ON'. Si io_error/sql_error tiene valor, la replicación ha fallado y se ha detenido; en configuraciones multi-source, cada channel_name debe estar sano por separado.","severity":"danger"}]},"health/mssql.json":{"engineId":"mssql","engine":"SQL Server","order":4,"checks":[{"id":"version-uptime","group":"General","title":"Versión y tiempo de actividad","command":"SELECT @@VERSION AS version, sqlserver_start_time AS start_time, DATEDIFF(HOUR, sqlserver_start_time, GETDATE()) AS uptime_hours FROM sys.dm_os_sys_info;","expect":"Verifique la versión y el nivel de parches. Si el tiempo de actividad es muy corto (menos de unas horas), puede haberse producido un reinicio inesperado; revise el error log de SQL Server.","severity":"safe"},{"id":"database-states","group":"General","title":"Bases de datos en estado problemático (SUSPECT / RECOVERY_PENDING)","command":"SELECT name AS database_name, state_desc AS state, recovery_model_desc AS recovery_model FROM sys.databases WHERE state_desc <> N'ONLINE' ORDER BY name;","expect":"Todas las bases de datos de producción deben estar ONLINE. SUSPECT, RECOVERY_PENDING o EMERGENCY requieren una acción inmediata; inicie la recuperación y revise el error log.","severity":"danger"},{"id":"recent-errors","group":"General","title":"Errores significativos recientes (ring buffer de memoria/errores)","command":"SELECT TOP 20 CAST(record AS XML).value('(//Record/@id)[1]', 'int') AS record_id, DATEADD(ms, timestamp - (SELECT ms_ticks FROM sys.dm_os_sys_info), GETDATE()) AS event_time, CAST(record AS XML) AS record_xml FROM sys.dm_os_ring_buffers WHERE ring_buffer_type = N'RING_BUFFER_EXCEPTION' ORDER BY timestamp DESC;","expect":"Revise los registros recientes de excepciones/errores. Los errores 823/824/825 (consistencia de E/S) recurrentes apuntan a corrupción del disco. Para más detalle, lea también el error log del servidor con xp_readerrorlog.","severity":"caution"},{"id":"data-log-file-usage","group":"Capacidad","title":"Uso de los archivos de datos y de log","command":"SELECT DB_NAME() AS database_name, name AS logical_name, type_desc AS file_type, size/128.0 AS allocated_mb, CAST(FILEPROPERTY(name, 'SpaceUsed') AS INT)/128.0 AS used_mb, (size - CAST(FILEPROPERTY(name, 'SpaceUsed') AS INT))/128.0 AS free_mb, CASE WHEN max_size = -1 THEN N'Unlimited' WHEN max_size = 268435456 THEN N'2 TB (log)' ELSE CAST(max_size/128 AS VARCHAR(20)) + N' MB' END AS max_size_limit FROM sys.database_files;","expect":"Ejecútelo en cada base de datos. Si el espacio usado está muy cerca del asignado (85%+) y el autogrowth está limitado, planifique el crecimiento o añada disco. Si el archivo de log está constantemente lleno, revise la configuración de backup/modelo de recuperación.","severity":"caution"},{"id":"largest-tables","group":"Capacidad","title":"Tablas más grandes (tamaño y número de filas)","command":"SELECT TOP 20 s.name AS schema_name, t.name AS table_name, p.rows AS row_count, SUM(a.total_pages)*8/1024 AS total_mb, SUM(a.used_pages)*8/1024 AS used_mb FROM sys.tables t JOIN sys.schemas s ON t.schema_id = s.schema_id JOIN sys.indexes i ON t.object_id = i.object_id JOIN sys.partitions p ON i.object_id = p.object_id AND i.index_id = p.index_id JOIN sys.allocation_units a ON p.partition_id = a.container_id WHERE i.index_id <= 1 GROUP BY s.name, t.name, p.rows ORDER BY total_mb DESC;","expect":"Identifique las tablas que más espacio consumen. Un crecimiento inesperado puede indicar la necesidad de archivado, mantenimiento de índices o particionamiento.","severity":"safe"},{"id":"tempdb-usage","group":"Capacidad","title":"Uso de espacio en tempdb","command":"SELECT SUM(user_object_reserved_page_count)*8/1024 AS user_objects_mb, SUM(internal_object_reserved_page_count)*8/1024 AS internal_objects_mb, SUM(version_store_reserved_page_count)*8/1024 AS version_store_mb, SUM(unallocated_extent_page_count)*8/1024 AS free_mb FROM sys.dm_db_file_space_usage;","expect":"Si tempdb está a punto de quedarse sin espacio libre (unallocated bajo), las consultas pueden fallar. Si el version store sigue creciendo, hay transacciones abiertas de larga duración; investíguelas con sys.dm_tran_active_snapshot_database_transactions.","severity":"caution"},{"id":"connection-count","group":"Capacidad","title":"Número de conexiones y distribución","command":"SELECT DB_NAME(dbid) AS database_name, COUNT(*) AS connection_count, SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END) AS active FROM sys.sysprocesses WHERE dbid > 0 GROUP BY dbid ORDER BY connection_count DESC;","expect":"Haga seguimiento del número de conexiones por aplicación/base de datos. Un número inusualmente alto de conexiones desde una sola aplicación puede indicar una fuga en el pool de conexiones. No se acerque al límite de max worker threads.","severity":"safe"},{"id":"vlf-count","group":"Capacidad","title":"Número de VLF del archivo de log (fragmentación)","command":"SELECT DB_NAME(database_id) AS database_name, COUNT(*) AS vlf_count FROM sys.dm_db_log_info(DB_ID()) GROUP BY database_id;","expect":"Un número alto de VLF (más de unos cientos, p. ej., 500+) significa que el archivo de log creció en incrementos muy pequeños; esto ralentiza la recuperación y el rendimiento de los backups. Reduzca el log y vuelva a hacerlo crecer en un único paso grande.","severity":"caution"},{"id":"buffer-cache-ple","group":"Rendimiento","title":"Buffer Cache Hit Ratio y Page Life Expectancy","command":"SELECT (SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name = 'Buffer cache hit ratio') AS hit_ratio_raw, (SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name = 'Buffer cache hit ratio base') AS hit_ratio_base, (SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name = 'Page life expectancy' AND object_name LIKE '%Buffer Manager%') AS page_life_expectancy_sec;","expect":"El buffer cache hit ratio debería ser idealmente 99%+ (hit_ratio_raw / hit_ratio_base). Si Page Life Expectancy es constantemente bajo (regla práctica: por debajo de 300 s por cada 4 GB por nodo NUMA), hay presión de memoria; se necesita más RAM u optimizar las consultas.","severity":"caution"},{"id":"expensive-queries","group":"Rendimiento","title":"Consultas más costosas (CPU y lecturas)","command":"SELECT TOP 15 qs.total_worker_time/1000 AS total_cpu_ms, qs.execution_count AS execution_count, qs.total_logical_reads AS total_logical_reads, qs.total_worker_time/qs.execution_count/1000 AS avg_cpu_ms, SUBSTRING(st.text, (qs.statement_start_offset/2)+1, ((CASE qs.statement_end_offset WHEN -1 THEN DATALENGTH(st.text) ELSE qs.statement_end_offset END - qs.statement_start_offset)/2)+1) AS query_text FROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st ORDER BY qs.total_worker_time DESC;","expect":"Identifique las consultas más costosas por CPU y lecturas lógicas. Las consultas con muchas lecturas son señal de índices que faltan; revise sus planes de ejecución y añada índices o reescriba las consultas.","severity":"safe"},{"id":"blocking-sessions","group":"Rendimiento","title":"Sesiones bloqueantes","command":"SELECT r.session_id AS blocked_session, r.blocking_session_id AS blocking_session, r.wait_type AS wait_type, r.wait_time/1000 AS wait_sec, r.status AS status, t.text AS query FROM sys.dm_exec_requests r CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t WHERE r.blocking_session_id <> 0 ORDER BY r.wait_time DESC;","expect":"Una sesión bloqueante (blocking_session_id <> 0) significa que las consultas se hacen esperar entre sí. Un bloqueo prolongado provoca timeouts en la aplicación; encuentre el head blocker y considere terminar la transacción larga.","severity":"danger"},{"id":"wait-stats","group":"Rendimiento","title":"Principales estadísticas de espera (wait stats)","command":"SELECT TOP 15 wait_type AS wait_type, waiting_tasks_count AS waiting_tasks, wait_time_ms/1000 AS total_wait_sec, (wait_time_ms - signal_wait_time_ms)/1000 AS resource_wait_sec, signal_wait_time_ms/1000 AS cpu_wait_sec FROM sys.dm_os_wait_stats WHERE wait_type NOT IN (N'CLR_SEMAPHORE', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'LAZYWRITER_SLEEP', N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'WAITFOR', N'BROKER_TASK_STOP', N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE', N'CHKPT', N'REQUEST_FOR_DEADLOCK_SEARCH', N'XE_TIMER_EVENT', N'XE_DISPATCHER_WAIT', N'XE_LIVE_TARGET_TVF', N'BROKER_TO_FLUSH', N'DISPATCHER_QUEUE_SEMAPHORE', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_WORK_QUEUE', N'HADR_TIMER_TASK', N'HADR_CLUSAPI_CALL', N'HADR_LOGCAPTURE_WAIT', N'HADR_NOTIFICATION_DEQUEUE', N'DIRTY_PAGE_POLL', N'SP_SERVER_DIAGNOSTICS_SLEEP', N'SOS_WORK_DISPATCHER', N'LOGMGR_QUEUE', N'ONDEMAND_TASK_QUEUE', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'PWAIT_EXTENSIBILITY_CLEANUP_TASK', N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'STARTUP_DEPENDENCY_MANAGER', N'PREEMPTIVE_XE_DISPATCHER', N'PVS_PREALLOCATE', N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'WAIT_XTP_HOST_WAIT', N'PARALLEL_REDO_DRAIN_WORKER', N'PARALLEL_REDO_LOG_CACHE', N'PARALLEL_REDO_TRAN_LIST', N'PARALLEL_REDO_WORKER_SYNC', N'PARALLEL_REDO_WORKER_WAIT_WORK') AND wait_type NOT LIKE N'SLEEP[_]%' AND wait_type NOT LIKE N'QDS[_]%' AND waiting_tasks_count > 0 ORDER BY wait_time_ms DESC;","expect":"Interprete los tipos de espera dominantes: PAGEIOLATCH = cuello de botella de E/S de disco, CXPACKET/CXCONSUMER = paralelismo, LCK_M = bloqueos, WRITELOG = disco de log, RESOURCE_SEMAPHORE = memoria. Un porcentaje alto de signal wait indica presión de CPU.","severity":"safe"},{"id":"last-backup","group":"Backup y recuperación","title":"Horas de los últimos backups (full / differential / log)","command":"SELECT d.name AS database_name, d.recovery_model_desc AS recovery_model, MAX(CASE WHEN b.type = 'D' THEN b.backup_finish_date END) AS last_full_backup, MAX(CASE WHEN b.type = 'I' THEN b.backup_finish_date END) AS last_differential, MAX(CASE WHEN b.type = 'L' THEN b.backup_finish_date END) AS last_log_backup FROM sys.databases d LEFT JOIN msdb.dbo.backupset b ON d.name = b.database_name WHERE d.database_id > 4 GROUP BY d.name, d.recovery_model_desc ORDER BY last_full_backup;","expect":"Toda base de datos de producción debe tener un backup completo reciente. Si last_full_backup es NULL o muy antiguo (más allá del RPO), el job de backup está fallando. En el modelo de recuperación FULL, sin backups de log el archivo de log sigue creciendo.","severity":"danger"},{"id":"recovery-model","group":"Backup y recuperación","title":"Coherencia del modelo de recuperación","command":"SELECT name AS database_name, recovery_model_desc AS recovery_model, log_reuse_wait_desc AS log_reuse_wait FROM sys.databases WHERE database_id > 4 ORDER BY name;","expect":"Las bases de datos críticas deben usar FULL para la recuperación point-in-time. log_reuse_wait_desc = LOG_BACKUP significa que hay un backup de log pendiente; ACTIVE_TRANSACTION apunta a una transacción de larga duración.","severity":"caution"},{"id":"sysadmin-members","group":"Seguridad","title":"Miembros del rol sysadmin","command":"SELECT p.name AS login_name, p.type_desc AS type, p.is_disabled AS disabled FROM sys.server_role_members rm JOIN sys.server_principals r ON rm.role_principal_id = r.principal_id JOIN sys.server_principals p ON rm.member_principal_id = p.principal_id WHERE r.name = N'sysadmin' ORDER BY p.name;","expect":"La pertenencia a sysadmin debe minimizarse. Los logins inesperados (sobre todo cuentas de aplicación o usuarios individuales) suponen un riesgo de escalada de privilegios; aplique el principio de mínimo privilegio.","severity":"danger"},{"id":"sa-and-password-policy","group":"Seguridad","title":"Cuenta sa y logins con la política de contraseñas deshabilitada","command":"SELECT name AS login_name, is_disabled AS disabled, is_policy_checked AS password_policy_on, is_expiration_checked AS expiration_check, LOGINPROPERTY(name, 'PasswordLastSetTime') AS last_password_change FROM sys.sql_logins WHERE is_policy_checked = 0 OR name = N'sa' ORDER BY name;","expect":"La cuenta sa debe estar deshabilitada o renombrada. Los logins SQL con is_policy_checked = 0 conllevan riesgo de contraseñas débiles/antiguas; habilite la política de contraseñas (CHECK_POLICY).","severity":"caution"},{"id":"alwayson-sync","group":"Replicación","title":"Estado de sincronización y colas de Always On","command":"SELECT ag.name AS availability_group, ar.replica_server_name AS replica_server, DB_NAME(drs.database_id) AS database_name, drs.synchronization_state_desc AS sync_state, drs.synchronization_health_desc AS health, drs.log_send_queue_size AS log_send_queue_kb, drs.redo_queue_size AS redo_queue_kb FROM sys.dm_hadr_database_replica_states drs JOIN sys.availability_replicas ar ON drs.replica_id = ar.replica_id JOIN sys.availability_groups ag ON ar.group_id = ag.group_id ORDER BY ag.name, replica_server;","expect":"synchronization_state_desc debe ser SYNCHRONIZED (síncrono) o SYNCHRONIZING (asíncrono), y synchronization_health debe ser HEALTHY. Si log_send_queue o redo_queue crecen, la réplica se está quedando atrás; aumenta el riesgo de pérdida de datos/RTO en un failover.","severity":"caution"}]},"health/mongodb.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"checks":[{"id":"version-uptime","group":"General","title":"Versión y tiempo de actividad","command":"db.serverStatus().version; db.serverStatus().uptime","expect":"Verifique que la versión tiene soporte y está parcheada. uptime está en segundos; si es muy pequeño (unos pocos miles de segundos), se ha producido un reinicio inesperado; revise los logs de mongod.","severity":"safe"},{"id":"replset-status","group":"General","title":"Estado de los miembros del replica set (health)","command":"rs.status().members.forEach(function(m) { print(m.name + \" | \" + m.stateStr + \" | health=\" + m.health); })","expect":"Todos los miembros deben tener health=1 y debe haber exactamente un PRIMARY. health=0 o los estados (not reachable/healthy), DOWN y RECOVERING indican un fallo. Si hay más de un PRIMARY o ninguno, hay un problema de elección.","severity":"danger"},{"id":"dbstats-sizes","group":"Capacidad","title":"Tamaños de las bases de datos (data / storage / index)","command":"db.stats(1024*1024)","expect":"Vigile dataSize, storageSize e indexSize (MB). Si storageSize es mucho mayor que dataSize, puede haber fragmentación. Mantener indexSize dentro de la RAM (caché de WiredTiger) es crítico para el rendimiento.","severity":"safe"},{"id":"disk-space","group":"Capacidad","title":"Uso de espacio en disco (fsUsedSize / fsTotalSize)","command":"var st = db.stats(); printjson({fsUsedSize_MB: (st.fsUsedSize/1048576).toFixed(0), fsTotalSize_MB: (st.fsTotalSize/1048576).toFixed(0), usedPct: (100*st.fsUsedSize/st.fsTotalSize).toFixed(1)})","expect":"Si la proporción de fsUsedSize sobre fsTotalSize supera el 85%, el disco está a punto de llenarse; MongoDB detiene las escrituras cuando el disco está lleno. Planifique añadir disco o archivar datos.","severity":"caution"},{"id":"largest-collections","group":"Capacidad","title":"Colecciones más grandes","command":"db.getCollectionNames().map(function(c) { var s = db.getCollection(c).stats(); return {collection: c, size_MB: (s.size/1048576).toFixed(1), storage_MB: (s.storageSize/1048576).toFixed(1), index_MB: (s.totalIndexSize/1048576).toFixed(1), docs: s.count}; }).sort(function(a,b){return b.size_MB - a.size_MB;})","expect":"Identifique las colecciones que más espacio usan. Un crecimiento inesperado puede indicar la necesidad de archivado, de un índice TTL o de sharding. Revise las colecciones cuyo tamaño de índices supera el de sus datos.","severity":"safe"},{"id":"connections","group":"Capacidad","title":"Número de conexiones (current / available)","command":"db.serverStatus().connections","expect":"Si current se acerca a available, el límite de conexiones está a punto de alcanzarse y se rechazarán conexiones nuevas. Revise la configuración del pool de conexiones de la aplicación y posibles fugas.","severity":"caution"},{"id":"wiredtiger-cache","group":"Rendimiento","title":"Uso de la caché de WiredTiger y eviction","command":"var wt = db.serverStatus().wiredTiger.cache; printjson({used_MB: (wt['bytes currently in the cache']/1048576).toFixed(0), max_MB: (wt['maximum bytes configured']/1048576).toFixed(0), dirty_MB: (wt['tracked dirty bytes in the cache']/1048576).toFixed(0), pages_evicted: wt['unmodified pages evicted']})","expect":"La eviction se activa cuando la memoria usada supera el 80% de la caché máxima; por encima del 95%, los hilos de la aplicación se ven obligados a hacer eviction y el rendimiento cae. Una proporción alta de páginas sucias indica presión de escritura.","severity":"caution"},{"id":"slow-queries-profiler","group":"Rendimiento","title":"Consultas lentas (profiler) y COLLSCAN","command":"printjson(db.getProfilingStatus()); db.system.profile.find({millis: {$gt: 100}}).sort({ts: -1}).limit(10).forEach(function(d){ print(d.ts + \" | \" + d.millis + \"ms | \" + (d.planSummary || '') + \" | \" + JSON.stringify(d.command)); })","expect":"Esta comprobación es de solo lectura: primero muestra el estado del profiler (was: 0 desactivado, 1 solo operaciones lentas, 2 todas) y luego lista las operaciones lentas registradas. Si el profiler está desactivado (was: 0), system.profile permanece vacío; para el diagnóstico, habilítelo como paso aparte con db.setProfilingLevel(1, {slowms: 100}) y desactívelo con db.setProfilingLevel(0) al terminar. Las consultas que muestran COLLSCAN en planSummary hacen un recorrido completo de la colección y les falta un índice. Optimice con índices las consultas con valores de millis altos. El profiler añade sobrecarga; úselo con cuidado en producción.","severity":"caution"},{"id":"long-running-ops","group":"Rendimiento","title":"Operaciones de larga duración (currentOp)","command":"db.getSiblingDB('admin').aggregate([{$currentOp: {}}, {$match: {active: true, secs_running: {$gt: 5}}}, {$project: {opid: 1, secs_running: 1, ns: 1, op: 1, planSummary: 1, waitingForLock: 1}}])","expect":"Las operaciones activas con valores altos de secs_running pueden provocar bloqueos. Termínelas con db.killOp(opid) cuando sea necesario. Las operaciones con waitingForLock=true están esperando un bloqueo; investigue la operación raíz.","severity":"danger"},{"id":"oplog-window","group":"Backup y recuperación","title":"Ventana del oplog (historial de replicación)","command":"rs.printReplicationInfo()","expect":"La ventana del oplog (tamaño de oplog configurado y duración del log en horas) debe ser lo bastante amplia. Si la ventana es corta (unas pocas horas), un secondary lento puede caer en una resincronización completa (initial sync). Las estrategias de backup y de recuperación point-in-time dependen del oplog.","severity":"caution"},{"id":"backup-note","group":"Backup y recuperación","title":"Estado de los backups y nota del último backup","command":"var r = db.getSiblingDB('local').getCollectionNames().includes('oplog.rs') ? db.getReplicationInfo() : null; printjson(r ? {oplogWindowHours: r.timeDiffHours, tFirst: r.tFirst, tLast: r.tLast} : 'standalone: no oplog'); print('Backup verification: check mongodump/mongorestore or Ops Manager / Percona Backup (PBM) status externally.')","expect":"MongoDB no tiene backups programados integrados; se usan mongodump, snapshots del sistema de archivos, Ops Manager o PBM. Verifique en la herramienta externa la hora del último backup correcto y la prueba de restauración. En un replica set, la ventana del oplog (oplogWindowHours) limita hasta dónde puede retroceder el PITR basado en oplog; no debe ser más corta que el intervalo de backup. Compruebe que se cumplen los objetivos de RPO/RTO.","severity":"danger"},{"id":"users-roles","group":"Seguridad","title":"Lista de usuarios y roles","command":"db.getSiblingDB('admin').system.users.find({}, {user: 1, db: 1, roles: 1, _id: 0}).forEach(printjson)","expect":"Revise los usuarios definidos y sus roles. Las cuentas inesperadas o los roles demasiado amplios (root, __system, dbOwner) suponen un riesgo de escalada de privilegios. Aplique el principio de mínimo privilegio.","severity":"caution"},{"id":"auth-enabled","group":"Seguridad","title":"¿Está habilitada la autenticación (authorization)?","command":"db.getSiblingDB('admin').runCommand({getCmdLineOpts: 1}).parsed.security","expect":"security.authorization debe ser 'enabled'. Si está deshabilitada (o no hay bloque security), la base de datos está abierta al acceso sin autenticación; es un grave agujero de seguridad, habilítela de inmediato.","severity":"danger"},{"id":"secondary-lag","group":"Replicación","title":"Retraso de replicación de los secondaries","command":"rs.printSecondaryReplicationInfo()","expect":"El valor de 'replication lag' de cada secondary debe ser bajo (unos segundos). Si el retraso es grande (minutos), el secondary no da abasto con la carga o hay un problema de red/disco; aumenta el riesgo de pérdida de datos y de lecturas inconsistentes en un failover.","severity":"caution"},{"id":"member-health","group":"Replicación","title":"Salud de los miembros y estado de la elección","command":"rs.status().members.map(function(m){ return {member: m.name, state: m.stateStr, health: m.health, configVersion: m.configVersion, last_heartbeat: m.lastHeartbeatMessage || 'OK'}; })","expect":"Todos los miembros deben estar sanos (health=1) y en el estado esperado (PRIMARY/SECONDARY). Un mensaje de error en lastHeartbeatMessage indica una partición de red o un miembro inaccesible. La pérdida de la mayoría detiene las escrituras.","severity":"caution"}]},"health/redis.json":{"engineId":"redis","engine":"Redis","order":6,"checks":[{"id":"server-version-uptime","group":"General","title":"Versión y tiempo de actividad","command":"redis-cli INFO server | grep -E \"redis_version|uptime_in_days|redis_mode|os\"","expect":"redis_version debe ser una versión con soporte y parcheada. Si uptime_in_days es muy pequeño, se ha producido un reinicio inesperado; redis_mode (standalone/sentinel/cluster) confirma la configuración.","severity":"safe"},{"id":"persistence-status","group":"General","title":"Estado de la persistencia (RDB / AOF)","command":"redis-cli INFO persistence | grep -E \"rdb_last_bgsave_status|aof_enabled|aof_last_bgrewrite_status|aof_last_write_status|rdb_bgsave_in_progress|loading\"","expect":"rdb_last_bgsave_status y aof_last_write_status deben ser 'ok'. Un valor 'err' indica un problema de disco/permisos o un disco lleno y conlleva riesgo de pérdida de datos. loading:1 significa que el servidor aún está cargando datos.","severity":"danger"},{"id":"memory-usage","group":"Capacidad","title":"Uso de memoria (used_memory / maxmemory)","command":"redis-cli INFO memory | grep -E \"used_memory_human|used_memory:|maxmemory_human|maxmemory:|maxmemory_policy\"","expect":"Si used_memory se acerca a maxmemory (85%+), hay riesgo de eviction o de OOM. Si maxmemory=0 no hay límite, y Redis puede consumir toda la RAM y tumbar el sistema; establezca siempre un límite y una maxmemory_policy adecuada.","severity":"caution"},{"id":"memory-fragmentation","group":"Capacidad","title":"Proporción de fragmentación de memoria","command":"redis-cli INFO memory | grep -E \"mem_fragmentation_ratio|mem_allocator\"","expect":"Lo ideal es que mem_fragmentation_ratio esté entre 1.0 y 1.5. Por encima de 1.5 indica una fragmentación alta (memoria desperdiciada). Por debajo de 1.0 indica que Redis se está paginando a disco (swap; un problema grave de rendimiento); revise la RAM del sistema.","severity":"caution"},{"id":"evicted-keys","group":"Capacidad","title":"Claves desalojadas (evicted_keys)","command":"redis-cli INFO stats | grep -E \"evicted_keys|expired_keys|keyspace_misses\"","expect":"Si evicted_keys sigue aumentando, Redis ha alcanzado su límite de memoria y está descartando datos; esto puede provocar una pérdida de datos no deseada. Considere añadir memoria, usar TTL o revisar maxmemory_policy.","severity":"caution"},{"id":"dbsize-keyspace","group":"Capacidad","title":"Número de claves y distribución del keyspace","command":"redis-cli DBSIZE; redis-cli INFO keyspace","expect":"Vigile el número total de claves (DBSIZE) y la distribución por base de datos (INFO keyspace: keys, expires, avg_ttl). Un keyspace que crece sin TTL (expires bajo) provoca un crecimiento excesivo de la memoria. Los saltos bruscos en el número de claves pueden ser señal de un comportamiento anómalo.","severity":"safe"},{"id":"hit-ratio","group":"Rendimiento","title":"Tasa de aciertos de caché (keyspace hits/misses)","command":"redis-cli INFO stats | grep -E \"keyspace_hits|keyspace_misses\"","expect":"Tasa de aciertos = hits / (hits + misses). Para uso como caché la tasa debe ser alta (80%+). Una tasa baja indica un cacheo ineficiente, TTL incorrectos o un patrón de cache-miss; asegúrese de que los datos solicitados con frecuencia estén cacheados.","severity":"safe"},{"id":"slowlog","group":"Rendimiento","title":"Log de comandos lentos (SLOWLOG)","command":"redis-cli SLOWLOG GET 10","expect":"Examine los comandos lentos (umbral por defecto 10ms). Los comandos O(N) (KEYS, SMEMBERS, HGETALL grandes, SORT) bloquean Redis, que es de un solo hilo, y retrasan todas las solicitudes. Sustitúyalos por alternativas como SCAN.","severity":"caution"},{"id":"clients","group":"Rendimiento","title":"Clientes conectados y bloqueados (clients)","command":"redis-cli INFO clients | grep -E \"connected_clients|blocked_clients|maxclients|client_recent_max_input_buffer\"","expect":"Si connected_clients se acerca a maxclients, se rechazan conexiones nuevas. Un número alto de blocked_clients (esperando en BLPOP/BRPOP) puede ser intencionado, pero un aumento inesperado es señal de congestión. Compruebe si hay fugas de conexiones.","severity":"safe"},{"id":"ops-per-sec","group":"Rendimiento","title":"Rendimiento instantáneo (ops/sec)","command":"redis-cli INFO stats | grep -E \"instantaneous_ops_per_sec|total_commands_processed|rejected_connections|total_net_input_bytes\"","expect":"instantaneous_ops_per_sec muestra la carga de trabajo actual. Las caídas/picos bruscos son señal de anomalías. Si rejected_connections > 0, se ha alcanzado el límite de maxclients. Al ser de un solo hilo, un número de ops muy alto puede crear un cuello de botella de CPU.","severity":"safe"},{"id":"rdb-last-save","group":"Backup y recuperación","title":"Último guardado RDB y cambios pendientes","command":"redis-cli INFO persistence | grep -E \"rdb_last_save_time|rdb_changes_since_last_save|rdb_last_bgsave_status\"; redis-cli LASTSAVE","expect":"Si rdb_changes_since_last_save es muy alto, se han acumulado muchos datos desde el último snapshot; estos cambios se pierden en un crash. Si rdb_last_save_time (LASTSAVE, hora Unix) es muy antiguo, revise el intervalo de guardado o la frecuencia de backups.","severity":"caution"},{"id":"aof-rewrite","group":"Backup y recuperación","title":"Estado del rewrite de AOF","command":"redis-cli INFO persistence | grep -E \"aof_enabled|aof_last_bgrewrite_status|aof_rewrite_in_progress|aof_current_size|aof_base_size\"","expect":"En escenarios que requieren durabilidad, debe estar configurado aof_enabled=1. aof_last_bgrewrite_status debe ser 'ok'; 'err' indica un problema de disco/persistencia. Si aof_current_size es mucho mayor que base_size, se necesita un rewrite.","severity":"caution"},{"id":"acl-auth","group":"Seguridad","title":"Configuración de ACL / autenticación (requirepass)","command":"redis-cli ACL LIST; redis-cli ACL GETUSER default","expect":"Debe haber al menos un usuario protegido con contraseña. Si los flags en la salida de ACL GETUSER default muestran 'on' y 'nopass' (requirepass es también la contraseña de este usuario), Redis está abierto al acceso sin autenticación; es un grave agujero de seguridad. Las contraseñas solo se muestran como hashes; no use CONFIG GET requirepass para esta comprobación, porque vuelca la contraseña en texto plano. Defina usuarios con mínimo privilegio mediante ACL.","severity":"danger"},{"id":"dangerous-commands","group":"Seguridad","title":"Comandos peligrosos y protected-mode","command":"redis-cli CONFIG GET protected-mode; redis-cli CONFIG GET bind; redis-cli COMMAND INFO flushall flushdb config keys","expect":"protected-mode debe ser 'yes' y bind debe limitarse a interfaces seguras. Los comandos peligrosos como FLUSHALL, FLUSHDB, CONFIG, KEYS y DEBUG deben deshabilitarse en producción con rename-command o restringirse con ACL.","severity":"caution"},{"id":"replication-status","group":"Replicación","title":"Estado de la replicación (role, master link)","command":"redis-cli INFO replication","expect":"Verifique role (master/slave), connected_slaves y el estado de cada réplica. En una réplica, master_link_status debe ser 'up'; 'down' significa que el enlace está roto. Si master_last_io_seconds_ago es alto (retraso), aumenta el riesgo de pérdida de datos e inconsistencias.","severity":"danger"}]},"health/elasticsearch.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"checks":[{"id":"cluster-health","group":"General","title":"Salud del clúster (green / yellow / red)","command":"GET _cluster/health","expect":"status debe ser 'green': todos los shards primary y replica están asignados. 'yellow' significa que los primary están intactos pero algunas réplicas no están asignadas (riesgo para la redundancia); 'red' significa que al menos un primary shard se ha perdido/no está asignado (parte de los datos no está disponible; se requiere una acción inmediata). unassigned_shards debe ser 0.","severity":"danger"},{"id":"cluster-version","group":"General","title":"Versión e información del clúster","command":"GET /","expect":"version.number debe ser una versión con soporte y con los parches de seguridad aplicados. Las versiones antiguas/EOL (end-of-life) conllevan riesgos de seguridad y estabilidad. Verifique también que todos los nodos ejecutan la misma versión con GET _cat/nodes?v&h=name,version (una mezcla temporal durante un rolling upgrade es normal).","severity":"safe"},{"id":"pending-tasks","group":"General","title":"Tareas pendientes del clúster","command":"GET _cluster/pending_tasks","expect":"La lista debe estar vacía o ser muy corta. Las tareas que se acumulan y esperan mucho tiempo (time_in_queue alto) indican que el nodo master está sobrecargado o que las actualizaciones del cluster state están atascadas; esto degrada el tiempo de respuesta de todo el clúster.","severity":"caution"},{"id":"disk-allocation-watermark","group":"Capacidad","title":"Asignación de disco y estado de los watermarks","command":"GET _cat/allocation?v","expect":"disk.percent de cada nodo debe estar por debajo del high watermark (por defecto 90%). Por encima del 85% es una advertencia, el 90% (high) provoca la reubicación de shards y el 95% (flood_stage) pone los índices en solo lectura. Verifique que la columna shards está equilibrada entre los nodos; una distribución desigual hace que un nodo se llene antes.","severity":"danger"},{"id":"largest-indices","group":"Capacidad","title":"Índices más grandes (store size)","command":"GET _cat/indices?v&s=store.size:desc","expect":"Haga seguimiento de los índices que más espacio consumen y de sus valores de doc.count. El crecimiento descontrolado de los índices de logs/métricas es la causa principal de discos llenos; defina para ellos políticas de rollover y eliminación con ILM (Index Lifecycle Management). Preste especial atención a los índices con status 'close' o con salud yellow/red.","severity":"caution"},{"id":"jvm-heap-usage","group":"Capacidad","title":"Uso del heap de la JVM y de la RAM (por nodo)","command":"GET _cat/nodes?v&h=name,heap.percent,ram.percent,cpu,load_1m","expect":"heap.percent debe mantenerse de forma constante por debajo del 75%; valores sostenidos de 85%+ implican presión de memoria, pausas de GC frecuentes y riesgo de circuit breaker. El heap de la JVM debe ser ~50% de la RAM del servidor y como máximo ~31GB (límite de compressed oops). Si el heap alto no baja, optimice las consultas o añada nodos.","severity":"caution"},{"id":"shard-count-limits","group":"Capacidad","title":"Número de shards y límite de shards por nodo","command":"GET _cluster/health?level=shards","expect":"active_shards y el número de shards por nodo deben ser razonables. El límite superior por defecto es cluster.max_shards_per_node = 1000 (por nodo de datos); acercarse a este límite y tener en total miles de shards pequeños (oversharding) genera presión sobre el master y desperdicia memoria. La antigua regla de \"~20 shards por GB de heap\" no se aplica a 8.3+; la recomendación actual es 10-50GB por shard y planificar el heap según el número de campos (mappings) por nodo. initializing_shards / relocating_shards no deben mantenerse altos durante mucho tiempo. Si hay demasiados shards, use shards más grandes/menos numerosos o rollover.","severity":"caution"},{"id":"http-connections","group":"Capacidad","title":"Número de conexiones HTTP","command":"GET _nodes/stats/http","expect":"http.current_open debe estar dentro de límites razonables. Si http.total_opened crece muy rápido, los clientes no usan un pool de conexiones y abren una conexión nueva por solicitud (ineficiente). Un current_open anormalmente alto apunta a una fuga de conexiones o a un posible agotamiento de recursos.","severity":"safe"},{"id":"thread-pool-rejections","group":"Rendimiento","title":"Rechazos del thread pool y saturación de colas","command":"GET _cat/thread_pool?v&h=node_name,name,active,queue,rejected","expect":"La columna rejected debe ser 0. Un valor rejected creciente en los pools write/bulk y search muestra que el clúster está sobrecargado y devuelve HTTP 429 (es_rejected_execution_exception). Una cola constantemente llena es señal de congestión. Aplique backoff en el cliente, reduzca el tamaño de los lotes o añada capacidad.","severity":"danger"},{"id":"query-cache-hit","group":"Rendimiento","title":"Tasa de aciertos de la query cache","command":"GET _nodes/stats/indices/query_cache","expect":"La proporción hit_count / (hit_count + miss_count) debe ser alta para consultas de filtro repetidas. Una tasa de aciertos baja con muchas evictions indica que el tamaño de la caché es insuficiente o que las consultas son demasiado variables para cachearse. Usar filtros estables (filter context) aumenta la tasa de aciertos.","severity":"safe"},{"id":"slowlog-config","group":"Rendimiento","title":"Configuración del log de consultas lentas (slowlog)","command":"GET /_all/_settings?filter_path=**.search.slowlog,**.indexing.slowlog","expect":"Deben definirse umbrales de slowlog de búsqueda e indexación en los índices importantes (p. ej., index.search.slowlog.threshold.query.warn: 10s). Con el slowlog deshabilitado, diagnosticar las consultas lentas es más difícil. Habilite los umbrales y vigile los logs para identificar consultas costosas (agregaciones amplias, wildcards, paginación profunda).","severity":"safe"},{"id":"hot-threads","group":"Rendimiento","title":"Hot threads","command":"GET _nodes/hot_threads","expect":"Muestra los hilos que más CPU consumen. Se usa en situaciones de CPU alta/lentitud para determinar qué operación (agregación pesada, merge, GC, script) es el cuello de botella. Si la misma consulta/merge costosa aparece una y otra vez, optimice esa consulta o la estrategia de indexación.","severity":"safe"},{"id":"fielddata-memory","group":"Rendimiento","title":"Uso de memoria de fielddata","command":"GET _cat/fielddata?v","expect":"El uso de memoria de fielddata debe mantenerse bajo. Agregar/ordenar sobre campos text carga fielddata en memoria y eleva el riesgo de errores de circuit breaker (Data too large) y de OOM. Para los campos con fielddata alto, cambie a un subcampo keyword (doc_values).","severity":"caution"},{"id":"snapshot-status","group":"Backup y recuperación","title":"Repositorios de snapshots y estado del último snapshot","command":"GET _snapshot/_all","expect":"Debe haber al menos un repositorio de snapshots definido. Tras confirmar que existen los repositorios, compruebe el estado de los últimos snapshots (junto con la verificación siguiente). Si no hay repositorio, el clúster no tiene backup y los datos pueden perderse de forma permanente en un estado RED. Configure SLM (Snapshot Lifecycle Management) para snapshots automáticos periódicos.","severity":"danger"},{"id":"snapshot-latest-state","group":"Backup y recuperación","title":"Estado de éxito de los últimos snapshots","command":"GET _snapshot/<repo>/_all?verbose=false","expect":"El state de los últimos snapshots debe ser 'SUCCESS'. 'PARTIAL' significa que no se pudo hacer backup de algunos shards, 'FAILED' que el backup falló; ambos suponen pérdida de datos al restaurar. La fecha del snapshot más reciente (end_time) no debe ser demasiado antigua (haga backups con una frecuencia que cumpla su objetivo de RPO).","severity":"danger"},{"id":"snapshot-repo-verify","group":"Backup y recuperación","title":"Verificación del repositorio de snapshots","command":"POST _snapshot/<repo>/_verify","expect":"La verificación confirma que todos los nodos pueden acceder al repositorio (sistema de archivos compartido, S3, HDFS, etc.) y lista en la respuesta los nodos que tienen acceso. Si devuelve un error, algunos nodos no pueden escribir en el repositorio de backups; en ese caso los snapshots quedarán incompletos/fallarán. Corrija el acceso al repositorio y las credenciales.","severity":"caution"},{"id":"security-enabled","group":"Seguridad","title":"¿Está habilitada la seguridad (X-Pack security)?","command":"GET _xpack/usage","expect":"security.enabled y security.available deben ser true. Con la seguridad deshabilitada, el clúster es accesible sin autenticación; un clúster así expuesto a internet o a una red no confiable es una vulnerabilidad grave (fuga/borrado de datos). Deben estar habilitados la autenticación, el acceso basado en roles y TLS.","severity":"danger"},{"id":"users-roles","group":"Seguridad","title":"Usuarios y roles","command":"GET _security/user","expect":"Revise los usuarios definidos y las asignaciones de roles. Asegúrese de que las cuentas integradas (elastic, kibana_system, etc.) estén protegidas con contraseñas robustas. Siguiendo el principio de mínimo privilegio, defina usuarios separados con roles limitados para cada aplicación; evite un uso innecesariamente extendido del rol superuser. Use GET _security/role para los roles.","severity":"caution"},{"id":"tls-transport-http","group":"Seguridad","title":"Estado de TLS / HTTPS (transport y HTTP)","command":"GET _nodes/settings?filter_path=**.xpack.security.transport.ssl.enabled,**.xpack.security.http.ssl.enabled","expect":"xpack.security.transport.ssl.enabled debe ser true (cifrado entre nodos, obligatorio). Lo ideal es que http.ssl.enabled también sea true (HTTPS del cliente al clúster). Con TLS deshabilitado, las credenciales y los datos viajan por la red en texto plano. Revise también la caducidad de los certificados y el modo de verificación (verification_mode: certificate/full).","severity":"danger"},{"id":"unassigned-shards","group":"Replicación","title":"Shards sin asignar y sus motivos","command":"GET _cat/shards?v&h=index,shard,prirep,state,unassigned.reason","expect":"Todos los shards deben tener el state 'STARTED'. Deben investigarse los shards en estado UNASSIGNED y su unassigned.reason (NODE_LEFT, ALLOCATION_FAILED, DISK_THRESHOLD, etc.). Un primary sin asignar implica salud RED; una réplica sin asignar, YELLOW. Use GET _cluster/allocation/explain para la causa raíz.","severity":"danger"},{"id":"recovery-active","group":"Replicación","title":"Recuperaciones de shards en curso","command":"GET _cat/recovery?v&active_only=true","expect":"Normalmente no debería haber ninguna recuperación activa (lista vacía). Una recuperación temporal tras añadir/quitar un nodo o tras un failover es normal, pero las recuperaciones que permanecen atascadas mucho tiempo sin avanzar (%progress que no aumenta) apuntan a un cuello de botella de red/disco. Si el reequilibrio (relocation) afecta a la carga de trabajo, revise la configuración de limitación de la recuperación (indices.recovery.max_bytes_per_sec).","severity":"caution"},{"id":"replica-count","group":"Replicación","title":"Número de réplicas por índice","command":"GET _cat/indices?v&h=index,health,pri,rep,docs.count","expect":"En los índices de producción, rep (number_of_replicas) debe ser al menos 1; en un índice con 0 réplicas, perder un nodo implica una pérdida de datos permanente. Sin embargo, en un clúster de varios nodos el número de réplicas no debe superar (número de nodos de datos - 1); de lo contrario, las réplicas sobrantes no se pueden asignar y la salud se queda en YELLOW. Investigue los índices cuya columna health no sea green.","severity":"danger"}]},"recipes/oracle.json":{"engineId":"oracle","engine":"Oracle","order":1,"recipes":[{"id":"redo-switch","task":"Redo log switch (cambiar el archivo de log actual)","category":"Redo y archivado","command":"ALTER SYSTEM SWITCH LOGFILE;","what":"Cierra el grupo de redo log actual y pasa al siguiente; en modo archivelog, desencadena el archivado.","when":"Para generar un archivo manualmente, forzar un log switch o para pruebas.","caution":"Los switches frecuentes degradan el rendimiento; si los archivos de log son demasiado pequeños, revise su dimensionamiento.","tags":["redo","switch","logfile","archive","archive","alter system"]},{"id":"redo-add-group","task":"Añadir un nuevo grupo de redo log","category":"Redo y archivado","command":"ALTER DATABASE ADD LOGFILE GROUP 4\n  ('/u01/oradata/ORCL/redo04a.log',\n   '/u02/oradata/ORCL/redo04b.log') SIZE 512M;","what":"Crea un nuevo grupo de redo log con el tamaño y los miembros indicados (varios miembros = espejo).","when":"Cuando el throughput de redo es insuficiente o se quiere aumentar el número de grupos.","caution":"Asegúrese de que el número de grupo no coincide con grupos existentes; compruébelo con V$LOG.","tags":["redo","logfile","group","add","add","alter database"]},{"id":"redo-resize","task":"Redimensionar un grupo de redo log (drop + add)","category":"Redo y archivado","command":"-- 1) The group to be resized must be INACTIVE; if not, switch:\nALTER SYSTEM SWITCH LOGFILE;\nALTER SYSTEM CHECKPOINT;\n-- 2) Drop the old group:\nALTER DATABASE DROP LOGFILE GROUP 1;\n-- 3) Add it again with the new size:\nALTER DATABASE ADD LOGFILE GROUP 1\n  ('/u01/oradata/ORCL/redo01a.log',\n   '/u02/oradata/ORCL/redo01b.log') SIZE 1G REUSE;","what":"Un redo log no se puede redimensionar directamente; se hace eliminando el grupo y volviendo a añadirlo con el nuevo tamaño.","when":"Cuando los archivos de redo son demasiado pequeños/grandes, para ajustar la frecuencia de checkpoints.","caution":"Solo se puede eliminar un grupo INACTIVE o UNUSED; no elimine el grupo CURRENT/ACTIVE. Siempre deben quedar al menos dos grupos. REUSE reutiliza el archivo existente.","tags":["redo","resize","size","drop","add","logfile","reuse"]},{"id":"enable-archivelog","task":"Poner la base de datos en modo ARCHIVELOG","category":"Redo y archivado","command":"SHUTDOWN IMMEDIATE;\nSTARTUP MOUNT;\nALTER DATABASE ARCHIVELOG;\nALTER DATABASE OPEN;\n-- Verify:\nARCHIVE LOG LIST;","what":"Pone la base de datos en modo archive log; los redo logs llenos se archivan, lo que hace posibles los backups en caliente y la recuperación point-in-time.","when":"Para la estrategia de backup y recuperación de una base de datos de producción (obligatorio antes de RMAN, Data Guard).","caution":"Requiere reiniciar la instancia; planifique la indisponibilidad. Asegúrese de que el destino de archivado (FRA o LOG_ARCHIVE_DEST) tiene espacio suficiente; si se llena, la BD se detiene. En RAC: detenga todas las instancias con srvctl stop database -d DB, abra una sola instancia con STARTUP MOUNT, cambie a ARCHIVELOG, ciérrela e inicie con srvctl start database -d DB (11gR2+ no requiere cluster_database=false).","tags":["archivelog","archive","mode","mount","recovery","recovery"]},{"id":"fra-full-fix","task":"Solucionar un FRA / destino de archivado lleno","category":"Redo y archivado","command":"-- Check usage:\nSELECT name, space_limit/1024/1024/1024 AS limit_gb,\n       space_used/1024/1024/1024  AS used_gb,\n       ROUND(space_used/space_limit*100,1) AS pct_used\nFROM   v$recovery_file_dest;\n\n-- Fix A) Increase the FRA size:\nALTER SYSTEM SET db_recovery_file_dest_size=100G SCOPE=BOTH;\n\n-- Fix B) Clean up already backed-up archives with RMAN:\n-- RMAN> DELETE ARCHIVELOG ALL BACKED UP 1 TIMES TO DISK COMPLETED BEFORE 'SYSDATE-3';\n-- If there is no backup (CAREFUL): RMAN> DELETE ARCHIVELOG ALL COMPLETED BEFORE 'SYSDATE-1';","what":"Cuando el Fast Recovery Area o el destino de archivado se llenan, la base de datos se cuelga (ORA-00257). Se soluciona ampliando el espacio o limpiando archivos.","when":"Error del archiver ORA-00257, cuando la BD deja de aceptar nuevas transacciones.","caution":"Eliminar archivos de los que no se ha hecho backup destruye la capacidad de recuperación. Haga primero un backup con RMAN y luego elimine. No los elimine con rm desde el SO; eso requiere un CROSSCHECK de RMAN. Mientras el FRA esté lleno, haga el backup de los archivos a un destino fuera del FRA (FORMAT '/backup/arc_%U'). Con Data Guard, no elimine archivos aún no enviados/aplicados en el standby; de lo contrario, se produce un gap en el standby. Use CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON ALL STANDBY.","tags":["fra","recovery area","ora-00257","archiver","archive","full","cleanup"]},{"id":"redo-status","task":"Ver el estado de los redo logs","category":"Redo y archivado","command":"SELECT l.group#, l.thread#, l.sequence#,\n       ROUND(l.bytes/1024/1024) AS mb,\n       l.members, l.archived, l.status,\n       lf.member AS file_path\nFROM   v$log l\n  JOIN v$logfile lf ON lf.group# = l.group#\nORDER BY l.group#, lf.member;","what":"Muestra el estado de los grupos de redo log (CURRENT/ACTIVE/INACTIVE), sus tamaños, los archivos miembro y el estado de archivado.","when":"Al revisar la configuración de redo, o para ver qué grupo está activo antes de redimensionar.","caution":"Solo consulta; sin riesgo.","tags":["redo","status","status","v$log","v$logfile","logfile"]},{"id":"asm-add-disk","task":"Añadir discos a un diskgroup de ASM","category":"ASM","command":"-- Connect to the ASM instance (sqlplus / as sysasm)\nALTER DISKGROUP DATA ADD DISK\n  '/dev/oracleasm/disks/DATA05' NAME DATA_0005,\n  '/dev/oracleasm/disks/DATA06' NAME DATA_0006\n  REBALANCE POWER 4;","what":"Añade discos nuevos al diskgroup DATA e inicia un rebalance automático; la capacidad aumenta.","when":"Cuando un diskgroup de ASM empieza a llenarse o al ampliar el almacenamiento.","caution":"Requiere el privilegio SYSASM. Los tamaños de disco deben ser similares. El rebalance genera carga de E/S; use un POWER bajo en horas de mucha actividad.","tags":["asm","diskgroup","add disk","add disk","rebalance","alter diskgroup"]},{"id":"asm-drop-disk","task":"Quitar un disco de un diskgroup de ASM","category":"ASM","command":"ALTER DISKGROUP DATA DROP DISK DATA_0005 REBALANCE POWER 4;\n\n-- WAIT until the drop completes (the disk can be physically removed once V$ASM_OPERATION is empty):\nSELECT group_number, operation, state, power,\n       sofar, est_work, est_minutes\nFROM   v$asm_operation;","what":"Quita el disco del diskgroup; sus extents se mueven a los discos restantes (rebalance). Al terminar, el HDR_STATUS del disco pasa a FORMER.","when":"Al sustituir un disco averiado, reducir capacidad o migrar el almacenamiento.","caution":"NO retire físicamente el disco ANTES de que termine el rebalance; de lo contrario, se pierden datos. Asegúrese de que los discos restantes tengan espacio libre suficiente.","tags":["asm","drop disk","remove disk","rebalance","v$asm_operation","alter diskgroup"]},{"id":"asm-rebalance-power","task":"Establecer el POWER del rebalance de ASM","category":"ASM","command":"-- Change the power of a running rebalance (1-1024, 0 stops it):\nALTER DISKGROUP DATA REBALANCE POWER 8;\n\n-- Stop the rebalance completely:\nALTER DISKGROUP DATA REBALANCE POWER 0;","what":"Establece el paralelismo/velocidad de un rebalance en curso. Un POWER más alto significa más rapidez, pero más E/S.","when":"Para acelerar un rebalance demasiado lento, o para ralentizar/detener uno que está afectando a producción.","caution":"POWER 0 suspende el rebalance (un drop de disco no se completará). Un valor alto afecta a la E/S de producción.","tags":["asm","rebalance","power","power","diskgroup","tuning"]},{"id":"asm-diskgroup-space","task":"Ver el espacio libre de los diskgroups de ASM","category":"ASM","command":"SELECT name,\n       state,\n       type,\n       total_mb/1024                 AS total_gb,\n       free_mb/1024                  AS free_gb,\n       ROUND((1-free_mb/total_mb)*100,1) AS pct_used,\n       usable_file_mb/1024           AS usable_gb\nFROM   v$asm_diskgroup\nORDER BY name;","what":"Muestra el espacio total, libre y utilizable (tras la redundancia) de cada diskgroup.","when":"Para la planificación de capacidad, cuando el espacio escasea, antes de decidir añadir discos.","caution":"usable_file_mb tiene en cuenta la redundancia; con redundancia NORMAL/HIGH esta columna es el espacio realmente utilizable. Solo consulta; sin riesgo.","tags":["asm","diskgroup","space","space","free","v$asm_diskgroup","capacity"]},{"id":"asm-monitor-rebalance","task":"Monitorear un rebalance de ASM","category":"ASM","command":"SELECT group_number, operation, pass, state, power,\n       sofar, est_work,\n       ROUND(sofar/NULLIF(est_work,0)*100,1) AS pct_done,\n       est_minutes\nFROM   v$asm_operation\nORDER BY group_number;","what":"Muestra el progreso, el power y el tiempo restante estimado de las operaciones de rebalance/resync en curso.","when":"Para seguir si el rebalance ha terminado tras añadir/quitar discos.","caution":"Resultado vacío = no hay ninguna operación activa (completada). Solo consulta; sin riesgo.","tags":["asm","rebalance","monitor","monitor","v$asm_operation","progress"]},{"id":"add-tablespace","task":"Añadir un nuevo tablespace","category":"Almacenamiento","command":"-- File system:\nCREATE TABLESPACE customer_data\n  DATAFILE '/u01/oradata/ORCL/customer_data01.dbf'\n  SIZE 1G AUTOEXTEND ON NEXT 256M MAXSIZE 20G\n  EXTENT MANAGEMENT LOCAL\n  SEGMENT SPACE MANAGEMENT AUTO;\n\n-- If you use ASM:\nCREATE TABLESPACE customer_data\n  DATAFILE '+DATA' SIZE 1G AUTOEXTEND ON NEXT 256M MAXSIZE 20G;","what":"Crea un nuevo tablespace permanente en el que se pueden ubicar segmentos de tablas/índices.","when":"Al asignar almacenamiento independiente para una nueva aplicación/esquema.","caution":"Indique MAXSIZE; de lo contrario, el disco puede llenarse. En ASM, el nombre del archivo se genera automáticamente.","tags":["tablespace","create","create","datafile","storage","storage","autoextend"]},{"id":"add-datafile","task":"Añadir un datafile a un tablespace existente","category":"Almacenamiento","command":"ALTER TABLESPACE customer_data\n  ADD DATAFILE '/u01/oradata/ORCL/customer_data02.dbf'\n  SIZE 2G AUTOEXTEND ON NEXT 256M MAXSIZE 30G;\n\n-- ASM:\nALTER TABLESPACE customer_data\n  ADD DATAFILE '+DATA' SIZE 2G AUTOEXTEND ON NEXT 256M MAXSIZE 30G;","what":"Aumenta la capacidad de un tablespace existente añadiendo un nuevo datafile.","when":"Cuando el tablespace está lleno y un solo archivo se acerca al límite de smallfile (32G/64G).","caution":"Un tablespace smallfile tiene un límite de bloques por archivo; añada un archivo nuevo si es necesario. Indique MAXSIZE.","tags":["datafile","add","add","tablespace","alter tablespace","storage"]},{"id":"resize-datafile","task":"Redimensionar un datafile / configurar autoextend","category":"Almacenamiento","command":"-- Grow/shrink to a fixed size:\nALTER DATABASE DATAFILE '/u01/oradata/ORCL/customer_data01.dbf' RESIZE 5G;\n\n-- Enable autoextend:\nALTER DATABASE DATAFILE '/u01/oradata/ORCL/customer_data01.dbf'\n  AUTOEXTEND ON NEXT 256M MAXSIZE 30G;\n\n-- Disable autoextend:\nALTER DATABASE DATAFILE '/u01/oradata/ORCL/customer_data01.dbf' AUTOEXTEND OFF;","what":"Fija un datafile a un tamaño concreto o configura su comportamiento de crecimiento automático.","when":"Para recuperar espacio (reducir), preasignar espacio o detener un crecimiento descontrolado.","caution":"La reducción con RESIZE no puede bajar de los bloques usados al final del archivo (ORA-03297). Mueva primero los segmentos.","tags":["datafile","resize","size","autoextend","maxsize","alter database"]},{"id":"move-datafile-online","task":"Mover un datafile en línea","category":"Almacenamiento","command":"ALTER DATABASE MOVE DATAFILE\n  '/u01/oradata/ORCL/customer_data01.dbf'\n  TO '/u03/oradata/ORCL/customer_data01.dbf';\n\n-- Move into ASM:\nALTER DATABASE MOVE DATAFILE\n  '/u01/oradata/ORCL/customer_data01.dbf' TO '+DATA';\n\n-- Use KEEP to retain the old file instead of deleting it:\n-- ALTER DATABASE MOVE DATAFILE '...' TO '...' KEEP;","what":"Mueve un datafile a una nueva ubicación con la base de datos abierta (12c+); cambia el almacenamiento sin indisponibilidad.","when":"Al cambiar el disco/punto de montaje, migrar a ASM o pasar a un almacenamiento rápido.","caution":"Requiere 12c o posterior y Enterprise Edition. El traslado genera E/S adicional. Si no usa KEEP, el archivo antiguo se elimina.","since":"Oracle 12c","note":"El traslado en línea de datafiles (ALTER DATABASE MOVE DATAFILE) llegó con Oracle 12c (12.1); antes, mover un datafile implicaba poner el tablespace/datafile OFFLINE, copiarlo a nivel del SO y volver a mapearlo con ALTER DATABASE RENAME FILE.","tags":["datafile","move","move","online","alter database move datafile","asm"]},{"id":"temp-tablespace","task":"Añadir un tablespace temporal / cambiar el predeterminado","category":"Almacenamiento","command":"-- Create a new temp tablespace:\nCREATE TEMPORARY TABLESPACE temp2\n  TEMPFILE '/u01/oradata/ORCL/temp2_01.dbf'\n  SIZE 2G AUTOEXTEND ON NEXT 256M MAXSIZE 20G;\n\n-- Change the database default temp:\nALTER DATABASE DEFAULT TEMPORARY TABLESPACE temp2;\n\n-- Add a tempfile to the old temp:\nALTER TABLESPACE temp ADD TEMPFILE '/u01/oradata/ORCL/temp_02.dbf' SIZE 2G;","what":"Proporciona espacio temporal para operaciones de sort/hash/tablas temporales globales; puede mover el predeterminado al nuevo tablespace.","when":"Ante ORA-01652 (unable to extend temp segment), un cuello de botella en temp o para redimensionar temp.","caution":"Tras cambiar el predeterminado, no haga DROP del temp antiguo de inmediato si está en uso; elimínelo cuando las sesiones lo hayan liberado.","tags":["temp","tablespace","tempfile","ora-01652","default temporary","storage"]},{"id":"tablespace-space","task":"Consulta del espacio libre de los tablespaces","category":"Almacenamiento","command":"SELECT df.tablespace_name,\n       ROUND(df.total_mb/1024,2)                 AS total_gb,\n       ROUND(NVL(fs.free_mb,0)/1024,2)           AS free_gb,\n       ROUND((df.total_mb-NVL(fs.free_mb,0))/df.total_mb*100,1) AS pct_used\nFROM  (SELECT tablespace_name, SUM(bytes)/1024/1024 total_mb\n         FROM dba_data_files GROUP BY tablespace_name) df\n  LEFT JOIN\n       (SELECT tablespace_name, SUM(bytes)/1024/1024 free_mb\n          FROM dba_free_space GROUP BY tablespace_name) fs\n    ON df.tablespace_name = fs.tablespace_name\nORDER BY pct_used DESC;","what":"Muestra el espacio total y libre y el porcentaje de uso de cada tablespace (según el espacio asignado actualmente, sin contar el potencial de autoextend).","when":"Monitoreo del espacio, para saber qué tablespace hay que ampliar.","caution":"Esta consulta se basa en el tamaño actual de los datafiles; puede crecer más con autoextend. Para temp use DBA_TEMP_FREE_SPACE. Solo consulta; sin riesgo.","tags":["tablespace","space","space","free","dba_free_space","usage","storage"]},{"id":"alter-parameter","task":"Cambiar un parámetro (con SCOPE)","category":"Memoria y parámetros","command":"-- Both the running instance and the spfile (default, dynamic parameters):\nALTER SYSTEM SET open_cursors=500 SCOPE=BOTH;\n\n-- Only for the next restart (static parameters):\nALTER SYSTEM SET processes=1000 SCOPE=SPFILE;\n\n-- Immediate only, do not write to the spfile:\nALTER SYSTEM SET optimizer_mode='ALL_ROWS' SCOPE=MEMORY;\n\n-- View the current value:\nSHOW PARAMETER open_cursors;","what":"Cambia un parámetro de inicialización en la instancia en ejecución y/o en el spfile. SCOPE determina el comportamiento.","when":"Para ajustes de rendimiento, de capacidad y correcciones de errores que requieren cambiar un parámetro.","caution":"Los parámetros estáticos (processes, sga_max_size...) solo aceptan SCOPE=SPFILE y requieren reinicio. Un valor erróneo puede impedir que la instancia arranque; para parámetros críticos, haga primero un backup del pfile.","tags":["parameter","parameter","alter system","scope","spfile","tuning"]},{"id":"flush-pool","task":"Flush del shared pool / buffer cache","category":"Memoria y parámetros","command":"-- Clear the SQL plan/cursor cache:\nALTER SYSTEM FLUSH SHARED_POOL;\n\n-- Clear the buffer cache (usually only for test/benchmark):\nALTER SYSTEM FLUSH BUFFER_CACHE;","what":"Hacer flush del shared pool expulsa de la memoria todo el SQL/PL-SQL cacheado y la información del diccionario; hacer flush del buffer cache expulsa todos los bloques de datos.","when":"Para probar problemas de planes, tras una operación grande puntual o para un inicio de benchmark consistente.","caution":"¡Cuidado EN PRODUCCIÓN! Tras un flush, todo el SQL se vuelve a analizar (tormenta de hard parses) y los bloques se leen de disco; le sigue una caída temporal importante del rendimiento.","tags":["flush","shared pool","buffer cache","memory","alter system","cache"]},{"id":"spfile-pfile","task":"Crear un pfile a partir del spfile y viceversa","category":"Memoria y parámetros","command":"-- spfile -> pfile (for backup/editing):\nCREATE PFILE='/tmp/init_orcl_backup.ora' FROM SPFILE;\n\n-- pfile -> spfile (make the edited pfile permanent; effective after restart):\nCREATE SPFILE FROM PFILE='/tmp/init_orcl_backup.ora';\n\n-- Create a pfile from the running instance values (11g+):\nCREATE PFILE='/tmp/init_current.ora' FROM MEMORY;","what":"Convierte entre el spfile binario y el pfile de texto; el pfile se puede editar a mano.","when":"Backup antes de cambiar parámetros, reparar un spfile dañado o corregir a mano un parámetro estático.","caution":"Tras CREATE SPFILE FROM PFILE, los cambios solo surten efecto después de un reinicio. Verifique la ruta antes de sobrescribir un spfile existente.","tags":["spfile","pfile","create pfile","create spfile","parameter","backup"]},{"id":"sga-pga-target","task":"Cambiar los objetivos de SGA / PGA","category":"Memoria y parámetros","command":"-- Automatic memory management (ASMM) targets:\nALTER SYSTEM SET sga_target=8G   SCOPE=BOTH;\nALTER SYSTEM SET pga_aggregate_target=4G SCOPE=BOTH;\n\n-- Upper limit (static, requires restart):\nALTER SYSTEM SET sga_max_size=12G SCOPE=SPFILE;\n\n-- View the current memory distribution:\nSELECT component, current_size/1024/1024 AS mb\nFROM   v$sga_dynamic_components ORDER BY component;","what":"Establece los objetivos de memoria de la SGA y la PGA; Oracle gestiona los componentes automáticamente (ASMM/AMM).","when":"Cuando se añade RAM al servidor, ante un cuello de botella de memoria o presión de memoria.","caution":"sga_target no puede superar sga_max_size. sga_max_size es estático y requiere reinicio. Deje RAM para el sistema operativo y otras instancias; una asignación excesiva provoca swapping/OOM.","tags":["sga","pga","memory","memory","target","sga_target","pga_aggregate_target"]},{"id":"kill-session","task":"Terminar una sesión (kill session)","category":"Sesiones y procesos","command":"-- First find the session to kill:\nSELECT sid, serial#, username, status, machine, program\nFROM   v$session\nWHERE  username = 'CUSTOMER_APP' AND status = 'ACTIVE';\n\n-- Kill it (starts rollback immediately):\nALTER SYSTEM KILL SESSION '145,12345' IMMEDIATE;\n\n-- If it hangs, the OS process can be terminated:\n-- ALTER SYSTEM DISCONNECT SESSION '145,12345' IMMEDIATE;","what":"Termina la sesión con el SID,SERIAL# indicado; se hace rollback de su transacción abierta.","when":"Para detener una sesión que retiene bloqueos, ejecuta una consulta desbocada o tiene fugas de conexiones.","caution":"Si hay una transacción abierta, el rollback puede tardar mucho (termina rápido si se hizo poco trabajo). Verifique primero con una consulta para no terminar el SID equivocado. El rollback de un DML grande genera carga de E/S.","tags":["kill session","kill session","v$session","alter system","sid","serial"]},{"id":"long-running-queries","task":"Ver consultas / sesiones de larga duración","category":"Sesiones y procesos","command":"-- Active, long-running SQL:\nSELECT s.sid, s.serial#, s.username, s.status,\n       s.last_call_et AS seconds_active,\n       s.sql_id, q.sql_text,\n       s.event, s.machine, s.program\nFROM   v$session s\n  LEFT JOIN v$sql q ON q.sql_id = s.sql_id\n                   AND q.child_number = s.sql_child_number\nWHERE  s.type = 'USER'\n  AND  s.status = 'ACTIVE'\n  AND  s.last_call_et > 60\nORDER BY s.last_call_et DESC;\n\n-- Long operations with percentage progress:\nSELECT sid, serial#, opname, sofar, totalwork,\n       ROUND(sofar/NULLIF(totalwork,0)*100,1) AS pct, time_remaining\nFROM   v$session_longops\nWHERE  sofar < totalwork ORDER BY start_time DESC;","what":"Lista las sesiones activas que llevan más de 60 segundos y el progreso de las operaciones por lotes de larga duración.","when":"Ante quejas de lentitud, para encontrar qué consulta mantiene ocupado el sistema.","caution":"last_call_et es el tiempo transcurrido desde la última llamada; también puede ser grande en una sesión inactiva, por lo que el filtro status='ACTIVE' es importante. Solo consulta; sin riesgo.","tags":["long running","long query","v$session","v$sql","v$session_longops","active"]},{"id":"find-blocking","task":"Encontrar la sesión bloqueante","category":"Sesiones y procesos","command":"SELECT bl.sid       AS blocker_sid,\n       bl.serial#   AS blocker_serial,\n       bl.username  AS blocker_user,\n       w.sid        AS waiter_sid,\n       w.serial#    AS waiter_serial,\n       w.username   AS waiter_user,\n       w.seconds_in_wait AS wait_sec,\n       w.event\nFROM   v$session w\n  JOIN v$session bl ON bl.sid = w.blocking_session\nWHERE  w.blocking_session IS NOT NULL\nORDER BY w.seconds_in_wait DESC;","what":"Lista los pares de sesiones bloqueante y en espera con el tiempo de espera y el evento.","when":"Para encontrar al culpable en problemas de bloqueo como \"aplicación colgada / espera enq: TX\".","caution":"Confirme con el responsable de negocio antes de terminar la sesión bloqueante; puede ser una operación crítica. Solo consulta; sin riesgo.","tags":["blocking","lock","lock","blocker","waiter","v$session","blocking_session"]},{"id":"gather-stats","task":"Recopilar estadísticas (DBMS_STATS)","category":"Mantenimiento","command":"-- Single table (including cascaded indexes):\nEXEC DBMS_STATS.GATHER_TABLE_STATS(ownname => 'CUSTOMER_APP', tabname => 'CUSTOMER', cascade => TRUE, estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE, degree => 4);\n\n-- Whole schema:\nEXEC DBMS_STATS.GATHER_SCHEMA_STATS(ownname => 'CUSTOMER_APP', estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE, cascade => TRUE, degree => 4);\n\n-- Whole database (run only in a maintenance window / off-peak hours):\nEXEC DBMS_STATS.GATHER_DATABASE_STATS(estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE, degree => 4);","what":"Recopila las estadísticas de tablas/índices para el optimizador; garantiza que se generen planes de ejecución correctos.","when":"Tras DML/cargas masivas, cuando los planes empeoran, tras crear un índice o tabla nuevos.","caution":"Los cambios de estadísticas pueden cambiar los planes de ejecución; en sistemas críticos pruebe/compare primero con AWR. El paralelismo DEGREE consume CPU. GATHER_DATABASE_STATS es pesado en horas de alta carga.","tags":["dbms_stats","statistics","statistics","gather","optimizer","maintenance"]},{"id":"rebuild-index-online","task":"Reconstruir un índice en línea","category":"Mantenimiento","command":"-- Regular index:\nALTER INDEX customer_app.idx_customer_email REBUILD ONLINE PARALLEL 4;\n\n-- Moving it to a different tablespace:\nALTER INDEX customer_app.idx_customer_email\n  REBUILD ONLINE TABLESPACE customer_idx PARALLEL 4;\n\n-- Reset parallelism after the rebuild (so plans are not affected):\nALTER INDEX customer_app.idx_customer_email NOPARALLEL;","what":"Reconstruye el índice sin bloquear el DML (ONLINE); elimina la fragmentación y puede cambiar el tablespace.","when":"Bloat/fragmentación del índice, mover un índice a otro tablespace, reparar un índice UNUSABLE.","caution":"ONLINE usa espacio temporal e I/O adicionales. PARALLEL persiste; restablézcalo con NOPARALLEL tras la reconstrucción, o las consultas se ejecutarán en paralelo sin querer. ONLINE no está soportado para índices bitmap en todas las versiones. La reconstrucción ONLINE solo está disponible en Enterprise Edition.","tags":["index","rebuild","online","rebuild","alter index","fragmentation","maintenance"]},{"id":"rman-full-backup","task":"Backup completo rápido con RMAN","category":"Mantenimiento","command":"# From the shell: rman target /\nRUN {\n  ALLOCATE CHANNEL c1 DEVICE TYPE DISK;\n  ALLOCATE CHANNEL c2 DEVICE TYPE DISK;\n  BACKUP AS COMPRESSED BACKUPSET\n    DATABASE\n    PLUS ARCHIVELOG\n    TAG 'FULL_DAILY';\n  BACKUP CURRENT CONTROLFILE;\n  DELETE NOPROMPT OBSOLETE;\n  RELEASE CHANNEL c1;\n  RELEASE CHANNEL c2;\n}","what":"Toma un backup completo comprimido de toda la base de datos y de los logs archivados en paralelo por dos canales, respalda el controlfile y purga los backups obsoletos.","when":"Backup completo diario/semanal, para crear un punto de recuperación.","caution":"COMPRESSED consume CPU. DELETE OBSOLETE elimina los backups antiguos según la política de retención; configure la política (CONFIGURE RETENTION POLICY) correctamente. ARCHIVELOG requiere que la BD esté en modo ARCHIVELOG. Asegúrese de que el destino del backup tenga espacio suficiente.","tags":["rman","backup","backup","full","compressed","archivelog","maintenance"]},{"id":"controlfile-trace","task":"Tomar un backup en trace del controlfile","category":"Mantenimiento","command":"-- Generate a trace (CREATE CONTROLFILE script):\nALTER DATABASE BACKUP CONTROLFILE TO TRACE\n  AS '/tmp/create_controlfile.sql' REUSE;\n\n-- Binary controlfile copy:\nALTER DATABASE BACKUP CONTROLFILE TO '/backup/controlfile_orcl.ctl' REUSE;","what":"Genera el script SQL (trace) que recrea el controlfile y/o una copia binaria de él.","when":"Tras cambios estructurales (añadir datafiles/redo), al preparar la recuperación ante desastres, o para recrear el controlfile si se pierden todas las copias.","caution":"Actualice el archivo trace cada vez que cambie la estructura; un script antiguo crea una estructura incorrecta. REUSE sobrescribe un archivo existente. Sin riesgo para el sistema, pero guarde el archivo generado de forma segura.","tags":["controlfile","trace","backup","backup","alter database","recovery","maintenance"]}]},"recipes/oracle-ha.json":{"engineId":"oracle","engine":"Oracle","order":1,"recipes":[{"id":"rac-srvctl-db-status","task":"Comprobar el estado de la base de datos, instancias y servicios RAC con srvctl","category":"RAC y Grid","command":"# Which node each instance runs on, and in what state:\nsrvctl status database -db <db_unique_name> -verbose\n# Configuration registered in Clusterware (Oracle home, spfile, disk group, policy, role):\nsrvctl config database -db <db_unique_name>\n# A single instance:\nsrvctl status instance -db <db_unique_name> -instance <instance>\n# Which instances the services are running on:\nsrvctl status service -db <db_unique_name>","what":"Muestra el estado en tiempo de ejecución de la base de datos, de cada instancia y de sus servicios según Clusterware (CRS), además de la configuración registrada; -verbose añade el modo de apertura de la instancia.","when":"Como primera comprobación antes y después de cualquier intervención en RAC; durante una caída, para ver qué nodo/instancia está afectado.","caution":"Solo lectura. Ejecútelo desde el Oracle home de la base de datos (usuario oracle); un srvctl de un Grid home de otra versión puede devolver resultados engañosos o errores.","since":"12c","note":"Las opciones largas -db/-instance/-service son de 12c+; las opciones cortas de 11g (-d, -i, -s) siguen funcionando en 19c pero están obsoletas.","tags":["rac","srvctl","status","instance","service","cluster","config"],"untested":true},{"id":"rac-srvctl-start-stop","task":"Detener / iniciar una base de datos RAC o una sola instancia con srvctl (rolling)","category":"RAC y Grid","command":"# Whole database (all instances):\nsrvctl stop database -db <db_unique_name> -stopoption immediate\nsrvctl start database -db <db_unique_name>\n# Rolling maintenance: stop one instance, moving its services to the other instances\nsrvctl stop instance -db <db_unique_name> -instance <instance> \\\n  -stopoption immediate -drain_timeout 300 -failover\nsrvctl start instance -db <db_unique_name> -instance <instance>\n# By node:\nsrvctl stop instance -db <db_unique_name> -node <node> -stopoption immediate\n# Start in a specific open mode (e.g. a physical standby in mount):\nsrvctl start database -db <db_unique_name> -startoption mount","what":"Detiene/inicia la base de datos o la instancia seleccionada de forma controlada a través de Clusterware; con -failover los servicios de la instancia detenida se mueven a instancias available, y -drain_timeout espera a que se drenen las sesiones.","when":"Para reinicios rolling (nodo a nodo) durante parches, mantenimiento del SO o cambios de parámetros; para una parada completa planificada.","caution":"En RAC use srvctl en lugar de SHUTDOWN de SQL*Plus; de lo contrario Clusterware no conoce el TARGET del recurso y puede reiniciar la instancia. stop database detiene todas las instancias = caída total. Si los servicios no tienen instancias available definidas, -failover no puede mover las conexiones.","since":"12c","note":"-drain_timeout y -failover son de 12.2+. Use -stopoption abort solo en emergencias; el siguiente arranque requiere instance recovery.","tags":["rac","srvctl","stop","start","rolling","drain","shutdown"],"untested":true},{"id":"rac-service-add","task":"Añadir un servicio RAC con instancias preferred/available (incluido Application Continuity)","category":"RAC y Grid","command":"srvctl add service -db <db_unique_name> -service <service> \\\n  -preferred <inst1>,<inst2> -available <inst3> \\\n  -pdb <pdb> -role PRIMARY -policy AUTOMATIC \\\n  -failovertype TRANSACTION -commit_outcome TRUE \\\n  -failoverretry 30 -failoverdelay 10 \\\n  -clbgoal SHORT -rlbgoal SERVICE_TIME -notification TRUE \\\n  -drain_timeout 300 -stopoption IMMEDIATE\nsrvctl start service -db <db_unique_name> -service <service>\n# Verify:\nsrvctl config service -db <db_unique_name> -service <service>\nsrvctl status service -db <db_unique_name> -service <service>","what":"Define un servicio para aplicaciones que normalmente se ejecuta en las instancias preferred y pasa a una instancia available cuando estas fallan; con -role PRIMARY solo se inicia en el rol primario bajo Data Guard.","when":"Para conectar las aplicaciones a un servicio en lugar de a una instancia, repartir cargas de trabajo entre nodos y habilitar un failover transparente (drain/failover) durante el mantenimiento planificado.","caution":"Las aplicaciones deben conectarse con este nombre de servicio (a través de SCAN), no con un SID o nombre de instancia. -failovertype TRANSACTION (Application Continuity) requiere soporte de la aplicación/driver; si no está seguro, empiece con -failovertype SELECT (TAF) o NONE. No use los servicios SYS$ ni el servicio por defecto con el nombre de db_unique_name para aplicaciones.","since":"12c","note":"-pdb es de 12c+, -drain_timeout y -stopoption son de 12.2+. Cámbielo con srvctl modify service; un cambio permanente de preferred requiere -modifyconfig.","tags":["rac","service","srvctl","preferred","available","taf","application continuity"],"untested":true},{"id":"rac-service-relocate","task":"Reubicar un servicio RAC en otra instancia","category":"RAC y Grid","command":"# First check the current placement:\nsrvctl status service -db <db_unique_name> -service <service>\n# Relocate by instance (sessions are drained):\nsrvctl relocate service -db <db_unique_name> -service <service> \\\n  -oldinst <inst1> -newinst <inst3> -drain_timeout 120 -wait YES\n# By node (policy-managed / singleton service):\nsrvctl relocate service -db <db_unique_name> -service <service> \\\n  -currentnode <node1> -targetnode <node2>\n# Session distribution per instance (SQL):\nSELECT inst_id, service_name, COUNT(*) AS sessions\nFROM   gv$session\nWHERE  type = 'USER'\nGROUP  BY inst_id, service_name\nORDER  BY service_name, inst_id;","what":"Mueve un servicio en ejecución de una instancia/nodo a otro; las conexiones nuevas van a la instancia de destino y las sesiones existentes se dejan cerrar por sí solas durante drain_timeout.","when":"Para vaciar un nodo antes del mantenimiento, devolver un servicio a su instancia preferred tras un failover o reequilibrar la carga.","caution":"La reubicación es temporal: no cambia la configuración del servicio (preferred/available); cuando la instancia se reinicia, el servicio vuelve a su instancia preferred. -force mata las sesiones existentes; si la aplicación no puede reconectarse, los usuarios ven una caída.","since":"12c","note":"-drain_timeout y -wait son de 12.2+. La consulta SQL también funciona en una sola instancia; la parte de srvctl requiere RAC.","tags":["rac","service","relocate","move","srvctl","drain","gv$session"],"untested":true},{"id":"rac-crsctl-stat-res","task":"Comprobar el estado de los recursos del clúster (crsctl stat res -t)","category":"RAC y Grid","command":"# From the Grid home, as the grid user:\ncrsctl stat res -t\n# OHASD (init) layer: ora.cssd, ora.crsd, ora.asm, ora.ctssd...\ncrsctl stat res -t -init\n# Only problem resources (TARGET is ONLINE but STATE is not):\ncrsctl stat res -t -w \"((TARGET = ONLINE) and (STATE != ONLINE))\"\n# Profile and runtime details of a single resource:\ncrsctl stat res ora.<db_unique_name>.db -p\ncrsctl stat res ora.<db_unique_name>.db -v","what":"Lista todos los recursos gestionados por Clusterware (base de datos, instancia, listener, SCAN, VIP, ASM, disk group, red) por nodo con TARGET/STATE en forma de tabla.","when":"Tras el arranque de un nodo, tras aplicar parches y en cada investigación de caída; para encontrar recursos OFFLINE, INTERMEDIATE o UNKNOWN.","caution":"Solo lectura. No gestione los recursos ora.* directamente con crsctl start/stop/modify; use srvctl para los recursos de base de datos (el método soportado).","note":"El nombre del recurso contiene el db_unique_name en minúsculas (ora.orcl.db). La salida de -init solo es completamente visible como root/grid.","tags":["rac","grid","crsctl","resource","resource","clusterware","status"],"untested":true},{"id":"rac-crsctl-check-cluster","task":"Comprobar la salud de la pila del clúster, la lista de nodos y la versión de Clusterware","category":"RAC y Grid","command":"# CRS, CSS, EVM health check on all nodes:\ncrsctl check cluster -all\n# Full stack on this node, including OHASD:\ncrsctl check crs\n# Node list: number, status (Active/Inactive), pinned:\nolsnodes -n -s -t\n# Active version, patch level and upgrade state (after a rolling patch):\ncrsctl query crs activeversion -f\ncrsctl query crs softwareversion -all","what":"Muestra que los daemons Cluster Ready Services, Cluster Synchronization Services y Event Manager se ejecutan en todos los nodos, los miembros del clúster y la versión activa/de software de Clusterware.","when":"En la comprobación de salud matinal, tras un node eviction o reinicio, y tras un parche/upgrade rolling para confirmar que todos los nodos están al mismo nivel.","caution":"Solo lectura. Si activeversion -f muestra \"ROLLING PATCH\" o un nivel de parche distinto, hay una operación rolling incompleta; termínela (crsctl stop rollingpatch / rootcrs -postpatch) antes de hacer nuevos cambios.","since":"12c","note":"activeversion -f y softwareversion -all son de 12c+.","tags":["rac","grid","crsctl","olsnodes","health","cluster","version"],"untested":true},{"id":"rac-ocr-backup","task":"Comprobar la integridad del OCR y tomar un backup del OCR (ocrcheck / ocrconfig)","category":"RAC y Grid","command":"# OCR integrity and capacity check (run as root for the logical check):\nocrcheck\nocrcheck -config\n# Automatic backups (every 4 hours, daily, weekly):\nocrconfig -showbackup\n# Manual backup before a risky change (root):\nocrconfig -manualbackup\nocrconfig -showbackup manual\n# Move the backup location to a disk group (root):\nocrconfig -backuploc +<diskgroup>","what":"Muestra la integridad, las ubicaciones y los backups del Oracle Cluster Registry (el repositorio de configuración del clúster); -manualbackup toma un backup físico inmediato del OCR.","when":"Justo antes de cambios en la configuración del clúster como añadir/quitar nodos, parches/upgrade de Grid, cambios de voting disk o de red (oifcfg).","caution":"ocrconfig -restore debe ejecutarse con CRS detenido en todos los nodos y en modo exclusivo; restaurar el backup equivocado puede dejar el clúster sin poder arrancar, así que hágalo con Oracle Support. Los backups automáticos pueden quedar por defecto en el disco local de un solo nodo; mueva la ubicación a un disk group compartido.","note":"Ejecutado como usuario grid, ocrcheck omite la comprobación de corrupción lógica (\"Logical corruption check bypassed\"). Los backups a un disk group son de 12c+.","tags":["rac","ocr","ocrcheck","ocrconfig","backup","backup","grid"],"untested":true},{"id":"rac-votedisk-query","task":"Comprobar los voting disks y los timeouts de CSS","category":"RAC y Grid","command":"# Voting disks, their state and disk group:\ncrsctl query css votedisk\n# Node eviction thresholds (seconds):\ncrsctl get css misscount\ncrsctl get css disktimeout\ncrsctl get css reboottime\n# Move the voting disks to another ASM disk group (root/grid):\ncrsctl replace votedisk +<diskgroup>","what":"Muestra el estado de los voting disks (voting files) usados para la pertenencia al clúster y los timeouts del heartbeat de red/disco; replace votedisk mueve todos los voting files al disk group de destino.","when":"Durante cambios de almacenamiento, rediseño de disk groups o la investigación de un node eviction; para confirmar que una mayoría (quorum) de voting disks es accesible.","caution":"Un nodo que no puede acceder a más de la mitad de los voting disks se reinicia a sí mismo. El disk group de destino necesita suficientes failure groups (normal redundancy: 3, high: 5; use un quorum failgroup si es necesario). No cambie misscount/disktimeout sin la aprobación de Oracle Support.","note":"Valores por defecto: misscount 30 s, disktimeout 200 s (Linux).","tags":["rac","voting disk","votedisk","css","misscount","quorum","eviction"],"untested":true},{"id":"rac-cluvfy-checks","task":"Ejecutar comprobaciones previas/posteriores con Cluster Verification Utility (cluvfy)","category":"RAC y Grid","command":"# Full check after a Grid installation/change:\ncluvfy stage -post crsinst -n all -verbose\n# Before adding a node:\ncluvfy stage -pre nodeadd -n <new_node> -verbose\n# Component checks:\ncluvfy comp nodecon -n all -verbose\ncluvfy comp clocksync -n all -verbose\ncluvfy comp ocr -n all -verbose\ncluvfy comp asm -n all -verbose\n# Report only failed checks:\ncluvfy stage -post crsinst -n all -verbose | grep -iE \"failed|warning\"","what":"Valida la conectividad de red, la sincronización de reloj, OCR, ASM, la equivalencia de usuarios y los requisitos previos del SO en todos los nodos; informa de los problemas con correcciones sugeridas.","when":"Antes y después de la instalación de Grid, la adición de nodos y los upgrades; como validación de referencia ante problemas de clúster sin explicación.","caution":"Solo lectura, pero se conecta a todos los nodos por ssh; el usuario grid necesita equivalencia ssh. No ejecute los scripts -fixup (root) sin revisarlos; cambian parámetros del SO.","note":"Para un análisis más amplio de buenas prácticas use orachk/exachk de AHF. Descargar el cluvfy más reciente de My Oracle Support ofrece más comprobaciones.","tags":["rac","cluvfy","check","verification","precheck","grid","orachk"],"untested":true},{"id":"rac-node-eviction-diag","task":"Diagnosticar la causa de un node eviction / reinicio inesperado de un nodo","category":"RAC y Grid","command":"# 1) When, which node, evicted by whom: CRS alert log (on every node)\ngrep -E \"CRS-1601|CRS-1607|CRS-1608|CRS-1609|CRS-1610|CRS-1611|CRS-1612|CRS-8011\" \\\n  $ORACLE_BASE/diag/crs/$(hostname -s)/crs/trace/alert.log\n# 2) CSS details: was the network heartbeat or the disk heartbeat lost\ngrep -iE \"evict|misscount|heartbeat|disk HB|network HB\" \\\n  $ORACLE_BASE/diag/crs/$(hostname -s)/crs/trace/ocssd.trc | tail -100\n# 3) Who triggered the reboot (cssdagent/cssdmonitor):\nls -lt $ORACLE_BASE/diag/crs/$(hostname -s)/crs/trace/ohasd_cssd*.trc\n# 4) OS resources at the time of the event (Cluster Health Monitor):\noclumon dumpnodeview -allnodes -s \"2026-01-15 03:10:00\" -e \"2026-01-15 03:20:00\"\n# 5) Diagnostic collection from all nodes (AHF/TFA):\ntfactl diagcollect -crs -os -last 4h\n# 6) OS side: /var/log/messages, journalctl -k (OOM, NIC, multipath, I/O errors)","what":"Sigue en orden la cadena del node eviction: mensajes CRS-16xx en el alert log de CRS, pérdidas de heartbeat en ocssd.trc, métricas del SO de CHM y los logs de todos los nodos recopilados con TFA.","when":"Cuando un nodo se reinicia inesperadamente o es expulsado del clúster, para el análisis de causa raíz (RCA) y un SR de Oracle Support.","caution":"Recopile logs de todos los nodos que cubran la hora del evento; los archivos trace rotan y se pueden perder evidencias. Verifique que las zonas horarias sean iguales en todos los nodos (CTSS/NTP). No aplique \"correcciones\" como subir misscount antes de recopilar las evidencias.","note":"Causas típicas: caída/pérdida de paquetes en el interconnect privado, latencia de I/O en los voting disks, agotamiento de CPU/memoria (OOM, swap), saltos de reloj. La ruta corresponde a la estructura ADR de 12c+; en 11g está en $GRID_HOME/log/<host>/.","tags":["rac","eviction","reboot","ocssd","tfactl","oclumon","rca"],"untested":true},{"id":"rac-gv-cluster-waits","task":"Analizar los wait events de todo el clúster (gc waits) por instancia con GV$","category":"RAC y Grid","command":"-- Cluster-class waits per instance (since startup):\nSELECT inst_id, event, total_waits,\n       ROUND(time_waited_micro/1e6) AS time_waited_s,\n       ROUND(time_waited_micro/NULLIF(total_waits,0)/1000, 2) AS avg_ms\nFROM   gv$system_event\nWHERE  wait_class = 'Cluster'\nORDER  BY time_waited_micro DESC\nFETCH FIRST 15 ROWS ONLY;\n\n-- Sessions waiting right now, by instance and event:\nSELECT inst_id, event, COUNT(*) AS sessions\nFROM   gv$session\nWHERE  wait_class <> 'Idle' AND state = 'WAITING'\nGROUP  BY inst_id, event\nORDER  BY sessions DESC;\n\n-- ASH for the last 15 minutes: which instance is waiting on gc\nSELECT inst_id, event, COUNT(*) AS samples\nFROM   gv$active_session_history\nWHERE  sample_time > SYSTIMESTAMP - INTERVAL '15' MINUTE\n  AND  wait_class = 'Cluster'\nGROUP  BY inst_id, event\nORDER  BY samples DESC\nFETCH FIRST 10 ROWS ONLY;","what":"Usa las vistas GV$ para mostrar en una sola consulta las esperas de clase Cluster (gc cr/current block 2-way/3-way, gc buffer busy, gc cr request) y su latencia media en todas las instancias.","when":"Cuando los usuarios reportan lentitud en RAC, para saber si el problema viene del interconnect/global cache y en qué instancia se concentra.","caution":"gv$active_session_history requiere la licencia de Diagnostics Pack. Si avg_ms supera unos pocos ms, revise primero el interconnect (MTU, pérdida de paquetes, netstat -s) y la CPU de LMS; gc buffer busy suele apuntar a un bloque caliente de la aplicación (secuencia, índice de lado derecho).","note":"También funciona en una sola instancia (devuelve inst_id = 1).","tags":["rac","gv$","gc","wait","wait event","interconnect","global cache"]},{"id":"asm-asmcmd-lsdg-du","task":"Mostrar el uso de disk groups, discos y directorios con asmcmd (lsdg / lsdsk / du)","category":"ASM","command":"# As the grid user, with ORACLE_SID=+ASM1 (or +ASM):\nasmcmd lsdg\n# Disks in the disk group: -k sizes, -p state (header/mount/mode):\nasmcmd lsdsk -k -G <diskgroup>\nasmcmd lsdsk -p -G <diskgroup>\n# I/O error and timing statistics:\nasmcmd lsdsk --statistics -G <diskgroup>\n# Disks that can be added (CANDIDATE/PROVISIONED/FORMER):\nasmcmd lsdsk --candidate -p\n# Space used by a database directory (MB):\nasmcmd du +<diskgroup>/<db_unique_name>\nasmcmd ls -l -s +<diskgroup>/<db_unique_name>/DATAFILE","what":"Muestra desde la línea de comandos la redundancia, el espacio total/libre y el Usable_file_MB de los disk groups, el estado de los discos y el espacio que ocupa una base de datos en ASM.","when":"Para comprobaciones de capacidad, identificar discos candidatos antes de añadir discos y averiguar qué base de datos consume espacio cuando salta una alerta de \"disk group full\".","caution":"Solo lectura. Si Usable_file_MB en lsdg es negativo, el disk group no puede restaurar la redundancia tras perder un disco/failgroup; libere espacio o añada discos de inmediato. du devuelve dos valores: Used_MB y Mirror_used_MB (uso con espejo).","note":"Para el equivalente en SQL vea las recetas asm-diskgroup-space y asm-disk-health.","tags":["asm","asmcmd","lsdg","lsdsk","du","space","disk group"],"untested":true},{"id":"asm-disk-health","task":"Comprobar con SQL el estado de los discos ASM, los contadores de errores y los discos candidatos","category":"ASM","command":"-- On the ASM instance (or from a DB instance; there only the disk groups in use are visible):\nSELECT dg.name AS diskgroup, d.disk_number, d.name AS disk_name, d.path,\n       d.header_status, d.mount_status, d.mode_status, d.state,\n       d.failgroup, d.total_mb, d.free_mb, d.read_errs, d.write_errs\nFROM   v$asm_disk d\nLEFT   JOIN v$asm_diskgroup dg ON dg.group_number = d.group_number\nORDER  BY dg.name, d.disk_number;\n\n-- Disk group summary: AU size, offline disks, voting files:\nSELECT name, type, allocation_unit_size/1048576 AS au_mb,\n       compatibility, database_compatibility, offline_disks, voting_files\nFROM   v$asm_diskgroup;\n\n-- Databases using this ASM:\nSELECT instance_name, db_name, status, software_version, compatible_version\nFROM   v$asm_client;","what":"Lista el header_status de cada disco (MEMBER, CANDIDATE, PROVISIONED, FORMER), el estado de mount/mode, el failgroup y los contadores de errores de lectura/escritura; el número de discos offline por disk group y los clientes de ASM.","when":"Antes de añadir discos, para comprobar que un LUN nuevo aparece como CANDIDATE/PROVISIONED; ante un fallo de disco, para ver qué disco está OFFLINE y si hay errores de I/O.","caution":"Solo lectura. v$asm_disk ejecuta el descubrimiento de discos en cada llamada y es lento en sistemas con muchos discos; para el monitoreo frecuente use v$asm_disk_stat (sin descubrimiento, no muestra discos nuevos). Nunca añada con FORCE a otro disk group un disco cuyo header_status sea MEMBER; destruye los datos existentes.","note":"No devuelve filas sin ASM; una salida útil requiere una instancia ASM.","tags":["asm","v$asm_disk","disk","header_status","error","candidate","failgroup"]},{"id":"asm-diskgroup-attributes","task":"Ver y cambiar los atributos de un disk group ASM (compatible, disk_repair_time)","category":"ASM","command":"-- Current attributes:\nSELECT dg.name AS diskgroup, a.name, a.value\nFROM   v$asm_attribute a\nJOIN   v$asm_diskgroup dg ON dg.group_number = a.group_number\nWHERE  a.name IN ('compatible.asm','compatible.rdbms','au_size','disk_repair_time',\n                  'failgroup_repair_time','content.type','sector_size')\nORDER  BY dg.name, a.name;\n\n-- Change (ASM instance, as SYSASM):\nALTER DISKGROUP <diskgroup> SET ATTRIBUTE 'disk_repair_time' = '8h';\nALTER DISKGROUP <diskgroup> SET ATTRIBUTE 'compatible.asm'   = '19.0.0.0.0';\nALTER DISKGROUP <diskgroup> SET ATTRIBUTE 'compatible.rdbms' = '19.0.0.0.0';\n\n-- asmcmd equivalent:\n-- asmcmd lsattr -l -G <diskgroup>\n-- asmcmd setattr -G <diskgroup> disk_repair_time '8h'","what":"Muestra y cambia los niveles de compatibilidad del disk group (compatible.asm/rdbms), el tamaño de AU y cuánto tiempo se conserva un disco con fallo temporal antes de eliminarlo (disk_repair_time).","when":"Tras un upgrade de Grid, para habilitar nuevas funciones de ASM (flex disk groups, límites de archivo mayores), o para ampliar disk_repair_time durante un mantenimiento planificado del almacenamiento.","caution":"compatible.asm y compatible.rdbms NO SE PUEDEN BAJAR UNA VEZ SUBIDOS. Si compatible.rdbms es mayor que el COMPATIBLE de la base de datos más antigua que usa este disk group, esa base de datos no puede montarlo. Suba primero compatible.asm y después compatible.rdbms. au_size solo se puede definir al crear el disk group.","note":"Sin ASM la consulta no devuelve filas. El disk_repair_time por defecto es 12h.","tags":["asm","attribute","compatible","disk_repair_time","disk group","au_size","v$asm_attribute"],"untested":true},{"id":"dg-show-configuration","task":"Mostrar la configuración del broker de Data Guard y el estado de las bases de datos (DGMGRL)","category":"Data Guard","command":"# From the shell (connect with a password; also required for switchover/failover):\ndgmgrl sys@<primary_db_unique_name>\nSHOW CONFIGURATION VERBOSE;\nSHOW DATABASE VERBOSE '<standby_db_unique_name>';\n# Monitorable properties:\nSHOW DATABASE '<primary_db_unique_name>' 'LogXptStatus';\nSHOW DATABASE '<standby_db_unique_name>' 'RecvQEntries';\nSHOW DATABASE '<primary_db_unique_name>' 'InconsistentLogXptProps';\nSHOW DATABASE '<standby_db_unique_name>' 'StatusReport';","what":"Muestra el estado general de la configuración del broker (SUCCESS/WARNING/ERROR), el modo de protección, el rol de cada base de datos, el lag de transporte/aplicación y las inconsistencias entre el broker y el spfile.","when":"Antes y después de cada operación de Data Guard (switchover, failover, cambio de parámetros); en la comprobación de salud diaria.","caution":"Solo lectura. Con el broker habilitado, no cambie con SQL parámetros de Data Guard como log_archive_dest_n o log_archive_config; el broker los sobrescribe con sus propios valores. Haga los cambios con EDIT DATABASE ... SET PROPERTY, y en 19c+ use EDIT DATABASE ... SET PARAMETER para los parámetros de inicialización.","note":"StatusReport lista los errores ORA- por base de datos; es la forma más rápida de encontrar la causa de un estado WARNING. InconsistentProperties está obsoleto en 19c y siempre devuelve vacío; use InconsistentLogXptProps para las inconsistencias del transporte de redo.","tags":["data guard","dgmgrl","broker","configuration","standby","status","lag"],"untested":true},{"id":"dg-readiness-sql","task":"Comprobar con SQL el rol primary/standby y la preparación para switchover","category":"Data Guard","command":"SELECT name, db_unique_name, database_role, open_mode, protection_mode,\n       switchover_status, force_logging, flashback_on, dataguard_broker\nFROM   v$database;\n\n-- State of redo destinations and gaps (on the primary):\nSELECT dest_id, dest_name, status, type, database_mode,\n       recovery_mode, gap_status, error\nFROM   v$archive_dest_status\nWHERE  status <> 'INACTIVE';\n\n-- Missing archivelog range on the standby:\nSELECT thread#, low_sequence#, high_sequence# FROM v$archive_gap;","what":"Muestra el rol de la base de datos, el modo de apertura, el modo de protección, switchover_status (TO STANDBY / TO PRIMARY / SESSIONS ACTIVE / NOT ALLOWED), el estado de FORCE LOGGING y flashback, además de los errores y gaps de los destinos de redo.","when":"Como comprobación previa a un switchover/failover, tras construir un standby nuevo y para el monitoreo básico donde no se usa el broker.","caution":"Solo lectura. Si FORCE_LOGGING=NO, las operaciones NOLOGGING dejan bloques corruptos en el standby; ALTER DATABASE FORCE LOGGING debe estar habilitado en un primario de Data Guard. Si FLASHBACK_ON=NO, el antiguo primario no se puede reinstaurar tras un failover y debe reconstruirse.","note":"También funciona en el primario; v$archive_gap solo tiene sentido en un physical standby.","tags":["data guard","v$database","switchover_status","force logging","gap","role","standby"]},{"id":"dg-lag-apply-rate","task":"Medir el lag de transporte/aplicación, la velocidad de aplicación y los procesos de Data Guard en el standby","category":"Data Guard","command":"-- Run on the standby:\nSELECT name, value, unit, time_computed, datum_time\nFROM   v$dataguard_stats\nWHERE  name IN ('transport lag','apply lag','apply finish time','estimated startup time');\n\n-- Redo apply rate (KB/s):\nSELECT item, units, sofar, total, TO_CHAR(timestamp,'YYYY-MM-DD HH24:MI:SS') AS ts\nFROM   v$recovery_progress\nWHERE  item IN ('Active Apply Rate','Average Apply Rate','Maximum Apply Rate','Last Applied Redo')\nORDER  BY start_time DESC, item;\n\n-- Transport and apply processes (12.2+; replaces v$managed_standby):\nSELECT name, role, action, thread#, sequence#, block#, delay_mins\nFROM   v$dataguard_process\nORDER  BY role;","what":"Muestra cuánto va retrasado el standby respecto al primario (transport lag = redo aún no recibido, apply lag = redo aún no aplicado), cuándo se aplicará el atraso, la velocidad de aplicación y qué proceso trabaja en qué secuencia.","when":"Para el monitoreo de RPO/RTO y, ante una alerta de lag, para saber si el problema está en la red (transporte) o en la I/O/CPU del standby (aplicación).","caution":"Solo lectura. Si datum_time es antiguo (muy por detrás de time_computed), el standby no está recibiendo datos del primario y el valor de lag no es fiable. Compruebe si DelayMins está definido; un retraso intencionado parece apply lag.","since":"12c","note":"v$dataguard_process es de 12.2+. Sin standby, v$dataguard_stats no devuelve filas.","tags":["data guard","lag","lag","apply","transport","v$dataguard_stats","rpo"]},{"id":"dg-validate-database","task":"Validar el standby con el broker antes de un switchover/failover (VALIDATE DATABASE)","category":"Data Guard","command":"VALIDATE DATABASE '<standby_db_unique_name>';\nVALIDATE DATABASE VERBOSE '<standby_db_unique_name>';\n# spfile parameter differences between the two sides:\nVALIDATE DATABASE '<standby_db_unique_name>' SPFILE;\n# Network and static listener registrations (18c+):\nVALIDATE NETWORK CONFIGURATION FOR ALL;\nVALIDATE STATIC CONNECT IDENTIFIER FOR ALL;","what":"Comprueba si el standby está listo para un cambio de rol (\"Ready for Switchover / Failover: Yes\"), el número/tamaño de los standby redo logs, el estado de flashback y de aplicación, las diferencias del spfile y los servicios estáticos que el broker necesita para reiniciar instancias.","when":"Como paso obligatorio antes de cada switchover planificado; tras construir un standby nuevo y tras cambios de red/listener.","caution":"Solo lectura. Si ve \"Ready for Switchover: No\" o una advertencia sobre los standby redo logs, no continúe con el switchover. Un error de static connect identifier significa que el broker no puede iniciar la instancia durante un switchover, lo que obliga a intervenir manualmente.","since":"12c","note":"VALIDATE DATABASE es de 12.1+, la opción SPFILE es de 12.2+, NETWORK CONFIGURATION y STATIC CONNECT IDENTIFIER son de 18c+.","tags":["data guard","dgmgrl","validate","validate","switchover","standby redo log","broker"],"untested":true},{"id":"dg-switchover","task":"Realizar un switchover planificado (DGMGRL, cambio de rol sin pérdida de datos)","category":"Data Guard","command":"# Connect with a password (with OS authentication \"/\" the broker cannot restart the remote instance):\ndgmgrl sys@<primary_db_unique_name>\n# 1) Precheck:\nSHOW CONFIGURATION;\nVALIDATE DATABASE '<standby_db_unique_name>';\n# 2) Role change:\nSWITCHOVER TO '<standby_db_unique_name>';\n# 3) Verify: new primary and new standby\nSHOW CONFIGURATION;\nSHOW DATABASE '<primary_db_unique_name>';","what":"Intercambia de forma controlada los roles del primario y el standby: todo el redo del primario se envía y se aplica en el standby, el antiguo primario se reinicia como standby y la aplicación de redo se inicia automáticamente.","when":"Para mantenimiento planificado en el sitio primario (SO, hardware, rolling upgrade), simulacros de DR o traslados de centro de datos.","caution":"El primario sufre una caída breve; las conexiones de la aplicación deben enrutarse al nuevo primario mediante servicios definidos con -role PRIMARY y FAN/TAF/AC. Las transacciones largas y un apply lag alto alargan el switchover; acerque primero el lag a cero. En RAC el broker gestiona las instancias por sí mismo; no intervenga con srvctl.","since":"12c","note":"En 12.1+ el broker realiza todos los pasos con un solo comando; sin el broker, en 11g se usaba ALTER DATABASE COMMIT TO SWITCHOVER en SQL (12c+: ALTER DATABASE SWITCHOVER TO <db> VERIFY).","tags":["data guard","switchover","dgmgrl","role change","planned","standby","primary"],"untested":true},{"id":"dg-failover","task":"Realizar un failover tras perder el primario (DGMGRL)","category":"Data Guard","command":"# Connect to the standby (the primary is unreachable):\ndgmgrl sys@<standby_db_unique_name>\n# 1) Assess the data that will be lost (Transport Lag / Apply Lag):\nSHOW DATABASE '<standby_db_unique_name>';\n# 2) Complete failover: all received redo is applied, then the role changes (recommended)\nFAILOVER TO '<standby_db_unique_name>';\n# 3) Emergency: immediately, without applying redo (more data loss)\n# FAILOVER TO '<standby_db_unique_name>' IMMEDIATE;\nSHOW CONFIGURATION;","what":"Promueve el standby al rol primario cuando el primario no está disponible; un failover completo aplica primero todo el redo recibido, IMMEDIATE omite la aplicación.","when":"Cuando el sitio primario, el almacenamiento o la base de datos se han perdido sin posibilidad de recuperación y el RTO no permite esperar a un switchover.","caution":"Este cambio de rol es irreversible. Asegúrese primero de que el primario está REALMENTE caído; si el antiguo primario sigue abierto y la aplicación sigue escribiendo en él, se produce split-brain y datos divergentes (detenga/aísle el antiguo primario). En modo MaxPerformance se pierden tantos datos como el transport lag. Después revise todos los standbys y la estrategia de backup para el nuevo primario.","note":"Para recuperar el antiguo primario vea la receta dg-reinstate-database (requiere flashback).","tags":["data guard","failover","dgmgrl","disaster","disaster recovery","data loss","split-brain"],"untested":true},{"id":"dg-reinstate-database","task":"Recuperar el antiguo primario como standby tras un failover (REINSTATE)","category":"Data Guard","command":"# 1) Bring the old primary to MOUNT (sqlplus / as sysdba):\n#    SQL> STARTUP MOUNT;\n#    (RAC: srvctl start database -db <old_primary_db_unique_name> -startoption mount)\n# 2) From DGMGRL connected to the new primary:\ndgmgrl sys@<new_primary_db_unique_name>\nREINSTATE DATABASE '<old_primary_db_unique_name>';\n# 3) Verify:\nSHOW CONFIGURATION;\nSHOW DATABASE '<old_primary_db_unique_name>';","what":"Lleva el antiguo primario con flashback database a un punto anterior al SCN del failover, lo convierte en physical standby y empieza a recibir redo del nuevo primario; no hace falta reconstruir el standby desde cero.","when":"Tras un failover, una vez que el servidor/almacenamiento del antiguo primario vuelve a estar accesible; tras un failover automático de FSFO (el observer suele reinstaurarlo por sí mismo).","caution":"Flashback debe haberse habilitado en el antiguo primario ANTES del failover y los flashback logs deben cubrir el SCN del failover (db_flashback_retention_target, tamaño de la FRA); de lo contrario el reinstate falla y el standby debe reconstruirse con RMAN duplicate. Los datos escritos en el antiguo primario después del failover se pierden definitivamente.","note":"Comprobación previa: la columna flashback_on de la receta dg-readiness-sql.","tags":["data guard","reinstate","flashback","failover","old primary","standby","dgmgrl"],"untested":true},{"id":"dg-apply-transport-state","task":"Detener / iniciar la aplicación de redo o el transporte de redo con el broker; configurar la aplicación diferida","category":"Data Guard","command":"# Redo apply on the standby:\nEDIT DATABASE '<standby_db_unique_name>' SET STATE='APPLY-OFF';\nEDIT DATABASE '<standby_db_unique_name>' SET STATE='APPLY-ON';\n# Redo transport on the primary (to all standbys):\nEDIT DATABASE '<primary_db_unique_name>' SET STATE='TRANSPORT-OFF';\nEDIT DATABASE '<primary_db_unique_name>' SET STATE='TRANSPORT-ON';\n# Delayed apply (minutes; protection against logical errors):\nEDIT DATABASE '<standby_db_unique_name>' SET PROPERTY DelayMins=240;\nSHOW DATABASE '<standby_db_unique_name>' DelayMins;","what":"Detiene/inicia de forma persistente la aplicación de redo (MRP) en el standby o el envío de redo en el primario a través del broker; DelayMins retiene el redo el tiempo indicado antes de aplicarlo.","when":"Para mantenimiento o backups en el standby, cortes de red planificados, controlar el transporte durante grandes cargas masivas en el primario, o construir un standby diferido contra borrados accidentales.","caution":"Con el broker habilitado, no use ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL en SQL; el broker restablece el estado. Durante APPLY-OFF crecen el apply lag y el tiempo de failover (RTO); durante TRANSPORT-OFF los datos generados en el primario quedan sin protección (RPO). Si FSFO está habilitado, estas operaciones pueden impedir un failover.","note":"Con DelayMins > 0, el redo se aplica desde los logs archivados tras el retraso en lugar de mediante real-time apply; el standby sigue recibiendo redo durante el retraso.","tags":["data guard","apply","transport","mrp","delaymins","stop","dgmgrl"],"untested":true},{"id":"dg-fsfo-observer","task":"Habilitar Fast-Start Failover (FSFO) e iniciar el observer","category":"Data Guard","command":"# 1) Targets and threshold (seconds):\nEDIT DATABASE '<primary_db_unique_name>' SET PROPERTY FastStartFailoverTarget='<standby_db_unique_name>';\nEDIT DATABASE '<standby_db_unique_name>' SET PROPERTY FastStartFailoverTarget='<primary_db_unique_name>';\nEDIT CONFIGURATION SET PROPERTY FastStartFailoverThreshold=30;\n# 2) MaxAvailability for zero data loss (SYNC/FASTSYNC transport):\nEDIT DATABASE '<primary_db_unique_name>' SET PROPERTY LogXptMode='FASTSYNC';\nEDIT DATABASE '<standby_db_unique_name>' SET PROPERTY LogXptMode='FASTSYNC';\nEDIT CONFIGURATION SET PROTECTION MODE AS MAXAVAILABILITY;\n# Acceptable loss in MaxPerformance: EDIT CONFIGURATION SET PROPERTY FastStartFailoverLagLimit=30;\nENABLE FAST_START FAILOVER;\n# 3) Observer: on a third server, separate from primary and standby (passwordless connection via wallet)\ndgmgrl /@<primary_tns> \"START OBSERVER obs1 IN BACKGROUND FILE IS '/u01/app/oracle/dg/fsfo_obs1.dat' LOGFILE IS '/u01/app/oracle/dg/obs1.log' CONNECT IDENTIFIER IS <primary_tns>\"\n# 4) Verify:\nSHOW FAST_START FAILOVER;\nSHOW OBSERVER;","what":"Permite que el observer haga failover al standby automáticamente tras el umbral cuando el primario no es accesible, y que reinstaure automáticamente el antiguo primario cuando vuelve.","when":"En sistemas críticos cuyo objetivo de RTO es de minutos, sin esperar intervención humana.","caution":"El observer no debe ejecutarse en el mismo servidor/centro de datos que el primario o el standby; de lo contrario FSFO falla ante la pérdida de un sitio o puede tomar la decisión equivocada durante una partición de red. Flashback debe estar habilitado en ambas bases de datos. Un umbral demasiado bajo convierte breves fallos de red en failovers innecesarios. Para el mantenimiento planificado, ejecute DISABLE FAST_START FAILOVER o tenga en cuenta el observer antes de detener el primario.","since":"12c","note":"FASTSYNC es de 12.2+, IN BACKGROUND y los observers múltiples (SHOW OBSERVER) son de 12.2+. IN BACKGROUND requiere credenciales en un Oracle wallet (mkstore).","tags":["data guard","fsfo","fast-start failover","observer","automatic failover","maxavailability","dgmgrl"],"untested":true},{"id":"dg-snapshot-standby","task":"Convertir un physical standby en snapshot standby y viceversa (entorno de pruebas)","category":"Data Guard","command":"# Make the standby read-write (redo is still received, apply stops):\nCONVERT DATABASE '<standby_db_unique_name>' TO SNAPSHOT STANDBY;\nSHOW DATABASE '<standby_db_unique_name>';\n# ... testing / application validation ...\n# Convert back: all test changes are discarded, accumulated redo is applied\nCONVERT DATABASE '<standby_db_unique_name>' TO PHYSICAL STANDBY;\nSHOW CONFIGURATION;","what":"Crea un guaranteed restore point automático en el standby y lo abre en lectura-escritura; al revertir la conversión hace flashback a ese punto y aplica el redo acumulado del primario.","when":"Para ensayar un upgrade, probar una versión de la aplicación o ejecutar Real Application Testing (DB Replay) sobre datos de producción sin crear una copia aparte.","caution":"Mientras es snapshot standby no se aplica redo: el RPO se mantiene, pero el tiempo de failover (RTO) crece con el redo acumulado y no puede ser destino de FSFO. Los flashback logs pueden llenar la FRA; monitoree el espacio de la FRA. Los cambios hechos en el snapshot se descartan definitivamente al revertir la conversión.","note":"Equivalente en SQL: ALTER DATABASE CONVERT TO SNAPSHOT STANDBY / CONVERT TO PHYSICAL STANDBY (sin el broker).","tags":["data guard","snapshot standby","test","restore point","flashback","convert","dgmgrl"],"untested":true},{"id":"rman-catalog-create","task":"Crear un recovery catalog de RMAN y registrar la base de datos","category":"RMAN","command":"-- 1) In the catalog database (separate DB/PDB), with sqlplus:\nCREATE TABLESPACE rcat_ts DATAFILE '/u02/oradata/RCAT/rcat01.dbf' SIZE 1G AUTOEXTEND ON NEXT 256M;\nCREATE USER rcat IDENTIFIED BY \"<password>\"\n  DEFAULT TABLESPACE rcat_ts QUOTA UNLIMITED ON rcat_ts;\nGRANT RECOVERY_CATALOG_OWNER TO rcat;\n# 2) From the shell: create the catalog schema\nrman catalog rcat@<rcat_tns>\nCREATE CATALOG;\n# 3) Register the target database and verify:\nrman target / catalog rcat@<rcat_tns>\nREGISTER DATABASE;\nREPORT SCHEMA;\n# Resync controlfile records into the catalog (done automatically after backups):\nRESYNC CATALOG;","what":"Construye un recovery catalog que guarda los metadatos de backup en un esquema central, independiente de la retención del controlfile (control_file_record_keep_time), y registra la base de datos de destino.","when":"Para mantener en un solo lugar el historial de backups de muchas bases de datos, usar indistintamente los backups del primario/standby en Data Guard, y para backups a largo plazo (KEEP) y stored scripts.","caution":"La base de datos del catálogo debe estar en un servidor distinto al de las bases de datos que protege y también debe respaldarse. La versión del esquema del catálogo no puede ser inferior a la del cliente RMAN más reciente en uso; un cliente más nuevo requiere UPGRADE CATALOG. En Data Guard registre solo el primario; los standbys se definen por DB_UNIQUE_NAME con CONFIGURE DB_UNIQUE_NAME ... CONNECT IDENTIFIER.","note":"Mantenga el esquema del catálogo en una base de datos separada; si convive con el destino, un desastre se lleva también el catálogo.","tags":["rman","catalog","catalog","recovery catalog","register","backup","backup"],"untested":true},{"id":"rman-duplicate-standby","task":"Crear un physical standby desde la base de datos activa con RMAN DUPLICATE","category":"RMAN","command":"# Prerequisites: standby instance in NOMOUNT with a pfile containing only db_name,\n# primary password file copied, static listener entries and tnsnames on both sides.\nrman target sys@<primary_tns> auxiliary sys@<standby_tns>\nRUN {\n  ALLOCATE CHANNEL p1 DEVICE TYPE DISK;\n  ALLOCATE CHANNEL p2 DEVICE TYPE DISK;\n  ALLOCATE AUXILIARY CHANNEL s1 DEVICE TYPE DISK;\n  ALLOCATE AUXILIARY CHANNEL s2 DEVICE TYPE DISK;\n  DUPLICATE TARGET DATABASE FOR STANDBY FROM ACTIVE DATABASE\n    USING COMPRESSED BACKUPSET SECTION SIZE 32G\n    DORECOVER\n    SPFILE\n      SET db_unique_name='<standby_db_unique_name>'\n      SET control_files='+DATA','+FRA'\n      SET db_create_file_dest='+DATA'\n      SET db_recovery_file_dest='+FRA'\n      SET fal_server='<primary_tns>'\n      SET log_archive_dest_2=''\n      SET dg_broker_start='FALSE'\n    NOFILENAMECHECK;\n}","what":"Copia los datafiles del primario por la red al servidor standby (sin archivos de backup intermedios), crea el spfile adaptado al standby y aplica los archivelogs disponibles con DORECOVER.","when":"Al construir un nuevo standby de Data Guard o reconstruir un standby que no se puede reinstaurar.","caution":"Genera carga de red y de I/O en el primario; no lo ejecute en horas pico, o limite el número de canales. NOFILENAMECHECK solo es seguro cuando el standby está en un servidor DISTINTO; en el mismo servidor puede sobrescribir los archivos del primario. Añada standby redo logs (mismo tamaño que el online redo del primario, un grupo extra por thread) antes o después, y luego configure el broker.","since":"12c","note":"El active duplicate con USING [COMPRESSED] BACKUPSET y SECTION SIZE es de 12c+; 11g usa image copies. Si usa un sistema de archivos en lugar de ASM, defina db_file_name_convert/log_file_name_convert.","tags":["rman","duplicate","standby","active database","data guard","setup","clone"],"untested":true},{"id":"rman-crosscheck-cleanup","task":"Validar y limpiar los registros de backup de RMAN (CROSSCHECK, EXPIRED, OBSOLETE)","category":"RMAN","command":"SHOW RETENTION POLICY;\n# 1) Compare records with the physical files (missing ones become EXPIRED):\nCROSSCHECK BACKUP;\nCROSSCHECK COPY;\nCROSSCHECK ARCHIVELOG ALL;\n# 2) Review before deleting:\nLIST EXPIRED BACKUP;\nLIST EXPIRED ARCHIVELOG ALL;\nREPORT OBSOLETE;\n# 3) Clean up:\nDELETE NOPROMPT EXPIRED BACKUP;\nDELETE NOPROMPT EXPIRED COPY;\nDELETE NOPROMPT EXPIRED ARCHIVELOG ALL;\nDELETE NOPROMPT OBSOLETE;","what":"CROSSCHECK comprueba si los backups de los registros de RMAN siguen existiendo en disco/cinta; DELETE EXPIRED elimina los registros de los archivos que faltan, y DELETE OBSOLETE borra tanto de los registros como del disco los backups que la política de retención ya no necesita.","when":"Cuando se han borrado/movido archivos de backup a nivel del SO, cuando se llena la FRA y como parte del job habitual de mantenimiento de backups.","caution":"DELETE OBSOLETE borra archivos físicamente; verifique antes la lista con REPORT OBSOLETE y asegúrese de que la política de retención (RECOVERY WINDOW / REDUNDANCY) es la esperada. Si ejecuta CROSSCHECK mientras la cinta/media manager está temporalmente inaccesible, los backups válidos aparecen como EXPIRED; no ejecute DELETE EXPIRED en ese momento.","note":"En Data Guard, use CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON ALL STANDBY para el borrado de archivelogs.","tags":["rman","crosscheck","expired","obsolete","cleanup","retention","backup"],"untested":true},{"id":"rman-validate","task":"Validar la base de datos y los backups frente a corrupción (VALIDATE)","category":"RMAN","command":"# Physical + logical block check (without producing a backup):\nVALIDATE CHECK LOGICAL DATABASE;\n# Scan large datafiles in parallel (11g+, needs multiple channels):\nVALIDATE CHECK LOGICAL DATABASE SECTION SIZE 16G;\n# A single datafile / tablespace:\nVALIDATE DATAFILE 7;\nVALIDATE TABLESPACE users;\n# Read as if taking a backup and check (including archivelogs):\nBACKUP VALIDATE CHECK LOGICAL DATABASE ARCHIVELOG ALL;\n# Readability of a specific backup set:\nLIST BACKUP SUMMARY;\nVALIDATE BACKUPSET <bs_key>;\n-- Corrupt blocks found and the affected segment (SQL):\nSELECT file#, block#, blocks, corruption_change#, corruption_type\nFROM   v$database_block_corruption ORDER BY file#, block#;\nSELECT DISTINCT e.owner, e.segment_name, e.segment_type\nFROM   dba_extents e\nJOIN   v$database_block_corruption c\n  ON   e.file_id = c.file#\n AND   c.block# BETWEEN e.block_id AND e.block_id + e.blocks - 1;","what":"Lee los datafiles y los backup sets para detectar corrupción física y (con CHECK LOGICAL) lógica de bloques, y registra los hallazgos en v$database_block_corruption. La consulta SQL muestra a qué tabla/índice pertenece el bloque corrupto.","when":"Tras errores de corrupción como ORA-01578/ORA-08103, tras problemas de almacenamiento y como comprobación de integridad periódica (semanal); para demostrar que los backups son realmente restaurables.","caution":"Realiza una lectura completa; en bases de datos grandes genera mucha I/O, así que ejecútelo fuera de las horas pico. La consulta a dba_extents puede ser lenta en bases de datos grandes. Si el bloque corrupto está en espacio temporal (TEMP) o libre, no se devuelve ningún objeto.","since":"11g","note":"Las consultas no devuelven filas cuando no hay corrupción. VALIDATE con SECTION SIZE es de 11g+.","tags":["rman","validate","corruption","corruption","check logical","block","ora-01578"],"untested":true},{"id":"rman-restore-preview","task":"Ver qué backups usaría una restauración y validarlos (PREVIEW / VALIDATE)","category":"RMAN","command":"# Backups and archivelogs a restore needs (without reading the files):\nRESTORE DATABASE PREVIEW SUMMARY;\n# For a return to a point in time:\nRESTORE DATABASE UNTIL TIME \"TO_DATE('2026-01-15 03:00:00','YYYY-MM-DD HH24:MI:SS')\" PREVIEW;\n# Validate the backups by actually reading them (without restoring):\nRESTORE DATABASE VALIDATE;\nRESTORE ARCHIVELOG FROM TIME 'SYSDATE-1' VALIDATE;\nRESTORE CONTROLFILE VALIDATE;\nRESTORE SPFILE VALIDATE;","what":"PREVIEW informa, a partir del catálogo/controlfile, de qué backup sets/copias y qué rango de archivelogs usaría una restauración; VALIDATE lee realmente esos backups y verifica que no haya corrupción ni archivos faltantes.","when":"Para planificar antes de una restauración real, demostrar que la estrategia de backup cumple el objetivo de RPO y como prueba periódica de restauración.","caution":"PREVIEW solo mira los registros; es engañoso si se borraron archivos, así que ejecute CROSSCHECK primero o use RESTORE ... VALIDATE. VALIDATE lee todo el backup; puede tardar mucho por cinta/red. Haga que la fecha dependa de la máscara de TO_DATE, no de la configuración NLS de la sesión.","note":"PREVIEW RECALL también puede solicitar los backups que deben recuperarse de cinta (requiere soporte del media manager).","tags":["rman","restore","preview","validate","restore test","backup","rpo"],"untested":true},{"id":"rman-block-media-recovery","task":"Reparar bloques corruptos en línea (block media recovery, RECOVER CORRUPTION LIST)","category":"RMAN","command":"# 1) Detect corrupt blocks (populates v$database_block_corruption):\nVALIDATE CHECK LOGICAL DATABASE;\n# 2) Repair all blocks in the list (the datafile stays online):\nRECOVER CORRUPTION LIST;\n# or the file/block from the error message (ORA-01578: file# 7, block# 233):\nRECOVER DATAFILE 7 BLOCK 233;\nRECOVER DATAFILE 7 BLOCK 233 DATAFILE 9 BLOCK 1001;\n# 3) Verify:\nVALIDATE DATAFILE 7;\nSELECT * FROM v$database_block_corruption;","what":"Restaura solo los bloques corruptos desde un backup (o desde los flashback logs / un standby de Active Data Guard) y los pone al día con redo; el datafile y el tablespace permanecen online, y la aplicación solo recibe errores al tocar esos bloques.","when":"Cuando aparece corrupción de bloques aislada con ORA-01578 / ORA-01110; cuando restaurar todo el datafile causaría un tiempo de inactividad innecesario.","caution":"Requiere un backup limpio que contenga el bloque y todos los archivelogs desde entonces hasta ahora; la corrupción causada por una operación NOLOGGING (ORA-26040) no se puede reparar así, el objeto debe reconstruirse. Es una función de Enterprise Edition. Si no encuentra el origen de la corrupción (almacenamiento, HBA, firmware), volverá a ocurrir.","note":"Con Active Data Guard, el automatic block media recovery de 11.2+ repara automáticamente los bloques corruptos desde el standby.","tags":["rman","block recovery","corrupt block","corruption","ora-01578","recover","repair"],"untested":true},{"id":"rman-tspitr","task":"Devolver un tablespace a un punto del pasado (TSPITR)","category":"RMAN","command":"-- 1) Is the tablespace self-contained (SQL, on the target DB):\nEXEC DBMS_TTS.TRANSPORT_SET_CHECK(ts_list => 'USERS', incl_constraints => TRUE, full_check => TRUE);\nSELECT * FROM transport_set_violations;\n-- 2) Objects created AFTER the target time that TSPITR will lose:\nSELECT owner, name, tablespace_name,\n       TO_CHAR(creation_time,'YYYY-MM-DD HH24:MI:SS') AS created\nFROM   ts_pitr_objects_to_be_dropped\nWHERE  tablespace_name = 'USERS'\n  AND  creation_time > TO_DATE('2026-01-15 03:00:00','YYYY-MM-DD HH24:MI:SS');\n# 3) RMAN: with an automatic auxiliary instance\nRECOVER TABLESPACE users\n  UNTIL TIME \"TO_DATE('2026-01-15 03:00:00','YYYY-MM-DD HH24:MI:SS')\"\n  AUXILIARY DESTINATION '/u03/aux';\n# 4) The tablespace is left OFFLINE: take a backup first, then bring it online\nBACKUP TABLESPACE users;\nALTER TABLESPACE users ONLINE;","what":"RMAN recupera SYSTEM/UNDO/SYSAUX y el tablespace de destino hasta la hora solicitada en una instancia auxiliar temporal y luego lo vuelve a conectar a la base de datos de destino con transportable tablespace; el resto de la base de datos sigue actualizado.","when":"Cuando un job batch defectuoso o un DROP/TRUNCATE erróneo dañó datos en un solo tablespace (o en un conjunto de tablespaces autoconsistente) y las opciones de flashback no bastan.","caution":"Se pierden TODOS los cambios hechos en ese tablespace y todos los objetos creados después de la hora de destino. El conjunto de tablespaces debe ser autocontenido (ningún índice/tabla, partición, LOB o FK en otro tablespace). No se puede usar con tablespaces que contengan SYSTEM, UNDO u objetos de SYS. Tras el TSPITR, los backups anteriores de ese tablespace no sirven; tome un backup de inmediato. AUXILIARY DESTINATION necesita espacio libre para SYSTEM+SYSAUX+UNDO+el tablespace de destino.","note":"Para una sola tabla, rman-recover-table es menos destructivo.","tags":["rman","tspitr","tablespace","pitr","point-in-time","auxiliary","rollback"],"untested":true},{"id":"rman-recover-table","task":"Recuperar una sola tabla a un punto del pasado desde un backup de RMAN (RECOVER TABLE)","category":"RMAN","command":"# Restore under a new name (the original table is not touched):\nRECOVER TABLE hr.employees\n  UNTIL TIME \"TO_DATE('2026-01-15 03:00:00','YYYY-MM-DD HH24:MI:SS')\"\n  AUXILIARY DESTINATION '/u03/aux'\n  REMAP TABLE hr.employees:employees_rec;\n# Multitenant: a table inside a PDB\nRECOVER TABLE hr.employees OF PLUGGABLE DATABASE <pdb>\n  UNTIL SCN 12345678\n  AUXILIARY DESTINATION '/u03/aux'\n  REMAP TABLE hr.employees:employees_rec;\n# Only produce a Data Pump dump, do not import:\nRECOVER TABLE hr.employees\n  UNTIL SCN 12345678\n  AUXILIARY DESTINATION '/u03/aux'\n  DATAPUMP DESTINATION '/u03/dump'\n  DUMP FILE 'employees_rec.dmp'\n  NOTABLEIMPORT;","what":"RMAN recupera una instancia auxiliar automática hasta la hora solicitada, exporta la tabla (o partición) con Data Pump y la importa en la base de datos de destino con otro nombre mediante REMAP; después la instancia auxiliar se elimina.","when":"Para recuperar los datos de una sola tabla borrados/actualizados por error o eliminada con PURGE, más allá de lo que permite la undo retention de Flashback Table/Query.","caution":"AUXILIARY DESTINATION necesita espacio libre para SYSTEM, SYSAUX, UNDO y el tablespace de la tabla; la operación puede tardar horas. Sin REMAP TABLE la operación falla si existe una tabla con el mismo nombre; use siempre REMAP o NOTABLEIMPORT para no sobrescribir la original. Las tablas de los esquemas SYS/SYSTEM y de los tablespaces SYSTEM/SYSAUX no se pueden recuperar así. Requiere Enterprise Edition.","since":"12c","note":"Requisito: un backup completo más los archivelogs que cubran la hora de destino. Para una partición: RECOVER TABLE hr.sales:p2025 ... REMAP TABLE hr.sales:p2025:sales_p2025_rec.","tags":["rman","recover table","table recovery","pitr","auxiliary","remap","accidental delete"],"untested":true},{"id":"pdb-create-from-seed","task":"Crear una PDB nueva desde PDB$SEED","category":"Multitenant","command":"-- In CDB$ROOT, as SYSDBA:\nCREATE PLUGGABLE DATABASE <pdb>\n  ADMIN USER pdbadmin IDENTIFIED BY \"<password>\" ROLES = (DBA)\n  DEFAULT TABLESPACE users\n    DATAFILE '/u02/oradata/<cdb>/<pdb>/users01.dbf' SIZE 512M AUTOEXTEND ON NEXT 64M\n  FILE_NAME_CONVERT = ('/u02/oradata/<cdb>/pdbseed/', '/u02/oradata/<cdb>/<pdb>/')\n  STORAGE (MAXSIZE 50G);\n-- With OMF/ASM, instead of FILE_NAME_CONVERT: CREATE_FILE_DEST = '+DATA'\nALTER PLUGGABLE DATABASE <pdb> OPEN;\nALTER PLUGGABLE DATABASE <pdb> SAVE STATE;\nSELECT con_id, name, open_mode, restricted FROM v$pdbs ORDER BY con_id;","what":"Crea una pluggable database nueva y vacía a partir de la plantilla seed; define en el mismo comando el usuario administrador local, el tablespace por defecto y el límite de tamaño total.","when":"Al abrir una base de datos aislada para una aplicación/esquema nuevo; al preparar entornos de prueba y desarrollo.","caution":"La nueva PDB queda en MOUNTED; necesita OPEN y SAVE STATE (para seguir abierta tras reiniciar la CDB). Rutas FILE_NAME_CONVERT erróneas escriben los archivos en el directorio equivocado; sin OMF es obligatoria una ruta de archivo completa para DATAFILE (ORA-02236). Licencias: en 19c EE se pueden usar 3 PDB de usuario por CDB sin la opción Multitenant.","since":"12c","note":"En RAC, añada un servicio de aplicación a la PDB con srvctl add service -pdb.","tags":["multitenant","pdb","create","create pluggable database","seed","cdb","pdb$seed"]},{"id":"pdb-clone-local","task":"Clonar una PDB dentro de la misma CDB (clon local / hot clone)","category":"Multitenant","command":"-- Hot clone prerequisites: local undo + ARCHIVELOG\nSELECT property_value AS local_undo\nFROM   database_properties WHERE property_name = 'LOCAL_UNDO_ENABLED';\nSELECT log_mode FROM v$database;\n\nCREATE PLUGGABLE DATABASE <new_pdb> FROM <pdb>\n  FILE_NAME_CONVERT = ('/<pdb>/', '/<new_pdb>/');\n-- OMF/ASM: CREATE PLUGGABLE DATABASE <new_pdb> FROM <pdb> CREATE_FILE_DEST = '+DATA';\nALTER PLUGGABLE DATABASE <new_pdb> OPEN;\nSELECT name, open_mode FROM v$pdbs WHERE name = UPPER('<new_pdb>');","what":"Crea una PDB nueva en la misma CDB copiando los datafiles de la PDB de origen; con local undo y ARCHIVELOG puede ejecutarse mientras el origen está abierto en lectura-escritura (hot clone).","when":"Para crear una copia de prueba/desarrollo de una PDB de producción, ensayar un upgrade o parche, o tomar una copia rápida antes de un cambio arriesgado.","caution":"La copia genera I/O adicional en el origen y necesita en el destino tanto espacio como el origen. En modo shared undo o en NOARCHIVELOG el origen debe estar READ ONLY (documentación de 19c). El clon contiene datos de producción: revise el enmascaramiento, los DB links, los jobs (DBMS_SCHEDULER) y las conexiones a sistemas externos antes de abrirlo.","since":"12c","note":"Un hot clone con el origen en lectura-escritura requiere local undo + ARCHIVELOG en 19c. Para una copia ligera rápida use SNAPSHOT COPY (requiere almacenamiento adecuado).","tags":["multitenant","pdb","clone","clone","hot clone","copy","local undo"]},{"id":"pdb-clone-remote","task":"Clonar una PDB desde otra CDB por un DB link (clon remoto)","category":"Multitenant","command":"-- 1) In the SOURCE CDB, a common user:\nCREATE USER c##clone_user IDENTIFIED BY \"<password>\" CONTAINER = ALL;\nGRANT CREATE SESSION, CREATE PLUGGABLE DATABASE TO c##clone_user CONTAINER = ALL;\n\n-- 2) In the TARGET CDB root, DB link and test:\nCREATE DATABASE LINK clone_link\n  CONNECT TO c##clone_user IDENTIFIED BY \"<password>\"\n  USING '//<source_host>:1521/<source_cdb_service>';\nSELECT * FROM dual@clone_link;\n\n-- 3) Clone and open:\nCREATE PLUGGABLE DATABASE <new_pdb> FROM <pdb>@clone_link\n  FILE_NAME_CONVERT = ('/<pdb>/', '/<new_pdb>/');\nALTER PLUGGABLE DATABASE <new_pdb> OPEN;\n\n-- 4) Violations (version, options, parameters, patches):\nSELECT name, cause, type, message\nFROM   pdb_plug_in_violations\nWHERE  name = UPPER('<new_pdb>') AND status <> 'RESOLVED';","what":"Copia una PDB de la CDB de origen a la CDB de destino por la red a través de un DB link; no necesita archivos intermedios ni unplug.","when":"Para mover una PDB a otro servidor/CDB, crear una copia de prueba de producción en un servidor aparte o pasar a una CDB de una versión más nueva.","caution":"Las CDB de origen y destino deben ser compatibles en endianness, juego de caracteres (o destino AL32UTF8) y opciones; una PDB clonada a una versión más nueva se abre primero en RESTRICTED y necesita datapatch/upgrade. Elimine el DB link y el usuario común al terminar el clon. El hot clone requiere local undo + ARCHIVELOG en el origen.","since":"12c","note":"Para moverla (el origen se elimina) use la opción RELOCATE de 12.2+: CREATE PLUGGABLE DATABASE <pdb> FROM <pdb>@clone_link RELOCATE.","tags":["multitenant","pdb","remote clone","db link","clone","migration","relocate"]},{"id":"pdb-unplug-plug","task":"Desconectar (unplug) una PDB y conectarla (plug) en otra CDB (o en la misma)","category":"Multitenant","command":"-- SOURCE CDB: close and unplug to an XML manifest\nALTER PLUGGABLE DATABASE <pdb> CLOSE IMMEDIATE;\nALTER PLUGGABLE DATABASE <pdb> UNPLUG INTO '/u01/stage/<pdb>.xml';\n-- (Single-file archive: UNPLUG INTO '/u01/stage/<pdb>.pdb' - includes datafiles, 12.2+)\nDROP PLUGGABLE DATABASE <pdb> KEEP DATAFILES;\n\n-- TARGET CDB: check compatibility first\nSET SERVEROUTPUT ON\nDECLARE\n  l_ok BOOLEAN;\nBEGIN\n  l_ok := DBMS_PDB.CHECK_PLUG_COMPATIBILITY(\n            pdb_descr_file => '/u01/stage/<pdb>.xml',\n            pdb_name       => '<pdb>');\n  DBMS_OUTPUT.PUT_LINE(CASE WHEN l_ok THEN 'COMPATIBLE' ELSE 'NOT COMPATIBLE - see PDB_PLUG_IN_VIOLATIONS' END);\nEND;\n/\nSELECT name, cause, type, status, message\nFROM   pdb_plug_in_violations\nWHERE  name = UPPER('<pdb>') AND status <> 'RESOLVED';\n\n-- NOCOPY if the files stay in place, COPY FILE_NAME_CONVERT if they move:\nCREATE PLUGGABLE DATABASE <pdb> USING '/u01/stage/<pdb>.xml' NOCOPY TEMPFILE REUSE;\n-- From a .pdb archive: CREATE PLUGGABLE DATABASE <pdb> USING '/u01/stage/<pdb>.pdb'\n--                    FILE_NAME_CONVERT = ('/u01/stage/', '/u02/oradata/<cdb>/<pdb>/');\nALTER PLUGGABLE DATABASE <pdb> OPEN;","what":"Separa la PDB de la CDB y escribe sus metadatos en un manifiesto XML (o en un único archivo .pdb junto con los datafiles); tras una comprobación de compatibilidad la vuelve a conectar en la CDB de destino con los mismos archivos.","when":"Para mover una PDB a una CDB de versión más nueva en el mismo servidor (upgrade por unplug/plug), para consolidar CDB o para traslados donde no es posible clonar por la red.","caution":"Tras el unplug, la PDB no está disponible en la CDB de origen (tiempo de inactividad). Los archivos conectados con NOCOPY pasan a pertenecer a la CDB de destino; tome antes una copia a nivel de archivo o un backup de RMAN para poder volver atrás si falla. Hasta resolver las violaciones de tipo ERROR, la PDB se abre en RESTRICTED. Al volver a conectarla en la misma CDB, omitir AS CLONE puede causar un conflicto de GUID.","since":"12c","note":"Un archivo .pdb (12.2+) incluye los datafiles, por lo que se mueve como un solo archivo; con el método XML los datafiles se copian por separado.","tags":["multitenant","pdb","unplug","plug","migration","xml manifest","check_plug_compatibility"]},{"id":"pdb-open-save-state","task":"Abrir PDB y mantenerlas abiertas tras reinicios de la CDB (SAVE STATE)","category":"Multitenant","command":"-- Open all PDBs (RAC: INSTANCES = ALL):\nALTER PLUGGABLE DATABASE ALL OPEN;\n-- ALTER PLUGGABLE DATABASE <pdb> OPEN INSTANCES = ALL;\n-- Save the current open state (opens automatically after a CDB restart):\nALTER PLUGGABLE DATABASE <pdb> SAVE STATE;\n-- Saved states:\nSELECT con_name, instance_name, state, restricted FROM dba_pdb_saved_states;\n-- Remove the saved state:\nALTER PLUGGABLE DATABASE <pdb> DISCARD STATE;\n-- If it opens RESTRICTED, find the reason:\nSELECT name, cause, type, message\nFROM   pdb_plug_in_violations\nWHERE  status <> 'RESOLVED';","what":"Abre las PDB en READ WRITE y guarda su estado de apertura actual para que la PDB se abra automáticamente en el mismo modo cuando se reinicie la CDB.","when":"Tras crear, conectar o clonar una PDB; cuando nota que las PDB quedan en MOUNTED tras reiniciar la CDB.","caution":"SAVE STATE registra el estado en el momento del comando: si lo ejecuta con la PDB cerrada, se registra el estado \"cerrado\" (o se elimina el registro). En RAC el estado es por instancia; con gestión basada en servicios (servicios de PDB mediante srvctl), se prefiere el arranque por Clusterware frente a SAVE STATE. En un standby de Data Guard el modo de apertura depende del rol.","since":"12c","note":"12.1.0.2+.","tags":["multitenant","pdb","open","save state","auto open","restricted","dba_pdb_saved_states"]},{"id":"pdb-resource-limits","task":"Definir límites de CPU, memoria, I/O y almacenamiento por PDB","category":"Multitenant","command":"ALTER SESSION SET CONTAINER = <pdb>;\n-- CPU (instance caging with Resource Manager; at PDB level in 19c):\nALTER SYSTEM SET cpu_count = 2 SCOPE = BOTH;\n-- Memory (sga_min_size guarantee, sga_target upper limit):\nALTER SYSTEM SET sga_target = 512M SCOPE = BOTH;\nALTER SYSTEM SET sga_min_size = 96M SCOPE = BOTH;\nALTER SYSTEM SET pga_aggregate_limit = 2G SCOPE = BOTH;\n-- I/O (non-Exadata):\nALTER SYSTEM SET max_iops = 5000 SCOPE = BOTH;\nALTER SYSTEM SET max_mbps = 200 SCOPE = BOTH;\n-- Number of sessions (after the PDB is reopened):\nALTER SYSTEM SET sessions = 200 SCOPE = SPFILE;\n-- Storage:\nALTER PLUGGABLE DATABASE STORAGE (MAXSIZE 10G MAX_SHARED_TEMP_SIZE 2G);\n\n-- Settings across all PDBs, from the root:\nALTER SESSION SET CONTAINER = CDB$ROOT;\nSELECT p.name AS pdb, sp.name, sp.value\nFROM   v$system_parameter sp JOIN v$pdbs p ON p.con_id = sp.con_id\nWHERE  sp.name IN ('cpu_count','sga_target','sga_min_size','max_iops','max_mbps','pga_aggregate_limit')\nORDER  BY p.name, sp.name;","what":"Limita mediante parámetros a nivel de PDB cuánta CPU, SGA/PGA, IOPS/MBPS y almacenamiento total puede usar una PDB de la CDB; evita que PDB \"vecinas ruidosas\" afecten a las demás.","when":"En CDB consolidadas, al ejecutar aplicaciones con distintos SLA en la misma CDB, o cuando se ve que una PDB agota todo el servidor.","caution":"El sga_target de la PDB debe ser menor que el sga_target de la CDB; valores demasiado bajos causan ORA-04031 y problemas de rendimiento. La suma de sga_min_size no debe reservar toda la SGA de la CDB. Para que cpu_count en una PDB surta efecto, debe haber un plan de resource manager activo en la CDB. Al alcanzar MAXSIZE la PDB recibe ORA-65114.","since":"12c","note":"23ai exige que sga_min_size sea inferior al 20% de sga_target (ORA-56746); el límite en 19c es el 50%. MAX_IOPS/MAX_MBPS son de 12.2+, los parámetros de memoria de PDB son de 12.2+.","tags":["multitenant","pdb","resource","resource","cpu_count","sga_target","maxsize"]},{"id":"pdb-container-context","task":"Confirmar en qué contenedor está y consultar todas las PDB desde el root (CDB_ / CONTAINERS)","category":"Multitenant","command":"-- Current container:\nSHOW CON_NAME\nSHOW CON_ID\nSHOW PDBS\nSELECT SYS_CONTEXT('USERENV','CON_NAME') AS con_name,\n       SYS_CONTEXT('USERENV','CON_ID')   AS con_id\nFROM   dual;\n\n-- All containers, from the root:\nSELECT con_id, name, open_mode FROM v$containers ORDER BY con_id;\n\n-- CDB_ views combine all open PDBs with CON_ID:\nSELECT c.name AS container, t.tablespace_name, t.status\nFROM   cdb_tablespaces t JOIN v$containers c ON c.con_id = t.con_id\nORDER  BY c.name, t.tablespace_name;\n\n-- Query any table/view across all PDBs with CONTAINERS():\nSELECT con_id, COUNT(*) AS users\nFROM   CONTAINERS(dba_users)\nGROUP  BY con_id ORDER BY con_id;","what":"Muestra el contenedor al que está conectada la sesión; desde el root, las vistas CDB_ y la cláusula CONTAINERS() recopilan en una sola consulta la información del diccionario de todas las PDB abiertas.","when":"Para asegurarse de estar en el contenedor correcto antes de un DDL/cambio de parámetro, y para elaborar un inventario de espacio, usuarios y objetos de todas las PDB.","caution":"Solo lectura. No cree por accidente objetos de aplicación ni usuarios locales en el root (CDB$ROOT); ALTER SYSTEM en el root afecta a toda la CDB. Las vistas CDB_ y CONTAINERS() solo devuelven las PDB abiertas; las PDB en MOUNTED no aparecen en el resultado.","since":"12c","note":"Para cambiar de contenedor: ALTER SESSION SET CONTAINER = <pdb>; (vea en rosetta \"Cambiar la base de datos activa\").","tags":["multitenant","container","con_name","cdb_","containers","pdb","root"]},{"id":"pdb-refreshable-clone","task":"Crear una PDB refreshable, refrescarla y convertirla en una PDB independiente","category":"Multitenant","command":"-- In the target CDB root (DB link to the source ready: see pdb-clone-remote):\nCREATE PLUGGABLE DATABASE <new_pdb> FROM <pdb>@clone_link\n  FILE_NAME_CONVERT = ('/<pdb>/', '/<new_pdb>/')\n  REFRESH MODE EVERY 60 MINUTES;\nSELECT pdb_name, status, refresh_mode, refresh_interval, last_refresh_scn\nFROM   dba_pdbs WHERE pdb_name = UPPER('<new_pdb>');\n\n-- Can only be opened READ ONLY; close it first to refresh manually:\nALTER PLUGGABLE DATABASE <new_pdb> OPEN READ ONLY;\nALTER PLUGGABLE DATABASE <new_pdb> CLOSE IMMEDIATE;\nALTER PLUGGABLE DATABASE <new_pdb> REFRESH;\n\n-- Change the mode (MANUAL) or convert to an independent, writable PDB (irreversible):\nALTER PLUGGABLE DATABASE <new_pdb> REFRESH MODE MANUAL;\nALTER PLUGGABLE DATABASE <new_pdb> REFRESH MODE NONE;\nALTER PLUGGABLE DATABASE <new_pdb> OPEN;","what":"Crea una copia que se mantiene actualizada aplicando el redo de la PDB de origen periódicamente o bajo demanda; se abre en READ ONLY para informes y se convierte en una PDB independiente de lectura-escritura con REFRESH MODE NONE.","when":"Para una copia de informes/pruebas de producción refrescada con regularidad, una migración de PDB con poco tiempo de inactividad (refresh final + REFRESH MODE NONE) o un \"standby barato a nivel de PDB\".","caution":"La PDB debe estar cerrada durante un refresh; mientras está abierta en READ ONLY el refresh automático espera. Los archivelogs necesarios para el refresh deben ser accesibles en el origen. REFRESH MODE NONE es irreversible; la copia ya no se puede refrescar. Requiere local undo + ARCHIVELOG en el origen. No sustituye a Data Guard para un DR real.","since":"12c","note":"12.2+. Tras REFRESH MODE NONE el clon se convierte en una PDB normal y se puede abrir en READ WRITE; esto no se puede deshacer.","tags":["multitenant","pdb","refreshable","refresh","refresh","reporting","clone"]}]},"recipes/oracle-ops.json":{"engineId":"oracle","engine":"Oracle","order":2,"recipes":[{"id":"awr-snapshot-report","task":"Tomar un snapshot de AWR y generar un informe AWR entre dos snapshots","category":"Rendimiento","command":"-- 1) Manual snapshot before and after the event (run in CDB root):\nEXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT;\n\n-- 2) List snapshots:\nSELECT snap_id,\n       TO_CHAR(begin_interval_time,'YYYY-MM-DD HH24:MI') AS begin_time,\n       TO_CHAR(end_interval_time,'YYYY-MM-DD HH24:MI')   AS end_time\nFROM   dba_hist_snapshot\nWHERE  dbid = (SELECT dbid FROM v$database)\nORDER  BY snap_id DESC\nFETCH FIRST 24 ROWS ONLY;\n\n-- 3a) Interactive report (prompts for HTML/TEXT and snapshot range):\n-- @?/rdbms/admin/awrrpt.sql      (RAC: awrgrpt.sql, specific instance: awrrpti.sql)\n\n-- 3b) TEXT report from a script:\nSET LINESIZE 8000 PAGESIZE 0 TRIMSPOOL ON HEADING OFF FEEDBACK OFF\nSPOOL /tmp/awr_<begin_snap>_<end_snap>.txt\nSELECT output\nFROM   TABLE(DBMS_WORKLOAD_REPOSITORY.AWR_REPORT_TEXT(\n         l_dbid     => (SELECT dbid FROM v$database),\n         l_inst_num => (SELECT instance_number FROM v$instance),\n         l_bid      => <begin_snap>,\n         l_eid      => <end_snap>));\nSPOOL OFF\n-- For HTML: DBMS_WORKLOAD_REPOSITORY.AWR_REPORT_HTML (same parameters)","what":"Genera un informe entre dos snapshots de AWR que cubren la ventana del problema: load profile, top wait events, top SQL y estadísticas de la instancia. La función de tabla permite obtener el informe desde un script o un job del scheduler.","when":"Al analizar una ralentización en una ventana de tiempo concreta, para comparar antes/después de una prueba de carga o un batch, y para informes de capacidad.","caution":"Requiere la licencia de Diagnostics Pack (CONTROL_MANAGEMENT_PACK_ACCESS='DIAGNOSTIC' o 'DIAGNOSTIC+TUNING'); sin ella, ponga el parámetro en 'NONE' y use Statspack. Un informe no puede abarcar un reinicio de la instancia. Los rangos de más de una hora promedian y ocultan picos cortos; mantenga la ventana ajustada.","note":"Para AWR a nivel de PDB (12.2+) compruebe AWR_PDB_AUTOFLUSH_ENABLED=TRUE y AWR_SNAPSHOT_TIME_OFFSET/AWR_PDB_MAX_PARALLEL_SLAVES; un informe tomado dentro de una PDB solo muestra los datos de esa PDB. Intervalo/retención: DBMS_WORKLOAD_REPOSITORY.MODIFY_SNAPSHOT_SETTINGS(interval => 30, retention => 43200).","tags":["awr","awr report","snapshot","awrrpt","dbms_workload_repository","performance report","diagnostics pack"]},{"id":"awr-diff-report","task":"Informe AWR diff (compare period) entre dos periodos","category":"Rendimiento","command":"-- Interactive: @?/rdbms/admin/awrddrpt.sql   (RAC: awrgdrpt.sql)\n\n-- From a script: period 1 (good) = <bid1>-<eid1>, period 2 (bad) = <bid2>-<eid2>\nSET LINESIZE 8000 PAGESIZE 0 TRIMSPOOL ON HEADING OFF FEEDBACK OFF\nSPOOL /tmp/awr_diff.txt\nSELECT output\nFROM   TABLE(DBMS_WORKLOAD_REPOSITORY.AWR_DIFF_REPORT_TEXT(\n         dbid1 => (SELECT dbid FROM v$database), inst_num1 => 1,\n         bid1  => <bid1>, eid1 => <eid1>,\n         dbid2 => (SELECT dbid FROM v$database), inst_num2 => 1,\n         bid2  => <bid2>, eid2 => <eid2>));\nSPOOL OFF\n-- HTML: DBMS_WORKLOAD_REPOSITORY.AWR_DIFF_REPORT_HTML","what":"Pone dos rangos de snapshots lado a lado y muestra las diferencias porcentuales en DB time, wait events, load profile y estadísticas por SQL. Responde directamente a \"ayer iba bien, hoy va lento\".","when":"Análisis de regresiones tras un upgrade/parche, un cambio de parámetros, un despliegue o una recopilación de estadísticas; comparar la misma franja horaria en dos días.","caution":"Requiere la licencia de Diagnostics Pack. Los periodos comparados deben tener la misma duración y una carga similar; de lo contrario los deltas son engañosos. Para comparar bases de datos distintas (p. ej., prod vs. test), traslade los datos de AWR con awrextr.sql/awrload.sql y pase el dbid2 correspondiente.","tags":["awr diff","comparison","compare period","awrddrpt","regression","performance","diagnostics pack"]},{"id":"ash-top-activity","task":"Top wait events y top SQL desde ASH para los últimos minutos","category":"Rendimiento","command":"-- Last 30 min: wait event breakdown (each sample ~1 s of active session time)\nSELECT NVL(event, 'ON CPU') AS event, wait_class,\n       COUNT(*) AS samples,\n       ROUND(RATIO_TO_REPORT(COUNT(*)) OVER () * 100, 1) AS pct\nFROM   v$active_session_history\nWHERE  sample_time > SYSTIMESTAMP - INTERVAL '30' MINUTE\nGROUP  BY NVL(event, 'ON CPU'), wait_class\nORDER  BY samples DESC\nFETCH FIRST 10 ROWS ONLY;\n\n-- Last 30 min: top SQL (with CPU / User I/O breakdown)\nSELECT sql_id, sql_opname,\n       COUNT(*) AS samples,\n       SUM(CASE WHEN session_state = 'ON CPU' THEN 1 ELSE 0 END) AS cpu,\n       SUM(CASE WHEN wait_class = 'User I/O' THEN 1 ELSE 0 END)  AS user_io,\n       ROUND(RATIO_TO_REPORT(COUNT(*)) OVER () * 100, 1) AS pct\nFROM   v$active_session_history\nWHERE  sample_time > SYSTIMESTAMP - INTERVAL '30' MINUTE\n  AND  sql_id IS NOT NULL\nGROUP  BY sql_id, sql_opname\nORDER  BY samples DESC\nFETCH FIRST 10 ROWS ONLY;\n\n-- Older periods: DBA_HIST_ACTIVE_SESS_HISTORY (one sample every 10 s; COUNT(*)*10 = seconds)\n\n-- Ready-made ASH report (last 30 min):\nSET LINESIZE 8000 PAGESIZE 0 TRIMSPOOL ON HEADING OFF FEEDBACK OFF\nSELECT output\nFROM   TABLE(DBMS_WORKLOAD_REPOSITORY.ASH_REPORT_TEXT(\n         l_dbid     => (SELECT dbid FROM v$database),\n         l_inst_num => (SELECT instance_number FROM v$instance),\n         l_btime    => SYSDATE - 30/1440,\n         l_etime    => SYSDATE));\n-- Interactive: @?/rdbms/admin/ashrpt.sql","what":"Usa las muestras de Active Session History para mostrar, en porcentajes, qué wait events y qué sentencias SQL consumieron DB time en los últimos minutos; diagnostica el incidente sin esperar a un snapshot de AWR.","when":"Durante una ralentización en vivo, para picos cortos que desaparecen en los promedios de AWR, y para ver qué esperaba una sesión o un SQL concreto.","caution":"Consultar V$ACTIVE_SESSION_HISTORY y DBA_HIST_ACTIVE_SESS_HISTORY requiere la licencia de Diagnostics Pack. ASH es muestreado: el número de muestras estima el tiempo, no son mediciones exactas, y los SQL muy cortos pueden no aparecer. El buffer de ASH en memoria solo guarda unas pocas horas en sistemas con mucha carga.","tags":["ash","active session history","top wait","top sql","wait","ashrpt","real-time analysis","diagnostics pack"]},{"id":"addm-task-report","task":"Ejecutar un análisis ADDM y obtener su informe","category":"Rendimiento","command":"-- Analyze the range between two snapshots (in CDB root):\nVAR tname VARCHAR2(60)\nBEGIN\n  :tname := 'ADDM_<begin_snap>_<end_snap>';\n  DBMS_ADDM.ANALYZE_DB(\n    task_name      => :tname,\n    begin_snapshot => <begin_snap>,\n    end_snapshot   => <end_snap>);\nEND;\n/\n\nSET LONG 1000000 LONGCHUNKSIZE 1000000 LINESIZE 200 PAGESIZE 0\nSELECT DBMS_ADDM.GET_REPORT(task_name => :tname) FROM dual;\n\n-- Findings summary (by impact):\nSET PAGESIZE 100\nSELECT finding_name, type, ROUND(impact/1e6) AS impact_sec, message\nFROM   dba_advisor_findings\nWHERE  task_name = :tname\nORDER  BY impact DESC;\n\n-- Interactive alternative: @?/rdbms/admin/addmrpt.sql\n-- Clean up the task when done:\nEXEC DBMS_ADDM.DELETE(task_name => :tname);","what":"Automatic Database Diagnostic Monitor analiza el DB time entre dos snapshots de AWR e informa de los cuellos de botella con su impacto (findings) y recomendaciones concretas.","when":"Para obtener un resumen rápido de \"dónde debo mirar\" antes de leer el informe AWR, o para analizar un rango personalizado más allá de la ejecución automática de ADDM tras cada snapshot.","caution":"Requiere la licencia de Diagnostics Pack. No aplique a ciegas las recomendaciones de ADDM (aumentos de parámetros, ejecutar SQL Tuning Advisor, etc.); valide las recomendaciones de memoria con las secciones de advisory de AWR. El rango no puede abarcar un reinicio.","tags":["addm","dbms_addm","advisor","finding","finding","recommendation","diagnostics pack","performance"]},{"id":"sql-monitor-report","task":"Obtener un informe de SQL Monitor (SQL largo en ejecución o finalizado)","category":"Rendimiento","command":"-- List monitored SQL (longer than 5 s, parallel, or /*+ MONITOR */):\nSELECT sql_id, status, username, sql_exec_id,\n       TO_CHAR(sql_exec_start,'YYYY-MM-DD HH24:MI:SS') AS exec_start,\n       ROUND(elapsed_time/1e6,1) AS elapsed_s,\n       ROUND(cpu_time/1e6,1)     AS cpu_s,\n       buffer_gets, px_servers_allocated\nFROM   v$sql_monitor\nORDER  BY sql_exec_start DESC\nFETCH FIRST 20 ROWS ONLY;\n\n-- TEXT report (actual rows, time and activity per plan line):\nSET LONG 10000000 LONGCHUNKSIZE 10000000 LINESIZE 300 PAGESIZE 0 TRIMSPOOL ON\nSELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR(\n         sql_id       => '<sql_id>',\n         type         => 'TEXT',\n         report_level => 'ALL') AS report\nFROM   dual;\n\n-- Shareable HTML (interactive in a browser):\nSPOOL /tmp/sqlmon_<sql_id>.html\nSELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR(\n         sql_id => '<sql_id>', type => 'ACTIVE', report_level => 'ALL')\nFROM   dual;\nSPOOL OFF","what":"Real-Time SQL Monitoring muestra, para cada paso del plan de ejecución de un SQL largo o paralelo, las filas reales, el tiempo transcurrido, el uso de memoria/temp y la actividad de ASH; puede seguir el progreso mientras el SQL aún se ejecuta.","when":"Cuando se pregunta si una consulta de batch/informe está atascada y cuánto le falta, para analizar la distribución de consultas paralelas (PX skew) y para encontrar diferencias entre las filas estimadas (E-Rows) y las reales.","caution":"Requiere la licencia de Tuning Pack (Diagnostics Pack es un requisito previo). Por defecto solo se monitorean los SQL que consumen 5 s de CPU/I-O o que se ejecutan en paralelo; use el hint /*+ MONITOR */ para los demás. Los planes muy grandes (_sqlmon_max_planlines, por defecto 300) no se monitorean. Las entradas permanecen en memoria un tiempo limitado; en 12c+ los informes antiguos también están disponibles en DBA_HIST_REPORTS.","tags":["sql monitor","report_sql_monitor","v$sql_monitor","real-time","long running query","parallel","tuning pack"]},{"id":"sql-trace-10046-tkprof","task":"Habilitar/deshabilitar el trace 10046 para una sesión y resumirlo con TKPROF","category":"Rendimiento","command":"-- 1) Find the target session and its trace file:\nSELECT s.sid, s.serial#, s.username, s.module, p.tracefile\nFROM   v$session s\n  JOIN v$process p ON p.addr = s.paddr\nWHERE  s.username = 'CUSTOMER_APP';\n\n-- 2) Enable trace (waits + binds = equivalent of level 12):\nBEGIN\n  DBMS_MONITOR.SESSION_TRACE_ENABLE(\n    session_id => <sid>,\n    serial_num => <serial#>,\n    waits      => TRUE,\n    binds      => TRUE,\n    plan_stat  => 'ALL_EXECUTIONS');\nEND;\n/\n-- Verify: SELECT sql_trace, sql_trace_waits, sql_trace_binds FROM v$session WHERE sid = <sid>;\n\n-- 3) Disable once the problem operation completes:\nEXEC DBMS_MONITOR.SESSION_TRACE_DISABLE(session_id => <sid>, serial_num => <serial#>);\n\n-- Alternatives:\n-- Your own session: ALTER SESSION SET EVENTS '10046 trace name context forever, level 12';\n-- Single SQL_ID, all sessions: ALTER SYSTEM SET EVENTS 'sql_trace[SQL: <sql_id>] wait=true, bind=true';\n--   disable: ALTER SYSTEM SET EVENTS 'sql_trace[SQL: <sql_id>] off';\n-- For a connection pool: DBMS_MONITOR.CLIENT_ID_TRACE_ENABLE / SERV_MOD_ACT_TRACE_ENABLE\n\n-- 4) Summarize with TKPROF on the OS (most expensive SQL first):\n-- $ tkprof /u01/app/oracle/diag/rdbms/orcl/ORCL/trace/ORCL_ora_12345.trc /tmp/ora_12345.prf \\\n--     sys=no sort=exeela,fchela,prsela waits=yes aggregate=yes","what":"El extended SQL trace (10046) escribe en un archivo trace los tiempos de parse/execute/fetch, los wait events y los valores bind de cada SQL que ejecuta la sesión; TKPROF lo convierte en un informe legible agregado por SQL.","when":"Cuando AWR/ASH no bastan y debe demostrar de extremo a extremo en qué gasta el tiempo una operación concreta de un usuario; para problemas de ida y vuelta aplicación-base de datos (SQL*Net).","caution":"No requiere licencia. Los archivos trace crecen rápido y añaden sobrecarga a la sesión de destino; manténgalo breve y deshabilítelo siempre (revise DBA_ENABLED_TRACES por si quedan restos). binds=TRUE escribe datos sensibles (documentos de identidad, números de tarjeta, etc.) en el archivo trace. ALTER SESSION SET EVENTS requiere el privilegio ALTER SESSION (si no, ORA-01031). TKPROF no muestra los valores bind por defecto; conserve el archivo .trc original.","note":"El tracing por connection pool o por servicio genera varios archivos; combínelos con trcsess (trcsess output=combined.trc clientid=<id> *.trc) y después ejecute TKPROF.","tags":["10046","trace","tkprof","dbms_monitor","sql trace","wait","bind","session tracing"]},{"id":"xplan-allstats-last","task":"Mostrar el plan de ejecución real con estadísticas de row source (ALLSTATS LAST)","category":"Rendimiento","command":"-- 1) Collect row source statistics (for this query only):\nSET SERVEROUTPUT OFF\nSELECT /*+ gather_plan_statistics */ customer_id, SUM(amount)\nFROM   customer_app.sales\nWHERE  created_at >= DATE '2026-06-01'\nGROUP  BY customer_id;\n-- or for the whole session: ALTER SESSION SET statistics_level = ALL;\n\n-- 2) Plan of the last cursor run in the same session (E-Rows vs A-Rows):\nSELECT *\nFROM   TABLE(DBMS_XPLAN.DISPLAY_CURSOR(\n         sql_id          => NULL,\n         cursor_child_no => NULL,\n         format          => 'ALLSTATS LAST +PEEKED_BINDS +OUTLINE'));\n\n-- 3) A cursor from another session / from earlier:\nSELECT *\nFROM   TABLE(DBMS_XPLAN.DISPLAY_CURSOR('<sql_id>', NULL, 'ALLSTATS LAST +PEEKED_BINDS'));","what":"EXPLAIN PLAN es una estimación; esto muestra el plan que realmente se ejecutó, con Starts, E-Rows (estimación), A-Rows (real), A-Time, Buffers y el uso de memoria por paso. Es la forma más directa de encontrar el paso donde empieza el error de estimación de cardinalidad.","when":"Al ajustar un único SQL lento, para demostrar la necesidad de estadísticas/histogramas y para medir el efecto de un hint o de un cambio de índice.","caution":"No requiere licencia. Con SET SERVEROUTPUT ON, sql_id=>NULL muestra la llamada a DBMS_OUTPUT como último cursor; póngalo en OFF primero. No habilite statistics_level=ALL a nivel de sistema; añade una sobrecarga de CPU notable. El hint cambia el texto del SQL, por lo que obtiene un sql_id nuevo. El usuario necesita acceso de lectura a V$SQL_PLAN_STATISTICS_ALL (SELECT_CATALOG_ROLE).","tags":["dbms_xplan","display_cursor","allstats last","gather_plan_statistics","actual plan","execution plan","a-rows","cardinality"]},{"id":"sql-tuning-advisor-task","task":"Crear y ejecutar una tarea de SQL Tuning Advisor y leer sus recomendaciones","category":"Rendimiento","command":"-- 1) Create and run a task for a SQL in the cursor cache:\nVAR task VARCHAR2(64)\nBEGIN\n  :task := DBMS_SQLTUNE.CREATE_TUNING_TASK(\n             sql_id      => '<sql_id>',\n             scope       => DBMS_SQLTUNE.SCOPE_COMPREHENSIVE,\n             time_limit  => 300,\n             task_name   => 'STA_<sql_id>',\n             description => 'Slow report query');\n  DBMS_SQLTUNE.EXECUTE_TUNING_TASK(task_name => :task);\nEND;\n/\n-- For a historical SQL in AWR: begin_snap => <b>, end_snap => <e>, sql_id => '<sql_id>'\n\n-- 2) Status and report:\nSELECT task_name, status FROM dba_advisor_log WHERE task_name = :task;\n\nSET LONG 1000000 LONGCHUNKSIZE 1000000 LINESIZE 200 PAGESIZE 0\nSELECT DBMS_SQLTUNE.REPORT_TUNING_TASK(:task) FROM dual;\n\n-- 3) Get the recommendations as an executable script:\nSELECT DBMS_SQLTUNE.SCRIPT_TUNING_TASK(:task) FROM dual;\n\n-- 4) When done:\nEXEC DBMS_SQLTUNE.DROP_TUNING_TASK(task_name => :task);","what":"Ejecuta el optimizador en un \"tuning mode\" extendido para el SQL y genera recomendaciones sobre estadísticas faltantes, índices nuevos, un SQL profile, planes alternativos y reestructuración del SQL; valida la recomendación del profile con ejecuciones de prueba.","when":"Cuando un único SQL problemático necesita una segunda opinión rápida, o al buscar una corrección mediante SQL profile para una regresión de plan sin cambiar el código.","caution":"Requiere la licencia de Tuning Pack. El alcance COMPREHENSIVE puede ejecutar el SQL para pruebas (solo la parte de consulta en DML); defina time_limit en producción. Las recomendaciones de índices ignoran el resto de la carga de trabajo; evalúe por separado el costo en DML. El usuario necesita el privilegio ADVISOR.","tags":["sql tuning advisor","dbms_sqltune","tuning task","recommendation","sql profile","index recommendation","tuning pack"]},{"id":"sql-profile-accept","task":"Aceptar, deshabilitar y eliminar un SQL profile","category":"Rendimiento","command":"-- 1) Accept the profile recommended by the tuning task:\nDECLARE\n  l_name VARCHAR2(128);\nBEGIN\n  l_name := DBMS_SQLTUNE.ACCEPT_SQL_PROFILE(\n              task_name   => '<task_name>',\n              name        => 'PROF_<sql_id>',\n              description => 'STA recommendation, CHG-1234',\n              force_match => TRUE,     -- also matches statements differing only in literals\n              replace     => TRUE);\n  DBMS_OUTPUT.PUT_LINE('Profile: ' || l_name);\nEND;\n/\n\n-- 2) Profiles and the cursors using them:\nSELECT name, category, status, force_matching,\n       TO_CHAR(created,'YYYY-MM-DD HH24:MI') AS created\nFROM   dba_sql_profiles\nORDER  BY created DESC;\n\nSELECT sql_id, child_number, plan_hash_value, sql_profile\nFROM   v$sql\nWHERE  sql_profile IS NOT NULL;\n\n-- 3) Temporarily disable / drop:\nEXEC DBMS_SQLTUNE.ALTER_SQL_PROFILE(name => 'PROF_<sql_id>', attribute_name => 'STATUS', value => 'DISABLED');\nEXEC DBMS_SQLTUNE.DROP_SQL_PROFILE(name => 'PROF_<sql_id>', ignore => TRUE);","what":"Un SQL profile da al optimizador correcciones adicionales de cardinalidad/selectividad (OPT_ESTIMATE y similares) para que genere un plan mejor sin cambiar el texto del SQL; con FORCE_MATCH también se aplica a sentencias que solo difieren en los literales.","when":"Cuando el SQL no se puede cambiar (aplicación empaquetada), cuando STA recomienda una mejora validada, o como corrección rápida de una regresión de plan.","caution":"Requiere la licencia de Tuning Pack. Un profile no \"fija\" el plan; corrige estimaciones, así que el plan aún puede cambiar cuando cambia la distribución de los datos. Si la estabilidad del plan es obligatoria, use un SQL plan baseline. Cuando existen ambos, el optimizador elige entre los planes aceptados del baseline. Lea siempre los resultados de validación (la sección 'Validation results' del informe) antes de aceptar.","tags":["sql profile","accept_sql_profile","dbms_sqltune","force_match","plan fix","dba_sql_profiles","tuning pack"]},{"id":"spm-load-from-cursor-cache","task":"Cargar un plan bueno desde el cursor cache como SQL plan baseline","category":"Rendimiento","command":"-- 1) Compare the SQL's plans and their performance:\nSELECT sql_id, child_number, plan_hash_value, executions,\n       ROUND(elapsed_time/1e3/NULLIF(executions,0),1) AS ms_per_exec,\n       sql_plan_baseline\nFROM   v$sql\nWHERE  sql_id = '<sql_id>';\n\n-- 2) Load the chosen plan as a baseline:\nSET SERVEROUTPUT ON\nDECLARE\n  n PLS_INTEGER;\nBEGIN\n  n := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(\n         sql_id          => '<sql_id>',\n         plan_hash_value => <plan_hash_value>,\n         fixed           => 'NO',\n         enabled         => 'YES');\n  DBMS_OUTPUT.PUT_LINE(n || ' plan(s) loaded');\nEND;\n/\n\n-- 3) Verify the baseline:\nSELECT sql_handle, plan_name, origin, enabled, accepted, fixed, reproduced,\n       TO_CHAR(last_executed,'YYYY-MM-DD HH24:MI') AS last_exec\nFROM   dba_sql_plan_baselines\nWHERE  signature = (SELECT exact_matching_signature FROM v$sql\n                    WHERE sql_id = '<sql_id>' AND ROWNUM = 1);\n-- On subsequent executions v$sql.sql_plan_baseline should be populated.","what":"Usa SQL Plan Management para convertir el plan elegido en un baseline ACCEPTED; a partir de entonces el optimizador usa solo planes aceptados del baseline y aparca los planes nuevos hasta que se evolucionen.","when":"Para volver a un plan bueno conocido durante una regresión de plan, y para proteger los planes de SQL críticos antes de un upgrade o un cambio de estadísticas.","caution":"Sin licencia adicional en Enterprise Edition (en SE2 está limitado a un plan de baseline por SQL). Requiere ADMINISTER SQL MANAGEMENT OBJECT. Un baseline se aplica exactamente al mismo texto SQL (firma); no ayuda a SQL que usa literales. Si se elimina un índice que usa el plan, este no se puede reproducir (REPRODUCED=NO) y el optimizador vuelve a la elección basada en costos. OPTIMIZER_USE_SQL_PLAN_BASELINES debe ser TRUE (por defecto).","note":"Si el plan bueno ya no está en el cursor cache, cárguelo desde AWR: en 12.2+ DBMS_SPM.LOAD_PLANS_FROM_AWR(begin_snap, end_snap, basic_filter => q'[sql_id='<sql_id>']'); en versiones anteriores pase por un SQL Tuning Set con LOAD_PLANS_FROM_SQLSET.","tags":["spm","sql plan baseline","dbms_spm","load_plans_from_cursor_cache","plan pinning","plan regression","plan_hash_value"]},{"id":"spm-evolve-baseline","task":"Evolucionar planes no aceptados de un SQL plan baseline","category":"Rendimiento","command":"-- 1) Plans waiting for acceptance:\nSELECT sql_handle, plan_name, origin, enabled, accepted, fixed,\n       TO_CHAR(created,'YYYY-MM-DD HH24:MI') AS created\nFROM   dba_sql_plan_baselines\nWHERE  accepted = 'NO'\nORDER  BY created DESC;\n\n-- 2) Evolve task: test-executes the candidate plan against the accepted plan\nVAR tname VARCHAR2(128)\nVAR ename VARCHAR2(128)\nBEGIN\n  :tname := DBMS_SPM.CREATE_EVOLVE_TASK(\n              sql_handle => '<sql_handle>',\n              time_limit => 600,\n              task_name  => 'EVOLVE_<sql_handle>');\n  :ename := DBMS_SPM.EXECUTE_EVOLVE_TASK(task_name => :tname);\nEND;\n/\n\nSET LONG 1000000 LONGCHUNKSIZE 1000000 LINESIZE 200 PAGESIZE 0\nSELECT DBMS_SPM.REPORT_EVOLVE_TASK(\n         task_name => :tname, execution_name => :ename,\n         type => 'TEXT', level => 'TYPICAL', section => 'ALL')\nFROM   dual;\n\n-- 3) If the report has recommendations (plans proven better), accept them:\nSET SERVEROUTPUT ON\nDECLARE\n  n PLS_INTEGER;\nBEGIN\n  n := DBMS_SPM.IMPLEMENT_EVOLVE_TASK(task_name => :tname);\n  DBMS_OUTPUT.PUT_LINE(n || ' plan(s) accepted');\nEND;\n/\nEXEC DBMS_SPM.DROP_EVOLVE_TASK(task_name => :tname);","what":"Cuando existe un baseline, los planes nuevos que encuentra el optimizador se guardan con ACCEPTED=NO. La tarea de evolve ejecuta en prueba estos candidatos frente al plan aceptado y acepta solo los que son claramente mejores.","when":"Para SQL con baseline cuando un índice nuevo o estadísticas nuevas pueden haber producido un plan mejor; para decidir manualmente sin esperar a la SYS_AUTO_SPM_EVOLVE_TASK automática (12c+).","caution":"Requiere Enterprise Edition. El evolve ejecuta realmente el SQL (solo la parte de consulta en DML); defina time_limit para SQL pesados y evite las horas pico. Requiere ADMINISTER SQL MANAGEMENT OBJECT y ADVISOR. Los SQL parseados como SYS no se pueden evolucionar (ORA-16963); ejecútelo como el usuario de la aplicación.","since":"Oracle 12c","note":"La función DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE anterior a 12c está obsoleta; 12c+ usa la API basada en tareas (CREATE/EXECUTE/IMPLEMENT_EVOLVE_TASK). Comportamiento de la tarea automática: DBMS_SPM.SET_EVOLVE_TASK_PARAMETER('SYS_AUTO_SPM_EVOLVE_TASK', 'ACCEPT_PLANS', 'FALSE').","tags":["spm","evolve","create_evolve_task","baseline","plan acceptance","dbms_spm","accepted"]},{"id":"spm-alter-drop-baseline","task":"Fijar, deshabilitar o eliminar un plan de un SQL plan baseline","category":"Rendimiento","command":"-- Show the plan content:\nSELECT *\nFROM   TABLE(DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE(\n         sql_handle => '<sql_handle>',\n         plan_name  => '<plan_name>',\n         format     => 'TYPICAL'));\n\nSET SERVEROUTPUT ON\nDECLARE\n  n PLS_INTEGER;\nBEGIN\n  -- FIXED: this plan is preferred, new plans are not evolved automatically\n  n := DBMS_SPM.ALTER_SQL_PLAN_BASELINE(\n         sql_handle => '<sql_handle>', plan_name => '<plan_name>',\n         attribute_name => 'fixed', attribute_value => 'YES');\n\n  -- Take the bad plan out of use (without dropping it):\n  n := DBMS_SPM.ALTER_SQL_PLAN_BASELINE(\n         sql_handle => '<sql_handle>', plan_name => '<bad_plan_name>',\n         attribute_name => 'enabled', attribute_value => 'NO');\nEND;\n/\n\n-- Drop one plan or the SQL's entire baseline:\nDECLARE\n  n PLS_INTEGER;\nBEGIN\n  n := DBMS_SPM.DROP_SQL_PLAN_BASELINE(sql_handle => '<sql_handle>', plan_name => '<plan_name>');\n  -- without plan_name, all plans of the SQL are dropped:\n  -- n := DBMS_SPM.DROP_SQL_PLAN_BASELINE(sql_handle => '<sql_handle>');\n  DBMS_OUTPUT.PUT_LINE(n || ' plan(s) dropped');\nEND;\n/","what":"Gestiona los atributos de los planes del baseline: un plan FIXED se prefiere frente a otros planes aceptados y detiene la evolución automática de planes nuevos; ENABLED=NO deshabilita un plan sin borrarlo; DROP lo elimina definitivamente.","when":"Para fijar firmemente un plan de un SQL crítico, deshabilitar un plan malo aceptado por error, o limpiar baselines que ya no se necesitan.","caution":"Requiere Enterprise Edition. Los planes FIXED pueden degradarse a medida que crecen los datos e impedir que el optimizador use un plan mejor; revíselos periódicamente. Un SQL cuyos planes son todos ENABLED=NO se optimiza libremente. DROP es irreversible; haga antes un backup en una tabla de staging con DBMS_SPM.PACK_STGTAB_BASELINE.","tags":["spm","baseline","fixed","alter_sql_plan_baseline","drop_sql_plan_baseline","disable plan","display_sql_plan_baseline"]},{"id":"bind-sensitivity-acs-check","task":"Comprobar el estado de bind peeking y adaptive cursor sharing","category":"Rendimiento","command":"-- 1) Child cursors: bind-sensitive / bind-aware / shareable?\nSELECT sql_id, child_number, executions, plan_hash_value,\n       is_bind_sensitive, is_bind_aware, is_shareable,\n       ROUND(buffer_gets/NULLIF(executions,0)) AS gets_per_exec\nFROM   v$sql\nWHERE  sql_id = '<sql_id>'\nORDER  BY child_number;\n\n-- 2) ACS histogram and selectivity ranges:\nSELECT child_number, bucket_id, count\nFROM   v$sql_cs_histogram\nWHERE  sql_id = '<sql_id>'\nORDER  BY child_number, bucket_id;\n\nSELECT child_number, predicate, range_id, low, high\nFROM   v$sql_cs_selectivity\nWHERE  sql_id = '<sql_id>';\n\n-- 3) Peeked / captured bind values:\nSELECT child_number, name, position, datatype_string, value_string,\n       TO_CHAR(last_captured,'YYYY-MM-DD HH24:MI:SS') AS captured\nFROM   v$sql_bind_capture\nWHERE  sql_id = '<sql_id>'\nORDER  BY child_number, position;\n\nSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('<sql_id>', NULL, 'TYPICAL +PEEKED_BINDS'));\n\n-- 4) Why was a new child created?\nSELECT child_number, bind_mismatch, bind_equiv_failure,\n       optimizer_mismatch, load_optimizer_stats\nFROM   v$sql_shared_cursor\nWHERE  sql_id = '<sql_id>';","what":"El optimizador elige un plan inspeccionando el valor bind en el primer hard parse. Adaptive cursor sharing puede crear distintos child cursors/planes por valor bind en columnas con histogramas; estas consultas muestran si el cursor es bind-sensitive/aware, qué valor se inspeccionó y por qué se crearon los children.","when":"Para quejas del tipo \"el mismo SQL a veces es rápido y a veces lento\", con distribución de datos sesgada, y cuando el número de child cursors se dispara (high version count).","caution":"No requiere licencia; solo lectura. V$SQL_BIND_CAPTURE es muestreado (aprox. cada 15 minutos por defecto) y solo contiene binds de WHERE/HAVING; no es el valor de cada ejecución. ACS hace que un cursor sea bind-aware solo tras unas cuantas ejecuciones malas. Para SQL críticos, cargar varios planes en un SQL plan baseline es más determinista que depender de ACS.","since":"Oracle 11g","note":"Adaptive cursor sharing se introdujo en 11g. Para el análisis de high version count, la columna REASON (XML) de V$SQL_SHARED_CURSOR da la causa detallada.","tags":["bind peeking","adaptive cursor sharing","acs","bind variable","v$sql_bind_capture","child cursor","version count"]},{"id":"top-sql-awr-history","task":"Encontrar los top SQL por tiempo transcurrido/CPU en un periodo pasado (AWR + V$SQLAREA)","category":"Rendimiento","command":"-- AWR: top 20 by elapsed time within a snapshot range (per plan)\nSELECT *\nFROM  (SELECT st.sql_id,\n              st.plan_hash_value,\n              SUM(st.executions_delta)                       AS execs,\n              ROUND(SUM(st.elapsed_time_delta)/1e6, 1)       AS elapsed_s,\n              ROUND(SUM(st.cpu_time_delta)/1e6, 1)           AS cpu_s,\n              ROUND(SUM(st.iowait_delta)/1e6, 1)             AS io_wait_s,\n              SUM(st.buffer_gets_delta)                      AS buffer_gets,\n              ROUND(SUM(st.elapsed_time_delta)/1e3\n                    / NULLIF(SUM(st.executions_delta),0), 2) AS ms_per_exec,\n              MAX(st.parsing_schema_name)                    AS parsing_schema\n       FROM   dba_hist_sqlstat st\n       WHERE  st.dbid = (SELECT dbid FROM v$database)\n         AND  st.snap_id BETWEEN <begin_snap> AND <end_snap>\n       GROUP  BY st.sql_id, st.plan_hash_value\n       ORDER  BY elapsed_s DESC)\nWHERE ROWNUM <= 20;\n-- For CPU ranking: ORDER BY cpu_s DESC\n-- SQL text: SELECT DBMS_LOB.SUBSTR(sql_text, 4000, 1) FROM dba_hist_sqltext WHERE sql_id = '<sql_id>';\n-- Plan history of a single SQL: @?/rdbms/admin/awrsqrpt.sql\n\n-- Current (since instance startup, still in cache) by CPU:\nSELECT sql_id,\n       ROUND(cpu_time/1e6,1)     AS cpu_s,\n       ROUND(elapsed_time/1e6,1) AS elapsed_s,\n       executions, buffer_gets, disk_reads,\n       ROUND(cpu_time/1e3/NULLIF(executions,0),2) AS cpu_ms_per_exec,\n       parsing_schema_name,\n       SUBSTR(sql_text,1,80) AS sql_text\nFROM   v$sqlarea\nWHERE  parsing_schema_name NOT IN ('SYS','SYSTEM')\nORDER  BY cpu_time DESC\nFETCH FIRST 20 ROWS ONLY;","what":"Suma las columnas *_DELTA de DBA_HIST_SQLSTAT para listar los SQL más costosos en un rango de snapshots, desglosados por plan_hash_value, lo que también revela cambios de plan del mismo SQL. La consulta a V$SQLAREA da valores acumulados de lo que sigue en el cache.","when":"Para \"¿qué cargó el sistema anoche entre las 02:00 y las 04:00?\", detectar cambios de plan (mismo sql_id, distinto plan_hash_value) y elaborar una lista de candidatos para tuning.","caution":"Consultar DBA_HIST_* requiere la licencia de Diagnostics Pack; V$SQLAREA no. AWR guarda solo los top SQL (top-N por defecto), por lo que pueden faltar sentencias pequeñas pero muy frecuentes. Los valores de V$SQLAREA se pierden cuando el cursor sale del cache. Los deltas siguen siendo correctos tras un reinicio, pero la consulta basada en el cache empieza desde cero.","tags":["top sql","dba_hist_sqlstat","v$sqlarea","elapsed","cpu","most expensive query","plan change","diagnostics pack"]},{"id":"stats-stale-prefs","task":"Encontrar estadísticas obsoletas y definir preferencias de estadísticas por tabla (prefs)","category":"Estadísticas","command":"-- 1) Flush DML counters to the dictionary and list stale objects:\nEXEC DBMS_STATS.FLUSH_DATABASE_MONITORING_INFO;\n\nSELECT owner, table_name, partition_name, num_rows,\n       TO_CHAR(last_analyzed,'YYYY-MM-DD HH24:MI') AS last_analyzed,\n       stale_stats, stattype_locked\nFROM   dba_tab_statistics\nWHERE  owner = 'CUSTOMER_APP'\n  AND  (stale_stats = 'YES' OR last_analyzed IS NULL)\nORDER  BY table_name, partition_name;\n\nSELECT table_name, partition_name, inserts, updates, deletes, truncated\nFROM   dba_tab_modifications\nWHERE  table_owner = 'CUSTOMER_APP';\n\n-- 2) Per-table preferences (used by the auto stats job and manual GATHER calls):\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALES', 'STALE_PERCENT', '5');\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALES', 'METHOD_OPT', 'FOR ALL COLUMNS SIZE AUTO FOR COLUMNS SIZE 254 CUSTOMER_ID');\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALES', 'DEGREE', '4');\n\nSELECT DBMS_STATS.GET_PREFS('STALE_PERCENT', 'CUSTOMER_APP', 'SALES') AS stale_pct FROM dual;\nSELECT table_name, preference_name, preference_value\nFROM   dba_tab_stat_prefs WHERE owner = 'CUSTOMER_APP';\n\n-- 3) Gather only stale objects:\nEXEC DBMS_STATS.GATHER_SCHEMA_STATS(ownname => 'CUSTOMER_APP', options => 'GATHER STALE');\n-- Revert a preference: EXEC DBMS_STATS.DELETE_TABLE_PREFS('CUSTOMER_APP', 'SALES', 'STALE_PERCENT');","what":"Las estadísticas se vuelven obsoletas (stale) cuando la proporción de cambios supera STALE_PERCENT (10% por defecto). Las preferencias guardan por tabla el muestreo, los histogramas, el paralelismo y los umbrales de obsolescencia para que la tarea automática nocturna de estadísticas aplique las mismas reglas.","when":"Cuando el umbral del 10% se dispara demasiado tarde en tablas grandes, cuando determinadas columnas deben tener siempre histograma, y para definir la estrategia de recopilación en el diccionario en lugar de en scripts.","caution":"No requiere licencia. Las preferencias se anulan con parámetros explícitos en una llamada GATHER manual. Cambiar METHOD_OPT añade o quita histogramas y puede cambiar planes. DBA_TAB_MODIFICATIONS solo está actualizada tras un flush (flush automático aproximadamente cada 15 minutos).","tags":["stale stats","stale_percent","set_table_prefs","dba_tab_statistics","dba_tab_modifications","statistics preference","gather stale"]},{"id":"stats-lock-unlock","task":"Bloquear y desbloquear las estadísticas de una tabla/esquema","category":"Estadísticas","command":"-- Lock (the auto stats job and normal GATHER calls no longer touch them):\nEXEC DBMS_STATS.LOCK_TABLE_STATS(ownname => 'CUSTOMER_APP', tabname => 'ORDERS');\nEXEC DBMS_STATS.LOCK_SCHEMA_STATS(ownname => 'CUSTOMER_APP');\n-- A single partition only: DBMS_STATS.LOCK_PARTITION_STATS('CUSTOMER_APP', 'SALE_PART', 'P2026_H1');\n\n-- Locked objects:\nSELECT owner, table_name, partition_name, stattype_locked\nFROM   dba_tab_statistics\nWHERE  owner = 'CUSTOMER_APP' AND stattype_locked IS NOT NULL;\n\n-- Deliberate gathering while locked (instead of ORA-20005):\nEXEC DBMS_STATS.GATHER_TABLE_STATS('CUSTOMER_APP', 'ORDERS', force => TRUE);\n\n-- Unlock:\nEXEC DBMS_STATS.UNLOCK_SCHEMA_STATS(ownname => 'CUSTOMER_APP');\nEXEC DBMS_STATS.UNLOCK_TABLE_STATS(ownname => 'CUSTOMER_APP', tabname => 'ORDERS');","what":"Bloquea las estadísticas frente a cambios; la tarea automática omite los objetos bloqueados y una llamada GATHER a nivel de tabla lanza ORA-20005. force=>TRUE recopila sin quitar el bloqueo.","when":"Para mantener estadísticas representativas en tablas de staging/volátiles que se vacían y rellenan durante el día, proteger los planes de una tabla crítica frente a cambios de estadísticas, o conservar valores definidos con DBMS_STATS.SET_TABLE_STATS.","caution":"No requiere licencia. Las estadísticas bloqueadas se alejan de la realidad con el tiempo y causan errores de cardinalidad; informe regularmente de los objetos bloqueados. Un bloqueo de esquema no cubre las tablas añadidas después al esquema. En una tabla bloqueada, CREATE/REBUILD INDEX tampoco recopila automáticamente estadísticas del índice.","tags":["lock stats","lock statistics","unlock_table_stats","lock_schema_stats","ora-20005","stattype_locked","dbms_stats"]},{"id":"stats-restore-history","task":"Restaurar estadísticas a un punto del pasado","category":"Estadísticas","command":"-- 1) Retention and the oldest point you can return to:\nSELECT DBMS_STATS.GET_STATS_HISTORY_RETENTION    AS retention_days,\n       DBMS_STATS.GET_STATS_HISTORY_AVAILABILITY AS oldest_available\nFROM   dual;\n\n-- 2) Statistics history of the table:\nSELECT table_name, partition_name,\n       TO_CHAR(stats_update_time,'YYYY-MM-DD HH24:MI:SS TZH:TZM') AS stats_update_time\nFROM   dba_tab_stats_history\nWHERE  owner = 'CUSTOMER_APP' AND table_name = 'SALES'\nORDER  BY stats_update_time DESC;\n\n-- 3) Return to the point before the plan regressed:\nBEGIN\n  DBMS_STATS.RESTORE_TABLE_STATS(\n    ownname         => 'CUSTOMER_APP',\n    tabname         => 'SALES',\n    as_of_timestamp => TIMESTAMP '2026-10-07 22:00:00 +03:00',\n    no_invalidate   => FALSE);\nEND;\n/\n-- Whole schema: DBMS_STATS.RESTORE_SCHEMA_STATS('CUSTOMER_APP', <timestamp>)\n\n-- Change the retention (days):\n-- EXEC DBMS_STATS.ALTER_STATS_HISTORY_RETENTION(31);","what":"DBMS_STATS guarda las estadísticas anteriores en SYSAUX antes de cada recopilación (31 días por defecto). Los procedimientos RESTORE_* devuelven las estadísticas de la tabla/esquema a su estado en la hora indicada; con no_invalidate=>FALSE los cursores se vuelven a parsear de inmediato.","when":"Como el rollback más rápido cuando una regresión de plan sigue a la recopilación nocturna de estadísticas, y para comprobar si un cambio fue causado por las estadísticas.","caution":"No requiere licencia. Las estadísticas bloqueadas no se pueden restaurar (desbloquee primero). La siguiente recopilación automática vuelve a traer estadísticas nuevas tras la restauración; para una solución permanente defina preferencias de tabla, bloquee las estadísticas o use un SQL plan baseline. Una retención larga hace crecer SYSAUX (tablas WRI$_OPTSTAT_*).","tags":["restore stats","restore statistics","restore_table_stats","dba_tab_stats_history","stats history","plan regression","dbms_stats"]},{"id":"stats-pending-publish","task":"Recopilar estadísticas como pending, probarlas y publicarlas","category":"Estadísticas","command":"-- 1) Disable publishing for the table (new statistics go to pending):\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'ORDERS', 'PUBLISH', 'FALSE');\nEXEC DBMS_STATS.GATHER_TABLE_STATS('CUSTOMER_APP', 'ORDERS');\n\n-- 2) Compare pending and published values:\nSELECT owner, table_name, partition_name, num_rows,\n       TO_CHAR(last_analyzed,'YYYY-MM-DD HH24:MI') AS last_analyzed\nFROM   dba_tab_pending_stats\nWHERE  owner = 'CUSTOMER_APP';\nSELECT num_rows, last_analyzed FROM dba_tables\nWHERE  owner = 'CUSTOMER_APP' AND table_name = 'ORDERS';\n\n-- 3) Try plans with pending statistics in this session only:\nALTER SESSION SET optimizer_use_pending_statistics = TRUE;\nEXPLAIN PLAN FOR SELECT COUNT(*) FROM customer_app.orders WHERE status = 7;\nSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(format => 'BASIC +ROWS'));\nALTER SESSION SET optimizer_use_pending_statistics = FALSE;\n\n-- 4a) If good, publish:\nEXEC DBMS_STATS.PUBLISH_PENDING_STATS('CUSTOMER_APP', 'ORDERS', no_invalidate => FALSE);\n-- 4b) If bad, discard:\n-- EXEC DBMS_STATS.DELETE_PENDING_STATS('CUSTOMER_APP', 'ORDERS');\n\n-- 5) Return to normal behavior:\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'ORDERS', 'PUBLISH', 'TRUE');","what":"Con la preferencia PUBLISH=FALSE, las estadísticas recopiladas se escriben en un área pending en lugar del diccionario, por lo que las demás sesiones no se ven afectadas. Los planes se validan en una sesión de prueba con optimizer_use_pending_statistics y luego se publican o descartan con un solo comando.","when":"Para probar sin riesgo en producción el impacto en los planes de estadísticas actualizadas en tablas OLTP críticas, y para un cambio controlado tras una gran carga de datos.","caution":"No requiere licencia. Mientras PUBLISH=FALSE se mantenga, las estadísticas recopiladas por la tarea automática también se acumulan como pending y nunca se publican; recuerde volver a poner la preferencia en TRUE. EXPLAIN PLAN es una estimación; cuando sea posible ejecute la consulta y revise DISPLAY_CURSOR.","since":"Oracle 11g","tags":["pending stats","publish","publish_pending_stats","optimizer_use_pending_statistics","statistics testing","dbms_stats","dba_tab_pending_stats"]},{"id":"stats-incremental-partitioned","task":"Configurar estadísticas incrementales para una tabla particionada","category":"Estadísticas","command":"-- 1) Enable incremental mode (global statistics are derived from partition synopses):\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALE_PART', 'INCREMENTAL', 'TRUE');\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALE_PART', 'INCREMENTAL_STALENESS', 'USE_STALE_PERCENT');\nEXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALE_PART', 'GRANULARITY', 'AUTO');\n\n-- 2) The first gather scans everything and builds synopses; later ones read only changed partitions:\nEXEC DBMS_STATS.GATHER_TABLE_STATS(ownname => 'CUSTOMER_APP', tabname => 'SALE_PART');\n\n-- 3) Verify:\nSELECT DBMS_STATS.GET_PREFS('INCREMENTAL', 'CUSTOMER_APP', 'SALE_PART') AS incremental FROM dual;\nSELECT partition_name, num_rows,\n       TO_CHAR(last_analyzed,'YYYY-MM-DD HH24:MI:SS') AS last_analyzed, stale_stats\nFROM   dba_tab_statistics\nWHERE  owner = 'CUSTOMER_APP' AND table_name = 'SALE_PART'\nORDER  BY partition_position NULLS FIRST;\n\n-- 4) Pre-build synopses on a staging table to be loaded via exchange (12c+):\n-- EXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALE_STAGE', 'INCREMENTAL', 'TRUE');\n-- EXEC DBMS_STATS.SET_TABLE_PREFS('CUSTOMER_APP', 'SALE_STAGE', 'INCREMENTAL_LEVEL', 'TABLE');\n-- EXEC DBMS_STATS.GATHER_TABLE_STATS('CUSTOMER_APP', 'SALE_STAGE');","what":"En modo incremental se guarda una synopsis por partición; las estadísticas globales (a nivel de tabla), en particular el NDV, se calculan combinando synopses sin volver a recorrer toda la tabla. Solo se vuelven a leer las particiones modificadas.","when":"En tablas grandes (escala de TB) particionadas por range/interval donde los datos nuevos solo llegan a las particiones recientes, cuando la recopilación de estadísticas globales excede la ventana de mantenimiento.","caution":"No tiene licencia propia, pero ya se requiere la opción Partitioning (EE con costo adicional). Las synopses consumen espacio en SYSAUX (el formato HyperLogLog de 12.2+ es mucho más pequeño). La primera recopilación es un full scan; iníciela en una ventana de mantenimiento. Poner ESTIMATE_PERCENT en algo distinto de AUTO_SAMPLE_SIZE deshabilita el modo incremental.","since":"Oracle 11g","note":"Las preferencias INCREMENTAL_STALENESS e INCREMENTAL_LEVEL se añadieron en 12c; en 12.2+ el formato de las synopses se controla con APPROXIMATE_NDV_ALGORITHM (HYPERLOGLOG).","tags":["incremental stats","synopsis","partition statistics","global stats","incremental_staleness","dbms_stats","large table"]},{"id":"partition-split-merge-online","task":"Dividir y combinar particiones en línea","category":"Particionamiento","command":"-- SPLIT: split a range partition in two, keep indexes usable\nALTER TABLE customer_app.sale_part\n  SPLIT PARTITION p2026_h2 AT (DATE '2026-10-01')\n  INTO (PARTITION p2026_q3, PARTITION p2026_q4)\n  UPDATE INDEXES ONLINE;\n\n-- MERGE: merge two adjacent partitions\nALTER TABLE customer_app.sale_part\n  MERGE PARTITIONS p2026_q3, p2026_q4 INTO PARTITION p2026_h2\n  UPDATE INDEXES ONLINE;\n\n-- ADD: new partition at the end (when there is no MAXVALUE)\nALTER TABLE customer_app.sale_part\n  ADD PARTITION p2027 VALUES LESS THAN (DATE '2028-01-01');\n\n-- Check: partition bounds and UNUSABLE index partitions\nSELECT partition_name, partition_position, high_value, num_rows\nFROM   dba_tab_partitions\nWHERE  table_owner = 'CUSTOMER_APP' AND table_name = 'SALE_PART'\nORDER  BY partition_position;\n\nSELECT index_name, partition_name, status\nFROM   dba_ind_partitions\nWHERE  index_owner = 'CUSTOMER_APP' AND status <> 'USABLE';","what":"SPLIT divide una partición en dos en un valor límite (12c+: en varias); MERGE combina particiones adyacentes. Con ONLINE, el DML continúa durante la operación; con UPDATE INDEXES, los índices locales y globales no pasan a UNUSABLE.","when":"Para dividir una partición MAXVALUE/\"otros\" que ha crecido, combinar particiones antiguas pequeñas antes de archivarlas o cambiar el esquema de particionamiento (de mensual a trimestral).","caution":"Requiere la opción Partitioning (EE con costo adicional); ONLINE requiere Enterprise Edition. SPLIT/MERGE reescribe físicamente los datos: lento en particiones grandes, genera redo/undo y espacio temporal. Sin ONLINE la tabla queda bloqueada para DML durante la operación; sin UPDATE INDEXES los índices globales pasan a UNUSABLE. Dividir una partición vacía es rápido (fast split).","since":"Oracle 12.2","note":"ONLINE SPLIT llegó en 12.2 y ONLINE MERGE en 18c; ambos están disponibles en 19c. En versiones anteriores quite la palabra clave ONLINE y ejecute la operación en una ventana de mantenimiento.","tags":["split partition","merge partition","add partition","split a partition","merge partitions","update indexes","online","partitioning"]},{"id":"partition-exchange","task":"Carga masiva o archivado de datos con exchange partition","category":"Particionamiento","command":"-- 1) Staging table that exactly matches the target (12.2+):\nCREATE TABLE customer_app.sale_stage\n  FOR EXCHANGE WITH TABLE customer_app.sale_part;\n\n-- 2) Load the staging table and build indexes matching the local indexes:\nINSERT /*+ APPEND */ INTO customer_app.sale_stage\nSELECT * FROM customer_app.sale_load WHERE created_at >= DATE '2027-01-01';\nCOMMIT;\nCREATE INDEX customer_app.sale_stage_customer_ix ON customer_app.sale_stage (customer_id);\n\n-- 3) Swap (dictionary-only operation, no data movement):\nALTER TABLE customer_app.sale_part\n  EXCHANGE PARTITION p2027 WITH TABLE customer_app.sale_stage\n  INCLUDING INDEXES WITHOUT VALIDATION\n  UPDATE GLOBAL INDEXES;\n\n-- 4) Verify:\nSELECT COUNT(*) FROM customer_app.sale_part PARTITION (p2027);\nSELECT index_name, partition_name, status\nFROM   dba_ind_partitions\nWHERE  index_owner = 'CUSTOMER_APP' AND status <> 'USABLE';","what":"Intercambia en el diccionario el segmento de una partición con el segmento de una tabla normal; millones de filas \"entran\" en la tabla o \"salen\" de ella para archivarse en segundos. FOR EXCHANGE WITH TABLE crea una tabla de staging que garantiza la compatibilidad de columnas y columnas ocultas.","when":"Para cargas masivas de DWH/ETL, sacar una partición antigua a una tabla de archivo, y preparar índices y estadísticas sin afectar a la tabla activa durante la carga.","caution":"Requiere la opción Partitioning (EE con costo adicional). WITHOUT VALIDATION no comprueba que las filas de staging encajen en los límites de la partición; los datos erróneos se quedan en la partición equivocada y las consultas pueden devolver resultados incorrectos; si no está seguro use WITH VALIDATION (full scan). Sin UPDATE GLOBAL INDEXES los índices globales pasan a UNUSABLE. Los estados de las restricciones unique/PK deben coincidir en ambos lados.","note":"CREATE TABLE ... FOR EXCHANGE WITH TABLE se introdujo en 12.2; antes, cree la tabla de staging con CREATE TABLE ... AS SELECT * FROM <table> WHERE 1=0 y compruebe manualmente las diferencias de columnas ocultas/virtuales.","tags":["exchange partition","staging","for exchange with table","bulk load","archive","update global indexes","partitioning"]},{"id":"partition-drop-truncate-gidx","task":"Truncar/eliminar una partición manteniendo utilizables los índices globales","category":"Particionamiento","command":"-- Empty a partition / remove a partition (global indexes stay maintained):\nALTER TABLE customer_app.sale_part TRUNCATE PARTITION p2025 UPDATE INDEXES;\nALTER TABLE customer_app.sale_part DROP PARTITION p2025 UPDATE INDEXES;\n\n-- 12c+ asynchronous global index maintenance: index is VALID but carries orphaned entries\nSELECT index_name, status, orphaned_entries\nFROM   dba_indexes\nWHERE  owner = 'CUSTOMER_APP' AND table_name = 'SALE_PART';\n\n-- Clean orphaned entries now (normally SYS.PMO_DEFERRED_GIDX_MAINT_JOB does it at night):\nEXEC DBMS_PART.CLEANUP_GIDX(schema_name_in => 'CUSTOMER_APP', table_name_in => 'SALE_PART');\n-- or a single index: ALTER INDEX customer_app.sale_part_id_gidx COALESCE CLEANUP;\n\n-- Target the partition by value instead of name:\n-- ALTER TABLE customer_app.sale_part DROP PARTITION FOR (DATE '2025-06-15') UPDATE INDEXES;","what":"Elimina al instante los datos antiguos a nivel de partición en lugar de un DELETE fila a fila. En 12c+, UPDATE INDEXES solo marca los índices globales en el diccionario (mantenimiento asíncrono) en lugar de actualizar cada entrada; el índice sigue utilizable de inmediato y las entradas huérfanas se limpian más tarde.","when":"Al purgar periodos antiguos según una política de retención de datos, en jobs de ILM/purga, y para evitar la carga de undo/redo de DELETE en tablas grandes.","caution":"Requiere la opción Partitioning (EE con costo adicional). DROP/TRUNCATE es irreversible (flashback table no recupera una partición); haga antes un backup o archívela mediante exchange. Si se olvida UPDATE INDEXES, los índices globales pasan a UNUSABLE y las consultas pueden recibir ORA-01502. La operación falla si hay tablas hijas que la referencian (FK). En tablas interval no se puede eliminar la última partición range (de transición).","since":"Oracle 12c","tags":["drop partition","truncate partition","update indexes","orphaned_entries","cleanup_gidx","global index","data purge","partitioning"]},{"id":"interval-partitioning","task":"Crear particionamiento interval y convertir una tabla range existente a interval","category":"Particionamiento","command":"-- New table: a partition is created automatically for each month\nCREATE TABLE customer_app.measurements (\n  ts        DATE   NOT NULL,\n  sensor_id NUMBER,\n  value     NUMBER)\nPARTITION BY RANGE (ts) INTERVAL (NUMTOYMINTERVAL(1, 'MONTH'))\n( PARTITION p_first VALUES LESS THAN (DATE '2026-01-01') );\n\n-- Convert an existing range table to interval (there must be no MAXVALUE partition):\nALTER TABLE customer_app.sale_part SET INTERVAL (NUMTOYMINTERVAL(1, 'MONTH'));\n-- Back to range: ALTER TABLE customer_app.sale_part SET INTERVAL ();\n\n-- Rename a system-named partition to a meaningful name:\nALTER TABLE customer_app.measurements RENAME PARTITION FOR (DATE '2026-03-15') TO p2026_03;\n\n-- Check:\nSELECT partition_name, partition_position, interval, high_value\nFROM   dba_tab_partitions\nWHERE  table_owner = 'CUSTOMER_APP' AND table_name = 'MEASUREMENTS'\nORDER  BY partition_position;\n\nSELECT table_name, partitioning_type, interval\nFROM   dba_part_tables\nWHERE  owner = 'CUSTOMER_APP';","what":"El particionamiento interval crea automáticamente una partición nueva cuando llega la primera fila más allá del último límite, eliminando los errores ORA-14400 causados por olvidar la partición del mes siguiente. SET INTERVAL convierte una tabla range existente sin mover datos.","when":"Para tablas que crecen por fecha, como series temporales, logs y pedidos, y para reducir los scripts de mantenimiento de particiones.","caution":"Requiere la opción Partitioning (EE con costo adicional). La clave de partición debe ser una única columna NUMBER/DATE/TIMESTAMP; antes hay que quitar la partición MAXVALUE. Una fecha errónea o extrema (p. ej., el año 9999) abre una partición lejana y distorsiona la numeración de particiones; con intervalos estrechos puede acercarse al límite de 1,048,575 particiones (ORA-14300), así que valide las fechas en la aplicación. Las particiones automáticas van al tablespace por defecto de la tabla (diríjalas con STORE IN).","since":"Oracle 11g","tags":["interval partitioning","interval","automatic partition","numtoyminterval","ora-14400","set interval","range partition","partitioning"]},{"id":"partition-wise-join-check","task":"Verificar las claves de partición y el uso de partition-wise join","category":"Particionamiento","command":"-- 1) Do both tables have the same partitioning type, key and count?\nSELECT p.owner, p.table_name, p.partitioning_type, k.column_name,\n       p.partition_count, p.subpartitioning_type\nFROM   dba_part_tables p\n  JOIN dba_part_key_columns k\n    ON k.owner = p.owner AND k.name = p.table_name AND k.object_type = 'TABLE'\nWHERE  p.owner = 'CUSTOMER_APP'\n  AND  p.table_name IN ('CUSTOMER_H', 'ORDERS_H');\n\n-- 2) Explain the join and inspect the plan shape:\nEXPLAIN PLAN FOR\nSELECT /*+ PARALLEL(4) */ m.city, SUM(s.amount)\nFROM   customer_app.customer_h m\n  JOIN customer_app.orders_h s ON s.customer_id = m.id\nGROUP  BY m.city;\n\nSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(format => 'BASIC +PARALLEL +PARTITION'));\n-- Full PWJ: HASH JOIN sits BELOW the PARTITION HASH ALL line (PX PARTITION HASH ALL in parallel)\n-- Partial PWJ: PX SEND PARTITION (KEY) on one side only\n-- No PWJ: PX SEND HASH / BROADCAST on both sides","what":"Dos tablas equiparticionadas por la clave de join pueden unirse par de particiones a par de particiones (full partition-wise join), lo que reduce mucho el tráfico de redistribución y la memoria en consultas paralelas. La forma del plan muestra si se usa PWJ.","when":"Al diseñar grandes joins fact-to-fact o fact-to-dimension en un DWH, cuando las consultas paralelas muestran mucha redistribución PX (PX Deq Credit: send blkd), y al validar la estrategia de particionamiento.","caution":"Licencia: opción Partitioning (EE con costo adicional); las consultas paralelas requieren Enterprise Edition. EXPLAIN PLAN es una estimación; use DISPLAY_CURSOR/SQL Monitor para el plan real. El PWJ completo necesita el mismo número de particiones (para hash se recomienda una potencia de dos) y tipos de datos de clave coincidentes. PWJ también puede aparecer en planes seriales, pero la ganancia real está en la ejecución paralela.","tags":["partition-wise join","pwj","equi-partitioning","hash partition","parallel query","dba_part_key_columns","partitioning"]},{"id":"online-redefinition-flow","task":"Redefinir una tabla en línea con DBMS_REDEFINITION (flujo completo)","category":"Operaciones en línea","command":"-- 1) Is it eligible? (use CONS_USE_ROWID if there is no PK)\nEXEC DBMS_REDEFINITION.CAN_REDEF_TABLE('CUSTOMER_APP', 'CUSTOMER', DBMS_REDEFINITION.CONS_USE_PK);\n\n-- 2) Interim table with the target structure: hash partitioned in this example\nCREATE TABLE customer_app.customer_int (\n  id NUMBER, name VARCHAR2(100), city VARCHAR2(30))\nPARTITION BY HASH (id) PARTITIONS 8;\n\n-- 3) Start: initial copy + change tracking through an MV log\nBEGIN\n  DBMS_REDEFINITION.START_REDEF_TABLE(\n    uname        => 'CUSTOMER_APP',\n    orig_table   => 'CUSTOMER',\n    int_table    => 'CUSTOMER_INT',\n    col_mapping  => NULL,\n    options_flag => DBMS_REDEFINITION.CONS_USE_PK);\nEND;\n/\n\n-- 4) Copy indexes, constraints, triggers, grants and statistics:\nSET SERVEROUTPUT ON\nDECLARE\n  l_errors PLS_INTEGER;\nBEGIN\n  DBMS_REDEFINITION.COPY_TABLE_DEPENDENTS(\n    uname            => 'CUSTOMER_APP',\n    orig_table       => 'CUSTOMER',\n    int_table        => 'CUSTOMER_INT',\n    copy_indexes     => DBMS_REDEFINITION.CONS_ORIG_PARAMS,\n    copy_triggers    => TRUE,\n    copy_constraints => TRUE,\n    copy_privileges  => TRUE,\n    ignore_errors    => FALSE,\n    num_errors       => l_errors,\n    copy_statistics  => TRUE);\n  DBMS_OUTPUT.PUT_LINE('Error count: ' || l_errors);\nEND;\n/\nSELECT object_name, base_table_name, ddl_txt FROM dba_redefinition_errors;\n\n-- 5) Close the gap before finishing (shortens finish; can be run several times):\nEXEC DBMS_REDEFINITION.SYNC_INTERIM_TABLE('CUSTOMER_APP', 'CUSTOMER', 'CUSTOMER_INT');\n\n-- 6) Swap under a short lock:\nEXEC DBMS_REDEFINITION.FINISH_REDEF_TABLE('CUSTOMER_APP', 'CUSTOMER', 'CUSTOMER_INT', dml_lock_timeout => 60);\n\n-- 7) Drop the interim table, which now holds the old structure:\nDROP TABLE customer_app.customer_int PURGE;\n\n-- On problems: EXEC DBMS_REDEFINITION.ABORT_REDEF_TABLE('CUSTOMER_APP', 'CUSTOMER', 'CUSTOMER_INT');\n-- Status: SELECT * FROM dba_redefinition_status WHERE base_table_owner = 'CUSTOMER_APP';","what":"Cambia la estructura de una tabla (particionamiento, tipos/orden de columnas, tablespace, compresión, configuración de LOB) mientras la aplicación sigue funcionando: los datos se copian a una tabla intermedia, los cambios se sincronizan mediante un MV log y al final se intercambian los nombres bajo un bloqueo muy breve.","when":"Para cambios que ALTER TABLE no puede hacer en línea, como particionar una tabla grande no particionada sin tiempo de inactividad, cambiar el tipo de una columna o convertir LOB BasicFile a SecureFiles.","caution":"Requiere Enterprise Edition (más la opción Partitioning si el destino está particionado). El proceso necesita el doble del espacio de la tabla y añade sobrecarga de redo y de MV log. FINISH toma un breve bloqueo exclusivo; las transacciones largas lo hacen esperar (dml_lock_timeout). Revise siempre los errores de COPY_TABLE_DEPENDENTS en DBA_REDEFINITION_ERRORS; las FK pueden copiarse como NOVALIDATE. Ensáyelo primero en un entorno de pruebas.","note":"El parámetro dml_lock_timeout se añadió en 12c. En 12.2+ monitoree el progreso en V$ONLINE_REDEF. Para un simple cambio de ubicación/compresión basta ALTER TABLE ... MOVE ONLINE en lugar de DBMS_REDEFINITION.","tags":["dbms_redefinition","online redefinition","redefinition","zero downtime","convert to partitioned","can_redef_table","finish_redef_table","enterprise edition"]},{"id":"table-move-online","task":"Mover una tabla o partición en línea (ALTER TABLE MOVE ONLINE)","category":"Operaciones en línea","command":"-- Move the table to another tablespace / reorganize it (DML continues):\nALTER TABLE customer_app.orders MOVE ONLINE TABLESPACE customer_data;\n\n-- Compress while moving (BASIC; for OLTP compression use ROW STORE COMPRESS ADVANCED):\nALTER TABLE customer_app.orders MOVE ONLINE COMPRESS;\n\n-- A single partition:\nALTER TABLE customer_app.sale_part\n  MOVE PARTITION p2026_h1 ONLINE TABLESPACE customer_archive UPDATE INDEXES;\n\n-- Verify: indexes must stay VALID\nSELECT index_name, status FROM dba_indexes\nWHERE  owner = 'CUSTOMER_APP' AND table_name = 'ORDERS';\nSELECT table_name, tablespace_name, compression, compress_for\nFROM   dba_tables\nWHERE  owner = 'CUSTOMER_APP' AND table_name = 'ORDERS';","what":"Reescribe el segmento de la tabla: reduce la fragmentación y el high water mark, cambia el tablespace y aplica compresión. Con ONLINE, el DML no se bloquea durante la operación y los índices se mantienen automáticamente y siguen VALID.","when":"Para recuperar espacio tras borrados masivos, mover una tabla a un nuevo tablespace/almacenamiento y comprimir particiones antiguas en un tablespace de archivo.","caution":"Requiere Enterprise Edition; ROW STORE COMPRESS ADVANCED requiere la opción Advanced Compression (costo adicional). La operación necesita un espacio adicional igual al tamaño de la tabla y genera mucho redo. Se toman bloqueos breves al inicio y al final; las transacciones largas abiertas la hacen esperar. Un MOVE sin ONLINE ni UPDATE INDEXES deja todos los índices UNUSABLE.","since":"Oracle 12.2","note":"MOVE ONLINE a nivel de tabla se introdujo en 12.2; MOVE PARTITION ONLINE a nivel de partición existe desde 12.1. En 12.1 y anteriores use DBMS_REDEFINITION para tablas.","tags":["move online","alter table move","move table","reorganization","high water mark","compression","enterprise edition"]},{"id":"tde-keystore-setup","task":"Crear, abrir, definir la master key y hacer backup de un keystore TDE (19c+)","category":"Seguridad","command":"-- 1) Keystore location (static parameter, restart required), then keystore type:\nALTER SYSTEM SET WALLET_ROOT = '/u01/app/oracle/admin/ORCL/wallet' SCOPE = SPFILE;\n-- SHUTDOWN IMMEDIATE;  STARTUP;\nALTER SYSTEM SET TDE_CONFIGURATION = 'KEYSTORE_CONFIGURATION=FILE' SCOPE = BOTH;\n\n-- 2) In CDB root: create a software keystore (under WALLET_ROOT/tde), open it, set the master key\nADMINISTER KEY MANAGEMENT CREATE KEYSTORE IDENTIFIED BY \"<keystore_pwd>\";\nADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN\n  IDENTIFIED BY \"<keystore_pwd>\" CONTAINER = ALL;\nADMINISTER KEY MANAGEMENT SET KEY USING TAG 'mk_2026_10'\n  IDENTIFIED BY \"<keystore_pwd>\" WITH BACKUP USING 'mk_before' CONTAINER = ALL;\n\n-- 3) Open automatically after restart (auto-login, cwallet.sso):\nADMINISTER KEY MANAGEMENT CREATE AUTO_LOGIN KEYSTORE\n  FROM KEYSTORE IDENTIFIED BY \"<keystore_pwd>\";\n\n-- 4) Backup (repeat after every SET KEY / rekey, store SEPARATELY from database backups):\nADMINISTER KEY MANAGEMENT BACKUP KEYSTORE USING 'daily'\n  FORCE KEYSTORE IDENTIFIED BY \"<keystore_pwd>\" TO '/backup/tde/';\n\n-- 5) Status:\nSELECT con_id, wrl_type, wrl_parameter, status, wallet_type, keystore_mode\nFROM   v$encryption_wallet;\nSELECT con_id, key_id, tag, creation_time, activation_time\nFROM   v$encryption_keys ORDER BY creation_time;","what":"Configura un software keystore para Transparent Data Encryption en modo united (todas las PDB comparten el keystore de la CDB), crea la master encryption key y habilita auto-login para que se abra tras un reinicio. En 19c+ el método recomendado es WALLET_ROOT/TDE_CONFIGURATION en lugar de ENCRYPTION_WALLET_LOCATION en sqlnet.ora.","when":"Como primer paso antes del cifrado de tablespaces/columnas en proyectos de cumplimiento que exigen cifrado de datos en reposo (GDPR, PCI-DSS, ISO 27001).","caution":"On-premises requiere la opción Advanced Security (costo adicional); está incluida en las licencias de Oracle Cloud (OCI/Autonomous). WALLET_ROOT es estático y necesita reiniciar la instancia (tiempo de inactividad). Si se pierden el keystore y su contraseña, los datos cifrados quedan ilegibles DE FORMA PERMANENTE; guarde los backups del keystore de forma segura y separada de los backups de la base de datos. Cualquiera que tenga el archivo de auto-login (cwallet.sso) puede abrir la base de datos; restrinja los permisos del archivo y use LOCAL AUTO_LOGIN cuando corresponda. Con Data Guard, copie también el keystore al standby.","since":"Oracle 18c","note":"WALLET_ROOT y TDE_CONFIGURATION se introdujeron en 18c y son estándar en 19c; el método ENCRYPTION_WALLET_LOCATION de sqlnet.ora de 12c-18c está obsoleto en 19c y sin soporte en 23ai. En 23ai el algoritmo por defecto es AES256; el cifrado con GOST y SEED ya no está soportado y el descifrado con ellos está obsoleto. WALLET_ROOT es un parámetro estático y requiere reiniciar la instancia.","tags":["tde","keystore","wallet","master key","encryption","advanced security","wallet_root","administer key management"],"untested":true},{"id":"tde-tablespace-encryption","task":"Cifrar tablespaces con TDE (nuevos y existentes, online/offline)","category":"Seguridad","command":"-- Prerequisite: keystore OPEN and master key set (v$encryption_wallet.status = 'OPEN')\n\n-- 1) New encrypted tablespace:\nCREATE TABLESPACE customer_enc\n  DATAFILE '+DATA' SIZE 1G AUTOEXTEND ON NEXT 256M MAXSIZE 20G\n  ENCRYPTION USING 'AES256' ENCRYPT;\n\n-- 2a) Encrypt an existing tablespace ONLINE (12.2+, needs extra space temporarily):\nALTER TABLESPACE customer_data ENCRYPTION ONLINE USING 'AES256' ENCRYPT;\n-- If not OMF: ... ENCRYPT FILE_NAME_CONVERT = ('customer_data01.dbf', 'customer_data01_enc.dbf');\n\n-- 2b) OFFLINE conversion (no extra space, tablespace unavailable meanwhile):\nALTER TABLESPACE customer_data OFFLINE NORMAL;\nALTER TABLESPACE customer_data ENCRYPTION OFFLINE ENCRYPT;\nALTER TABLESPACE customer_data ONLINE;\n\n-- 3) Progress and status:\nSELECT t.name AS tablespace_name, e.encryptionalg, e.status, e.con_id\nFROM   v$encrypted_tablespaces e\n  JOIN v$tablespace t ON t.ts# = e.ts# AND t.con_id = e.con_id;\nSELECT tablespace_name, encrypted FROM dba_tablespaces ORDER BY tablespace_name;","what":"El cifrado de tablespaces cifra cada bloque escrito en los datafiles; los datos están en claro en el buffer cache, por lo que no se necesitan cambios en la aplicación ni en el SQL. La conversión online construye una copia cifrada del datafile y cambia a ella mientras continúa el DML; la conversión offline cifra los bloques in situ.","when":"Para cifrar en reposo los tablespaces de aplicación que contienen datos sensibles, mover datos a un nuevo tablespace cifrado o cerrar un hallazgo de auditoría.","caution":"On-premises requiere la opción Advanced Security (costo adicional). La conversión online necesita un espacio adicional igual al de los datafiles que se cifran y genera mucha I/O y redo; un standby de Data Guard realiza el mismo trabajo. La conversión offline debe ejecutarse por separado en un standby de Data Guard. SYSTEM/SYSAUX/UNDO se pueden cifrar online en 12.2+ pero requieren cuidado adicional. Los backups de tablespaces cifrados no se pueden restaurar sin el keystore.","since":"Oracle 12.2","note":"La conversión online/offline de tablespaces se introdujo en 12.2. Para cifrar automáticamente los tablespaces nuevos, 19c usa TABLESPACE_ENCRYPTION=AUTO_ENABLE (RU 19.16+) o ENCRYPT_NEW_TABLESPACES=ALWAYS. Primero el keystore debe estar abierto y con una master key definida (vea tde-keystore-setup).","tags":["tde","tablespace encryption","encryption online","aes256","v$encrypted_tablespaces","advanced security","at-rest"],"untested":true},{"id":"unified-audit-policy-lifecycle","task":"Crear una política de Unified Auditing, habilitarla de forma condicional, revisar los registros y eliminarla","category":"Seguridad","command":"-- 1) Object + condition based policy (excluding the batch module):\nCREATE AUDIT POLICY pol_customer_dml\n  ACTIONS SELECT ON customer_app.customer,\n          UPDATE ON customer_app.customer,\n          DELETE ON customer_app.customer\n  WHEN 'SYS_CONTEXT(''USERENV'',''MODULE'') NOT LIKE ''batch%'''\n  EVALUATE PER SESSION;\n\n-- 2) Privileged operations policy:\nCREATE AUDIT POLICY pol_ddl_priv\n  PRIVILEGES DROP ANY TABLE, ALTER SYSTEM\n  ACTIONS DROP USER, ALTER USER, CREATE USER;\n\n-- 3) Enable:\nAUDIT POLICY pol_customer_dml EXCEPT customer_app;   -- everyone except the schema owner\nAUDIT POLICY pol_ddl_priv WHENEVER SUCCESSFUL;\nAUDIT POLICY ORA_LOGON_FAILURES WHENEVER NOT SUCCESSFUL;\n\n-- 4) Which policy is enabled for whom?\nSELECT policy_name, enabled_option, entity_name, success, failure\nFROM   audit_unified_enabled_policies ORDER BY policy_name;\nSELECT policy_name, audit_option, object_schema, object_name, audit_condition\nFROM   audit_unified_policies WHERE policy_name LIKE 'POL_%';\n\n-- 5) Records:\nSELECT event_timestamp, dbusername, action_name, object_schema, object_name,\n       return_code, unified_audit_policies, os_username, userhost\nFROM   unified_audit_trail\nWHERE  event_timestamp > SYSTIMESTAMP - INTERVAL '1' DAY\n  AND  unified_audit_policies LIKE '%POL_%'\nORDER  BY event_timestamp DESC;\n-- 19c (queued-write): EXEC DBMS_AUDIT_MGMT.FLUSH_UNIFIED_AUDIT_TRAIL;  (not in 23ai)\n\n-- 6) Disable and remove:\nNOAUDIT POLICY pol_customer_dml;\nNOAUDIT POLICY pol_ddl_priv;\nDROP AUDIT POLICY pol_customer_dml;\nDROP AUDIT POLICY pol_ddl_priv;","what":"Unified Auditing reúne todos los registros de auditoría en un único trail (esquema AUDSYS, UNIFIED_AUDIT_TRAIL). Las políticas se definen por acción sobre objetos, privilegio de sistema o acción; la condición WHEN y las listas de usuarios BY/EXCEPT reducen el ruido.","when":"Para registrar el acceso a tablas sensibles, monitorear DDL privilegiado y cambios de privilegios, ver intentos de inicio de sesión fallidos y cumplir los requisitos de auditoría.","caution":"Sin licencia adicional. Auditar SELECT en tablas con mucho uso genera muchos registros y crecimiento de SYSAUX; acótelo con condiciones y EXCEPT, y programe el archivado/limpieza con DBMS_AUDIT_MGMT (CLEAN_AUDIT_TRAIL, SET_LAST_ARCHIVE_TIMESTAMP). Una política habilitada no se puede eliminar (ORA-46361); ejecute NOAUDIT primero. Los cambios de política surten efecto en las sesiones nuevas. 19c funciona en modo mixto por defecto, por lo que también pueden escribirse registros del AUDIT_TRAIL tradicional.","since":"Oracle 12c","note":"En 23ai la auditoría tradicional ya no está soportada; solo existe unified auditing, y FLUSH_UNIFIED_AUDIT_TRAIL se eliminó porque los registros se escriben directamente en la tabla. En 19c, pasar al modo unified puro requiere volver a enlazar el binario oracle con uniaud_on.","tags":["unified auditing","audit policy","auditing","unified_audit_trail","create audit policy","noaudit","ora_logon_failures","security"]},{"id":"vpd-row-level-policy","task":"Añadir una política de acceso a nivel de fila con VPD (DBMS_RLS)","category":"Seguridad","command":"-- 1) Policy function returning the predicate:\nCREATE OR REPLACE FUNCTION customer_app.customer_vpd_fn (\n  p_schema IN VARCHAR2,\n  p_object IN VARCHAR2\n) RETURN VARCHAR2\nAS\nBEGIN\n  -- The schema owner sees all rows (NULL = no restriction)\n  IF SYS_CONTEXT('USERENV', 'SESSION_USER') = 'CUSTOMER_APP' THEN\n    RETURN NULL;\n  END IF;\n  -- Other users see only their own city\n  RETURN 'city = SYS_CONTEXT(''USERENV'', ''CLIENT_IDENTIFIER'')';\nEND;\n/\n\n-- 2) Attach the policy to the table:\nBEGIN\n  DBMS_RLS.ADD_POLICY(\n    object_schema   => 'CUSTOMER_APP',\n    object_name     => 'CUSTOMER',\n    policy_name     => 'CUSTOMER_CITY_VPD',\n    function_schema => 'CUSTOMER_APP',\n    policy_function => 'CUSTOMER_VPD_FN',\n    statement_types => 'SELECT, UPDATE, DELETE',\n    update_check    => TRUE,\n    policy_type     => DBMS_RLS.CONTEXT_SENSITIVE);\nEND;\n/\n\n-- 3) Check:\nSELECT object_owner, object_name, policy_name, function,\n       sel, upd, del, chk_option, enable, policy_type\nFROM   dba_policies WHERE object_owner = 'CUSTOMER_APP';\n-- Test (as the application user): EXEC DBMS_SESSION.SET_IDENTIFIER('Ankara');\n\n-- 4) Temporarily disable / drop:\nEXEC DBMS_RLS.ENABLE_POLICY('CUSTOMER_APP', 'CUSTOMER', 'CUSTOMER_CITY_VPD', FALSE);\nEXEC DBMS_RLS.DROP_POLICY('CUSTOMER_APP', 'CUSTOMER', 'CUSTOMER_CITY_VPD');","what":"Virtual Private Database añade automáticamente el predicado WHERE devuelto por la función de política a cada SQL sobre la tabla, de modo que los usuarios de la misma tabla solo ven/modifican las filas a las que tienen derecho. update_check=>TRUE impide que un usuario actualice una fila a un valor que no podría ver.","when":"En aplicaciones multiinquilino, para el aislamiento de datos por región/sucursal y para añadir seguridad de filas sin tocar el código de la aplicación.","caution":"Incluido en Enterprise Edition, sin licencia adicional. El cliente puede definir libremente CLIENT_IDENTIFIER; en producción use un application context definido por un paquete de confianza (CREATE CONTEXT ... USING). SYS y los usuarios con EXEMPT ACCESS POLICY omiten la política; las exportaciones de Data Pump sin ese privilegio exportan datos parciales sin avisar. Un error en la función bloquea todo el acceso con ORA-28112. Un policy_type erróneo (p. ej., STATIC) guarda en cache el predicado y devuelve resultados incorrectos.","tags":["vpd","virtual private database","dbms_rls","row security","row level security","add_policy","policy function","security"]},{"id":"password-profile-hardening","task":"Crear un profile de política de contraseñas y asignarlo a usuarios","category":"Seguridad","command":"-- Hardened profile for application/human users:\nCREATE PROFILE app_user_prf LIMIT\n  FAILED_LOGIN_ATTEMPTS    5\n  PASSWORD_LOCK_TIME       1/24          -- 1 hour lock\n  PASSWORD_LIFE_TIME       90\n  PASSWORD_GRACE_TIME      7\n  PASSWORD_REUSE_TIME      365\n  PASSWORD_REUSE_MAX       10\n  INACTIVE_ACCOUNT_TIME    60            -- accounts unused for 60 days are locked\n  PASSWORD_VERIFY_FUNCTION ora12c_strong_verify_function;\n\n-- Service accounts: password never expires, gradual password rollover enabled:\nCREATE PROFILE service_account_prf LIMIT\n  FAILED_LOGIN_ATTEMPTS    UNLIMITED\n  PASSWORD_LIFE_TIME       UNLIMITED\n  PASSWORD_ROLLOVER_TIME   1;            -- old and new password both valid for 1 day\n\nALTER USER customer_app PROFILE app_user_prf;\n\n-- Check:\nSELECT username, profile, account_status,\n       TO_CHAR(expiry_date,'YYYY-MM-DD') AS expiry_date, lock_date\nFROM   dba_users\nWHERE  profile IN ('APP_USER_PRF', 'SERVICE_ACCOUNT_PRF');\n\nSELECT profile, resource_name, limit\nFROM   dba_profiles\nWHERE  profile = 'APP_USER_PRF' AND resource_type = 'PASSWORD'\nORDER  BY resource_name;","what":"Un profile aplica a un grupo de usuarios reglas de bloqueo por inicios de sesión fallidos, vigencia de la contraseña, reutilización, complejidad (verify function) y bloqueo de cuentas inactivas. PASSWORD_ROLLOVER_TIME permite que las aplicaciones cambien sin caída mientras se cambia la contraseña de una cuenta de servicio.","when":"Para requisitos de auditoría de seguridad/CIS benchmark, al crear usuarios de aplicación nuevos y para separar por grupo de usuarios los valores laxos del profile DEFAULT.","caution":"Sin licencia adicional. Un límite FAILED_LOGIN_ATTEMPTS en una cuenta de servicio permite que un solo servidor de aplicaciones con una contraseña errónea bloquee la cuenta y cause una caída en producción. PASSWORD_LIFE_TIME en cuentas de aplicación puede causar un estado EXPIRED inesperado. La verify function debe existir en SYS (si no, @?/rdbms/admin/utlpwdmg.sql; ese script también modifica el profile DEFAULT). Cambiar el profile no vuelve a validar las contraseñas existentes.","note":"INACTIVE_ACCOUNT_TIME se añadió en 12.2; PASSWORD_ROLLOVER_TIME llegó en 21c y se portó a 19c con la RU 19.12. Las sesiones que inician sesión con la contraseña antigua durante el periodo de rollover se pueden rastrear mediante los registros de auditoría.","tags":["profile","password policy","password_verify_function","failed_login_attempts","inactive_account_time","password_rollover_time","account lock","security"]},{"id":"privilege-analysis-capture","task":"Encontrar privilegios no usados con privilege analysis","category":"Seguridad","command":"-- 1) Capture privilege usage for a specific user:\nBEGIN\n  DBMS_PRIVILEGE_CAPTURE.CREATE_CAPTURE(\n    name        => 'CAP_APP_USER',\n    description => 'Actual privilege usage of the application user',\n    type        => DBMS_PRIVILEGE_CAPTURE.G_CONTEXT,\n    condition   => 'SYS_CONTEXT(''USERENV'',''SESSION_USER'') = ''CUSTOMER_APP''');\n  DBMS_PRIVILEGE_CAPTURE.ENABLE_CAPTURE(name => 'CAP_APP_USER', run_name => 'RUN_2026_10');\nEND;\n/\n-- Other types: G_DATABASE (whole DB), G_ROLE (roles => role_name_list('DBA')), G_ROLE_AND_CONTEXT\n\n-- 2) After a representative workload (including month end), stop and generate results:\nBEGIN\n  DBMS_PRIVILEGE_CAPTURE.DISABLE_CAPTURE(name => 'CAP_APP_USER');\n  DBMS_PRIVILEGE_CAPTURE.GENERATE_RESULT(name => 'CAP_APP_USER', run_name => 'RUN_2026_10');\nEND;\n/\n\n-- 3) Used and never-used privileges:\nSELECT username, sys_priv, obj_priv, object_owner, object_name, used_role\nFROM   dba_used_privs   WHERE capture = 'CAP_APP_USER';\nSELECT username, sys_priv, obj_priv, object_owner, object_name\nFROM   dba_unused_privs WHERE capture = 'CAP_APP_USER';\n\n-- 4) Through which role chain was the privilege used?\nSELECT username, sys_priv, used_role, path\nFROM   dba_used_sysprivs_path WHERE capture = 'CAP_APP_USER';\n\n-- 5) Cleanup:\nEXEC DBMS_PRIVILEGE_CAPTURE.DROP_CAPTURE(name => 'CAP_APP_USER');","what":"Privilege analysis registra en tiempo de ejecución qué privilegios de sistema/objeto se usan realmente dentro del alcance elegido (toda la base de datos, rol, condición); el resultado lista los privilegios no usados para planificar los REVOKE según el principio de mínimo privilegio.","when":"Al limpiar cuentas de aplicación con privilegios DBA/ANY, para revisiones de privilegios antes de una auditoría y para averiguar qué necesita realmente una aplicación empaquetada.","caution":"Incluido en Enterprise Edition desde 2018 (antes requería licencia de Database Vault); sin licencia adicional. Hay una pequeña sobrecarga durante la captura. El resultado refleja solo la carga de trabajo de la ventana de captura: si no se ejecutaron los jobs de fin de mes, fin de año o de emergencia, un privilegio mostrado como \"no usado\" puede seguir siendo necesario, así que revoque gradualmente. Solo se puede habilitar una captura a la vez; la única excepción es que una captura G_DATABASE puede habilitarse junto con una captura de rol o de contexto.","since":"Oracle 12c","tags":["privilege analysis","dbms_privilege_capture","unused privileges","least privilege","dba_unused_privs","privilege cleanup","security"]},{"id":"opatch-inventory-prereq","task":"Inspeccionar el inventario con OPatch y ejecutar comprobaciones previas al parche","category":"Parches y ciclo de vida","command":"# OPatch version (must meet the minimum in the patch README; update with patch 6880880 if needed)\n$ORACLE_HOME/OPatch/opatch version\n\n# Installed patches: short list / detailed inventory\n$ORACLE_HOME/OPatch/opatch lspatches\n$ORACLE_HOME/OPatch/opatch lsinventory -detail -oh $ORACLE_HOME\n\n# Pre-apply checks (in the unzipped patch directory):\ncd /u01/stage/<patch_id>\n$ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -ph ./\n$ORACLE_HOME/OPatch/opatch prereq CheckSystemSpace -ph ./\n\n# For Grid Infrastructure / RAC homes (as root), analyze mode:\n# opatchauto apply /u01/stage/<patch_id> -analyze","what":"Lista los parches binarios (RU, one-offs) aplicados al Oracle Home y comprueba, antes de aplicar un parche nuevo, si entra en conflicto con los parches existentes y si hay suficiente espacio en disco.","when":"Antes de una RU trimestral/parche de seguridad, cuando soporte pide la salida del inventario en un SR, y al comparar el nivel de parches de dos entornos.","caution":"Ejecute los comandos como el usuario del SO propietario del Oracle Home (oracle); opatchauto requiere root. Las comprobaciones prereq son de solo lectura y no afectan a la base de datos, pero si se informa de un conflicto, no aplique el parche antes de obtener un parche merge/overlay de Oracle Support. Haga un backup del Oracle Home (tar) y del inventario antes de aplicar.","note":"Para ver el inventario desde SQL se puede usar DBMS_QOPATCH.GET_OPATCH_LSINVENTORY (en instalaciones con inventario de OPatch).","tags":["opatch","lsinventory","lspatches","prereq","patch","patch conflict","opatchauto","release update"],"untested":true},{"id":"datapatch-apply-verify","task":"Actualizar la capa SQL con datapatch -verbose tras un parche binario","category":"Parches y ciclo de vida","command":"# 1) After the binary patch (opatch apply), start the DB; all PDBs must be open:\nsqlplus / as sysdba <<EOF\nSTARTUP;\nALTER PLUGGABLE DATABASE ALL OPEN;\nEXIT;\nEOF\n\n# 2) Check only first, then apply:\ncd $ORACLE_HOME/OPatch\n./datapatch -prereq\n./datapatch -verbose\n# For specific PDBs: ./datapatch -verbose -pdbs PDB1,PDB2\n\n# 3) Recompile invalid objects:\nsqlplus / as sysdba @?/rdbms/admin/utlrp.sql\n\n# Logs: $ORACLE_BASE/cfgtoollogs/sqlpatch/","what":"datapatch aplica los componentes SQL/PL-SQL (diccionario, paquetes) de los parches binarios del Oracle Home a CDB root, PDB$SEED y todas las PDB abiertas, y registra el resultado en DBA_REGISTRY_SQLPATCH. También revierte la parte SQL de los parches desinstalados.","when":"Tras cada parche binario RU/RUR/one-off o su rollback; para PDB movidas o desconectadas/conectadas en un home recién parcheado; y para volver a ejecutarlo cuando DBA_REGISTRY_SQLPATCH muestra WITH ERRORS.","caution":"Las PDB cerradas o en MOUNTED no se parchean y pueden abrirse en modo RESTRICTED; ábralas todas primero. No lo ejecute con mucha carga (bloqueos del diccionario, invalidación de paquetes). En RAC ejecútelo una sola vez desde un único nodo. Con Data Guard ejecute datapatch solo en el primario; los cambios llegan al standby mediante redo (parchee primero los binarios del standby).","note":"El datapatch incluido en las RU recientes de 19c ofrece -sanity_checks para verificar la salud del diccionario y del entorno (objetos inválidos, violaciones de PDB, estado de tablespaces/TEMP) antes de parchear.","tags":["datapatch","sql patch","release update","dba_registry_sqlpatch","post-patch","utlrp","pdb","patching"],"untested":true},{"id":"sqlpatch-registry-check","task":"Verificar el historial de parches SQL, el estado de los componentes y los objetos inválidos","category":"Parches y ciclo de vida","command":"-- 1) datapatch history (most recent at the bottom):\nSELECT patch_id, patch_type, action, status, source_version, target_version,\n       TO_CHAR(action_time,'YYYY-MM-DD HH24:MI') AS action_time, description\nFROM   dba_registry_sqlpatch\nORDER  BY action_time;\n\n-- Failed entries and their log files:\nSELECT install_id, patch_id, action, status, logfile\nFROM   dba_registry_sqlpatch\nWHERE  status <> 'SUCCESS';\n\n-- In a CDB, for all PDBs (from root):\nSELECT con_id, patch_id, action, status\nFROM   cdb_registry_sqlpatch ORDER BY con_id, action_time;\n\n-- 2) Components must be VALID:\nSELECT comp_id, comp_name, version_full, status\nFROM   dba_registry ORDER BY comp_id;\n\n-- 3) Invalid object summary (compare before/after utlrp):\nSELECT owner, object_type, COUNT(*) AS invalid_cnt\nFROM   dba_objects\nWHERE  status = 'INVALID'\nGROUP  BY owner, object_type\nORDER  BY invalid_cnt DESC;","what":"Muestra de un vistazo si la capa SQL se actualizó realmente tras el parche (diccionario acorde con los binarios), qué parche falló en qué PDB, si los componentes del diccionario están VALID y el número de objetos inválidos.","when":"Como paso de verificación tras cada datapatch, para capturar una referencia antes de parchear o hacer un upgrade, y cuando se sospecha de una discrepancia tras conectar una PDB.","caution":"Solo lectura; no requiere licencia. SOURCE_VERSION/TARGET_VERSION se rellenan en el modelo de RU de 18c+. Si STATUS='WITH ERRORS', corrija el error indicado en LOGFILE y vuelva a ejecutar datapatch. Algunos componentes (p. ej., XDB/JAVA no usados) pueden aparecer como INVALID/REMOVED; compare con el estado previo al parche.","note":"Para ver el inventario binario desde SQL, use SELECT XMLTRANSFORM(DBMS_QOPATCH.GET_OPATCH_LIST, DBMS_QOPATCH.GET_OPATCH_XSLT) FROM dual; lanza ORA-20013 en instalaciones sin inventario de OPatch.","tags":["dba_registry_sqlpatch","patch history","datapatch","dba_registry","invalid objects","invalid object","patch verification"]},{"id":"scheduler-job-lifecycle","task":"Crear, ejecutar manualmente, deshabilitar y eliminar un job del scheduler","category":"Scheduler","command":"-- 1) Create disabled, set attributes, then enable:\nBEGIN\n  DBMS_SCHEDULER.CREATE_JOB(\n    job_name        => 'CUSTOMER_APP.NIGHTLY_STATS',\n    job_type        => 'PLSQL_BLOCK',\n    job_action      => q'[BEGIN DBMS_STATS.GATHER_SCHEMA_STATS(ownname => 'CUSTOMER_APP', options => 'GATHER STALE'); END;]',\n    start_date      => SYSTIMESTAMP,\n    repeat_interval => 'FREQ=DAILY; BYHOUR=2; BYMINUTE=30; BYSECOND=0',\n    enabled         => FALSE,\n    auto_drop       => FALSE,\n    comments        => 'Nightly stale statistics gathering');\n  DBMS_SCHEDULER.SET_ATTRIBUTE('CUSTOMER_APP.NIGHTLY_STATS', 'max_run_duration', INTERVAL '2' HOUR);\n  DBMS_SCHEDULER.SET_ATTRIBUTE('CUSTOMER_APP.NIGHTLY_STATS', 'logging_level', DBMS_SCHEDULER.LOGGING_FULL);\n  DBMS_SCHEDULER.ENABLE('CUSTOMER_APP.NIGHTLY_STATS');\nEND;\n/\n\n-- 2) Validate the calendar expression (next run time):\nSET SERVEROUTPUT ON\nDECLARE\n  l_next TIMESTAMP WITH TIME ZONE;\nBEGIN\n  DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING(\n    calendar_string   => 'FREQ=DAILY; BYHOUR=2; BYMINUTE=30; BYSECOND=0',\n    start_date        => SYSTIMESTAMP,\n    return_date_after => SYSTIMESTAMP,\n    next_run_date     => l_next);\n  DBMS_OUTPUT.PUT_LINE('Next run: ' || l_next);\nEND;\n/\n\n-- 3) Run now in the background (does not affect the schedule):\nEXEC DBMS_SCHEDULER.RUN_JOB('CUSTOMER_APP.NIGHTLY_STATS', use_current_session => FALSE);\n\n-- 4) Status:\nSELECT owner, job_name, enabled, state, run_count, failure_count,\n       last_start_date, next_run_date\nFROM   dba_scheduler_jobs WHERE owner = 'CUSTOMER_APP';\n\n-- 5) Stop (if running) / disable / drop:\n-- EXEC DBMS_SCHEDULER.STOP_JOB('CUSTOMER_APP.NIGHTLY_STATS', force => FALSE);\nEXEC DBMS_SCHEDULER.DISABLE('CUSTOMER_APP.NIGHTLY_STATS', force => TRUE);\nEXEC DBMS_SCHEDULER.DROP_JOB('CUSTOMER_APP.NIGHTLY_STATS', force => TRUE);","what":"Define un job PL/SQL programado con DBMS_SCHEDULER; establece max_run_duration (genera un evento al superarse) y un nivel de logging detallado, valida de antemano la expresión de calendario y permite una ejecución manual sin alterar la programación.","when":"Para ejecutar dentro de la base de datos, en lugar de cron, tareas recurrentes como mantenimiento, ETL y limpieza, teniendo en cuenta RAC/Data Guard.","caution":"Sin licencia adicional. Importa la zona horaria de start_date: SYSTIMESTAMP usa la TZ del servidor de base de datos; use una región explícita (p. ej., 'Europe/Istanbul') para evitar desfases en los cambios de horario de verano. Si JOB_QUEUE_PROCESSES=0 no se ejecuta ningún job. DISABLE con force=>TRUE no detiene una ejecución en curso, solo impide las futuras; use STOP_JOB para interrumpirla. Crear un job en otro esquema requiere CREATE ANY JOB.","tags":["dbms_scheduler","job","scheduled job","create_job","run_job","repeat_interval","disable job","scheduler"]},{"id":"scheduler-job-history","task":"Monitorear el historial de ejecuciones y los fallos de los jobs del scheduler","category":"Scheduler","command":"-- 1) Recent runs: duration, CPU, error code and message\nSELECT owner, job_name, status, error#,\n       TO_CHAR(actual_start_date,'YYYY-MM-DD HH24:MI:SS') AS started,\n       run_duration, cpu_used,\n       SUBSTR(additional_info, 1, 200) AS info\nFROM   dba_scheduler_job_run_details\nWHERE  owner = 'CUSTOMER_APP'\nORDER  BY actual_start_date DESC\nFETCH FIRST 20 ROWS ONLY;\n\n-- 2) Failures in the last 24 hours (whole DB):\nSELECT owner, job_name, error#, actual_start_date, SUBSTR(errors, 1, 300) AS errors\nFROM   dba_scheduler_job_run_details\nWHERE  status <> 'SUCCEEDED'\n  AND  log_date > SYSTIMESTAMP - INTERVAL '1' DAY\nORDER  BY log_date DESC;\n\n-- 3) Currently running:\nSELECT owner, job_name, session_id, running_instance, elapsed_time, cpu_used\nFROM   dba_scheduler_running_jobs;\n\n-- 4) Lifecycle events (ENABLE/DISABLE/RUN/STOP...):\nSELECT log_date, owner, job_name, operation, status\nFROM   dba_scheduler_job_log\nWHERE  owner = 'CUSTOMER_APP'\nORDER  BY log_date DESC\nFETCH FIRST 20 ROWS ONLY;\n\n-- Log retention (days): EXEC DBMS_SCHEDULER.SET_SCHEDULER_ATTRIBUTE('log_history', '30');","what":"Lista, a partir de las vistas de log del scheduler, la hora de inicio, la duración, el uso de CPU, el estado y el mensaje de error de cada ejecución, además de los jobs en ejecución y los eventos del ciclo de vida de los jobs.","when":"Cuando un job nocturno no se ejecutó o tardó demasiado, para el seguimiento del SLA de los jobs y al definir reglas de fallo en un sistema de monitoreo (OEM/Zabbix/Prometheus).","caution":"Solo lectura; no requiere licencia. La retención de log por defecto es de 30 días, y los jobs con logging_level OFF no generan registros. La columna ERRORS existe en 12c+; en versiones anteriores revise ADDITIONAL_INFO. En RAC, RUNNING_INSTANCE muestra qué nodo ejecutó el job.","tags":["scheduler","job history","dba_scheduler_job_run_details","failed job","dba_scheduler_running_jobs","job log","monitoring"]},{"id":"resource-plan-basic","task":"Crear y activar un plan sencillo de Resource Manager","category":"Scheduler","command":"-- 1) Define groups, plan, directives and mapping inside a pending area (PDB or non-CDB):\nBEGIN\n  DBMS_RESOURCE_MANAGER.CLEAR_PENDING_AREA;\n  DBMS_RESOURCE_MANAGER.CREATE_PENDING_AREA;\n\n  DBMS_RESOURCE_MANAGER.CREATE_CONSUMER_GROUP('OLTP_GRP',  'Online transactions');\n  DBMS_RESOURCE_MANAGER.CREATE_CONSUMER_GROUP('REPORT_GRP', 'Reporting / batch');\n  DBMS_RESOURCE_MANAGER.CREATE_PLAN('DAYTIME_PLAN', 'Business hours plan');\n\n  DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE(\n    plan => 'DAYTIME_PLAN', group_or_subplan => 'OLTP_GRP',\n    comment => 'OLTP first', mgmt_p1 => 70);\n  DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE(\n    plan => 'DAYTIME_PLAN', group_or_subplan => 'REPORT_GRP',\n    comment => 'Reports limited', mgmt_p1 => 20,\n    parallel_degree_limit_p1 => 4,\n    switch_group => 'CANCEL_SQL', switch_time => 1800, switch_for_call => TRUE);\n  DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE(\n    plan => 'DAYTIME_PLAN', group_or_subplan => 'OTHER_GROUPS',\n    comment => 'Everyone else (mandatory)', mgmt_p1 => 10);\n\n  DBMS_RESOURCE_MANAGER.SET_CONSUMER_GROUP_MAPPING(\n    attribute      => DBMS_RESOURCE_MANAGER.ORACLE_USER,\n    value          => 'REPORT_USER',\n    consumer_group => 'REPORT_GRP');\n\n  DBMS_RESOURCE_MANAGER.VALIDATE_PENDING_AREA;\n  DBMS_RESOURCE_MANAGER.SUBMIT_PENDING_AREA;\nEND;\n/\n-- Switch privilege required for the mapping to take effect:\nEXEC DBMS_RESOURCE_MANAGER_PRIVS.GRANT_SWITCH_CONSUMER_GROUP('REPORT_USER', 'REPORT_GRP', FALSE);\n\n-- 2) Activate (FORCE: prevents maintenance windows from switching the plan):\nALTER SYSTEM SET resource_manager_plan = 'FORCE:DAYTIME_PLAN' SCOPE = BOTH;\n\n-- 3) Monitor:\nSELECT sid, username, resource_consumer_group FROM v$session WHERE username IS NOT NULL;\nSELECT name, active_sessions, execution_waiters, consumed_cpu_time, cpu_wait_time\nFROM   v$rsrc_consumer_group;\n\n-- 4) Roll back: deactivate the plan, then delete it\n-- ALTER SYSTEM SET resource_manager_plan = '' SCOPE = BOTH;\n-- BEGIN DBMS_RESOURCE_MANAGER.CREATE_PENDING_AREA;\n--       DBMS_RESOURCE_MANAGER.DELETE_PLAN_CASCADE('DAYTIME_PLAN');\n--       DBMS_RESOURCE_MANAGER.SUBMIT_PENDING_AREA; END;\n-- /","what":"Decide qué carga de trabajo gana cuando hay contención de CPU: da el 70% de la CPU al grupo OLTP y el 20% al grupo de informes, limita el paralelismo a 4 en el grupo de informes y cancela las llamadas que superan 30 minutos (CANCEL_SQL). Los usuarios se asignan automáticamente a consumer groups.","when":"Para cargas mixtas en las que los informes diurnos ralentizan el OLTP, limitar consultas paralelas sin control y cancelar automáticamente consultas desbocadas.","caution":"Resource Manager requiere Enterprise Edition (muy limitado en SE2); sin licencia adicional. Los porcentajes de mgmt_p1 solo se aplican cuando hay contención de CPU y no pueden superar el 100% en total en un mismo nivel. CANCEL_SQL mata la sentencia en curso con ORA-00040; una asignación errónea puede matar trabajo crítico, así que pruebe los umbrales con cuidado. Mientras un plan está activo, la limitación de CPU aparece como el wait event 'resmgr:cpu quantum'. Para repartir entre PDB a nivel de CDB use CREATE_CDB_PLAN.","tags":["resource manager","dbms_resource_manager","resource plan","consumer group","cpu priority","cancel_sql","resmgr:cpu quantum","resource management"]}]},"recipes/postgres.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"recipes":[{"id":"wal-switch","task":"Cambiar de segmento WAL","category":"WAL y checkpoint","command":"SELECT pg_switch_wal();","what":"Cierra el segmento WAL actual y cambia a uno nuevo; dispara el archivado.","when":"Para generar un archivo de archive o forzar la salida del WAL tras un backup.","caution":"Antes de PostgreSQL 10 era pg_switch_xlog().","since":"PostgreSQL 10","note":"Esta función se renombró a pg_switch_wal() en PostgreSQL 10; antes era pg_switch_xlog() (el WAL se llamaba 'xlog').","tags":["wal","switch","checkpoint","archive"]},{"id":"manual-checkpoint","task":"Ejecutar un CHECKPOINT manual","category":"WAL y checkpoint","command":"CHECKPOINT;","what":"Escribe en disco los buffers sucios y crea un registro de checkpoint; adelanta el punto de inicio de la recuperación.","when":"Antes de un backup, en una ventana de mantenimiento o para sincronizar con el disco antes de detener el servidor.","caution":"Puede generar mucha I/O; no lo ejecute en producción en horas pico. Solo puede ejecutarlo un superusuario o (PG15+) el rol pg_checkpoint.","tags":["checkpoint","wal","io","flush"]},{"id":"wal-status-size","task":"Mostrar el tamaño del WAL y el LSN actual","category":"WAL y checkpoint","command":"SELECT pg_current_wal_lsn() AS current_lsn, pg_walfile_name(pg_current_wal_lsn()) AS current_wal_file, pg_size_pretty(sum(size)) AS wal_dir_size FROM pg_ls_waldir();","what":"Devuelve el LSN de WAL actual, el nombre del archivo WAL que se está escribiendo y el tamaño total del directorio pg_wal.","when":"Para monitorear la acumulación de WAL, el uso de disco y el espaciado de los checkpoints.","caution":"pg_ls_waldir y pg_walfile_name están disponibles en PostgreSQL 10+; el acceso puede requerir el rol pg_monitor.","tags":["wal","lsn","size","monitor"]},{"id":"archive-lag","task":"Mostrar el retraso del archivado (WAL aún no archivado)","category":"WAL y checkpoint","command":"SELECT last_archived_wal, last_failed_wal, archived_count, failed_count, pg_walfile_name(pg_current_wal_lsn()) AS current_wal FROM pg_stat_archiver;","what":"Muestra el retraso del archivado comparando el último archivo WAL archivado, el WAL que se está escribiendo y los contadores de fallos de archivado.","when":"Para monitorear si archive_command se está quedando atrás y detectar fallos de archivado.","caution":"Si failed_count sigue aumentando, archive_command está fallando y pg_wal puede llenarse; actúe de inmediato para evitar que se desborde el disco.","tags":["archive","wal","lag","monitor"]},{"id":"create-tablespace","task":"Añadir un nuevo tablespace","category":"Almacenamiento","command":"CREATE TABLESPACE ssd_ts OWNER postgres LOCATION '/mnt/ssd/pgdata';","what":"Define un tablespace nuevo en el directorio indicado; se pueden colocar tablas e índices en ese disco.","when":"Para repartir datos en otro disco (p. ej., un SSD rápido) y separar la I/O.","caution":"El directorio debe existir previamente, estar vacío y pertenecer al usuario postgres. No reparta datos en otros discos con enlaces simbólicos; use el mecanismo de tablespaces propio de PostgreSQL.","tags":["tablespace","storage","disk","create"]},{"id":"move-table-tablespace","task":"Mover una tabla a otro tablespace","category":"Almacenamiento","command":"ALTER TABLE customer SET TABLESPACE ssd_ts;","what":"Mueve físicamente todos los datos de la tabla al nuevo tablespace (disco).","when":"Para mover una tabla a un disco más rápido/independiente.","caution":"Durante el movimiento se toma un bloqueo ACCESS EXCLUSIVE sobre la tabla; la tabla es inaccesible hasta que termina. Hágalo en una ventana de mantenimiento para tablas grandes.","tags":["tablespace","table","move","storage"]},{"id":"move-index-tablespace","task":"Mover un índice a otro tablespace","category":"Almacenamiento","command":"ALTER INDEX customer_email_idx SET TABLESPACE ssd_ts;","what":"Mueve el índice al tablespace indicado.","when":"Para colocar los índices en un disco distinto al de los datos y repartir la I/O.","caution":"Durante el movimiento se toma un bloqueo ACCESS EXCLUSIVE; el índice no se puede usar. ALTER TABLE ... SET TABLESPACE no abarca los índices de la tabla, por lo que no puede moverlos todos a la vez; mueva cada índice por separado.","tags":["tablespace","index","move","storage"]},{"id":"db-table-size","task":"Consultar el tamaño de la base de datos y de las tablas","category":"Almacenamiento","command":"SELECT relname AS table_name, pg_size_pretty(pg_total_relation_size(relid)) AS total_size, pg_size_pretty(pg_relation_size(relid)) AS data_only FROM pg_catalog.pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 20;","what":"Lista las 20 tablas más grandes con su tamaño total, incluidos índices y TOAST.","when":"Para analizar el uso de disco y averiguar qué tablas ocupan espacio.","caution":"Para el tamaño de toda la base de datos: SELECT pg_size_pretty(pg_database_size(current_database()));","tags":["size","table","database","storage"]},{"id":"table-bloat","task":"Estimar el bloat de una tabla","category":"Almacenamiento","command":"SELECT schemaname, relname AS table_name, n_dead_tup AS dead_rows, n_live_tup AS live_rows, round(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 1) AS dead_pct, last_autovacuum FROM pg_stat_user_tables WHERE n_dead_tup > 0 ORDER BY n_dead_tup DESC LIMIT 20;","what":"Muestra la proporción de bloat según el número de dead tuples y la hora del último autovacuum.","when":"Para determinar la necesidad de VACUUM y si autovacuum va al día.","caution":"n_dead_tup es una estimación que se actualiza continuamente a partir de los contadores de DML (baja tras VACUUM; ANALYZE solo corrige la estimación); para el bloat físico exacto la extensión pgstattuple (pgstattuple('customer')) es más precisa.","tags":["bloat","dead-tuple","vacuum","storage"]},{"id":"vacuum-analyze-table","task":"VACUUM ANALYZE de una tabla concreta","category":"Mantenimiento","command":"VACUUM (ANALYZE, VERBOSE) customer;","what":"Limpia los dead tuples de la tabla, hace reutilizable el espacio y actualiza las estadísticas del planificador.","when":"Tras DELETE/UPDATE masivos, o cuando los planes de consulta empeoran.","caution":"VACUUM no devuelve el espacio en disco al SO; recuperar espacio requiere VACUUM FULL, que toma un bloqueo ACCESS EXCLUSIVE. El VACUUM normal no bloquea la tabla.","tags":["vacuum","analyze","maintenance","cleanup"]},{"id":"vacuum-freeze","task":"Ejecutar un VACUUM FREEZE agresivo","category":"Mantenimiento","command":"VACUUM (FREEZE, VERBOSE, ANALYZE) customer;","what":"Marca de inmediato como 'frozen' todas las tuplas elegibles de la tabla; reduce el riesgo de transaction ID wraparound.","when":"Cuando aparece una advertencia de wraparound, o de forma preventiva en tablas grandes que se van a archivar/pasar a solo lectura.","caution":"Genera mucha I/O porque recorre toda la tabla. En una emergencia de wraparound la base de datos puede quedar en la práctica en solo lectura; monitoree datfrozenxid de antemano.","tags":["vacuum","freeze","wraparound","maintenance"]},{"id":"reindex-concurrently","task":"Reconstruir un índice sin bloqueos (REINDEX CONCURRENTLY)","category":"Mantenimiento","command":"REINDEX INDEX CONCURRENTLY customer_email_idx;","what":"Reconstruye el índice sin bloquear las escrituras; elimina el bloat del índice.","when":"Cuando un índice ha crecido o se ha corrompido, en un sistema en producción sin ventana de mantenimiento.","caution":"Requiere PostgreSQL 12+. Si falla puede dejar un índice INVALID con el sufijo _ccnew; revise pg_index.indisvalid y límpielo con DROP INDEX si es necesario. Para reconstruir toda la tabla: REINDEX TABLE CONCURRENTLY customer;","since":"PostgreSQL 12","note":"REINDEX ... CONCURRENTLY llegó en PostgreSQL 12; antes, REINDEX bloqueaba el índice con un bloqueo ACCESS EXCLUSIVE (una reconstrucción sin bloqueos requería CREATE INDEX CONCURRENTLY + DROP).","tags":["reindex","concurrently","index","maintenance"]},{"id":"autovacuum-per-table","task":"Ajustar autovacuum para una tabla concreta","category":"Mantenimiento","command":"ALTER TABLE customer SET (autovacuum_vacuum_scale_factor = 0.02, autovacuum_vacuum_threshold = 5000, autovacuum_analyze_scale_factor = 0.01);","what":"Reduce los umbrales de activación de autovacuum solo para esta tabla, de modo que las tablas grandes/con mucha actividad se limpien con más frecuencia.","when":"Cuando autovacuum se queda atrás en tablas grandes con mucha actividad de UPDATE/DELETE.","caution":"Una configuración demasiado agresiva puede provocar ejecuciones constantes de autovacuum y carga de I/O. Para revertir el ajuste: ALTER TABLE customer RESET (autovacuum_vacuum_scale_factor);","tags":["autovacuum","tuning","table","maintenance"]},{"id":"statistics-target","task":"Aumentar el statistics target de una columna","category":"Mantenimiento","command":"ALTER TABLE customer ALTER COLUMN city SET STATISTICS 500; ANALYZE customer;","what":"Aumenta el detalle del histograma/muestra recopilado para una columna concreta; mejora las estimaciones del planificador.","when":"Cuando se producen planes de consulta malos en columnas con distribuciones sesgadas.","caution":"Un valor alto (por defecto 100, máx. 10000) aumenta el tiempo de ANALYZE y el tiempo de planificación. El cambio solo surte efecto tras ANALYZE.","tags":["statistics","planner","analyze","tuning"]},{"id":"alter-system-reload","task":"Cambiar un parámetro y recargar (ALTER SYSTEM)","category":"Parámetros","command":"ALTER SYSTEM SET work_mem = '64MB'; SELECT pg_reload_conf();","what":"Escribe el parámetro en postgresql.auto.conf y recarga la configuración (efecto inmediato para los parámetros que no requieren reinicio).","when":"Para cambiar de forma persistente parámetros que se pueden aplicar con reload, como work_mem y la configuración de logging.","caution":"Algunos parámetros como shared_buffers y max_connections solo surten efecto con un reinicio, no con reload; los que tienen pg_settings.context = 'postmaster' requieren reinicio.","tags":["alter-system","reload","parameter","config"]},{"id":"session-set","task":"Cambiar un parámetro para la sesión (SET)","category":"Parámetros","command":"SET work_mem = '256MB';","what":"Cambia el parámetro solo para la conexión/sesión actual; se restablece al terminar la sesión.","when":"Cuando hay que subir temporalmente un ajuste para una única consulta pesada o un job batch.","caution":"Use SET LOCAL para limitarlo a una sola transacción. Un cambio global requiere ALTER SYSTEM.","tags":["set","session","parameter","work-mem"]},{"id":"show-setting","task":"Mostrar el valor actual de un parámetro","category":"Parámetros","command":"SELECT name, setting, unit, context, source FROM pg_settings WHERE name = 'shared_buffers';","what":"Muestra el valor efectivo del parámetro, su unidad, cómo se puede aplicar (context) y su origen.","when":"Para conocer el valor en ejecución de un parámetro y si requiere reinicio o reload.","caution":"Atajo: SHOW shared_buffers; o SELECT current_setting('shared_buffers'); Si la columna context es 'postmaster', se requiere reinicio.","tags":["show","pg_settings","parameter","current-setting"]},{"id":"physical-replica-basebackup","task":"Construir una réplica física (pg_basebackup)","category":"Replicación","command":"pg_basebackup -h primary_host -U replicator -D /var/lib/postgresql/16/main -Fp -Xs -P -R --slot=replica_slot_1 -C","what":"Toma un backup físico completo del primario, transmite el WAL (-Xs), escribe standby.signal y primary_conninfo (-R) y crea el replication slot (-C).","when":"Para construir un nuevo standby de streaming (réplica de lectura o destino de failover).","caution":"El directorio de destino (-D) debe estar vacío. En el primario, pg_hba.conf debe conceder acceso 'replication' al usuario replicator, y debe haber suficiente wal_keep_size / un slot. Con PostgreSQL 12+, -R genera standby.signal en lugar de recovery.conf.","tags":["replication","basebackup","standby","physical"]},{"id":"promote-replica","task":"Promover una réplica a primario","category":"Replicación","command":"SELECT pg_promote(wait := true, wait_seconds := 60);","what":"Saca el servidor standby del modo de recuperación y lo convierte en un primario con escritura.","when":"Para hacer del standby el nuevo primario en un failover planificado o de emergencia.","caution":"La promoción no se puede deshacer; el servidor ya no puede seguir al antiguo primario. Asegúrese de que el antiguo primario está realmente caído para evitar un split-brain. Alternativa: pg_ctl promote.","since":"PostgreSQL 12","note":"La función SQL pg_promote() llegó en PostgreSQL 12; antes, para promover un standby se usaba un archivo trigger (promote_trigger_file) o pg_ctl promote.","tags":["replication","promote","failover","standby"]},{"id":"replication-lag","task":"Mostrar el lag de replicación","category":"Replicación","command":"SELECT client_addr, state, sent_lsn, replay_lsn, pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replay_lag_bytes, write_lag, flush_lag, replay_lag FROM pg_stat_replication;","what":"En el primario, muestra la diferencia de replay de cada standby en bytes respecto a la posición WAL actual del primario, además del lag de write/flush/replay.","when":"Para monitorear cuánto va retrasada la replicación streaming (ejecutar en el primario).","caution":"Para el lag en segundos en el standby: SELECT now() - pg_last_xact_replay_timestamp(); este valor también crece cuando no hay escrituras en el primario, así que interprételo junto con la diferencia de LSN. Un resultado vacío significa que no hay ningún standby conectado.","tags":["replication","lag","lsn","monitor"]},{"id":"replication-slot","task":"Crear y eliminar un replication slot","category":"Replicación","command":"SELECT pg_create_physical_replication_slot('replica_slot_1'); -- To drop: SELECT pg_drop_replication_slot('replica_slot_1');","what":"Crea un slot físico que garantiza que el primario no elimine el WAL necesario hasta que el standby lo confirme; el segundo comando elimina el slot.","when":"Para asegurar que el standby no pierda WAL durante cortes breves.","caution":"Un slot sin uso/abandonado provoca acumulación de WAL y un disco lleno. Monitoree el estado de los slots: SELECT slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained FROM pg_replication_slots; Un slot activo no se puede eliminar.","tags":["replication","slot","wal","cleanup"]},{"id":"logical-replication","task":"Configurar una publicación y una suscripción de replicación lógica","category":"Replicación","command":"-- Source (publisher): CREATE PUBLICATION pub_customer FOR TABLE customer; -- Target (subscriber): CREATE SUBSCRIPTION sub_customer CONNECTION 'host=primary_host dbname=appdb user=replicator password=secret' PUBLICATION pub_customer;","what":"Inicia la replicación lógica a nivel de tabla creando una publicación para tablas concretas en el origen y una suscripción que se conecta a ella en el destino.","when":"Para upgrades de versión, replicar tablas seleccionadas a otro servidor o distribuir datos en un solo sentido.","caution":"Requiere PostgreSQL 10+. El esquema de las tablas de destino debe existir previamente; la suscripción realiza automáticamente el COPY inicial. wal_level debe ser logical (requiere reinicio). Los cambios de DDL y los valores de las secuencias no se replican; aplique los cambios de esquema por separado en ambos lados y sincronice las secuencias con setval en el cambio final.","since":"PostgreSQL 10","note":"La replicación lógica integrada (CREATE PUBLICATION / CREATE SUBSCRIPTION) llegó en PostgreSQL 10; antes se necesitaban extensiones externas como pglogical para lo mismo.","tags":["logical","replication","publication","subscription"]},{"id":"terminate-backend","task":"Terminar un backend (pg_terminate_backend)","category":"Sesiones","command":"SELECT pg_terminate_backend(12345);","what":"Termina a la fuerza el proceso backend con el PID indicado; corta la conexión y la transacción en curso.","when":"Para cerrar una conexión atascada/colgada o una sesión que retiene un bloqueo.","caution":"La transacción en curso se revierte; el trabajo sin commit se pierde. Pruebe primero el más suave pg_cancel_backend. Verifique el PID en pg_stat_activity.","tags":["terminate","backend","session","kill"]},{"id":"cancel-backend","task":"Cancelar una consulta en ejecución (pg_cancel_backend)","category":"Sesiones","command":"SELECT pg_cancel_backend(12345);","what":"Cancela la consulta que se está ejecutando en el PID indicado; la conexión sigue abierta.","when":"Para detener una consulta larga sin cortar la conexión.","caution":"Solo cancela una consulta activa; no cierra una sesión en estado idle in transaction, que requiere pg_terminate_backend.","tags":["cancel","backend","query","session"]},{"id":"active-blocking-queries","task":"Mostrar consultas activas y bloqueantes","category":"Sesiones","command":"SELECT pid, usename, state, wait_event_type, wait_event, pg_blocking_pids(pid) AS blocking_pids, now() - query_start AS duration, left(query, 100) AS query FROM pg_stat_activity WHERE state <> 'idle' AND pid <> pg_backend_pid() ORDER BY duration DESC;","what":"Lista todos los backends no inactivos, qué PID los están bloqueando y cuánto tiempo llevan ejecutándose.","when":"Para diagnosticar problemas de bloqueos y consultas de larga duración.","caution":"pg_blocking_pids está disponible en PostgreSQL 9.6+. Si el array blocking_pids no está vacío, esos PID retienen el bloqueo; cancele/termine ese PID si es necesario.","since":"PostgreSQL 9.6","note":"La función pg_blocking_pids() llegó en PostgreSQL 9.6; antes, encontrar los procesos bloqueantes requería hacer manualmente un join con pg_locks.","tags":["activity","blocking","lock","session"]},{"id":"create-extension","task":"Añadir una extensión","category":"Extensiones","command":"CREATE EXTENSION IF NOT EXISTS pg_stat_statements;","what":"Instala la extensión indicada en la base de datos actual (añade funciones, vistas, tipos, etc.).","when":"Cuando se necesitan capacidades adicionales como pg_stat_statements, pgcrypto o postgis.","caution":"Para las extensiones que requieren shared_preload_libraries, como pg_stat_statements, añádalas primero a postgresql.conf y reinicie el servidor. Los archivos de la extensión (paquete contrib) deben estar instalados en el servidor.","tags":["extension","create","pg_stat_statements","plugin"]},{"id":"list-extensions","task":"Listar las extensiones instaladas","category":"Extensiones","command":"SELECT extname AS extension, extversion AS version FROM pg_extension ORDER BY extname;","what":"Lista todas las extensiones instaladas en la base de datos actual y sus versiones.","when":"Para ver qué extensiones están instaladas y si hace falta actualizarlas.","caution":"Para ver las extensiones instalables (aún no instaladas): SELECT * FROM pg_available_extensions; Actualización: ALTER EXTENSION pg_stat_statements UPDATE;","tags":["extension","list","pg_extension","plugin"]}]},"recipes/postgres-ha.json":{"engineId":"postgres","engine":"PostgreSQL","order":1,"recipes":[{"id":"patroni-cluster-status","task":"Mostrar el estado, la topología y el historial del clúster Patroni","category":"Patroni","command":"# Members, roles, state, timeline and lag (MB):\npatronictl -c /etc/patroni/patroni.yml list <cluster>\n# Extended info (pending restart, tags) + timestamp, refresh every 5 s:\npatronictl -c /etc/patroni/patroni.yml list <cluster> -e -t -w 5\n# Leader -> replica tree (including cascading replicas):\npatronictl -c /etc/patroni/patroni.yml topology <cluster>\n# Past failovers/switchovers (timeline, LSN, reason, time):\npatronictl -c /etc/patroni/patroni.yml history <cluster>","what":"Muestra la vista del clúster de Patroni desde el DCS: roles leader/replica, estado de los miembros (running/streaming), timeline, lag de replicación y reinicios pendientes; history lista los cambios de timeline con el LSN en que se produjeron.","when":"Antes y después de cada switchover/failover/mantenimiento, y como primer paso de diagnóstico cuando salta una alerta.","caution":"La salida refleja lo que está almacenado en el DCS; si el DCS no es accesible el comando falla o puede mostrar datos obsoletos. No inicie un switchover sin confirmar que todos los miembros están en el mismo timeline y que Lag in MB está por debajo de maximum_lag_on_failover.","untested":true,"tags":["patroni","cluster status","topology","history","lag"]},{"id":"patroni-switchover","task":"Realizar un switchover planificado con Patroni","category":"Patroni","command":"# Immediate switchover without confirmation:\npatronictl -c /etc/patroni/patroni.yml switchover <cluster> --leader <leader_member> --candidate <member> --force\n# Scheduled switchover (ISO 8601 timestamp with time zone):\npatronictl -c /etc/patroni/patroni.yml switchover <cluster> --leader <leader_member> --candidate <member> --scheduled '2026-10-10T02:00:00+03:00' --force\n# Cancel a pending scheduled switchover:\npatronictl -c /etc/patroni/patroni.yml flush <cluster> switchover","what":"Degrada de forma controlada al leader sano y promueve la réplica elegida (--candidate); el antiguo leader se reincorpora automáticamente como réplica del nuevo leader. --scheduled aplaza la operación a la hora indicada.","when":"Para mover el primario a otro nodo antes de un mantenimiento (parches del SO, sustitución de hardware, upgrade de versión menor) o para restaurar los roles originales tras un failover.","caution":"Todas las conexiones se cortan cuando cambia el leader; la aplicación debe poder reconectarse. Patroni rechaza el switchover si el lag del candidato supera maximum_lag_on_failover. Con synchronous_mode habilitado, el candidato debe ser un standby síncrono.","note":"En versiones antiguas de Patroni la opción --leader se llamaba --master; Patroni 4 eliminó --master. --primary equivale a --leader.","untested":true,"tags":["patroni","switchover","planned switchover","maintenance","leader"]},{"id":"patroni-failover","task":"Realizar un failover manual con Patroni","category":"Patroni","command":"# Manual failover while the leader is unhealthy (candidate is mandatory):\npatronictl -c /etc/patroni/patroni.yml failover <cluster> --candidate <member> --force\n# Verify the result and the new timeline:\npatronictl -c /etc/patroni/patroni.yml list <cluster>\npatronictl -c /etc/patroni/patroni.yml history <cluster>","what":"Promueve la réplica indicada independientemente del estado del leader; pensado para los casos en que el failover automático no se produjo o eligió el nodo equivocado.","when":"Cuando el leader no es accesible/no está sano y el failover automático no se activó (p. ej., en modo pause), o cuando debe forzar el paso a un nodo concreto.","caution":"Con replicación asíncrona, las transacciones que el candidato aún no ha recibido se pierden (RPO > 0). Si el leader está sano, use switchover en lugar de failover. Cuando el antiguo leader vuelva puede necesitar pg_rewind (si use_pg_rewind está habilitado) o un reinit.","note":"En Patroni 3.2+ --candidate es obligatorio para failover; la opción --leader/--primary de failover quedó obsoleta en 3.2 y se eliminó en Patroni 4.0. Use switchover si necesita indicar el leader.","untested":true,"tags":["patroni","failover","manual failover","promote","rpo"]},{"id":"patroni-reinit-replica","task":"Reconstruir una réplica dañada con Patroni (reinit)","category":"Patroni","command":"# Wipe the replica's PGDATA and rebuild it (create_replica_methods: pgbackrest/basebackup):\npatronictl -c /etc/patroni/patroni.yml reinit <cluster> <member> --wait --force\n# Take the basebackup directly from the leader instead of the backup repository:\npatronictl -c /etc/patroni/patroni.yml reinit <cluster> <member> --from-leader --wait --force","what":"Borra el directorio de datos de la réplica indicada y la reconstruye desde cero según los create_replica_methods de Patroni (restauración con pgBackRest o pg_basebackup).","when":"Cuando una réplica no puede reincorporarse a la replicación por una discrepancia de timeline, un pg_rewind fallido, WAL faltante o corrupción de datos.","caution":"Se borran todos los datos locales del miembro; compruebe dos veces que el comando apunta al miembro correcto (Patroni rechaza el reinit en el leader). En bases de datos grandes puede tardar horas y añade carga de I/O/red al leader; mientras tanto el clúster funciona con una réplica menos. --from-leader solo existe en versiones recientes de Patroni 4.x (compruébelo con patronictl reinit --help); omite create_replica_methods y toma un pg_basebackup del leader.","untested":true,"tags":["patroni","reinit","rebuild replica","rebuild","basebackup"]},{"id":"patroni-edit-config","task":"Cambiar parámetros de todo el clúster en Patroni (edit-config) y aplicar los reinicios pendientes","category":"Patroni","command":"# Show the current dynamic configuration stored in the DCS:\npatronictl -c /etc/patroni/patroni.yml show-config <cluster>\n# Cluster-wide PostgreSQL parameter (distributed to all members):\npatronictl -c /etc/patroni/patroni.yml edit-config <cluster> -p 'work_mem=64MB' -p 'max_connections=500' --force\n# Patroni behavior parameter:\npatronictl -c /etc/patroni/patroni.yml edit-config <cluster> -s 'maximum_lag_on_failover=1048576' --force\n# Parameters requiring a restart are flagged as 'Pending restart':\npatronictl -c /etc/patroni/patroni.yml list <cluster> -e\n# Restart replicas first, then the leader:\npatronictl -c /etc/patroni/patroni.yml restart <cluster> --role replica --pending --force\npatronictl -c /etc/patroni/patroni.yml restart <cluster> --role leader --pending --force","what":"Escribe parámetros de PostgreSQL y Patroni en la configuración dinámica del DCS; Patroni aplica el cambio a todos los miembros, activa de inmediato los que admiten reload y marca como 'Pending restart' los que necesitan reinicio.","when":"Para cambiar de forma coherente en todos los nodos de un clúster gestionado por Patroni parámetros como max_connections, shared_buffers o work_mem.","caution":"Con Patroni, no edite postgresql.conf a mano ni use ALTER SYSTEM: Patroni reescribe postgresql.conf, y postgresql.auto.conf anula los ajustes de Patroni, lo que provoca diferencias entre nodos. max_connections, max_worker_processes, max_wal_senders, max_prepared_transactions y max_locks_per_transaction en un standby no pueden ser menores que en el primario; planifique el orden de reinicio en consecuencia.","untested":true,"tags":["patroni","edit-config","parameter","pending restart","dcs"]},{"id":"patroni-pause-resume","task":"Poner Patroni en modo mantenimiento y salir de él (pause/resume)","category":"Patroni","command":"# Maintenance mode: automatic failover and Patroni's control over PostgreSQL stop\npatronictl -c /etc/patroni/patroni.yml pause <cluster> --wait\npatronictl -c /etc/patroni/patroni.yml list <cluster>          # should show 'Maintenance mode: on'\n# ... maintenance work (e.g. manual PostgreSQL restart, DCS maintenance) ...\npatronictl -c /etc/patroni/patroni.yml resume <cluster> --wait","what":"Pone el clúster en modo mantenimiento: Patroni no realiza failover automático ni reinicia un PostgreSQL detenido; resume vuelve a la gestión normal.","when":"Durante el mantenimiento del DCS (etcd/Consul), upgrades de Patroni, o cuando PostgreSQL debe gestionarse manualmente sin que Patroni interfiera.","caution":"Si el primario cae mientras está en pausa no hay failover automático y la caída continúa. No olvide ejecutar resume tras el mantenimiento; añada una comprobación de 'Maintenance mode' a sus alertas. Iniciar manualmente un PostgreSQL como primario durante la pausa puede causar split-brain.","untested":true,"tags":["patroni","pause","resume","maintenance mode","maintenance"]},{"id":"patroni-dcs-health","task":"Comprobar la salud del DCS de Patroni (etcd/Consul) y de la REST API","category":"Patroni","command":"# etcd v3 (default API in etcd 3.4+): member health, leader, raft term and DB size\netcdctl --endpoints=https://<etcd1>:2379,https://<etcd2>:2379,https://<etcd3>:2379 --cacert=/etc/etcd/ca.crt endpoint health --cluster -w table\netcdctl --endpoints=https://<etcd1>:2379 --cacert=/etc/etcd/ca.crt endpoint status --cluster -w table\netcdctl --endpoints=https://<etcd1>:2379 --cacert=/etc/etcd/ca.crt alarm list\n# Consul:\nconsul members\nconsul operator raft list-peers\n# Patroni REST API (on each member):\ncurl -s http://<member>:8008/cluster | python3 -m json.tool\ncurl -s -o /dev/null -w '%{http_code}\\n' http://<member>:8008/primary   # 200 = leader, 503 = not leader","what":"Comprueba el quorum, el leader y la capacidad del DCS donde Patroni mantiene el leader lock, y la visión que cada miembro tiene de su propio rol a través de la REST API de Patroni. El endpoint /primary es la base de los health checks del balanceador de carga (HAProxy).","when":"Cuando Patroni registra 'DCS is not accessible', tras un demote/failover inesperado y como comprobación de salud rutinaria.","caution":"Si el DCS pierde el quorum, Patroni no puede renovar el leader lock y degrada el primario a solo lectura (salvo que esté habilitado failsafe_mode, Patroni 3.0+); por tanto, una caída de etcd es una caída de escritura de la base de datos. Cuando el tamaño de la BD de etcd alcanza su cuota (2 GB por defecto), una alarma NOSPACE detiene las escrituras. Ejecute el DCS en al menos 3 nodos con discos separados.","untested":true,"tags":["patroni","etcd","consul","dcs health","rest api"]},{"id":"pgbackrest-archive-config","task":"Configurar el archivado de WAL para pgBackRest (archive-push/archive-get)","category":"Backup y PITR","command":"# postgresql.conf (changing archive_mode requires a restart):\nwal_level = replica\narchive_mode = on\narchive_command = 'pgbackrest --stanza=<stanza> archive-push %p'\n# On the restore/standby side (in postgresql.conf on PG12+):\nrestore_command = 'pgbackrest --stanza=<stanza> archive-get %f \"%p\"'\n\n# /etc/pgbackrest/pgbackrest.conf (database host, local repo example):\n[global]\nrepo1-path=/var/lib/pgbackrest\nrepo1-retention-full=2\narchive-async=y\nspool-path=/var/spool/pgbackrest\nprocess-max=4\ncompress-type=zst\nstart-fast=y\n\n[<stanza>]\npg1-path=/var/lib/pgsql/16/data","what":"Hace que PostgreSQL envíe cada segmento WAL completado al repositorio de pgBackRest (archive-push) y lo recupere de allí durante la recuperación (archive-get); archive-async hace el envío paralelo y asíncrono mediante un directorio de spool.","when":"Como primer paso para configurar backups físicos y PITR con pgBackRest, antes de stanza-create.","caution":"Mientras archive_command falle, el WAL se acumula en pg_wal y el primario se detiene cuando se llena el disco; monitoree pg_stat_archiver.failed_count. Con Patroni, defina estos parámetros mediante patroni.yml / edit-config en lugar de postgresql.conf. Archivar desde dos clústeres distintos en la misma stanza corrompe el repositorio.","untested":true,"tags":["pgbackrest","archiving","archive_command","wal archive","configuration"]},{"id":"pgbackrest-stanza-create-check","task":"Crear la stanza de pgBackRest y verificar el archivado (stanza-create/check)","category":"Backup y PITR","command":"# Create the stanza in the repository (once):\nsudo -u postgres pgbackrest --stanza=<stanza> --log-level-console=info stanza-create\n# Verify configuration and archiving end to end (switches WAL and waits for the segment to reach the repo):\nsudo -u postgres pgbackrest --stanza=<stanza> --log-level-console=info check","what":"stanza-create crea en el repositorio los metadatos del clúster (system identifier, versión); check prueba la conexión a PostgreSQL, archive_command y la llegada del WAL al repositorio con un WAL switch real.","when":"En la configuración inicial, tras cambiar archive_command o el repositorio, y como comprobación de salud de monitoreo/cron.","caution":"Ejecute los comandos como postgres (o el propietario del repositorio); ejecutarlos como root rompe la propiedad de los archivos. Tras un upgrade de versión mayor se requiere stanza-upgrade, no stanza-create. Si check falla, PITR puede ser imposible aunque los backups terminen.","untested":true,"tags":["pgbackrest","stanza","stanza-create","check","archive verification"]},{"id":"pgbackrest-backup-full-diff-incr","task":"Tomar backups completos, diferenciales e incrementales con pgBackRest","category":"Backup y PITR","command":"sudo -u postgres pgbackrest --stanza=<stanza> --type=full backup\nsudo -u postgres pgbackrest --stanza=<stanza> --type=diff backup   # changes since the last full\nsudo -u postgres pgbackrest --stanza=<stanza> --type=incr backup   # changes since the last backup of any type\n# cron example (postgres user): full on Sunday, diff on other days\n# 30 1 * * 0   pgbackrest --stanza=<stanza> --type=full backup\n# 30 1 * * 1-6 pgbackrest --stanza=<stanza> --type=diff backup","what":"Un backup full copia todo el clúster, diff copia los archivos cambiados desde el último full, incr copia los archivos cambiados desde el último backup de cualquier tipo; al final se verifica que el WAL necesario está archivado y expire se ejecuta automáticamente según la retención.","when":"En una programación de backups regular: normalmente full semanal + diff/incr diario, incr más frecuentes en clústeres grandes.","caution":"Sin un full previo, las solicitudes diff/incr se promueven automáticamente a full. Una cadena incr más larga aumenta el tiempo de restauración y la dependencia; perder un backup de la cadena inutiliza los posteriores. Para descargar al primario, haga el backup desde un standby con backup-standby=y (el primario sigue siendo necesario para la coordinación).","untested":true,"tags":["pgbackrest","backup","full","differential","incremental"]},{"id":"pgbackrest-info","task":"Mostrar el catálogo de backups de pgBackRest y el rango del archivo de WAL (info)","category":"Backup y PITR","command":"sudo -u postgres pgbackrest --stanza=<stanza> info\n# JSON for monitoring/automation:\nsudo -u postgres pgbackrest --stanza=<stanza> --output=json info\n# Details of a single backup (databases, LSN range):\nsudo -u postgres pgbackrest --stanza=<stanza> --set=<backup_label> info","what":"Lista el estado de la stanza, la etiqueta, el tipo, la hora de inicio/fin, el tamaño de cada backup, el backup del que depende y el rango mín./máx. del archivo de WAL en el repositorio.","when":"Antes de una restauración/PITR para confirmar que la hora de destino está cubierta, para monitorear que los backups se ejecutan con regularidad y para comprobar la retención.","caution":"info muestra que un backup existe, no que sea restaurable; valídelo con restauraciones de prueba regulares y pgbackrest verify. Si hay un hueco en el rango mín./máx. de WAL, PITR no puede cruzarlo.","untested":true,"tags":["pgbackrest","info","backup list","catalog","monitoring"]},{"id":"pgbackrest-restore-latest","task":"Restaurar el último backup con pgBackRest","category":"Backup y PITR","command":"# PostgreSQL must be stopped and PGDATA empty (otherwise use --delta)\nsudo systemctl stop postgresql-16\nsudo -u postgres pgbackrest --stanza=<stanza> --log-level-console=info restore\n# From a specific backup: --set=<backup_label>\n# When restoring to another server for testing, disable archiving:\n# sudo -u postgres pgbackrest --stanza=<stanza> --archive-mode=off restore\nsudo systemctl start postgresql-16","what":"Restaura el último backup y escribe recovery.signal y restore_command; al arrancar, PostgreSQL reproduce todo el WAL archivado hasta el punto consistente más reciente y se promueve en un timeline nuevo.","when":"Cuando el directorio de datos del primario se ha perdido o corrompido, o al comprobar regularmente que los backups son restaurables.","caution":"Si el clúster restaurado y el antiguo archivan a la vez en la misma stanza, el repositorio se corrompe; use --archive-mode=off en las restauraciones de prueba. Con Patroni, ponga el clúster en pausa o detenga Patroni primero; de lo contrario Patroni interfiere. Si existen tablespaces o rutas fuera de PGDATA, revise las opciones --tablespace-map y link.","untested":true,"tags":["pgbackrest","restore","restore","recovery","disaster recovery"]},{"id":"pgbackrest-pitr-restore","task":"Restaurar a un punto en el tiempo con pgBackRest (PITR, --type=time)","category":"Backup y PITR","command":"sudo systemctl stop postgresql-16\nsudo -u postgres pgbackrest --stanza=<stanza> --delta --type=time \"--target=2026-10-08 14:30:00+03\" --target-action=pause restore\nsudo systemctl start postgresql-16\n# Recovery stops at the target (read-only); validate the data:\npsql -X -c \"SELECT pg_is_in_recovery(), pg_last_xact_replay_timestamp();\"\n# If correct, end recovery and open read-write on a new timeline:\npsql -X -c \"SELECT pg_wal_replay_resume();\"","what":"Restaura un backup que terminó antes de la hora de destino y reproduce el WAL solo hasta el destino; target-action=pause se detiene en el destino para que pueda validar los datos, y pg_wal_replay_resume() lo promueve.","when":"Para volver al momento justo anterior a un error lógico como un DELETE/DROP/UPDATE accidental.","caution":"En este clúster se pierden todos los cambios posteriores al destino; cuando sea posible, ejecute el PITR en un servidor aparte y traslade solo los datos afectados con pg_dump. Indique explícitamente la zona horaria en el destino. pgBackRest 2.24+ selecciona automáticamente un backup adecuado anterior al destino; en versiones anteriores indique con --set un backup que terminó antes del destino. Si el destino es anterior a la hora de fin del backup, la restauración no puede alcanzar la consistencia.","untested":true,"tags":["pgbackrest","pitr","point in time recovery","point in time","recovery"]},{"id":"pgbackrest-delta-restore-standby","task":"Reconstruir rápidamente un standby con delta restore de pgBackRest","category":"Backup y PITR","command":"sudo systemctl stop postgresql-16\n# Keeps the existing PGDATA and fetches only differing files; writes standby.signal:\nsudo -u postgres pgbackrest --stanza=<stanza> --delta --type=standby \"--recovery-option=primary_conninfo=host=<primary_host> port=5432 user=replicator application_name=<standby_name>\" restore\nsudo systemctl start postgresql-16\npsql -X -c \"SELECT status, sender_host FROM pg_stat_wal_receiver;\"","what":"--delta compara los archivos del PGDATA existente con el backup (tamaño/marca de tiempo y checksum) y copia solo los que difieren; --type=standby escribe standby.signal y recovery-option añade primary_conninfo. El standby primero se pone al día desde el archivo y luego pasa a streaming.","when":"Para convertir de nuevo en réplica, sin una copia completa, un standby retrasado o con timeline divergente, o un antiguo primario donde falló pg_rewind.","caution":"Delta sustituye/elimina los archivos distintos o sobrantes del directorio de destino por los del backup; ejecutarlo en el servidor equivocado destruye los datos de ese servidor. pgBackRest se niega a restaurar sobre un PostgreSQL en ejecución, pero verifique usted mismo que el servicio está detenido. Guarde la contraseña en .pgpass en lugar de en primary_conninfo.","untested":true,"tags":["pgbackrest","delta restore","rebuild standby","rebuild","replica"]},{"id":"pgbackrest-retention-expire","task":"Configurar la retención de pgBackRest y ejecutar expire","category":"Backup y PITR","command":"# /etc/pgbackrest/pgbackrest.conf [global] - count based:\nrepo1-retention-full-type=count\nrepo1-retention-full=2\nrepo1-retention-diff=6\n# Time-based alternative (at least a 14-day PITR window):\n# repo1-retention-full-type=time\n# repo1-retention-full=14\n\n# Expire runs automatically after each backup; to trigger it manually:\nsudo -u postgres pgbackrest --stanza=<stanza> --log-level-console=info expire","what":"Define cuántos backups full/diff conservar (count) o cuántos días de backups full (time); expire borra los backups fuera de la política, los backups diff/incr que dependen de ellos y el archivo de WAL que ya no se necesita.","when":"Para mantener bajo control el tamaño del repositorio y garantizar el requisito de ventana de RPO/PITR (p. ej., poder volver 14 días atrás).","caution":"La ventana de PITR está limitada por el backup full más antiguo conservado; con fulls semanales y retention-full=2, puede volver como máximo unas 2 semanas atrás. repo1-retention-archive sigue por defecto la retención de full; ponerlo más bajo deja los backups antiguos utilizables solo hasta su punto de consistencia. Expire no se puede deshacer.","untested":true,"tags":["pgbackrest","retention","retention period","expire","backup cleanup"]},{"id":"standby-manual-config","task":"Configurar un standby manualmente (standby.signal + primary_conninfo)","category":"Replicación","command":"# On the standby, with PostgreSQL stopped (PGDATA came from a backup/copy):\ntouch $PGDATA/standby.signal\ncat >> $PGDATA/postgresql.auto.conf <<'EOF'\nprimary_conninfo = 'host=<primary_host> port=5432 user=replicator application_name=<standby_name>'\nprimary_slot_name = '<slot>'\nEOF\npg_ctl -D $PGDATA start\n# PG13+: change the connection on a running standby without restart (e.g. point it to a new primary):\npsql -X -c \"ALTER SYSTEM SET primary_conninfo = 'host=<new_primary_host> port=5432 user=replicator application_name=<standby_name>'\" -c \"SELECT pg_reload_conf();\"","what":"El archivo standby.signal hace que el servidor arranque como standby; primary_conninfo define la conexión de streaming y primary_slot_name el replication slot que se usará. En PG13+ primary_conninfo y primary_slot_name se pueden cambiar con un reload.","when":"Para convertir en standby un directorio de datos preparado sin pg_basebackup -R (pgBackRest, snapshot, rsync), o para apuntar un standby existente a un primario nuevo.","caution":"El slot debe crearse previamente en el primario (pg_create_physical_replication_slot); de lo contrario el walreceiver sigue fallando. Ponga la contraseña en ~/.pgpass en lugar de en conninfo. application_name debe coincidir exactamente con el nombre en synchronous_standby_names. Si se olvida standby.signal, el servidor arranca como primario y pasa a un timeline separado.","since":"PostgreSQL 12","note":"Antes de PostgreSQL 12 estos ajustes estaban en recovery.conf (standby_mode = on); PG12 los trasladó a standby.signal y postgresql.conf. Antes de PG13, cambiar primary_conninfo requería reinicio.","tags":["standby","standby.signal","primary_conninfo","replica setup","streaming"]},{"id":"standby-wal-receiver-lag","task":"Monitorear el lag de recepción y replay de WAL en el standby (pg_stat_wal_receiver)","category":"Replicación","command":"-- Run on the standby:\nSELECT pg_is_in_recovery() AS in_recovery,\n       pg_last_wal_receive_lsn() AS receive_lsn,\n       pg_last_wal_replay_lsn() AS replay_lsn,\n       pg_size_pretty(pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn())) AS replay_backlog,\n       now() - pg_last_xact_replay_timestamp() AS last_replay_age,\n       pg_get_wal_replay_pause_state() AS pause_state;\nSELECT pid, status, sender_host, sender_port, slot_name, received_tli, flushed_lsn, latest_end_lsn, now() - last_msg_receipt_time AS since_last_msg FROM pg_stat_wal_receiver;","what":"Muestra cuánto WAL ha recibido el standby y cuánto ha aplicado (atraso de replay), la antigüedad de la última transacción reproducida, si el replay está en pausa y el estado de la conexión del walreceiver (qué primario, slot, timeline, hora del último mensaje).","when":"Para diagnosticar el lag cuando el primario no es accesible o en standbys en cascada; ante una alerta de 'standby retrasado', para separar un problema de red (recepción) de uno de replay (I/O, conflictos).","caution":"Si pg_stat_wal_receiver no devuelve filas, el standby no está haciendo streaming (se alimenta del archivo o está desconectado). last_replay_age crece incluso cuando el primario no tiene escrituras; interprételo junto con la diferencia receive_lsn/replay_lsn. Si el atraso de replay crece mientras la recepción avanza, el standby tiene consultas largas (max_standby_streaming_delay) o un cuello de botella de I/O.","since":"PostgreSQL 14","note":"pg_get_wal_replay_pause_state() se añadió en PostgreSQL 14; antes solo existía pg_is_wal_replay_paused(). El resto de la consulta funciona en PG10+.","tags":["standby","replication lag","wal receiver","replay","lag"]},{"id":"pg-rewind-old-primary","task":"Reincorporar el antiguo primario como réplica tras un failover con pg_rewind","category":"Replicación","command":"# Prerequisite (must be set BEFORE the incident): wal_log_hints = on OR data checksums; full_page_writes = on\npg_controldata $PGDATA | grep -E 'wal_log_hints|checksum'\n# 1) The old primary must be stopped:\npg_ctl -D $PGDATA stop -m fast\n# 2) Dry run first:\npg_rewind --target-pgdata=$PGDATA --source-server='host=<new_primary_host> port=5432 user=postgres dbname=postgres' --progress --dry-run\n# 3) Actual rewind + standby configuration (standby.signal + primary_conninfo):\npg_rewind --target-pgdata=$PGDATA --source-server='host=<new_primary_host> port=5432 user=postgres dbname=postgres' --progress --write-recovery-conf\n# 4) Review postgresql.auto.conf copied from the source (port, primary_slot_name), then start:\npg_ctl -D $PGDATA start","what":"Hace coherente el directorio de datos del antiguo primario con el nuevo primario copiando desde este los bloques cambiados tras el punto de divergencia del timeline; lo convierte en réplica en minutos sin un basebackup completo.","when":"Cuando el antiguo primario vuelve tras un failover/promote y está por detrás del nuevo timeline con cambios divergentes.","caution":"Las transacciones que quedaron en el antiguo primario tras el failover y nunca llegaron al nuevo primario se eliminan definitivamente. pg_rewind también copia los archivos de configuración (postgresql.conf, postgresql.auto.conf, pg_hba.conf) desde el origen; corrija el puerto y los ajustes de slot antes de arrancar. Si el WAL necesario desde el punto de divergencia ya no está en el origen, obténgalo del archivo con -c (--restore-target-wal). La conexión al origen necesita un superusuario o un rol con los privilegios sobre funciones indicados en la documentación. En Patroni esto se hace automáticamente con use_pg_rewind: true.","since":"PostgreSQL 13","note":"-R/--write-recovery-conf y -c/--restore-target-wal se añadieron en PostgreSQL 13; en PG13+ pg_rewind ejecuta él mismo crash recovery sobre un destino detenido de forma no limpia (deshabilítelo con --no-ensure-shutdown). pg_rewind existe desde 9.5.","tags":["pg_rewind","after failover","old primary","timeline","rejoin"]},{"id":"sync-standby-quorum","task":"Configurar la replicación síncrona con quorum (ANY) o prioridad (FIRST)","category":"Replicación","command":"-- Quorum: commit completes once any 1 standby in the list confirms\nALTER SYSTEM SET synchronous_standby_names = 'ANY 1 (stby_a, stby_b)';\n-- Priority based: the first connected standby in the list is sync, the others 'potential'\n-- ALTER SYSTEM SET synchronous_standby_names = 'FIRST 1 (stby_a, stby_b)';\nSELECT pg_reload_conf();\nSELECT application_name, state, sync_state, sync_priority FROM pg_stat_replication ORDER BY application_name;","what":"Define qué standbys deben confirmar antes de que se complete un commit: ANY N se basa en quorum (sync_state = quorum), FIRST N hace síncronos (sync) los primeros N standbys conectados en el orden de la lista y deja el resto como standbys en espera (potential). El nivel de confirmación lo fija synchronous_commit (remote_write, on, remote_apply).","when":"En sistemas que exigen no perder datos en un failover (RPO = 0); con varios standbys se prefiere ANY para que el fallo de un solo standby no detenga las escrituras.","caution":"Si no está conectado el número necesario de standbys síncronos, los commits se quedan colgados sin error y la aplicación se congela. Los nombres de la lista deben coincidir con el application_name de los standbys. En una emergencia, una sola sesión puede saltárselo con SET synchronous_commit = local. Con Patroni, gestiónelo con synchronous_mode en lugar de configurarlo manualmente.","since":"PostgreSQL 10","note":"La sintaxis FIRST se añadió en PostgreSQL 9.6 y ANY (quorum commit) en PostgreSQL 10; la forma 'N (a, b)' sin palabra clave significa FIRST.","tags":["synchronous replication","synchronous_standby_names","quorum","rpo","sync"]},{"id":"slot-retained-wal-monitor","task":"Monitorear el WAL retenido por los replication slots y eliminar con seguridad un slot abandonado","category":"Replicación","command":"SELECT slot_name, slot_type, database, active, active_pid, wal_status,\n       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal,\n       pg_size_pretty(safe_wal_size) AS safe_wal_size,\n       xmin, catalog_xmin\nFROM pg_replication_slots\nORDER BY pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) DESC NULLS LAST;\n-- Safe drop: drops only if not active (returns 0 rows if active)\nSELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = '<slot>' AND NOT active;","what":"Muestra cuánto WAL retiene cada slot en el primario, wal_status (reserved/extended/unreserved/lost), el margen seguro restante bajo max_slot_wal_keep_size (safe_wal_size) y los valores xmin/catalog_xmin que frenan el VACUUM; la segunda consulta elimina el slot solo si no está en uso.","when":"Cuando crece el directorio pg_wal, ante una alerta de disco y en el monitoreo rutinario; para limpiar slots después de retirar definitivamente un standby o un suscriptor.","caution":"Un slot inactivo retiene WAL sin límite y puede llenar el disco y detener el primario; un slot que retiene xmin/catalog_xmin también bloquea la limpieza de dead tuples y crea riesgo de bloat/wraparound. Eliminar un slot impide que su standby/suscriptor continúe donde lo dejó (se requiere reconstrucción); verifique su propietario antes de eliminarlo. Ejecútelo en el primario (pg_current_wal_lsn() falla en un standby).","since":"PostgreSQL 13","note":"wal_status y safe_wal_size se añadieron en PostgreSQL 13. PG16 añadió conflicting para los slots lógicos y PG17 añadió inactive_since; inactive_since es el indicador más directo de un slot abandonado.","tags":["replication slot","wal accumulation","disk full","drop slot","retained wal"]},{"id":"slot-max-wal-keep-size","task":"Limitar el WAL que pueden retener los slots (max_slot_wal_keep_size)","category":"Replicación","command":"ALTER SYSTEM SET max_slot_wal_keep_size = '100GB';\nSELECT pg_reload_conf();\nSELECT slot_name, active, wal_status, pg_size_pretty(safe_wal_size) AS safe_wal_size FROM pg_replication_slots;","what":"Fija un límite superior al WAL que los replication slots pueden mantener en pg_wal (por defecto -1 = ilimitado); al superarse, el checkpoint elimina el WAL antiguo y el slot pasa a wal_status = 'lost'. El parámetro surte efecto con un reload.","when":"Como salvaguarda en todo clúster que use slots, frente al riesgo de que un standby o suscriptor desconectado llene el disco del primario y detenga todo el sistema.","caution":"El límite prioriza mantener vivo el primario a costa del propietario del slot: un standby con un slot 'lost' debe reconstruirse y una suscripción lógica debe recrearse. Elija un valor inferior al espacio libre del disco de pg_wal pero mayor que el WAL generado durante la caída más larga esperada. Si wal_status es 'unreserved', el slot está a punto de perderse; genere una alerta.","since":"PostgreSQL 13","note":"max_slot_wal_keep_size se añadió en PostgreSQL 13; antes no había forma de limitar el WAL retenido por los slots y la única protección era monitorear y eliminar slots manualmente.","tags":["max_slot_wal_keep_size","slot limit","wal accumulation","disk protection","replication slot"]},{"id":"logical-add-table-refresh","task":"Añadir una tabla a una publicación y refrescar la suscripción (REFRESH PUBLICATION)","category":"Replicación lógica","command":"-- Publisher:\nALTER PUBLICATION pub_app ADD TABLE orders;\n-- PG15+: all tables in a schema: ALTER PUBLICATION pub_app ADD TABLES IN SCHEMA app_schema;\nSELECT pubname, schemaname, tablename FROM pg_publication_tables WHERE pubname = 'pub_app';\n-- Subscriber (the table structure must already exist):\nALTER SUBSCRIPTION sub_app REFRESH PUBLICATION WITH (copy_data = true);\nSELECT srrelid::regclass AS table_name, srsubstate FROM pg_subscription_rel ORDER BY 1;","what":"Añade una tabla nueva a la publicación en el publicador; en el suscriptor, REFRESH PUBLICATION incluye las tablas nuevas en la suscripción e inicia la copia inicial (tablesync) con copy_data = true. srsubstate: i = init, d = data copy, f = copy finished, s = synchronized, r = ready.","when":"Cuando se añade una tabla nueva a una replicación lógica en marcha, o se quita una tabla de la publicación.","caution":"Incluso con publicaciones FOR ALL TABLES o TABLES IN SCHEMA, el suscriptor no incorpora una tabla nueva hasta que se ejecuta REFRESH. REFRESH falla si la tabla no existe en el suscriptor; el DDL no se replica. Si una tabla publicada no tiene PRIMARY KEY ni REPLICA IDENTITY, UPDATE/DELETE fallan en el publicador. La copia inicial de una tabla grande retiene un slot y un snapshot en el publicador durante mucho tiempo.","since":"PostgreSQL 10","note":"ADD TABLES IN SCHEMA se añadió en PostgreSQL 15 (PG15 también añadió filtros de filas con WHERE y listas de columnas).","tags":["logical replication","publication","add table","refresh publication","subscription"]},{"id":"logical-subscription-monitor","task":"Monitorear la replicación lógica (pg_stat_subscription, errores de apply, lag de slots)","category":"Replicación lógica","command":"-- Subscriber:\nSELECT subname, pid, leader_pid, relid::regclass AS syncing_table, received_lsn, latest_end_lsn, now() - last_msg_receipt_time AS since_last_msg, latest_end_time FROM pg_stat_subscription;\nSELECT subname, apply_error_count, sync_error_count, stats_reset FROM pg_stat_subscription_stats;\n-- Publisher:\nSELECT slot_name, plugin, database, active, wal_status,\n       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) AS confirmed_lag,\n       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal\nFROM pg_replication_slots WHERE slot_type = 'logical';","what":"En el suscriptor, muestra los workers de apply/tablesync, el LSN recibido y confirmado, la hora del último mensaje y el número acumulado de errores de apply/sync; en el publicador, muestra el WAL no confirmado (confirmed_flush) del slot lógico y el WAL total que retiene.","when":"Durante la configuración de la replicación lógica, para confirmar que el lag se acerca a cero durante el cambio final de un upgrade/migración, y para las alertas de errores.","caution":"Si pid es NULL, el apply worker no está en ejecución (suscripción deshabilitada o fallando y reiniciándose repetidamente); revise el log del servidor. Un apply_error_count creciente indica un conflicto. Una suscripción detenida acumula WAL y catalog_xmin en el publicador; monitoree el disco del publicador y el bloat del catálogo.","since":"PostgreSQL 15","note":"pg_stat_subscription_stats se añadió en PostgreSQL 15 y pg_stat_subscription.leader_pid en PG16 (parallel apply); PG17 añadió la columna worker_type. Las columnas básicas de pg_stat_subscription existen desde PG10.","tags":["logical replication","subscription monitoring","pg_stat_subscription","apply error","lag"]},{"id":"logical-conflict-skip","task":"Resolver un conflicto de replicación lógica (disable_on_error, SKIP)","category":"Replicación lógica","command":"-- 1) Prevent a failing apply worker from looping forever:\nALTER SUBSCRIPTION sub_app SET (disable_on_error = true);\n-- 2) Find the finish LSN of the conflicting transaction in the subscriber log:\n--    CONTEXT: ... in transaction 749, finished at 0/5035378\n-- 3a) Preferred: fix/delete the conflicting row on the subscriber, then re-enable:\nALTER SUBSCRIPTION sub_app ENABLE;\n-- 3b) Or skip the whole remote transaction (NONE of its changes are applied on the subscriber):\nALTER SUBSCRIPTION sub_app SKIP (lsn = '0/5035378');\nALTER SUBSCRIPTION sub_app ENABLE;\nSELECT subname, subenabled, subskiplsn FROM pg_subscription;","what":"Cuando un error como una clave duplicada, una fila inexistente o un problema de permisos detiene el apply en el suscriptor: disable_on_error deshabilita automáticamente la suscripción; corrija los datos en conflicto y ejecute ENABLE, o use SKIP para omitir toda la transacción remota identificada por el LSN 'finished at' del log.","when":"Cuando el apply worker falla y se reinicia continuamente por escrituras directas en el suscriptor, una copia inicial incompleta o escrituras bidireccionales.","caution":"SKIP también descarta los cambios no conflictivos de esa transacción y ambos lados divergen definitivamente; es preferible corregir los datos, y compare la tabla tras un SKIP. Hasta que se resuelva el conflicto, el slot del publicador sigue acumulando WAL. Impida con privilegios las escrituras de la aplicación en las tablas del suscriptor.","since":"PostgreSQL 15","note":"SKIP y disable_on_error se añadieron en PostgreSQL 15; antes se hacía lo mismo avanzando el origin en el suscriptor con pg_replication_origin_advance() (más arriesgado).","tags":["logical replication","conflict","conflict","skip","duplicate key"]},{"id":"logical-sync-sequences","task":"Sincronizar los valores de las secuencias en el suscriptor en la replicación lógica","category":"Replicación lógica","command":"# Generate the setval script on the publisher and apply it on the subscriber (at cutover, after writes are stopped):\npsql -h <publisher_host> -d <db> -XAtc \"SELECT format('SELECT setval(%L, %s, %s);', quote_ident(schemaname) || '.' || quote_ident(sequencename), COALESCE(last_value, start_value), (last_value IS NOT NULL)::text) FROM pg_sequences;\" | psql -h <subscriber_host> -d <db> -X","what":"Como la replicación lógica no transporta los valores de las secuencias (incluidas las identity), lee el último valor de cada secuencia en el publicador y genera y aplica directamente comandos setval que fijan el mismo valor en el suscriptor.","when":"En el cambio final de un upgrade/migración mediante replicación lógica, justo antes de apuntar la aplicación al suscriptor.","caution":"Si se ejecuta mientras el publicador sigue recibiendo escrituras, los valores quedan obsoletos de inmediato y tras el cambio aparecen errores de clave duplicada; ejecútelo después de detener las escrituras o añada un margen de seguridad. El DDL tampoco se replica: aplique los cambios de esquema (ALTER TABLE, índices nuevos) por separado en ambos lados, primero en el suscriptor. pg_sequences devuelve last_value NULL para las secuencias que no puede leer; ejecute el comando como propietario o como superusuario.","since":"PostgreSQL 10","tags":["logical replication","sequence","setval","ddl not replicated","cutover"]},{"id":"logical-orphan-slot-cleanup","task":"Eliminar una suscripción y limpiar un slot lógico huérfano en el publicador","category":"Replicación lógica","command":"-- Subscriber: while the publisher is unreachable DROP SUBSCRIPTION cannot drop the remote slot and fails; detach the slot first:\nALTER SUBSCRIPTION sub_app DISABLE;\nALTER SUBSCRIPTION sub_app SET (slot_name = NONE);\nDROP SUBSCRIPTION sub_app;\n-- Publisher: find and drop orphaned logical slots (including tablesync slots pg_<suboid>_sync_<relid>_<sysid>):\nSELECT slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal FROM pg_replication_slots WHERE slot_type = 'logical' AND NOT active;\nSELECT pg_drop_replication_slot('sub_app');","what":"Elimina la suscripción sin tocar el slot remoto (slot_name = NONE); después busca y elimina en el publicador los slots lógicos que ya no tienen consumidor, además de los slots de tablesync sobrantes.","when":"Cuando hay que eliminar una suscripción con el publicador inaccesible, al terminar una migración, o cuando un slot lógico con active = false está acumulando WAL en el publicador.","caution":"Un slot lógico huérfano retiene tanto WAL como catalog_xmin: el disco se llena y los catálogos del sistema sufren bloat. Sin embargo, eliminar el slot impide que esa suscripción continúe; antes de eliminarlo, verifique que ningún suscriptor (incluido uno en otro servidor) usa este slot. Un slot activo no se puede eliminar; primero hay que terminar el walsender conectado.","since":"PostgreSQL 10","tags":["logical replication","slot cleanup","drop subscription","orphaned slot","catalog_xmin"]},{"id":"pg-upgrade-link-runbook","task":"Upgrade de versión mayor con pg_upgrade --link (runbook de principio a fin)","category":"Upgrade","command":"# As the postgres user, in a writable directory; example 16 -> 17\nexport OLD_BIN=/usr/pgsql-16/bin NEW_BIN=/usr/pgsql-17/bin\nexport OLD_DATA=/var/lib/pgsql/16/data NEW_DATA=/var/lib/pgsql/17/data\n# 0) initdb the new cluster with the same encoding/locale and checksum setting as the old one (-k if the old cluster has checksums)\n# 1) Compatibility check (can run while the old cluster is up, changes no data):\n$NEW_BIN/pg_upgrade --old-bindir=$OLD_BIN --new-bindir=$NEW_BIN --old-datadir=$OLD_DATA --new-datadir=$NEW_DATA --check\n# 2) Stop the old cluster and upgrade with hard links:\n$OLD_BIN/pg_ctl -D $OLD_DATA stop -m fast\n$NEW_BIN/pg_upgrade --old-bindir=$OLD_BIN --new-bindir=$NEW_BIN --old-datadir=$OLD_DATA --new-datadir=$NEW_DATA --link --jobs=4\n# 3) Carry postgresql.conf/pg_hba.conf customizations over to the new cluster, start it:\n$NEW_BIN/pg_ctl -D $NEW_DATA -l /tmp/pg17_start.log start\n# 4) Gather statistics, run generated scripts if any:\n$NEW_BIN/vacuumdb --all --analyze-in-stages\n# psql -X -f update_extensions.sql   (if pg_upgrade generated it)\n# 5) After validation and a fresh backup, delete the old cluster:\n./delete_old_cluster.sh","what":"Primero valida la compatibilidad con --check y luego realiza el upgrade mayor en minutos, independientemente del tamaño de los datos, creando hard links de los archivos de datos del clúster antiguo hacia la nueva versión en lugar de copiarlos; después recopila estadísticas por etapas y elimina el clúster antiguo.","when":"Para pasar in situ un clúster grande a una nueva versión mayor con una caída aceptablemente breve (minutos).","caution":"Con --link, una vez arrancado el clúster nuevo, el antiguo ya no se puede usar con seguridad; la única vuelta atrás es un backup tomado antes del upgrade. Los directorios de datos antiguo y nuevo deben estar en el mismo sistema de archivos. Los planes pueden ser malos en el clúster nuevo hasta que termine analyze. Si usa pgBackRest, ejecute stanza-upgrade y tome de inmediato un backup full nuevo; los backups de la versión antigua no se pueden restaurar en el clúster nuevo.","note":"Los standbys físicos no se actualizan con pg_upgrade: tras actualizar el primario, actualícelos con el método rsync --hard-links --size-only de la documentación o reconstrúyalos. Los slots lógicos no se migran antes de PG17 (sí se migran cuando el clúster antiguo es PG17+); las estadísticas del planificador no se migran antes de PG18.","tags":["pg_upgrade","major upgrade","version upgrade","link","check"]},{"id":"logical-upgrade-near-zero-downtime","task":"Upgrade de versión mayor casi sin tiempo de inactividad con replicación lógica","category":"Upgrade","command":"# 0) On the old version wal_level = logical (restart); every table needs a PRIMARY KEY or REPLICA IDENTITY\n# 1) Move roles and schema to the new version (a 'role postgres already exists' error is expected):\npg_dumpall -h <old_host> --globals-only | psql -h <new_host> -X -d postgres\npg_dump -h <old_host> --schema-only --create -d <db> | psql -h <new_host> -X -d postgres\n# 2) Publication on the old version, subscription on the new version (initial copy + continuous stream):\npsql -h <old_host> -d <db> -X -c \"CREATE PUBLICATION upg_pub FOR ALL TABLES;\"\npsql -h <new_host> -d <db> -X -c \"CREATE SUBSCRIPTION upg_sub CONNECTION 'host=<old_host> dbname=<db> user=<repl_user>' PUBLICATION upg_pub;\"\n# 3) Wait until all tables are 'r' (ready) and lag is ~0:\npsql -h <new_host> -d <db> -X -c \"SELECT srsubstate, count(*) FROM pg_subscription_rel GROUP BY 1;\"\npsql -h <old_host> -d <db> -X -c \"SELECT slot_name, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) AS lag FROM pg_replication_slots WHERE slot_name = 'upg_sub';\"\n# 4) Cutover: stop application writes, confirm lag = 0, synchronize sequences (logical-sync-sequences)\n# 5) Drop the subscription (the remote slot is dropped too), point the application to the new server:\npsql -h <new_host> -d <db> -X -c \"DROP SUBSCRIPTION upg_sub;\"","what":"Construye un clúster vacío en la nueva versión, traslada el esquema y transmite continuamente los datos desde la versión antigua con replicación lógica; solo el paso final de cambio (detener las escrituras, sincronizar las secuencias, redirigir) provoca una breve caída. Una segunda replicación en sentido inverso puede mantener abierta una vía de rollback.","when":"Cuando la caída que requiere pg_upgrade es inaceptable, al cambiar a la vez a nuevo hardware/SO, o al saltar varias versiones mayores.","caution":"El DDL, las secuencias, los large objects y el contenido de las materialized views no se replican; congele el esquema durante la migración. En tablas sin primary key/replica identity, UPDATE/DELETE fallan en el publicador. En bases de datos grandes la copia inicial tarda mucho y retiene WAL y catalog_xmin en el servidor antiguo. Cada base de datos necesita su propia publicación/suscripción. Compare el número de filas/checksums antes del cambio final.","since":"PostgreSQL 10","note":"pg_createsubscriber, añadido en PostgreSQL 17, convierte un standby físico en suscriptor lógico sin copia inicial, eliminando el tiempo de copia inicial en bases de datos grandes.","tags":["upgrade","logical replication","zero downtime migration","major upgrade","cutover"]},{"id":"pgbouncer-show-pools-stats","task":"Mostrar pools, estadísticas y conexiones de PgBouncer (SHOW POOLS/STATS/CLIENTS/SERVERS)","category":"PgBouncer","command":"# Connect to the PgBouncer admin console (user must be in admin_users/stats_users):\npsql -h 127.0.0.1 -p 6432 -U pgbouncer -d pgbouncer -X\nSHOW POOLS;    -- if cl_waiting > 0 and maxwait grows, the pool is saturated\nSHOW STATS;    -- per-database total/average query, transaction and wait times\nSHOW CLIENTS;  -- client connections (state, source address, linked server connection)\nSHOW SERVERS;  -- server connections opened to PostgreSQL\nSHOW CONFIG;","what":"Para cada pool de base de datos/usuario, muestra los clientes activos y en espera (cl_active, cl_waiting), las conexiones de servidor activas/inactivas (sv_active, sv_idle), el mayor tiempo de espera (maxwait) y los contadores de consultas/transacciones.","when":"Cuando la aplicación informa de fallos/timeouts de conexión, para dimensionar el pool y para ver las conexiones activas antes de un mantenimiento.","caution":"La consola de administración no acepta SQL; solo ejecuta comandos de PgBouncer. Si cl_waiting se mantiene alto, revise primero en el lado del servidor si hay transacciones largas o bloqueos; subir a ciegas default_pool_size empuja a PostgreSQL hacia su límite de max_connections y a presión de memoria. Los valores de SHOW STATS son acumulados; los promedios se refieren al intervalo stats_period.","untested":true,"tags":["pgbouncer","show pools","connection pooling","statistics","connection pool"]},{"id":"pgbouncer-pause-resume-reload","task":"Retener conexiones durante el mantenimiento con PgBouncer (PAUSE/RESUME) y recargar la configuración (RELOAD)","category":"PgBouncer","command":"psql -h 127.0.0.1 -p 6432 -U pgbouncer -d pgbouncer -X\nRELOAD;            -- apply pgbouncer.ini and auth_file changes without dropping connections\nPAUSE <db>;        -- closes server connections once running queries/transactions finish; new requests are queued\n-- ... maintenance: switchover, PostgreSQL restart, etc. ...\nRESUME <db>;\n-- If the host in [databases] was changed (after RELOAD), gradually refresh server connections:\nRECONNECT <db>;","what":"PAUSE drena las conexiones del lado de PostgreSQL sin cortar las conexiones de los clientes y pone en cola las consultas nuevas; RESUME deja fluir de nuevo la cola. Así, un reinicio breve del primario o un switchover se percibe en la aplicación como un breve retraso en lugar de errores.","when":"Durante ventanas de mantenimiento cortas (switchover, reinicio por versión menor, reinicio por parámetros) y al aplicar cambios en pgbouncer.ini/userlist.","caution":"PAUSE solo vuelve cuando terminan todas las transacciones activas; una consulta larga retiene PAUSE (termine esa sesión en PostgreSQL si es necesario). Los clientes en espera reciben un error cuando vence query_wait_timeout (120 s por defecto); termine el mantenimiento dentro de ese tiempo. Olvidar RESUME después de PAUSE es una caída total. Algunos ajustes (listen_addr, listen_port, etc.) no cambian con RELOAD y requieren reinicio.","untested":true,"tags":["pgbouncer","pause","resume","reload","maintenance"]},{"id":"pgbouncer-pool-mode","task":"Elección de pool_mode en PgBouncer y dimensionamiento básico","category":"PgBouncer","command":"; /etc/pgbouncer/pgbouncer.ini\n[databases]\nappdb = host=<primary_host> port=5432 dbname=appdb pool_mode=transaction\nreportdb = host=<primary_host> port=5432 dbname=appdb pool_mode=session\n\n[pgbouncer]\nlisten_addr = 0.0.0.0\nlisten_port = 6432\nauth_type = scram-sha-256\nauth_file = /etc/pgbouncer/userlist.txt\npool_mode = transaction\nmax_client_conn = 5000\ndefault_pool_size = 20\nreserve_pool_size = 5\nmax_prepared_statements = 200\nquery_wait_timeout = 120","what":"El modo session vincula un cliente a una conexión de servidor durante toda la vida de su conexión; el modo transaction asigna una conexión de servidor solo mientras dura una transacción y atiende a miles de clientes con decenas de conexiones de PostgreSQL; el modo statement devuelve la conexión tras cada consulta y no permite transacciones de varias sentencias. pool_mode también se puede sobrescribir por base de datos.","when":"Modo transaction para aplicaciones OLTP/microservicios que abren muchas conexiones de corta duración; modo session para trabajos que dependen del estado de la sesión (informes, migraciones, servicios que usan LISTEN).","caution":"En el modo transaction el estado de la sesión no se conserva entre transacciones: SET (salvo SET LOCAL), los advisory locks a nivel de sesión, LISTEN, los cursores WITH HOLD, las tablas temporales entre transacciones y el PREPARE a nivel de SQL no son fiables. Los prepared statements a nivel de protocolo solo están soportados con PgBouncer 1.21+ y max_prepared_statements > 0 (200 por defecto desde 1.24, 0 antes). default_pool_size multiplicado por el número de pares (base de datos, usuario) no debe superar el max_connections de PostgreSQL (menos el margen de superuser_reserved_connections).","untested":true,"tags":["pgbouncer","pool_mode","transaction mode","connection pooling","sizing"]}]},"recipes/postgres-ops.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"recipes":[{"id":"pgss-top-queries","task":"Encontrar las consultas más costosas (pg_stat_statements: tiempo total y medio)","category":"Rendimiento","command":"-- By total time: queries consuming the most server time\nSELECT queryid,\n       calls,\n       round(total_exec_time::numeric, 1) AS total_ms,\n       round(mean_exec_time::numeric, 2) AS mean_ms,\n       round(stddev_exec_time::numeric, 2) AS stddev_ms,\n       round((100 * total_exec_time / sum(total_exec_time) OVER ())::numeric, 1) AS pct_total,\n       rows / NULLIF(calls, 0) AS rows_per_call,\n       left(regexp_replace(query, '\\s+', ' ', 'g'), 120) AS query\nFROM pg_stat_statements\nWHERE dbid = (SELECT oid FROM pg_database WHERE datname = current_database())\nORDER BY total_exec_time DESC\nLIMIT 20;\n\n-- By mean time per call: individually slow queries\nSELECT queryid,\n       calls,\n       round(mean_exec_time::numeric, 2) AS mean_ms,\n       round(max_exec_time::numeric, 2) AS max_ms,\n       round(mean_plan_time::numeric, 2) AS mean_plan_ms,\n       left(regexp_replace(query, '\\s+', ' ', 'g'), 120) AS query\nFROM pg_stat_statements\nWHERE calls >= 50\nORDER BY mean_exec_time DESC\nLIMIT 20;","what":"Lista las consultas normalizadas que consumen más tiempo del servidor en la base de datos actual, con su porcentaje del total; la segunda consulta lista las más lentas por llamada.","when":"Primera parada cuando sube la carga de CPU/IO o los usuarios dicen que 'la base de datos va lenta'; para decidir por dónde empezar el tuning.","caution":"pg_stat_statements no recopila nada hasta que se añade a shared_preload_libraries, se reinicia el servidor y se ejecuta CREATE EXTENSION en la base de datos. Los valores son acumulados desde el último reset. Un stddev_ms alto en relación con mean_ms indica que el plan o la distribución de los datos varían entre llamadas. mean_plan_time solo se rellena con pg_stat_statements.track_planning = on (off por defecto; habilitarlo añade sobrecarga). Ver los textos de consulta de otros usuarios requiere superusuario o el rol pg_read_all_stats.","since":"PostgreSQL 13","note":"En PostgreSQL 13 las columnas total_time/mean_time se renombraron a total_exec_time/mean_exec_time y los tiempos de planificación (total_plan_time) obtuvieron columnas propias; use los nombres antiguos en PG12 y anteriores.","tags":["pg_stat_statements","slow query","most expensive query","top sql","performance","query tuning"]},{"id":"pgss-io-temp-heavy","task":"Encontrar las consultas que generan I/O de disco, archivos temporales y WAL (pg_stat_statements)","category":"Rendimiento","command":"SELECT queryid,\n       calls,\n       shared_blks_hit,\n       shared_blks_read,\n       round(100.0 * shared_blks_hit / NULLIF(shared_blks_hit + shared_blks_read, 0), 1) AS hit_pct,\n       pg_size_pretty(temp_blks_written * current_setting('block_size')::bigint) AS temp_written,\n       pg_size_pretty(wal_bytes) AS wal,\n       left(regexp_replace(query, '\\s+', ' ', 'g'), 120) AS query\nFROM pg_stat_statements\nORDER BY shared_blks_read + temp_blks_read + temp_blks_written DESC\nLIMIT 20;","what":"Muestra las consultas que más bloques leen desde fuera de shared buffers, más escriben en archivos temporales (sorts/hashes que no caben en work_mem) y más WAL generan, junto con su cache hit ratio.","when":"Para encontrar la consulta responsable cuando sube la latencia de lectura de disco, saltan alertas de archivos temporales (log_temp_files) o el volumen de WAL crece de repente.","caution":"shared_blks_read también cuenta las lecturas servidas desde la page cache del SO; el tiempo real de disco requiere track_io_timing = on (mida antes su sobrecarga con pg_test_timing). Si temp_written es alto, la solución suele ser subir work_mem por rol/sesión o corregir la consulta/índice; subir work_mem globalmente aumenta el riesgo de memoria.","since":"PostgreSQL 13","note":"Las columnas wal_bytes/wal_records llegaron en PG13. En PG17 blk_read_time/blk_write_time se renombraron a shared_blk_read_time/shared_blk_write_time y se añadió local_blk_*_time.","tags":["pg_stat_statements","io","temp files","temp_blks_written","work_mem","wal"]},{"id":"pgss-reset","task":"Restablecer las estadísticas de pg_stat_statements (todas o una sola consulta)","category":"Rendimiento","command":"-- Last reset time and number of entries evicted because the table was full (dealloc)\nSELECT dealloc, stats_reset FROM pg_stat_statements_info;\n\n-- Reset a single query only (userid, dbid, queryid; 0 = all)\nSELECT pg_stat_statements_reset(0, 0, 5927525816675996975);\n\n-- Reset all statistics\nSELECT pg_stat_statements_reset();","what":"Restablece los contadores de pg_stat_statements por completo o por usuario/base de datos/queryid; pg_stat_statements_info muestra la hora del último reset y cuántas entradas se descartaron por falta de espacio.","when":"Para empezar una medición limpia antes de un cambio de tuning (índice, parámetro, despliegue) o para medir de forma aislada el nuevo plan de una sola consulta.","caution":"Un reset no se puede deshacer; las herramientas de monitoreo/APM que dependen de contadores acumulados mostrarán un hueco, así que tome antes un snapshot (vea la receta de regresión de planes). Si dealloc sigue creciendo, pg_stat_statements.max es demasiado pequeño (cambiarlo requiere reinicio). Por defecto solo los superusuarios pueden ejecutarlo; concédalo con GRANT EXECUTE ON FUNCTION.","since":"PostgreSQL 14","note":"El reset parametrizado (userid, dbid, queryid) llegó en PG12 y la vista pg_stat_statements_info en PG14. PG17 añadió un cuarto parámetro, minmax_only: pg_stat_statements_reset(0, 0, 0, true) restablece solo los tiempos mín./máx.; las columnas stats_since y minmax_stats_since también son de PG17.","tags":["pg_stat_statements","reset","clear statistics","queryid","statistics","baseline"]},{"id":"auto-explain-setup","task":"Registrar los planes de ejecución de las consultas lentas con auto_explain","category":"Rendimiento","command":"-- 1) Per session: effective immediately, no restart (superuser)\nLOAD 'auto_explain';\nSET auto_explain.log_min_duration = '500ms';\nSET auto_explain.log_analyze = on;\nSET auto_explain.log_buffers = on;\nSET auto_explain.log_timing = off;\nSET auto_explain.log_nested_statements = on;\n\n-- 2) Persistent: loaded by new sessions, a reload is enough.\n--    The module must be LOADed in this session before ALTER SYSTEM accepts auto_explain.* parameters.\nLOAD 'auto_explain';\nALTER SYSTEM SET session_preload_libraries = 'auto_explain';\nALTER SYSTEM SET auto_explain.log_min_duration = '2s';\nALTER SYSTEM SET auto_explain.log_analyze = on;\nALTER SYSTEM SET auto_explain.log_buffers = on;\nALTER SYSTEM SET auto_explain.log_timing = off;\nALTER SYSTEM SET auto_explain.sample_rate = 0.1;\nSELECT pg_reload_conf();","what":"Escribe en el log del servidor el plan de ejecución de las consultas que superan el umbral (con número real de filas y datos de buffers cuando log_analyze está activado); captura el plan generado con los parámetros reales de la aplicación.","when":"Cuando una consulta solo es lenta desde la aplicación o con parámetros concretos y no se puede reproducir con un EXPLAIN manual; para detectar cambios de plan intermitentes.","caution":"log_analyze = on añade sobrecarga de instrumentación a cada consulta muestreada, incluidas las que están por debajo del umbral; log_timing = off y sample_rate < 1 la reducen. log_min_duration = 0 inunda el log en producción. ALTER SYSTEM SET session_preload_libraries sobrescribe el valor existente; compruebe antes SHOW session_preload_libraries. session_preload_libraries no afecta a las sesiones existentes; cargarlo en todos los procesos requiere shared_preload_libraries más un reinicio.","note":"auto_explain es un módulo contrib y no necesita CREATE EXTENSION. También están disponibles las opciones log_wal (PG13+) y log_settings (PG12+).","tags":["auto_explain","execution plan","slow query","log","plan capture","explain"]},{"id":"explain-analyze-buffers-wal","task":"Leer un plan en profundidad con EXPLAIN (ANALYZE, BUFFERS, SETTINGS, WAL)","category":"Rendimiento","command":"-- ANALYZE really executes the statement: run DML inside a transaction and roll it back\nBEGIN;\nEXPLAIN (ANALYZE, BUFFERS, SETTINGS, WAL, VERBOSE)\nUPDATE customer SET balance = balance + 1 WHERE city = 'Ankara';\nROLLBACK;\n\n-- For SELECT\nEXPLAIN (ANALYZE, BUFFERS, SETTINGS)\nSELECT name FROM customer ORDER BY name DESC LIMIT 10 OFFSET 1000;","what":"Devuelve el plan con estadísticas reales de ejecución: BUFFERS muestra los bloques shared hit/read/dirtied y temp por nodo, SETTINGS muestra los parámetros no predeterminados que afectan a la planificación, WAL muestra los registros/bytes de WAL generados.","when":"Para analizar la diferencia entre filas estimadas y reales, los sorts/hashes que se vuelcan a disco (Sort Method: external merge, temp read/written) y el costo en WAL del DML.","caution":"ANALYZE ejecuta realmente la sentencia; ejecute siempre el DML dentro de BEGIN/ROLLBACK (los triggers y los incrementos de secuencias siguen ocurriendo). Consejos de lectura: una diferencia de 10x o más entre 'rows' estimadas y 'actual rows' apunta a un problema de estadísticas; en los nodos dentro de bucles, el tiempo y las filas son por iteración, multiplique por loops para obtener el total; muchos 'Heap Fetches' en un Index Only Scan significan que el visibility map está desactualizado, y 'Workers Planned' > 'Workers Launched' significa que el pool de workers está agotado. Use TIMING OFF donde la sobrecarga de temporización sea alta; COSTS OFF también oculta la sección JIT.","since":"PostgreSQL 13","note":"La opción SETTINGS llegó en PG12, WAL en PG13. PG16 añadió GENERIC_PLAN, PG17 añadió SERIALIZE y MEMORY; en PG18 BUFFERS está activado por defecto con ANALYZE.","tags":["explain analyze","buffers","execution plan","reading plans","wal","performance"]},{"id":"plan-regression-queryid","task":"Detectar regresiones de plan por queryid (comparación de snapshots de pg_stat_statements)","category":"Rendimiento","command":"-- 1) Take a reference snapshot (before deploy/upgrade/ANALYZE)\nCREATE TABLE IF NOT EXISTS pgss_snapshot AS\n  SELECT now() AS snap_time, userid, dbid, queryid, calls, total_exec_time,\n         shared_blks_hit, shared_blks_read, left(query, 200) AS query\n  FROM pg_stat_statements WITH NO DATA;\nINSERT INTO pgss_snapshot\n  SELECT now(), userid, dbid, queryid, calls, total_exec_time,\n         shared_blks_hit, shared_blks_read, left(query, 200)\n  FROM pg_stat_statements;\n\n-- 2) Later: queries whose time per call grew the most since the snapshot\nWITH base AS (\n  SELECT DISTINCT ON (userid, dbid, queryid) *\n  FROM pgss_snapshot\n  ORDER BY userid, dbid, queryid, snap_time DESC\n)\nSELECT s.queryid,\n       s.calls - b.calls AS new_calls,\n       round((b.total_exec_time / b.calls)::numeric, 2) AS old_mean_ms,\n       round(((s.total_exec_time - b.total_exec_time) / (s.calls - b.calls))::numeric, 2) AS new_mean_ms,\n       round((b.shared_blks_hit + b.shared_blks_read)::numeric / b.calls, 0) AS old_blks_per_call,\n       round((s.shared_blks_hit + s.shared_blks_read - b.shared_blks_hit - b.shared_blks_read)::numeric\n             / (s.calls - b.calls), 0) AS new_blks_per_call,\n       left(regexp_replace(s.query, '\\s+', ' ', 'g'), 100) AS query\nFROM pg_stat_statements s\nJOIN base b USING (userid, dbid, queryid)\nWHERE s.calls - b.calls >= 10\n  AND b.calls > 0\nORDER BY ((s.total_exec_time - b.total_exec_time) / (s.calls - b.calls))\n         / NULLIF(b.total_exec_time / b.calls, 0) DESC NULLS LAST\nLIMIT 20;\n\n-- 3) Is the suspicious queryid running right now?\nSELECT pid, state, now() - query_start AS runtime, left(query, 100) AS query\nFROM pg_stat_activity\nWHERE query_id = -4094682686355101727;","what":"Guarda en una tabla los contadores acumulados de pg_stat_statements y los compara por queryid con el estado posterior; muestra, junto a los valores antiguos, el tiempo medio y los buffers por llamada de las llamadas hechas después del snapshot.","when":"Para encontrar la consulta cuyo plan cambió tras un despliegue, un upgrade mayor, una actualización de estadísticas o un cambio de parámetros ('ayer iba rápido, hoy va lento').","caution":"Si se ejecuta pg_stat_statements_reset() entre el snapshot y la comparación, o se descartan entradas por falta de espacio (dealloc), los deltas no tienen sentido; revise pg_stat_statements_info.stats_reset. Un aumento de los buffers por llamada es una señal de cambio de plan más fiable que un aumento del tiempo, porque no depende del cache. Para confirmar el plan, haga EXPLAIN de la consulta con parámetros reales o use auto_explain; PostgreSQL no tiene fijación de planes integrada (se requiere la extensión pg_hint_plan). Elimine la tabla de snapshot al terminar.","since":"PostgreSQL 14","note":"pg_stat_activity.query_id y el parámetro compute_query_id llegaron en PG14 (por defecto auto: se calcula cuando pg_stat_statements está cargado). La línea 'Query Identifier' de la salida de EXPLAIN VERBOSE muestra el mismo valor. Los pasos 1 y 2 también funcionan en PG13.","tags":["plan regression","queryid","pg_stat_statements","snapshot","plan change","performance"]},{"id":"parallel-query-usage","task":"Comprobar la capacidad de consultas paralelas y el uso actual de workers","category":"Rendimiento","command":"-- Parallel worker pool settings\nSELECT name, current_setting(name) AS value\nFROM pg_settings\nWHERE name IN ('max_worker_processes', 'max_parallel_workers', 'max_parallel_workers_per_gather',\n               'max_parallel_maintenance_workers', 'parallel_setup_cost', 'parallel_tuple_cost',\n               'min_parallel_table_scan_size', 'min_parallel_index_scan_size')\nORDER BY name;\n\n-- Queries currently running in parallel and their worker counts\nSELECT a.pid AS leader_pid,\n       count(w.pid) AS workers_running,\n       current_setting('max_parallel_workers')::int AS max_parallel_workers,\n       now() - a.query_start AS runtime,\n       left(a.query, 80) AS query\nFROM pg_stat_activity a\nJOIN pg_stat_activity w ON w.leader_pid = a.pid AND w.pid <> a.pid\nGROUP BY a.pid, a.query_start, a.query\nORDER BY workers_running DESC;\n\n-- Pin / reset the planned worker count for a single table\nALTER TABLE orders SET (parallel_workers = 4);\nALTER TABLE orders RESET (parallel_workers);","what":"Muestra los límites de las consultas paralelas y cuántos parallel workers usa en este momento cada proceso leader; ilustra el ajuste del número de workers por tabla.","when":"Cuando las consultas analíticas son más lentas de lo esperado, el plan muestra 'Workers Planned' > 'Workers Launched' o hay núcleos de CPU ociosos.","caution":"max_parallel_workers está limitado por max_worker_processes (requiere reinicio), y ese pool se comparte con los workers de replicación lógica y otros background workers. En OLTP, un max_parallel_workers_per_gather alto satura la CPU con concurrencia; además, cada worker usa su propio work_mem. Las consultas que escriben datos o bloquean filas (salvo CREATE TABLE AS / SELECT INTO), los cursores y las consultas que llaman a funciones PARALLEL UNSAFE no obtienen planes paralelos. ALTER TABLE ... SET (parallel_workers) toma un breve ShareUpdateExclusiveLock.","since":"PostgreSQL 13","note":"pg_stat_activity.leader_pid llegó en PG13; antes, los parallel workers solo aparecen como backend_type = 'parallel worker' y no se pueden vincular a su leader.","tags":["parallel query","parallel worker","leader_pid","max_parallel_workers","gather","cpu"]},{"id":"jit-impact-check","task":"Medir el impacto de la compilación JIT en el tiempo de consulta","category":"Rendimiento","command":"-- Share of JIT time in total execution time (PG15+)\nSELECT queryid,\n       calls,\n       jit_functions,\n       round((jit_generation_time + jit_inlining_time + jit_optimization_time + jit_emission_time)::numeric, 1) AS jit_ms,\n       round(total_exec_time::numeric, 1) AS total_ms,\n       round((100 * (jit_generation_time + jit_inlining_time + jit_optimization_time + jit_emission_time)\n             / NULLIF(total_exec_time, 0))::numeric, 1) AS jit_pct,\n       left(regexp_replace(query, '\\s+', ' ', 'g'), 100) AS query\nFROM pg_stat_statements\nWHERE jit_functions > 0\nORDER BY jit_pct DESC NULLS LAST\nLIMIT 20;\n\n-- Compare on a single query: check the \"JIT:\" section at the end of the output and Execution Time\nSET jit = on;\nEXPLAIN (ANALYZE) SELECT city, sum(balance) FROM customer GROUP BY city;\nSET jit = off;\nEXPLAIN (ANALYZE) SELECT city, sum(balance) FROM customer GROUP BY city;\nRESET jit;","what":"Muestra, por consulta, la proporción del tiempo de compilación JIT (generation, inlining, optimization, emission) en el tiempo de ejecución; mide el efecto neto ejecutando la misma consulta con JIT activado y desactivado.","when":"Cuando consultas OLTP o de informes cortas muestran retrasos inexplicables de decenas a cientos de ms; sobre todo en tablas muy particionadas donde estimaciones de costo infladas disparan JIT.","caution":"JIT solo se activa cuando el costo del plan supera jit_above_cost (100000 por defecto); con estimaciones de costo infladas, la compilación puede tardar más que la ejecución en consultas cortas. En lugar de deshabilitarlo globalmente, considere primero ALTER ROLE ... SET jit = off para el rol/base de datos afectado o subir jit_above_cost. EXPLAIN (ANALYZE, COSTS OFF) no muestra la sección JIT. Si PostgreSQL se compiló sin soporte de LLVM, JIT nunca se usa; compruébelo con SELECT pg_jit_available();","since":"PostgreSQL 15","note":"Las columnas jit_* se añadieron a pg_stat_statements en PG15; PG17 añadió jit_deform_count/jit_deform_time. JIT se introdujo en PG11 y está activado por defecto desde PG12.","tags":["jit","llvm","jit_above_cost","pg_stat_statements","compilation time","performance"]},{"id":"blocking-tree","task":"Construir el árbol de bloqueos (encontrar el bloqueador raíz)","category":"Sesiones","command":"WITH RECURSIVE tree AS (\n  SELECT a.pid, a.pid AS root_pid, 0 AS level, ARRAY[a.pid] AS path\n  FROM pg_stat_activity a\n  WHERE cardinality(pg_blocking_pids(a.pid)) = 0\n    AND EXISTS (SELECT 1 FROM pg_stat_activity w WHERE a.pid = ANY (pg_blocking_pids(w.pid)))\n  UNION ALL\n  SELECT w.pid, t.root_pid, t.level + 1, t.path || w.pid\n  FROM tree t\n  JOIN pg_stat_activity w ON t.pid = ANY (pg_blocking_pids(w.pid))\n  WHERE w.pid <> ALL (t.path)\n), node AS (\n  SELECT DISTINCT ON (pid) pid, root_pid, level\n  FROM tree\n  ORDER BY pid, level DESC\n)\nSELECT repeat('  ', n.level) || n.pid AS pid_tree,\n       n.root_pid,\n       pg_blocking_pids(n.pid) AS blocked_by,\n       a.usename,\n       a.state,\n       a.wait_event_type || ':' || a.wait_event AS wait,\n       now() - a.xact_start AS xact_age,\n       left(regexp_replace(a.query, '\\s+', ' ', 'g'), 80) AS query\nFROM node n\nJOIN pg_stat_activity a USING (pid)\nORDER BY n.root_pid, n.level, n.pid;","what":"Sigue recursivamente la cadena de esperas con pg_blocking_pids(); partiendo del bloqueador raíz (que no espera a nadie), muestra cada sesión en espera como un árbol indentado con los PID que la bloquean directamente y la antigüedad de su transacción.","when":"Cuando la aplicación sufre timeouts masivos o se acumulan colas; para encontrar, entre decenas de sesiones en espera, la única sesión raíz sobre la que hay que intervenir.","caution":"La raíz suele ser una sesión 'idle in transaction' olvidada que retiene el bloqueo; la columna query muestra la ÚLTIMA sentencia de esa sesión, no la que tomó el bloqueo. Un DDL que espera ACCESS EXCLUSIVE también bloquea los SELECT simples encolados detrás de él y aparece como nodo intermedio. pg_blocking_pids() bloquea brevemente el lock manager; no ejecute esta consulta en un bucle cerrado. Una sesión alcanzable por varias ramas se muestra una sola vez, en su nivel más profundo.","since":"PostgreSQL 9.6","tags":["blocking","lock tree","lock chain","pg_blocking_pids","blocking session","lock"]},{"id":"lock-wait-detail","task":"Inspeccionar en detalle las esperas de bloqueo por objeto y modo de bloqueo (pg_locks)","category":"Sesiones","command":"SELECT DISTINCT\n       w.pid AS waiting_pid,\n       w.locktype,\n       coalesce(w.relation::regclass::text, 'xid ' || w.transactionid, w.virtualxid, w.objid::text) AS target,\n       w.mode AS wanted_mode,\n       b.pid AS blocker_pid,\n       b.mode AS blocker_mode,\n       b.granted AS blocker_holds,\n       now() - wa.state_change AS waiting_for,\n       left(wa.query, 60) AS waiting_query,\n       left(ba.query, 60) AS blocker_query\nFROM pg_locks w\nJOIN pg_locks b\n  ON b.pid = ANY (pg_blocking_pids(w.pid))\n AND b.locktype = w.locktype\n AND b.database IS NOT DISTINCT FROM w.database\n AND b.relation IS NOT DISTINCT FROM w.relation\n AND b.page IS NOT DISTINCT FROM w.page\n AND b.tuple IS NOT DISTINCT FROM w.tuple\n AND b.virtualxid IS NOT DISTINCT FROM w.virtualxid\n AND b.transactionid IS NOT DISTINCT FROM w.transactionid\n AND b.classid IS NOT DISTINCT FROM w.classid\n AND b.objid IS NOT DISTINCT FROM w.objid\n AND b.objsubid IS NOT DISTINCT FROM w.objsubid\nJOIN pg_stat_activity wa ON wa.pid = w.pid\nJOIN pg_stat_activity ba ON ba.pid = b.pid\nWHERE NOT w.granted\nORDER BY waiting_for DESC, waiting_pid;","what":"Para cada solicitud de bloqueo no concedida (granted = false), muestra el objeto de destino (tabla, transaction id, etc.), el modo solicitado, el PID bloqueante y su modo, si el bloqueador retiene realmente el bloqueo y el tiempo de espera aproximado.","when":"Para entender qué conflicto de tabla y modo de bloqueo causa el bloqueo; p. ej., un DDL que espera AccessExclusiveLock, o un bloqueo de fila que aparece como espera transactionid.","caution":"Como la correspondencia usa pg_blocking_pids(), solo se listan las solicitudes realmente en conflicto. blocker_holds = false significa que el bloqueador aún no retiene el bloqueo pero está en la cola por delante con un modo en conflicto (lock queue). locktype = 'transactionid' es un conflicto a nivel de fila: el que espera aguarda a que termine la transacción que modificó la fila; la fila en sí no es visible en pg_locks. pg_locks puede contener miles de filas en sistemas grandes; no lo ejecute con frecuencia. waiting_for es una aproximación basada en state_change.","since":"PostgreSQL 9.6","tags":["pg_locks","lock wait","lock waiting","accessexclusivelock","transactionid","lock"]},{"id":"long-idle-transactions","task":"Encontrar transacciones de larga duración e 'idle in transaction'","category":"Sesiones","command":"SELECT pid,\n       usename,\n       datname,\n       application_name,\n       client_addr,\n       state,\n       now() - xact_start AS xact_age,\n       now() - state_change AS in_state_for,\n       backend_xid,\n       age(backend_xmin) AS xmin_age,\n       left(regexp_replace(query, '\\s+', ' ', 'g'), 100) AS last_query\nFROM pg_stat_activity\nWHERE xact_start IS NOT NULL\n  AND backend_type = 'client backend'\n  AND pid <> pg_backend_pid()\n  AND (now() - xact_start > interval '5 minutes' OR state LIKE 'idle in transaction%')\nORDER BY xact_start;","what":"Muestra las sesiones cuya transacción abierta tiene más de 5 minutos o que están inactivas dentro de una transacción; con la antigüedad de la transacción, el tiempo en el estado actual, el XID asignado y la antigüedad del xmin que retienen.","when":"Cuando crece el bloat, VACUUM informa de 'dead row versions cannot be removed yet', durante esperas de bloqueo o cuando se sospecha una fuga en el connection pool.","caution":"Las transacciones largas (sobre todo REPEATABLE READ/SERIALIZABLE o las que mantienen cursores abiertos) frenan el horizonte de limpieza de VACUUM (xmin horizon) mediante backend_xmin, causando bloat y envejecimiento de XID en toda la base de datos. 'idle in transaction (aborted)' es una sesión que tuvo un error y espera un ROLLBACK. Solución permanente: idle_in_transaction_session_timeout por rol y límites de transacción correctos en la aplicación.","tags":["long transaction","idle in transaction","xmin","bloat","session","long running"]},{"id":"terminate-idle-in-transaction","task":"Terminar de forma masiva y controlada las sesiones 'idle in transaction' largas","category":"Sesiones","command":"-- 1) Dry run first: who will be affected?\nSELECT pid, usename, application_name, client_addr,\n       now() - state_change AS idle_for, left(query, 80) AS last_query\nFROM pg_stat_activity\nWHERE state IN ('idle in transaction', 'idle in transaction (aborted)')\n  AND now() - state_change > interval '15 minutes'\n  AND backend_type = 'client backend'\n  AND pid <> pg_backend_pid();\n\n-- 2) Terminate; wait up to 5 s for each process to exit\nSELECT pid, usename, now() - state_change AS idle_for,\n       pg_terminate_backend(pid, 5000) AS terminated\nFROM pg_stat_activity\nWHERE state IN ('idle in transaction', 'idle in transaction (aborted)')\n  AND now() - state_change > interval '15 minutes'\n  AND backend_type = 'client backend'\n  AND pid <> pg_backend_pid();","what":"Primero lista las sesiones que han dejado una transacción abierta e inactiva durante un tiempo determinado y luego las termina; el parámetro timeout espera hasta que el proceso haya salido realmente (o venza el timeout).","when":"Para una intervención de emergencia cuando sesiones olvidadas que retienen bloqueos causan bloqueos, o frenan el xmin horizon y paralizan el VACUUM.","caution":"pg_cancel_backend no hace nada con una sesión 'idle in transaction' (no hay ninguna consulta en curso que cancelar); se requiere pg_terminate_backend y se revierten todos los cambios sin commit. Acote el filtro por usename/application_name; excluya las sesiones de replicación, backup y monitoreo. Los connection pools volverán a abrir las conexiones terminadas. Si vence el timeout, la función emite una advertencia y devuelve false. Como solución permanente, defina idle_in_transaction_session_timeout por rol.","since":"PostgreSQL 14","note":"pg_terminate_backend(pid, timeout) con dos argumentos llegó en PG14; antes solo existe pg_terminate_backend(pid), que vuelve justo después de enviar la señal.","tags":["pg_terminate_backend","idle in transaction","terminate session","kill session","bulk","lock"]},{"id":"role-timeouts","task":"Definir statement_timeout / lock_timeout por rol y base de datos","category":"Sesiones","command":"-- Application role: short queries, short lock waits, a limit on forgotten transactions\nALTER ROLE web_app SET statement_timeout = '30s';\nALTER ROLE web_app SET lock_timeout = '5s';\nALTER ROLE web_app SET idle_in_transaction_session_timeout = '10min';\n\n-- Let the reporting role run longer, only in this database\nALTER ROLE report_app IN DATABASE mydb SET statement_timeout = '15min';\n\n-- Show configured role/database settings\nSELECT coalesce(r.rolname, '(all)') AS role,\n       coalesce(d.datname, '(all)') AS database,\n       s.setconfig\nFROM pg_db_role_setting s\nLEFT JOIN pg_roles r ON r.oid = s.setrole\nLEFT JOIN pg_database d ON d.oid = s.setdatabase\nORDER BY 1, 2;\n\n-- Revert\nALTER ROLE web_app RESET lock_timeout;","what":"Hace persistentes los parámetros de timeout a nivel de rol (y opcionalmente de base de datos), de modo que cada carga de trabajo se ejecute con sus propios límites sin tocar el ajuste global.","when":"Para evitar que consultas desbocadas agoten los recursos, que las colas de DDL/bloqueos congelen la aplicación y que las transacciones olvidadas generen bloat.","caution":"Los ajustes solo se aplican a las sesiones NUEVAS; recicle las conexiones existentes del pool. Los usuarios pueden sobrescribir el valor con SET en su sesión; no es una barrera de seguridad. No defina statement_timeout globalmente en postgresql.conf: también se cancelarían jobs de mantenimiento como pg_dump, VACUUM y CREATE INDEX. Precedencia: rol+base de datos > rol > base de datos > postgresql.conf.","note":"idle_in_transaction_session_timeout llegó en PG9.6, idle_session_timeout en PG14, transaction_timeout en PG17.","tags":["statement_timeout","lock_timeout","idle_in_transaction_session_timeout","alter role","query timeout","timeout"]},{"id":"ddl-lock-timeout-retry","task":"Aplicar DDL de forma segura con lock_timeout y reintentos (evitar colas de bloqueo)","category":"Sesiones","command":"-- Single attempt: give up if the lock is not acquired within 3 s instead of queueing and blocking traffic\nSET lock_timeout = '3s';\nALTER TABLE customer ADD COLUMN IF NOT EXISTS segment text;\nRESET lock_timeout;\n\n-- Automatic retry: up to 5 attempts, 3 s apart\nDO $$\nDECLARE\n  attempt int := 0;\nBEGIN\n  LOOP\n    attempt := attempt + 1;\n    BEGIN\n      SET LOCAL lock_timeout = '2s';\n      ALTER TABLE customer ADD COLUMN IF NOT EXISTS segment text;\n      RAISE NOTICE 'OK, attempt %', attempt;\n      EXIT;\n    EXCEPTION WHEN lock_not_available THEN\n      RAISE NOTICE 'lock not acquired, attempt %', attempt;\n      IF attempt >= 5 THEN RAISE; END IF;\n      PERFORM pg_sleep(3);\n    END;\n  END LOOP;\nEND $$;","what":"Evita que un DDL que necesita ACCESS EXCLUSIVE espere indefinidamente cuando no obtiene el bloqueo, y que bloquee todas las consultas que se encolan detrás de él (lock queue); reintenta a intervalos cortos.","when":"Al aplicar en producción, sobre tablas con mucha actividad, cambios rápidos pero con bloqueos fuertes como ALTER TABLE, CREATE/DROP TRIGGER, ATTACH PARTITION o RENAME.","caution":"Sin lock_timeout, un ALTER TABLE encolado detrás de un solo SELECT largo también hace esperar a todas las lecturas/escrituras posteriores y puede agotar el connection pool en minutos. lock_timeout solo limita la espera del bloqueo; no limita el tiempo de ejecución de un DDL que reescribe la tabla (p. ej., cambiar el tipo de una columna). El bloque DO es una sola transacción y mantiene un snapshot entre intentos; para escenarios de reintentos largos es preferible un bucle externo en shell.","tags":["lock_timeout","ddl","alter table","lock queue","retry","lock"]},{"id":"connection-usage","task":"Medir el uso de conexiones respecto a max_connections","category":"Sesiones","command":"-- Capacity: used slots and slots left for regular users\nSELECT max_conn,\n       reserved,\n       used,\n       max_conn - reserved - used AS free_for_normal_users,\n       round(100.0 * used / max_conn, 1) AS used_pct\nFROM (SELECT current_setting('max_connections')::int AS max_conn,\n             current_setting('superuser_reserved_connections')::int\n             + coalesce(current_setting('reserved_connections', true)::int, 0) AS reserved,\n             (SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'client backend') AS used) x;\n\n-- Breakdown by database, user, application and state\nSELECT datname, usename, application_name, state,\n       count(*) AS sessions,\n       max(now() - state_change) AS longest_in_state\nFROM pg_stat_activity\nWHERE backend_type = 'client backend'\nGROUP BY datname, usename, application_name, state\nORDER BY sessions DESC;\n\n-- Role and database connection limits\nSELECT rolname, rolconnlimit FROM pg_roles WHERE rolconnlimit <> -1;\nSELECT datname, datconnlimit FROM pg_database WHERE datconnlimit <> -1;","what":"Muestra los slots de conexión usados y los que quedan para usuarios normales en relación con max_connections, el desglose por base de datos/usuario/aplicación/estado y los CONNECTION LIMIT configurados.","when":"Ante errores 'too many clients already' / 'remaining connection slots are reserved', durante la planificación de capacidad o al evaluar la necesidad de un pooler.","caution":"max_connections solo limita los client backends; los autovacuum workers, background workers y walsenders usan slots aparte. Los últimos superuser_reserved_connections slots (más reserved_connections en PG16+) quedan cerrados a los usuarios normales. En lugar de subir max_connections a cientos o miles, use un pooler como PgBouncer: cada conexión es un proceso independiente, y los costos de memoria y de snapshots crecen con el número de conexiones. Muchas sesiones 'idle' indican un pool de aplicación sobredimensionado. Evite que una aplicación desbocada consuma todos los slots con ALTER ROLE ... CONNECTION LIMIT; cambiar max_connections requiere reinicio.","note":"El parámetro reserved_connections y el rol pg_use_reserved_connections llegaron en PG16; la consulta también funciona en versiones anteriores (si no existe, el parámetro cuenta como 0).","tags":["max_connections","connection count","connection","pooler","superuser_reserved_connections","session"]},{"id":"autovacuum-progress","task":"Monitorear el progreso de las operaciones autovacuum/VACUUM en curso","category":"Mantenimiento","command":"SELECT p.pid,\n       a.backend_type,\n       p.datname,\n       p.relid::regclass AS table_name,\n       p.phase,\n       pg_size_pretty(p.heap_blks_total * current_setting('block_size')::bigint) AS table_size,\n       round(100.0 * p.heap_blks_scanned / NULLIF(p.heap_blks_total, 0), 1) AS scanned_pct,\n       round(100.0 * p.heap_blks_vacuumed / NULLIF(p.heap_blks_total, 0), 1) AS vacuumed_pct,\n       p.index_vacuum_count,\n       now() - a.xact_start AS running_for,\n       a.wait_event_type || ':' || a.wait_event AS wait,\n       left(a.query, 80) AS query\nFROM pg_stat_progress_vacuum p\nJOIN pg_stat_activity a USING (pid)\nORDER BY running_for DESC;\n\n-- Busy autovacuum workers / upper limit\nSELECT count(*) FILTER (WHERE backend_type = 'autovacuum worker') AS av_workers_busy,\n       current_setting('autovacuum_max_workers') AS av_max_workers\nFROM pg_stat_activity;","what":"Muestra, para cada VACUUM en ejecución (autovacuum worker o manual), la tabla, la fase, el porcentaje del heap recorrido/limpiado, el número de pasadas de index vacuum y el wait event; además del número de autovacuum workers ocupados.","when":"Para estimar cuándo terminará un VACUUM sobre una tabla grande, si todos los autovacuum workers están ocupados (tablas esperando en la cola) y si el vacuum está esperando por throttling.","caution":"index_vacuum_count > 1 significa que la lista de dead tuples no cupo en memoria y los índices se recorrieron varias veces; aumente maintenance_work_mem/autovacuum_work_mem (antes de PG17 se usa como máximo 1GB para esto). wait = Timeout:VacuumDelay es el throttling basado en costos (autovacuum_vacuum_cost_delay/cost_limit). No termine un autovacuum cuyo texto de consulta diga 'to prevent wraparound'; se reinicia de inmediato. VACUUM FULL y CLUSTER aparecen en pg_stat_progress_cluster, no en esta vista.","since":"PostgreSQL 9.6","note":"En PG17 las columnas max_dead_tuples/num_dead_tuples cambiaron a max_dead_tuple_bytes/num_dead_item_ids y se añadieron indexes_total/indexes_processed; por eso la consulta usa columnas independientes de la versión.","tags":["autovacuum","vacuum progress","pg_stat_progress_vacuum","vacuum","maintenance","progress"]},{"id":"xid-wraparound-risk","task":"Medir el riesgo de transaction ID wraparound (antigüedad de XID por base de datos y tabla)","category":"Mantenimiento","command":"-- Per database: distance to autovacuum_freeze_max_age and the ~2^31 limit\nSELECT datname,\n       age(datfrozenxid) AS xid_age,\n       mxid_age(datminmxid) AS mxid_age,\n       round(100.0 * age(datfrozenxid) / current_setting('autovacuum_freeze_max_age')::int, 1) AS pct_of_freeze_max_age,\n       round(100.0 * age(datfrozenxid) / 2147483647, 1) AS pct_of_wraparound\nFROM pg_database\nORDER BY age(datfrozenxid) DESC;\n\n-- Oldest tables in the current database (including TOAST)\nSELECT c.oid::regclass AS table_name,\n       greatest(age(c.relfrozenxid), age(t.relfrozenxid)) AS xid_age,\n       mxid_age(c.relminmxid) AS mxid_age,\n       pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size,\n       s.last_autovacuum,\n       s.last_vacuum\nFROM pg_class c\nLEFT JOIN pg_class t ON t.oid = c.reltoastrelid\nLEFT JOIN pg_stat_all_tables s ON s.relid = c.oid\nWHERE c.relkind IN ('r', 'm')\nORDER BY 2 DESC\nLIMIT 20;","what":"Muestra la antigüedad del XID no congelado más antiguo y de multixact de cada base de datos respecto a autovacuum_freeze_max_age y al límite de wraparound; en la base de datos actual, las tablas más antiguas (incluido TOAST) con su tamaño y la hora del último vacuum.","when":"En el monitoreo rutinario (umbrales típicos: pct_of_wraparound 50% advertencia / 75% crítico), cuando el log muestra 'must be vacuumed within N transactions', y para priorizar el trabajo de freeze.","caution":"En las tablas donde pct_of_freeze_max_age supera 100, autovacuum inicia un vacuum obligatorio (anti-wraparound); es intensivo en I/O y se reinicia aunque se termine. Si la antigüedad no baja, la causa casi siempre es algo que frena el xmin horizon (transacción larga, prepared transaction, replication slot, hot_standby_feedback); corríjalo primero, o ni siquiera VACUUM FREEZE podrá bajar la antigüedad. Cerca del límite el servidor primero advierte y luego se niega a asignar XID nuevos (escrituras) cuando solo quedan unos pocos millones. Para un freeze dirigido: VACUUM (FREEZE, VERBOSE) <table>;","note":"PG14 añadió vacuum_failsafe_age / vacuum_multixact_failsafe_age (1.600 millones por defecto): cuando una tabla alcanza esa antigüedad, VACUUM pasa a un modo de emergencia que omite el cost delay y la limpieza de índices.","tags":["wraparound","xid age","datfrozenxid","relfrozenxid","freeze","multixact"]},{"id":"xmin-horizon-holders","task":"Encontrar los que retienen el xmin horizon e impiden que VACUUM elimine dead tuples","category":"Mantenimiento","command":"SELECT 'session' AS source, pid::text AS id, usename::text AS owner, state AS detail,\n       age(backend_xmin) AS xmin_age\nFROM pg_stat_activity\nWHERE backend_xmin IS NOT NULL AND pid <> pg_backend_pid()\nUNION ALL\nSELECT 'prepared_xact', gid, owner::text, prepared::text, age(transaction)\nFROM pg_prepared_xacts\nUNION ALL\nSELECT 'replication_slot', slot_name::text, slot_type, active::text,\n       greatest(age(xmin), age(catalog_xmin))\nFROM pg_replication_slots\nWHERE xmin IS NOT NULL OR catalog_xmin IS NOT NULL\nUNION ALL\nSELECT 'standby_feedback', application_name, usename::text, state, age(backend_xmin)\nFROM pg_stat_replication\nWHERE backend_xmin IS NOT NULL\nORDER BY xmin_age DESC NULLS LAST;","what":"Lista, ordenadas por antigüedad de xmin, las cuatro fuentes que pueden bloquear la limpieza de dead tuples: sesiones que mantienen snapshots, prepared transactions, replication slots y standbys que envían hot_standby_feedback.","when":"Cuando VACUUM VERBOSE informa de 'dead row versions cannot be removed yet', el bloat sigue creciendo y la antigüedad de XID no baja.","caution":"El xmin más antiguo define el horizonte de limpieza; los replication slots y el feedback de los standbys afectan a todas las bases de datos. Las prepared transactions sobreviven a los reinicios; verifique el propietario y ciérrelas con COMMIT PREPARED / ROLLBACK PREPARED '<gid>'. Eliminar un slot sin uso rompe su consumidor (standby, CDC); verifique antes el propietario. Ejecutado en un standby, solo muestra las sesiones de ese standby.","tags":["xmin horizon","vacuum blocker","dead tuple","prepared transaction","replication slot","bloat"]},{"id":"pgstattuple-bloat","task":"Medir el bloat de tablas e índices con pgstattuple","category":"Mantenimiento","command":"CREATE EXTENSION IF NOT EXISTS pgstattuple;\n\n-- Fast estimate: skips pages marked all-visible in the visibility map\nSELECT pg_size_pretty(table_len) AS table_size,\n       round(approx_tuple_percent::numeric, 1) AS live_pct,\n       round(dead_tuple_percent::numeric, 1) AS dead_pct,\n       round(approx_free_percent::numeric, 1) AS free_pct\nFROM pgstattuple_approx('orders');\n\n-- Exact measurement: reads the whole table\nSELECT pg_size_pretty(table_len) AS table_size,\n       round(dead_tuple_percent::numeric, 1) AS dead_pct,\n       round(free_percent::numeric, 1) AS free_pct,\n       pg_size_pretty(dead_tuple_len + free_space) AS reclaimable\nFROM pgstattuple('orders');\n\n-- B-tree indexes: leaf density (~90 when freshly built; below 50 is bloated)\nWITH idx AS MATERIALIZED (\n  SELECT i.indexrelid\n  FROM pg_index i\n  JOIN pg_class c ON c.oid = i.indexrelid\n  JOIN pg_am am ON am.oid = c.relam\n  WHERE am.amname = 'btree' AND i.indrelid = 'orders'::regclass\n)\nSELECT idx.indexrelid::regclass AS index_name,\n       pg_size_pretty(s.index_size) AS index_size,\n       s.avg_leaf_density,\n       s.leaf_fragmentation\nFROM idx\nCROSS JOIN LATERAL pgstatindex(idx.indexrelid) s;","what":"Muestra los porcentajes de tuplas vivas/muertas y de espacio libre de una tabla (estimación rápida y exacta) y el espacio recuperable; para índices B-tree, la densidad de hojas y la fragmentación.","when":"Cuando la estimación n_dead_tup de pg_stat_user_tables no basta; para respaldar con datos la decisión de usar pg_repack, VACUUM FULL o REINDEX.","caution":"pgstattuple y pgstatindex leen toda la relación y generan mucha I/O en tablas grandes (solo toman AccessShareLock). Use primero pgstattuple_approx. Por defecto se requiere superusuario o el rol pg_stat_scan_tables. Algo de espacio libre es normal (fillfactor, espacio para HOT updates); VACUUM lo hace reutilizable pero no reduce el archivo; recuperarlo requiere pg_repack o VACUUM FULL (¡bloqueo ACCESS EXCLUSIVE!). Reconstruya un índice con avg_leaf_density baja mediante REINDEX CONCURRENTLY.","since":"PostgreSQL 12","note":"pgstattuple es una extensión contrib; pgstattuple_approx llegó en PG9.5. MATERIALIZED en la CTE (PG12+) evita que se llame a pgstatindex, y falle, sobre índices que no son B-tree.","tags":["bloat","pgstattuple","pgstatindex","index bloat","dead tuple","free space"]},{"id":"pg-repack-online","task":"Reorganizar una tabla con bloat sin bloqueos largos (pg_repack)","category":"Mantenimiento","command":"-- Once per target database (the pg_repack package must be installed on the server)\nCREATE EXTENSION pg_repack;\n\n# Dry run: show what would be done\npg_repack -h localhost -U postgres -d mydb --table=public.orders --dry-run\n\n# Rewrite the table and its indexes: 2 parallel index builds, wait at most 60 s for locks\npg_repack -h localhost -U postgres -d mydb --table=public.orders --jobs=2 --wait-timeout=60 --no-kill-backend\n\n# Rebuild indexes only\npg_repack -h localhost -U postgres -d mydb --table=public.orders --only-indexes","what":"Construye en segundo plano una copia compactada de la tabla, captura los cambios concurrentes en una tabla de log mediante un trigger e intercambia las tablas al final bajo un breve bloqueo ACCESS EXCLUSIVE; a diferencia de VACUUM FULL, no bloquea lecturas/escrituras durante toda la operación.","when":"Cuando hay que recuperar en un sistema en producción un bloat severo que VACUUM no puede devolver (verificado con pgstattuple).","caution":"La tabla debe tener una PRIMARY KEY o un índice UNIQUE sobre columnas NOT NULL. Necesita temporalmente un espacio en disco adicional igual al de la tabla más sus índices y genera mucho WAL (lag de réplicas, volumen de archivado). Se necesitan bloqueos ACCESS EXCLUSIVE al inicio y al final; cuando vence --wait-timeout, por defecto se cancelan las consultas en conflicto y, si es necesario, se terminan sus sesiones, mientras que --no-kill-backend omite la tabla en su lugar. No ejecute DDL sobre la tabla de destino mientras se ejecuta. Las versiones del cliente y de la extensión deben coincidir. Si se interrumpe, limpie los restos con DROP EXTENSION pg_repack CASCADE; CREATE EXTENSION pg_repack;","note":"pg_repack no forma parte del núcleo de PostgreSQL (paquete aparte, p. ej., postgresql-17-repack).","tags":["pg_repack","bloat","online","vacuum full alternative","table reorganization","repack"]},{"id":"invalid-indexes","task":"Encontrar y limpiar índices INVALID","category":"Mantenimiento","command":"SELECT n.nspname AS schema_name,\n       c.relname AS index_name,\n       t.relname AS table_name,\n       pg_size_pretty(pg_relation_size(c.oid)) AS index_size,\n       i.indisvalid,\n       i.indisready,\n       format('DROP INDEX CONCURRENTLY %I.%I;', n.nspname, c.relname) AS fix\nFROM pg_index i\nJOIN pg_class c ON c.oid = i.indexrelid\nJOIN pg_class t ON t.oid = i.indrelid\nJOIN pg_namespace n ON n.oid = c.relnamespace\nWHERE NOT i.indisvalid OR NOT i.indisready;\n\n-- Before dropping: is an index build still in progress?\nSELECT pid, relid::regclass AS table_name, index_relid::regclass AS index_name, command, phase\nFROM pg_stat_progress_create_index;","what":"Lista, con su tamaño, los índices inválidos que dejaron ejecuciones fallidas de CREATE INDEX CONCURRENTLY o REINDEX CONCURRENTLY, y genera el comando para eliminarlos.","when":"Tras operaciones de índice en línea fallidas o canceladas, en las comprobaciones de salud rutinarias y antes de un upgrade mayor.","caution":"Un CREATE INDEX CONCURRENTLY / REINDEX CONCURRENTLY en curso también aparece como INVALID; revise pg_stat_progress_create_index antes de eliminarlo. Las consultas no usan un índice INVALID, pero si indisready = true se sigue manteniendo en cada INSERT/UPDATE, es decir, es pura sobrecarga. Se puede reconstruir con REINDEX INDEX CONCURRENTLY, pero vuelve a fallar si no se corrige la causa (p. ej., una violación de unicidad). Los restos de REINDEX CONCURRENTLY llevan el sufijo _ccnew/_ccold. DROP INDEX CONCURRENTLY no se puede ejecutar dentro de un bloque de transacción.","tags":["invalid index","indisvalid","create index concurrently","broken index","_ccnew","index"]},{"id":"extended-statistics","task":"Crear estadísticas extendidas para columnas correlacionadas (CREATE STATISTICS)","category":"Mantenimiento","command":"-- city and district are correlated: the planner assumes independence and underestimates rows\nCREATE STATISTICS customer_city_district_stx (dependencies, ndistinct, mcv) ON city, district FROM customer;\nANALYZE customer;\n\n-- Collected statistics\nSELECT statistics_name, attnames, kinds, n_distinct, dependencies\nFROM pg_stats_ext\nWHERE tablename = 'customer';\n\n-- Verify: estimated rows should now be close to actual rows\nEXPLAIN (ANALYZE, TIMING OFF)\nSELECT * FROM customer WHERE city = 'Ankara' AND district = 'Cankaya';","what":"Enseña al planificador la correlación entre columnas (dependencias funcionales, recuentos distintos combinados, valores más comunes conjuntos); corrige las estimaciones de filas para condiciones WHERE y GROUP BY de varias columnas.","when":"Cuando EXPLAIN ANALYZE muestra filas estimadas muy por debajo de las reales en filtros de varias columnas (p. ej., 12 frente a 570) y, como consecuencia, se elige un método de join/índice equivocado.","caution":"Solo surte efecto tras ANALYZE. Cuando el número de combinaciones de columnas está muy por encima del statistics target (100 por defecto), la lista mcv no puede cubrir la mayoría, y entonces mcv puede no corregir una estimación que dependencies sí corregiría. Verifíquelo con EXPLAIN; si es necesario quite mcv de la definición o suba el target con ALTER STATISTICS customer_city_district_stx SET STATISTICS 1000;. dependencies solo se usa en condiciones de igualdad/IN, ndistinct en estimaciones de GROUP BY. Cada definición alarga el ANALYZE; añádala solo a grupos de columnas con un error de estimación demostrado.","since":"PostgreSQL 12","note":"CREATE STATISTICS llegó en PG10 (ndistinct, dependencies), el tipo mcv en PG12, ALTER STATISTICS ... SET STATISTICS en PG13 y las estadísticas de expresiones en PG14.","tags":["extended statistics","create statistics","correlation","row estimate","cardinality","analyze"]},{"id":"analyze-staleness","task":"Encontrar tablas con estadísticas obsoletas (estrategia de ANALYZE)","category":"Mantenimiento","command":"SELECT schemaname,\n       relname AS table_name,\n       n_live_tup,\n       n_mod_since_analyze,\n       round(100.0 * n_mod_since_analyze / NULLIF(n_live_tup, 0), 1) AS mod_pct,\n       greatest(last_analyze, last_autoanalyze) AS last_analyzed,\n       round(current_setting('autovacuum_analyze_threshold')::int\n             + current_setting('autovacuum_analyze_scale_factor')::numeric * n_live_tup) AS autoanalyze_at\nFROM pg_stat_user_tables\nWHERE n_mod_since_analyze > 0\nORDER BY mod_pct DESC NULLS LAST\nLIMIT 20;\n\n-- After a bulk load, without waiting for autovacuum\nANALYZE (VERBOSE) orders;","what":"Muestra el número de filas modificadas desde el último ANALYZE, su proporción respecto a las filas vivas y el umbral aproximado de modificaciones con el que autovacuum disparará ANALYZE.","when":"Tras cargas/borrados masivos, cuando los planes muestran estimaciones de filas erróneas, o cuando se sospecha que autoanalyze se queda atrás en tablas grandes.","caution":"El umbral calculado usa los ajustes globales; si existen reloptions autovacuum_analyze_* específicas de la tabla (pg_class.reloptions), el umbral real es distinto. En tablas grandes el scale_factor por defecto (0.1) se dispara demasiado tarde; redúzcalo por tabla. Autovacuum no hace ANALYZE del padre de una tabla particionada; las estadísticas a nivel del padre necesitan un ANALYZE manual. Autovacuum nunca ve las tablas temporales. ANALYZE lee una muestra y toma ShareUpdateExclusiveLock (no bloquea el DML).","tags":["analyze","stale statistics","n_mod_since_analyze","autoanalyze","statistics","planner"]},{"id":"visibility-map-index-only","task":"Comprobar la cobertura del visibility map y la eficiencia de Index Only Scan","category":"Mantenimiento","command":"-- Tables with a low all-visible page ratio: Index Only Scans must visit the heap\nSELECT c.oid::regclass AS table_name,\n       c.relpages,\n       c.relallvisible,\n       round(100.0 * c.relallvisible / NULLIF(c.relpages, 0), 1) AS all_visible_pct,\n       pg_size_pretty(pg_relation_size(c.oid)) AS heap_size,\n       s.n_ins_since_vacuum,\n       s.last_autovacuum\nFROM pg_class c\nJOIN pg_stat_user_tables s ON s.relid = c.oid\nWHERE c.relpages > 1000\nORDER BY all_visible_pct NULLS FIRST\nLIMIT 20;\n\n-- If \"Heap Fetches\" in the plan is close to the returned rows, the visibility map is stale\nEXPLAIN (ANALYZE, BUFFERS) SELECT id FROM customer WHERE id BETWEEN 1 AND 50000;\n\n-- Exact count (pg_visibility extension)\nCREATE EXTENSION IF NOT EXISTS pg_visibility;\nSELECT * FROM pg_visibility_map_summary('customer');","what":"Muestra la proporción de páginas all-visible de las tablas, las filas insertadas desde el último vacuum y con qué frecuencia un Index Only Scan tuvo que visitar el heap (Heap Fetches).","when":"Cuando un Index Only Scan rinde por debajo de lo esperado; en tablas con muchas inserciones/append-only y al validar el diseño de un índice covering (INCLUDE).","caution":"relpages/relallvisible son estimaciones registradas por el último VACUUM/ANALYZE. Solo VACUUM activa los bits del visibility map; cada UPDATE/DELETE borra el bit de la página afectada. Para una proporción baja en tablas que se actualizan con frecuencia, reduzca autovacuum_vacuum_scale_factor por tabla. pg_visibility_map_summary lee el pequeño fork VM, no el heap.","since":"PostgreSQL 13","note":"n_ins_since_vacuum y el autovacuum disparado por inserciones (autovacuum_vacuum_insert_threshold/scale_factor) llegaron en PG13; antes, las tablas append-only podían no recibir ningún vacuum hasta un vacuum anti-wraparound, por lo que el visibility map no se activaba.","tags":["visibility map","index only scan","heap fetches","relallvisible","pg_visibility","vacuum"]},{"id":"range-partition-monthly","task":"Crear una tabla particionada por range mensual (generar las particiones en un bucle)","category":"Particionamiento","command":"CREATE TABLE sales (\n  id         bigint GENERATED ALWAYS AS IDENTITY,\n  created_at      date   NOT NULL,\n  customer_id bigint NOT NULL,\n  amount      numeric(12,2),\n  PRIMARY KEY (id, created_at)\n) PARTITION BY RANGE (created_at);\n\n-- An index defined on the parent propagates to all partitions automatically\nCREATE INDEX sales_customer_id_idx ON sales (customer_id);\n\n-- 12 monthly partitions for 2026\nDO $$\nDECLARE\n  m date;\nBEGIN\n  FOR m IN SELECT generate_series(date '2026-01-01', date '2026-12-01', interval '1 month')::date LOOP\n    EXECUTE format(\n      'CREATE TABLE IF NOT EXISTS %I PARTITION OF sales FOR VALUES FROM (%L) TO (%L)',\n      'sales_' || to_char(m, 'YYYY_MM'), m, (m + interval '1 month')::date);\n  END LOOP;\nEND $$;\n\n-- Hierarchy, bounds and sizes\nSELECT t.relid::regclass AS partition_name,\n       t.parentrelid::regclass AS parent,\n       t.isleaf,\n       t.level,\n       pg_get_expr(c.relpartbound, c.oid) AS bounds,\n       pg_size_pretty(pg_total_relation_size(t.relid)) AS total_size\nFROM pg_partition_tree('sales') t\nJOIN pg_class c ON c.oid = t.relid\nORDER BY t.level, bounds;","what":"Crea un padre particionado por RANGE sobre una columna de fecha, un índice que se propaga a todas las particiones y 12 particiones mensuales en un bucle; después lista la jerarquía con los límites y tamaños.","when":"Para tablas grandes y en crecimiento continuo que eliminan periódicamente datos antiguos (DETACH/DROP) o cuyas consultas filtran sobre todo por rango de fechas (pedidos, logs, series temporales).","caution":"En una tabla particionada, las restricciones PRIMARY KEY/UNIQUE deben incluir la clave de partición (no hay índices únicos globales). El límite inferior es inclusivo y el superior exclusivo (FROM inclusivo, TO exclusivo). Sin un job que cree por adelantado las particiones futuras (pg_cron, pg_partman), los INSERT fuera de rango fallan. Miles de particiones aumentan el tiempo de planificación y la sobrecarga de bloqueos. CREATE INDEX CONCURRENTLY no está soportado en el padre: use CREATE INDEX ... ON ONLY en el padre, CREATE INDEX CONCURRENTLY en cada partición y después ALTER INDEX ... ATTACH PARTITION.","since":"PostgreSQL 12","note":"El particionamiento declarativo llegó en PG10, los índices/PRIMARY KEY en el padre y las particiones default en PG11, pg_partition_tree() en PG12.","tags":["partition","range partition","declarative partitioning","monthly partition","pg_partition_tree","partitioning"]},{"id":"attach-partition-safely","task":"Conectar una tabla preparada como partición con un bloqueo breve (ATTACH PARTITION)","category":"Particionamiento","command":"-- 1) Prepare the data in a separate table without touching the live table\nCREATE TABLE sales_2027_01 (LIKE sales INCLUDING DEFAULTS INCLUDING CONSTRAINTS);\n-- ... bulk load (COPY / INSERT) ...\nALTER TABLE sales_2027_01 ADD PRIMARY KEY (id, created_at);\nCREATE INDEX CONCURRENTLY sales_2027_01_customer_id_idx ON sales_2027_01 (customer_id);\n\n-- 2) A CHECK that exactly matches the bounds: avoids a full scan during ATTACH\nALTER TABLE sales_2027_01 ADD CONSTRAINT sales_2027_01_bound_chk\n  CHECK (created_at IS NOT NULL AND created_at >= DATE '2027-01-01' AND created_at < DATE '2027-02-01') NOT VALID;\nALTER TABLE sales_2027_01 VALIDATE CONSTRAINT sales_2027_01_bound_chk;\n\n-- 3) Attach (SHARE UPDATE EXCLUSIVE on the parent; reads/writes continue)\nSET lock_timeout = '5s';\nALTER TABLE sales ATTACH PARTITION sales_2027_01\n  FOR VALUES FROM ('2027-01-01') TO ('2027-02-01');\nRESET lock_timeout;\n\n-- 4) Drop the now redundant CHECK\nALTER TABLE sales_2027_01 DROP CONSTRAINT sales_2027_01_bound_chk;","what":"Convierte en partición los datos cargados en una tabla aparte, sin full scan ni bloqueos largos, usando una restricción CHECK prevalidada que coincide con los límites e índices construidos de antemano.","when":"Para cargas masivas (ETL) o restauraciones desde archivo: preparar los datos sin tocar la tabla activa y luego ponerlos en línea en un solo paso.","caution":"ATTACH toma SHARE UPDATE EXCLUSIVE sobre el padre y ACCESS EXCLUSIVE sobre la tabla que se conecta. Sin un CHECK válido coincidente, la tabla se recorre por completo bajo ese bloqueo para validar los límites; si faltan índices que coincidan con los del padre, ATTACH los construye bajo el bloqueo, así que prepare ambos de antemano. Si existe una partición default, también se recorre para demostrar que no contiene filas del nuevo rango (vea la receta de la partición default). Los nombres y tipos de columna deben coincidir exactamente con los del padre; crear la tabla con LIKE ... INCLUDING es la forma más segura.","since":"PostgreSQL 12","note":"Que ATTACH PARTITION tome solo SHARE UPDATE EXCLUSIVE sobre el padre llegó en PG12; en PG11 y anteriores el padre se bloquea con ACCESS EXCLUSIVE.","tags":["attach partition","add partition","check constraint","not valid","etl","partitioning"]},{"id":"detach-partition-concurrently","task":"Desconectar una partición antigua sin bloquear el tráfico activo (DETACH CONCURRENTLY)","category":"Particionamiento","command":"-- Must run outside a transaction block\nALTER TABLE sales DETACH PARTITION sales_2026_01 CONCURRENTLY;\n\n-- If interrupted (cancel, lost connection) the partition stays 'detach pending'; complete it:\nALTER TABLE sales DETACH PARTITION sales_2026_01 FINALIZE;\n\n-- Any pending detach?\nSELECT inhrelid::regclass AS partition_name, inhparent::regclass AS parent\nFROM pg_inherits\nWHERE inhdetachpending;\n\n-- The detached table is now standalone: archive or drop it\nDROP TABLE sales_2026_01;","what":"Desconecta la partición en dos transacciones; toma solo SHARE UPDATE EXCLUSIVE sobre el padre, por lo que no bloquea las consultas en curso, y añade a la tabla desconectada una restricción CHECK equivalente al límite de la partición.","when":"Al desconectar periodos antiguos en un sistema en producción por retención/archivado (seguido de DROP, un traslado a otro tablespace o un archivo con pg_dump).","caution":"No se puede ejecutar dentro de un bloque BEGIN ... COMMIT ni usar cuando el padre tiene una partición default. Espera a todas las transacciones que usan el padre y empezaron antes que él; las transacciones largas lo prolongan. Si se interrumpe, la partición queda oculta a las consultas pero no desconectada ('detach pending') y debe completarse con FINALIZE. Un DETACH normal sin CONCURRENTLY es más rápido pero necesita ACCESS EXCLUSIVE sobre el padre; pruébelo con lock_timeout.","since":"PostgreSQL 14","tags":["detach partition","concurrently","finalize","retention","archiving","partitioning"]},{"id":"default-partition-pitfall","task":"Trampa de la partición default: la nueva partición falla con 'would be violated by some row'","category":"Particionamiento","command":"-- Which periods have rows in the default partition?\nSELECT date_trunc('month', created_at)::date AS month, count(*) AS row_count\nFROM sales_default\nGROUP BY 1\nORDER BY 1;\n\n-- Move the rows into a new partition and attach it in one transaction\nBEGIN;\nSET LOCAL lock_timeout = '5s';\nLOCK TABLE sales_default IN ACCESS EXCLUSIVE MODE;\nCREATE TABLE sales_2027_02 (LIKE sales INCLUDING DEFAULTS INCLUDING CONSTRAINTS);\nWITH moved AS (\n  DELETE FROM sales_default\n  WHERE created_at >= DATE '2027-02-01' AND created_at < DATE '2027-03-01'\n  RETURNING *\n)\nINSERT INTO sales_2027_02 SELECT * FROM moved;\nALTER TABLE sales ATTACH PARTITION sales_2027_02\n  FOR VALUES FROM ('2027-02-01') TO ('2027-03-01');\nCOMMIT;","what":"Cuenta por periodo las filas que han caído en la partición default; mueve las filas del rango de la nueva partición a una tabla nueva y la conecta, todo en una sola transacción.","when":"Cuando CREATE TABLE ... PARTITION OF o ATTACH falla con 'updated partition constraint for default partition would be violated by some row', o cuando se detecta que la partición default está creciendo.","caution":"Si hay una partición default, cada partición nueva la recorre por completo y mientras tanto mantiene ACCESS EXCLUSIVE sobre ella; una partición default grande significa una caída en cada mantenimiento. La default queda cerrada a escrituras mientras se mueven las filas; hágalo con poco tráfico. Solución permanente: cree las particiones de antemano y mantenga vacía la default con monitoreo (alerta si el número de filas > 0), o no la use, para que los INSERT fuera de rango fallen y el problema aflore pronto. Una partición default también impide DETACH ... CONCURRENTLY.","since":"PostgreSQL 11","tags":["default partition","partition error","violated by some row","partition constraint","pitfall","partitioning"]},{"id":"partition-pruning-check","task":"Verificar que el partition pruning funciona (en tiempo de planificación y de ejecución)","category":"Particionamiento","command":"SHOW enable_partition_pruning;\n\n-- Plan-time pruning: only the relevant partition should appear in the plan\nEXPLAIN (COSTS OFF)\nSELECT count(*) FROM sales WHERE created_at >= DATE '2026-03-01' AND created_at < DATE '2026-04-01';\n\n-- Run-time pruning: generic plan / parameterized query, \"Subplans Removed: N\"\nPREPARE q_sales(date) AS SELECT count(*) FROM sales WHERE created_at = $1;\nSET plan_cache_mode = force_generic_plan;\nEXPLAIN (ANALYZE, COSTS OFF, TIMING OFF) EXECUTE q_sales('2026-03-15');\nRESET plan_cache_mode;\nDEALLOCATE q_sales;\n\n-- Pruning killer: a function on the key column scans every partition\nEXPLAIN (COSTS OFF)\nSELECT count(*) FROM sales WHERE date_trunc('month', created_at) = DATE '2026-03-01';","what":"Compara el pruning en tiempo de planificación, el pruning en tiempo de ejecución con un plan genérico ('Subplans Removed: N') y cómo se pierde el pruning cuando se aplica una función a la clave de partición.","when":"Cuando las consultas sobre una tabla particionada son más lentas de lo esperado, el plan recorre todas las particiones o la aplicación usa un ORM/prepared statements.","caution":"Aplicar una función o un cast a la clave de partición (date_trunc(created_at), created_at::text) deshabilita el pruning; escriba la condición como un rango sobre la clave. Con un plan genérico, el pruning ocurre al arrancar el executor y solo se ve como 'Subplans Removed'; para expresiones STABLE como now() también es visible en EXPLAIN sin ANALYZE. Con enable_partition_pruning = off no hay ningún pruning. Con muchas particiones, mida también el Planning Time.","since":"PostgreSQL 12","note":"El pruning en tiempo de ejecución llegó en PG11; plan_cache_mode llegó en PG12, que además mejoró mucho el rendimiento de planificación con muchas particiones.","tags":["partition pruning","subplans removed","generic plan","plan_cache_mode","explain","partitioning"]},{"id":"pg-partman-setup","task":"Automatizar la creación y la retención de particiones con pg_partman","category":"Particionamiento","command":"CREATE SCHEMA partman;\nCREATE EXTENSION pg_partman SCHEMA partman;\n\nCREATE TABLE public.measurements (\n  id    bigint GENERATED ALWAYS AS IDENTITY,\n  ts    timestamptz NOT NULL,\n  value numeric,\n  PRIMARY KEY (id, ts)\n) PARTITION BY RANGE (ts);\n\n-- Daily partitions, pre-create 7 days ahead (pg_partman 5.x syntax)\nSELECT partman.create_parent(\n  p_parent_table := 'public.measurements',\n  p_control      := 'ts',\n  p_interval     := '1 day',\n  p_premake      := 7\n);\n\n-- Drop partitions older than 30 days\nUPDATE partman.part_config\n   SET retention = '30 days',\n       retention_keep_table = false\n WHERE parent_table = 'public.measurements';\n\n-- Maintenance: run periodically (schedule with pg_cron or pg_partman_bgw)\nCALL partman.run_maintenance_proc();\n\n-- Any rows in the default partition?\nSELECT * FROM partman.check_default(p_exact_count := true);","what":"Registra una tabla padre en pg_partman; crea de antemano las particiones futuras, elimina las que superan el periodo de retención e informa de las filas que han caído en la partición default.","when":"Cuando necesita una automatización estándar y observable en lugar de gestionar a mano o con scripts propios las particiones diarias/mensuales (tablas de logs, métricas, eventos).","caution":"La API cambió entre pg_partman 4.x y 5.x: en 5.x p_type solo acepta 'range'/'list' (por defecto 'range'), y el p_type := 'native' de 4.x no es válido; compruebe la versión instalada con SELECT extversion FROM pg_extension WHERE extname = 'pg_partman';. create_parent también crea por defecto una partición default. retention_keep_table = false hace DROP definitivo de las particiones; si necesita un archivo, déjelo en true y use retention_schema. Si run_maintenance_proc() no está programado, no se crean particiones nuevas y los datos caen en la default. pg_partman_bgw requiere shared_preload_libraries más un reinicio.","note":"pg_partman no forma parte del núcleo de PostgreSQL (paquete aparte, p. ej., postgresql-17-partman). Los comandos usan la sintaxis de pg_partman 5.x.","tags":["pg_partman","partition maintenance","retention","create_parent","automatic partitioning","partitioning"]},{"id":"least-privilege-roles","task":"Construir una jerarquía de roles de mínimo privilegio (GRANT + ALTER DEFAULT PRIVILEGES)","category":"Seguridad","command":"-- Group roles (NOLOGIN): object owner, read-only, read-write\nCREATE ROLE app_owner NOLOGIN;\nCREATE ROLE app_readonly NOLOGIN;\nCREATE ROLE app_readwrite NOLOGIN;\nGRANT app_readonly TO app_readwrite;\nCREATE SCHEMA app AUTHORIZATION app_owner;\n\n-- Restrict PUBLIC's default privileges\nREVOKE ALL ON DATABASE mydb FROM PUBLIC;\nREVOKE CREATE ON SCHEMA public FROM PUBLIC;\n\n-- Existing objects\nGRANT CONNECT ON DATABASE mydb TO app_readonly;\nGRANT USAGE ON SCHEMA app TO app_readonly;\nGRANT SELECT ON ALL TABLES IN SCHEMA app TO app_readonly;\nGRANT INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_readwrite;\nGRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA app TO app_readwrite;\n\n-- Objects app_owner will create IN THE FUTURE\nALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA app GRANT SELECT ON TABLES TO app_readonly;\nALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA app GRANT INSERT, UPDATE, DELETE ON TABLES TO app_readwrite;\nALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA app GRANT USAGE, SELECT ON SEQUENCES TO app_readwrite;\n\n-- Login users get privileges only through group membership\nCREATE ROLE web_app LOGIN PASSWORD 'Change#2026' IN ROLE app_readwrite;\nCREATE ROLE report_app LOGIN PASSWORD 'Change#2026' IN ROLE app_readonly;\n\n-- Configured default privileges (psql: \\ddp)\nSELECT pg_get_userbyid(d.defaclrole) AS owner_role,\n       n.nspname AS schema_name,\n       d.defaclobjtype AS object_type,\n       d.defaclacl\nFROM pg_default_acl d\nLEFT JOIN pg_namespace n ON n.oid = d.defaclnamespace\nORDER BY 1, 2, 3;","what":"Construye una jerarquía de roles de grupo propietario, de solo lectura y de lectura-escritura; concede los privilegios adecuados sobre tablas/secuencias existentes y futuras, y autoriza a los usuarios de aplicación solo mediante la pertenencia a grupos.","when":"Al configurar un esquema de aplicación nuevo, al corregir aplicaciones que se conectan como superusuario o como propietario de las tablas, y para simplificar los privilegios antes de una auditoría.","caution":"ALTER DEFAULT PRIVILEGES solo se aplica a los objetos creados DESPUÉS por el rol indicado en FOR ROLE; si la herramienta de migración crea las tablas con otro usuario, los privilegios no se aplican, así que ejecute el DDL con SET ROLE app_owner. GRANT ... ON ALL TABLES solo cubre los objetos que existen en ese momento. Las bases de datos creadas en PG15+ ya no conceden CREATE sobre el esquema public a PUBLIC; las bases de datos actualizadas desde versiones antiguas necesitan el REVOKE. REVOKE ALL ON DATABASE ... FROM PUBLIC también quita CONNECT y TEMP; concédalos por separado a los demás roles que los necesiten. Use \\password de psql en lugar de poner las contraseñas en la línea de comandos (así no quedan en los logs ni en el historial).","note":"Los roles pg_read_all_data / pg_write_all_data llegaron en PG14, las opciones INHERIT/SET en la pertenencia a roles (GRANT ... WITH INHERIT FALSE) en PG16.","tags":["least privilege","role hierarchy","alter default privileges","grant","privilege","security"]},{"id":"row-level-security","task":"Configurar el aislamiento de inquilinos con row-level security","category":"Seguridad","command":"ALTER TABLE app.orders ENABLE ROW LEVEL SECURITY;\n-- Make the table owner subject to the policy too\nALTER TABLE app.orders FORCE ROW LEVEL SECURITY;\n\nCREATE POLICY orders_tenant_isolation ON app.orders\n  FOR ALL\n  TO app_readwrite\n  USING (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::int)\n  WITH CHECK (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::int);\n\n-- The application sets the tenant at the start of every transaction (pooler-friendly)\nBEGIN;\nSET LOCAL app.tenant_id = '42';\nSELECT * FROM app.orders;\nCOMMIT;\n\n-- Configured policies\nSELECT schemaname, tablename, policyname, roles, cmd, qual, with_check\nFROM pg_policies\nWHERE tablename = 'orders';","what":"Habilita RLS en la tabla y define una política que filtra las filas por el id de inquilino de una variable de sesión (USING) y bloquea las escrituras para otro inquilino (WITH CHECK).","when":"Para imponer el aislamiento de datos en la base de datos en aplicaciones multiinquilino en lugar de depender del código de la aplicación.","caution":"Los superusuarios y los roles con el atributo BYPASSRLS siempre omiten las políticas; el propietario de la tabla solo está sujeto a ellas con FORCE ROW LEVEL SECURITY. Con RLS habilitado y sin ninguna política coincidente, el resultado está vacío (sin error). current_setting(..., true) devuelve NULL cuando la variable no está definida; NULLIF evita que la cadena vacía que queda tras RESET falle como ''::int. Los usuarios pueden cambiar app.tenant_id con SET; solo una capa de aplicación de confianza debe definirlo y los usuarios finales no deben tener acceso SQL. Con un pooler en modo transaction, use SET LOCAL en lugar de SET. Indexe la columna tenant_id usada en la política. pg_dump falla cuando se ejecuta como un usuario sujeto a RLS; haga los backups con un rol BYPASSRLS.","since":"PostgreSQL 9.5","tags":["row level security","rls","create policy","multi-tenant","tenant isolation","security"]},{"id":"pg-hba-validate-reload","task":"Validar el orden de las reglas de pg_hba.conf y recargar con seguridad","category":"Seguridad","command":"-- File location\nSHOW hba_file;\n\n# Rules are read top to bottom and the FIRST matching rule wins;\n# specific rules must come BEFORE general ones:\n#   hostssl  appdb  web_app  10.10.0.0/16  scram-sha-256\n#   host     all    all      10.10.0.0/16  reject\n#   host     all    all      0.0.0.0/0     scram-sha-256\n\n-- Validate BEFORE reloading: pg_hba_file_rules reads the file on disk\nSELECT line_number, type, database, user_name, address, netmask, auth_method, error\nFROM pg_hba_file_rules\nORDER BY line_number;\n\n-- Apply if the error column is empty\nSELECT pg_reload_conf();","what":"Muestra dónde está pg_hba.conf, cómo lo interpreta PostgreSQL (línea, tipo, base de datos, usuario, dirección, método) y los errores de sintaxis antes del reload; después recarga la configuración.","when":"Al añadir una nueva red cliente/aplicación, migrar de md5 a scram-sha-256 o investigar errores 'no pg_hba.conf entry for host'.","caution":"pg_hba_file_rules muestra el archivo tal como está ahora, no las reglas activas. Si alguna línea tiene un error no vacío, el reload NO carga pg_hba.conf en absoluto y las reglas antiguas siguen vigentes (el log dice 'pg_hba.conf was not reloaded'). Los cambios solo afectan a las conexiones nuevas. Una regla amplia colocada al principio (p. ej., host all all 0.0.0.0/0 trust) anula todas las restricciones posteriores. Con SSL desactivado, las líneas hostssl no pueden coincidir y muestran un mensaje en la columna error. Por defecto solo los superusuarios pueden leer la vista. Asegúrese de que las herramientas que editan el archivo como root no cambien su propietario; si el usuario postgres no puede leerlo, el reload falla.","since":"PostgreSQL 10","note":"pg_hba_file_rules llegó en PG10; las versiones más nuevas también tienen las columnas rule_number y file_name. PG16 admite directivas include en pg_hba.conf y coincidencia por regex en los campos de usuario/base de datos.","tags":["pg_hba.conf","authentication","rule order","reload","pg_hba_file_rules","security"]},{"id":"ssl-enable-verify","task":"Habilitar SSL/TLS y verificar las conexiones cifradas (pg_stat_ssl)","category":"Seguridad","command":"# Server certificate (self-signed for testing; use a corporate CA in production)\nopenssl req -new -x509 -days 365 -nodes -subj \"/CN=db.example.com\" -keyout server.key -out server.crt\nchmod 600 server.key    # in the data directory, owned by postgres\n\n-- Enable SSL (a reload is enough)\nALTER SYSTEM SET ssl = on;\nALTER SYSTEM SET ssl_cert_file = 'server.crt';\nALTER SYSTEM SET ssl_key_file = 'server.key';\nALTER SYSTEM SET ssl_min_protocol_version = 'TLSv1.2';\nSELECT pg_reload_conf();\n\n-- Which connections are encrypted?\nSELECT a.pid, a.usename, a.client_addr, a.application_name,\n       s.ssl, s.version, s.cipher, s.bits\nFROM pg_stat_ssl s\nJOIN pg_stat_activity a USING (pid)\nWHERE a.backend_type = 'client backend'\nORDER BY s.ssl, a.usename;\n\n# On the client, verify the server identity as well\npsql \"host=db.example.com dbname=postgres user=postgres sslmode=verify-full sslrootcert=root.crt\"","what":"Habilita TLS con un certificado de servidor sin reiniciar, fija la versión mínima del protocolo y lista si las conexiones actuales están cifradas, con la versión de TLS y el cipher.","when":"Para requisitos de auditoría de seguridad/cumplimiento (PCI, GDPR), para proteger las contraseñas y los datos en tránsito por la red.","caution":"ssl = on por sí solo no obliga al cifrado; los clientes aún pueden conectarse sin cifrar con sslmode=prefer/disable. Para imponerlo, use hostssl en lugar de host en pg_hba.conf (y añada hostnossl ... reject). sslmode=require no verifica la identidad del servidor (expuesto a MITM); use verify-full para verificar el certificado. El archivo de clave debe pertenecer al usuario postgres con modo 0600 (o a root con 0640); de lo contrario SSL no se carga, así que revise el log y SHOW ssl tras el reload. Las renovaciones de certificados también se aplican con un reload.","since":"PostgreSQL 10","note":"Cambiar los ajustes de SSL con un reload en lugar de un reinicio llegó en PG10, ssl_min_protocol_version en PG12; su valor por defecto es TLSv1.2 desde PG13.","tags":["ssl","tls","pg_stat_ssl","hostssl","certificate","encryption"]},{"id":"scram-migration","task":"Migrar de contraseñas md5 a scram-sha-256","category":"Seguridad","command":"-- 1) Store new passwords as SCRAM\nALTER SYSTEM SET password_encryption = 'scram-sha-256';\nSELECT pg_reload_conf();\n\n-- 2) Find roles still carrying md5 password hashes (requires superuser)\nSELECT rolname,\n       CASE\n         WHEN rolpassword LIKE 'SCRAM-SHA-256$%' THEN 'scram-sha-256'\n         WHEN rolpassword LIKE 'md5%' THEN 'md5'\n         WHEN rolpassword IS NULL THEN 'no password'\n         ELSE 'other'\n       END AS password_type,\n       rolvaliduntil\nFROM pg_authid\nWHERE rolcanlogin\nORDER BY password_type, rolname;\n\n-- 3) Reset the password of each md5 role (preferably in psql: \\password legacy_app)\nALTER ROLE legacy_app PASSWORD 'New#Password2';\n\n# 4) Once all roles use SCRAM, change md5 -> scram-sha-256 in pg_hba.conf and reload","what":"Hace que las contraseñas nuevas se guarden como SCRAM-SHA-256, encuentra los roles de login que aún tienen hashes md5 y da los pasos para pasar pg_hba.conf a scram-sha-256 una vez renovadas las contraseñas.","when":"Cuando una auditoría de seguridad señala md5, tras un upgrade a PG14+, o al prepararse para versiones en las que md5 está obsoleto.","caution":"SCRAM no se puede derivar de los hashes md5 existentes; cada contraseña debe definirse de nuevo en texto claro (requiere coordinación con los equipos de aplicación). El método 'md5' de pg_hba.conf realiza automáticamente la autenticación SCRAM para los roles cuya contraseña es SCRAM, por lo que mantener md5 durante la migración es un paso intermedio seguro, pero una vez que el método es 'scram-sha-256' los roles con contraseñas md5 ya no pueden conectarse. Los drivers de cliente antiguos (libpq < 10, drivers JDBC/.NET antiguos) pueden no soportar SCRAM; actualice primero los drivers. ALTER ROLE ... PASSWORD puede escribir la contraseña en el log mediante log_statement; \\password de psql la convierte en hash en el cliente antes de enviarla.","since":"PostgreSQL 10","note":"SCRAM-SHA-256 llegó en PG10, y el valor por defecto de password_encryption pasó a ser scram-sha-256 en PG14. PG18 declara obsoletas las contraseñas md5 y emite una advertencia.","tags":["scram-sha-256","md5","password_encryption","password","authentication","security"]},{"id":"pgaudit-object-audit","task":"Mantener logs de auditoría a nivel de rol y de objeto con pgaudit","category":"Seguridad","command":"-- Prerequisite: shared_preload_libraries = 'pgaudit' + restart, then:\nCREATE EXTENSION pgaudit;\n\n-- Session auditing: server-wide, only DDL and role/privilege changes\nALTER SYSTEM SET pgaudit.log = 'ddl, role';\nALTER SYSTEM SET pgaudit.log_relation = on;\nALTER SYSTEM SET pgaudit.log_parameter = off;\nSELECT pg_reload_conf();\n\n-- All reads/writes of a specific application role\nALTER ROLE web_app SET pgaudit.log = 'read, write';\n\n-- Object auditing: privileges granted to the auditor role = operations to log\nCREATE ROLE auditor NOLOGIN;\nALTER SYSTEM SET pgaudit.role = 'auditor';\nSELECT pg_reload_conf();\nGRANT SELECT, DELETE ON customer TO auditor;\nGRANT UPDATE (balance) ON customer TO auditor;","what":"Registra los cambios de DDL y de privilegios en todo el servidor, todas las lecturas/escrituras de un rol concreto y el acceso a tablas/columnas sensibles concedidas al rol auditor, como líneas de log estructuradas con el prefijo 'AUDIT:'.","when":"Para demostrar quién accedió a datos sensibles en auditorías de cumplimiento como GDPR, PCI-DSS o SOX.","caution":"Habilitar pgaudit.log = 'all' o 'read' en todo el servidor aumenta mucho el volumen de log y la I/O; acote el alcance con auditoría por rol o por objeto. Un superusuario puede desactivar la auditoría (ALTER SYSTEM/ALTER ROLE); envíe los registros a un sistema de logs central (syslog/SIEM) y almacénelos de forma independiente del servidor de base de datos. log_parameter = on también registra los parámetros bind (datos personales). Los ajustes de rol surten efecto en las sesiones nuevas. La versión mayor de pgaudit debe coincidir con la versión mayor de PostgreSQL (pgaudit 17.x = PG17).","note":"pgaudit no forma parte del núcleo de PostgreSQL (paquete aparte, p. ej., postgresql-17-pgaudit).","tags":["pgaudit","audit","auditing","object audit","log","security"]},{"id":"memory-settings-check","task":"Revisar los ajustes de memoria (shared_buffers, work_mem, maintenance_work_mem)","category":"Parámetros","command":"SELECT name,\n       current_setting(name) AS value,\n       source,\n       context\nFROM pg_settings\nWHERE name IN ('shared_buffers', 'effective_cache_size', 'work_mem', 'hash_mem_multiplier',\n               'maintenance_work_mem', 'autovacuum_work_mem', 'max_connections',\n               'max_parallel_workers_per_gather', 'huge_pages', 'temp_buffers', 'wal_buffers')\nORDER BY name;\n\n-- Rough ceilings: work_mem x max_connections and the autovacuum memory ceiling\nSELECT pg_size_pretty(pg_size_bytes(current_setting('work_mem'))\n                      * current_setting('max_connections')::int) AS work_mem_x_max_connections,\n       pg_size_pretty(pg_size_bytes(current_setting('maintenance_work_mem'))\n                      * current_setting('autovacuum_max_workers')::int) AS autovacuum_mem_ceiling;\n\n-- Trace of operations that did not fit in work_mem: temp files per database\nSELECT datname, temp_files, pg_size_pretty(temp_bytes) AS temp_size, stats_reset\nFROM pg_stat_database\nWHERE datname IS NOT NULL\nORDER BY temp_bytes DESC;","what":"Muestra los parámetros relacionados con la memoria con su valor, su origen (default/archivo de configuración) y su contexto de cambio (postmaster = reinicio); techos aproximados de memoria para work_mem y autovacuum; y el volumen de archivos temporales por base de datos.","when":"Al montar un servidor nuevo, tras añadir RAM, tras un evento del OOM killer o cuando se observa mucho uso de archivos temporales.","caution":"work_mem no se asigna por conexión sino por NODO de sort/hash (multiplicado por hash_mem_multiplier en los hashes) y por separado para cada parallel worker; una consulta compleja puede usar varias veces work_mem, así que el producto calculado es una advertencia aproximada, no un límite superior. Puntos de partida típicos: shared_buffers ~25% de la RAM (requiere reinicio), effective_cache_size ~50-75% (solo una pista para el planificador, no asigna nada), maintenance_work_mem 512MB-2GB. Con autovacuum_work_mem = -1, cada autovacuum worker puede usar maintenance_work_mem. Si temp_bytes es alto, suba work_mem por rol o consulta, no globalmente. Con shared_buffers grandes en Linux, considere huge_pages.","note":"hash_mem_multiplier llegó en PG13 (2.0 por defecto desde PG15).","tags":["shared_buffers","work_mem","maintenance_work_mem","memory","memory tuning","temp_bytes"]},{"id":"pending-restart-config-errors","task":"Mostrar los parámetros pendientes de reinicio y los errores de los archivos de configuración","category":"Parámetros","command":"-- Parameters changed but not effective until a restart\nSELECT name, setting, unit, context, source, sourcefile, pending_restart\nFROM pg_settings\nWHERE pending_restart;\n\n-- Lines in configuration files that were not applied or have errors\nSELECT sourcefile, sourceline, seqno, name, setting, applied, error\nFROM pg_file_settings\nWHERE NOT applied OR error IS NOT NULL\nORDER BY sourcefile, sourceline;\n\n-- Where does a parameter's effective value come from?\nSELECT name, setting, source, sourcefile, sourceline\nFROM pg_settings\nWHERE name = 'work_mem';","what":"Muestra los parámetros cambiados en los archivos que esperan un reinicio, las líneas de los archivos de configuración que no se aplicaron o tienen errores, y de qué archivo/línea procede el valor efectivo de un parámetro.","when":"Para verificar tras un ALTER SYSTEM o un cambio en postgresql.conf que el reload tuvo el efecto esperado, y para listar los cambios pendientes antes de un reinicio planificado.","caution":"pg_file_settings lee los archivos tal como están ahora; si un parámetro está definido en varios sitios, solo el último tiene applied = true (postgresql.auto.conf se lee el último, por lo que ALTER SYSTEM siempre gana). Una línea con applied = false y error vacío está anulada por otra definición; no es un error. Un parámetro que necesita reinicio muestra el error 'setting could not be applied'. Un archivo defectuoso puede aplicarse parcialmente en el reload; tras un cambio, ejecute pg_reload_conf() y después revise estas consultas y el log del servidor. No edite postgresql.auto.conf a mano; use ALTER SYSTEM RESET.","since":"PostgreSQL 9.5","tags":["pending_restart","pg_file_settings","restart","configuration error","alter system","config"]},{"id":"checkpoint-stats","task":"Revisar el comportamiento de los checkpoints (PG17+ pg_stat_checkpointer, PG16 y anteriores pg_stat_bgwriter)","category":"Parámetros","command":"-- PG17+\nSELECT num_timed,\n       num_requested,\n       round(100.0 * num_requested / NULLIF(num_timed + num_requested, 0), 1) AS req_pct,\n       restartpoints_timed,\n       restartpoints_req,\n       round((write_time / 1000)::numeric / NULLIF(num_timed + num_requested, 0), 1) AS avg_write_s,\n       round((sync_time / 1000)::numeric / NULLIF(num_timed + num_requested, 0), 1) AS avg_sync_s,\n       pg_size_pretty(buffers_written * current_setting('block_size')::bigint) AS written_by_checkpoint,\n       stats_reset\nFROM pg_stat_checkpointer;\n\n-- PG16 and earlier\nSELECT checkpoints_timed,\n       checkpoints_req,\n       round(100.0 * checkpoints_req / NULLIF(checkpoints_timed + checkpoints_req, 0), 1) AS req_pct,\n       round((checkpoint_write_time / 1000)::numeric / NULLIF(checkpoints_timed + checkpoints_req, 0), 1) AS avg_write_s,\n       round((checkpoint_sync_time / 1000)::numeric / NULLIF(checkpoints_timed + checkpoints_req, 0), 1) AS avg_sync_s,\n       pg_size_pretty(buffers_checkpoint * current_setting('block_size')::bigint) AS written_by_checkpoint,\n       pg_size_pretty(buffers_clean * current_setting('block_size')::bigint) AS written_by_bgwriter,\n       pg_size_pretty(buffers_backend * current_setting('block_size')::bigint) AS written_by_backends,\n       maxwritten_clean,\n       stats_reset\nFROM pg_stat_bgwriter;\n\n-- Related settings\nSELECT name, current_setting(name) AS value\nFROM pg_settings\nWHERE name IN ('checkpoint_timeout', 'max_wal_size', 'min_wal_size',\n               'checkpoint_completion_target', 'log_checkpoints')\nORDER BY name;","what":"Muestra la proporción entre los checkpoints programados y los forzados por el volumen de WAL, el tiempo medio de escritura/sync por checkpoint y el volumen de buffers escritos (en PG16 y anteriores, junto con las escrituras de bgwriter y de los backends).","when":"Cuando los periodos de mucha escritura muestran picos de I/O, la generación de WAL es alta o el log advierte 'checkpoints are occurring too frequently'.","caution":"Un req_pct alto (normalmente por encima del 10-20%) significa que los checkpoints se fuerzan porque se llena max_wal_size; aumente max_wal_size y vigile las líneas 'checkpoint starting: wal' con log_checkpoints = on. Un checkpoint_timeout más largo reparte la I/O pero alarga la crash recovery. En PG16 y anteriores, un written_by_backends alto significa que los backends tienen que escribir ellos mismos las páginas sucias (shared_buffers/bgwriter insuficientes); en PG17 esto está en pg_stat_io. Los promedios también cuentan los checkpoints programados omitidos por falta de trabajo, así que léalos como cifras aproximadas. Los contadores son acumulados desde stats_reset.","since":"PostgreSQL 17","note":"En PG17 las estadísticas del checkpointer pasaron de pg_stat_bgwriter a la nueva vista pg_stat_checkpointer (checkpoints_timed → num_timed, checkpoints_req → num_requested, buffers_checkpoint → buffers_written); se eliminaron buffers_backend y buffers_backend_fsync. PG18 añadió las columnas num_done y slru_written.","tags":["checkpoint","pg_stat_checkpointer","pg_stat_bgwriter","max_wal_size","checkpoint_timeout","wal"]},{"id":"relation-size-breakdown","task":"Listar el tamaño de las tablas desglosado en heap / índices / TOAST, y los índices más grandes","category":"Almacenamiento","command":"SELECT n.nspname AS schema_name,\n       c.relname AS table_name,\n       pg_size_pretty(pg_total_relation_size(c.oid)) AS total,\n       pg_size_pretty(pg_relation_size(c.oid)) AS heap,\n       pg_size_pretty(pg_indexes_size(c.oid)) AS indexes,\n       pg_size_pretty(pg_total_relation_size(c.oid) - pg_relation_size(c.oid) - pg_indexes_size(c.oid)) AS toast_fsm_vm,\n       round(100.0 * pg_total_relation_size(c.oid) / NULLIF(pg_database_size(current_database()), 0), 1) AS pct_of_db\nFROM pg_class c\nJOIN pg_namespace n ON n.oid = c.relnamespace\nWHERE c.relkind IN ('r', 'm')\n  AND n.nspname NOT IN ('pg_catalog', 'information_schema')\nORDER BY pg_total_relation_size(c.oid) DESC\nLIMIT 20;\n\n-- Largest indexes and their usage counts\nSELECT s.schemaname,\n       s.relname AS table_name,\n       s.indexrelname AS index_name,\n       pg_size_pretty(pg_relation_size(s.indexrelid)) AS index_size,\n       s.idx_scan,\n       i.indisunique,\n       i.indisprimary\nFROM pg_stat_user_indexes s\nJOIN pg_index i ON i.indexrelid = s.indexrelid\nORDER BY pg_relation_size(s.indexrelid) DESC\nLIMIT 20;","what":"Muestra las tablas más grandes desglosadas en total, heap, índices y TOAST (+FSM/VM) con su porcentaje de la base de datos; y los índices más grandes con su número de scans y los indicadores unique/primary.","when":"Para averiguar de dónde viene el crecimiento (datos, índices o TOAST) cuando aumenta el uso de disco, y para detectar índices grandes sin uso.","caution":"Las funciones de tamaño acceden al sistema de archivos para cada relación y pueden ser lentas en bases de datos con decenas de miles de tablas. Un padre particionado (relkind 'p') ocupa 0 bytes por sí mismo y se excluye; para el total de una jerarquía: SELECT pg_size_pretty(sum(pg_total_relation_size(relid))) FROM pg_partition_tree('sales'); Los índices grandes con idx_scan = 0 son candidatos a eliminarse, pero los contadores solo cubren el tiempo desde el último reset de estadísticas y solo este servidor (el uso en las réplicas no es visible); los índices unique/primary imponen restricciones y no se eliminan.","tags":["table size","index size","toast","disk usage","pg_total_relation_size","storage"]},{"id":"extension-update-check","task":"Encontrar las extensiones con actualizaciones pendientes y actualizarlas","category":"Extensiones","command":"SELECT e.extname,\n       e.extversion AS installed,\n       a.default_version AS available,\n       n.nspname AS schema_name,\n       format('ALTER EXTENSION %I UPDATE;', e.extname) AS fix\nFROM pg_extension e\nJOIN pg_available_extensions a ON a.name = e.extname\nJOIN pg_namespace n ON n.oid = e.extnamespace\nWHERE e.extversion IS DISTINCT FROM a.default_version\nORDER BY e.extname;\n\n-- Is there an update path?\nSELECT * FROM pg_extension_update_paths('pgstattuple') WHERE path IS NOT NULL;\n\nALTER EXTENSION pgstattuple UPDATE;","what":"Compara la versión instalada de cada extensión en la base de datos con la versión por defecto del paquete en el servidor, y genera el comando ALTER EXTENSION para las que tienen actualizaciones pendientes.","when":"Tras upgrades menores/mayores de PostgreSQL y actualizaciones de paquetes de extensiones, y durante el mantenimiento periódico.","caution":"Actualizar el paquete del SO no actualiza automáticamente la parte SQL; ALTER EXTENSION ... UPDATE debe ejecutarse por separado en cada base de datos (especialmente importante tras pg_upgrade). El script de actualización puede tomar bloqueos y modificar objetos (p. ej., PostGIS); pruébelo primero y lea las release notes. Para pasar a una versión concreta: ALTER EXTENSION pgstattuple UPDATE TO '1.5'; Algunas extensiones (p. ej., pg_stat_statements) también requieren un reinicio para recargar la shared library.","tags":["alter extension update","extension version","extension","pg_available_extensions","upgrade","update"]},{"id":"pg-stat-io-analysis","task":"Analizar de dónde viene la I/O con pg_stat_io (tipo de backend, objeto, contexto)","category":"Extensiones","command":"SELECT backend_type,\n       object,\n       context,\n       reads,\n       pg_size_pretty(reads * op_bytes) AS read_size,\n       writes,\n       pg_size_pretty(writes * op_bytes) AS write_size,\n       extends,\n       hits,\n       round(100.0 * hits / NULLIF(hits + reads, 0), 1) AS hit_pct,\n       evictions,\n       reuses,\n       fsyncs,\n       round(read_time::numeric, 1) AS read_ms,\n       round(write_time::numeric, 1) AS write_ms\nFROM pg_stat_io\nWHERE coalesce(reads, 0) + coalesce(writes, 0) + coalesce(extends, 0) + coalesce(evictions, 0) > 0\nORDER BY coalesce(reads, 0) + coalesce(writes, 0) DESC;\n\n-- Who writes? Ideally most writes come from the checkpointer and background writer\nSELECT backend_type,\n       pg_size_pretty(sum(writes * op_bytes)) AS written,\n       round(100.0 * sum(writes) / NULLIF(sum(sum(writes)) OVER (), 0), 1) AS pct_of_writes,\n       sum(fsyncs) AS fsyncs\nFROM pg_stat_io\nWHERE object = 'relation' AND writes > 0\nGROUP BY backend_type\nORDER BY sum(writes) DESC;","what":"Muestra las lecturas, escrituras, extends, evictions y fsyncs por tipo de backend (client backend, autovacuum, checkpointer...), objeto (relation/temp relation) y contexto (normal, vacuum, bulkread, bulkwrite); y de qué tipos de proceso vienen las escrituras.","when":"Durante un cuello de botella de I/O, para saber si la carga viene de las consultas, de autovacuum o de los checkpoints, y para validar los ajustes de shared_buffers/bgwriter.","caution":"pg_stat_io abarca todo el clúster y es acumulado desde el último reset; tome la diferencia entre dos muestras para ver el cambio (para restablecer: SELECT pg_stat_reset_shared('io');). read_time/write_time solo se rellenan con track_io_timing = on. reads también incluye las lecturas servidas desde la page cache del SO. Muchas escrituras o evictions de client backend significan que los backends escriben ellos mismos los buffers para liberar espacio; revise shared_buffers, bgwriter_lru_maxpages y los ajustes de checkpoint. Las filas con context = 'vacuum'/'bulkread' reflejan el uso de ring buffers; los reuses ahí son normales.","since":"PostgreSQL 16","note":"pg_stat_io llegó en PG16. PG18 eliminó la columna op_bytes, añadió read_bytes/write_bytes/extend_bytes y también informa de la I/O de WAL en esta vista; en PG18 use read_bytes en lugar de reads * op_bytes.","tags":["pg_stat_io","io statistics","evictions","fsync","shared_buffers","io"]}]},"recipes/mysql.json":{"engineId":"mysql","engine":"MySQL / MariaDB","order":3,"recipes":[{"id":"rotate-binlog","task":"Rotar el binary log","category":"Binlog y logs","command":"FLUSH BINARY LOGS;","what":"Cierra el archivo de binary log actual y cambia a uno nuevo.","when":"Para crear un punto de partida limpio del binlog antes de un backup, o para limitar el tamaño de los archivos.","caution":"Una rotación frecuente genera muchos archivos binlog pequeños. El mismo comando funciona en MariaDB; FLUSH LOGS rota todos los logs.","tags":["binlog","rotate","flush","log"]},{"id":"purge-binlog","task":"Purgar binary logs antiguos (PURGE)","category":"Binlog y logs","command":"PURGE BINARY LOGS BEFORE NOW() - INTERVAL 7 DAY;","what":"Borra del disco los archivos de binary log anteriores a la fecha indicada.","when":"Cuando los binlogs están llenando el disco, para limpiar logs antiguos que ya no se necesitan.","caution":"Si una réplica aún necesita un binlog que se está borrando, la replicación se rompe; verifique primero con SHOW REPLICA STATUS que todas las réplicas lo han superado. Para borrar hasta un archivo concreto: PURGE BINARY LOGS TO 'mysql-bin.000123'; Para la limpieza automática, use el ajuste binlog_expire_logs_seconds.","tags":["binlog","purge","cleanup","log"]},{"id":"show-binlogs","task":"Ver el estado de los binary logs","category":"Binlog y logs","command":"SHOW BINARY LOGS;","what":"Lista todos los archivos de binary log del servidor y sus tamaños.","when":"Para ver el uso de disco de los binlogs y qué archivos existen.","caution":"Para el archivo binlog que se está escribiendo y su posición: SHOW BINARY LOG STATUS; (MySQL 8.2+). Las versiones anteriores usan SHOW MASTER STATUS (obsoleto en 8.2–8.3, eliminado en 8.4).","tags":["binlog","status","show","log"]},{"id":"enable-general-slow-log","task":"Habilitar el general log y el slow query log","category":"Binlog y logs","command":"SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; SET GLOBAL general_log = 'ON';","what":"Habilita en tiempo de ejecución el slow query log (consultas de más de 1 segundo) y el general log, que registra todas las consultas.","when":"Para analizar temporalmente las consultas lentas o todo el tráfico de consultas.","caution":"general_log añade una sobrecarga importante de rendimiento y de disco; desactívelo siempre al terminar el diagnóstico (SET GLOBAL general_log='OFF'). Estos ajustes se restablecen al reiniciar; use SET PERSIST para hacerlos permanentes.","tags":["slow-log","general-log","log","diagnostics"]},{"id":"innodb-redo-capacity","task":"Cambiar el tamaño del redo log de InnoDB","category":"Almacenamiento","command":"SET GLOBAL innodb_redo_log_capacity = 2147483648;","what":"Define dinámicamente en tiempo de ejecución la capacidad total del redo log de InnoDB (en bytes, aquí 2 GB).","when":"Para reducir la frecuencia de checkpoints y mejorar el rendimiento con cargas de trabajo de mucha escritura.","caution":"innodb_redo_log_capacity llegó en MySQL 8.0.30 y sustituye a los antiguos ajustes innodb_log_file_size + innodb_log_files_in_group. En versiones anteriores/MariaDB, innodb_log_file_size se cambia en my.cnf y requiere reinicio. Para hacerlo permanente: SET PERSIST innodb_redo_log_capacity = 2147483648;","since":"MySQL 8.0.30","note":"innodb_redo_log_capacity llegó en MySQL 8.0.30 y permitió redimensionar el redo log de forma dinámica (sin reinicio); antes había que definir innodb_log_file_size + innodb_log_files_in_group y reiniciar el servidor.","tags":["innodb","redo-log","capacity","storage"]},{"id":"innodb-undo-tablespace","task":"Añadir / gestionar un undo tablespace de InnoDB","category":"Almacenamiento","command":"CREATE UNDO TABLESPACE undo_003 ADD DATAFILE 'undo_003.ibu';","what":"Crea un nuevo undo tablespace de InnoDB; los undo logs se gestionan por separado de los archivos de datos y se pueden truncar.","when":"Para gestionar el espacio cuando las transacciones largas inflan el undo log, o para mover el undo a un disco aparte.","caution":"El nombre del archivo debe terminar en .ibu. Un undo tablespace debe ponerse inactivo antes de poder eliminarlo: ALTER UNDO TABLESPACE undo_003 SET INACTIVE; y después DROP UNDO TABLESPACE undo_003; Es una función de MySQL 8.0+.","since":"MySQL 8.0","note":"La creación/eliminación de undo tablespaces con SQL (CREATE/DROP UNDO TABLESPACE) llegó en MySQL 8.0; antes el número de undo tablespaces lo fijaba innodb_undo_tablespaces y no se podía gestionar en tiempo de ejecución.","tags":["innodb","undo-log","tablespace","storage"]},{"id":"create-tablespace","task":"Añadir un general tablespace","category":"Almacenamiento","command":"CREATE TABLESPACE ssd_ts ADD DATAFILE 'ssd_ts.ibd' ENGINE=InnoDB;","what":"Crea un general tablespace de InnoDB que pueden compartir varias tablas.","when":"Para agrupar tablas en un archivo/disco concreto, o para reducir el número de archivos.","caution":"Si la ruta de DATAFILE es relativa, se crea bajo datadir; colocarlo en otro disco requiere una ruta absoluta y los permisos necesarios. Para mover una tabla aquí: ALTER TABLE customer TABLESPACE ssd_ts; Un tablespace solo se puede eliminar cuando está vacío.","tags":["tablespace","innodb","storage","create"]},{"id":"optimize-table","task":"OPTIMIZE de una tabla","category":"Almacenamiento","command":"OPTIMIZE TABLE customer;","what":"Reconstruye la tabla para eliminar la fragmentación, recuperar el espacio no usado y actualizar las estadísticas de los índices.","when":"Para recuperar espacio en una tabla InnoDB inflada por muchos DELETE/UPDATE.","caution":"En InnoDB, OPTIMIZE se ejecuta en realidad como un ALTER online que reconstruye la tabla, pero aun así necesita I/O y espacio temporal en disco; las tablas grandes requieren un espacio libre en disco igual al tamaño de la tabla. Ejecútelo con poco tráfico para reducir los bloqueos.","tags":["optimize","table","defragment","storage"]},{"id":"table-schema-size","task":"Consultar el tamaño de las tablas y los esquemas","category":"Almacenamiento","command":"SELECT table_schema AS schema_name, table_name AS table_name, ROUND((data_length + index_length) / 1024 / 1024, 1) AS size_mb FROM information_schema.tables WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','sys') ORDER BY (data_length + index_length) DESC LIMIT 20;","what":"Lista las 20 tablas más grandes por tamaño combinado de datos e índices (MB).","when":"Para analizar el uso de disco y averiguar qué tablas ocupan espacio.","caution":"Los tamaños de information_schema son aproximados para InnoDB. Para los totales por esquema, haga GROUP BY solo por table_schema, en lugar de table_name.","tags":["size","table","schema","information-schema"]},{"id":"analyze-table","task":"Actualizar las estadísticas de una tabla (ANALYZE TABLE)","category":"Mantenimiento","command":"ANALYZE TABLE customer;","what":"Vuelve a calcular las estadísticas de distribución de índices/claves de la tabla para que el optimizador pueda elegir mejores planes.","when":"Cuando aparecen planes de consulta malos tras cambios masivos de datos.","caution":"Mientras se ejecuta se toma un breve metadata lock sobre la tabla. En InnoDB se basa en muestras; aumente innodb_stats_persistent_sample_pages para mayor precisión.","tags":["analyze","statistics","optimizer","maintenance"]},{"id":"rebuild-table","task":"Reconstruir una tabla/sus índices","category":"Mantenimiento","command":"ALTER TABLE customer ENGINE=InnoDB;","what":"Reconstruye la tabla con el mismo motor; recrea los índices y las páginas de datos para eliminar la fragmentación y el bloat de los índices.","when":"Para refrescar una tabla con un ALTER nulo, o para reparar una disposición de páginas dañada.","caution":"Las tablas grandes requieren un espacio temporal en disco igual al tamaño de la tabla. Para reducir los bloqueos: ALTER TABLE customer ENGINE=InnoDB, ALGORITHM=INPLACE, LOCK=NONE;","tags":["rebuild","alter","innodb","maintenance"]},{"id":"check-table","task":"Comprobar la integridad de una tabla (CHECK TABLE)","category":"Mantenimiento","command":"CHECK TABLE customer;","what":"Comprueba si la tabla y sus índices tienen corrupción y devuelve un estado.","when":"Ante errores inesperados o cuando se sospecha corrupción.","caution":"Durante la comprobación se toma un bloqueo de lectura sobre la tabla. Si se encuentra corrupción, InnoDB normalmente se repara mediante dump/restore; REPAIR TABLE solo funciona con motores como MyISAM/Archive.","tags":["check","integrity","corruption","maintenance"]},{"id":"set-global-persist","task":"Cambiar una variable global y hacerla persistente","category":"Parámetros","command":"SET PERSIST innodb_buffer_pool_size = 4294967296;","what":"Define la variable tanto en el servidor en ejecución como de forma persistente entre reinicios (mysqld-auto.cnf).","when":"Para cambiar una variable del servidor de forma permanente sin reiniciar.","caution":"SET PERSIST es una función de MySQL 8.0+; MariaDB no la soporta (allí, edite my.cnf y reinicie). Use SET GLOBAL para un cambio solo en tiempo de ejecución. Las variables que solo cambian con reinicio requieren SET PERSIST_ONLY.","since":"MySQL 8.0","note":"SET PERSIST llegó en MySQL 8.0 y hace persistente una variable escribiéndola en mysqld-auto.cnf; antes (y en MariaDB) la persistencia requería editar my.cnf manualmente y reiniciar.","tags":["set-persist","global","variable","parameter"]},{"id":"show-variables","task":"Ver el valor actual de una variable","category":"Parámetros","command":"SHOW VARIABLES LIKE 'innodb_buffer_pool_size';","what":"Muestra el valor efectivo de la variable de servidor indicada (o de las que coinciden con el patrón).","when":"Para comprobar el valor en ejecución de un parámetro.","caution":"Use SHOW GLOBAL VARIABLES para el valor global en lugar del de sesión. Para ver de dónde procede el valor (archivo, línea de comandos, persist): SELECT * FROM performance_schema.variables_info WHERE variable_name = 'innodb_buffer_pool_size';","tags":["show-variables","parameter","current-value","config"]},{"id":"flush-privileges","task":"Recargar las tablas de privilegios (FLUSH PRIVILEGES)","category":"Parámetros","command":"FLUSH PRIVILEGES;","what":"Recarga en memoria las tablas de privilegios como mysql.user después de haberlas modificado directamente.","when":"Para activar los cambios hechos con INSERT/UPDATE manuales en las tablas de privilegios.","caution":"GRANT/REVOKE/CREATE USER ya aplican los privilegios de inmediato; FLUSH PRIVILEGES después de ellos es innecesario. Solo hace falta cuando modifica directamente las tablas de privilegios.","tags":["flush-privileges","grants","privileges","reload"]},{"id":"setup-replica","task":"Configurar una réplica (CHANGE REPLICATION SOURCE)","category":"Replicación","command":"CHANGE REPLICATION SOURCE TO SOURCE_HOST='source_host', SOURCE_PORT=3306, SOURCE_USER='replicator', SOURCE_PASSWORD='secret', SOURCE_AUTO_POSITION=1;","what":"Configura este servidor como una réplica que sigue al origen indicado usando auto-positioning basado en GTID.","when":"Para configurar una réplica nueva o cambiar el origen de la replicación.","caution":"Es terminología de MySQL 8.0.23+; las versiones anteriores usan CHANGE MASTER TO ... MASTER_HOST (un alias en 8.0.23–8.3, eliminado en 8.4). SOURCE_AUTO_POSITION=1 requiere GTID habilitado (gtid_mode=ON); de lo contrario hay que indicar SOURCE_LOG_FILE y SOURCE_LOG_POS. La replicación debe estar detenida antes de ejecutar el comando. Si el usuario de replicación se creó con caching_sha2_password, el valor por defecto de 8.0+, la conexión requiere TLS: añada SOURCE_SSL=1 o, sin TLS, defina GET_SOURCE_PUBLIC_KEY=1; de lo contrario el I/O thread no consigue conectarse y da el error 2061.","since":"MySQL 8.0.23","note":"CHANGE REPLICATION SOURCE TO (SOURCE_HOST, etc.) llegó en MySQL 8.0.23; antes se usaba CHANGE MASTER TO ... MASTER_HOST / MASTER_AUTO_POSITION.","tags":["replication","change-source","replica","setup"]},{"id":"start-stop-replica","task":"Iniciar / detener una réplica","category":"Replicación","command":"STOP REPLICA; START REPLICA;","what":"Detiene y vuelve a iniciar los threads de replicación I/O y SQL.","when":"Para mantenimiento, cambios de configuración o para corregir un error de replicación.","caution":"Es terminología de MySQL 8.0.22+; las versiones anteriores usan STOP SLAVE / START SLAVE. Solo para el SQL thread: STOP REPLICA SQL_THREAD; Tras STOP REPLICA puede ejecutar CHANGE REPLICATION SOURCE.","since":"MySQL 8.0.22","note":"START REPLICA / STOP REPLICA llegaron en MySQL 8.0.22; antes se usaba START SLAVE / STOP SLAVE (la sintaxis antigua funciona como alias en 8.0.22–8.3 y se eliminó en 8.4).","tags":["replication","start","stop","replica"]},{"id":"replica-status","task":"Ver el estado de la réplica (SHOW REPLICA STATUS)","category":"Replicación","command":"SHOW REPLICA STATUS\\G","what":"Muestra en detalle la conexión de la réplica con el origen, el estado de los threads, el lag y los posibles errores.","when":"Para comprobar si la replicación está sana y cuánto va retrasada.","caution":"Es terminología de MySQL 8.0.22+; las versiones anteriores usan SHOW SLAVE STATUS (un alias en 8.0.22–8.3, eliminado en 8.4). Campos clave: Replica_IO_Running y Replica_SQL_Running deben ser 'Yes', Seconds_Behind_Source muestra el lag, Last_Error muestra el error.","since":"MySQL 8.0.22","note":"SHOW REPLICA STATUS llegó en MySQL 8.0.22; antes se usaba SHOW SLAVE STATUS y los campos se llamaban, por ejemplo, Slave_IO_Running / Seconds_Behind_Master.","tags":["replication","status","replica","lag"]},{"id":"skip-gtid-transaction","task":"Omitir una transacción rota (GTID)","category":"Replicación","command":"STOP REPLICA; SET GTID_NEXT='aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:15'; BEGIN; COMMIT; SET GTID_NEXT='AUTOMATIC'; START REPLICA;","what":"Hace commit de una transacción vacía para el GTID indicado, de modo que esa transacción cuente como 'aplicada', y permite que la replicación continúe.","when":"Cuando un evento duplicado/no aplicable en la réplica detiene la replicación, para omitir ese evento.","caution":"Omitir una transacción crea una inconsistencia de datos entre la réplica y el origen; úselo solo cuando conozca el efecto del evento omitido. Tome el valor de GTID de Last_Error / Retrieved_Gtid_Set en SHOW REPLICA STATUS. Si GTID está desactivado, use SET GLOBAL sql_replica_skip_counter = 1; (8.0.26+; antes sql_slave_skip_counter, un alias obsoleto en 8.0.26+).","tags":["replication","gtid","skip","recovery"]},{"id":"kill-query-connection","task":"Matar una consulta o una conexión (KILL)","category":"Sesión","command":"KILL QUERY 4567;","what":"Termina la consulta que se está ejecutando en el ID de conexión indicado sin cerrar la conexión.","when":"Para detener una consulta larga/atascada.","caution":"Para cerrar toda la conexión (sesión): KILL CONNECTION 4567; (o simplemente KILL 4567). Si se mata una transacción de escritura en curso, se revierte, lo que puede tardar mucho en tablas grandes.","tags":["kill","query","connection","session"]},{"id":"process-list","task":"Ver la lista de procesos","category":"Sesión","command":"SELECT id, user, host, db, command, time, state, LEFT(info, 100) AS query FROM performance_schema.processlist WHERE command <> 'Sleep' ORDER BY time DESC;","what":"Lista todas las conexiones no inactivas, la consulta que ejecutan y el tiempo transcurrido.","when":"Para diagnosticar las sesiones activas y las consultas de larga duración.","caution":"Atajo: SHOW FULL PROCESSLIST; performance_schema.processlist ofrece todas las columnas y se puede filtrar con WHERE. La columna time está en segundos.","tags":["processlist","session","activity","performance-schema"]},{"id":"blocking-locks","task":"Ver los bloqueos que bloquean (data_lock_waits)","category":"Sesión","command":"SELECT r.trx_id AS waiting_trx, r.trx_mysql_thread_id AS waiting_thread, LEFT(r.trx_query,80) AS waiting_query, b.trx_id AS blocking_trx, b.trx_mysql_thread_id AS blocking_thread, LEFT(b.trx_query,80) AS blocking_query FROM performance_schema.data_lock_waits w JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_engine_transaction_id JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_engine_transaction_id;","what":"Muestra qué transacción está bloqueada por cuál, con las consultas y los ID de thread relacionados.","when":"Para diagnosticar las esperas de bloqueo y las ralentizaciones causadas por deadlocks.","caution":"performance_schema.data_lock_waits llegó en MySQL 8.0; las versiones anteriores usan information_schema.innodb_lock_waits. Para detener el thread bloqueante, use KILL blocking_thread;.","since":"MySQL 8.0","note":"performance_schema.data_lock_waits llegó en MySQL 8.0; en MySQL 5.7 y anteriores se usaba information_schema.innodb_lock_waits para la misma información.","tags":["blocking","lock","data-lock-waits","deadlock"]}]},"recipes/mysql-ext.json":{"engineId":"mysql","engine":"MySQL","order":2,"recipes":[{"id":"replica-applier-health","task":"Monitorear el lag de la réplica y los errores de los workers del applier (performance_schema)","category":"Replicación","command":"-- Receiver (I/O) thread: connection state and last error\nSELECT CHANNEL_NAME, SERVICE_STATE, LAST_ERROR_NUMBER,\n       LEFT(LAST_ERROR_MESSAGE, 100) AS last_error,\n       LAST_QUEUED_TRANSACTION, LAST_HEARTBEAT_TIMESTAMP\nFROM performance_schema.replication_connection_status;\n\n-- Applier workers: error, applied GTID and real lag\nSELECT CHANNEL_NAME, WORKER_ID, SERVICE_STATE, LAST_ERROR_NUMBER,\n       LEFT(LAST_ERROR_MESSAGE, 120) AS last_error,\n       LAST_APPLIED_TRANSACTION,\n       APPLYING_TRANSACTION,\n       IF(APPLYING_TRANSACTION = '', 0,\n          TIMESTAMPDIFF(SECOND, APPLYING_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP, NOW(6))) AS current_lag_sec,\n       TIMESTAMPDIFF(MICROSECOND, LAST_APPLIED_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP,\n                     LAST_APPLIED_TRANSACTION_END_APPLY_TIMESTAMP) / 1000000 AS last_trx_lag_sec\nFROM performance_schema.replication_applier_status_by_worker\nORDER BY CHANNEL_NAME, WORKER_ID;","what":"Desglosa por canal y por worker lo que SHOW REPLICA STATUS resume en una sola fila: el estado del receiver thread, el último error de cada worker, el GTID aplicado / en aplicación y el lag real calculado a partir del timestamp de commit original.","when":"Cuando Seconds_Behind_Source parece poco fiable (salta entre 0 y valores grandes, NULL), para encontrar qué worker está atascado en una réplica multi-threaded, y para alimentar el monitoreo con una métrica de lag precisa.","caution":"current_lag_sec solo tiene sentido mientras se está aplicando una transacción; una réplica inactiva devuelve 0. Para ver si el receiver también va retrasado, compare LAST_QUEUED_TRANSACTION con el gtid_executed del origen. El timestamp de commit original procede del reloj del origen; el desfase de reloj entre servidores (NTP) distorsiona el resultado. Seconds_Behind_Source se mantiene cerca de cero al principio de una transacción grande y salta al final; las transacciones largas hacen que esa métrica sea engañosa.","note":"Las columnas ORIGINAL_COMMIT_TIMESTAMP / END_APPLY_TIMESTAMP llegaron en 8.0.2 y cubren toda la cadena (origen → intermedia → réplica); 5.7 solo tiene Seconds_Behind_Master.","since":"MySQL 8.0","tags":["replication","lag","applier","worker","performance_schema","delay"]},{"id":"replica-parallel-applier","task":"Ajustar la réplica multi-threaded (replica_parallel_workers)","category":"Replicación","command":"-- Replica: stop the applier, set worker count and commit order\nSTOP REPLICA SQL_THREAD;\nSET PERSIST replica_parallel_workers = 8;\nSET PERSIST replica_preserve_commit_order = ON;\nSTART REPLICA SQL_THREAD;\nSELECT COUNT(*) AS workers FROM performance_schema.replication_applier_status_by_worker;\n\n-- Source (8.0 only): switch dependency tracking to WRITESET (default in 8.4, variable removed)\nSET PERSIST binlog_transaction_dependency_tracking = WRITESET;","what":"Define el número de workers que aplican transacciones en paralelo en la réplica; replica_preserve_commit_order mantiene el orden de commit idéntico al del origen. El seguimiento de dependencias WRITESET en el origen permite aplicar en paralelo transacciones que tocan filas distintas aunque se hayan confirmado en serie en el origen.","when":"Cuando la réplica no puede seguir el ritmo de escritura del origen y el lag se acumula mientras el applier funciona en la práctica con un solo thread.","caution":"Un nuevo número de workers surte efecto cuando se reinicia el applier. Ejecutar más workers que núcleos de CPU añade sobrecarga de coordinación; empiece en el rango de 8–16 y vigile el lag y los errores de los workers. Una sola transacción enorme o un DML pesado sobre una tabla sin PK no se pueden paralelizar, y suelen ser la causa real de un lag persistente. replica_preserve_commit_order=OFF da más paralelismo pero permite breves commits fuera de orden en la réplica; manténgalo en ON en las réplicas que sirven lecturas.","note":"Desde 8.0.27 los valores por defecto son replica_parallel_workers=4, replica_parallel_type=LOGICAL_CLOCK y replica_preserve_commit_order=ON. Antes de 8.0.26 las variables se llamaban slave_parallel_workers / slave_preserve_commit_order. binlog_transaction_dependency_tracking quedó obsoleto en 8.0.35 y se eliminó en 8.4, donde el comportamiento WRITESET es el predeterminado.","since":"MySQL 8.0.26","tags":["replication","parallel","replica_parallel_workers","writeset","lag","mta"]},{"id":"replica-fix-row-conflict","task":"Reparar una réplica rota: conflictos de filas 1062 / 1032","category":"Replicación","command":"-- 1) Failing worker, error and GTID\nSELECT WORKER_ID, LAST_ERROR_NUMBER, LEFT(LAST_ERROR_MESSAGE, 250) AS last_error, APPLYING_TRANSACTION\nFROM performance_schema.replication_applier_status_by_worker\nWHERE LAST_ERROR_NUMBER <> 0;\n\n-- 2) Shell: decode the row images of that transaction from the relay log\nmysqlbinlog --base64-output=DECODE-ROWS -vv --include-gtids='af6bc361-c353-11f1-8f12-f69e63ad6c0e:11' /var/lib/mysql/db2-relay-bin.0* | grep '^###'\n\n-- 3a) 1062 Duplicate entry: delete the extra row on the replica without binlogging\nSET SESSION sql_log_bin = 0;\nDELETE FROM shop.orders WHERE id = 20001;\n-- 3b) 1032 Can't find record: insert the missing row from the WHERE (before image) values\nINSERT INTO shop.orders (id, cust, amount, note, created) VALUES (6, 6, 9.00, 'n6', '2026-10-08 20:09:44');\nSET SESSION sql_log_bin = 1;\n\n-- 4) Start the applier and verify\nSTART REPLICA SQL_THREAD;\nSELECT SERVICE_STATE, LAST_ERROR_NUMBER FROM performance_schema.replication_applier_status_by_coordinator;","what":"Corrige las dos roturas más comunes de la replicación basada en filas (1062 clave duplicada, 1032 fila no encontrada) sin omitir la transacción, llevando la fila de la réplica al estado que espera el origen; se preserva la consistencia de los datos.","when":"Cuando Replica_SQL_Running = No con el error 1062 / 1032; como reparación correcta en lugar de omitir el GTID con una transacción vacía.","caution":"La corrección en la réplica debe ejecutarse con sql_log_bin=0; de lo contrario crea un GTID errant que da problemas en el failover. Si super_read_only=ON, desactívelo temporalmente y vuelva a activarlo después. Los valores de la before-image suponen binlog_row_image=FULL; con MINIMAL solo se registra la PK y el resto de la fila debe leerse del origen. Un conflicto en una tabla suele significar que hay más; tras la reparación compare la tabla con el origen usando pt-table-checksum. Ruta del relay log: SELECT @@relay_log_basename;","note":"replica_exec_mode=IDEMPOTENT se traga estos errores sin avisar; está pensado para NDB y casos especiales y no debe dejarse activado de forma permanente. Si realmente hay que omitir la transacción, vea la receta skip-gtid-transaction.","since":"MySQL 8.0.22","tags":["replication","1062","1032","mysqlbinlog","relay log","broken replica"]},{"id":"gtid-errant-transactions","task":"Encontrar y neutralizar transacciones GTID errant","category":"Replicación","command":"-- 1) On the source: get gtid_executed\nSELECT @@GLOBAL.gtid_executed;\n\n-- 2) On the replica: GTIDs missing on the source (empty result = clean)\nSELECT GTID_SUBTRACT(@@GLOBAL.gtid_executed,\n       'af6bc361-c353-11f1-8f12-f69e63ad6c0e:1-13') AS errant_gtids;\n\n-- 3) If the content is harmless: commit an empty transaction per errant GTID on the source\nSET GTID_NEXT = 'b030e5a6-c353-11f1-a887-523817ec642a:1'; BEGIN; COMMIT;\nSET GTID_NEXT = 'b030e5a6-c353-11f1-a887-523817ec642a:2'; BEGIN; COMMIT;\nSET GTID_NEXT = 'AUTOMATIC';","what":"Detecta, mediante la diferencia de conjuntos GTID, las transacciones ejecutadas directamente en una réplica sin equivalente en el origen (errant), y alinea los conjuntos GTID en toda la topología marcándolas con transacciones vacías en el origen.","when":"Como comprobación rutinaria previa a un failover / switchover; cuando Orchestrator o MySQL Shell se niegan a promover una réplica por GTID errant, o una réplica falla con 1236 al conectarse a un origen nuevo.","caution":"Averigüe primero qué hizo la transacción errant a partir del binlog de la réplica (mysqlbinlog --include-gtids=...). Si cambió datos, la transacción vacía solo tapa el GTID y la diferencia de datos permanece; reconstruya la réplica (CLONE) o alinee los datos manualmente. Las transacciones vacías confirmadas en el origen se propagan a todas las réplicas; es lo que se pretende. Mantenga super_read_only=ON en las réplicas para evitar que se repita.","note":"Una vez promovida la réplica, las demás réplicas le solicitan estos GTID; si ese binlog se ha purgado, la replicación se rompe con el error 1236. Por eso los GTID errant son un riesgo que permanece invisible hasta el failover.","since":"MySQL 5.6","tags":["gtid","errant","gtid_subtract","failover","replication","empty transaction"]},{"id":"replica-rebuild-clone","task":"Reconstruir una réplica desde cero con el plugin CLONE","category":"Replicación","command":"-- Donor (healthy replica or source)\nINSTALL PLUGIN clone SONAME 'mysql_clone.so';\nCREATE USER 'clone_donor'@'%' IDENTIFIED BY 'Cl0ne#Donor';\nGRANT BACKUP_ADMIN ON *.* TO 'clone_donor'@'%';\n\n-- Recipient (replica to rebuild; its data is replaced)\nINSTALL PLUGIN clone SONAME 'mysql_clone.so';\nSET GLOBAL clone_valid_donor_list = 'db1:3306';\nCLONE INSTANCE FROM 'clone_donor'@'db1':3306 IDENTIFIED BY 'Cl0ne#Donor';\n\n-- After the restart: verify and start replication\nSELECT STATE, ERROR_NO, BINLOG_FILE, BINLOG_POSITION, GTID_EXECUTED FROM performance_schema.clone_status;\nSELECT STAGE, STATE, ROUND(DATA / 1024 / 1024) AS data_mb FROM performance_schema.clone_progress;\nCHANGE REPLICATION SOURCE TO SOURCE_HOST = 'db1', SOURCE_PORT = 3306, SOURCE_USER = 'repl', SOURCE_PASSWORD = 'Repl#2024', SOURCE_AUTO_POSITION = 1, SOURCE_SSL = 1;\nSTART REPLICA;","what":"Copia físicamente por la red los datos InnoDB del donante, reinicia el receptor y traslada el estado de GTID; genera una réplica consistente sin pasos separados de backup, transferencia y restauración.","when":"Para reconstruir una réplica corrupta, una con GTID errant o una tan retrasada que sus binlogs se han purgado, o para añadir una réplica nueva.","caution":"Todos los datos del receptor (usuarios incluidos) se sustituyen por la copia del donante; la contraseña de root pasa a ser la del donante. El donante y el receptor necesitan la misma serie de versiones, plataforma y conjunto de plugins (antes de 8.0.37 se requería exactamente la misma versión puntual). El usuario del receptor que ejecuta el comando necesita CLONE_ADMIN. Al terminar el clon mysqld se reinicia solo; sin un supervisor como systemd / mysqld_safe devuelve 3707 'Restart server failed (mysqld is not managed by supervisor process)'. No es un fallo: el clon está completo, arranque el servidor manualmente. El donante soporta mucha carga de IO / red; limítela con clone_max_concurrency y clone_max_data_bandwidth. Con GTID desactivado, use BINLOG_FILE / BINLOG_POSITION de clone_status como SOURCE_LOG_FILE / SOURCE_LOG_POS.","note":"Solo se clonan los datos InnoDB; las tablas MyISAM y CSV llegan vacías. En InnoDB Cluster, addInstance(..., {recoveryMethod: 'clone'}) usa el mismo mecanismo.","since":"MySQL 8.0.17","tags":["clone","rebuild","replica","provisioning","gtid","reseed"]},{"id":"delayed-replica-recover","task":"Recuperarse de un DROP / DELETE erróneo con una réplica diferida (SOURCE_DELAY + UNTIL)","category":"Replicación","command":"-- Setup: the replica applies events one hour behind\nSTOP REPLICA;\nCHANGE REPLICATION SOURCE TO SOURCE_DELAY = 3600;\nSTART REPLICA;\n\n-- During the incident: stop the applier (the receiver keeps fetching binlogs)\nSTOP REPLICA SQL_THREAD;\n\n-- Find the bad transaction's GTID in the source binlog, apply up to just before it\nCHANGE REPLICATION SOURCE TO SOURCE_DELAY = 0;\nSTART REPLICA SQL_THREAD UNTIL SQL_BEFORE_GTIDS = 'af6bc361-c353-11f1-8f12-f69e63ad6c0e:17';\nSELECT SERVICE_STATE FROM performance_schema.replication_applier_status_by_coordinator;","what":"Hace que la réplica aplique los eventos con un retraso fijo; cuando un DROP / DELETE erróneo afecta al origen, se avanza la réplica hasta justo antes de esa transacción y se recuperan desde ella los datos perdidos.","when":"Como capa de recuperación rápida frente a errores humanos en bases de datos grandes donde restaurar desde un backup llevaría horas.","caution":"Cuando el coordinator está en OFF, los datos están en el estado justo anterior a la operación errónea; vuelque la tabla afectada con mysqldump y cárguela en el origen. Después vuelva a poner la réplica en modo diferido; si aplica también la transacción errónea, los datos se pierden aquí también. Una réplica diferida no debe ser candidata a failover (exclúyala en Orchestrator / Router / ProxySQL). Los relay logs se acumulan durante todo el retraso; dimensione el disco en consecuencia.","note":"Antes de 8.0.23 use CHANGE MASTER TO MASTER_DELAY. El retraso se mide desde el commit en el origen inmediato, por lo que las réplicas intermedias diferidas de una cadena se suman. SQL_BEFORE_GTIDS se detiene antes del GTID indicado, SQL_AFTER_GTIDS también lo aplica; antes de 8.2 (incluida 8.0), usar SQL_AFTER_GTIDS con un applier multi-threaded genera una advertencia y la réplica pasa a modo de un solo thread.","since":"MySQL 8.0.23","tags":["delayed replica","source_delay","until","sql_before_gtids","recovery","drop table"]},{"id":"mysqldump-consistent","task":"Tomar un backup lógico consistente con información de GTID usando mysqldump","category":"Backup y PITR","command":"mysqldump -h db1 -u backup -p \\\n  --single-transaction --source-data=2 --set-gtid-purged=ON \\\n  --routines --events --triggers --hex-blob \\\n  --all-databases | gzip > /backup/full_$(date +%F_%H%M).sql.gz\n\n# Restore: GTID sets must not overlap on the target\nzcat /backup/full_2026-10-08_0200.sql.gz | mysql -h db2 -u root -p","what":"Vuelca las tablas InnoDB dentro de un único snapshot consistente y escribe al principio el archivo / posición del binlog (como comentario) y una sentencia SET @@GLOBAL.GTID_PURGED, de modo que el volcado pueda inicializar una réplica o servir como punto de partida de un PITR basado en binlog.","when":"Para bases de datos de hasta unas pocas decenas de GB, para trasladar esquemas / tablas y para migraciones lógicas entre versiones.","caution":"--single-transaction solo es consistente para InnoDB; las tablas MyISAM salen inconsistentes. Un DDL (ALTER / RENAME / TRUNCATE) sobre una tabla durante el volcado hace que este falle o que omita la tabla. --source-data / --set-gtid-purged toman un breve global read lock al inicio (requiere el privilegio RELOAD); si en ese momento hay una consulta larga en ejecución, todas las escrituras se detienen mientras el bloqueo espera. Un volcado parcial (--databases) sigue escribiendo todos los GTID; use --set-gtid-purged=OFF al trasladar un subconjunto a otro servidor. El volcado es de un solo thread; para datos grandes es preferible el dump de MySQL Shell o XtraBackup.","note":"--source-data sustituyó a --master-data en 8.0.26. mysqlpump quedó obsoleto en 8.0.34 y se eliminó en 8.4; use util.dumpInstance de MySQL Shell para backups lógicos en paralelo.","since":"MySQL 8.0.26","tags":["mysqldump","backup","single-transaction","gtid","logical backup","dump"]},{"id":"mysqlsh-dump-load","task":"Dump / load en paralelo con MySQL Shell (util.dumpInstance, loadDump)","category":"Backup y PITR","command":"# Parallel, zstd-compressed, consistent instance dump\nmysqlsh backup@db1:3306 -- util dump-instance /backup/dump_20261008 --threads=8 --compression=zstd --consistent=true\n# Single schema\nmysqlsh backup@db1:3306 -- util dump-schemas shop --outputUrl=/backup/shop_20261008 --threads=8\n\n# Load: local_infile must be temporarily ON on the target\nmysql -h db2 -u root -p -e \"SET GLOBAL local_infile = ON\"\nmysqlsh root@db2:3306 -- util load-dump /backup/dump_20261008 --threads=8 --skipBinlog=false\nmysql -h db2 -u root -p -e \"SET GLOBAL local_infile = OFF\"","what":"Divide las tablas en chunks y las vuelca / carga con muchos threads; tanto el volcado como la restauración son muchas veces más rápidos que mysqldump. El directorio de volcado guarda las coordenadas de GTID y binlog en @.json.","when":"Para backups lógicos de cientos de GB, migraciones lógicas durante upgrades y traslados de on-prem → cloud (RDS, HeatWave).","caution":"--consistent=true toma un breve global read lock / backup lock; las escrituras esperan si hay una consulta larga en ejecución. loadDump usa LOAD DATA LOCAL, por lo que el destino necesita local_infile=ON, que debería desactivarse después. Una carga interrumpida se reanuda desde donde se detuvo (archivo de progreso); use --resetProgress para empezar de nuevo. Las cuentas de usuario no se cargan por defecto (--loadUsers=true). --skipBinlog=true deshabilita el binlog durante la carga; manténgalo en false si el destino tiene réplicas.","note":"No es un backup físico; para PITR aplique los binlogs a partir del valor gtidExecuted del @.json del volcado. Las opciones ocimds / compatibility generan volcados que respetan las restricciones de HeatWave / RDS.","since":"MySQL Shell 8.0.21","tags":["mysqlsh","dumpinstance","loaddump","parallel","logical backup","migration"]},{"id":"xtrabackup-full-incremental","task":"Backup físico completo + incremental y restauración con XtraBackup","category":"Backup y PITR","command":"# Full backup\nxtrabackup --backup --host=127.0.0.1 --user=bkp --password='Bkp#2024' --target-dir=/backup/full --parallel=4\n# Incremental (based on the previous backup)\nxtrabackup --backup --host=127.0.0.1 --user=bkp --password='Bkp#2024' --target-dir=/backup/inc1 --incremental-basedir=/backup/full\n\n# Prepare: full and intermediate incrementals with --apply-log-only, final step without it\nxtrabackup --prepare --apply-log-only --target-dir=/backup/full\nxtrabackup --prepare --apply-log-only --target-dir=/backup/full --incremental-dir=/backup/inc1\nxtrabackup --prepare --target-dir=/backup/full\n\n# Restore: mysqld stopped and datadir empty\nsystemctl stop mysqld\nxtrabackup --copy-back --target-dir=/backup/full --datadir=/var/lib/mysql\nchown -R mysql:mysql /var/lib/mysql\nsystemctl start mysqld\ncat /backup/full/xtrabackup_binlog_info","what":"Toma un backup físico en caliente copiando los archivos InnoDB y el redo log mientras el servidor funciona; un backup incremental contiene solo las páginas cambiadas desde el backup anterior. El paso prepare aplica el redo y hace consistente el backup.","when":"Para bases de datos grandes que necesitan restauraciones rápidas (muchas veces más rápidas que las lógicas), para inicializar réplicas y para estrategias de full diario + incremental cada hora.","caution":"La versión de XtraBackup debe coincidir con la serie de versiones del servidor (servidor 8.0 → XtraBackup 8.0, 8.4 → 8.4); si el servidor usa un formato de redo / data dictionary más nuevo que el build de XtraBackup, el backup se rechaza. Un backup sin prepare es inconsistente. Olvidar --apply-log-only en los pasos intermedios inutiliza los incrementales posteriores. El datadir debe estar vacío para la restauración; --move-back ahorra disco pero consume el backup. Usuario de backup: BACKUP_ADMIN, PROCESS, RELOAD, LOCK TABLES, REPLICATION CLIENT y SELECT sobre performance_schema.log_status, keyring_component_status, replication_group_members.","note":"xtrabackup_binlog_info contiene el archivo / posición del binlog y el conjunto GTID del backup; el PITR reproduce los binlogs desde ese punto. Para transmitir a otro host use --stream=xbstream | ssh host 'xbstream -x -C /backup'.","since":"XtraBackup 8.0","tags":["xtrabackup","physical backup","incremental","prepare","restore","percona"]},{"id":"pitr-binlog-replay","task":"Recuperación a un punto en el tiempo con binlogs: encontrar y omitir la transacción errónea","category":"Backup y PITR","command":"# 1) Find the bad event's GTID and position (narrow by time range)\nmysqlbinlog --base64-output=DECODE-ROWS -vv --start-datetime='2026-10-08 20:00:00' --stop-datetime='2026-10-08 20:30:00' /var/lib/mysql/binlog.000003 | grep -B12 -iE '^(DROP|TRUNCATE)' | grep -E 'GTID_NEXT|^# at|^#[0-9]{6}|^DROP|^TRUNCATE'\n\n# 2) Restore the backup on a separate server (the dump sets GTID_PURGED)\nzcat /backup/full_2026-10-08_0200.sql.gz | mysql -h restore-db -u root -p\n\n# 3a) Replay all binlogs skipping only the bad transaction\nmysqlbinlog --exclude-gtids='af6bc361-c353-11f1-8f12-f69e63ad6c0e:23' /binlogs/binlog.000003 /binlogs/binlog.000004 | mysql -h restore-db -u root -p\n# 3b) Or stop right before the bad event's GTID event\nmysqlbinlog --stop-position=537205 /binlogs/binlog.000003 | mysql -h restore-db -u root -p","what":"Tras restaurar el backup, reproduce los binlogs en orden para llevar la base de datos al punto deseado. Con GTID activado, las transacciones que ya están en el backup se omiten automáticamente; --exclude-gtids elimina solo la operación errónea y conserva todos los cambios posteriores.","when":"Tras un DROP / DELETE / UPDATE erróneo, para recuperarse sin perder los cambios hechos entre el backup y el incidente.","caution":"Restaure en un servidor aparte, no en producción; verifique y luego traslade solo la tabla afectada. --start/--stop-datetime se interpretan en la zona horaria de la máquina que ejecuta mysqlbinlog. --stop-position solo se aplica al último archivo indicado; con varios archivos, reproduzca por completo los anteriores en un comando aparte. --exclude-gtids solo es seguro para una operación independiente; si transacciones posteriores escriben en la tabla eliminada, también fallan. Los binlogs necesarios no deben purgarse en el origen; mantenga una retención de binlogs más larga que al menos un intervalo de backup.","note":"Ejecute mysqlbinlog y mysql en una sola tubería con todos los archivos en un solo comando; reproducir los archivos de uno en uno rompe las tablas temporales y las transacciones que abarcan varios archivos. Para conjuntos de binlogs muy grandes, una alternativa es colocar los binlogs como relay logs en el servidor de restauración y aplicarlos con el SQL thread (paralelo).","since":"MySQL 8.0","tags":["pitr","mysqlbinlog","binlog","point in time","exclude-gtids","recovery"]},{"id":"innodb-cluster-status","task":"Comprobar la salud de InnoDB Cluster / Group Replication","category":"InnoDB Cluster","command":"# Shell: topology, primary, fault tolerance, lag\nmysqlsh icadmin@db1:3306 -- cluster status --extended=1\nmysqlsh icadmin@db1:3306 -- cluster list-routers\n\n-- SQL: group members and queues from any member\nSELECT m.MEMBER_HOST, m.MEMBER_PORT, m.MEMBER_STATE, m.MEMBER_ROLE, m.MEMBER_VERSION,\n       s.COUNT_TRANSACTIONS_IN_QUEUE AS certify_queue,\n       s.COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE AS apply_queue,\n       s.COUNT_CONFLICTS_DETECTED AS conflicts\nFROM performance_schema.replication_group_members m\nJOIN performance_schema.replication_group_member_stats s ON s.MEMBER_ID = m.MEMBER_ID\nORDER BY m.MEMBER_ROLE, m.MEMBER_HOST;","what":"Muestra el estado general del clúster (OK / OK_NO_TOLERANCE / NO_QUORUM), el primario, el estado de cada miembro y las colas de certificación / applier; las colas revelan qué miembro va retrasado y el riesgo de flow control.","when":"Antes y después de cada intervención; ante alertas, ralentizaciones de escritura o cuando un miembro muestra RECOVERING / ERROR / UNREACHABLE.","caution":"statusText debería decir 'can tolerate up to ONE failure' (3 miembros). Con NO_QUORUM las escrituras se detienen; use forceQuorumUsingPartitionOf solo cuando esté seguro de que la otra partición está caída, o tendrá split-brain. Un miembro cuyo apply_queue no deja de crecer dispara el flow control y ralentiza las escrituras en el primario. Un RECOVERING prolongado significa que se está ejecutando un clone / recuperación incremental; ante ERROR revise el error log e intente cluster.rejoinInstance(). Los caracteres especiales de las contraseñas (#, @) deben codificarse en URL en la URI (# → %23).","note":"MEMBER_COMMUNICATION_STACK llegó en 8.0.27; el stack MYSQL transporta el tráfico de GR por 3306 con TLS y autenticación de usuarios normales (XCOM usa un puerto aparte, normalmente 33061). En 8.4 group_replication_consistency vale por defecto BEFORE_ON_PRIMARY_FAILOVER y exit_state_action vale por defecto OFFLINE_MODE.","since":"MySQL 8.0","tags":["innodb cluster","group replication","mysqlsh","status","flow control","quorum"]},{"id":"innodb-cluster-switch-primary","task":"Switchover planificado del primario en InnoDB Cluster","category":"InnoDB Cluster","command":"# Shell: planned switchover\nmysqlsh icadmin@db1:3306 -- cluster set-primary-instance db2:3306\n\n-- SQL alternative: target member's server_uuid, 30 s timeout for open transactions\nSELECT MEMBER_HOST, MEMBER_ID, MEMBER_ROLE FROM performance_schema.replication_group_members;\nSELECT group_replication_set_as_primary('3f06e651-c356-11f1-8aac-927a9034ef99', 30);","what":"Mueve de forma controlada el rol primario a otro miembro en modo single-primary; el antiguo primario pasa a super_read_only y el nuevo aplica su atraso y se abre a escrituras. MySQL Router conoce automáticamente el nuevo primario.","when":"Antes de un mantenimiento, parches del SO o un upgrade del primario, y para traslados de hardware.","caution":"Las transacciones abiertas en el antiguo primario se matan cuando vence el timeout; sin timeout, el cambio las espera y tarda más. Si la aplicación no se conecta a través de Router, debe redirigirla usted mismo. Con BEFORE_ON_PRIMARY_FAILOVER las escrituras esperan hasta que el nuevo primario ha vaciado su atraso; confirme antes que apply_queue está vacío en cluster status. En InnoDB Cluster es preferible el comando de Shell; se mantiene coherente con los metadatos.","note":"group_replication_set_as_primary llegó en 8.0.13; el parámetro timeout se añadió en 8.0.29. En un failover no planificado GR elige por sí mismo el nuevo primario (group_replication_member_weight define la preferencia).","since":"MySQL 8.0.13","tags":["innodb cluster","switchover","primary","group replication","set_as_primary","maintenance"]},{"id":"innodb-cluster-reboot-outage","task":"Recuperar el clúster tras una caída completa (rebootClusterFromCompleteOutage)","category":"InnoDB Cluster","command":"# Are all members OFFLINE? (on every node)\nmysql -h db1 -u icadmin -p -e \"SELECT MEMBER_HOST, MEMBER_STATE FROM performance_schema.replication_group_members\"\n# Dry run first: shows the most up-to-date member and the planned steps\nmysqlsh icadmin@db1:3306 --js -e \"dba.rebootClusterFromCompleteOutage('prodCluster', {dryRun: true})\"\nmysqlsh icadmin@db1:3306 --js -e \"dba.rebootClusterFromCompleteOutage('prodCluster')\"\nmysqlsh icadmin@db1:3306 -- cluster status","what":"Vuelve a hacer el bootstrap del grupo tras un corte de energía o un reinicio simultáneo de todos los nodos, cuando no hay quorum; elige el miembro con el conjunto GTID más alto y reincorpora a los demás.","when":"Cuando cluster status informa de 'Cluster is not reachable' y todos los miembros aparecen como OFFLINE.","caution":"Úselo solo cuando todos los miembros estén OFFLINE; si un miembro sigue ONLINE el grupo está vivo y los demás se unen con rejoinInstance. Arranque todos los nodos antes de ejecutarlo: si un miembro inaccesible está más actualizado, sus transacciones se pierden. force: true ignora los miembros que faltan; es un último recurso. La opción primary: 'host:port' fuerza un miembro concreto como primario.","note":"Las opciones dryRun, primary y force llegaron en Shell 8.0.31; las versiones anteriores funcionaban mediante preguntas interactivas. Incluso con group_replication_start_on_boot=ON el grupo no se forma solo tras una caída completa; el bootstrap debe hacerse deliberadamente desde un único lugar.","since":"MySQL Shell 8.0.31","tags":["innodb cluster","outage","reboot","bootstrap","mysqlsh","disaster"]},{"id":"mysql-router-bootstrap","task":"Hacer el bootstrap de MySQL Router contra un InnoDB Cluster","category":"Router y proxy","command":"# Generates config from metadata and creates the router account; --user is required when run as root\nmysqlrouter --bootstrap icadmin@db1:3306 --directory /etc/mysqlrouter/prod --account routeruser --account-create if-not-exists --name router01 --conf-bind-address 0.0.0.0 --user mysqlrouter\n/etc/mysqlrouter/prod/start.sh\n# Ports: 6446 RW, 6447 RO, 6450 RW split (classic); 6448 / 6449 X protocol\nmysql -h app-host -P 6446 -u app -p -e \"SELECT @@hostname\"\nmysql -h app-host -P 6447 -u app -p -e \"SELECT @@hostname\"\nmysqlsh icadmin@db1:3306 -- cluster list-routers","what":"Registra Router en los metadatos del clúster y genera la cuenta y la configuración; la aplicación llega al primario actual a través del puerto RW y a los secundarios en round-robin a través del puerto RO.","when":"Al poner una capa de failover transparente delante de un InnoDB Cluster / ClusterSet / ReplicaSet; normalmente un Router por servidor de aplicaciones.","caution":"Router no tiene estado; ejecutarlo junto a la aplicación evita un punto único de fallo, un único Router central no. La cuenta de bootstrap solo se usa durante el bootstrap; Router lee los metadatos con su propia cuenta (routeruser). Conoce los cambios de topología a través del metadata cache sin volver a hacer el bootstrap. El puerto de división RW (6450) envía al primario las lecturas dentro de una transacción y las demás a los secundarios; tenga cuidado con las aplicaciones que esperan read-your-writes. La versión de Router no debe ser más antigua que la de los servidores MySQL del clúster.","note":"El puerto de división de lectura/escritura (6450) llegó en 8.2. Las conexiones existentes se cortan en un failover; la aplicación debe tener lógica de reconexión (retry).","since":"MySQL Router 8.0","tags":["mysql router","bootstrap","innodb cluster","failover","read write split","6446"]},{"id":"proxysql-rw-split","task":"División de lectura/escritura y comprobaciones de salud de réplicas con ProxySQL","category":"Router y proxy","command":"-- On MySQL (source): monitor user\nCREATE USER 'proxysql_mon'@'%' IDENTIFIED BY 'M0n#itor2024';\nGRANT USAGE, REPLICATION CLIENT ON *.* TO 'proxysql_mon'@'%';\n\n-- ProxySQL admin: mysql -u admin -p -h 127.0.0.1 -P 6032\nUPDATE global_variables SET variable_value = 'proxysql_mon' WHERE variable_name = 'mysql-monitor_username';\nUPDATE global_variables SET variable_value = 'M0n#itor2024' WHERE variable_name = 'mysql-monitor_password';\nLOAD MYSQL VARIABLES TO RUNTIME; SAVE MYSQL VARIABLES TO DISK;\nINSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type, comment) VALUES (10, 20, 'read_only', 'shop');\nINSERT INTO mysql_servers (hostgroup_id, hostname, port, max_replication_lag) VALUES (10, 'db1', 3306, 0), (20, 'db2', 3306, 30), (20, 'db3', 3306, 30);\nLOAD MYSQL SERVERS TO RUNTIME; SAVE MYSQL SERVERS TO DISK;\nINSERT INTO mysql_users (username, password, default_hostgroup, transaction_persistent) VALUES ('app', 'App#Pass2024', 10, 1);\nLOAD MYSQL USERS TO RUNTIME; SAVE MYSQL USERS TO DISK;\nINSERT INTO mysql_query_rules (rule_id, active, match_digest, destination_hostgroup, apply) VALUES (100, 1, '^SELECT .* FOR (UPDATE|SHARE)', 10, 1), (200, 1, '^SELECT ', 20, 1);\nLOAD MYSQL QUERY RULES TO RUNTIME; SAVE MYSQL QUERY RULES TO DISK;\n\n-- Verify\nSELECT hostgroup_id, hostname, status FROM runtime_mysql_servers;\nSELECT hostgroup, srv_host, status, ConnUsed, Queries, Latency_us FROM stats_mysql_connection_pool;\nSELECT hostgroup, substr(digest_text, 1, 60) AS digest, count_star, sum_time / 1000 AS sum_ms FROM stats_mysql_query_digest ORDER BY sum_time DESC LIMIT 10;","what":"Gestiona automáticamente los hostgroups de escritura (10) y lectura (20) según el indicador read_only; enruta los SELECT a las réplicas y las lecturas con bloqueo y las escrituras al origen; retira temporalmente una réplica que supera max_replication_lag (SHUNNED).","when":"Para repartir la carga de lectura en una topología de replicación asíncrona sin tocar la aplicación; para connection pooling y enrutamiento / cache / firewall por consulta.","caution":"La configuración tiene 3 capas (MEMORY → RUNTIME → DISK): sin LOAD ... TO RUNTIME no cambia nada, sin SAVE ... TO DISK se pierde al reiniciar. La comprobación de read_only requiere super_read_only=ON en las réplicas; de lo contrario las réplicas se mueven al hostgroup de escritura. Enviar todos los SELECT a las réplicas provoca errores de read-after-write por el lag; es más seguro escribir reglas solo para las consultas pesadas / de informes basándose en stats_mysql_query_digest. La interfaz de administración es SQLite: funciones de MySQL como LEFT() no existen, use substr(). Cambie la cuenta admin:admin por defecto (admin-admin_credentials).","note":"La comprobación de max_replication_lag requiere REPLICATION CLIENT para el usuario de monitoreo. Para Group Replication / InnoDB Cluster use la tabla mysql_group_replication_hostgroups.","since":"ProxySQL 2.0","tags":["proxysql","read write split","hostgroup","query rules","connection pool","replication lag"]},{"id":"online-ddl-instant-inplace","task":"Online DDL: ALGORITHM=INSTANT / INPLACE, LOCK=NONE y seguimiento del progreso","category":"Operaciones en línea","command":"-- Always state ALGORITHM/LOCK: unsupported operations fail immediately instead of silently falling back to COPY\nALTER TABLE shop.orders ADD COLUMN status TINYINT NOT NULL DEFAULT 0, ALGORITHM=INSTANT;\nALTER TABLE shop.orders RENAME COLUMN note TO remark, ALGORITHM=INSTANT;\nALTER TABLE shop.orders ADD INDEX ix_status (status), ALGORITHM=INPLACE, LOCK=NONE;\nALTER TABLE shop.orders MODIFY remark VARCHAR(200), ALGORITHM=INPLACE, LOCK=NONE;\n\n-- INSTANT row version counter (INSTANT is refused at 64, a rebuild resets it)\nSELECT NAME, TOTAL_ROW_VERSIONS FROM information_schema.INNODB_TABLES WHERE NAME = 'shop/orders';\n\n-- Progress of a long ALTER (enable the instruments once)\nUPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME LIKE 'stage/innodb/alter%';\nUPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE 'events_stages_%';\nSELECT t.PROCESSLIST_ID, s.EVENT_NAME, s.WORK_COMPLETED, s.WORK_ESTIMATED,\n       ROUND(100 * s.WORK_COMPLETED / NULLIF(s.WORK_ESTIMATED, 0), 1) AS pct_done\nFROM performance_schema.events_stages_current s\nJOIN performance_schema.threads t ON t.THREAD_ID = s.THREAD_ID\nWHERE s.EVENT_NAME LIKE 'stage/innodb/alter%';","what":"INSTANT solo cambia el data dictionary y termina de inmediato independientemente del tamaño de la tabla. INPLACE + LOCK=NONE procesa la tabla in situ permitiendo lecturas y escrituras concurrentes. La última consulta muestra en qué fase está un ALTER en ejecución y su porcentaje.","when":"Antes de cambios de esquema en tablas de producción; para saber qué operaciones pueden ejecutarse sin tiempo de inactividad y estimar cuándo terminará un ALTER largo.","caution":"INSTANT e INPLACE siguen tomando un breve metadata lock exclusivo al inicio y al final; si una transacción larga retiene la tabla, el ALTER espera y todas las consultas que vienen detrás se encolan (vea el diagnóstico de metadata locks). Reduzca lock_wait_timeout en la sesión (SET SESSION lock_wait_timeout = 5). El DML que llega durante INPLACE se registra hasta innodb_online_alter_log_max_size; si se supera, el ALTER se revierte al final. Los cambios de tipo de columna, la conversión de charset y los cambios de PK necesitan COPY y bloquean las escrituras; en tablas grandes use gh-ost / pt-online-schema-change. En una réplica el ALTER se ejecuta con un solo thread durante el mismo tiempo; un ALTER INPLACE grande causa lag en la réplica, INSTANT no.","note":"INSTANT ADD COLUMN llegó en 8.0.12 solo para la última columna; desde 8.0.29 se pueden añadir columnas en cualquier posición y se admite INSTANT DROP COLUMN. Ampliar un VARCHAR solo afecta a los metadatos cuando no cambia el número de bytes de longitud (no se cruza el límite de 255 bytes); se completa de inmediato con INPLACE, no con INSTANT. Una columna indexada no se puede eliminar con INSTANT; elimine primero el índice.","since":"MySQL 8.0.29","tags":["online ddl","instant","inplace","alter table","lock=none","progress"]},{"id":"pt-online-schema-change","task":"ALTER sin bloqueos sobre una tabla grande con pt-online-schema-change","category":"Operaciones en línea","command":"# First --dry-run, then the same command with --execute; throttles on replica lag\npt-online-schema-change --alter \"ADD COLUMN region CHAR(2) NULL, ADD INDEX ix_region (region)\" \\\n  D=shop,t=orders,h=db1,u=osc,p='Osc#2024',s=1 \\\n  --max-lag=5 --check-replica-lag h=db2,u=osc,p='Osc#2024',s=1 \\\n  --max-load Threads_running=50 --critical-load Threads_running=200 \\\n  --set-vars lock_wait_timeout=5 --noversion-check --dry-run","what":"Crea una copia vacía con la nueva estructura, copia las filas en chunks, replica los cambios durante la copia con triggers y finalmente intercambia las tablas con un RENAME atómico. Se ralentiza / pausa por sí solo cuando la carga o el lag de las réplicas superan los umbrales.","when":"Para ALTER que necesitan COPY (tipo de columna, charset, cambio de PK) o que causarían un lag largo en las réplicas.","caution":"La tabla necesita una PK o una unique key. Los triggers añaden sobrecarga a cada escritura durante la migración; en tablas con muchísimas escrituras gh-ost tiene menos impacto. Si hay tablas hijas que referencian esta tabla mediante foreign keys, se requiere --alter-foreign-keys-method=auto. Para cuentas caching_sha2_password añada s=1 (TLS) al DSN; de lo contrario la conexión a la réplica falla con 'Authentication requires secure connection'. El DSN de --check-replica-lag no hereda usuario / contraseña del DSN principal; indíquelos. El intercambio toma un breve metadata lock; mantenga bajo lock_wait_timeout, reintenta si no consigue el bloqueo.","note":"Por defecto las réplicas se descubren a través del processlist por IP; si esas IP no son accesibles, use una tabla DSN con --recursion-method=dsn=D=percona,t=dsns. En 3.7 --check-slave-lag se renombró a --check-replica-lag (el nombre antiguo está obsoleto).","since":"Percona Toolkit 3.7","tags":["pt-online-schema-change","pt-osc","percona toolkit","alter","trigger","online schema change"]},{"id":"gh-ost-migration","task":"Cambio de esquema en línea sin triggers con gh-ost","category":"Operaciones en línea","command":"# Connects to a replica and reads its binlog; postpones cut-over until the flag file is removed\ngh-ost --host=db2 --port=3306 --user=ghost --password='Gh0st#2024' \\\n  --database=shop --table=orders \\\n  --alter=\"ADD COLUMN channel VARCHAR(20) NULL, ADD INDEX ix_channel (channel)\" \\\n  --max-load=Threads_running=50 --critical-load=Threads_running=200 --max-lag-millis=1500 \\\n  --exact-rowcount --concurrent-rowcount --default-retries=120 \\\n  --postpone-cut-over-flag-file=/tmp/ghost.postpone --panic-flag-file=/tmp/ghost.panic \\\n  --serve-socket-file=/tmp/gh-ost.shop.orders.sock --ssl --ssl-allow-insecure --execute\n# While running: status / throttle / cut-over\necho status | nc -U /tmp/gh-ost.shop.orders.sock\necho throttle | nc -U /tmp/gh-ost.shop.orders.sock\nrm /tmp/ghost.postpone","what":"Construye una tabla ghost y copia las filas; aplica los cambios concurrentes leyendo el binlog en lugar de usar triggers. El throttling, la pausa y el momento del cut-over se controlan en tiempo de ejecución mediante el socket o archivos flag.","when":"En tablas grandes con muchas escrituras donde la sobrecarga de los triggers es inaceptable y quiere elegir usted mismo el momento del cut-over (p. ej., una ventana de poco tráfico).","caution":"Requiere binlog_format=ROW y binlog_row_image=FULL; la réplica a la que se conecta necesita log_replica_updates=ON. El usuario necesita SUPER o REPLICATION CLIENT / REPLICATION SLAVE más ALL sobre la tabla. No se admiten tablas con foreign keys (padre o hija). Tras el cut-over la tabla antigua queda como _<table>_del; elimínela manualmente una vez verificada (--ok-to-drop-table la elimina automáticamente). Las versiones de gh-ost anteriores a 1.1.8 usan SHOW SLAVE STATUS y se detienen con el error 1064 en MySQL 8.4; el soporte de 8.4 llegó en 1.1.8. gh-ost crea por sí mismo el archivo indicado en --postpone-cut-over-flag-file; el cut-over no se produce hasta que se elimina el archivo.","note":"Las opciones --test-on-replica y --migrate-on-replica permiten probar y verificar primero el cambio solo en una réplica. El motivo del throttling y el ETA aparecen en la salida del comando 'status'.","tags":["gh-ost","online schema change","binlog","cut-over","alter","triggerless"]},{"id":"ps-top-digests","task":"Encontrar las consultas más costosas (performance_schema digest)","category":"Rendimiento","command":"SELECT SCHEMA_NAME AS db,\n       LEFT(DIGEST_TEXT, 80) AS query,\n       COUNT_STAR AS calls,\n       ROUND(SUM_TIMER_WAIT / 1e12, 2) AS total_sec,\n       ROUND(AVG_TIMER_WAIT / 1e9, 2) AS avg_ms,\n       ROUND(QUANTILE_95 / 1e9, 2) AS p95_ms,\n       ROUND(100 * SUM_TIMER_WAIT / SUM(SUM_TIMER_WAIT) OVER (), 1) AS pct_total,\n       ROUND(SUM_ROWS_EXAMINED / NULLIF(COUNT_STAR, 0)) AS rows_exam_per_call,\n       ROUND(SUM_ROWS_SENT / NULLIF(COUNT_STAR, 0)) AS rows_sent_per_call,\n       SUM_NO_INDEX_USED AS no_index,\n       SUM_CREATED_TMP_DISK_TABLES AS tmp_disk,\n       LAST_SEEN\nFROM performance_schema.events_statements_summary_by_digest\nWHERE SCHEMA_NAME IS NOT NULL\nORDER BY SUM_TIMER_WAIT DESC\nLIMIT 20;\n\n-- Server-wide top wait events\nSELECT events, total, total_latency, avg_latency FROM sys.waits_global_by_latency LIMIT 10;","what":"Clasifica las consultas normalizadas por tiempo total con número de llamadas, latencia media y p95, porcentaje del total, filas examinadas / enviadas por llamada, ejecuciones sin índice y tablas temporales volcadas a disco. La segunda consulta muestra en qué esperas (IO, bloqueos, redo) se consume el tiempo.","when":"Primera parada cuando sube la carga de CPU / IO o los usuarios reportan 'la base de datos va lenta'; funciona incluso con el slow log desactivado.","caution":"Los valores son acumulados desde el último reinicio o TRUNCATE TABLE performance_schema.events_statements_summary_by_digest; tome dos instantáneas y calcule la diferencia para ver la actividad actual. La tabla está limitada a performance_schema_digests_size filas; una vez llena, las consultas nuevas se agregan en una única fila con DIGEST NULL. DIGEST_TEXT se corta en performance_schema_max_digest_length (1024) bytes. Los tiempos están en picosegundos. Una relación alta rows_exam_per_call / rows_sent_per_call es la señal más clara de un índice faltante.","note":"Las columnas QUANTILE_95 / QUANTILE_99 y QUERY_SAMPLE_TEXT llegaron en 8.0. Los mismos datos en unidades legibles están disponibles en las vistas sys.statement_analysis y sys.statements_with_full_table_scans.","since":"MySQL 8.0","tags":["performance_schema","digest","top sql","slow query","sys","wait"]},{"id":"sys-session-activity","task":"Qué se está ejecutando ahora: sesiones activas, latencia, bloqueos y transacciones (sys.session)","category":"Rendimiento","command":"SELECT conn_id, user, db, command, state, time,\n       LEFT(current_statement, 100) AS current_statement,\n       statement_latency, progress, lock_latency,\n       rows_examined, full_scan, tmp_disk_tables,\n       trx_state, trx_latency, last_wait\nFROM sys.session\nWHERE command <> 'Sleep' OR trx_state = 'ACTIVE'\nORDER BY time DESC\nLIMIT 25;","what":"Enriquece SHOW PROCESSLIST con datos de performance_schema: latencia de la sentencia en ejecución, porcentaje de progreso, tiempo de espera de bloqueo, filas examinadas, información de full scan / tabla temporal en disco y estado de la transacción abierta en una sola fila. También lista las sesiones en Sleep que mantienen una transacción abierta.","when":"En los primeros 30 segundos de un incidente: qué consulta lleva cuánto tiempo ejecutándose, quién espera bloqueos, qué sesión inactiva mantiene una transacción abierta.","caution":"sys.session realiza varios joins sobre performance_schema; con miles de conexiones la propia consulta puede tardar segundos, así que use las vistas x$ y LIMIT para el monitoreo frecuente. progress solo se rellena con los instrumentos de stage habilitados y para operaciones que reportan progreso, como ALTER. Una sesión en Sleep con trx_state=ACTIVE es una transacción olvidada por una aplicación / pool de conexiones que usa autocommit=0, la causa más común de problemas de metadata lock y purge.","note":"sys.processlist devuelve los mismos datos incluyendo los hilos en segundo plano. Para terminar: KILL QUERY conn_id (solo la sentencia) o KILL conn_id (la conexión; la transacción se revierte).","since":"MySQL 8.0","tags":["sys.session","processlist","active sessions","incident","trx","session"]},{"id":"metadata-lock-diagnose","task":"Diagnosticar 'Waiting for table metadata lock' y encontrar el bloqueador real","category":"Rendimiento","command":"-- Waiter -> blocker pairs; filter out the ALTER's own SHARED_UPGRADABLE entry\nSELECT object_schema, object_name, waiting_pid, LEFT(waiting_query, 60) AS waiting_query,\n       waiting_lock_type, blocking_pid, blocking_lock_type, sql_kill_blocking_connection\nFROM sys.schema_table_lock_waits\nWHERE blocking_lock_type <> 'SHARED_UPGRADABLE';\n\n-- All MDLs on the table, owner age and open transaction\nSELECT ml.OBJECT_SCHEMA, ml.OBJECT_NAME, ml.LOCK_TYPE, ml.LOCK_DURATION, ml.LOCK_STATUS,\n       t.PROCESSLIST_ID AS conn_id, t.PROCESSLIST_COMMAND AS command, t.PROCESSLIST_TIME AS sec,\n       LEFT(t.PROCESSLIST_INFO, 60) AS query, trx.trx_started\nFROM performance_schema.metadata_locks ml\nJOIN performance_schema.threads t ON t.THREAD_ID = ml.OWNER_THREAD_ID\nLEFT JOIN information_schema.INNODB_TRX trx ON trx.trx_mysql_thread_id = t.PROCESSLIST_ID\nWHERE ml.OBJECT_TYPE = 'TABLE' AND ml.OBJECT_SCHEMA NOT IN ('performance_schema', 'mysql')\nORDER BY ml.OBJECT_NAME, ml.LOCK_STATUS DESC;","what":"Muestra en el metadata lock de qué sesión espera un DDL como ALTER / TRUNCATE / RENAME, y las consultas encoladas detrás del DDL. El bloqueador suele ser una sesión en Sleep que leyó la tabla y nunca cerró su transacción.","when":"Cuando, tras iniciar un ALTER, todas las consultas sobre esa tabla se acumulan en 'Waiting for table metadata lock'.","caution":"Un DDL en espera también bloquea los SELECT que llegan después; decida rápido durante el incidente: KILL de la sesión bloqueadora o cancelar el ALTER (KILL QUERY). PROCESSLIST_INFO del bloqueador puede ser NULL (inactivo); use performance_schema.events_statements_history o sys.session.last_statement para ver su última sentencia. Prevención: ejecute el DDL después de SET SESSION lock_wait_timeout = 5 para que el ALTER desista rápido, y evite transacciones abiertas largas en la aplicación.","note":"El instrumento wait/lock/metadata/sql/mdl está habilitado por defecto en 8.0; en 5.7 debe habilitarse en setup_instruments. sys.schema_table_lock_waits también lista como bloqueador el propio lock SHARED_UPGRADABLE de un ALTER; el verdadero culpable es la sesión que tiene un SHARED_READ / SHARED_WRITE en estado GRANTED.","since":"MySQL 8.0","tags":["metadata lock","mdl","alter","waiting for table metadata lock","lock","blocking"]},{"id":"innodb-deadlock-analysis","task":"Analizar los deadlocks de InnoDB y registrarlos todos","category":"Rendimiento","command":"-- Latest deadlock: LATEST DETECTED DEADLOCK section (only the most recent is kept)\nSHOW ENGINE INNODB STATUS\\G\n-- Total deadlock counter\nSELECT NAME, COUNT FROM information_schema.INNODB_METRICS WHERE NAME = 'lock_deadlocks';\n-- Write every deadlock to the error log (logged at Note level, needs verbosity 3)\nSET PERSIST innodb_print_all_deadlocks = ON;\nSET PERSIST log_error_verbosity = 3;","what":"Muestra las dos transacciones del deadlock, el bloqueo que cada una mantiene y espera (índice, lock_mode, registro) y cuál revirtió InnoDB; el contador mide la frecuencia y el error log conserva los deadlocks pasados.","when":"Cuando la aplicación recibe el error 1213 'Deadlock found when trying to get lock' o aumentan las alertas de deadlock.","caution":"LATEST DETECTED DEADLOCK solo conserva el último evento y se pierde al reiniciar. El nombre del índice y el lock_mode (X, S, gap, insert intention) en las líneas 'HOLDS THE LOCK(S)' / 'WAITING FOR THIS LOCK TO BE GRANTED' explican la causa; 'WE ROLL BACK TRANSACTION (n)' muestra la víctima. Ante un 1213 la aplicación debe reintentar la transacción completa. Soluciones permanentes: acceder a las filas en un orden consistente, mantener las transacciones cortas, usar el índice adecuado para reducir los gap locks y READ COMMITTED donde corresponda. log_error_verbosity=3 también habilita todos los mensajes Note; vigile el crecimiento del error log. La variable de estado Innodb_deadlocks no existe en MySQL Community; use INNODB_METRICS para el contador.","note":"Con concurrencia muy alta, la propia detección de deadlocks puede convertirse en un cuello de botella de CPU; innodb_deadlock_detect=OFF solo se considera con un innodb_lock_wait_timeout bajo. Un lock wait timeout (1205) no es un deadlock; use la receta de bloqueos bloqueantes para encontrar al bloqueador.","since":"MySQL 8.0","tags":["deadlock","innodb","1213","innodb status","lock","error log"]},{"id":"long-trx-history-list","task":"Transacciones de larga duración e history list length (retraso de purge)","category":"Rendimiento","command":"-- Undo records waiting for purge (millions = trouble)\nSELECT NAME, COUNT AS history_list_length\nFROM information_schema.INNODB_METRICS WHERE NAME = 'trx_rseg_history_len';\n\n-- Oldest transactions, their owner and last SQL\nSELECT trx.trx_id, trx.trx_mysql_thread_id AS conn_id, trx.trx_state,\n       TIMESTAMPDIFF(SECOND, trx.trx_started, NOW()) AS age_sec,\n       trx.trx_rows_modified, trx.trx_rows_locked, trx.trx_isolation_level,\n       p.USER, p.HOST, p.COMMAND, p.TIME AS state_sec,\n       LEFT(COALESCE(trx.trx_query, esc.SQL_TEXT), 80) AS current_or_last_sql\nFROM information_schema.INNODB_TRX trx\nJOIN performance_schema.processlist p ON p.ID = trx.trx_mysql_thread_id\nLEFT JOIN performance_schema.threads th ON th.PROCESSLIST_ID = trx.trx_mysql_thread_id\nLEFT JOIN performance_schema.events_statements_current esc ON esc.THREAD_ID = th.THREAD_ID\nORDER BY trx.trx_started\nLIMIT 10;","what":"Muestra cuánto va retrasado el purge (history list length) y las transacciones más antiguas que lo frenan: propietario, antigüedad, filas modificadas y la última SQL que ejecutaron.","when":"Cuando crece el tablespace de undo, cuando las mismas consultas se vuelven más lentas con el tiempo (recorren versiones antiguas de filas) o ante alertas de espacio en disco.","caution":"La causa más común es una sesión en Sleep con una transacción abierta, o un snapshot consistente olvidado bajo REPEATABLE READ (p. ej. un mysqldump / informe abandonado). Tras el KILL, el rollback tarda tanto como filas cambió la transacción; matar una transacción con un trx_rows_modified alto implica un rollback largo y pesado en IO, y un reinicio no lo acorta. El undo se trunca automáticamente cuando el purge se pone al día (innodb_undo_log_truncate=ON).","note":"performance_schema.processlist llegó en 8.0.22 y reemplaza a information_schema.PROCESSLIST, que está obsoleto. 'History list length' en SHOW ENGINE INNODB STATUS da el mismo valor.","since":"MySQL 8.0.22","tags":["history list length","purge","undo","long transaction","innodb_trx","idle in transaction"]},{"id":"slow-log-pt-query-digest","task":"Capturar un slow log completo breve y analizarlo con pt-query-digest","category":"Rendimiento","command":"-- Temporary full capture (e.g. 10 minutes); only new connections are affected\nSET GLOBAL slow_query_log_file = '/var/lib/mysql/slow.log';\nSET GLOBAL log_slow_extra = ON;\nSET GLOBAL long_query_time = 0;\nSET GLOBAL slow_query_log = ON;\n-- ... then return to the normal threshold\nSET GLOBAL long_query_time = 1;\n\n# Shell: top 10 queries by total time\npt-query-digest --limit=10 --order-by=Query_time:sum --noversion-check /var/lib/mysql/slow.log > /tmp/digest.txt\npt-query-digest --since='2026-10-08 09:00:00' --until='2026-10-08 10:00:00' --filter '$event->{db} && $event->{db} eq \"shop\"' --report-format=profile --noversion-check /var/lib/mysql/slow.log","what":"Captura todas las consultas durante una ventana corta con long_query_time=0; pt-query-digest las agrupa por fingerprint y produce un informe ordenado con tiempo total / medio / p95, filas examinadas y una consulta de ejemplo.","when":"Cuando los digests de performance_schema no bastan: análisis con el texto completo de la sentencia y parámetros reales, comparación antes / después de un despliegue, revisión offline en otro host.","caution":"long_query_time se copia del valor global al iniciar una sesión; las conexiones existentes de un pool no ven el nuevo umbral hasta que se reconectan. long_query_time=0 escribe miles de entradas por segundo en un servidor con carga; vigile el espacio en disco y el IO, mantenga la ventana corta y revierta siempre. Copie el archivo fuera de producción para analizarlo. El usuario que ejecuta pt-query-digest necesita acceso de lectura al archivo (modo 640, mysql:mysql).","note":"log_slow_extra (8.0.14) escribe campos adicionales como Bytes_sent, Read_first / Read_next y Created_tmp_disk_tables; pt-query-digest los muestra en el informe. Para habilitarlo / deshabilitarlo de forma permanente, vea la receta enable-general-slow-log.","since":"MySQL 8.0.14","tags":["slow log","pt-query-digest","long_query_time","percona toolkit","query analysis","slow query"]},{"id":"explain-analyze-optimizer-trace","task":"Depurar un mal plan con EXPLAIN ANALYZE y el optimizer trace","category":"Rendimiento","command":"-- Plan + actual time and rows (the query actually runs)\nEXPLAIN ANALYZE\nSELECT region, COUNT(*) FROM shop.big WHERE id < 20000 GROUP BY region;\n\n-- Why the optimizer chose this plan: optimizer trace\nSET SESSION optimizer_trace = 'enabled=on', SESSION optimizer_trace_max_mem_size = 1048576;\nSELECT * FROM shop.big WHERE region = 'TR' AND id > 100 LIMIT 5;\nSELECT JSON_PRETTY(JSON_EXTRACT(TRACE, '$**.considered_execution_plans')) AS plans,\n       MISSING_BYTES_BEYOND_MAX_MEM_SIZE\nFROM information_schema.OPTIMIZER_TRACE;\nSET SESSION optimizer_trace = 'enabled=off';","what":"EXPLAIN ANALYZE muestra lado a lado los valores estimados (cost, rows) y reales (actual time, rows, loops) de cada paso del plan; el optimizer trace muestra las rutas de acceso consideradas, sus costos y por qué se eligió o descartó cada una.","when":"Cuando una consulta se ralentiza de repente (cambio de plan), el optimizador elige el índice equivocado, o para encontrar el origen de una discrepancia entre filas estimadas y reales.","caution":"EXPLAIN ANALYZE ejecuta realmente la consulta; tenga cuidado con consultas pesadas en producción. Funciona con SELECT, TABLE y UPDATE / DELETE multitabla. Una diferencia de más de 10x entre filas estimadas y reales apunta a un problema de estadísticas / histogramas (ANALYZE TABLE, histogramas). En el lado interno de un nested loop, las filas reales son por loop; total = rows × loops. Si MISSING_BYTES_BEYOND_MAX_MEM_SIZE > 0 la traza se truncó; aumente optimizer_trace_max_mem_size. La traza solo contiene la última sentencia de la misma sesión.","note":"EXPLAIN FORMAT=TREE llegó en 8.0.16 y EXPLAIN ANALYZE en 8.0.18. Para fijar un plan, los optimizer hints (/*+ INDEX(t ix) */, JOIN_ORDER) ofrecen un control más fino que FORCE INDEX.","since":"MySQL 8.0.18","tags":["explain analyze","optimizer trace","execution plan","query tuning","index","plan"]},{"id":"histogram-statistics","task":"Crear y gestionar histogramas para columnas no indexadas","category":"Índices y estadísticas","command":"ANALYZE TABLE shop.big UPDATE HISTOGRAM ON status WITH 64 BUCKETS;\n-- 8.4+: histogram refreshed automatically as data changes\nANALYZE TABLE shop.big UPDATE HISTOGRAM ON status WITH 64 BUCKETS AUTO UPDATE;\n\nSELECT SCHEMA_NAME, TABLE_NAME, COLUMN_NAME,\n       HISTOGRAM->>'$.\"histogram-type\"' AS type,\n       JSON_LENGTH(HISTOGRAM->'$.buckets') AS buckets,\n       HISTOGRAM->>'$.\"last-updated\"' AS last_updated,\n       HISTOGRAM->>'$.\"sampling-rate\"' AS sampling_rate\nFROM information_schema.COLUMN_STATISTICS;\n\nANALYZE TABLE shop.big DROP HISTOGRAM ON status;","what":"Almacena la distribución de valores de una columna (singleton o equi-height) en el diccionario de datos; el optimizador estima correctamente la selectividad de los filtros sobre columnas no indexadas, mejorando el orden de join y la calidad del plan.","when":"Cuando EXPLAIN ANALYZE muestra una gran diferencia entre filas estimadas y reales en columnas sesgadas (p. ej. status, type) donde no conviene añadir un índice.","caution":"En columnas indexadas el optimizador usa index dives, por lo que un histograma normalmente no tiene efecto (salvo que se supere eq_range_index_dive_limit). En el modo MANUAL por defecto el histograma no se refresca al cambiar los datos; programe ANALYZE ... UPDATE HISTOGRAM. Las tablas grandes se muestrean según histogram_generation_max_mem_size (sampling-rate < 1). ANALYZE TABLE toma un metadata lock breve; actualizar un histograma no cambia las estadísticas de la tabla (cardinalidad de índices).","note":"Los histogramas llegaron en 8.0; la opción AUTO UPDATE se añadió en 8.4 y refresca junto con el recálculo automático de estadísticas. El máximo es 1024 buckets; si hay menos valores distintos que buckets, se crea un histograma singleton.","since":"MySQL 8.0","tags":["histogram","statistics","optimizer","analyze table","selectivity","cardinality"]},{"id":"invisible-redundant-index","task":"Encontrar índices no usados / redundantes, hacerlos invisibles y eliminarlos con seguridad","category":"Índices y estadísticas","command":"-- Candidate 1: indexes unused since the last restart\nSELECT * FROM sys.schema_unused_indexes WHERE object_schema = 'shop';\n-- Candidate 2: redundant indexes that are a left prefix of another\nSELECT table_name, redundant_index_name, redundant_index_columns, dominant_index_name, sql_drop_index\nFROM sys.schema_redundant_indexes WHERE table_schema = 'shop';\n\n-- Make it invisible before dropping; revert instantly if anything regresses\nALTER TABLE shop.big ALTER INDEX ix_region INVISIBLE;\nSELECT INDEX_NAME, IS_VISIBLE FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = 'shop' AND TABLE_NAME = 'big' AND SEQ_IN_INDEX = 1;\n-- Try the invisible index for a single query\nEXPLAIN SELECT /*+ SET_VAR(optimizer_switch = 'use_invisible_indexes=on') */ COUNT(*) FROM shop.big WHERE region = 'TR';\nALTER TABLE shop.big ALTER INDEX ix_region VISIBLE;\n-- After a few business cycles (incl. month-end) without issues\nALTER TABLE shop.big DROP INDEX ix_region, ALGORITHM=INPLACE, LOCK=NONE;","what":"Lista los índices no usados y los índices cubiertos por otro; hacer invisible un índice antes de eliminarlo permite probar el comportamiento de producción sin él y revertir al instante (sin reconstrucción) si hace falta.","when":"Al depurar índices para reducir el costo de escritura, la presión sobre el buffer pool y el uso de disco.","caution":"schema_unused_indexes se basa solo en estadísticas recopiladas desde el último reinicio; consultas poco frecuentes como los informes mensuales pueden no aparecer. Si las réplicas atienden cargas distintas, revise cada servidor por separado. Un índice invisible se sigue manteniendo en el DML (el costo de escritura permanece) pero el optimizador lo ignora; un índice único invisible sigue imponiendo la unicidad. Una PRIMARY KEY no puede hacerse invisible. Las consultas que nombran el índice en FORCE INDEX / USE INDEX fallan una vez que es invisible.","note":"Cambiar la visibilidad de un índice es solo de metadatos y termina de inmediato. La lista de redundantes se basa en el orden de columnas; el índice (a) está cubierto por (a,b), pero (b) no.","since":"MySQL 8.0","tags":["invisible index","unused index","redundant index","sys","drop index","index cleanup"]},{"id":"buffer-pool-sizing","task":"¿Es suficiente el buffer pool?: hit ratio, wait_free y redimensionamiento en línea","category":"Memoria","command":"SELECT ROUND(@@innodb_buffer_pool_size / 1024 / 1024) AS bp_mb,\n       @@innodb_buffer_pool_chunk_size / 1024 / 1024 AS chunk_mb,\n       @@innodb_buffer_pool_instances AS instances;\nSELECT\n  ROUND(100 * (1 - (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads')\n                 / NULLIF((SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests'), 0)), 3) AS hit_pct,\n  (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_pages_free') AS pages_free,\n  (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_pages_dirty') AS pages_dirty,\n  (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_wait_free') AS wait_free;\n\n-- Online resize and status tracking\nSET PERSIST innodb_buffer_pool_size = 8589934592;\nSELECT VARIABLE_NAME, VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME LIKE 'Innodb_buffer_pool_resize_status%';","what":"Muestra el tamaño del buffer pool, la tasa de aciertos de caché, el número de páginas libres / sucias y el contador de esperas de página libre (wait_free); lo redimensiona sin reinicio y sigue el progreso.","when":"Cuando el IO de lectura es alto, después de añadir / quitar RAM o durante la planificación de capacidad.","caution":"El hit ratio por sí solo engaña; incluso un 99% puede significar miles de lecturas de disco por segundo. Mire el crecimiento por segundo de Innodb_buffer_pool_reads; wait_free > 0 significa que el buffer pool es demasiado pequeño o que los page cleaners no dan abasto. El nuevo tamaño se redondea a un múltiplo de innodb_buffer_pool_chunk_size × instancias. Durante un redimensionamiento son posibles pausas breves, más al reducir; evite las horas pico. En un servidor dedicado empiece con el 60–75% de la RAM; deje margen para la memoria por conexión, la caché del SO y otros procesos, ya que el swapping siempre es peor que un buffer pool más pequeño.","note":"Innodb_buffer_pool_resize_status_code / _progress llegaron en 8.0.31. innodb_dedicated_server=ON dimensiona automáticamente el buffer pool y la capacidad de redo según la RAM. sys.innodb_buffer_stats_by_table muestra el uso del buffer pool por tabla; recorre INNODB_BUFFER_PAGE y es pesado en buffer pools grandes.","since":"MySQL 8.0","tags":["buffer pool","innodb","memory","hit ratio","resize","sizing"]},{"id":"partition-maintenance","task":"Mantenimiento de particiones RANGE: añadir particiones futuras, archivar / eliminar las antiguas","category":"Particionamiento","command":"-- Current partitions\nSELECT PARTITION_NAME, PARTITION_DESCRIPTION, TABLE_ROWS\nFROM information_schema.PARTITIONS\nWHERE TABLE_SCHEMA = 'shop' AND TABLE_NAME = 'events_log' ORDER BY PARTITION_ORDINAL_POSITION;\n\n-- Add a new month by splitting the empty pmax\nALTER TABLE shop.events_log REORGANIZE PARTITION pmax INTO (\n  PARTITION p2026_11 VALUES LESS THAN ('2026-12-01'),\n  PARTITION pmax VALUES LESS THAN (MAXVALUE));\n\n-- Move the old month into an archive table (instant), then drop the empty partition\nCREATE TABLE shop.events_log_2026_08 LIKE shop.events_log;\nALTER TABLE shop.events_log_2026_08 REMOVE PARTITIONING;\nALTER TABLE shop.events_log EXCHANGE PARTITION p2026_08 WITH TABLE shop.events_log_2026_08;\nALTER TABLE shop.events_log DROP PARTITION p2026_08;\n\n-- Pruning check: the partitions column should list a single partition\nEXPLAIN SELECT * FROM shop.events_log WHERE created >= '2026-10-01' AND created < '2026-11-01';","what":"En una tabla particionada por RANGE basada en fechas, añade la partición del siguiente periodo, mueve un periodo antiguo a una tabla aparte en milisegundos mediante EXCHANGE y elimina la partición con un costo mucho menor que DELETE.","when":"Para la retención mensual y el archivado en tablas que crecen con el tiempo, como logs, eventos y pistas de auditoría.","caution":"La columna de particionamiento debe formar parte de todas las claves únicas (incluida la PK). REORGANIZE PARTITION pmax reescribe los datos de pmax; mantenga pmax vacía y cree las particiones con antelación (p. ej. 3 meses). DROP PARTITION no se puede deshacer y no se registra fila por fila; en las réplicas se ejecuta como DDL. El destino del EXCHANGE debe tener la misma estructura y no estar particionado; las filas fuera del rango provocan un error (WITHOUT VALIDATION es más rápido pero puede dejar entrar datos erróneos). El DDL de particiones toma un metadata lock a nivel de tabla. Para el pruning, use la columna de particionamiento en el WHERE sin envolverla en una función.","note":"En 8.0 el particionamiento solo lo soporta InnoDB (y NDB). Añadir particiones futuras / eliminar las antiguas suele automatizarse con el Event Scheduler o cron; ALTER TABLE ... TRUNCATE PARTITION elimina los datos pero conserva la partición.","since":"MySQL 8.0","tags":["partition","range","reorganize","exchange partition","retention","archive"]},{"id":"roles-least-privilege","task":"Construir un modelo de mínimo privilegio con roles (CREATE ROLE, DEFAULT ROLE)","category":"Seguridad","command":"CREATE ROLE IF NOT EXISTS 'app_read', 'app_write';\nGRANT SELECT ON shop.* TO 'app_read';\nGRANT INSERT, UPDATE, DELETE ON shop.* TO 'app_write';\n\n-- Application account: network range, TLS required, roles active by default\nCREATE USER 'svc_app'@'10.0.%' IDENTIFIED BY 'S3cure#App2024' DEFAULT ROLE 'app_read', 'app_write' REQUIRE SSL;\nCREATE USER 'ana'@'%' IDENTIFIED BY 'An4#Report2024';\nGRANT 'app_read' TO 'ana'@'%';\nSET DEFAULT ROLE ALL TO 'ana'@'%';\n\n-- Show grants with roles expanded\nSHOW GRANTS FOR 'svc_app'@'10.0.%' USING 'app_read', 'app_write';\nSELECT FROM_USER, FROM_HOST, TO_USER, TO_HOST FROM mysql.role_edges;","what":"Otorga privilegios a roles en lugar de a usuarios individuales; las cuentas obtienen los privilegios mediante la pertenencia a roles. Los cambios se hacen en un solo lugar y la auditoría y las bajas se simplifican.","when":"Al crear nuevas cuentas de aplicación / equipo y al sanear servidores heredados con privilegios enmarañados.","caution":"Un rol que no es rol por defecto no está activo en la sesión (CURRENT_ROLE() NONE) y parece que el usuario carece de privilegios; use SET DEFAULT ROLE ALL o activate_all_roles_on_login=ON. Un rol también es una cuenta (bloqueada, sin contraseña) y aparece en mysql.user. Use un rango de red en lugar de '%' para las cuentas de aplicación. Nunca otorgue ALL, SUPER, FILE, PROCESS ni CONNECTION_ADMIN a una cuenta de aplicación. Revocar un privilegio de un rol también afecta a las sesiones activas.","note":"mandatory_roles asigna roles automáticamente a todas las cuentas. CREATE USER ... DEFAULT ROLE otorga los roles y además los hace por defecto.","since":"MySQL 8.0","tags":["role","grant","least privilege","default role","security","privilege"]},{"id":"password-policy-lockout","task":"Política de contraseñas, bloqueo por intentos fallidos y rotación de contraseñas sin tiempo de inactividad","category":"Seguridad","command":"-- Password strength policy (component)\nINSTALL COMPONENT 'file://component_validate_password';\nSET PERSIST validate_password.policy = 'MEDIUM';\nSET PERSIST validate_password.length = 12;\nSELECT VALIDATE_PASSWORD_STRENGTH('Summer2024') AS score;\n\n-- Failed-login lockout, password lifetime, history\nALTER USER 'ana'@'%' FAILED_LOGIN_ATTEMPTS 5 PASSWORD_LOCK_TIME 1\n  PASSWORD EXPIRE INTERVAL 90 DAY PASSWORD HISTORY 5 PASSWORD REUSE INTERVAL 365 DAY;\n\n-- Zero-downtime rotation: dual password\nALTER USER 'svc_app'@'10.0.%' IDENTIFIED BY 'N3w#AppSecret2025' RETAIN CURRENT PASSWORD;\n-- Once every application uses the new password\nALTER USER 'svc_app'@'10.0.%' DISCARD OLD PASSWORD;","what":"Habilita una política que rechaza contraseñas débiles, bloquea temporalmente las cuentas tras inicios de sesión fallidos consecutivos y fija la vigencia de la contraseña y las reglas de reutilización; con contraseñas duales, la contraseña de una cuenta de servicio se cambia sin tiempo de inactividad mientras las aplicaciones se actualizan una a una.","when":"Para auditorías de seguridad / cumplimiento (PCI, ISO 27001) y la rotación de contraseñas de cuentas de servicio.","caution":"La política solo se aplica a contraseñas nuevas / cambiadas; no detecta las débiles existentes. En cuentas de servicio, FAILED_LOGIN_ATTEMPTS permite que un solo cliente mal configurado bloquee la cuenta y provoque una caída; úselo para cuentas de personas. PASSWORD_LOCK_TIME está en días (UNBOUNDED: hasta desbloqueo manual); desbloquee con ALTER USER ... ACCOUNT UNLOCK. PASSWORD EXPIRE provoca caídas inesperadas en cuentas de servicio; allí prefiera PASSWORD EXPIRE NEVER más rotación con contraseña dual.","note":"Las contraseñas duales llegaron en 8.0.14, FAILED_LOGIN_ATTEMPTS / PASSWORD_LOCK_TIME en 8.0.19. El plugin validate_password de 5.7 se sustituyó por un componente en 8.0; los nombres de variables usan un punto (validate_password.policy).","since":"MySQL 8.0.19","tags":["password policy","validate_password","failed_login_attempts","dual password","security","password"]},{"id":"require-tls-transport","task":"Forzar TLS, encontrar conexiones sin TLS y renovar certificados sin reinicio","category":"Seguridad","command":"SELECT @@require_secure_transport, @@tls_version, @@ssl_ca, @@ssl_cert;\nSELECT * FROM performance_schema.tls_channel_status\nWHERE PROPERTY IN ('Enabled', 'Current_tls_version', 'Current_tls_cert', 'Ssl_server_not_after');\n\n-- Active connections and their TLS versions (empty = no TLS)\nSELECT t.PROCESSLIST_USER AS user, t.PROCESSLIST_HOST AS host, t.CONNECTION_TYPE, sv.VARIABLE_VALUE AS tls_version\nFROM performance_schema.threads t\nLEFT JOIN performance_schema.status_by_thread sv ON sv.THREAD_ID = t.THREAD_ID AND sv.VARIABLE_NAME = 'Ssl_version'\nWHERE t.TYPE = 'FOREGROUND' AND t.PROCESSLIST_USER IS NOT NULL;\n\nSET PERSIST require_secure_transport = ON;\n-- After copying the new certificate files\nALTER INSTANCE RELOAD TLS;","what":"Muestra la configuración TLS del servidor, la fecha de caducidad del certificado y si cada conexión usa TLS; prohíbe por completo las conexiones TCP sin cifrar y renueva los certificados sin detener el servidor.","when":"Para auditorías de seguridad / cumplimiento, antes de que caduque el certificado y al rastrear clientes heredados que se conectan sin cifrado.","caution":"require_secure_transport=ON no afecta a las conexiones por socket Unix ni por memoria compartida; esas cuentan como seguras. Encuentre los clientes que se conectan sin TLS antes de habilitarlo; de lo contrario fallan con el error 3159. ALTER INSTANCE RELOAD TLS no afecta a las conexiones existentes, solo a las nuevas; si el archivo de certificado no es válido, da error y conserva el certificado anterior. El certificado autofirmado generado automáticamente no puede ser verificado por clientes con VERIFY_CA / VERIFY_IDENTITY; en producción use un certificado firmado por su CA.","note":"ALTER INSTANCE RELOAD TLS llegó en 8.0.16 y performance_schema.tls_channel_status en 8.0.21. Para imponerlo por cuenta use ALTER USER ... REQUIRE SSL (o REQUIRE X509).","since":"MySQL 8.0.21","tags":["tls","ssl","require_secure_transport","certificate","encryption","cert"]},{"id":"risky-accounts-audit","task":"Auditar cuentas de riesgo: contraseña vacía, host '%', SUPER, mysql_native_password","category":"Seguridad","command":"SELECT user, host, plugin,\n       CONCAT_WS(', ',\n         IF(authentication_string = '' AND account_locked = 'N', 'EMPTY PASSWORD', NULL),\n         IF(host = '%', 'ANY HOST', NULL),\n         IF(plugin = 'mysql_native_password', 'NATIVE AUTH', NULL),\n         IF(Super_priv = 'Y', 'SUPER', NULL),\n         IF(Grant_priv = 'Y', 'GRANT OPTION', NULL),\n         IF(File_priv = 'Y', 'FILE', NULL),\n         IF(ssl_type = '', 'NO TLS REQUIRED', NULL),\n         IF(password_expired = 'Y', 'EXPIRED', NULL),\n         IF(account_locked = 'Y', 'LOCKED', NULL)) AS findings,\n       password_last_changed\nFROM mysql.user\nWHERE user NOT IN ('mysql.sys', 'mysql.session', 'mysql.infoschema')\nORDER BY (Super_priv = 'Y') DESC, user;\n\n-- Accounts holding powerful dynamic privileges\nSELECT GRANTEE, PRIVILEGE_TYPE FROM information_schema.USER_PRIVILEGES\nWHERE PRIVILEGE_TYPE IN ('SUPER', 'SYSTEM_VARIABLES_ADMIN', 'CONNECTION_ADMIN', 'BINLOG_ADMIN', 'REPLICATION_SLAVE_ADMIN')\nORDER BY GRANTEE;","what":"Lista los hallazgos de seguridad por cuenta en una fila: sin contraseña, conectable desde cualquier lugar, plugin de autenticación heredado, privilegios SUPER / GRANT / FILE y sin exigencia de TLS; la segunda consulta muestra los privilegios dinámicos peligrosos.","when":"En auditorías de seguridad periódicas, en un servidor recién heredado y antes de una actualización a 8.4.","caution":"La consulta lee mysql.user directamente y necesita SELECT sobre ella. Las cuentas de rol están bloqueadas sin contraseña, por lo que EMPTY PASSWORD solo se reporta para cuentas desbloqueadas. Super_priv es el privilegio estático SUPER; como 8.0 lo divide en privilegios dinámicos, revise también la segunda consulta. Las cuentas marcadas NATIVE AUTH deben pasar a caching_sha2_password antes de 8.4: ALTER USER 'u'@'h' IDENTIFIED WITH caching_sha2_password BY '...'; los clientes / conectores antiguos pueden no soportar caching_sha2_password, pruébelos primero.","note":"mysql_native_password quedó obsoleto en 8.0.34, está deshabilitado por defecto en 8.4 (puede habilitarse con mysql_native_password=ON) y se eliminó en 9.0.","since":"MySQL 8.0","tags":["security audit","mysql.user","mysql_native_password","super","privileges","audit"]},{"id":"upgrade-checker-84","task":"Comprobación previa de la actualización 8.0 → 8.4 LTS (util.checkForServerUpgrade)","category":"Upgrade","command":"# State the target version explicitly; the Shell must be the same version as the target or newer\nmysqlsh root@db1:3306 -- util check-for-server-upgrade --target-version=8.4.10 --output-format=TEXT\n# JSON output for automation, including a my.cnf check\nmysqlsh root@db1:3306 -- util check-for-server-upgrade --target-version=8.4.10 --output-format=JSON --config-path=/etc/my.cnf > /tmp/upgrade_check.json","what":"Comprueba el servidor actual frente a la versión de destino: variables y privilegios eliminados, parámetros con nuevos valores por defecto, métodos de autenticación heredados, definiciones de columna no válidas, problemas de claves foráneas y particiones. Los hallazgos de nivel Error bloquean la actualización.","when":"Antes de cada actualización mayor / LTS; vuelva a ejecutarla tras corregir los hallazgos.","caution":"Revise también los avisos: en 8.4 cambian valores por defecto como innodb_io_capacity (10000), innodb_log_buffer_size (64 MB), innodb_adaptive_hash_index (OFF), innodb_change_buffering (none), innodb_buffer_pool_instances y group_replication_consistency, lo que puede afectar al rendimiento; fije en my.cnf los valores cuyo comportamiento actual quiera conservar. Los privilegios eliminados como SET_USER_ID se descartan durante la actualización. Actualice primero una réplica, obsérvela bajo carga y luego haga el switchover; el downgrade binario de 8.4 a 8.0 no está soportado, así que la vía de vuelta es un backup lógico o una réplica que se deja en la versión antigua.","note":"Eliminado en 8.4: la sintaxis de replicación antigua como START / STOP SLAVE, SHOW SLAVE STATUS y CHANGE MASTER TO, default_authentication_plugin (sustituido por authentication_policy), binlog_transaction_dependency_tracking y mysqlpump. mysql_native_password está deshabilitado por defecto.","since":"MySQL Shell 8.0","tags":["upgrade","8.4","checkforserverupgrade","mysqlsh","lts","version upgrade"]},{"id":"charset-utf8mb4-convert","task":"Convertir tablas latin1 / utf8mb3 a utf8mb4","category":"Mantenimiento","command":"-- Columns that are not utf8mb4\nSELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME, COLUMN_TYPE\nFROM information_schema.COLUMNS\nWHERE CHARACTER_SET_NAME IS NOT NULL AND CHARACTER_SET_NAME <> 'utf8mb4'\n  AND TABLE_SCHEMA NOT IN ('mysql', 'sys', 'information_schema', 'performance_schema')\nORDER BY TABLE_SCHEMA, TABLE_NAME;\n\n-- Generate conversion DDL for tables whose default charset is not utf8mb4\nSELECT CONCAT('ALTER TABLE `', t.TABLE_SCHEMA, '`.`', t.TABLE_NAME,\n              '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, ALGORITHM=COPY, LOCK=SHARED;') AS ddl\nFROM information_schema.TABLES t\nJOIN information_schema.COLLATIONS c ON c.COLLATION_NAME = t.TABLE_COLLATION\nWHERE c.CHARACTER_SET_NAME <> 'utf8mb4' AND t.TABLE_TYPE = 'BASE TABLE'\n  AND t.TABLE_SCHEMA NOT IN ('mysql', 'sys', 'information_schema', 'performance_schema');\n\n-- Schema default and a single table\nALTER DATABASE legacy CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;\nALTER TABLE legacy.cust CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, ALGORITHM=COPY, LOCK=SHARED;","what":"Encuentra columnas y tablas que no son utf8mb4 y genera el DDL de conversión; CONVERT TO cambia tanto el valor por defecto de la tabla como todas las columnas de caracteres, habilitando emojis / caracteres de 4 bytes.","when":"En esquemas migrados desde 5.x, ante errores 'Incorrect string value' (1366) y cuando los joins entre columnas con collations distintas no pueden usar índices.","caution":"CONVERT TO reescribe la tabla con COPY y bloquea las escrituras mientras dura (INPLACE no está soportado); en tablas grandes ejecute el mismo --alter mediante pt-online-schema-change / gh-ost. utf8mb4 usa 4 bytes por carácter: un VARCHAR(255) indexado pasa a 1020 bytes; el límite es de 3072 bytes con el row format DYNAMIC / COMPRESSED y de 767 con COMPACT / REDUNDANT, por encima del cual obtiene el error 1071. Las columnas VARCHAR / TEXT se promueven a un tipo mayor (TEXT → MEDIUMTEXT) cuando hace falta para mantener la misma capacidad de caracteres. Un cambio de collation afecta a la ordenación y la comparación: utf8mb4_0900_ai_ci no distingue acentos ni mayúsculas, así que valores antes distintos pueden colisionar en un índice único (1062). Convierta juntas las tablas que se combinan en joins. La conexión del cliente también debe usar utf8mb4.","note":"utf8 (utf8mb3) está obsoleto en 8.0; desde 8.0.30 se muestra como utf8mb3 en SHOW CREATE TABLE. Para las reglas de collation del turco (I con punto y sin punto) use utf8mb4_tr_0900_ai_ci. El valor por defecto de 8.0 es utf8mb4 / utf8mb4_0900_ai_ci.","since":"MySQL 8.0","tags":["utf8mb4","character set","collation","convert","emoji","charset"]}]},"recipes/mssql.json":{"engineId":"mssql","engine":"SQL Server","order":4,"recipes":[{"id":"backup-log","task":"Tomar un backup del log de transacciones","category":"Backup y log","command":"BACKUP LOG [Customer]\nTO DISK = N'D:\\Backup\\Customer_log_20260706_1200.trn'\nWITH COMPRESSION, CHECKSUM, STATS = 10;","what":"Toma un backup del log de transacciones y (en el modelo de recuperación FULL/BULK_LOGGED) trunca la parte usada del log para que pueda reutilizarse.","when":"Regularmente en el modelo de recuperación FULL (p. ej. cada 15 min), para la cadena de recuperación a un punto en el tiempo y cuando crece el archivo de log.","caution":"Solo tiene sentido en el modelo de recuperación FULL/BULK_LOGGED (en SIMPLE el log se trunca automáticamente). Primero debe existir al menos un backup FULL para que la cadena de log no se rompa. Establezca una estrategia correcta de borrado/sobrescritura para los archivos de backup.","tags":["backup log","log backup","transaction log","trn","recovery","backup"]},{"id":"backup-full-diff","task":"Tomar backups FULL y DIFFERENTIAL","category":"Backup y log","command":"-- Full backup:\nBACKUP DATABASE [Customer]\nTO DISK = N'D:\\Backup\\Customer_full_20260706.bak'\nWITH COMPRESSION, CHECKSUM, INIT, STATS = 10;\n\n-- Differential backup:\nBACKUP DATABASE [Customer]\nTO DISK = N'D:\\Backup\\Customer_diff_20260706.bak'\nWITH DIFFERENTIAL, COMPRESSION, CHECKSUM, INIT, STATS = 10;","what":"FULL respalda la base de datos completa; DIFFERENTIAL respalda las extensiones modificadas desde el último FULL.","when":"FULL: backup base diario/semanal. DIFF: backups intermedios rápidos entre FULL.","caution":"Restaurar un DIFF requiere su backup FULL base. INIT sobrescribe el archivo existente; con un nombre de archivo equivocado pierde un backup. CHECKSUM detecta la corrupción a tiempo.","tags":["backup","full","differential","differential backup","bak","backup"]},{"id":"log-reuse-wait","task":"Por qué creció el archivo de log / por qué no se trunca","category":"Backup y log","command":"SELECT name,\n       recovery_model_desc,\n       log_reuse_wait_desc\nFROM   sys.databases\nWHERE  name = N'Customer';\n\n-- Log space usage for all databases:\nSELECT DB_NAME(database_id) AS db, name, type_desc,\n       size*8/1024        AS size_mb,\n       CAST(FILEPROPERTY(name,'SpaceUsed') AS BIGINT)*8/1024 AS used_mb\nFROM   sys.master_files\nWHERE  type_desc = 'LOG';","what":"La columna log_reuse_wait_desc indica por qué no se puede truncar el log de transacciones (p. ej. LOG_BACKUP, ACTIVE_TRANSACTION, REPLICATION, AVAILABILITY_REPLICA).","when":"Para encontrar la causa raíz cuando el archivo de log sigue creciendo o el log está lleno (error 9002).","caution":"Si aparece LOG_BACKUP, la solución es tomar un backup del log, no reducir el archivo. Solo consulta; sin riesgo.","tags":["log_reuse_wait_desc","log growth","sys.databases","log backup","recovery","diagnostics"]},{"id":"shrink-log","task":"Reducir (shrink) el archivo de log","category":"Backup y log","command":"-- First take a log backup so the log can be truncated (FULL model):\nBACKUP LOG [Customer] TO DISK = N'D:\\Backup\\Customer_log_pre_shrink.trn' WITH COMPRESSION;\n\n-- Find the logical name of the log file:\nSELECT name, type_desc FROM sys.database_files WHERE type_desc = 'LOG';\n\n-- Shrink (target 2048 MB):\nUSE [Customer];\nDBCC SHRINKFILE (N'Customer_log', 2048);","what":"Reduce el tamaño físico del archivo de log de transacciones y devuelve el espacio al sistema operativo.","when":"Cuando el log ha quedado inflado de forma permanente tras un crecimiento anómalo puntual (transacción grande/reconstrucción de índice).","caution":"PELIGROSO/PRECAUCIÓN: Reducir de forma rutinaria es una MALA práctica; el rendimiento cae mientras el log vuelve a crecer y aumenta la fragmentación de VLF. Tome primero un backup del log; de lo contrario el tamaño no bajará. Reduzca solo un archivo de log que creció innecesariamente, no el archivo de datos. Después vuelva a hacerlo crecer a un tamaño adecuado en un solo paso.","tags":["shrink","dbcc shrinkfile","shrink log","log","vlf","caution"]},{"id":"vlf-count","task":"Mostrar el número de VLF (Virtual Log File)","category":"Backup y log","command":"-- SQL Server 2016 SP2 / 2017 and later:\nSELECT DB_NAME(database_id) AS db,\n       COUNT(*)             AS vlf_count,\n       SUM(CASE WHEN vlf_active = 1 THEN 1 ELSE 0 END) AS active_vlf\nFROM   sys.dm_db_log_info(DB_ID(N'Customer'))\nGROUP BY database_id;\n\n-- For older versions: DBCC LOGINFO('Customer'); (row count = VLF count)","what":"Devuelve el número de archivos de log virtuales (VLF) dentro del log de transacciones. Demasiados VLF degradan el rendimiento del arranque/recuperación/backup del log.","when":"Cuando el log va lento o el arranque de la base de datos tarda mucho; al revisar la configuración de crecimiento del log.","caution":"Cientos/miles de VLF son una señal de alerta; la solución es reducir el log y volver a hacerlo crecer en incrementos adecuadamente grandes (p. ej. pasos de 8GB). Solo consulta; sin riesgo.","since":"SQL Server 2016 SP2","note":"La DMV sys.dm_db_log_info llegó con SQL Server 2016 SP2 y 2017; antes, los VLF se contaban por el número de filas de la salida no documentada de DBCC LOGINFO.","tags":["vlf","dm_db_log_info","dbcc loginfo","log","virtual log file","performance"]},{"id":"open-transaction","task":"Encontrar la transacción abierta más antigua (ACTIVE_TRANSACTION)","category":"Backup y log","command":"USE [Customer];\nDBCC OPENTRAN;\n\n-- Detailed: show long-running open transactions and their owners:\nSELECT s.session_id, s.login_name, s.host_name, s.program_name,\n       t.transaction_id,\n       DATEDIFF(SECOND, t.transaction_begin_time, GETDATE()) AS duration_sec,\n       t.transaction_begin_time\nFROM   sys.dm_tran_active_transactions t\n  JOIN sys.dm_tran_session_transactions st ON st.transaction_id = t.transaction_id\n  JOIN sys.dm_exec_sessions s ON s.session_id = st.session_id\nORDER BY t.transaction_begin_time;","what":"Muestra la transacción abierta (sin commit/rollback) más antigua y la sesión que la mantiene. Una transacción abierta impide el truncado del log (LOG_REUSE_WAIT = ACTIVE_TRANSACTION).","when":"Para encontrar la causa raíz cuando el log no se reduce a pesar de los backups de log y log_reuse_wait_desc = ACTIVE_TRANSACTION.","caution":"Confirme con el responsable de negocio antes de hacer KILL a la sesión que mantiene una transacción abierta larga; el rollback puede tardar mucho. Solo consulta; sin riesgo.","tags":["dbcc opentran","open transaction","active_transaction","dm_tran_active_transactions","log","diagnostics"]},{"id":"add-data-file","task":"Añadir un archivo de datos a una base de datos","category":"Almacenamiento","command":"ALTER DATABASE [Customer]\nADD FILE (\n  NAME = N'Customer_data2',\n  FILENAME = N'E:\\Data\\Customer_data2.ndf',\n  SIZE = 4GB,\n  FILEGROWTH = 512MB,\n  MAXSIZE = 100GB\n) TO FILEGROUP [PRIMARY];","what":"Añade un nuevo archivo de datos (.ndf) a un filegroup existente para aumentar la capacidad y repartir la E/S.","when":"Cuando el disco está lleno o cuando quiere repartir la E/S entre varios discos.","caution":"Las escrituras en el nuevo archivo se equilibran por proportional fill; el archivo más vacío se llena primero. Especifique FILEGROWTH en MB fijos, no en porcentaje. Fijar MAXSIZE evita el crecimiento descontrolado.","tags":["add file","add data file","ndf","alter database","filegroup","storage"]},{"id":"add-filegroup","task":"Añadir un filegroup","category":"Almacenamiento","command":"-- 1) Create the filegroup:\nALTER DATABASE [Customer] ADD FILEGROUP [FG_Archive];\n\n-- 2) Add a file to it:\nALTER DATABASE [Customer]\nADD FILE (\n  NAME = N'Customer_archive1',\n  FILENAME = N'F:\\Data\\Customer_archive1.ndf',\n  SIZE = 8GB, FILEGROWTH = 1GB\n) TO FILEGROUP [FG_Archive];\n\n-- (Optional) make it the default filegroup:\n-- ALTER DATABASE [Customer] MODIFY FILEGROUP [FG_Archive] DEFAULT;","what":"Crea un nuevo filegroup con un archivo dentro; permite ubicar tablas/índices en un disco/filegroup separado.","when":"Para separar datos de archivo/fríos, para backup/restauración a nivel de filegroup y para separar la E/S.","caution":"Un filegroup vacío no se puede respaldar/usar; añádale al menos un archivo. Cambiar el valor por defecto afecta a dónde se ubican los nuevos objetos.","tags":["filegroup","filegroup","add filegroup","alter database","ndf","storage"]},{"id":"modify-file-growth","task":"Cambiar el tamaño de archivo / autogrowth","category":"Almacenamiento","command":"-- Grow (SIZE only grows; shrinking requires shrink, not a resize):\nALTER DATABASE [Customer]\nMODIFY FILE (NAME = N'Customer_data', SIZE = 20GB);\n\n-- Set autogrowth (fixed MB recommended):\nALTER DATABASE [Customer]\nMODIFY FILE (NAME = N'Customer_data', FILEGROWTH = 1GB, MAXSIZE = 200GB);\n\n-- Disable autogrowth:\nALTER DATABASE [Customer]\nMODIFY FILE (NAME = N'Customer_log', FILEGROWTH = 0);","what":"Hace crecer de antemano un archivo de datos/log o fija su incremento de autogrowth/límite superior.","when":"Para corregir incrementos de crecimiento pequeños y descontrolados, preasignar espacio o fijar un MAXSIZE.","caution":"El autogrowth basado en porcentaje produce incrementos muy grandes en archivos grandes; use MB fijos. Sin Instant File Initialization, el crecimiento de los archivos de datos bloquea. SIZE no puede reducir un archivo (hace falta un shrink).","tags":["modify file","autogrowth","filegrowth","size","alter database","storage"]},{"id":"move-file","task":"Mover un archivo de datos/log a otra ubicación","category":"Almacenamiento","command":"-- 1) Tell SQL Server the new path (nothing is moved yet):\nALTER DATABASE [Customer]\nMODIFY FILE (NAME = N'Customer_data',\n             FILENAME = N'G:\\Data\\Customer_data.mdf');\n\n-- 2) Take the database offline:\nALTER DATABASE [Customer] SET OFFLINE WITH ROLLBACK IMMEDIATE;\n\n-- 3) Physically copy/move the file to the new path AT THE OPERATING SYSTEM level (G:\\Data\\...).\n\n-- 4) Bring it online:\nALTER DATABASE [Customer] SET ONLINE;","what":"Cambia la ubicación física de un archivo de base de datos: la nueva ruta se define en los metadatos, el archivo se mueve físicamente mientras la base de datos está offline y luego la base de datos se pone online.","when":"Al sustituir un disco o al mover a otra unidad para ganar espacio.","caution":"OFFLINE + ROLLBACK IMMEDIATE corta todas las conexiones activas; planifique un tiempo de inactividad. No ponga la base de datos ONLINE antes de copiar realmente el archivo a la nueva ruta en el SO, o la base de datos pasa a SUSPECT. tempdb requiere un método distinto (MODIFY FILE + reinicio del servicio de SQL Server).","tags":["move file","move file","modify file","offline","online","mdf","ldf","storage"]},{"id":"add-tempdb-file","task":"Añadir un archivo de datos a tempdb","category":"Almacenamiento","command":"-- Show current tempdb files:\nSELECT name, physical_name, size*8/1024 AS size_mb, growth\nFROM   sys.master_files WHERE database_id = DB_ID(N'tempdb');\n\n-- Add a new file (up to the number of CPU cores, equally sized):\nALTER DATABASE [tempdb]\nADD FILE (\n  NAME = N'tempdev2',\n  FILENAME = N'T:\\tempdb\\tempdb_mssql_2.ndf',\n  SIZE = 8GB, FILEGROWTH = 1GB\n);","what":"Añade un archivo de datos adicional a tempdb para reducir la contención de asignación (PFS/GAM/SGAM) y repartir la E/S.","when":"En sistemas con contención en tempdb (esperas PAGELATCH_UP) o uso intensivo de tempdb.","caution":"Todos los archivos de datos de tempdb deben tener el MISMO tamaño e incremento de crecimiento (para el proportional fill). El archivo añadido se usa de inmediato sin reinicio; si los archivos existentes difieren en tamaño, iguálelos todos primero; de lo contrario el nuevo archivo (vacío) recibe una carga desproporcionada. El número de archivos suele ajustarse al número de núcleos (hasta 8); el instalador de SQL Server 2016+ lo hace por defecto.","tags":["tempdb","add file","tempdb file","ndf","contention","pagelatch","storage"]},{"id":"index-rebuild-reorg","task":"Reconstruir / reorganizar índices","category":"Mantenimiento","command":"-- Rebuild (high fragmentation > 30%). ONLINE requires Enterprise:\nALTER INDEX IX_Customer_Email ON dbo.Customer\n  REBUILD WITH (ONLINE = ON, MAXDOP = 4, SORT_IN_TEMPDB = ON);\n\n-- All indexes of a table:\nALTER INDEX ALL ON dbo.Customer REBUILD WITH (FILLFACTOR = 90);\n\n-- Reorganize (moderate fragmentation 5-30%, always online, can be stopped):\nALTER INDEX IX_Customer_Email ON dbo.Customer REORGANIZE;","what":"REBUILD recrea el índice desde cero (elimina por completo la fragmentación y refresca las estadísticas); REORGANIZE compacta el nivel hoja en el sitio (ligero, no bloqueante).","when":"REORGANIZE para una fragmentación del 5-30%, REBUILD por encima del 30%. En ventanas de mantenimiento para tablas grandes.","caution":"ONLINE = ON solo está disponible en Enterprise/Developer; use OFF en Standard (la tabla se bloquea). REBUILD genera mucho log y necesita espacio. MAXDOP y SORT_IN_TEMPDB afectan al uso de recursos.","tags":["index","rebuild","reorganize","alter index","fragmentation","maintenance","online"]},{"id":"update-statistics","task":"Actualizar estadísticas","category":"Mantenimiento","command":"-- Single table, full scan:\nUPDATE STATISTICS dbo.Customer WITH FULLSCAN;\n\n-- Specific statistic, with sampling:\nUPDATE STATISTICS dbo.Customer IX_Customer_Email WITH SAMPLE 30 PERCENT;\n\n-- Bulk for the whole database (user tables), sampled:\nEXEC sys.sp_updatestats;","what":"Actualiza las estadísticas de columnas/índices que usa el optimizador; asegura planes de ejecución y estimaciones de cardinalidad correctos.","when":"Tras grandes cambios/cargas de datos y ante regresiones de plan; una reconstrucción de índice ya refresca por completo las estadísticas (no hace falta por separado).","caution":"FULLSCAN es preciso pero costoso; genera carga de E/S en tablas grandes. sp_updatestats actualiza solo las estadísticas modificadas y usa el muestreo por defecto. Repetir UPDATE STATISTICS sobre el mismo índice después de un REBUILD es innecesario.","tags":["update statistics","statistics","sp_updatestats","fullscan","optimizer","maintenance"]},{"id":"dbcc-checkdb","task":"Comprobación de integridad (DBCC CHECKDB)","category":"Mantenimiento","command":"-- Full integrity check:\nDBCC CHECKDB (N'Customer') WITH NO_INFOMSGS, ALL_ERRORMSGS;\n\n-- Lighter on large databases (physical consistency):\nDBCC CHECKDB (N'Customer') WITH PHYSICAL_ONLY, NO_INFOMSGS;","what":"Comprueba la integridad física y lógica de la base de datos (estructuras de página, asignación, consistencia de índices) e informa de la corrupción.","when":"En el mantenimiento regular (semanal), ante sospechas de problemas de hardware/IO y después de errores 823/824.","caution":"Usa mucha E/S y tempdb; ejecútelo en una ventana de mantenimiento. Si se encuentran errores, REPAIR_ALLOW_DATA_LOSS debe ser el ÚLTIMO recurso y PROVOCA PÉRDIDA DE DATOS; considere primero restaurar desde un backup bueno. PHYSICAL_ONLY es más rápido pero omite las comprobaciones lógicas.","tags":["dbcc checkdb","integrity","corruption","corruption","integrity","maintenance"]},{"id":"index-fragmentation","task":"Mostrar la fragmentación de índices","category":"Mantenimiento","command":"SELECT OBJECT_SCHEMA_NAME(ips.object_id) AS schema_name,\n       OBJECT_NAME(ips.object_id)        AS table_name,\n       i.name                            AS index_name,\n       ips.index_type_desc,\n       ROUND(ips.avg_fragmentation_in_percent,1) AS frag_pct,\n       ips.page_count\nFROM   sys.dm_db_index_physical_stats(DB_ID(N'Customer'), NULL, NULL, NULL, 'LIMITED') ips\n  JOIN sys.indexes i\n    ON i.object_id = ips.object_id AND i.index_id = ips.index_id\nWHERE  ips.avg_fragmentation_in_percent > 10\n  AND  ips.page_count > 1000\nORDER BY ips.avg_fragmentation_in_percent DESC;","what":"Lista el porcentaje de fragmentación y el número de páginas de los índices; la base para decidir entre rebuild/reorganize.","when":"Antes del mantenimiento, para decidir qué índices tratar.","caution":"La fragmentación es irrelevante en índices pequeños (< 1000 páginas); se filtran. El modo 'DETAILED' es más preciso pero muy costoso; 'LIMITED' suele bastar. Solo consulta; sin riesgo.","tags":["fragmentation","dm_db_index_physical_stats","index","frag","maintenance","diagnostics"]},{"id":"kill-spid","task":"Matar una sesión (SPID)","category":"Sesión","command":"-- First find the offending SPID:\nSELECT s.session_id, s.login_name, s.host_name, s.program_name,\n       r.status, r.command, r.wait_type, r.blocking_session_id,\n       DB_NAME(r.database_id) AS db\nFROM   sys.dm_exec_sessions s\n  LEFT JOIN sys.dm_exec_requests r ON r.session_id = s.session_id\nWHERE  s.is_user_process = 1;\n\n-- Kill (example SPID 63):\nKILL 63;\n\n-- Show rollback progress:\nKILL 63 WITH STATUSONLY;","what":"Termina el session_id (SPID) indicado; su transacción abierta se revierte.","when":"Para detener una sesión que mantiene bloqueos, ejecuta una consulta desbocada/larga o no libera su conexión.","caution":"En una transacción abierta grande el rollback puede tardar mucho y no se puede cancelar; siga el progreso con KILL WITH STATUSONLY. No mate SPID del sistema (is_user_process=0). Confirme que es el SPID correcto.","tags":["kill","spid","kill session","dm_exec_sessions","dm_exec_requests","session"]},{"id":"blocking-chain","task":"Encontrar la cadena de bloqueos (lock)","category":"Sesión","command":"SELECT r.session_id            AS waiting_spid,\n       r.blocking_session_id  AS blocking_spid,\n       r.wait_type,\n       r.wait_time / 1000.0   AS wait_sec,\n       r.status, r.command,\n       DB_NAME(r.database_id) AS db,\n       t.text                 AS waiting_query\nFROM   sys.dm_exec_requests r\n  CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t\nWHERE  r.blocking_session_id <> 0\nORDER BY r.wait_time DESC;\n\n-- To find the head (root) blocker of the chain, look for the SPID whose\n-- own blocking_session_id is not set (it is not blocked itself).","what":"Lista las sesiones en espera y las sesiones que las bloquean, con el tiempo de espera y la consulta que ejecutan; hace visible la cadena de bloqueos.","when":"Para encontrar el origen de la contención de bloqueos ante quejas de \"la aplicación se cuelga / da timeout\".","caution":"Confirme con el responsable de negocio antes de matar al bloqueador raíz (el SPID que nadie bloquea pero que bloquea a otros). Solo consulta; sin riesgo.","tags":["blocking","lock","lock","dm_exec_requests","blocking_session_id","session","chain"]},{"id":"active-queries","task":"Mostrar las consultas activas y su costo","category":"Sesión","command":"SELECT r.session_id,\n       r.status,\n       r.command,\n       DB_NAME(r.database_id) AS db,\n       r.cpu_time,\n       r.total_elapsed_time / 1000 AS elapsed_sec,\n       r.reads, r.writes, r.logical_reads,\n       r.wait_type,\n       SUBSTRING(t.text,\n         (r.statement_start_offset/2)+1,\n         ((CASE r.statement_end_offset WHEN -1 THEN DATALENGTH(t.text)\n            ELSE r.statement_end_offset END - r.statement_start_offset)/2)+1) AS running_statement\nFROM   sys.dm_exec_requests r\n  JOIN sys.dm_exec_sessions s ON s.session_id = r.session_id\n  CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t\nWHERE  s.is_user_process = 1 AND r.session_id <> @@SPID\nORDER BY r.total_elapsed_time DESC;","what":"Muestra las consultas de usuario en ejecución con CPU, tiempo transcurrido, lecturas/escrituras y el texto exacto de la sentencia.","when":"Para encontrar qué consulta consume recursos durante una ralentización en vivo.","caution":"statement_start/end_offset extrae la sentencia exacta en ejecución (no el batch completo). Solo consulta; sin riesgo.","tags":["active queries","active query","dm_exec_requests","dm_exec_sql_text","cpu","session"]},{"id":"sp-configure-memory","task":"Configurar con sp_configure (max server memory)","category":"Configuración","command":"-- Make advanced options visible:\nEXEC sys.sp_configure 'show advanced options', 1;\nRECONFIGURE;\n\n-- Set max server memory (MB). Example: allow 24 GB:\nEXEC sys.sp_configure 'max server memory (MB)', 24576;\nRECONFIGURE;\n\n-- Verify:\nEXEC sys.sp_configure 'max server memory (MB)';","what":"Limita la memoria máxima que SQL Server puede usar para el buffer pool; deja RAM para el sistema operativo y otros componentes.","when":"Cuando el SO/otros servicios del servidor se quedan sin memoria, o se ha añadido RAM nueva.","caution":"Deje suficiente RAM para el sistema operativo, SSAS/SSIS, CLR y las pilas de hilos (normalmente al menos 4-6 GB para el SO). Fijarlo demasiado bajo hunde el rendimiento de SQL Server. El cambio no se aplica sin RECONFIGURE; este ajuste es dinámico (no requiere reinicio).","tags":["sp_configure","max server memory","reconfigure","memory","configuration","memory"]},{"id":"reconfigure-general","task":"Ajuste general de sp_configure y RECONFIGURE","category":"Configuración","command":"EXEC sys.sp_configure 'show advanced options', 1;\nRECONFIGURE;\n\n-- Example: cost threshold for parallelism and MAXDOP:\nEXEC sys.sp_configure 'cost threshold for parallelism', 50;\nEXEC sys.sp_configure 'max degree of parallelism', 4;\nRECONFIGURE;\n\n-- Show all configured/running values:\nSELECT name, value AS configured, value_in_use AS running, is_dynamic, is_advanced\nFROM   sys.configurations\nORDER BY name;","what":"Fija valores de configuración a nivel de servidor; RECONFIGURE los aplica al valor en ejecución.","when":"Al cambiar ajustes del servidor como MAXDOP, el umbral de paralelismo u optimize for ad hoc workloads.","caution":"Los ajustes con is_dynamic = 0 requieren reiniciar SQL Server. RECONFIGURE WITH OVERRIDE omite la validación de algunos ajustes; úselo con cuidado. Anote value_in_use antes de cambiar nada.","tags":["sp_configure","reconfigure","maxdop","cost threshold","sys.configurations","configuration"]},{"id":"ag-failover","task":"Failover de Availability Group","category":"Configuración","command":"-- Run on the TARGET (secondary) REPLICA - planned, no data loss (synchronous):\nALTER AVAILABILITY GROUP [AG_Customer] FAILOVER;\n\n-- Forced (asynchronous replica / primary unreachable, DATA LOSS risk):\n-- ALTER AVAILABILITY GROUP [AG_Customer] FORCE_FAILOVER_ALLOW_DATA_LOSS;\n\n-- Check status:\nSELECT ag.name, ar.replica_server_name,\n       rs.role_desc, rs.synchronization_health_desc, ar.availability_mode_desc\nFROM   sys.availability_groups ag\n  JOIN sys.availability_replicas ar ON ar.group_id = ag.group_id\n  JOIN sys.dm_hadr_availability_replica_states rs ON rs.replica_id = ar.replica_id;","what":"Mueve el rol primario del Availability Group a otra réplica; un failover planificado a una réplica síncrona se realiza sin pérdida de datos.","when":"Transferencia de rol para mantenimiento/parches, failover cuando falla el nodo primario.","caution":"El comando FAILOVER normal se ejecuta en la réplica SECUNDARIA que tomará el relevo. FORCE_FAILOVER_ALLOW_DATA_LOSS solo se usa cuando el primario es inalcanzable o en una réplica asíncrona y puede PROVOCAR PÉRDIDA DE DATOS; tenga mucho cuidado si la sincronización no está sana.","tags":["availability group","ag","failover","hadr","failover","alwayson","configuration"]},{"id":"change-recovery-model","task":"Cambiar el modelo de recuperación de la base de datos","category":"Configuración","command":"-- Show the current model:\nSELECT name, recovery_model_desc FROM sys.databases WHERE name = N'Customer';\n\n-- FULL recovery (for point-in-time recovery):\nALTER DATABASE [Customer] SET RECOVERY FULL;\n\n-- SIMPLE recovery (log truncated automatically, no log backups needed):\n-- ALTER DATABASE [Customer] SET RECOVERY SIMPLE;\n\n-- BULK_LOGGED (temporary, for large bulk operations):\n-- ALTER DATABASE [Customer] SET RECOVERY BULK_LOGGED;","what":"Cambia el modelo de recuperación de la base de datos; determina cómo se gestiona el log y qué opciones de recuperación existen.","when":"FULL: cuando se requiere recuperación a un punto en el tiempo/backups de log/AG. SIMPLE: en sistemas que no necesitan punto de recuperación (dev/test, DW).","caution":"Tras pasar a FULL, tome un backup FULL para iniciar la cadena de log y configure backups de log regulares; de lo contrario el log sigue creciendo. Pasar de FULL a SIMPLE rompe la cadena de log; pierde la recuperación a un punto en el tiempo. Los miembros de un AG deben estar en el modelo de recuperación FULL.","tags":["recovery model","recovery full","simple","bulk_logged","alter database","configuration"]}]},"recipes/mssql-ext.json":{"engineId":"mssql","engine":"SQL Server","order":2,"recipes":[{"id":"ag-health-lag","task":"Monitorear la salud y el retraso del availability group (log send / redo queue, RPO)","category":"Always On y HA","command":"-- Replica health (run on the primary to see all replicas):\nSELECT ag.name AS ag_name, ar.replica_server_name, ars.role_desc,\n       ar.availability_mode_desc, ar.failover_mode_desc,\n       ars.connected_state_desc, ars.synchronization_health_desc,\n       ars.operational_state_desc, ars.last_connect_error_description\nFROM   sys.availability_groups ag\n  JOIN sys.availability_replicas ar                ON ar.group_id = ag.group_id\n  JOIN sys.dm_hadr_availability_replica_states ars ON ars.replica_id = ar.replica_id\nORDER BY ag.name, ars.role_desc;\n\n-- Per-database lag: log send queue (RPO), redo queue (RTO):\nSELECT ag.name AS ag_name, ar.replica_server_name, DB_NAME(drs.database_id) AS db,\n       drs.is_primary_replica, drs.synchronization_state_desc, drs.synchronization_health_desc,\n       drs.is_suspended, drs.suspend_reason_desc,\n       drs.log_send_queue_size  AS log_send_queue_kb,\n       drs.log_send_rate        AS log_send_kb_s,\n       drs.redo_queue_size      AS redo_queue_kb,\n       drs.redo_rate            AS redo_kb_s,\n       CAST(drs.redo_queue_size * 1.0 / NULLIF(drs.redo_rate, 0) AS DECIMAL(18,1)) AS est_redo_catchup_s,\n       drs.last_commit_time,\n       DATEDIFF(SECOND, drs.last_commit_time, pri.last_commit_time) AS data_loss_window_s,\n       drs.secondary_lag_seconds\nFROM   sys.dm_hadr_database_replica_states drs\n  JOIN sys.availability_replicas ar ON ar.replica_id = drs.replica_id\n  JOIN sys.availability_groups ag   ON ag.group_id = drs.group_id\n  LEFT JOIN sys.dm_hadr_database_replica_states pri\n         ON pri.group_database_id = drs.group_database_id AND pri.is_primary_replica = 1\nORDER BY ag.name, db, drs.is_primary_replica DESC;\n\n-- WSFC quorum state:\nSELECT cluster_name, quorum_type_desc, quorum_state_desc FROM sys.dm_hadr_cluster;\nSELECT member_name, member_type_desc, member_state_desc, number_of_quorum_votes\nFROM   sys.dm_hadr_cluster_members;","what":"Muestra la salud de conexión y sincronización de las réplicas; por base de datos, la log send queue, la redo queue, las tasas de envío/redo, el tiempo estimado de puesta al día del redo y la diferencia del último commit respecto al primario (ventana potencial de pérdida de datos); también devuelve el estado del quorum de WSFC.","when":"Ante una alerta NOT_HEALTHY / NOT SYNCHRONIZING en el dashboard del AG, al comprobar RPO/RTO antes de un failover planificado o cuando los usuarios reportan datos desactualizados en un secundario legible.","caution":"Una log_send_queue creciente es la cantidad de log que se pierde si falla el primario (RPO); una redo_queue creciente es lo que tarda la base de datos en ponerse online tras el failover (RTO). En un secundario solo es visible la réplica local; ejecútelo en el primario para ver el panorama completo. data_loss_window_s solo tiene sentido mientras hay tráfico de escritura. Consulta de solo lectura; segura.","since":"2016","note":"secondary_lag_seconds es una columna de 2016+. Si la redo queue sigue creciendo, compruebe si el hilo de redo del secundario está bloqueado (consultas de lectura largas, DDL) y busque esperas REDO_THREAD_PENDING_WORK / PARALLEL_REDO_*.","tags":["availability group","ag","log send queue","redo queue","synchronization","rpo"],"untested":true},{"id":"ag-forced-failover-dr","task":"Failover forzado (FORCE_FAILOVER_ALLOW_DATA_LOSS) y recuperación posterior","category":"Always On y HA","command":"-- 1) On each candidate secondary: pick the most current one (highest end_of_log_lsn)\nSELECT ag.name AS ag_name, DB_NAME(drs.database_id) AS db,\n       drs.synchronization_state_desc, drs.end_of_log_lsn, drs.last_commit_time\nFROM   sys.dm_hadr_database_replica_states drs\n  JOIN sys.availability_groups ag ON ag.group_id = drs.group_id\nWHERE  drs.is_local = 1;\n\n-- 2) On the chosen replica (while the primary is unreachable):\nALTER AVAILABILITY GROUP [AG_Musteri] FORCE_FAILOVER_ALLOW_DATA_LOSS;\n\n-- 3) On the other secondaries, resume the suspended data movement:\nALTER DATABASE [Musteri] SET HADR RESUME;\n\n-- 4) When the old primary returns (now a SUSPENDED secondary):\n--    take a snapshot first to inspect the lost transactions, then resume\nCREATE DATABASE [Musteri_lost_snap]\n  ON (NAME = N'Musteri', FILENAME = N'D:\\Data\\Musteri_lost_snap.ss')\n  AS SNAPSHOT OF [Musteri];\nALTER DATABASE [Musteri] SET HADR RESUME;","what":"Convierte una réplica de DR en primario, aceptando pérdida de datos, cuando el primario se ha perdido por completo; luego reanuda el movimiento de datos suspendido en las demás réplicas y, cuando vuelve el antiguo primario, toma un snapshot para inspeccionar las transacciones que nunca llegaron al nuevo primario.","when":"Cuando el centro de datos primario está caído, solo queda una réplica asíncrona, o una réplica síncrona no está SYNCHRONIZED y aun así se requiere un failover.","caution":"PÉRDIDA DE DATOS: se pierden las transacciones posteriores al último LSN endurecido (hardened). Obtenga primero la aprobación del RPO por parte del negocio. Tras un failover forzado todas las bases de datos secundarias quedan SUSPENDED; el log no se trunca y sigue creciendo hasta HADR RESUME. Si se pierde el quorum de WSFC, inicie primero el clúster con quorum forzado (PowerShell: Start-ClusterNode -FixQuorum); de lo contrario el comando falla. HADR RESUME en el antiguo primario descarta de forma permanente las transacciones perdidas; no reanude sin tomar el snapshot.","note":"Para un failover planificado sin pérdida de datos use el comando FAILOVER de la receta ag-failover. Los AG con CLUSTER_TYPE = NONE / EXTERNAL también usan FORCE_FAILOVER_ALLOW_DATA_LOSS; en EXTERNAL (Pacemaker) el failover lo dirige el gestor del clúster. Un snapshot necesita todos los archivos de datos lógicos del origen; para bases de datos con varios archivos añada una entrada ON por archivo de datos.","tags":["force failover","allow data loss","dr","availability group","hadr resume","data loss"],"untested":true},{"id":"ag-add-db-autoseed","task":"Añadir una base de datos a un AG (automatic seeding) y monitorear el progreso del seeding","category":"Always On y HA","command":"-- Prerequisite (primary): FULL recovery and at least one FULL backup\nSELECT name, recovery_model_desc FROM sys.databases WHERE name = N'Musteri';\nBACKUP DATABASE [Musteri] TO DISK = N'D:\\Backup\\Musteri_pre_ag.bak' WITH COMPRESSION, CHECKSUM, INIT;\n\n-- 1) Primary: switch the replica to automatic seeding\nALTER AVAILABILITY GROUP [AG_Musteri]\n  MODIFY REPLICA ON N'SQL02' WITH (SEEDING_MODE = AUTOMATIC);\n\n-- 2) On each secondary: allow the AG to create databases\nALTER AVAILABILITY GROUP [AG_Musteri] GRANT CREATE ANY DATABASE;\n\n-- 3) Primary: add the database\nALTER AVAILABILITY GROUP [AG_Musteri] ADD DATABASE [Musteri];\n\n-- 4) Progress (primary or secondary):\nSELECT local_database_name, role_desc, internal_state_desc,\n       transferred_size_bytes / 1048576         AS transferred_mb,\n       database_size_bytes / 1048576            AS db_mb,\n       transfer_rate_bytes_per_second / 1048576 AS mb_s,\n       estimate_time_complete_utc, failure_message\nFROM   sys.dm_hadr_physical_seeding_stats;\n\n-- 5) Seeding history and failure reason:\nSELECT s.start_time, s.completion_time, adc.database_name, ar.replica_server_name,\n       s.current_state, s.failure_state_desc, s.error_code, s.number_of_attempts\nFROM   sys.dm_hadr_automatic_seeding s\n  JOIN sys.availability_databases_cluster adc ON adc.group_database_id = s.ag_db_id\n  JOIN sys.availability_replicas ar           ON ar.replica_id = s.ag_remote_replica_id\nORDER BY s.start_time DESC;","what":"Añade la base de datos al AG transmitiéndola a los secundarios a través del endpoint (VDI) sin backup/restauración manual; muestra el rendimiento del seeding, la finalización estimada y el motivo de fallo.","when":"Al añadir una nueva base de datos a un AG, añadir una nueva réplica o reconstruir una base de datos secundaria dañada.","caution":"El seeding satura el enlace del endpoint y no va comprimido por defecto; para bases de datos muy grandes o una WAN débil, un backup/restauración manual + JOIN es más predecible. El log del primario no se trunca durante el seeding (log_reuse_wait_desc = AVAILABILITY_REPLICA). En 2016 el secundario necesita las mismas rutas de carpetas de datos/log que el primario; en 2017+, si los archivos del primario están en las rutas por defecto de la instancia, el secundario usa sus propias rutas por defecto. Para una base de datos con TDE, el certificado debe restaurarse primero en cada secundario. Sin GRANT CREATE ANY DATABASE en el secundario, el seeding queda pendiente.","since":"2016","note":"La compresión del seeding se habilita con el trace flag 9567 (cuesta CPU). El inicio, la finalización y los fallos del seeding se escriben en el error log de SQL Server; para un seguimiento detallado cree una sesión de Extended Events aparte con los eventos hadr_automatic_seeding_* y hadr_physical_seeding_*. Vuelva atrás con SEEDING_MODE = MANUAL.","tags":["automatic seeding","availability group","add database","seeding_mode","dm_hadr_physical_seeding_stats","ag"],"untested":true},{"id":"ag-listener-routing","task":"Comprobar el listener, sus IP y la configuración de read-only routing","category":"Always On y HA","command":"-- Listener, port and IP states:\nSELECT ag.name AS ag_name, l.dns_name, l.port, l.is_conformant,\n       ip.ip_address, ip.ip_subnet_mask, ip.state_desc\nFROM   sys.availability_group_listeners l\n  JOIN sys.availability_groups ag ON ag.group_id = l.group_id\n  LEFT JOIN sys.availability_group_listener_ip_addresses ip ON ip.listener_id = l.listener_id;\n\n-- Read-only routing list: where read intent goes when X is primary:\nSELECT ag.name AS ag_name, pr.replica_server_name AS when_primary_is,\n       rl.routing_priority, sr.replica_server_name AS route_to,\n       sr.read_only_routing_url, sr.secondary_role_allow_connections_desc\nFROM   sys.availability_read_only_routing_lists rl\n  JOIN sys.availability_replicas pr ON pr.replica_id = rl.replica_id\n  JOIN sys.availability_replicas sr ON sr.replica_id = rl.read_only_replica_id\n  JOIN sys.availability_groups ag   ON ag.group_id = pr.group_id\nORDER BY ag.name, when_primary_is, rl.routing_priority;\n\n-- Replica endpoint and connection settings:\nSELECT replica_server_name, endpoint_url, read_only_routing_url,\n       secondary_role_allow_connections_desc, seeding_mode_desc, session_timeout\nFROM   sys.availability_replicas;\n\n-- Routing test from the command line (-d is required with -K ReadOnly):\n-- sqlcmd -S tcp:AGL-Musteri,1433 -d Musteri -E -M -K ReadOnly -Q \"SELECT @@SERVERNAME\"","what":"Muestra el nombre DNS del listener, el puerto y el estado en el clúster de cada IP de subred; para cada escenario de primario en la lista de read-only routing, a qué réplica y URL se enrutan las conexiones con intención de lectura.","when":"Cuando las aplicaciones no pueden conectarse tras un failover, las conexiones ApplicationIntent=ReadOnly acaban en el primario, o se añadió una nueva subred/réplica.","caution":"El read-only routing solo funciona para conexiones hechas a través del listener con ApplicationIntent=ReadOnly y una base de datos explícita; las conexiones directas a un nombre de instancia no se enrutan. Una réplica con read_only_routing_url vacío no puede ser destino de enrutamiento. En un listener multisubred, los clientes sin MultiSubnetFailover=True sufren timeouts de conexión de 20-30 s tras el failover. Consulta de solo lectura; segura.","note":"Para el balanceo de carga de 2016+ la lista de enrutamiento usa paréntesis anidados: READ_ONLY_ROUTING_LIST = (('SQL02','SQL03'),'SQL01'). Si las IP del listener aparecen OFFLINE, compruebe las dependencias de recursos en el lado del clúster (Failover Cluster Manager / Get-ClusterResource).","tags":["listener","read-only routing","applicationintent","multisubnetfailover","availability group","connection"],"untested":true},{"id":"logshipping-monitor","task":"Monitorear el estado y la latencia de log shipping","category":"Always On y HA","command":"-- Summary (on the primary, secondary or monitor server):\nEXEC master.sys.sp_help_log_shipping_monitor;\n\n-- PRIMARY: when the last log backup was taken:\nSELECT p.primary_server, p.primary_database,\n       p.last_backup_date, p.last_backup_file,\n       DATEDIFF(MINUTE, p.last_backup_date, GETDATE()) AS backup_age_min,\n       p.backup_threshold                              AS threshold_min\nFROM   msdb.dbo.log_shipping_monitor_primary p;\n\n-- SECONDARY: copy and restore latency:\nSELECT s.secondary_server, s.secondary_database, s.primary_server,\n       s.last_copied_date, s.last_copied_file,\n       s.last_restored_date, s.last_restored_file,\n       s.last_restored_latency                           AS restore_latency_min,\n       DATEDIFF(MINUTE, s.last_restored_date, GETDATE()) AS since_last_restore_min,\n       s.restore_threshold                               AS threshold_min\nFROM   msdb.dbo.log_shipping_monitor_secondary s;\n\n-- Errors in the last 24 hours:\nSELECT e.database_name, e.agent_type, e.log_time, e.message\nFROM   msdb.dbo.log_shipping_monitor_error_detail e\nWHERE  e.log_time >= DATEADD(HOUR, -24, GETDATE())\nORDER BY e.log_time DESC;\n\n-- Secondary DB mode (RESTORING = NORECOVERY, is_in_standby = STANDBY):\nSELECT name, state_desc, is_in_standby FROM sys.databases WHERE name = N'Musteri';","what":"Muestra la antigüedad del último backup de log en el primario, el último archivo copiado/restaurado y la latencia de restauración en el secundario junto con sus umbrales, y los errores de los jobs de backup/copia/restauración de las últimas 24 horas.","when":"Cuando salta el job de alerta de log shipping (errores 14420/14421), antes de una prueba de DR o al informar de cuánto va retrasado el secundario.","caution":"Las tablas de monitoreo solo contienen el lado que ve el servidor: la tabla del primario se rellena en el primario/monitor y la del secundario en el secundario/monitor. Un backup de log manual (ajeno a log shipping) rompe la cadena y el job de restauración sigue fallando; use COPY_ONLY para backups de log puntuales. En modo STANDBY las conexiones de usuario se cortan antes de cada restauración. Consulta de solo lectura; segura.","note":"Las columnas de fecha están en hora local del servidor; use las columnas *_utc al comparar servidores en zonas horarias distintas. Si el job de restauración registra \"skipping log backup file\", ese archivo ya se aplicó; para encontrar un hueco real, compruebe la secuencia FirstLSN/LastLSN con RESTORE HEADERONLY en el secundario.","tags":["log shipping","sp_help_log_shipping_monitor","restore latency","dr","secondary","latency"],"untested":true},{"id":"backup-copy-only","task":"Tomar un backup puntual sin romper la cadena (COPY_ONLY, striping, throughput)","category":"Backup y log","command":"-- Ad-hoc FULL that does not reset the differential base:\nBACKUP DATABASE [Musteri]\nTO DISK = N'D:\\Backup\\Musteri_copyonly_20261008.bak'\nWITH COPY_ONLY, COMPRESSION, CHECKSUM, INIT, STATS = 10;\n\n-- Ad-hoc log backup that does not truncate the log:\nBACKUP LOG [Musteri]\nTO DISK = N'D:\\Backup\\Musteri_copyonly_20261008.trn'\nWITH COPY_ONLY, COMPRESSION, CHECKSUM, INIT;\n\n-- Large DB: stripe across files and tune buffers:\nBACKUP DATABASE [Musteri]\nTO DISK = N'D:\\Backup\\Musteri_s1.bak', DISK = N'E:\\Backup\\Musteri_s2.bak'\nWITH COPY_ONLY, COMPRESSION, CHECKSUM, INIT,\n     MAXTRANSFERSIZE = 4194304, BUFFERCOUNT = 64, STATS = 5;","what":"Toma un backup FULL o de log independiente que no afecta a la cadena de backups regular (base diferencial y cadena de log); en bases de datos grandes, el striping entre archivos y un tamaño de transferencia mayor acortan el backup.","when":"Al entregar una copia a test/dev, antes de un cambio arriesgado o cuando se solicita un backup para una auditoría; siempre que necesite un backup sin tocar la estrategia de backups programada.","caution":"Un FULL puntual sin COPY_ONLY se convierte en la base de los DIFF siguientes; si se borra ese archivo, los DIFF no sirven. COPY_ONLY no tiene efecto junto con DIFFERENTIAL. Restaurar un backup con striping necesita todos los archivos; perder uno inutiliza el backup completo. Se asigna BUFFERCOUNT x MAXTRANSFERSIZE de memoria fuera del buffer pool (256 MB en el ejemplo); redúzcalo en un servidor con presión de memoria.","note":"Para una base de datos con TDE, la compresión del backup requiere MAXTRANSFERSIZE mayor que 65536 (2016+). Valores por defecto del servidor: sp_configure 'backup compression default' y 'backup checksum default'. En 2022 con hardware Intel QAT está disponible WITH COMPRESSION (ALGORITHM = QAT_DEFLATE).","tags":["copy_only","ad-hoc backup","stripe","maxtransfersize","buffercount","backup"]},{"id":"backup-to-url","task":"Backup a URL (Azure Blob SAS / almacenamiento compatible con S3)","category":"Backup y log","command":"-- A) Azure Blob: SAS credential (named after the container URL, SECRET without a leading '?')\nCREATE CREDENTIAL [https://dbabackup.blob.core.windows.net/sql]\nWITH IDENTITY = N'SHARED ACCESS SIGNATURE',\n     SECRET   = N'sv=2022-11-02&ss=b&srt=co&sp=rwdlac&se=2027-01-01T00:00:00Z&sig=XXXX';\n\nBACKUP DATABASE [Musteri]\nTO URL = N'https://dbabackup.blob.core.windows.net/sql/Musteri_full_1.bak',\n   URL = N'https://dbabackup.blob.core.windows.net/sql/Musteri_full_2.bak'\nWITH COMPRESSION, CHECKSUM, MAXTRANSFERSIZE = 4194304, BLOCKSIZE = 65536, STATS = 10;\n\n-- B) S3-compatible storage (2022+):\nCREATE CREDENTIAL [s3://s3.eu-central-1.amazonaws.com/dba-backup/sql]\nWITH IDENTITY = N'S3 Access Key',\n     SECRET   = N'<AccessKeyID>:<SecretKeyID>';\n\nBACKUP DATABASE [Musteri]\nTO URL = N's3://s3.eu-central-1.amazonaws.com/dba-backup/sql/Musteri_full.bak'\nWITH COMPRESSION, CHECKSUM, FORMAT, STATS = 10;\n\n-- Verify (uses the same credential):\nRESTORE VERIFYONLY\nFROM URL = N's3://s3.eu-central-1.amazonaws.com/dba-backup/sql/Musteri_full.bak'\nWITH CHECKSUM;","what":"Escribe el backup directamente en almacenamiento de objetos: en Azure Blob como block blobs con una credencial SAS, o en 2022 en almacenamiento compatible con la API de S3 (AWS S3, MinIO etc.) con una credencial de access key. La credencial se elige automáticamente por coincidencia con el prefijo de la URL.","when":"Para requisitos de backup fuera del sitio, escenarios de migración a la nube/DR híbrido o cuando el almacenamiento local de backups es insuficiente.","caution":"En Azure un único block blob admite como máximo 50,000 bloques; con un MAXTRANSFERSIZE de 4 MB eso son unos 195 GB por archivo, así que haga striping de las bases de datos grandes en varias URL. En S3 un archivo tiene como máximo 10,000 partes; para bases de datos grandes aumente MAXTRANSFERSIZE (hasta 20 MB, requiere COMPRESSION) y haga striping en varias URL (hasta 64). S3 no soporta INIT/append; FORMAT sobrescribe un backup existente con el mismo nombre. Cuando caduca la SAS, los jobs de backup fallan con error; anote la fecha de caducidad en el calendario. El secreto de la credencial se guarda en master; los principals con CONTROL SERVER pueden usarlo.","since":"2022","note":"El backup a block blob con SAS es de 2016+ y el soporte de S3 de 2022+. El método heredado con la clave de la cuenta de almacenamiento (IDENTITY = nombre de la cuenta) produce page blobs y no se recomienda. Para S3 el servidor debe confiar en el certificado TLS del endpoint; para soluciones on-prem como MinIO el puerto va en la URL (s3://minio.local:9000/bucket/...).","tags":["backup to url","azure blob","s3","credential","sas","cloud backup"],"untested":true},{"id":"backup-history-msdb","task":"Historial de backups desde msdb y comprobación de backups faltantes","category":"Backup y log","command":"-- Last FULL / DIFF / LOG per database (never backed up first):\nSELECT d.name AS db,\n       d.recovery_model_desc,\n       MAX(CASE WHEN b.type = 'D' THEN b.backup_finish_date END) AS last_full,\n       MAX(CASE WHEN b.type = 'I' THEN b.backup_finish_date END) AS last_diff,\n       MAX(CASE WHEN b.type = 'L' THEN b.backup_finish_date END) AS last_log,\n       DATEDIFF(MINUTE, MAX(CASE WHEN b.type = 'L' THEN b.backup_finish_date END), GETDATE()) AS log_age_min\nFROM   sys.databases d\n  LEFT JOIN msdb.dbo.backupset b\n         ON b.database_name = d.name AND b.is_copy_only = 0\nWHERE  d.name <> N'tempdb'\nGROUP BY d.name, d.recovery_model_desc\nORDER BY last_full;\n\n-- Chain for one DB over the last 7 days (LSN, size, file):\nSELECT b.backup_start_date, b.backup_finish_date, b.type, b.is_copy_only,\n       b.first_lsn, b.last_lsn, b.database_backup_lsn,\n       CAST(b.backup_size / 1048576.0 AS DECIMAL(12,1))            AS size_mb,\n       CAST(b.compressed_backup_size / 1048576.0 AS DECIMAL(12,1)) AS compressed_mb,\n       b.has_backup_checksums, mf.physical_device_name\nFROM   msdb.dbo.backupset b\n  JOIN msdb.dbo.backupmediafamily mf ON mf.media_set_id = b.media_set_id\nWHERE  b.database_name = N'Musteri'\n  AND  b.backup_start_date >= DATEADD(DAY, -7, GETDATE())\nORDER BY b.backup_start_date;","what":"Lista la hora del último backup FULL/DIFF/LOG y la antigüedad del backup de log de cada base de datos; para una base de datos, la cadena de backups con la secuencia de LSN, el tamaño comprimido, el indicador de checksum y la ruta del archivo.","when":"En la revisión matutina, para saber qué archivos necesita una restauración, para responder \"cuándo fue el último backup\" en una auditoría o para ver si los backups de log se están ejecutando durante una alerta de log lleno.","caution":"msdb solo conoce los backups tomados por esta instancia; los backups tomados en otro servidor o en otra réplica del AG no son visibles (revise cada réplica de un AG). El first_lsn de un backup de log debe ser igual al last_lsn del anterior; un hueco significa que la cadena está rota. En recuperación FULL, un last_log NULL significa que el log nunca se respalda y seguirá creciendo. Consulta de solo lectura; segura.","note":"El historial de msdb crece sin límite y ralentiza los jobs de backup; púrguelo regularmente con EXEC msdb.dbo.sp_delete_backuphistory @oldest_date = '2026-01-01';. Valores de type: D = FULL, I = DIFF, L = LOG, F = archivo/filegroup.","tags":["backupset","msdb","backup history","last backup","lsn","backup chain"]},{"id":"backup-restore-progress","task":"Ver el progreso y la finalización estimada de backup / restauración / CHECKDB","category":"Backup y log","command":"SELECT r.session_id, r.command, DB_NAME(r.database_id) AS db,\n       CAST(r.percent_complete AS DECIMAL(5,1))                     AS pct,\n       DATEADD(SECOND, r.estimated_completion_time / 1000, GETDATE()) AS eta,\n       r.total_elapsed_time / 60000                                 AS elapsed_min,\n       r.wait_type, r.wait_time, r.blocking_session_id\nFROM   sys.dm_exec_requests r\nWHERE  r.percent_complete > 0;\n\n-- Restore phases and throughput are in the error log:\nEXEC sys.xp_readerrorlog 0, 1, N'Restore';","what":"Muestra el progreso porcentual, la hora estimada de finalización y la espera actual de las operaciones largas que reportan percent_complete (BACKUP, RESTORE, DBCC CHECKDB/SHRINK, ALTER INDEX REORGANIZE, rollback tras KILL).","when":"Cuando el backup nocturno se mete en el horario laboral, para responder \"cuándo terminará\" durante una restauración grande o para seguir el rollback de una sesión matada.","caution":"La estimación supone un progreso lineal; durante las fases de redo/undo de una restauración y durante la inicialización a ceros del log, el porcentaje puede quedarse detenido mucho tiempo. Los archivos de log no se benefician de instant file initialization (salvo crecimientos de hasta 64 MB en 2022); las restauraciones con logs grandes pasan mucho tiempo en la primera fase. Consulta de solo lectura; segura.","note":"Si wait_type es mayoritariamente BACKUPIO / ASYNC_IO_COMPLETION, el cuello de botella es el disco de destino/origen; un BACKUPTHREAD o CPU alto apunta al costo de la compresión. Para ver en detalle en el error log la larga fase de puesta a ceros de una restauración, use los trace flags 3004 + 3605.","tags":["percent_complete","backup progress","restore progress","eta","dm_exec_requests","progress"]},{"id":"tail-log-backup","task":"Tomar un backup tail-log antes de una restauración (NORECOVERY / NO_TRUNCATE)","category":"Restauración y recuperación","command":"-- A) Healthy DB: back up the tail and put the DB into RESTORING (no new writes):\nBACKUP LOG [Musteri]\nTO DISK = N'D:\\Backup\\Musteri_tail.trn'\nWITH NORECOVERY, CHECKSUM, INIT;\n\n-- B) Damaged DB / data file lost, log file intact:\n-- BACKUP LOG [Musteri]\n-- TO DISK = N'D:\\Backup\\Musteri_tail_damaged.trn'\n-- WITH NO_TRUNCATE, CONTINUE_AFTER_ERROR, INIT;\n\nSELECT name, state_desc FROM sys.databases WHERE name = N'Musteri';","what":"Respalda la parte del log escrita desde el último backup de log (la cola), de modo que la cadena de restauración llegue al momento del fallo y no se pierdan los últimos minutos de transacciones.","when":"Antes de restaurar sobre la misma base de datos, cuando falla el disco del archivo de datos o en el corte final de una migración.","caution":"NORECOVERY pone la base de datos en RESTORING y termina el acceso de los usuarios; no lo ejecute en la base de datos equivocada. Restaurar WITH REPLACE sin un backup tail-log descarta de forma permanente las transacciones posteriores al último backup de log. Un backup tomado con NO_TRUNCATE puede contener metadatos incompletos; ejecute DBCC CHECKDB tras la restauración. En recuperación SIMPLE no es posible un backup tail-log.","note":"Para sacar la base de datos de RESTORING sin restaurar: RESTORE DATABASE [Musteri] WITH RECOVERY;. NO_TRUNCATE equivale a COPY_ONLY + CONTINUE_AFTER_ERROR e intenta leer el log aunque la base de datos no se pueda abrir.","tags":["tail-log","backup log","norecovery","no_truncate","restore","recovery"]},{"id":"restore-pitr-stopat","task":"Restaurar a un punto en el tiempo (point-in-time restore, STOPAT)","category":"Restauración y recuperación","command":"-- Restore under a side name, keep the original intact:\nRESTORE DATABASE [Musteri_PITR]\nFROM DISK = N'D:\\Backup\\Musteri_full_20261008.bak'\nWITH MOVE N'Musteri'     TO N'D:\\Data\\Musteri_PITR.mdf',\n     MOVE N'Musteri_log' TO N'D:\\Log\\Musteri_PITR_log.ldf',\n     NORECOVERY, STATS = 10;\n\nRESTORE DATABASE [Musteri_PITR]\nFROM DISK = N'D:\\Backup\\Musteri_diff_20261008.bak' WITH NORECOVERY;\n\n-- All log backups in order, each with the same STOPAT:\nRESTORE LOG [Musteri_PITR] FROM DISK = N'D:\\Backup\\Musteri_log_1200.trn'\nWITH NORECOVERY, STOPAT = '2026-10-08T14:29:00';\nRESTORE LOG [Musteri_PITR] FROM DISK = N'D:\\Backup\\Musteri_log_1500.trn'\nWITH NORECOVERY, STOPAT = '2026-10-08T14:29:00';\n\nRESTORE DATABASE [Musteri_PITR] WITH RECOVERY;\n\n-- Alternative: step forward with STANDBY and read data at each point:\n-- RESTORE LOG [Musteri_PITR] FROM DISK = N'...' WITH STANDBY = N'D:\\Backup\\Musteri_PITR_undo.dat', STOPAT = '...';","what":"Aplica FULL + DIFF (opcional) + backups de log WITH NORECOVERY y detiene la reproducción del log en la hora indicada; devuelve la base de datos a justo antes de la operación errónea (DELETE/UPDATE equivocado, DROP).","when":"Para recuperar datos borrados/actualizados por accidente, para inspeccionar los datos en un momento concreto o para una auditoría.","caution":"Restaure con un nombre alternativo en lugar de sobre la original y luego copie de vuelta las filas necesarias; si debe restaurar encima, tome primero un backup tail-log. STOPAT está en hora local del servidor. El modelo de recuperación debe ser FULL; si el backup de log que contiene la hora de STOPAT incluye operaciones con registro mínimo bajo BULK_LOGGED, no puede detenerse dentro de ese backup. Tras RESTORE ... WITH RECOVERY no se pueden aplicar más logs.","note":"Especificar STOPAT en cada restauración de log es el patrón recomendado; el mensaje informativo \"contains records that were logged before the designated point in time\" en los archivos anteriores es normal. Para detenerse en una marca con nombre (BEGIN TRAN ... WITH MARK) use STOPATMARK / STOPBEFOREMARK. Use fn_dump_dblog o los logs de la aplicación para encontrar la hora exacta.","tags":["stopat","point-in-time","restore log","norecovery","standby","rollback to time"]},{"id":"restore-verify-header","task":"Inspeccionar y verificar un archivo de backup (VERIFYONLY, HEADERONLY, FILELISTONLY)","category":"Restauración y recuperación","command":"-- Readability and page checksum verification:\nRESTORE VERIFYONLY FROM DISK = N'D:\\Backup\\Musteri_full_20261008.bak' WITH CHECKSUM;\n\n-- Backup sets in the file (type, LSN, date, version, compression):\nRESTORE HEADERONLY FROM DISK = N'D:\\Backup\\Musteri_full_20261008.bak';\n\n-- Logical file names and sizes (needed for MOVE):\nRESTORE FILELISTONLY FROM DISK = N'D:\\Backup\\Musteri_full_20261008.bak';\n\n-- Media info (stripe count: FamilyCount):\nRESTORE LABELONLY FROM DISK = N'D:\\Backup\\Musteri_full_20261008.bak';","what":"Verifica que el backup es legible y que sus checksums de página son válidos sin restaurarlo; muestra los conjuntos de backup que contiene (Position, BackupType, FirstLSN/LastLSN, DatabaseVersion), los nombres de archivo lógicos y la información de stripes.","when":"Antes de restaurar un backup recibido de otro equipo, para conocer los nombres de archivo para MOVE, como comprobación rápida tras un job de backup o para elegir el FILE = n correcto cuando un archivo contiene varios conjuntos.","caution":"VERIFYONLY no garantiza que la base de datos sea restaurable y consistente; solo comprueba la estructura del backup y (si se tomó WITH CHECKSUM) los checksums de página. Para una garantía real haga restauraciones de prueba regulares + DBCC CHECKDB. Un backup de una versión más nueva no se puede restaurar en una más antigua (compruebe DatabaseVersion). Operaciones de lectura; seguras.","note":"Si HEADERONLY muestra HasBackupChecksums = 0, el backup se tomó sin CHECKSUM y VERIFYONLY WITH CHECKSUM no puede validar las páginas. Los archivos escritos sin INIT acumulan varios conjuntos; pase WITH FILE = <Position> en la restauración.","tags":["restore verifyonly","headeronly","filelistonly","labelonly","backup verification","checksum"]},{"id":"page-restore-suspect","task":"Encontrar páginas corruptas (suspect_pages) y restaurar páginas individuales","category":"Restauración y recuperación","command":"-- Pages that hit 823/824 or checksum errors:\nSELECT DB_NAME(sp.database_id) AS db, sp.file_id, sp.page_id,\n       CASE sp.event_type WHEN 1 THEN '823/824 error' WHEN 2 THEN 'bad checksum'\n            WHEN 3 THEN 'torn page'     WHEN 4 THEN 'restored'\n            WHEN 5 THEN 'repaired'      WHEN 7 THEN 'deallocated by DBCC' END AS event_desc,\n       sp.error_count, sp.last_update_date\nFROM   msdb.dbo.suspect_pages sp\nORDER BY sp.last_update_date DESC;\n\n-- Page restore (example 1:336): FULL + later logs + a new log backup\nRESTORE DATABASE [Musteri] PAGE = '1:336'\nFROM DISK = N'D:\\Backup\\Musteri_full_20261008.bak' WITH NORECOVERY;\nRESTORE LOG [Musteri] FROM DISK = N'D:\\Backup\\Musteri_log_1200.trn' WITH NORECOVERY;\nBACKUP LOG [Musteri] TO DISK = N'D:\\Backup\\Musteri_log_pagetail.trn' WITH INIT;\nRESTORE LOG [Musteri] FROM DISK = N'D:\\Backup\\Musteri_log_pagetail.trn' WITH RECOVERY;\n\n-- Verify:\nDBCC CHECKDB (N'Musteri') WITH NO_INFOMSGS, ALL_ERRORMSGS;","what":"Lista desde msdb.dbo.suspect_pages las páginas con errores de E/S (823/824), de checksum o de torn page; repara páginas corruptas individuales tomándolas del backup FULL y aplicándoles los logs, sin restaurar la base de datos completa.","when":"Ante errores 823/824 en el error log, errores de consistencia limitados a unas pocas páginas en CHECKDB o una consulta de usuario que falla con \"incorrect checksum\".","caution":"Requiere recuperación FULL o BULK_LOGGED y una cadena de log ininterrumpida. La restauración de páginas online es solo de Enterprise/Developer; en otras ediciones la base de datos está offline durante la restauración. Las páginas de asignación (GAM/SGAM/PFS), las cabeceras de archivo y la boot page (1:9) no pueden restaurarse así. 823/824 suele indicar un problema de almacenamiento; no cierre el caso sin revisar el disco/controladora/SAN, o se repetirá. Con muchas páginas dañadas, una restauración completa de la base de datos es más rápida.","note":"suspect_pages contiene como máximo 1,000 filas; cuando está llena, los nuevos errores no se registran, así que borre las filas antiguas (event_type 4/5/7). Varias páginas se separan con comas: PAGE = '1:336, 1:337'. En un AG, una página corrupta en el primario se corrige automáticamente desde un secundario mediante automatic page repair (sys.dm_hadr_auto_page_repair).","tags":["suspect_pages","page restore","823","824","corruption","bad page"]},{"id":"top-queries-cpu","task":"Encontrar las consultas más costosas en la plan cache (CPU, lecturas, duración)","category":"Rendimiento","command":"SELECT TOP (20)\n       qs.query_hash,\n       SUM(qs.execution_count)                                       AS execs,\n       SUM(qs.total_worker_time) / 1000                              AS total_cpu_ms,\n       SUM(qs.total_worker_time) / SUM(qs.execution_count) / 1000    AS avg_cpu_ms,\n       SUM(qs.total_elapsed_time) / SUM(qs.execution_count) / 1000   AS avg_ms,\n       SUM(qs.total_logical_reads) / SUM(qs.execution_count)         AS avg_reads,\n       SUM(qs.total_grant_kb) / SUM(qs.execution_count)              AS avg_grant_kb,\n       COUNT(DISTINCT qs.query_plan_hash)                            AS plan_count,\n       MAX(qs.last_execution_time)                                   AS last_exec,\n       MIN(SUBSTRING(st.text, qs.statement_start_offset / 2 + 1,\n           ((CASE qs.statement_end_offset WHEN -1 THEN DATALENGTH(st.text)\n             ELSE qs.statement_end_offset END - qs.statement_start_offset) / 2) + 1)) AS statement_text,\n       MIN(DB_NAME(pa.dbid))                                         AS db\nFROM   sys.dm_exec_query_stats qs\n  CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st\n  OUTER APPLY (SELECT CONVERT(int, value) AS dbid FROM sys.dm_exec_plan_attributes(qs.plan_handle)\n               WHERE attribute = N'dbid') pa\nGROUP BY qs.query_hash\nORDER BY total_cpu_ms DESC;\n\n-- To rank by reads: ORDER BY SUM(qs.total_logical_reads) DESC\n-- Plan: SELECT query_plan FROM sys.dm_exec_query_plan(<plan_handle>);","what":"Agrupa las sentencias de la plan cache por query_hash (las consultas ad hoc que solo difieren en literales se reducen a una fila) y devuelve el total y la media de CPU, duración, lecturas lógicas, memory grant y número de planes.","when":"Cuando la CPU está al máximo, ante una queja de \"el servidor va lento\", para bases de datos sin Query Store o cuando necesita una vista de todo el servidor a través de todas las bases de datos.","caution":"Solo ve los planes que siguen en caché: faltan las consultas cuyos planes se expulsaron por reinicio, DBCC FREEPROCCACHE, presión de memoria o RECOMPILE. Use Query Store para un análisis persistente y por ventanas de tiempo. plan_count > 1 sugiere sensibilidad a parámetros o distintas opciones SET. Con una plan cache muy grande la consulta puede tardar unos segundos; segura.","since":"2016","note":"total_grant_kb es una columna de 2016+. Para totales por procedimiento almacenado use sys.dm_exec_procedure_stats.","tags":["dm_exec_query_stats","top queries","cpu","query_hash","plan cache","expensive query"]},{"id":"wait-stats-top","task":"Estadísticas de espera: en qué está esperando el servidor","category":"Rendimiento","command":"WITH w AS (\n    SELECT wait_type, waiting_tasks_count,\n           wait_time_ms / 1000.0                         AS wait_s,\n           (wait_time_ms - signal_wait_time_ms) / 1000.0 AS resource_s,\n           signal_wait_time_ms / 1000.0                  AS signal_s\n    FROM   sys.dm_os_wait_stats\n    WHERE  waiting_tasks_count > 0\n      AND  wait_type NOT LIKE N'SLEEP[_]%'\n      AND  wait_type NOT IN (\n           N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP', N'BROKER_TO_FLUSH',\n           N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT',\n           N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE', N'FT_IFTS_SCHEDULER_IDLE_WAIT',\n           N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_WORK_QUEUE', N'LAZYWRITER_SLEEP',\n           N'LOGMGR_QUEUE', N'ONDEMAND_TASK_QUEUE', N'PVS_PREALLOCATE', N'REQUEST_FOR_DEADLOCK_SEARCH',\n           N'SNI_HTTP_ACCEPT', N'SOS_WORK_DISPATCHER', N'SP_SERVER_DIAGNOSTICS_SLEEP',\n           N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'WAITFOR',\n           N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT', N'XE_LIVE_TARGET_TVF', N'PREEMPTIVE_XE_DISPATCHER',\n           N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP', N'QDS_ASYNC_QUEUE', N'QDS_SHUTDOWN_QUEUE',\n           N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'PWAIT_ALL_COMPONENTS_INITIALIZED',\n           N'PWAIT_EXTENSIBILITY_CLEANUP_TASK', N'STARTUP_DEPENDENCY_MANAGER', N'WAIT_XTP_CKPT_CLOSE')\n)\nSELECT TOP (20) wait_type, waiting_tasks_count,\n       CAST(wait_s AS DECIMAL(18,1))                                AS wait_s,\n       CAST(100.0 * wait_s / SUM(wait_s) OVER () AS DECIMAL(5,1))   AS pct,\n       CAST(resource_s AS DECIMAL(18,1))                            AS resource_s,\n       CAST(signal_s AS DECIMAL(18,1))                              AS signal_s,\n       CAST(1000.0 * wait_s / waiting_tasks_count AS DECIMAL(18,2)) AS avg_wait_ms\nFROM   w\nORDER BY wait_s DESC;\n\n-- Values accumulate since:\nSELECT sqlserver_start_time FROM sys.dm_os_sys_info;\n\n-- User requests waiting right now:\nSELECT wt.session_id, wt.wait_type, wt.wait_duration_ms, wt.resource_description, wt.blocking_session_id\nFROM   sys.dm_os_waiting_tasks wt\n  JOIN sys.dm_exec_sessions s ON s.session_id = wt.session_id\nWHERE  s.is_user_process = 1;","what":"Filtra las esperas benignas en segundo plano y desglosa el tiempo total de espera del servidor por tipo de espera con porcentaje y la parte de recurso frente a señal (cola de CPU); también muestra las peticiones de usuario que están esperando ahora mismo.","when":"Como primer paso de una investigación de rendimiento para clasificar el cuello de botella (E/S, CPU, bloqueo, log, memoria, paralelismo); para comparaciones de línea base antes/después.","caution":"Los valores son acumulados desde el reinicio; para un problema actual tome dos instantáneas y compare el delta. DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR) reinicia los contadores de todo el servidor y estropea las herramientas de monitoreo; evítelo. Interpretación: PAGEIOLATCH_* = lecturas de disco, WRITELOG = disco de log, LCK_M_* = bloqueos, CXPACKET/CXCONSUMER = paralelismo, RESOURCE_SEMAPHORE = memory grants, SOS_SCHEDULER_YIELD + señal alta = presión de CPU. Consulta de solo lectura; segura.","note":"Para esperas por sesión use sys.dm_exec_session_wait_stats (2016+); para categorías de espera por consulta en Query Store use sys.query_store_wait_stats (2017+).","tags":["wait stats","dm_os_wait_stats","waits","pageiolatch","writelog","performance"]},{"id":"deadlock-system-health","task":"Leer deadlocks pasados desde la sesión system_health (deadlock graph)","category":"Rendimiento","command":"SET QUOTED_IDENTIFIER ON;  -- required for XML methods (sqlcmd/Agent default is OFF)\nWITH dl AS (\n    SELECT CAST(event_data AS XML) AS x\n    FROM   sys.fn_xe_file_target_read_file(N'system_health*.xel', NULL, NULL, NULL)\n    WHERE  object_name = N'xml_deadlock_report'\n)\nSELECT TOP (20)\n       x.value('(event/@timestamp)[1]', 'datetime2')                                          AS utc_time,\n       x.value('(event/data/value/deadlock/victim-list/victimProcess/@id)[1]', 'varchar(50)')  AS victim,\n       x.value('count(event/data/value/deadlock/process-list/process)', 'int')                 AS process_count,\n       x.value('(event/data/value/deadlock/process-list/process/@currentdbname)[1]', 'sysname') AS db,\n       x.query('event/data/value/deadlock')                                                    AS deadlock_graph\nFROM   dl\nORDER BY utc_time DESC;","what":"Lee los eventos xml_deadlock_report del destino event_file de la sesión de Extended Events system_health (activa por defecto); por cada deadlock devuelve la hora (UTC), el proceso víctima, el número de participantes, la base de datos y el XML completo del deadlock graph.","when":"Cuando la aplicación reporta el error 1205, al analizar la frecuencia de deadlocks y los objetos implicados; en servidores sin el trace flag 1222.","caution":"Los archivos de system_health rotan; en un servidor con carga los deadlocks antiguos pueden haber desaparecido, así que configure una sesión XE dedicada para un archivo permanente. Leer muchos archivos cuesta CPU; mantenga el TOP en horario laboral. Los eventos se escriben con retraso, por lo que un deadlock nuevo aparece tras unos segundos. Solo lectura; segura.","note":"Haga clic en el valor deadlock_graph y guárdelo como .xdl para abrirlo gráficamente en SSMS. El valor por defecto del event_file de system_health es 10 x 100 MB en 2016 SP2 CU12 / 2017 CU20 / 2019 CU3 y posteriores (Standard/Enterprise); 4 x 5 MB en builds anteriores y otras ediciones. En el grafo, inputbuf, waitresource (KEY/PAGE/OBJECT) y lockMode son los campos clave; la mayoría de los deadlocks se resuelven con un índice faltante o un orden de acceso consistente.","tags":["deadlock","system_health","xml_deadlock_report","extended events","1205","deadlock graph"]},{"id":"xe-long-query-session","task":"Crear una sesión de Extended Events: capturar consultas lentas a archivo y leerlas","category":"Rendimiento","command":"SET QUOTED_IDENTIFIER ON;\nCREATE EVENT SESSION [dba_long_queries] ON SERVER\nADD EVENT sqlserver.sql_statement_completed (\n    ACTION (sqlserver.client_app_name, sqlserver.client_hostname, sqlserver.database_name,\n            sqlserver.session_id, sqlserver.sql_text, sqlserver.username, sqlserver.query_hash)\n    WHERE (duration >= 1000000 AND sqlserver.is_system = 0)),   -- 1 s (microseconds)\nADD EVENT sqlserver.rpc_completed (\n    ACTION (sqlserver.client_app_name, sqlserver.client_hostname, sqlserver.database_name,\n            sqlserver.session_id, sqlserver.sql_text, sqlserver.username, sqlserver.query_hash)\n    WHERE (duration >= 1000000 AND sqlserver.is_system = 0))\nADD TARGET package0.event_file (SET filename = N'D:\\XE\\dba_long_queries.xel',\n                                    max_file_size = 256, max_rollover_files = 4)\nWITH (MAX_MEMORY = 8 MB, EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,\n      MAX_DISPATCH_LATENCY = 15 SECONDS, STARTUP_STATE = OFF);\n\nALTER EVENT SESSION [dba_long_queries] ON SERVER STATE = START;\n\n-- Read:\nSELECT x.value('(event/@name)[1]', 'sysname')                                AS event_name,\n       x.value('(event/@timestamp)[1]', 'datetime2')                         AS utc_time,\n       x.value('(event/data[@name=\"duration\"]/value)[1]', 'bigint') / 1000   AS duration_ms,\n       x.value('(event/data[@name=\"cpu_time\"]/value)[1]', 'bigint') / 1000   AS cpu_ms,\n       x.value('(event/data[@name=\"logical_reads\"]/value)[1]', 'bigint')     AS logical_reads,\n       x.value('(event/action[@name=\"database_name\"]/value)[1]', 'sysname')  AS db,\n       x.value('(event/action[@name=\"client_app_name\"]/value)[1]', 'nvarchar(256)') AS app,\n       x.value('(event/action[@name=\"sql_text\"]/value)[1]', 'nvarchar(max)') AS sql_text\nFROM  (SELECT CAST(event_data AS XML) AS x\n       FROM sys.fn_xe_file_target_read_file(N'D:\\XE\\dba_long_queries*.xel', NULL, NULL, NULL)) t\nORDER BY duration_ms DESC;\n\n-- When done:\n-- ALTER EVENT SESSION [dba_long_queries] ON SERVER STATE = STOP;\n-- DROP EVENT SESSION [dba_long_queries] ON SERVER;","what":"Crea una sesión ligera de Extended Events que escribe en disco las sentencias y llamadas RPC de más de 1 segundo con aplicación, host, usuario, base de datos y texto SQL; convierte los archivos .xel en filas con T-SQL.","when":"Ante quejas de lentitud intermitente, cuando necesita datos del cliente (app/host) que Query Store no muestra y como sustituto de SQL Trace/Profiler.","caution":"Un umbral muy bajo (p. ej. 0) llena los archivos rápidamente y añade sobrecarga en un servidor con carga; filtre en el predicado WHERE, no después. La acción sql_text es costosa con alto volumen. La carpeta debe tener permiso de escritura para la cuenta de servicio de SQL Server. duration y cpu_time están en microsegundos. Detenga/elimine la sesión al terminar; con STARTUP_STATE = OFF no se inicia tras un reinicio.","note":"Para limitarlo a una base de datos añada sqlserver.database_name = N'Musteri' a la cláusula WHERE. Para un vistazo rápido use SSMS > Management > Extended Events > Watch Live Data.","tags":["extended events","xe","event session","slow query","sql_statement_completed","event_file"]},{"id":"tempdb-contention","task":"Diagnosticar la contención y el uso de espacio de tempdb","category":"Rendimiento","command":"-- 1) Current tempdb page latch waits (2:1:1 PFS, 2:1:2 GAM, 2:1:3 SGAM):\nSELECT wt.session_id, wt.wait_type, wt.wait_duration_ms, wt.resource_description\nFROM   sys.dm_os_waiting_tasks wt\nWHERE  wt.wait_type LIKE N'PAGELATCH[_]%'\n  AND  wt.resource_description LIKE N'2:%';\n\n-- 2) Sessions using the most tempdb (MB):\nSELECT TOP (20) su.session_id, s.login_name, s.program_name,\n       (su.user_objects_alloc_page_count - su.user_objects_dealloc_page_count) * 8 / 1024         AS user_obj_mb,\n       (su.internal_objects_alloc_page_count - su.internal_objects_dealloc_page_count) * 8 / 1024 AS internal_obj_mb\nFROM   sys.dm_db_session_space_usage su\n  JOIN sys.dm_exec_sessions s ON s.session_id = su.session_id\nWHERE  s.is_user_process = 1\nORDER BY su.user_objects_alloc_page_count + su.internal_objects_alloc_page_count\n       - su.user_objects_dealloc_page_count - su.internal_objects_dealloc_page_count DESC;\n\n-- 3) Overall breakdown and version store:\nSELECT SUM(unallocated_extent_page_count) * 8 / 1024       AS free_mb,\n       SUM(user_object_reserved_page_count) * 8 / 1024     AS user_obj_mb,\n       SUM(internal_object_reserved_page_count) * 8 / 1024 AS internal_obj_mb,\n       SUM(version_store_reserved_page_count) * 8 / 1024   AS version_store_mb\nFROM   tempdb.sys.dm_db_file_space_usage;\n\nSELECT DB_NAME(database_id) AS db, reserved_page_count * 8 / 1024 AS version_store_mb\nFROM   sys.dm_tran_version_store_space_usage\nORDER BY reserved_page_count DESC;\n\n-- 4) File count / equal sizing and memory-optimized metadata:\nSELECT name, type_desc, size * 8 / 1024 AS size_mb, growth * 8 / 1024 AS growth_mb, is_percent_growth\nFROM   tempdb.sys.database_files;\nSELECT SERVERPROPERTY('IsTempdbMetadataMemoryOptimized') AS tempdb_metadata_memopt;","what":"Muestra las esperas PAGELATCH sobre las páginas de asignación de tempdb (PFS/GAM/SGAM) y las tablas del sistema, las sesiones que más tempdb usan (tablas temporales frente a spills de sort/hash), qué base de datos está inflando el version store y la configuración de archivos.","when":"Cuando suben las esperas PAGELATCH_UP/EX con cargas intensivas de tablas temporales, cuando tempdb se llena (1105/9002) o cuando crece el version store.","caution":"Las páginas 2:1:1, 2:1:2, 2:1:3 y las páginas PFS en múltiplos de 8088 indican contención de asignación: la solución son varios archivos de datos del mismo tamaño con igual crecimiento (uno por núcleo hasta 8 y luego añadir de 4 en 4). Para la contención en páginas de tablas del sistema, 2019+ ofrece memory-optimized tempdb metadata (requiere reinicio). Si crece el version store, busque la transacción snapshot abierta más antigua; reducir tempdb no es una solución. Consulta de solo lectura; segura.","note":"Metadatos optimizados para memoria: ALTER SERVER CONFIGURATION SET MEMORY_OPTIMIZED TEMPDB_METADATA = ON; + reinicio (2019+). 2022 añadió actualizaciones de GAM/SGAM sin latch, lo que reduce mucho la contención. La transacción que retiene el version store: sys.dm_tran_active_snapshot_database_transactions ORDER BY elapsed_time_seconds DESC.","tags":["tempdb","pagelatch","pfs","gam","version store","contention"]},{"id":"qs-enable-config","task":"Habilitar Query Store, aplicar los ajustes recomendados y comprobar su estado","category":"Query Store","command":"ALTER DATABASE [Musteri] SET QUERY_STORE = ON;\nALTER DATABASE [Musteri] SET QUERY_STORE (\n    OPERATION_MODE = READ_WRITE,\n    QUERY_CAPTURE_MODE = AUTO,\n    MAX_STORAGE_SIZE_MB = 2048,\n    INTERVAL_LENGTH_MINUTES = 15,\n    DATA_FLUSH_INTERVAL_SECONDS = 900,\n    CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 30),\n    SIZE_BASED_CLEANUP_MODE = AUTO,\n    MAX_PLANS_PER_QUERY = 200,\n    WAIT_STATS_CAPTURE_MODE = ON);\n\n-- State: if actual_state dropped to READ_ONLY, check readonly_reason\nUSE [Musteri];\nSELECT actual_state_desc, desired_state_desc, readonly_reason,\n       current_storage_size_mb, max_storage_size_mb,\n       query_capture_mode_desc, size_based_cleanup_mode_desc, wait_stats_capture_mode_desc\nFROM   sys.database_query_store_options;","what":"Habilita Query Store en la base de datos; empieza a persistir textos de consulta, planes y estadísticas de ejecución y de espera en intervalos de tiempo. Las opciones definen el límite de almacenamiento, la limpieza y el modo de captura.","when":"Cuando una nueva base de datos entra en producción, antes de una actualización de versión/nivel de compatibilidad (para una línea base) o cuando se sospecha una regresión de plan.","caution":"Al alcanzar MAX_STORAGE_SIZE_MB, Query Store pasa silenciosamente a READ_ONLY (readonly_reason = 65536) y deja de recopilar; vigile current_storage_size_mb. QUERY_CAPTURE_MODE = ALL llena el almacenamiento rápidamente y añade sobrecarga con cargas ad hoc intensas. Los sistemas OLTP muy cargados pueden mostrar esperas QDS_*. Ponerlo en OFF conserva los datos recopilados; CLEAR los elimina.","since":"2016","note":"En 2022, las bases de datos recién creadas tienen Query Store activo en READ_WRITE por defecto. En 2019+ QUERY_CAPTURE_MODE = CUSTOM acepta umbrales (EXECUTION_COUNT, TOTAL_COMPILE_CPU_TIME_MS, TOTAL_EXECUTION_CPU_TIME_MS). Query Store para secundarios legibles (ALTER DATABASE ... FOR SECONDARY SET QUERY_STORE = ON) es una versión preliminar limitada en 2022, habilitada solo con el trace flag 12606 y no soportada en producción; 2025 no necesita trace flag (sigue en versión preliminar).","tags":["query store","enable","max_storage_size_mb","query_capture_mode","read_only","qds"]},{"id":"qs-regressed-queries","task":"Encontrar regresiones de plan con Query Store (misma consulta, planes distintos)","category":"Query Store","command":"USE [Musteri];\n-- Queries with more than one plan in the last 24 hours, per-plan performance:\nWITH plan_stats AS (\n    SELECT p.query_id, p.plan_id, p.is_forced_plan,\n           SUM(rs.count_executions) AS execs,\n           SUM(rs.avg_duration * rs.count_executions) / SUM(rs.count_executions) / 1000.0     AS avg_ms,\n           SUM(rs.avg_cpu_time * rs.count_executions) / SUM(rs.count_executions) / 1000.0     AS avg_cpu_ms,\n           SUM(rs.avg_logical_io_reads * rs.count_executions) / SUM(rs.count_executions)      AS avg_reads,\n           MAX(rs.last_execution_time) AS last_exec\n    FROM   sys.query_store_plan p\n      JOIN sys.query_store_runtime_stats rs ON rs.plan_id = p.plan_id\n      JOIN sys.query_store_runtime_stats_interval i\n        ON i.runtime_stats_interval_id = rs.runtime_stats_interval_id\n    WHERE  i.start_time >= DATEADD(HOUR, -24, SYSUTCDATETIME())\n    GROUP BY p.query_id, p.plan_id, p.is_forced_plan\n)\nSELECT ps.query_id, ps.plan_id, ps.is_forced_plan, ps.execs,\n       CAST(ps.avg_ms AS DECIMAL(18,2))     AS avg_ms,\n       CAST(ps.avg_cpu_ms AS DECIMAL(18,2)) AS avg_cpu_ms,\n       CAST(ps.avg_reads AS BIGINT)         AS avg_reads,\n       CAST(ps.avg_ms / NULLIF(MIN(ps.avg_ms) OVER (PARTITION BY ps.query_id), 0) AS DECIMAL(18,1)) AS x_slower_than_best,\n       ps.last_exec,\n       OBJECT_NAME(q.object_id)     AS module,\n       LEFT(qt.query_sql_text, 120) AS query_text\nFROM   plan_stats ps\n  JOIN sys.query_store_query q       ON q.query_id = ps.query_id\n  JOIN sys.query_store_query_text qt ON qt.query_text_id = q.query_text_id\nWHERE  ps.query_id IN (SELECT query_id FROM plan_stats GROUP BY query_id HAVING COUNT(*) > 1)\nORDER BY ps.query_id, ps.avg_ms;","what":"Encuentra consultas que se ejecutaron con más de un plan en las últimas 24 horas; muestra lado a lado el número de ejecuciones de cada plan, la duración/CPU/lecturas medias ponderadas y cuántas veces es más lento que el mejor plan.","when":"Cuando una consulta \"ayer iba rápida y hoy va lenta\", tras una actualización de estadísticas, un cambio de índices, una actualización del nivel de compatibilidad o un parche.","caution":"La duración media también depende de la distribución de los datos; al comparar planes que se ejecutaron con valores de parámetros distintos, mire también avg_reads (parameter sniffing). Las ventanas de tiempo están en UTC. Los resultados son estadísticamente débiles para planes con pocas ejecuciones. Consulta de solo lectura; segura.","since":"2016","note":"SSMS > Query Store > Regressed Queries hace lo mismo de forma gráfica. En 2022 con el nivel de compatibilidad 160, la optimización Parameter Sensitive Plan (PSP) crea varios query_id variantes para una misma sentencia; asócielos al padre con sys.query_store_query_variant (parent_query_id). El automatic tuning de 2017+ (FORCE_LAST_GOOD_PLAN) puede corregir automáticamente estas regresiones.","tags":["query store","plan regression","regressed queries","parameter sniffing","query_store_runtime_stats","plan"]},{"id":"qs-force-plan-hints","task":"Forzar un buen plan o adjuntar un hint con Query Store","category":"Query Store","command":"USE [Musteri];\n-- Force the good plan (query_id / plan_id from the regression query):\nEXEC sys.sp_query_store_force_plan @query_id = 42, @plan_id = 7;\n\n-- Forced plans and failure reason:\nSELECT p.query_id, p.plan_id, p.is_forced_plan, p.plan_forcing_type_desc,\n       p.force_failure_count, p.last_force_failure_reason_desc\nFROM   sys.query_store_plan p\nWHERE  p.is_forced_plan = 1;\n\n-- Undo:\nEXEC sys.sp_query_store_unforce_plan @query_id = 42, @plan_id = 7;\n\n-- 2022+: attach / remove a hint without changing code:\nEXEC sys.sp_query_store_set_hints @query_id = 42, @query_hints = N'OPTION (RECOMPILE)';\nSELECT query_id, query_hint_text, last_query_hint_failure_reason_desc\nFROM   sys.query_store_query_hints;\nEXEC sys.sp_query_store_clear_hints @query_id = 42;","what":"Fuerza un plan concreto de Query Store para la consulta elegida; el optimizador usa ese plan en cada compilación. En 2022 también puede adjuntar hints como RECOMPILE, MAXDOP u OPTIMIZE FOR sin cambiar el código de la aplicación.","when":"Cuando una regresión de plan necesita una solución urgente en producción, hasta que esté lista una solución permanente (índice, reescritura de la consulta, estadísticas).","caution":"Un plan forzado puede degradarse al crecer los datos y no es una solución permanente; revíselo regularmente. Si se elimina/renombra un índice que usa el plan, el forzado falla (last_force_failure_reason_desc = NO_INDEX etc.) y la consulta se compila normalmente. Tras forzar, puede aparecer un plan \"moralmente equivalente\" con un nuevo plan_id; es lo esperado. El forzado funciona mientras Query Store está en READ_ONLY, pero verifíquelo tras una restauración o un failover de AG.","since":"2016","note":"Los hints de Query Store son de 2022+ (antes en Azure SQL). Los hints sustituyen a las plan guides; si un hint no puede aplicarse, la consulta se ejecuta sin él y el motivo aparece en last_query_hint_failure_reason_desc. El forzado de planes en secundarios legibles (@replica_group_id) requiere Query Store para réplicas secundarias, que en 2022 es una versión preliminar limitada habilitada con el trace flag 12606.","tags":["force plan","sp_query_store_force_plan","query store hints","plan forcing","regression","recompile"]},{"id":"missing-index-dmv","task":"Leer las sugerencias de índices faltantes desde las DMV (con salvedades)","category":"Mantenimiento","command":"USE [Musteri];\nSELECT TOP (25)\n       mid.statement                                   AS table_name,\n       mid.equality_columns, mid.inequality_columns, mid.included_columns,\n       migs.user_seeks, migs.user_scans,\n       CAST(migs.avg_total_user_cost AS DECIMAL(18,2)) AS avg_query_cost,\n       migs.avg_user_impact                            AS est_impact_pct,\n       CAST(migs.user_seeks * migs.avg_total_user_cost * migs.avg_user_impact / 100.0 AS DECIMAL(18,0)) AS score,\n       migs.last_user_seek,\n       N'CREATE INDEX [IX_' + OBJECT_NAME(mid.object_id, mid.database_id) + N'_missing_'\n         + CAST(mid.index_handle AS NVARCHAR(10)) + N'] ON ' + mid.statement + N' ('\n         + ISNULL(mid.equality_columns, N'')\n         + CASE WHEN mid.equality_columns IS NOT NULL AND mid.inequality_columns IS NOT NULL THEN N', ' ELSE N'' END\n         + ISNULL(mid.inequality_columns, N'') + N')'\n         + ISNULL(N' INCLUDE (' + mid.included_columns + N')', N'') AS create_stmt_draft\nFROM   sys.dm_db_missing_index_details mid\n  JOIN sys.dm_db_missing_index_groups mig       ON mig.index_handle = mid.index_handle\n  JOIN sys.dm_db_missing_index_group_stats migs ON migs.group_handle = mig.index_group_handle\nWHERE  mid.database_id = DB_ID()\nORDER BY score DESC;\n\n-- 2019+: the query text behind each suggestion:\nSELECT TOP (25) mid.statement AS table_name, misq.group_handle, misq.user_seeks,\n       SUBSTRING(st.text, misq.last_statement_start_offset / 2 + 1, 300) AS statement_text\nFROM   sys.dm_db_missing_index_group_stats_query misq\n  JOIN sys.dm_db_missing_index_groups mig  ON mig.index_group_handle = misq.group_handle\n  JOIN sys.dm_db_missing_index_details mid ON mid.index_handle = mig.index_handle\n  CROSS APPLY sys.dm_exec_sql_text(misq.last_sql_handle) st\nWHERE  mid.database_id = DB_ID()\nORDER BY misq.user_seeks DESC;","what":"Puntúa las sugerencias de índices faltantes que el optimizador registró en tiempo de compilación según uso, costo estimado e impacto, y genera un borrador de sentencia CREATE INDEX; en 2019+ también muestra la consulta detrás de cada sugerencia.","when":"Tras una nueva versión de la aplicación, para una revisión rápida de las necesidades de índices de consultas con mucha lectura o como lista inicial para una revisión del diseño de índices.","caution":"No aplique las sugerencias a ciegas: el optimizador ignora los índices existentes, a menudo produce sugerencias casi duplicadas (solapadas) y listas INCLUDE demasiado anchas, y ordena las columnas por igualdad/desigualdad en lugar de por selectividad. Cada índice nuevo cuesta escrituras y almacenamiento; considere primero ampliar un índice existente. Las DMV se reinician con un reinicio, con la base de datos offline y con cambios de índices en la tabla. Consulta de solo lectura; segura.","note":"Antes de crear un índice sugerido, compárelo con los índices existentes de la tabla (sp_helpindex o sys.index_columns) y revíselo junto con la receta de índices no usados. El elemento MissingIndex del XML del plan da la misma información por consulta.","tags":["missing index","missing index dmv","dm_db_missing_index_details","index suggestion","create index","tuning"]},{"id":"unused-indexes","task":"Encontrar índices no usados / de solo escritura","category":"Mantenimiento","command":"USE [Musteri];\nSELECT OBJECT_SCHEMA_NAME(i.object_id) AS schema_name,\n       OBJECT_NAME(i.object_id)        AS table_name,\n       i.name                          AS index_name,\n       ISNULL(us.user_seeks, 0) + ISNULL(us.user_scans, 0) + ISNULL(us.user_lookups, 0) AS reads,\n       ISNULL(us.user_updates, 0)      AS writes,\n       ps.used_mb,\n       us.last_user_seek, us.last_user_scan,\n       N'DROP INDEX ' + QUOTENAME(i.name) + N' ON ' + QUOTENAME(OBJECT_SCHEMA_NAME(i.object_id))\n         + N'.' + QUOTENAME(OBJECT_NAME(i.object_id)) + N';' AS drop_stmt_draft\nFROM   sys.indexes i\n  LEFT JOIN sys.dm_db_index_usage_stats us\n         ON us.database_id = DB_ID() AND us.object_id = i.object_id AND us.index_id = i.index_id\n  CROSS APPLY (SELECT CAST(SUM(p.used_page_count) * 8 / 1024.0 AS DECIMAL(18,1)) AS used_mb\n               FROM sys.dm_db_partition_stats p\n               WHERE p.object_id = i.object_id AND p.index_id = i.index_id) ps\nWHERE  OBJECTPROPERTY(i.object_id, 'IsUserTable') = 1\n  AND  i.type_desc = N'NONCLUSTERED'\n  AND  i.is_primary_key = 0 AND i.is_unique_constraint = 0 AND i.is_unique = 0\n  AND  ISNULL(us.user_seeks, 0) + ISNULL(us.user_scans, 0) + ISNULL(us.user_lookups, 0) = 0\nORDER BY writes DESC, ps.used_mb DESC;\n\n-- Usage counters since:\nSELECT sqlserver_start_time FROM sys.dm_os_sys_info;","what":"Lista los índices nonclustered que no se han leído (seek/scan/lookup = 0) desde el reinicio pero siguen generando costo de escritura, con su tamaño, y genera un borrador de sentencia DROP INDEX.","when":"Ante INSERT/UPDATE lentos en tablas con mucha escritura, para reducir el almacenamiento y el tiempo de mantenimiento (rebuild, CHECKDB) o antes de consolidar índices.","caution":"Los contadores se reinician con un reinicio, con la base de datos offline/detach y (en algunas builds antiguas de 2012/2014) con la reconstrucción del índice; los índices usados solo por informes de fin de mes/fin de año parecen \"no usados\" en una ventana corta. Observe al menos un ciclo de negocio completo antes de eliminar, y en un AG revise el uso en los secundarios legibles por separado (los contadores son por réplica). Los índices únicos imponen restricciones aunque no se lean; se excluyen. Consulta de solo lectura; segura.","note":"Deshabilitar con ALTER INDEX ... DISABLE antes de eliminar facilita la marcha atrás (la definición se conserva; REBUILD lo recupera). Buscar el nombre del índice en Query Store o en la plan cache muestra si un hint lo referencia.","tags":["unused index","write-only index","dm_db_index_usage_stats","drop index","write overhead","index"]},{"id":"stats-health","task":"Salud de las estadísticas: cuándo se actualizaron, cuántas filas cambiaron","category":"Mantenimiento","command":"USE [Musteri];\nSELECT OBJECT_SCHEMA_NAME(s.object_id) AS schema_name,\n       OBJECT_NAME(s.object_id)        AS table_name,\n       s.name                          AS stats_name,\n       s.auto_created, s.has_filter, s.no_recompute,\n       sp.last_updated,\n       sp.rows, sp.rows_sampled,\n       CAST(100.0 * sp.rows_sampled / NULLIF(sp.rows, 0) AS DECIMAL(5,1))         AS sample_pct,\n       sp.modification_counter,\n       CAST(100.0 * sp.modification_counter / NULLIF(sp.rows, 0) AS DECIMAL(9,1)) AS modified_pct,\n       CAST(CASE WHEN 500 + 0.20 * sp.rows < SQRT(1000.0 * sp.rows) THEN 500 + 0.20 * sp.rows\n                 ELSE SQRT(1000.0 * sp.rows) END AS BIGINT)                       AS auto_update_threshold,\n       sp.persisted_sample_percent\nFROM   sys.stats s\n  CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) sp\nWHERE  OBJECTPROPERTY(s.object_id, 'IsUserTable') = 1\nORDER BY sp.modification_counter DESC;\n\nSELECT name, is_auto_create_stats_on, is_auto_update_stats_on, is_auto_update_stats_async_on\nFROM   sys.databases\nWHERE  name = DB_NAME();","what":"Muestra para cada estadística la hora de la última actualización, la tasa de muestreo, las filas modificadas desde la última actualización y el umbral al que se dispara la actualización automática (umbral dinámico para compat 130+).","when":"Para planes con una gran diferencia entre filas estimadas y reales, tras una carga de datos grande o al comprobar si el job de mantenimiento de estadísticas es eficaz.","caution":"La fórmula del umbral se aplica al nivel de compatibilidad 130+ (MIN(500 + 20%, SQRT(1000 x rows))); los niveles inferiores usan la regla del 20% + 500. Las estadísticas nunca actualizadas (tablas vacías) devuelven NULL. Las columnas sesgadas con un sample_pct bajo pueden necesitar FULLSCAN o PERSIST_SAMPLE_PERCENT. Consulta de solo lectura; segura.","since":"2016","note":"persisted_sample_percent refleja UPDATE STATISTICS ... WITH PERSIST_SAMPLE_PERCENT = ON (2016 SP1 CU4 / 2017 CU1+). Vea la receta update-statistics para los comandos de actualización.","tags":["statistics","stats health","dm_db_stats_properties","modification_counter","auto update","cardinality"]},{"id":"checkdb-strategy","task":"Estrategia de DBCC CHECKDB: última comprobación correcta, división del trabajo, interpretación de resultados","category":"Mantenimiento","command":"-- Last successful CHECKDB (1900-01-01 = never):\nSELECT name, DATABASEPROPERTYEX(name, 'LastGoodCheckDbTime') AS last_good_checkdb\nFROM   sys.databases\nWHERE  state_desc = N'ONLINE' AND name <> N'tempdb'\nORDER BY last_good_checkdb;\n\n-- Estimated tempdb requirement:\nDBCC CHECKDB (N'Musteri') WITH ESTIMATEONLY;\n\n-- Split a large DB across days: allocation + catalog + table by table CHECKTABLE\nUSE [Musteri];\nDBCC CHECKALLOC (N'Musteri') WITH NO_INFOMSGS;\nDBCC CHECKCATALOG (N'Musteri') WITH NO_INFOMSGS;\nDBCC CHECKTABLE (N'dbo.Siparis') WITH NO_INFOMSGS, ALL_ERRORMSGS;\n\n-- Get results as rows (error rows with object and page):\nDBCC CHECKDB (N'Musteri') WITH TABLERESULTS, NO_INFOMSGS, ALL_ERRORMSGS, MAXDOP = 4;","what":"Muestra la hora del último CHECKDB correcto de cada base de datos, estima el tempdb necesario, permite dividir la comprobación de bases de datos grandes en piezas CHECKALLOC + CHECKCATALOG + CHECKTABLE y devuelve los resultados como filas (objeto, índice, página).","when":"Al auditar el mantenimiento, para bases de datos muy grandes que no caben en la ventana de mantenimiento o al determinar el alcance de la corrupción tras 823/824.","caution":"La frase \"repair_allow_data_loss is the minimum repair level\" en el resultado significa que la reparación borrará datos; considere primero una restauración desde un backup bueno (o una restauración de páginas). Si solo están afectados índices nonclustered (index_id > 1), eliminar y recrear el índice lo resuelve sin pérdida de datos. Tras encontrar corrupción, investigue el subsistema de disco. PHYSICAL_ONLY omite las comprobaciones lógicas; sigue siendo necesario un CHECKDB completo periódicamente. Al dividir con CHECKTABLE, la cobertura es incompleta salvo que también se ejecuten CHECKALLOC/CHECKCATALOG.","note":"DATABASEPROPERTYEX(..., 'LastGoodCheckDbTime') está disponible en 2016 SP2, 2017 CU9 y 2019+; en builds anteriores lea dbi_dbccLastKnownGood de DBCC DBINFO WITH TABLERESULTS. Para descargar la carga, ejecutar CHECKDB sobre una copia restaurada desde backup (p. ej. en un servidor de pruebas) también valida la base de datos de producción. La opción MAXDOP es de 2014 SP2 / 2016+.","tags":["dbcc checkdb","lastgoodcheckdbtime","checktable","corruption","repair_allow_data_loss","integrity"]},{"id":"resource-governor","task":"Limitar la carga de informes con Resource Governor (pool, workload group, classifier)","category":"Configuración","command":"USE master;\n-- 1) Pool and workload group:\nCREATE RESOURCE POOL [pool_report]\nWITH (MIN_CPU_PERCENT = 0, MAX_CPU_PERCENT = 30, CAP_CPU_PERCENT = 40,\n      MIN_MEMORY_PERCENT = 0, MAX_MEMORY_PERCENT = 25);\nCREATE WORKLOAD GROUP [wg_report]\nWITH (IMPORTANCE = LOW, REQUEST_MAX_MEMORY_GRANT_PERCENT = 15,\n      MAX_DOP = 2, REQUEST_MAX_CPU_TIME_SEC = 600)\nUSING [pool_report];\nGO\n-- 2) Classifier function (in master, SCHEMABINDING required):\nCREATE FUNCTION dbo.fn_rg_classifier()\nRETURNS sysname\nWITH SCHEMABINDING\nAS\nBEGIN\n    DECLARE @grp sysname = N'default';\n    IF SUSER_SNAME() = N'rapor_user' OR APP_NAME() LIKE N'Power BI%'\n        SET @grp = N'wg_report';\n    RETURN @grp;\nEND;\nGO\n-- 3) Enable:\nALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = dbo.fn_rg_classifier);\nALTER RESOURCE GOVERNOR RECONFIGURE;\n\n-- 4) Which session landed in which group:\nSELECT s.session_id, s.login_name, s.program_name, wg.name AS workload_group, rp.name AS pool\nFROM   sys.dm_exec_sessions s\n  JOIN sys.dm_resource_governor_workload_groups wg ON wg.group_id = s.group_id\n  JOIN sys.dm_resource_governor_resource_pools rp  ON rp.pool_id = wg.pool_id\nWHERE  s.is_user_process = 1;\n\n-- Rollback:\n-- ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = NULL); ALTER RESOURCE GOVERNOR DISABLE;","what":"Dirige las sesiones de logins o aplicaciones concretas a un workload group separado en el momento de la conexión; protege la carga OLTP limitando la CPU, los memory grants, el DOP y el tiempo de CPU por petición de ese grupo.","when":"Cuando las consultas de informes/BI dejan sin recursos al OLTP, cuando el ETL se solapa con la carga del horario laboral o para repartir recursos en un servidor multi-tenant.","caution":"La función classifier se ejecuta en cada conexión nueva; una función lenta o que falla ralentiza o rompe todos los inicios de sesión (sin consultas ni acceso a tablas dentro). Si se comporta mal, conéctese mediante la DAC y establezca CLASSIFIER_FUNCTION = NULL. La clasificación solo ocurre al conectar; las sesiones existentes no se ven afectadas. CAP_CPU_PERCENT es un límite duro que se aplica incluso con la CPU ociosa. MAX_CPU_PERCENT solo se aplica con contención de CPU. Resource Governor es una característica de Enterprise/Developer.","note":"Superar REQUEST_MAX_CPU_TIME_SEC solo genera un evento por defecto (cpu_threshold_exceeded); con el trace flag 2422 (2016 SP2 / 2017 CU3+) la petición se aborta. Para límites de E/S use MAX_IOPS_PER_VOLUME en el pool (2014+). Estadísticas: sys.dm_resource_governor_resource_pools / _workload_groups.","tags":["resource governor","resource pool","workload group","classifier","cpu limit","resource management"]},{"id":"tde-enable","task":"Configurar TDE: master key, certificado, backup del certificado y progreso del cifrado","category":"Seguridad","command":"USE master;\nIF NOT EXISTS (SELECT 1 FROM sys.symmetric_keys WHERE name = N'##MS_DatabaseMasterKey##')\n    CREATE MASTER KEY ENCRYPTION BY PASSWORD = N'<GucluParola_1>';\nCREATE CERTIFICATE [TDE_Cert_2026]\n    WITH SUBJECT = N'TDE certificate', EXPIRY_DATE = '20361231';\n\n-- Back it up NOW: without cert + private key the backups cannot be restored anywhere\nBACKUP CERTIFICATE [TDE_Cert_2026]\n    TO FILE = N'D:\\Keys\\TDE_Cert_2026.cer'\n    WITH PRIVATE KEY (FILE = N'D:\\Keys\\TDE_Cert_2026.pvk',\n                      ENCRYPTION BY PASSWORD = N'<GucluParola_2>');\n\nUSE [Musteri];\nCREATE DATABASE ENCRYPTION KEY\n    WITH ALGORITHM = AES_256\n    ENCRYPTION BY SERVER CERTIFICATE [TDE_Cert_2026];\nALTER DATABASE [Musteri] SET ENCRYPTION ON;\n\n-- Progress and certificate backup date:\nSELECT DB_NAME(dek.database_id) AS db, dek.encryption_state_desc, dek.percent_complete,\n       dek.encryption_scan_state_desc, c.name AS cert_name, c.expiry_date, c.pvt_key_last_backup_date\nFROM   sys.dm_database_encryption_keys dek\n  LEFT JOIN master.sys.certificates c ON c.thumbprint = dek.encryptor_thumbprint;\n\n-- On the target server (before restore / AG secondary):\n-- CREATE CERTIFICATE [TDE_Cert_2026] FROM FILE = N'D:\\Keys\\TDE_Cert_2026.cer'\n--   WITH PRIVATE KEY (FILE = N'D:\\Keys\\TDE_Cert_2026.pvk', DECRYPTION BY PASSWORD = N'<GucluParola_2>');","what":"Crea una database master key y un certificado en master, respalda el certificado con su clave privada, crea una database encryption key AES-256 y cifra en reposo los archivos de datos y de log; muestra el progreso del escaneo de cifrado en segundo plano.","when":"Para requisitos de cifrado de datos en reposo como GDPR/PCI y contra el robo de archivos de backup y de archivos MDF/LDF.","caution":"Si se pierde el backup del certificado + clave privada o su contraseña, la base de datos TDE y todos sus backups quedan ilegibles de forma PERMANENTE; guarde el backup en una bóveda segura lejos del servidor. Habilitar TDE en cualquier base de datos cifra también tempdb (un pequeño costo de CPU en todo el servidor). El escaneo de cifrado es intensivo en E/S; en 2019+ se puede pausar con ALTER DATABASE ... SET ENCRYPTION SUSPEND/RESUME. Para AG/log shipping, el certificado debe instalarse primero en cada servidor de destino. TDE no cifra los datos en memoria ni en la red.","note":"La caducidad del certificado no detiene el TDE existente, pero para la rotación cree un nuevo certificado y vuelva a cifrar con ALTER DATABASE ENCRYPTION KEY ENCRYPTION BY SERVER CERTIFICATE [new] (solo se vuelve a cifrar la DEK, no los datos). encryption_state_desc y encryption_scan_state_desc son columnas de 2019+; en versiones anteriores use encryption_state (3 = cifrado, 2 = en curso). TDE está disponible en la edición Standard desde 2019.","tags":["tde","transparent data encryption","certificate","backup certificate","encryption","dek"]},{"id":"sql-server-audit","task":"Configurar SQL Server Audit: inicios de sesión, cambios de permisos y acceso a tablas","category":"Seguridad","command":"USE master;\nCREATE SERVER AUDIT [Audit_DBA]\nTO FILE (FILEPATH = N'D:\\Audit\\', MAXSIZE = 256 MB, MAX_ROLLOVER_FILES = 20, RESERVE_DISK_SPACE = OFF)\nWITH (QUEUE_DELAY = 1000, ON_FAILURE = CONTINUE);\nALTER SERVER AUDIT [Audit_DBA] WITH (STATE = ON);\n\n-- Server-wide (covers all databases):\nCREATE SERVER AUDIT SPECIFICATION [AuditSpec_Server]\nFOR SERVER AUDIT [Audit_DBA]\n    ADD (FAILED_LOGIN_GROUP),\n    ADD (SERVER_ROLE_MEMBER_CHANGE_GROUP),\n    ADD (DATABASE_ROLE_MEMBER_CHANGE_GROUP),\n    ADD (SERVER_PRINCIPAL_CHANGE_GROUP),\n    ADD (DATABASE_PRINCIPAL_CHANGE_GROUP),\n    ADD (SERVER_PERMISSION_CHANGE_GROUP),\n    ADD (DATABASE_PERMISSION_CHANGE_GROUP),\n    ADD (AUDIT_CHANGE_GROUP)\nWITH (STATE = ON);\n\n-- Database: access to a sensitive table:\nUSE [Musteri];\nCREATE DATABASE AUDIT SPECIFICATION [AuditSpec_Musteri]\nFOR SERVER AUDIT [Audit_DBA]\n    ADD (SELECT, INSERT, UPDATE, DELETE ON OBJECT::[dbo].[Siparis] BY [public])\nWITH (STATE = ON);\n\n-- Read:\nSELECT event_time, action_id, succeeded, server_principal_name, database_name,\n       schema_name, object_name, LEFT(statement, 200) AS stmt, client_ip, application_name\nFROM   sys.fn_get_audit_file(N'D:\\Audit\\Audit_DBA_*.sqlaudit', DEFAULT, DEFAULT)\nWHERE  event_time >= DATEADD(HOUR, -24, SYSUTCDATETIME())\nORDER BY event_time DESC;","what":"Crea un server audit que escribe a archivo; registra en todo el servidor los inicios de sesión fallidos, la pertenencia a roles y los cambios de principals y permisos, y a nivel de base de datos el acceso SELECT/DML a una tabla elegida; lee los registros con T-SQL.","when":"Para requisitos de auditoría/cumplimiento (GDPR, PCI, SOX), para preguntas de \"quién leyó/cambió esta tabla\" o cuando se sospecha una escalada de privilegios.","caution":"Auditar SELECT en una tabla con mucho uso genera registros muy rápido y cuesta rendimiento; acótelo por objeto y principal. ON_FAILURE = SHUTDOWN detiene el servidor cuando no se puede escribir la auditoría; úselo solo cuando sea obligatorio. La carpeta de auditoría debe tener permiso de escritura para la cuenta de servicio de SQL Server y no debe poder borrarla nadie que no sea DBA. event_time está en UTC. Una database audit specification se restaura con la base de datos, pero queda huérfana si el GUID del server audit no coincide.","note":"Las database audit specifications están disponibles en la edición Standard desde 2016 SP1. Incluya siempre AUDIT_CHANGE_GROUP para que se registren los cambios en la propia auditoría, incluida su desactivación.","tags":["sql server audit","server audit","audit specification","fn_get_audit_file","auditing","compliance"]},{"id":"orphaned-users-fix","task":"Encontrar usuarios huérfanos y volver a asociarlos a logins (tras restore / AG)","category":"Seguridad","command":"USE [Musteri];\n-- Database users without a matching login:\nSELECT dp.name AS user_name, dp.type_desc, dp.authentication_type_desc, dp.sid, dp.create_date\nFROM   sys.database_principals dp\n  LEFT JOIN sys.server_principals sp ON sp.sid = dp.sid\nWHERE  sp.sid IS NULL\n  AND  dp.type IN ('S', 'U', 'G')\n  AND  dp.authentication_type_desc IN (N'INSTANCE', N'WINDOWS')\n  AND  dp.principal_id > 4;\n\n-- If a login with the same name exists, remap the SID:\nALTER USER [app_user] WITH LOGIN = [app_user];\n\n-- Permanent fix: generate CREATE LOGIN with SID + password hash on the source, run it on the target:\nSELECT N'CREATE LOGIN ' + QUOTENAME(sp.name)\n     + N' WITH PASSWORD = ' + CONVERT(NVARCHAR(MAX), LOGINPROPERTY(sp.name, 'PasswordHash'), 1)\n     + N' HASHED, SID = ' + CONVERT(NVARCHAR(MAX), sp.sid, 1)\n     + N', CHECK_POLICY = OFF;' AS create_login_stmt\nFROM   sys.server_principals sp\nWHERE  sp.type = 'S' AND sp.name = N'app_user';","what":"Encuentra usuarios de base de datos cuyo SID no coincide con ningún login del servidor (huérfanos); vuelve a asociar un usuario a un login existente y genera un script para recrear los logins en otro servidor con el mismo SID y hash de contraseña.","when":"Tras restaurar/adjuntar una base de datos en otro servidor, cuando la aplicación recibe \"Login failed for user\" tras un failover de AG/log shipping o durante una migración de servidor.","caution":"ALTER USER ... WITH LOGIN cambia el SID del usuario al del login; en un AG este cambio fluye del primario a los secundarios, pero si los logins de los servidores secundarios tienen SID distintos el problema vuelve en el siguiente failover. La solución permanente es crear los logins con el mismo SID en todas las réplicas. El hash de contraseña es sensible; guarde el script de forma segura. Los usuarios de bases de datos contenidas (authentication_type = DATABASE) no necesitan login y no se listan.","note":"sp_change_users_login está obsoleto; use ALTER USER WITH LOGIN. Para un dbo huérfano (falta el login propietario de la base de datos) use ALTER AUTHORIZATION ON DATABASE::[Musteri] TO [sa];. Para migrar logins en bloque use PowerShell dbatools: Copy-DbaLogin -Source SQL01 -Destination SQL02.","tags":["orphaned user","alter user with login","sid","login failed","after restore","sp_change_users_login"]},{"id":"permission-audit","task":"Auditoría de logins y permisos: miembros de sysadmin, permisos de riesgo, contraseñas débiles","category":"Seguridad","command":"-- 1) Server role memberships:\nSELECT r.name AS server_role, m.name AS member, m.type_desc, m.is_disabled, m.create_date\nFROM   sys.server_role_members rm\n  JOIN sys.server_principals r ON r.principal_id = rm.role_principal_id\n  JOIN sys.server_principals m ON m.principal_id = rm.member_principal_id\nORDER BY r.name, m.name;\n\n-- 2) Risky server permissions:\nSELECT pr.name AS grantee, pr.type_desc, pe.permission_name, pe.state_desc\nFROM   sys.server_permissions pe\n  JOIN sys.server_principals pr ON pr.principal_id = pe.grantee_principal_id\nWHERE  pe.permission_name IN (N'CONTROL SERVER', N'IMPERSONATE ANY LOGIN', N'ALTER ANY LOGIN',\n                              N'ALTER ANY SERVER ROLE', N'ALTER SETTINGS', N'IMPERSONATE')\n   OR  pe.state_desc = N'GRANT_WITH_GRANT_OPTION';\n\n-- 3) SQL login health (policy, lockout, password = login name):\nSELECT sl.name, sl.is_disabled, sl.is_policy_checked, sl.is_expiration_checked,\n       LOGINPROPERTY(sl.name, 'PasswordLastSetTime') AS pwd_last_set,\n       LOGINPROPERTY(sl.name, 'IsLocked')            AS is_locked,\n       LOGINPROPERTY(sl.name, 'BadPasswordCount')    AS bad_pwd_count,\n       PWDCOMPARE(sl.name, sl.password_hash)         AS pwd_equals_name,\n       PWDCOMPARE(N'', sl.password_hash)             AS pwd_empty\nFROM   sys.sql_logins sl;\n\n-- 4) Database role memberships and explicit object permissions:\nUSE [Musteri];\nSELECT r.name AS db_role, m.name AS member, m.type_desc\nFROM   sys.database_role_members rm\n  JOIN sys.database_principals r ON r.principal_id = rm.role_principal_id\n  JOIN sys.database_principals m ON m.principal_id = rm.member_principal_id\nWHERE  m.name <> N'dbo'\nORDER BY r.name, m.name;\n\nSELECT pr.name AS grantee, pe.class_desc, pe.permission_name, pe.state_desc,\n       OBJECT_SCHEMA_NAME(pe.major_id) AS schema_name,\n       CASE pe.class WHEN 1 THEN OBJECT_NAME(pe.major_id) WHEN 3 THEN SCHEMA_NAME(pe.major_id) END AS object_name\nFROM   sys.database_permissions pe\n  JOIN sys.database_principals pr ON pr.principal_id = pe.grantee_principal_id\nWHERE  (pr.name NOT IN (N'public', N'dbo') AND NOT (pe.class = 0 AND pe.permission_name = N'CONNECT'))\n   OR  (pr.name = N'public' AND OBJECT_SCHEMA_NAME(pe.major_id) <> N'sys')\nORDER BY pr.name, pe.class_desc;","what":"Lista las pertenencias a roles de servidor, los permisos de riesgo como CONTROL SERVER/IMPERSONATE, los SQL logins con la política de contraseñas desactivada, bloqueados o con una contraseña igual al nombre del login o vacía; en la base de datos, las pertenencias a roles y los permisos explícitos sobre objetos que no son del sistema.","when":"Para revisiones periódicas de accesos, antes de una auditoría, después de que alguien deje el equipo o cuando se sospecha un incidente de seguridad.","caution":"Los permisos concedidos mediante grupos de Windows aparecen con el nombre del grupo; para ver las personas use EXEC xp_logininfo N'DOMAIN\\Group', 'members';. Verifique las dependencias de las aplicaciones antes de revocar nada. PWDCOMPARE solo funciona con SQL logins y requiere CONTROL SERVER. Consulta de solo lectura; segura.","note":"Para los permisos efectivos de un principal: EXECUTE AS USER = N'app_user'; SELECT * FROM fn_my_permissions(NULL, 'DATABASE'); REVERT;. En 2022 los nuevos roles granulares (##MS_ServerStateReader##, ##MS_DefinitionReader## etc.) conceden derechos de monitoreo sin sysadmin.","tags":["permission audit","sysadmin","server_role_members","database_permissions","pwdcompare","security"]},{"id":"ledger-tables","task":"Crear una tabla ledger, leer su historial de cambios y verificar la integridad","category":"Seguridad","command":"USE [Musteri];\n-- Ledger verification requires snapshot isolation:\nALTER DATABASE [Musteri] SET ALLOW_SNAPSHOT_ISOLATION ON;\n\n-- Updatable ledger table (history table maintained automatically):\nCREATE TABLE dbo.Hesap (\n    HesapId int           NOT NULL PRIMARY KEY,\n    Bakiye  decimal(18,2) NOT NULL\n)\nWITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Hesap_History), LEDGER = ON);\n\n-- Append-only ledger (UPDATE/DELETE are blocked):\nCREATE TABLE dbo.IslemLog (Id bigint IDENTITY, Mesaj nvarchar(400))\nWITH (LEDGER = ON (APPEND_ONLY = ON));\n\n-- Who changed what, and when:\nSELECT t.commit_time, t.principal_name, l.HesapId, l.Bakiye, l.ledger_operation_type_desc\nFROM   dbo.Hesap_Ledger l\n  JOIN sys.database_ledger_transactions t ON t.transaction_id = l.ledger_transaction_id\nORDER BY t.commit_time DESC;\n\n-- Take a digest (store it somewhere trusted outside) and verify:\nEXEC sys.sp_generate_database_ledger_digest;\nEXEC sys.sp_verify_database_ledger\n     N'{\"database_name\":\"Musteri\",\"block_id\":0,\"hash\":\"0x...\",\"last_transaction_commit_time\":\"...\",\"digest_time\":\"...\"}';","what":"Crea una tabla ledger que registra los cambios de filas en una cadena de hashes criptográficos; la vista _Ledger muestra quién hizo cada INSERT/UPDATE/DELETE y cuándo, y la verificación contra un digest almacenado demuestra que los datos no se alteraron después (tampoco por los DBA).","when":"Para datos cuya inmutabilidad debe poder demostrarse: registros financieros, pistas de auditoría, registros de contratos/aprobaciones.","caution":"Ledger no se puede desactivar una vez creada la tabla; una tabla ledger no se puede convertir en tabla normal, y una tabla ledger eliminada se conserva como dropped ledger table. El ledger actualizable escribe una fila de historial en cada cambio y el almacenamiento crece. Salvo que los digests se guarden fuera de la base de datos en almacenamiento inmutable (WORM, Azure immutable blob), la verificación no tiene valor probatorio. sp_verify_database_ledger falla con el error 37498 sin snapshot isolation.","since":"2022","note":"sp_generate_database_ledger_digest devuelve JSON; sp_verify_database_ledger acepta un único digest o un array JSON y puede verificar una sola tabla con @table_name. Para el almacenamiento automático de digests: ALTER DATABASE SCOPED CONFIGURATION SET LEDGER_DIGEST_STORAGE_ENDPOINT = '<blob url>'. CREATE DATABASE ... WITH LEDGER = ON hace que todas las tablas sean ledger por defecto.","tags":["ledger","append-only","sp_verify_database_ledger","digest","immutable","tamper-evident"]},{"id":"agent-job-failures","task":"Historial de fallos de jobs de SQL Agent y estado de la última ejecución","category":"SQL Agent","command":"-- Job steps that failed / were cancelled in the last 24 hours:\nSELECT j.name AS job_name, h.step_id, h.step_name,\n       msdb.dbo.agent_datetime(h.run_date, h.run_time) AS run_at,\n       STUFF(STUFF(RIGHT('000000' + CAST(h.run_duration AS VARCHAR(6)), 6), 3, 0, ':'), 6, 0, ':') AS duration_hhmmss,\n       CASE h.run_status WHEN 0 THEN 'Failed' WHEN 1 THEN 'Succeeded' WHEN 2 THEN 'Retry'\n                         WHEN 3 THEN 'Canceled' WHEN 4 THEN 'In progress' END AS status,\n       h.sql_message_id, h.sql_severity,\n       LEFT(h.message, 500) AS message\nFROM   msdb.dbo.sysjobhistory h\n  JOIN msdb.dbo.sysjobs j ON j.job_id = h.job_id\nWHERE  h.run_status IN (0, 3)\n  AND  msdb.dbo.agent_datetime(h.run_date, h.run_time) >= DATEADD(HOUR, -24, GETDATE())\nORDER BY run_at DESC;\n\n-- Each job's last outcome, running now, next run:\nSELECT j.name, j.enabled,\n       CASE lr.run_status WHEN 0 THEN 'Failed' WHEN 1 THEN 'Succeeded' WHEN 3 THEN 'Canceled' END AS last_outcome,\n       lr.run_at AS last_run_at,\n       CASE WHEN a.start_execution_date IS NOT NULL AND a.stop_execution_date IS NULL THEN 'RUNNING' END AS running_now,\n       a.next_scheduled_run_date\nFROM   msdb.dbo.sysjobs j\n  LEFT JOIN msdb.dbo.sysjobactivity a\n         ON a.job_id = j.job_id AND a.session_id = (SELECT MAX(session_id) FROM msdb.dbo.syssessions)\n  OUTER APPLY (SELECT TOP (1) h.run_status, msdb.dbo.agent_datetime(h.run_date, h.run_time) AS run_at\n               FROM msdb.dbo.sysjobhistory h\n               WHERE h.job_id = j.job_id AND h.step_id = 0\n               ORDER BY h.instance_id DESC) lr\nORDER BY j.name;\n\n-- Is the Agent service running:\nSELECT servicename, status_desc, startup_type_desc FROM sys.dm_server_services;","what":"Muestra los pasos de jobs que fallaron o se cancelaron en las últimas 24 horas con su mensaje de error; el último resultado de cada job, si se está ejecutando ahora y su próxima ejecución programada; y el estado del servicio Agent.","when":"En la revisión matutina, ante una alerta de un job de backup/mantenimiento, para \"¿se ejecutó el job nocturno?\" o tras un failover para verificar que los jobs se ejecutan en la réplica correcta.","caution":"El historial de jobs en msdb es limitado (por defecto 1,000 filas en total, 100 por job); los jobs que se ejecutan con frecuencia desplazan rápidamente los fallos antiguos, así que aumente el límite en las propiedades de Agent. La columna message se trunca a 4,000 caracteres; defina un archivo de salida en el paso del job para salidas largas. Si el servicio Agent está detenido, no se ejecuta ningún job programado y ninguno reporta error. Consulta de solo lectura; segura.","note":"msdb.dbo.agent_datetime no está documentado pero se usa mucho. En un AG, los jobs deben existir en cada réplica y comprobar el rol en el primer paso con sys.fn_hadr_is_primary_replica(N'Musteri'). dbatools: Get-DbaAgentJobHistory -SqlInstance SQL01 -StartDate (Get-Date).AddDays(-1) -OutcomeType Failed.","tags":["sql agent","job history","sysjobhistory","job failure","msdb","scheduled job"]}]},"recipes/mongodb.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"recipes":[{"id":"rs-add-member","task":"Añadir un miembro al replica set","category":"Replica Set","command":"rs.add({ host: \"mongo2:27017\", priority: 1, votes: 1 })","what":"Añade un nuevo miembro (secundario) al replica set existente e inicia una sincronización inicial.","when":"Al añadir un nuevo nodo para aumentar la capacidad de lectura o por redundancia.","caution":"El nuevo miembro extrae los datos del primario durante la sincronización inicial; con conjuntos de datos grandes esto genera carga de red y de disco.","tags":["replica","rs.add","member","member","add"]},{"id":"rs-remove-member","task":"Quitar un miembro del replica set","category":"Replica Set","command":"rs.remove(\"mongo2:27017\")","what":"Quita el host indicado de la configuración del replica set.","when":"Al retirar un nodo del servicio o al sustituir hardware.","caution":"No quite el primario; quitar miembros de forma que se rompa la mayoría puede dejar el conjunto sin disponibilidad para lecturas y escrituras.","tags":["replica","rs.remove","member","member","remove"]},{"id":"rs-step-down","task":"Degradar el primario (stepDown)","category":"Replica Set","command":"rs.stepDown(60)","what":"Degrada el primario actual a secundario durante el número de segundos indicado y provoca una nueva elección.","when":"Para pasar el rol de primario a otro nodo antes de un mantenimiento planificado.","caution":"Durante el stepDown se produce una breve interrupción de escrituras; la aplicación debe reconectarse.","tags":["replica","stepdown","primary","failover","step down"]},{"id":"rs-set-priority","task":"Cambiar la prioridad de un miembro","category":"Replica Set","command":"cfg = rs.conf(); cfg.members[1].priority = 2; rs.reconfig(cfg)","what":"Cambia la prioridad del miembro indicado para convertirse en primario en las elecciones.","when":"Cuando quiere que un nodo concreto sea el primario preferido.","caution":"reconfig puede provocar una elección y causar un breve cambio de primario/interrupción de escrituras.","tags":["replica","priority","reconfig","priority","election"]},{"id":"rs-status","task":"Ver el estado del replica set","category":"Replica Set","command":"rs.status()","what":"Muestra el estado, los roles, el retraso de replicación y la salud de los miembros del conjunto.","when":"Al comprobar la salud y el retraso de la replicación.","caution":"Comando de solo lectura; sin riesgo.","tags":["replica","status","status","health","rs.status"]},{"id":"rs-hidden-member","task":"Configurar un miembro oculto / sin voto","category":"Replica Set","command":"cfg = rs.conf(); cfg.members[2].hidden = true; cfg.members[2].priority = 0; cfg.members[2].votes = 0; rs.reconfig(cfg)","what":"Hace que un miembro sea oculto (invisible para la aplicación), sin voto y con prioridad 0, usándolo solo para backup/analítica.","when":"Cuando quiere aislar los backups o las consultas analíticas.","caution":"Un miembro oculto debe tener prioridad 0; de lo contrario reconfig falla.","tags":["replica","hidden","votes","hidden","non-voting"]},{"id":"rs-resync","task":"Resincronizar un miembro desde cero","category":"Replica Set","command":"// On the SECONDARY to be resynced (NEVER on the PRIMARY):\ndb.getSiblingDB(\"admin\").shutdownServer()\n// At the OS level: empty the dbPath contents (or move them elsewhere as a backup)\n// and restart mongod with the same configuration; the member starts an initial sync automatically.\n// Monitor progress (stateStr: STARTUP2 -> SECONDARY):\nrs.status()","what":"MongoDB no tiene un comando resync aparte; detener el secundario, vaciar su directorio de datos y reiniciarlo inicia una nueva sincronización inicial desde el primario (o una sync source).","when":"Cuando un miembro se retrasa tanto que queda fuera de la ventana del oplog (RECOVERING / too stale) o sus datos se vuelven inconsistentes.","caution":"Todos los datos locales del miembro se borran y se vuelven a extraer; hágalo solo en un secundario y asegúrese de que la mayoría sigue activa. El tiempo de sincronización y la carga de red/disco son altos; para conjuntos de datos muy grandes, sembrar desde un snapshot del sistema de archivos de un miembro sano es más rápido.","tags":["replica","resync","initialsync","sync","resync"]},{"id":"sh-add-shard","task":"Añadir un shard al clúster","category":"Sharding","command":"sh.addShard(\"shardRS/mongoShard1:27018\")","what":"Añade un nuevo shard (replica set) al clúster shardeado.","when":"Al escalar horizontalmente la capacidad de almacenamiento o de throughput.","caution":"Tras añadir un shard, el balancer empieza a migrar chunks; esto genera carga de IO adicional.","tags":["sharding","addshard","shard","add","cluster"]},{"id":"sh-shard-collection","task":"Aplicar sharding a una colección","category":"Sharding","command":"sh.shardCollection(\"shop.customer\", { customerId: \"hashed\" })","what":"Shardea la colección indicada sobre la shard key dada.","when":"Cuando quiere distribuir una colección en crecimiento entre varios shards.","caution":"La shard key puede cambiarse con reshardCollection en 5.0+ (y ampliarse con refineCollectionShardKey en 4.4+), pero es costoso porque redistribuye todos los datos; una mala elección de clave produce una distribución desigual.","tags":["sharding","shardcollection","key","customer","shard key"]},{"id":"sh-start-balancer","task":"Iniciar el balancer","category":"Sharding","command":"sh.startBalancer()","what":"Habilita el proceso balancer que distribuye los chunks.","when":"Cuando quiere volver a habilitar el balanceo tras un mantenimiento.","caution":"El balancer genera carga de IO y de red al migrar chunks; defina una ventana de balanceo fuera de las horas pico.","tags":["sharding","balancer","start","start","rebalance"]},{"id":"sh-stop-balancer","task":"Detener el balancer","category":"Sharding","command":"sh.stopBalancer()","what":"Deshabilita el balancer que realiza las migraciones de chunks.","when":"Para detener las migraciones antes de un backup, un mantenimiento o una carga masiva de datos.","caution":"Si se queda apagado mucho tiempo, los shards se desequilibran; no olvide volver a habilitarlo después.","tags":["sharding","balancer","stop","stop","maintenance"]},{"id":"sh-status","task":"Ver el estado del sharding","category":"Sharding","command":"sh.status()","what":"Resume los shards, las bases de datos, las colecciones shardeadas y la distribución de chunks.","when":"Al examinar el estado de distribución del clúster y la salud del balancer.","caution":"Comando de solo lectura; sin riesgo.","tags":["sharding","status","status","chunk","sh.status"]},{"id":"sh-add-zone","task":"Añadir una zona (tag) y asignar un rango","category":"Sharding","command":"sh.addShardToZone(\"shard0000\", \"TR\"); sh.updateZoneKeyRange(\"shop.customer\", { region: \"TR\" }, { region: \"TR~\" }, \"TR\")","what":"Asigna un shard a una zona y vincula un rango de claves concreto a esa zona (zone sharding).","when":"Cuando quiere mantener los datos en shards concretos por geografía o hardware.","caution":"Los rangos de zona deben corresponder a la shard key; un rango erróneo impide migrar los chunks.","tags":["sharding","zone","tag","region","range"]},{"id":"sh-move-chunk","task":"Mover un chunk manualmente","category":"Sharding","command":"sh.moveChunk(\"shop.customer\", { customerId: 1000 }, \"shard0001\")","what":"Mueve manualmente el chunk que contiene la clave indicada al shard de destino.","when":"Para corregir una distribución desigual o ubicar datos concretos.","caution":"Un movimiento manual puede entrar en conflicto con el balancer; las migraciones de chunks grandes generan carga de IO y bloqueos breves.","tags":["sharding","movechunk","chunk","move","manual"]},{"id":"compact-collection","task":"Compactar una colección","category":"Mantenimiento","command":"db.runCommand({ compact: \"customer\" })","what":"Reescribe en disco la colección y sus índices, recuperando el espacio libre.","when":"Cuando quiere recuperar espacio en disco tras muchos borrados/actualizaciones.","caution":"En 4.4+, compact no bloquea lecturas/escrituras; solo bloquea ciertas operaciones DDL sobre la misma colección (drop, createIndex, dropIndex, etc.); aun así genera mucho IO y no siempre devuelve el espacio al sistema operativo. Ejecútelo en un secundario y en una ventana de mantenimiento cuando sea posible.","tags":["compact","storage","maintenance","disk","storage"]},{"id":"create-index-background","task":"Crear un índice (no bloqueante)","category":"Mantenimiento","command":"db.customer.createIndex({ email: 1 }, { name: \"idx_email\" })","what":"Crea un índice sobre el campo indicado; desde MongoDB 4.2, la creación de índices no bloquea las escrituras por defecto.","when":"Al añadir un nuevo índice para mejorar el rendimiento de las consultas.","caution":"En colecciones grandes, la creación de índices consume CPU e IO; la carga en el primario puede aumentar.","since":"MongoDB 4.2","note":"MongoDB 4.2 introdujo un nuevo proceso de creación de índices, y la creación de índices ya no bloquea las escrituras por defecto; por eso la antigua opción { background: true } es innecesaria y se ignora a partir de 4.2.","tags":["index","createindex","background","create","performance"]},{"id":"collection-stats","task":"Ver el tamaño de índices y colecciones","category":"Mantenimiento","command":"db.customer.stats({ indexDetails: true })","what":"Informa del tamaño de la colección, el tamaño de almacenamiento y el tamaño de cada índice.","when":"Al analizar el uso de almacenamiento y el costo de los índices.","caution":"Comando de solo lectura; sin riesgo.","tags":["stats","index","size","collection","storage"]},{"id":"log-rotate","task":"Rotar el archivo de log","category":"Mantenimiento","command":"db.adminCommand({ logRotate: 1 })","what":"Cierra el archivo de log actual y abre uno nuevo (rotación de logs).","when":"Cuando el archivo de log ha crecido o antes de archivarlo.","caution":"Si se usa junto con la rotación de logs del SO, revise la configuración para evitar conflictos.","tags":["log","logrotate","rotate","maintenance","archive"]},{"id":"current-op-long","task":"Encontrar operaciones de larga duración","category":"Mantenimiento","command":"db.currentOp({ \"active\": true, \"secs_running\": { $gt: 5 } })","what":"Lista las operaciones activas que llevan más de 5 segundos en ejecución.","when":"Al identificar consultas problemáticas durante una lentitud o un bloqueo.","caution":"Comando de solo lectura; sin riesgo.","tags":["currentop","long","long-running","operation","diagnostics"]},{"id":"kill-op","task":"Matar una operación","category":"Mantenimiento","command":"db.killOp(12345)","what":"Termina la operación en ejecución con el opid indicado.","when":"Cuando una consulta bloquea el sistema o se ejecuta durante demasiado tiempo.","caution":"Matar una operación de escritura puede dejar cambios parciales; evite matar operaciones del sistema/de replicación.","tags":["killop","kill","operation","kill","diagnostics"]},{"id":"set-parameter","task":"Cambiar un parámetro en tiempo de ejecución","category":"Parámetros","command":"db.adminCommand({ setParameter: 1, cursorTimeoutMillis: 600000 })","what":"Cambia un parámetro en tiempo de ejecución sin reiniciar el servidor.","when":"Cuando quiere ajustar temporalmente un comportamiento del servidor.","caution":"El cambio no es persistente; al reiniciar vuelve al valor del archivo de configuración. Un valor erróneo puede degradar el rendimiento.","tags":["setparameter","parameter","runtime","setting","admin"]},{"id":"set-profiling","task":"Fijar el nivel de profiling","category":"Parámetros","command":"db.setProfilingLevel(1, { slowms: 100 })","what":"Registra las consultas lentas (más de 100 ms) en la colección system.profile (nivel 1).","when":"Cuando quiere diagnosticar consultas lentas.","caution":"El nivel 2 registra todas las operaciones y añade una sobrecarga considerable; los datos de profiling ocupan espacio en disco.","tags":["profiling","slowms","profiler","slow","diagnostics"]}]},"recipes/mongodb-ext.json":{"engineId":"mongodb","engine":"MongoDB","order":2,"recipes":[{"id":"rs-oplog-window","task":"Medir la ventana del oplog y el retraso de replicación","category":"Replica Set","command":"// Oplog size and the time span it covers (oplog window):\nrs.printReplicationInfo()\n// Lag of each secondary relative to the primary:\nrs.printSecondaryReplicationInfo()\n// Lag in seconds and sync source, for monitoring/scripts:\nconst s = rs.status(); const p = s.members.find(m => m.stateStr === \"PRIMARY\");\ns.members.filter(m => m.stateStr === \"SECONDARY\").map(m => ({ host: m.name, lagSec: (p.optimeDate - m.optimeDate) / 1000, syncSource: m.syncSourceHost }))\n// Window (hours):\ndb.getReplicationInfo().timeDiffHours","what":"Muestra cuántas horas de escrituras contiene el oplog (ventana del oplog) y cuántos segundos va retrasado cada secundario respecto al primario, junto con su sync source.","when":"Ante una alerta de retraso de replicación, antes de un mantenimiento planificado para calcular cuánto tiempo puede estar caído un miembro y tras jobs de escritura masiva.","caution":"Si un miembro está caído más tiempo que la ventana del oplog, pasa a 'too stale' y necesita una sincronización inicial. Los jobs masivos de update/delete pueden reducir la ventana en cuestión de horas; juzgue la ventana por su valor con la carga pico. El cálculo del retraso se basa en la diferencia de optimeDate; si el primario no recibe escrituras, el retraso no parece alto, pero la ventana tampoco se reduce.","since":"MongoDB 4.4","note":"rs.printSlaveReplicationInfo() pasó a llamarse rs.printSecondaryReplicationInfo() en 4.4. Si la ventana es demasiado pequeña, haga crecer el oplog con replSetResizeOplog (vea rs-oplog-resize). Los miembros con retraso configurado aparecen retrasados en secondaryDelaySecs (slaveDelay en 4.4); exclúyalos de las alertas de retraso.","tags":["oplog","oplog window","replication lag","delay","secondary","printReplicationInfo"],"untested":true},{"id":"rs-oplog-resize","task":"Redimensionar el oplog en línea (replSetResizeOplog)","category":"Replica Set","command":"// Current oplog size (MB):\ndb.getSiblingDB(\"local\").oplog.rs.stats().maxSize / 1024 / 1024\n// Resize the oplog to 50 GB (in MB; run on each member separately, secondaries first):\ndb.adminCommand({ replSetResizeOplog: 1, size: 51200 })\n// Minimum retention period (hours), independent of size:\ndb.adminCommand({ replSetResizeOplog: 1, minRetentionHours: 48 })\n// Verify the result:\nrs.printReplicationInfo()","what":"Hace crecer o reduce el oplog de un miembro en ejecución sin reinicio; minRetentionHours conserva las entradas del oplog durante un periodo mínimo aunque se supere el límite de tamaño.","when":"Cuando la ventana del oplog no cubre la duración del mantenimiento o de la sincronización inicial, o los jobs de escritura masiva reducen la ventana.","caution":"El comando solo afecta al miembro en el que se ejecuta y no se replica; aplíquelo en cada miembro. La ventana también debe ser suficiente en los miembros que actúan como sync sources. Reducirlo no devuelve el espacio en disco al SO de inmediato; para eso hace falta compact sobre oplog.rs. Con minRetentionHours el oplog puede crecer por encima de size según el volumen de escritura; planifique el disco en consecuencia.","since":"MongoDB 4.4","note":"El nuevo tamaño persiste tras los reinicios; minRetentionHours fijado por el comando no, así que fije también storage.oplogMinRetentionHours para conservarlo. size debe ser de al menos 990 MB. Cambiar replication.oplogSizeMB en la configuración solo tiene efecto cuando el oplog se crea por primera vez.","tags":["oplog","resize","replSetResizeOplog","minRetentionHours","size","oplog window"],"untested":true},{"id":"rs-force-reconfig","task":"Forzar una reconfiguración del replica set tras perder la mayoría (force reconfig)","category":"Replica Set","command":"// On a surviving member with the most recent optime (runs as a secondary):\nrs.status().members.map(m => ({ host: m.name, state: m.stateStr, health: m.health, optime: m.optimeDate }))\ncfg = rs.conf()\ncfg.members.map((m, i) => ({ i: i, host: m.host }))\n// Keep only the reachable members (example: members 0 and 2):\ncfg.members = [cfg.members[0], cfg.members[2]]\nrs.reconfig(cfg, { force: true })\n// Verify the new primary election:\nrs.status().members.map(m => ({ host: m.name, state: m.stateStr }))","what":"Cuando la mayoría de los miembros con voto se ha perdido de forma permanente (p. ej. ha desaparecido un centro de datos entero), construye una nueva configuración con los miembros supervivientes para que el conjunto pueda volver a elegir un primario.","when":"Solo en un escenario de desastre: la mayoría no volverá y el conjunto está atascado en solo lectura.","caution":"Es el último recurso. Si los miembros perdidos vuelven después con la configuración antigua, pueden producirse split-brain y rollback; aíslelos de la red y, al reincorporarlos, borre sus datos y añádalos con una sincronización inicial. Elija el miembro con el optime más reciente; de lo contrario, las escrituras no confirmadas por mayoría en otros miembros caen en rollback. No use force para cambios rutinarios de pertenencia.","since":"MongoDB 4.4","note":"force reconfig incrementa la versión de la configuración en un valor aleatorio grande y es la única forma de reconfig que puede ejecutarse en un secundario. En 4.4+ los reconfig normales pueden añadir o quitar como máximo un miembro con voto a la vez; force no está sujeto a este límite.","tags":["force reconfig","majority","disaster","recovery","split-brain","rs.reconfig"],"untested":true},{"id":"rs-rolling-maintenance","task":"Mantenimiento rolling en un replica set (reinicio, parche del SO, actualización menor)","category":"Replica Set","command":"// 1) Before starting: all members healthy, low lag, and an oplog window longer than the maintenance\nrs.printSecondaryReplicationInfo()\n// 2) Clean shutdown on the SECONDARY under maintenance (then OS/binary work, restart with the same config):\ndb.getSiblingDB(\"admin\").shutdownServer({ timeoutSecs: 60 })\n// 3) Wait until the member is SECONDARY and lag has closed, then move to the next secondary:\nrs.status().members.map(m => ({ host: m.name, state: m.stateStr, health: m.health }))\n// 4) When all secondaries are done, on the PRIMARY: hand over to a secondary that catches up within 30 s\nrs.stepDown(120, 30)\n// 5) The old primary is now a SECONDARY: apply steps 2 and 3 to it","what":"Indica el orden para poner los miembros en mantenimiento de uno en uno, primero los secundarios y el primario al final, de modo que los reinicios, parches del SO o actualizaciones de binarios se hagan sin sacar el conjunto de servicio.","when":"Durante actualizaciones planificadas del SO/kernel, actualizaciones de versión menor, cambios de parámetros de mongod o mantenimiento de disco/hardware.","caution":"Nunca apague más de un miembro a la vez; en un conjunto de 3 miembros, perder dos supone perder la mayoría. Después de cada miembro, confirme el estado SECONDARY y que el retraso se ha cerrado. stepDown provoca una breve interrupción de escrituras; asegúrese de que los drivers se reconectan con retryable writes. Si hay un árbitro, trátelo en la misma secuencia.","since":"MongoDB 4.4","note":"rs.stepDown(stepDownSecs, secondaryCatchUpPeriodSecs): el segundo parámetro es cuánto espera el primario a que un secundario se ponga al día; si ninguno lo hace, stepDown falla y el primario se mantiene. Para evitar que un miembro concreto se convierta en primario durante el mantenimiento, ejecute rs.freeze(seconds) en él.","tags":["rolling","maintenance","restart","stepDown","upgrade","patching"],"untested":true},{"id":"sh-balancer-window","task":"Estado del balancer, ventana activa y balanceo por colección","category":"Sharding","command":"// Is the balancer enabled, is it running a round right now:\nsh.getBalancerState()\ndb.adminCommand({ balancerStatus: 1 })\n// Run the balancer only between 01:00 and 05:00 (on mongos):\ndb.getSiblingDB(\"config\").settings.updateOne({ _id: \"balancer\" }, { $set: { activeWindow: { start: \"01:00\", stop: \"05:00\" } } }, { upsert: true })\n// Remove the window:\ndb.getSiblingDB(\"config\").settings.updateOne({ _id: \"balancer\" }, { $unset: { activeWindow: true } })\n// Disable / enable balancing for a single collection:\nsh.disableBalancing(\"shop.musteri\")\nsh.enableBalancing(\"shop.musteri\")\n// Whether the collection is balanced (4.4+):\ndb.adminCommand({ balancerCollectionStatus: \"shop.musteri\" })","what":"Muestra si el balancer está habilitado y ejecutando una ronda, restringe las migraciones de chunks a una ventana de tiempo y deshabilita o habilita el balanceo para una sola colección.","when":"Cuando las migraciones de chunks perjudican el rendimiento diurno, durante la ventana de backup o al aislar un problema de migración en una colección.","caution":"Una ventana demasiado estrecha no puede seguir el ritmo del crecimiento de datos y los shards se desequilibran; compruebe el equilibrio regularmente con balancerCollectionStatus. Una migración iniciada antes de que se cierre la ventana se ejecuta hasta terminar. Si se deja deshabilitado el balanceo por colección, esa colección sigue creciendo en un único shard.","since":"MongoDB 6.0","note":"Las horas de activeWindow se evalúan en la zona horaria local del primario del replica set de config servers. En 6.0.3+ el balancer decide según la diferencia de tamaño de datos entre shards, no según el número de chunks; el número de chunks en sh.status() puede parecer desigual mientras los datos están equilibrados.","tags":["balancer","activeWindow","window","migration","sharding","balancerCollectionStatus"],"untested":true},{"id":"sh-jumbo-chunks","task":"Encontrar jumbo chunks, dividirlos y borrar la marca","category":"Sharding","command":"// Chunks flagged as jumbo (5.0+ config.chunks links to the collection by uuid):\nconst uuid = db.getSiblingDB(\"config\").collections.findOne({ _id: \"shop.musteri\" }).uuid;\ndb.getSiblingDB(\"config\").chunks.find({ uuid: uuid, jumbo: true }, { min: 1, max: 1, shard: 1 })\n// If the chunk can be split, split it at the median:\nsh.splitFind(\"shop.musteri\", { musteriId: 1000 })\n// Last resort for an indivisible chunk, clear the flag manually (a successful split already clears it):\ndb.adminCommand({ clearJumboFlag: \"shop.musteri\", find: { musteriId: 1000 } })\n// If a single key value is too large, refine the key (4.4+):\ndb.adminCommand({ refineCollectionShardKey: \"shop.musteri\", key: { musteriId: 1, siparisId: 1 } })","what":"Lista los chunks marcados como jumbo porque el balancer no pudo moverlos, divide los que pueden dividirse y borra la marca jumbo; muestra cómo refinar la shard key para chunks enormes de un solo valor.","when":"Cuando un shard sigue siendo claramente mayor que los demás, aparece jumbo en la salida de sh.status() o el balancer reporta errores de migración.","caution":"Un chunk cuyos documentos comparten todos el mismo valor de shard key no puede dividirse; la única solución duradera es cambiar la shard key con refineCollectionShardKey o reshardCollection. refineCollectionShardKey requiere que exista de antemano un índice que soporte la nueva clave. Borrar la marca jumbo sin dividir hace que el balancer reintente el mismo chunk y vuelva a fallar.","since":"MongoDB 5.0","note":"Las shard keys de baja cardinalidad o concentradas en un valor (p. ej. status, country) son la causa más común de jumbo chunks. Antes de 5.0, los documentos de config.chunks se consultan por el campo ns. clearJumboFlag llegó en 4.2.3/4.0.15.","tags":["jumbo","chunk","split","clearJumboFlag","shard key","refineCollectionShardKey"],"untested":true},{"id":"sh-shard-key-check","task":"Comprobar la elección de shard key y la distribución de datos (getShardDistribution, analyzeShardKey)","category":"Sharding","command":"// Data and document distribution of the collection across shards:\ndb.getSiblingDB(\"shop\").musteri.getShardDistribution()\n// Cardinality, frequency and monotonicity analysis of the current or candidate shard key:\ndb.adminCommand({ analyzeShardKey: \"shop.musteri\", key: { musteriId: 1 }, keyCharacteristics: true, readWriteDistribution: false })\n// For read/write distribution, enable query sampling first (10 samples per second):\ndb.adminCommand({ configureQueryAnalyzer: \"shop.musteri\", mode: \"full\", samplesPerSecond: 10 })\ndb.adminCommand({ analyzeShardKey: \"shop.musteri\", key: { musteriId: 1 }, keyCharacteristics: false, readWriteDistribution: true })\n// Turn sampling off when done:\ndb.adminCommand({ configureQueryAnalyzer: \"shop.musteri\", mode: \"off\" })","what":"Muestra el porcentaje de los datos de una colección en cada shard; analyzeShardKey informa de la cardinalidad, los valores más frecuentes y la monotonía de la shard key actual o candidata, y de si las consultas muestreadas van a un solo shard o a todos (scatter-gather).","when":"Antes de aplicar sharding a una colección nueva, cuando se detecta un shard caliente o un crecimiento desigual y antes de decidir un reshardCollection.","caution":"analyzeShardKey necesita un índice que soporte la clave y muestrea documentos en colecciones grandes; añade carga de lectura en horas pico. Mientras el muestreo de consultas está activo, las muestras se escriben en config.sampledQueries; desactívelo al terminar el análisis. Una clave monótonamente creciente (ObjectId, timestamp) concentra todas las inserciones en un shard.","since":"MongoDB 7.0","note":"analyzeShardKey y configureQueryAnalyzer llegaron en 7.0 y también funcionan en replica sets. Una buena shard key tiene alta cardinalidad, baja frecuencia, no es monótona y aparece en el filtro de las consultas frecuentes. Una clave hashed arregla la distribución, pero convierte las consultas por rango en scatter-gather.","tags":["shard key","analyzeShardKey","getShardDistribution","cardinality","distribution","hot shard"],"untested":true},{"id":"explain-execstats","task":"Inspeccionar un plan de consulta con estadísticas de ejecución reales (explain executionStats)","category":"Rendimiento","command":"db.musteri.find({ sehir: \"Ankara\", durum: \"aktif\" }).sort({ kayitTarihi: -1 }).explain(\"executionStats\")\n// Summary: winning plan, returned documents, examined keys/documents and time\nconst e = db.musteri.find({ sehir: \"Ankara\", durum: \"aktif\" }).sort({ kayitTarihi: -1 }).explain(\"executionStats\");\n({ plan: e.queryPlanner.winningPlan, n: e.executionStats.nReturned, keys: e.executionStats.totalKeysExamined, docs: e.executionStats.totalDocsExamined, ms: e.executionStats.executionTimeMillis })\n// For an aggregation:\ndb.musteri.explain(\"executionStats\").aggregate([{ $match: { sehir: \"Ankara\" } }, { $group: { _id: \"$durum\", n: { $sum: 1 } } }])","what":"Ejecuta la consulta y muestra el plan ganador (IXSCAN/COLLSCAN, etapa SORT), el número de documentos devueltos, las claves de índice y documentos examinados y el tiempo empleado.","when":"Al analizar una consulta que aparece lenta en el profiler o en el log, y al verificar que un índice nuevo se usa realmente.","caution":"executionStats ejecuta realmente la consulta; hacer explain de una consulta pesada en horas pico también añade carga. Para comandos de escritura (update/delete/findAndModify), explain no modifica datos pero sí ejecuta el plan. Una relación totalDocsExamined/nReturned alta o una etapa SORT en memoria en el plan indica que el índice no es adecuado.","since":"MongoDB 4.4","note":"Aplique la regla ESR al diseñar índices compuestos: primero los campos de Igualdad (Equality), luego Ordenación (Sort) y luego Rango (Range). Use explain(\"allPlansExecution\") para ver también los planes descartados. En 7.0+, cuando una consulta se ejecuta en el Slot-Based Engine, winningPlan contiene los campos queryPlan y slotBasedPlan.","tags":["explain","executionStats","IXSCAN","COLLSCAN","plan","index"]},{"id":"profiler-analyze","task":"Extraer las consultas y query shapes más lentas de los datos del profiler","category":"Rendimiento","command":"// Recent slow operations (in the db where profiling is on; see set-profiling):\ndb.system.profile.find({ millis: { $gte: 100 } }, { ts: 1, ns: 1, op: 1, millis: 1, planSummary: 1, keysExamined: 1, docsExamined: 1, nreturned: 1, appName: 1 }).sort({ ts: -1 }).limit(20)\n// Total time per query shape (those consuming the most time):\ndb.system.profile.aggregate([\n  { $group: { _id: { ns: \"$ns\", plan: \"$planSummary\", shape: \"$queryHash\" }, n: { $sum: 1 }, totalMs: { $sum: \"$millis\" }, maxMs: { $max: \"$millis\" }, avgDocs: { $avg: \"$docsExamined\" } } },\n  { $sort: { totalMs: -1 } },\n  { $limit: 10 }\n])\n// Queries not using an index (COLLSCAN):\ndb.system.profile.find({ planSummary: \"COLLSCAN\" }, { ns: 1, millis: 1, command: 1 }).sort({ millis: -1 }).limit(10)","what":"A partir de los registros de la colección system.profile, lista las operaciones lentas recientes, el tiempo total por query shape (queryHash) y las consultas COLLSCAN que no usan índice.","when":"Ante una queja de lentitud, para encontrar qué consulta consume el tiempo del servidor y determinar las necesidades de índices.","caution":"system.profile es por defecto una capped collection de 1 MB y es por base de datos; en un sistema con carga los registros rotan en minutos, así que analice mientras el profiling está activo o justo después. El nivel de profiling 2 o slowms 0 escribe todas las operaciones y añade una sobrecarga considerable. Los registros se guardan solo en el miembro donde se ejecutaron y no se replican.","since":"MongoDB 4.4","note":"Incluso con el profiling desactivado, las operaciones por encima de slowms (por defecto 100 ms) se escriben en el log de mongod como 'Slow query'; attr.durationMillis y attr.planSummary en el log dan el mismo diagnóstico. En 8.0+ también se escribe un campo planCacheShapeHash para la query shape.","tags":["profiler","system.profile","slow query","top queries","COLLSCAN","queryHash"]},{"id":"index-usage-hide","task":"Encontrar índices no usados y ocultarlos antes de eliminarlos (hideIndex)","category":"Rendimiento","command":"// Usage counter per index (on this member, since the last restart):\ndb.musteri.aggregate([{ $indexStats: {} }, { $project: { name: 1, ops: \"$accesses.ops\", since: \"$accesses.since\" } }, { $sort: { ops: 1 } }])\n// Hide it from the planner before dropping (the index keeps being maintained):\ndb.musteri.hideIndex(\"idx_email\")\n// Revert immediately if problems appear:\ndb.musteri.unhideIndex(\"idx_email\")\n// If the observation period passes without issues:\ndb.musteri.dropIndex(\"idx_email\")","what":"$indexStats muestra cuántas veces se ha usado cada índice; hideIndex oculta un índice al query planner sin eliminarlo, de modo que el efecto de quitarlo puede probarse de forma reversible.","when":"Al depurar índices innecesarios que añaden sobrecarga de escritura y almacenamiento.","caution":"Los contadores son por miembro y se reinician al reiniciar; si las lecturas van a los secundarios, revise todos los miembros y observe un periodo suficientemente largo (incluidos los informes de fin de mes). Los índices únicos o TTL con ops 0 pueden seguir siendo necesarios: la restricción de unicidad y los borrados TTL no aparecen en el contador. Un índice oculto se sigue manteniendo en las escrituras, y un índice único oculto sigue imponiendo la restricción.","since":"MongoDB 4.4","note":"hideIndex requiere featureCompatibilityVersion 4.4 o superior. El índice _id no se puede ocultar. Volver a mostrarlo con unhideIndex no requiere reconstrucción y tiene efecto inmediato.","tags":["indexStats","hideIndex","unused index","index cleanup","dropIndex","index"]},{"id":"connections-by-app","task":"Contar conexiones por aplicación y cliente (diagnóstico de connection storm)","category":"Rendimiento","command":"// Total, available and rejected connection counts:\ndb.serverStatus().connections\n// Count connections by application name and client IP:\ndb.getSiblingDB(\"admin\").aggregate([\n  { $currentOp: { allUsers: true, idleConnections: true } },\n  { $group: { _id: { app: \"$appName\", client: { $arrayElemAt: [{ $split: [\"$client\", \":\"] }, 0] } }, conns: { $sum: 1 }, active: { $sum: { $cond: [\"$active\", 1, 0] } } } },\n  { $sort: { conns: -1 } },\n  { $limit: 20 }\n])","what":"Muestra el número total y disponible de conexiones en el servidor y luego agrupa las conexiones abiertas por appName e IP de cliente junto con cuántas están activas.","when":"Cuando el número de conexiones se dispara de repente, ante errores 'connection refused' o de timeout del pool, para encontrar qué servidor de aplicaciones pierde conexiones.","caution":"idleConnections: true también devuelve todas las conexiones inactivas, por lo que en sistemas con miles de conexiones la salida es grande; por eso es importante resumir con $group. Las filas con client null son hilos internos. Cada conexión consume aproximadamente 1 MB de memoria; un connection storm también hace crecer la memoria fuera de la caché de WiredTiger.","since":"MongoDB 4.4","note":"La solución duradera suele estar en el lado del driver: maxPoolSize, maxIdleTimeMS y el número de instancias de la aplicación. Si las aplicaciones no fijan appName, distinguirlas es difícil; pida appName=<service> en la cadena de conexión. net.maxIncomingConnections y el valor ulimit -n del SO fijan el límite superior duro.","tags":["connections","connection count","currentOp","appName","connection storm","pool"]},{"id":"index-build-monitor","task":"Monitorear y abortar una creación de índice en curso","category":"Mantenimiento","command":"// Index builds in progress and their completion percentage:\ndb.getSiblingDB(\"admin\").aggregate([\n  { $currentOp: { allUsers: true } },\n  { $match: { $or: [{ \"command.createIndexes\": { $exists: true } }, { desc: /^IndexBuildsCoordinator/ }] } },\n  { $project: { opid: 1, desc: 1, ns: 1, secs_running: 1, msg: 1, progress: 1, \"command.indexes.name\": 1 } }\n])\n// Abort the build (on the primary; in a replica set it stops on all members):\ndb.musteri.dropIndex(\"idx_email\")","what":"Lista las creaciones de índices en ejecución con el número de documentos recorridos y el porcentaje de progreso (progress.done / progress.total); si hace falta, aborta la creación con dropIndex.","when":"Para estimar cuándo terminará un createIndex iniciado en una colección grande, o para detener una creación que genera carga en horas pico.","caution":"Aborte la creación de un índice con dropIndex/dropIndexes, no con killOp; en un replica set ejecútelo en el primario para que el aborto se propague a todos los miembros. Algunas operaciones DDL sobre la colección esperan durante la creación. Al final de la creación se toma un breve lock exclusivo; con escrituras muy intensas ese momento puede causar latencia.","since":"MongoDB 4.4","note":"En los replica sets de 4.4+ la creación de un índice se ejecuta simultáneamente en todos los miembros con datos y se confirma cuando se cumple commitQuorum (por defecto votingMembers); si un secundario va retrasado, la creación tampoco termina en el primario. En 5.0+ una creación interrumpida por un apagado limpio se reanuda donde se quedó tras el reinicio.","tags":["index build","createIndex","progress","monitor","dropIndex","commitQuorum"]},{"id":"rolling-index-build","task":"Creación de índices rolling en un replica set (miembro a miembro)","category":"Mantenimiento","command":"// 1) Shut down a SECONDARY; comment out replication.replSetName and start it standalone on another port:\n//    mongod --config /etc/mongod.conf --port 27217 --setParameter disableLogicalSessionCacheRefresh=true\n// 2) Build the index on the standalone member:\ndb.getSiblingDB(\"shop\").musteri.createIndex({ email: 1 }, { name: \"idx_email\" })\n// 3) Restart with the normal configuration; wait until it is SECONDARY and lag has closed:\nrs.printSecondaryReplicationInfo()\n// 4) When all secondaries are done, step down the primary and apply steps 1-3 to the old primary:\nrs.stepDown(120, 30)","what":"Crea el índice en cada miembro por separado ejecutando temporalmente el miembro en modo standalone fuera del conjunto; el primario nunca soporta la carga de la creación.","when":"Cuando el impacto de la creación distribuida por defecto sobre el primario y sobre el retraso de replicación es inaceptable para una colección muy grande.","caution":"El tiempo de mantenimiento de cada miembro debe ser menor que la ventana del oplog; de lo contrario el miembro estará too stale al volver. Al crear un índice único de forma rolling, detenga las escrituras en la colección durante toda la creación; de lo contrario algunos miembros pueden acabar con documentos que lo violan. Inicie el miembro standalone en un puerto distinto para que las aplicaciones no se conecten a él.","since":"MongoDB 4.4","note":"En 4.4+ la creación por defecto ya es no bloqueante y se ejecuta simultáneamente en todos los miembros; el método rolling solo hace falta cuando esa carga es inaceptable. En un clúster shardeado el procedimiento se hace para cada replica set de shard por separado, con el balancer detenido previamente.","tags":["rolling index","index build","standalone","replica","createIndex","maintenance"],"untested":true},{"id":"ttl-index","task":"Crear un índice TTL, cambiar su caducidad y monitorear los borrados TTL","category":"Mantenimiento","command":"// Automatically delete documents lastSeen + 3600 s later:\ndb.oturum.createIndex({ lastSeen: 1 }, { expireAfterSeconds: 3600, name: \"ttl_lastSeen\" })\n// Change the expiry without rebuilding the index:\ndb.runCommand({ collMod: \"oturum\", index: { name: \"ttl_lastSeen\", expireAfterSeconds: 7200 } })\n// TTL indexes on the collection and their expiry:\ndb.oturum.getIndexes().filter(i => i.expireAfterSeconds !== undefined)\n// TTL monitor statistics (deleted documents, passes):\ndb.serverStatus().metrics.ttl","what":"Crea un índice TTL que borra automáticamente los documentos un tiempo fijado después de un campo de fecha, cambia la caducidad con collMod sin reconstrucción y muestra cuántos documentos ha borrado el TTL monitor.","when":"Para limpiar datos con plazo como sesiones, logs o tokens temporales sin escribir un job de borrado en la aplicación, y para investigar por qué una colección en crecimiento no se reduce.","caution":"El TTL monitor se ejecuta cada 60 segundos por defecto; el borrado no es instantáneo. El campo debe ser de tipo Date (o un array de Date); las fechas en string o los documentos sin el campo nunca se borran. Acortar la caducidad provoca que se borren muchos documentos a la vez, carga de escritura y retraso de replicación. Los borrados solo ocurren en el primario y se escriben en el oplog.","since":"MongoDB 5.1","note":"Un índice TTL debe ser de un solo campo (no se soporta TTL compuesto). Añadir expireAfterSeconds con collMod a un índice existente de un solo campo que no es TTL es posible en 5.1+; cambiar la caducidad de un TTL existente también funciona en versiones anteriores. Para borrar en un momento concreto, use expireAfterSeconds: 0 y guarde la hora de borrado en el campo.","tags":["ttl","expireAfterSeconds","collMod","auto delete","index","metrics.ttl"]},{"id":"wt-cache-pressure","task":"Medir la presión sobre la caché de WiredTiger (llenado, proporción sucia, eviction por la aplicación)","category":"Rendimiento","command":"const c = db.serverStatus().wiredTiger.cache;\nconst max = c[\"maximum bytes configured\"];\n({\n  maxGB: (max / 1024 ** 3).toFixed(1),\n  usedPct: (100 * c[\"bytes currently in the cache\"] / max).toFixed(1),\n  dirtyPct: (100 * c[\"tracked dirty bytes in the cache\"] / max).toFixed(1),\n  appThreadEvictedPages: c[\"pages evicted by application threads\"],\n  pagesReadIntoCache: c[\"pages read into cache\"]\n})\n// Read/write ticket usage and queue:\ndb.serverStatus().wiredTiger.concurrentTransactions","what":"Muestra el tamaño de la caché de WiredTiger, sus porcentajes de llenado y de datos sucios, cuántas páginas se vieron obligados a desalojar los hilos de la aplicación y el estado de los tickets de lectura/escritura.","when":"Cuando aumentan las lecturas de disco, la latencia se dispara de repente o cae el throughput de escritura; al dimensionar la caché.","caution":"Cuando el llenado supera el 80% (eviction_target) empieza la eviction en segundo plano; al 95% (eviction_trigger) los hilos de la aplicación también participan en la eviction y las consultas se ralentizan. Para los datos sucios, el 5% es el objetivo y el 20% el disparador; estar constantemente cerca del 20% significa que la capacidad de escritura en disco es insuficiente. Un contador pages evicted by application threads en aumento es latencia que perciben directamente los usuarios. Los valores son contadores acumulados; mire la diferencia entre dos muestras.","since":"MongoDB 7.0","note":"El tamaño de caché por defecto es el mayor entre (RAM - 1 GB) / 2 y 256 MB; dentro de contenedores se calcula a partir del límite del cgroup. Aumentar storage.wiredTiger.engineConfig.cacheSizeGB reduce la page cache del SO; pasar de la mitad de la RAM rara vez ayuda. En 7.0 el número de tickets se ajusta dinámicamente; en 8.0+ las métricas de tickets también aparecen en serverStatus().queues.execution.","tags":["wiredtiger","cache","eviction","dirty","tickets","memory"]},{"id":"dump-restore-archive","task":"Tomar un backup de archivo con mongodump/mongorestore y restaurar una sola colección","category":"Backup","command":"# Single database, one compressed archive file (password is prompted):\nmongodump --uri=\"mongodb://admin@mongo1:27017/?authSource=admin\" --db=shop --gzip --archive=/backup/shop_$(date +%F).archive.gz\n# Dry run before restoring the archive under a different name:\nmongorestore --uri=\"mongodb://admin@mongo1:27017/?authSource=admin\" --gzip --archive=/backup/shop_2026-10-08.archive.gz --nsFrom=\"shop.*\" --nsTo=\"shop_restore.*\" --dryRun\n# Restore a single collection from the archive under a new name:\nmongorestore --uri=\"mongodb://admin@mongo1:27017/?authSource=admin\" --gzip --archive=/backup/shop_2026-10-08.archive.gz --nsInclude=\"shop.musteri\" --nsFrom=\"shop.*\" --nsTo=\"shop_restore.*\" --numInsertionWorkersPerCollection=4\n# Restore under the same name, dropping the existing collection:\nmongorestore --uri=\"mongodb://admin@mongo1:27017/?authSource=admin\" --gzip --archive=/backup/shop_2026-10-08.archive.gz --nsInclude=\"shop.musteri\" --drop","what":"Respalda una base de datos en un único archivo gzip; restaura la base de datos completa o una sola colección desde el archivo, opcionalmente con otro nombre (nsFrom/nsTo).","when":"Para backups lógicos, para mover datos entre entornos o para recuperar una sola colección eliminada o dañada por error.","caution":"mongodump es un backup lógico: es lento con conjuntos de datos grandes y los índices se reconstruyen durante la restauración. Un dump tomado sin --oplog no es consistente a un punto en el tiempo en un sistema activo. --drop borra la colección de destino antes de que empiece la restauración; restaure primero con otro nombre usando nsTo y verifique. La restauración escribe en el primario y puede causar retraso de replicación.","since":"MongoDB 4.4","note":"Desde 4.4 las database tools (mongodump/mongorestore) se versionan aparte del servidor (100.x). Añada readPreference=secondary a la URI para quitar carga de lectura al primario durante el backup. Para mover también usuarios y roles, use un dump completo de la instancia con --restoreDbUsersAndRoles o respalde aparte la base de datos admin.","tags":["mongodump","mongorestore","backup","archive","nsFrom","restore"]},{"id":"pitr-oplog-replay","task":"Restauración a un punto en el tiempo con replay del oplog (volver a antes de un drop/delete erróneo)","category":"Backup","command":"# 1) Full backup + oplog written during the backup (whole instance, from a replica set member):\nmongodump --uri=\"mongodb://admin@mongo2:27017/?authSource=admin&readPreference=secondary\" --oplog --gzip --out=/backup/full\n# 2) Find the time of the bad operation (mongosh): db.getSiblingDB(\"local\").oplog.rs.find({ \"o.drop\": \"musteri\" }).sort({ $natural: -1 }).limit(1)\n# 3) Dump the oplog after the backup (t: start time of the full backup, epoch seconds):\nmongodump --uri=\"mongodb://admin@mongo2:27017/?authSource=admin\" --db=local --collection=oplog.rs --query='{\"ts\":{\"$gte\":{\"$timestamp\":{\"t\":1791400000,\"i\":1}}}}' --out=/backup/oplog\n# 4) Restore the full backup together with its own oplog:\nmongorestore --uri=\"mongodb://admin@target:27017/?authSource=admin\" --gzip --oplogReplay /backup/full\n# 5) Replay the oplog up to the bad operation (oplogLimit is exclusive):\nmkdir /backup/pitr && mv /backup/oplog/local/oplog.rs.bson /backup/pitr/oplog.bson\nmongorestore --uri=\"mongodb://admin@target:27017/?authSource=admin\" --oplogReplay --oplogLimit=1791403600:1 /backup/pitr","what":"Hace que el backup completo sea consistente con --oplog y luego reproduce el oplog acumulado tras el backup hasta el timestamp de la operación errónea (drop, borrado masivo), devolviendo los datos a ese momento.","when":"Cuando se eliminó una colección por error o se ejecutó un update/delete masivo defectuoso y necesita el último estado anterior al daño.","caution":"Restaure en un destino aparte, no en el sistema activo, y mueva los datos necesarios tras verificarlos. El oplog desde el inicio del backup hasta la operación errónea debe seguir dentro de la ventana del oplog; las operaciones que salieron de la ventana no se pueden recuperar. --oplog solo funciona con un dump completo de la instancia (no se puede combinar con --db). En un clúster shardeado no es posible un PITR consistente con mongodump; se requiere un snapshot del sistema de archivos o un producto de backup.","since":"MongoDB 4.4","note":"--oplogLimit=<seconds>:<ordinal> no aplica las entradas en o después del timestamp indicado; pasar directamente el ts de la operación errónea la deja fuera. Los valores ts aparecen en el oplog como Timestamp(t, i). Haga el dump del oplog justo después de la operación errónea, antes de que se cierre la ventana.","tags":["pitr","point in time","oplogReplay","oplogLimit","rollback","mongorestore"],"untested":true},{"id":"user-role-admin","task":"Definir un rol personalizado, crear un usuario, otorgar/revocar roles y cambiar la contraseña","category":"Seguridad","command":"use admin\ndb.createRole({\n  role: \"opsRead\",\n  privileges: [\n    { resource: { db: \"shop\", collection: \"\" }, actions: [\"find\", \"listCollections\", \"listIndexes\", \"collStats\", \"dbStats\"] },\n    { resource: { cluster: true }, actions: [\"serverStatus\", \"inprog\"] }\n  ],\n  roles: []\n})\ndb.createUser({ user: \"shop_app\", pwd: passwordPrompt(), roles: [{ role: \"readWrite\", db: \"shop\" }, { role: \"opsRead\", db: \"admin\" }] })\ndb.grantRolesToUser(\"shop_app\", [{ role: \"clusterMonitor\", db: \"admin\" }])\ndb.revokeRolesFromUser(\"shop_app\", [{ role: \"clusterMonitor\", db: \"admin\" }])\ndb.changeUserPassword(\"shop_app\", passwordPrompt())\ndb.getUser(\"shop_app\", { showPrivileges: true })\ndb.runCommand({ usersInfo: { forAllDBs: true } })","what":"Define un rol personalizado con acciones concretas de base de datos y de clúster, crea un usuario con ese rol y roles integrados, otorga y revoca roles, cambia la contraseña y lista los usuarios de todas las bases de datos.","when":"Al crear cuentas de aplicación y de monitoreo con mínimo privilegio, durante revisiones de accesos o en la rotación de contraseñas.","caution":"La base de datos donde se define el usuario (aquí admin) se usa como authSource; debe coincidir con la cadena de conexión de la aplicación. No otorgue roles amplios como root/dbOwner a las cuentas de aplicación. Los cambios de roles se aplican de inmediato a las sesiones existentes; un cambio de contraseña no corta las conexiones abiertas y se aplica a las nuevas.","since":"MongoDB 4.4","note":"passwordPrompt() pide la contraseña sin dejarla en el historial del shell ni en la lista de procesos. En replica sets y clústeres shardeados los usuarios se gestionan a través del primario/mongos y se replican. Use showPrivileges: true en getUser para ver los privilegios efectivos de un usuario.","tags":["createUser","createRole","grantRolesToUser","privileges","rbac","password"]},{"id":"fcv-upgrade","task":"Comprobar featureCompatibilityVersion (fCV) y la secuencia de actualización mayor","category":"Upgrade","command":"// 1) Current fCV (must be the same on all members; targetVersion appears if a transition is incomplete):\ndb.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })\n// 2) Upgrade binaries in a rolling fashion (secondaries -> stepDown -> old primary); fCV stays old\ndb.version()\n// 3) After the observation period, raise fCV on the primary (on mongos in a sharded cluster):\ndb.adminCommand({ setFeatureCompatibilityVersion: \"7.0\", confirm: true })\n// 4) If a rollback is needed, fCV is lowered BEFORE the binaries:\ndb.adminCommand({ setFeatureCompatibilityVersion: \"6.0\", confirm: true })","what":"Muestra el fCV en ejecución; indica el orden en que se realizan la actualización de binarios y la actualización del fCV, que son pasos separados.","when":"Al planificar una actualización de versión mayor (p. ej. 6.0 -> 7.0), cuando no hay funcionalidades nuevas disponibles tras una actualización o antes de pasar a la siguiente mayor.","caution":"No se pueden saltar versiones mayores: para 5.0 -> 7.0 pase primero a 6.0 y fije el fCV en 6.0. Tras subir el fCV, los archivos de datos pueden pasar al nuevo formato y el downgrade de binarios se complica; suba el fCV solo después de que la nueva versión haya funcionado sin problemas unos días. El downgrade de binarios no está soportado oficialmente en 7.0+ Community; base el plan de vuelta atrás en un backup. Compruebe la compatibilidad de los drivers antes de actualizar.","since":"MongoDB 7.0","note":"Desde 7.0, setFeatureCompatibilityVersion requiere confirm: true. Si falla una transición de fCV, aparece targetVersion/previousVersion en la salida de getParameter; vuelva a ejecutar el mismo comando para completarla. El valor de fCV se replica dentro del replica set.","tags":["fcv","featureCompatibilityVersion","upgrade","major upgrade","major","downgrade"]}]},"recipes/redis.json":{"engineId":"redis","engine":"Redis","order":6,"recipes":[{"id":"bgrewriteaof","task":"Reescribir (compactar) el archivo AOF","category":"Persistencia","command":"BGREWRITEAOF","what":"Reescribe el archivo AOF en segundo plano de forma más compacta.","when":"Cuando el archivo AOF se ha inflado, para acortar el tiempo de reinicio.","caution":"El uso de memoria y de IO aumenta durante el fork; tenga cuidado en horas pico.","tags":["aof","rewrite","persistence","persistence"]},{"id":"bgsave","task":"Tomar un snapshot RDB en segundo plano","category":"Persistencia","command":"BGSAVE","what":"Escribe un snapshot RDB en disco en segundo plano mediante un proceso hijo creado con fork.","when":"Cuando quiere tomar un backup o crear un snapshot.","caution":"En el momento del fork, el uso de memoria puede subir temporalmente por el copy-on-write.","tags":["rdb","bgsave","snapshot","persistence","backup"]},{"id":"save","task":"Tomar un snapshot RDB síncrono","category":"Persistencia","command":"SAVE","what":"Escribe un snapshot RDB en disco de forma síncrona en el hilo principal.","when":"Solo antes de un mantenimiento/apagado, cuando no se está atendiendo a ningún cliente.","caution":"SAVE bloquea el servidor entero y no se procesa ningún comando hasta que termina; en producción prefiera BGSAVE.","tags":["rdb","save","snapshot","blocking","persistence"]},{"id":"lastsave","task":"Ver la hora del último snapshot correcto","category":"Persistencia","command":"LASTSAVE","what":"Devuelve el timestamp Unix del último guardado RDB correcto.","when":"Para comprobar si un BGSAVE ha terminado.","caution":"Comando de solo lectura; sin riesgo.","tags":["lastsave","rdb","timestamp","check","persistence"]},{"id":"config-rewrite","task":"Escribir la configuración en ejecución en disco","category":"Persistencia","command":"CONFIG REWRITE","what":"Persiste en el archivo redis.conf los cambios hechos en tiempo de ejecución con CONFIG SET.","when":"Cuando quiere que los cambios en tiempo de ejecución sobrevivan a un reinicio.","caution":"Sobrescribe el archivo redis.conf existente; el archivo debe estar definido y tener permiso de escritura.","tags":["config","rewrite","persistent","conf","setting"]},{"id":"config-appendonly","task":"Habilitar o deshabilitar AOF","category":"Persistencia","command":"CONFIG SET appendonly yes","what":"Habilita (yes) o deshabilita (no) la persistencia AOF en tiempo de ejecución.","when":"Cuando quiere activar o desactivar la persistencia AOF sin reinicio.","caution":"Deshabilitar AOF debilita la garantía de durabilidad; al habilitarlo, la reescritura inicial del AOF genera carga de IO.","tags":["aof","appendonly","config","persistence","setting"]},{"id":"config-save-rdb","task":"Fijar las reglas de snapshot RDB","category":"Persistencia","command":"CONFIG SET save \"900 1 300 10 60 10000\"","what":"Fija las reglas (segundos/número de cambios) que disparan los snapshots RDB automáticos.","when":"Cuando quiere cambiar la frecuencia de los snapshots automáticos.","caution":"Si pasa un valor vacío (\"\"), el guardado RDB automático se deshabilita por completo y puede perderse la persistencia.","tags":["rdb","save","config","snapshot","setting"]},{"id":"replicaof","task":"Convertir un nodo en réplica","category":"Replicación","command":"REPLICAOF redis-master 6379","what":"Convierte este nodo en réplica del master en el host y puerto indicados e inicia la sincronización.","when":"Al añadir una nueva réplica de lectura o al asignar roles.","caution":"Los datos existentes en el nodo que pasa a réplica se descartan y se extrae una sincronización completa del master.","since":"Redis 5.0","note":"El comando REPLICAOF sustituyó a SLAVEOF en Redis 5.0; el antiguo comando SLAVEOF sigue funcionando como alias.","tags":["replicaof","replica","replication","master","sync"]},{"id":"replicaof-no-one","task":"Promover una réplica a master","category":"Replicación","command":"REPLICAOF NO ONE","what":"Rompe el enlace de réplica del nodo, convirtiéndolo en un master independiente.","when":"Cuando quiere promover una réplica en un escenario de failover.","caution":"Tras la promoción, gestione la topología con cuidado para que no haya divergencia de datos (split-brain) con el antiguo master.","since":"Redis 5.0","note":"REPLICAOF NO ONE sustituyó a SLAVEOF NO ONE en Redis 5.0; el antiguo SLAVEOF NO ONE sigue funcionando como alias.","tags":["replicaof","promote","master","failover","replication"]},{"id":"cluster-failover","task":"Realizar un failover manual","category":"Clúster","command":"CLUSTER FAILOVER","what":"Ejecutado en una réplica, inicia un failover controlado promoviendo esa réplica a master.","when":"Cuando quiere pasar el rol de master a una réplica antes de un mantenimiento.","caution":"Este comando debe ejecutarse en la réplica de destino, no en el propio master.","tags":["cluster","failover","promote","maintenance","replication"]},{"id":"cluster-meet","task":"Añadir un nodo al clúster","category":"Clúster","command":"CLUSTER MEET 10.0.0.12 6379","what":"Une el nodo en la IP y el puerto indicados a la red gossip del clúster.","when":"Al añadir un nuevo nodo al clúster (escalado).","caution":"Si tras añadir el nodo no se hace una asignación de slots (ADDSLOTS) ni un reshard, no recibe datos.","tags":["cluster","meet","node","add","node"]},{"id":"cluster-addslots","task":"Asignar slots del clúster","category":"Clúster","command":"CLUSTER ADDSLOTS 0 1 2 3 4 5","what":"Asigna los hash slots indicados a este nodo master.","when":"Al configurar manualmente un nuevo nodo master o corregir la distribución de slots.","caution":"El mismo slot no puede asignarse a más de un nodo; si no están asignados los 16384 slots, el clúster pasa al estado down.","tags":["cluster","addslots","slot","assign","node"]},{"id":"info-replication","task":"Ver el estado de la replicación","category":"Clúster","command":"INFO replication","what":"Muestra el rol (master/replica), las réplicas conectadas y la información del offset de replicación.","when":"Al comprobar el retraso de replicación y el estado del enlace.","caution":"Comando de solo lectura; sin riesgo.","tags":["info","replication","status","replication","lag"]},{"id":"cluster-info","task":"Ver el estado del clúster","category":"Clúster","command":"CLUSTER INFO","what":"Muestra el estado general del clúster (ok/fail), el número de slots asignados y el número de nodos conocidos.","when":"Al verificar la salud del clúster y la integridad de los slots.","caution":"Comando de solo lectura; sin riesgo.","tags":["cluster","info","status","slot","health"]},{"id":"config-maxmemory","task":"Fijar maxmemory y la política","category":"Memoria","command":"CONFIG SET maxmemory 4gb maxmemory-policy allkeys-lru","what":"Fija el límite de memoria y la política de eviction que se aplica al superarlo. Redis 7.0+ acepta varios parámetros en un solo CONFIG SET; en versiones anteriores ejecute dos comandos CONFIG SET por separado.","when":"Cuando quiere limitar el uso de memoria y definir el comportamiento de eviction.","caution":"Con la política noeviction, las escrituras fallan al alcanzar el límite; un límite erróneo puede provocar pérdida de datos/indisponibilidad.","tags":["maxmemory","policy","memory","eviction","config"]},{"id":"memory-doctor","task":"Analizar la memoria","category":"Memoria","command":"MEMORY DOCTOR","what":"Analiza el uso de memoria y produce un informe legible sobre posibles problemas.","when":"Para el diagnóstico cuando se sospecha un uso de memoria alto o fragmentación.","caution":"Análisis de solo lectura; para más detalle también puede usar el comando MEMORY STATS.","tags":["memory","doctor","memory","analysis","diagnostics"]},{"id":"slowlog-get","task":"Ver el log de comandos lentos","category":"Mantenimiento","command":"SLOWLOG GET 10","what":"Devuelve las 10 entradas más recientes de comandos lentos (duración, hora, argumentos) del slowlog.","when":"Al identificar comandos lentos e investigar cuellos de botella de rendimiento.","caution":"Comando de solo lectura; use SLOWLOG RESET para borrar las entradas.","tags":["slowlog","get","slow","diagnostics","performance"]},{"id":"slowlog-config","task":"Fijar el umbral de comandos lentos","category":"Mantenimiento","command":"CONFIG SET slowlog-log-slower-than 10000","what":"Fija el umbral de duración, en microsegundos, a partir del cual un comando se registra en el slowlog (10000 = 10 ms).","when":"Cuando quiere ajustar la sensibilidad del slowlog.","caution":"Fijarlo en 0 registra todos los comandos y añade sobrecarga; -1 deshabilita el slowlog.","tags":["slowlog","threshold","threshold","config","slow"]},{"id":"slowlog-reset","task":"Reiniciar el log de comandos lentos","category":"Mantenimiento","command":"SLOWLOG RESET","what":"Borra todas las entradas del slowlog.","when":"Al borrar las entradas para iniciar una nueva ventana de observación.","caution":"El historial existente de comandos lentos se borra de forma permanente.","tags":["slowlog","reset","reset","clear","maintenance"]},{"id":"client-kill","task":"Matar una conexión de cliente","category":"Mantenimiento","command":"CLIENT KILL ADDR 10.0.0.5:53210","what":"Termina la conexión de cliente en la dirección indicada.","when":"Cuando quiere terminar un cliente atascado o que se comporta mal.","caution":"La conexión se corta de inmediato; las operaciones en curso pueden quedar incompletas. Asegúrese de elegir la dirección correcta.","tags":["client","kill","client","kill","maintenance"]},{"id":"client-list","task":"Listar los clientes conectados","category":"Mantenimiento","command":"CLIENT LIST","what":"Lista todos los clientes conectados con detalles como dirección, tiempo de inactividad y último comando.","when":"Al inspeccionar conexiones o buscar un cliente problemático.","caution":"Comando de solo lectura; sin riesgo.","tags":["client","list","client","list","connection"]},{"id":"config-get","task":"Ver el valor de un parámetro","category":"Tiempo de ejecución","command":"CONFIG GET maxmemory-policy","what":"Devuelve el valor actual de uno o varios parámetros de configuración (admite patrones glob).","when":"Al verificar el valor en tiempo de ejecución de un ajuste.","caution":"Comando de solo lectura; sin riesgo.","tags":["config","get","parameter","view","runtime"]},{"id":"config-set","task":"Cambiar el valor de un parámetro","category":"Tiempo de ejecución","command":"CONFIG SET appendfsync everysec","what":"Cambia un parámetro de configuración en tiempo de ejecución sin reinicio.","when":"Cuando quiere ajustar de inmediato un comportamiento del servidor.","caution":"El cambio es temporal; ejecute CONFIG REWRITE para persistirlo. Un valor erróneo puede degradar el rendimiento/la durabilidad.","tags":["config","set","parameter","runtime","setting"]}]},"recipes/redis-ext.json":{"engineId":"redis","engine":"Redis","order":2,"recipes":[{"id":"mem-fragmentation","task":"Analizar el uso de memoria y la fragmentación, habilitar active defrag","category":"Memoria","command":"INFO memory\n# Fields to check: used_memory_human, used_memory_rss_human, mem_fragmentation_ratio, mem_fragmentation_bytes, allocator_frag_ratio\nMEMORY STATS\n# If fragmentation is high and persistent, enable active defrag (requires jemalloc):\nCONFIG SET activedefrag yes\nCONFIG SET active-defrag-ignore-bytes 100mb\nCONFIG SET active-defrag-threshold-lower 10\n# Ask the allocator to return free pages to the OS:\nMEMORY PURGE","what":"Muestra la diferencia entre la memoria que Redis asigna a los datos (used_memory) y el RSS que ve el SO, la proporción de fragmentación y el desglose de memoria; habilita active defrag en tiempo de ejecución si hace falta.","when":"Cuando el RSS es claramente mayor que el dataset, la memoria no baja tras borrados/expiraciones masivos o aparece riesgo de OOM.","caution":"mem_fragmentation_ratio engaña con datasets pequeños (p. ej. 4.0 con 2 MB de datos); mire mem_fragmentation_bytes para el valor absoluto. Una proporción inferior a 1 significa swapping, no fragmentación, y causa latencia. Active defrag consume CPU; limítelo con active-defrag-cycle-max. Hace falta CONFIG REWRITE o redis.conf para hacerlo persistente.","since":"Redis 4.0","note":"Active defrag solo funciona en builds compiladas con el jemalloc que incluye Redis. allocator_frag_ratio muestra la fragmentación interna que defrag puede corregir; allocator_rss_ratio muestra el espacio que el allocator no ha devuelto al SO. La fragmentación crece más rápido en cargas que actualizan con frecuencia valores de tamaño variable.","tags":["memory","ram","fragmentation","activedefrag","rss","info memory"]},{"id":"bigkeys-memkeys","task":"Encontrar big keys (--bigkeys, --memkeys, MEMORY USAGE)","category":"Memoria","command":"# Largest keys per type by element count:\nredis-cli -h redis1 -p 6379 --bigkeys -i 0.01\n# By memory size:\nredis-cli -h redis1 -p 6379 --memkeys -i 0.01\n# Size and length together, top 20 keys (redis-cli 7.4+):\nredis-cli -h redis1 -p 6379 --keystats --top 20 -i 0.01\n# Memory usage (SAMPLES 0: all elements) and encoding of a single suspicious key:\nredis-cli -h redis1 -p 6379 MEMORY USAGE kullanici:1001:sepet SAMPLES 0\nredis-cli -h redis1 -p 6379 OBJECT ENCODING kullanici:1001:sepet","what":"Recorre el keyspace con SCAN e informa de las claves con más elementos y más memoria por tipo; muestra el consumo real de memoria y el encoding de una clave sospechosa.","when":"Cuando la memoria crece de forma inesperada, un solo comando (HGETALL, SMEMBERS, DEL) causa latencia o un nodo de un clúster usa más memoria que los demás.","caution":"El recorrido usa SCAN y no bloquea el servidor, pero recorre todo el keyspace; -i 0.01 hace una pausa cada 100 llamadas SCAN para reducir la carga. Borrar una big key con DEL bloquea el hilo principal; use UNLINK. MEMORY USAGE con SAMPLES 0 es O(N) en colecciones grandes. Ejecutarlo en una réplica no carga el master.","since":"Redis 6.0","note":"--bigkeys mira el número de elementos y --memkeys MEMORY USAGE; para tipos como list/hash pueden destacar claves distintas. Los hashes/zsets/lists pequeños usan el encoding listpack y ocupan mucha menos memoria; una vez superados umbrales como hash-max-listpack-entries pasan a hashtable. En un clúster ejecútelo en cada master por separado.","tags":["bigkeys","memkeys","big key","memory usage","keystats","large key"]},{"id":"hotkeys-lfu","task":"Encontrar hot keys (--hotkeys, OBJECT FREQ)","category":"Rendimiento","command":"# Requires an LFU-based policy; note the current policy:\nCONFIG GET maxmemory-policy\nCONFIG SET maxmemory-policy allkeys-lfu\n# Hottest keys by access frequency:\nredis-cli -h redis1 -p 6379 --hotkeys -i 0.01\n# LFU counter of a single key:\nOBJECT FREQ urun:populer:liste\n# Revert to the previous policy when the analysis is done:\nCONFIG SET maxmemory-policy noeviction","what":"Lista las claves a las que se accede con más frecuencia usando los contadores LFU; muestra el contador de frecuencia de acceso de una clave concreta.","when":"Cuando la CPU de un nodo se acerca al 100%, un slot/nodo de un clúster recibe mucho más tráfico que los demás o una sola clave satura el ancho de banda de red.","caution":"--hotkeys solo funciona cuando maxmemory-policy es *-lfu. Cambiar la política también cambia el comportamiento de eviction: si maxmemory está fijado y la política anterior era volatile-* o noeviction, allkeys-lfu también puede desalojar claves sin TTL. Mantenga el análisis breve y revierta la política. Los contadores LFU son logarítmicos y decaen con el tiempo; no son conteos absolutos de accesos.","since":"Redis 4.0","note":"Tras el cambio de política, hace falta algo de tráfico antes de que los contadores LFU tengan sentido. Si no se puede cambiar la política, una muestra breve con MONITOR es una alternativa, pero MONITOR reduce seriamente el throughput en sistemas con carga. Para hot keys de lectura, la caché del lado del cliente (CLIENT TRACKING) o dividir la clave es la solución duradera.","tags":["hotkeys","hot keys","lfu","object freq","hot key","cpu"]},{"id":"scan-unlink-pattern","task":"Encontrar claves que coinciden con un patrón sin KEYS y borrarlas con seguridad (SCAN + UNLINK)","category":"Mantenimiento","command":"# Cursor-based scan instead of KEYS (call again with the returned cursor until it returns 0):\nSCAN 0 MATCH oturum:* COUNT 1000 TYPE string\n# Number of keys matching the pattern:\nredis-cli -h redis1 -p 6379 --scan --pattern 'oturum:*' --count 1000 | wc -l\n# Bulk delete in batches of 500, freeing memory in the background:\nredis-cli -h redis1 -p 6379 --scan --pattern 'oturum:*' --count 1000 | xargs -r -n 500 redis-cli -h redis1 -p 6379 UNLINK","what":"Recorre el keyspace con un cursor sin bloquear el servidor, cuenta las claves que coinciden con un patrón y un tipo, y las borra en bloque con UNLINK, liberando la memoria en segundo plano.","when":"Al limpiar claves dejadas por una versión antigua de la aplicación, al averiguar cuántas claves hay bajo un prefijo o cuando de otro modo haría falta KEYS en producción.","caution":"KEYS devuelve todo el keyspace de una vez y bloquea el servidor durante segundos con muchos datos; no lo use en producción. SCAN no da una garantía completa para las claves añadidas/eliminadas durante el recorrido y puede devolver la misma clave más de una vez. Los nombres de clave con espacios o comillas se rompen con xargs. Compruebe el recuento y algunas muestras antes de borrar; no hay deshacer.","since":"Redis 4.0","note":"La opción TYPE de SCAN llegó en 6.0. COUNT es una indicación y no garantiza el número de elementos devueltos. En un clúster, --scan solo recorre el nodo conectado; ejecútelo en cada master. UNLINK libera los valores grandes en un hilo aparte mediante lazyfree; para claves pequeñas cuesta lo mismo que DEL.","tags":["scan","unlink","keys","bulk delete","pattern","match"]},{"id":"latency-diagnosis","task":"Diagnosticar el origen de la latencia (LATENCY DOCTOR, histograma, commandstats)","category":"Rendimiento","command":"# Enable the latency monitor (record events above 100 ms):\nCONFIG SET latency-monitor-threshold 100\nLATENCY LATEST\nLATENCY HISTORY command\nLATENCY DOCTOR\n# Per-command latency distribution and percentiles:\nLATENCY HISTOGRAM GET SET HGETALL\nINFO latencystats\n# Most expensive commands by total CPU time:\nredis-cli -h redis1 -p 6379 INFO commandstats | sort -t= -k3 -rn | head\n# On the server host, baseline latency outside Redis (VM/steal detection, 30 s):\nredis-cli --intrinsic-latency 30\nLATENCY RESET","what":"El latency monitor registra los picos de latencia de eventos como fork, AOF fsync, el ciclo de expiración y los comandos lentos; muestra los percentiles de latencia por comando y los comandos más costosos por tiempo total de CPU; mide la latencia base a nivel de host.","when":"Cuando la aplicación ve picos de latencia p99 y las quejas de lentitud continúan aunque SLOWLOG esté limpio.","caution":"latency-monitor-threshold vale 0 por defecto (desactivado); restaure el valor anterior tras el diagnóstico. SLOWLOG solo mide el tiempo de ejecución de los comandos y no ve las esperas de red ni de cola; los eventos LATENCY también capturan pausas dentro del servidor como fork y fsync. --intrinsic-latency consume CPU y debe ejecutarse en la máquina que aloja el servidor Redis.","since":"Redis 7.0","note":"LATENCY HISTOGRAM e INFO latencystats llegaron en 7.0 (latency-tracking está activo por defecto). Si los eventos fork dominan LATENCY HISTORY, revise el tamaño del dataset y THP (transparent huge pages); si los eventos aof-fsync-always/aof-write son altos, revise la latencia del disco. Los comandos con usec_per_call alto en commandstats indican un uso O(N).","tags":["latency","slowness","latency doctor","histogram","commandstats","intrinsic-latency"]},{"id":"eviction-oom-diag","task":"Diagnosticar eviction, expiración y errores OOM","category":"Memoria","command":"INFO stats\n# Fields to check: evicted_keys, expired_keys, expired_stale_perc, keyspace_hits, keyspace_misses, total_eviction_exceeded_time (7.0+)\nINFO keyspace\nCONFIG GET maxmemory\nCONFIG GET maxmemory-policy\nINFO memory\n# The value that triggers eviction: used_memory - mem_not_counted_for_evict, compared with maxmemory","what":"Muestra el número de claves desalojadas y expiradas, la tasa de aciertos de caché, la proporción de claves con TTL y el valor de memoria que dispara la eviction respecto a maxmemory.","when":"Cuando la aplicación recibe 'OOM command not allowed when used memory > maxmemory', cae la tasa de aciertos de caché o se informa de que las claves desaparecen de forma inesperada.","caution":"Con la política noeviction, los comandos de escritura reciben un error OOM al alcanzar maxmemory mientras las lecturas siguen funcionando. Con las políticas volatile-* las claves sin TTL nunca se desalojan; si no hay claves con TTL se comporta como noeviction (compruebe expires en INFO keyspace). No fije maxmemory demasiado cerca de la RAM física: deje margen para los buffers de replicación y el copy-on-write durante el fork.","since":"Redis 6.0","note":"Tasa de aciertos = keyspace_hits / (keyspace_hits + keyspace_misses). Si evicted_keys crece de forma constante mientras cae la tasa de aciertos, la memoria es insuficiente. Un expired_stale_perc alto significa una proporción alta de claves expiradas aún no eliminadas; el ciclo de expiración puede acelerarse con active-expire-effort. Los buffers de réplica y el buffer del AOF se incluyen en mem_not_counted_for_evict.","tags":["eviction","oom","maxmemory","expire","hit ratio","evicted_keys"]},{"id":"client-memory","task":"Encontrar conexiones que inflan la memoria (output buffer, query buffer, maxmemory-clients)","category":"Memoria","command":"INFO clients\n# Top 10 connections by tot-mem (tot-mem, omem, qbuf, id, addr, user, cmd, age):\nredis-cli -h redis1 -p 6379 CLIENT LIST | awk '{for(i=1;i<=NF;i++){split($i,a,\"=\"); f[a[1]]=a[2]} print f[\"tot-mem\"], f[\"omem\"], f[\"qbuf\"], f[\"id\"], f[\"addr\"], f[\"user\"], f[\"cmd\"], f[\"age\"]}' | sort -rn | head -10\nCLIENT LIST TYPE pubsub\nCONFIG GET client-output-buffer-limit\n# Upper limit for the total memory of all clients (largest clients are closed when exceeded):\nCONFIG SET maxmemory-clients 5%","what":"Ordena las conexiones por memoria total (tot-mem), output buffer (omem) y query buffer (qbuf); muestra las conexiones pub/sub y los límites de buffer; fija un límite superior global para la memoria de clientes.","when":"Cuando used_memory crece sin que crezcan los datos, mem_clients_normal parece alto o un cliente pub/sub/MONITOR que consume despacio llena la memoria.","caution":"Un omem grande suele apuntar a un cliente que lee los resultados despacio (respuesta grande de KEYS/HGETALL, suscriptor pub/sub lento, un MONITOR que se dejó abierto). Al superar maxmemory-clients, los clientes que más memoria tienen se cierran sin aviso; los enlaces de réplica y master están excluidos, pero las conexiones de la aplicación pueden caer. Use CLIENT NO-EVICT on para las conexiones críticas.","since":"Redis 7.0","note":"maxmemory-clients llegó en 7.0 y vale 0 por defecto (ilimitado). client-output-buffer-limit aplica límites hard/soft para las clases pubsub y replica; los clientes normales son ilimitados por defecto. Cierre una conexión problemática con CLIENT KILL ID <id> (vea client-kill).","tags":["client list","omem","output buffer","maxmemory-clients","connections","pubsub"]},{"id":"persistence-health","task":"Diagnosticar el estado de error de RDB/AOF, el tiempo de fork y la causa de MISCONF","category":"Persistencia","command":"INFO persistence\n# Critical fields: rdb_last_bgsave_status, aof_last_bgrewrite_status, aof_last_write_status, rdb_changes_since_last_save, aof_delayed_fsync\nINFO stats\n# latest_fork_usec: duration of the last fork (microseconds), total_forks\nCONFIG GET stop-writes-on-bgsave-error\nCONFIG GET dir\nCONFIG GET appendfsync","what":"Muestra si el último snapshot RDB y la reescritura/escritura del AOF tuvieron éxito, cuántas claves cambiaron desde el último guardado, el número de fsync retrasados y la duración del fork.","when":"Cuando la aplicación recibe 'MISCONF Redis is configured to save RDB snapshots, but it's currently unable to persist to disk', BGSAVE/BGREWRITEAOF falla o se observa latencia relacionada con el fork.","caution":"Con stop-writes-on-bgsave-error yes (por defecto), todas las escrituras se rechazan tras un BGSAVE fallido. Silenciar el error poniéndolo en 'no' oculta el riesgo de pérdida de datos; corrija primero la causa: disco lleno, sin permiso de escritura en dir o el fork no consigue memoria (vm.overcommit_memory=0). El tiempo de fork puede llegar a cientos de milisegundos con un dataset grande, y Redis no responde durante ese tiempo.","since":"Redis 7.0","note":"vm.overcommit_memory=1 y las transparent huge pages deshabilitadas en Linux son las recomendaciones oficiales de Redis. Un aof_delayed_fsync en aumento significa que el disco no da abasto con el fsync (appendfsync everysec). Los detalles del error están en el log de Redis ('Background saving error', 'Can't save in background: fork').","tags":["misconf","bgsave","aof","fork","persistence","durability"]},{"id":"aof-repair","task":"Reparar un Redis que no arranca por un AOF corrupto (redis-check-aof, redis-check-rdb)","category":"Persistencia","command":"# With Redis stopped, copy the files first:\ncp -a /var/lib/redis/appendonlydir /var/lib/redis/appendonlydir.bak\n# Check (7.0+ multi-part AOF: pass the manifest file):\nredis-check-aof /var/lib/redis/appendonlydir/appendonly.aof.manifest\n# Repair by truncating everything after the first corrupt point (asks for confirmation):\nredis-check-aof --fix /var/lib/redis/appendonlydir/appendonly.aof.manifest\n# Verify the RDB file:\nredis-check-rdb /var/lib/redis/dump.rdb","what":"Valida los archivos AOF (base + incr + manifest) y el archivo RDB; trunca la cola incompleta o corrupta del AOF para que Redis pueda volver a arrancar.","when":"Cuando Redis no arranca con 'Bad file format reading the append only file' o 'Unexpected EOF' tras un corte de luz, un disco lleno o un apagado repentino.","caution":"--fix borra todo desde el primer punto corrupto hasta el final del archivo; si la corrupción está en mitad del archivo, se pierden todas las escrituras posteriores. Compruebe en la salida cuántos bytes se truncarán (ok_up_to, diff) antes de confirmar. Un último comando incompleto (p. ej. un HSET grande) se descarta por completo. Haga siempre primero una copia.","since":"Redis 7.0","note":"Con aof-load-truncated yes (por defecto), un AOF truncado solo al final se recorta automáticamente al arrancar; redis-check-aof suele hacer falta cuando la corrupción está en mitad del archivo. En el AOF multiparte de 7.0+ los archivos están en el directorio appenddirname y el archivo base puede estar en formato RDB. Para un RDB corrupto, redis-check-rdb solo diagnostica y no repara.","tags":["aof","redis-check-aof","corrupted","corrupt","repair","redis-check-rdb"]},{"id":"replication-backlog","task":"Corregir caídas de replicación y un bucle infinito de full resync (backlog, buffer de réplica)","category":"Replicación","command":"INFO replication\n# Master: connected_slaves, slaveN:...,state=online,offset=,lag=  Replica: master_link_status, master_last_io_seconds_ago, master_sync_in_progress\nINFO stats\n# sync_full / sync_partial_ok / sync_partial_err: repeated full syncs = backlog too small\nCONFIG GET repl-backlog-size\nCONFIG SET repl-backlog-size 256mb\n# If the replica buffer exceeds its limit during a full sync, the link drops and sync starts over:\nCONFIG GET client-output-buffer-limit\nCONFIG SET client-output-buffer-limit \"replica 1gb 512mb 120\"","what":"Muestra el estado del enlace, la diferencia de offset y los contadores de sincronización parcial/completa en el master y la réplica; hace crecer el replication backlog para que las desconexiones cortas no se conviertan en sincronizaciones completas, y el límite del output buffer de réplica para que una sincronización completa no se corte a mitad.","when":"Cuando una réplica entra en bucle con 'MASTER <-> REPLICA sync started', el contador sync_full crece o el log del master muestra 'scheduled to be closed ASAP for overcoming of output buffer limits'.","caution":"Cada sincronización completa supone un fork + generación de RDB en el master; el bucle castiga la memoria y el disco del master. repl-backlog-size se asigna de la memoria del master. Subir el límite del buffer de réplica significa que el master puede usar esa memoria adicional durante una sincronización completa; inclúyala en el cálculo de maxmemory. Hace falta CONFIG REWRITE o redis.conf para hacer persistentes los cambios.","since":"Redis 4.0","note":"Dimensionado aproximado del backlog: el volumen de escritura por segundo del master (crecimiento de master_repl_offset) x la duración de desconexión que se quiere tolerar. El nombre de clase 'replica' en client-output-buffer-limit se acepta en lugar de 'slave' desde 5.0; la salida de CONFIG GET aún puede mostrar slave. Si el disco es lento, repl-diskless-sync yes (por defecto en 7.0+) transmite el RDB por el socket sin escribirlo en disco.","tags":["replication","backlog","full sync","psync","replica","output buffer"]},{"id":"sentinel-status","task":"Comprobar el estado de Sentinel, el master actual y el quorum","category":"Sentinel","command":"redis-cli -p 26379 SENTINEL MASTERS\nredis-cli -p 26379 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster\nredis-cli -p 26379 SENTINEL REPLICAS mymaster\nredis-cli -p 26379 SENTINEL SENTINELS mymaster\n# Are there enough sentinels (quorum and majority) for a failover:\nredis-cli -p 26379 SENTINEL CKQUORUM mymaster\nredis-cli -p 26379 INFO sentinel","what":"Lista los masters monitoreados por Sentinel, la dirección del master actual, las réplicas conocidas y los demás sentinels; verifica que el quorum y la mayoría necesarios para el failover están disponibles.","when":"Cuando la aplicación se conecta al nodo equivocado, al verificar la topología tras un failover y antes de cada mantenimiento.","caution":"Cada sentinel informa de su propia visión; GET-MASTER-ADDR-BY-NAME debe devolver la misma dirección en todos los sentinels; de lo contrario hay una partición de red o una configuración discordante. s_down/o_down en el campo flags o disconnected en una réplica requiere investigación urgente. El puerto de Sentinel (26379) debe estar abierto en el firewall solo entre sentinels y clientes.","since":"Redis 5.0","note":"SENTINEL REPLICAS sustituyó a SENTINEL SLAVES en 5.0 (el nombre antiguo sigue funcionando). El número de sentinels debe ser impar y como mínimo 3, repartidos en dominios de fallo distintos (host/rack/AZ). Si Redis usa ACL/contraseñas, sentinel auth-user/auth-pass deben fijarse en sentinel.conf.","tags":["sentinel","master","quorum","ckquorum","topology","failover"],"untested":true},{"id":"sentinel-failover","task":"Realizar un failover manual con Sentinel y limpiar la topología","category":"Sentinel","command":"# First check quorum and an eligible replica:\nredis-cli -p 26379 SENTINEL CKQUORUM mymaster\nredis-cli -p 26379 SENTINEL REPLICAS mymaster\n# Manual failover without waiting for agreement from other sentinels:\nredis-cli -p 26379 SENTINEL FAILOVER mymaster\n# Verify the new master (must be the same on all sentinels):\nredis-cli -p 26379 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster\n# If old/removed replicas remain listed, reset sentinels one at a time, 30 s apart:\nredis-cli -p 26379 SENTINEL RESET mymaster","what":"Pasa el rol de master a la réplica más adecuada incluso cuando el master está sano; después verifica el nuevo master y borra de la memoria de los sentinels las réplicas que ya no existen.","when":"Antes de un mantenimiento planificado en el host del master, durante un simulacro de failover o al mover el master a otro centro de datos.","caution":"Durante el failover hay una interrupción de escrituras de unos segundos, y los datos escritos en el master pero aún no replicados pueden perderse. Las réplicas con replica-priority 0 nunca se eligen; si no existe ninguna réplica elegible, el comando devuelve un error. Si SENTINEL RESET se ejecuta a la vez en todos los sentinels, la información de topología se pierde temporalmente; ejecútelo en cada uno por separado y espaciado. Asegúrese de que los clientes usan un driver que pide a Sentinel la dirección del master.","since":"Redis 5.0","note":"SENTINEL FAILOVER no pide acuerdo a los demás sentinels, pero sí propaga el nuevo config epoch. Orden de selección: replica-priority (menor primero), replication offset (mayor primero), run ID. Cuando el antiguo master vuelve tras el failover, Sentinel lo reconfigura automáticamente como réplica del nuevo master.","tags":["sentinel","failover","manual failover","master","reset","switchover"],"untested":true},{"id":"cluster-check-fix","task":"Comprobar la salud del clúster y reparar inconsistencias de slots (--cluster check/fix)","category":"Clúster","command":"redis-cli -h node1 -p 6379 CLUSTER INFO\nredis-cli -h node1 -p 6379 CLUSTER NODES\nredis-cli -h node1 -p 6379 CLUSTER SHARDS\n# Slot coverage, open (migrating/importing) slots and node consistency:\nredis-cli --cluster check node1:6379\n# Repair if there are open or unowned slots:\nredis-cli --cluster fix node1:6379\n# Which slot and node a key lives on (-c: follow redirects):\nredis-cli -h node1 -p 6379 CLUSTER KEYSLOT kullanici:1001\nredis-cli -c -h node1 -p 6379 GET kullanici:1001","what":"Comprueba que todos los nodos ven el mismo mapa de slots, que los 16384 slots están asignados y que no hay slots incompletos (migrating/importing); corrige los problemas encontrados y muestra en qué slot/nodo está una clave.","when":"Cuando aparece cluster_state:fail, los clientes reciben CLUSTERDOWN o errores MOVED/ASK constantes, y tras un reshard interrumpido.","caution":"--cluster fix puede asignar slots sin propietario a nodos arbitrarios y mover claves de slots abiertos; revise primero la salida de check. Si se pierden un master y todas sus réplicas, los datos de esos slots desaparecen; fix solo asigna el slot vacío a otro nodo. Con cluster-require-full-coverage yes (por defecto), un solo slot sin propietario detiene las escrituras en todo el clúster.","since":"Redis 7.0","note":"CLUSTER SHARDS llegó en 7.0 y sustituye a CLUSTER SLOTS. Si la autenticación está habilitada, añada -a <password> o --user/--pass a los comandos --cluster. Las marcas fail/pfail en la salida de CLUSTER NODES significan que los demás ven el nodo como inalcanzable.","tags":["cluster","check","fix","slot","clusterdown","cluster nodes"],"untested":true},{"id":"cluster-reshard","task":"Añadir nodos, mover slots y quitar nodos en un clúster (add-node, reshard, rebalance)","category":"Clúster","command":"# Add the new master to the cluster (joins empty, with no slots):\nredis-cli --cluster add-node node7:6379 node1:6379\n# Attach a new replica to a specific master:\nredis-cli --cluster add-node node8:6379 node1:6379 --cluster-slave --cluster-master-id <master-node-id>\n# Move 1000 slots from all masters to the new node:\nredis-cli --cluster reshard node1:6379 --cluster-from all --cluster-to <node7-id> --cluster-slots 1000 --cluster-yes\n# Or rebalance automatically (give slots to empty masters too):\nredis-cli --cluster rebalance node1:6379 --cluster-use-empty-masters\n# Remove a node whose slots have been emptied:\nredis-cli --cluster del-node node1:6379 <node-id>","what":"Añade un nuevo master o réplica al clúster, mueve slots y sus claves entre nodos en línea, reequilibra automáticamente la distribución de slots y quita del clúster un nodo vaciado.","when":"Al escalar horizontalmente la memoria/el throughput, al repartir la carga de un nodo caliente o al retirar un nodo.","caution":"La migración de slots transfiere las claves una a una con MIGRATE; mientras se mueven big keys, ambos nodos pueden bloquearse brevemente. Durante la migración los clientes reciben redirecciones ASK; se requiere un cliente compatible con cluster. Si se interrumpe un reshard, los slots quedan en estado migrating/importing; complételo con --cluster fix. No quite con del-node un master que aún tiene slots; mueva antes sus slots.","since":"Redis 5.0","note":"Los ID de nodo se obtienen de CLUSTER NODES o CLUSTER MYID. --cluster-pipeline fija cuántas claves mueve cada llamada MIGRATE (por defecto 10). --cluster rebalance con --cluster-weight puede dar pesos distintos a los nodos. Los comandos redis-cli --cluster sustituyeron a redis-trib.rb en 5.0.","tags":["cluster","reshard","rebalance","add-node","slot","del-node"],"untested":true},{"id":"acl-users","task":"Crear usuarios ACL, probar permisos y hacerlos persistentes","category":"Seguridad","command":"# Application user: only app:* keys and channels, dangerous commands excluded:\nACL SETUSER app_rw on >S3cret-Change ~app:* resetchannels &app:* +@read +@write -@dangerous\n# Monitoring user: no key access, only specific commands:\nACL SETUSER monitor on >M0n-Secret resetkeys resetchannels -@all +info +ping +client|list +slowlog|get +latency +config|get\n# Test permissions without running the command:\nACL DRYRUN app_rw GET app:1001\nACL GETUSER app_rw\nACL LIST\n# Make it persistent: ACL SAVE if aclfile is set, otherwise CONFIG REWRITE into redis.conf\nACL SAVE","what":"Crea usuarios de aplicación y de monitoreo autorizados por prefijo de clave, canal pub/sub y categoría de comandos; prueba con DRYRUN si un comando está permitido sin ejecutarlo y escribe las ACL en disco.","when":"Al pasar de una única contraseña compartida (requirepass) a accesos por usuario, al aislar las aplicaciones de las claves de las demás o al crear una cuenta de solo lectura para una herramienta de monitoreo.","caution":"El orden de las reglas importa: resetchannels elimina cualquier regla &channel dada antes. -@dangerous también bloquea comandos como KEYS, FLUSHALL, CONFIG y DEBUG; verifique con DRYRUN los comandos que usa la aplicación. Si el usuario default sigue habilitado con nopass y +@all, las ACL no protegen nada; ejecute ACL SETUSER default off tras crear un usuario administrador. En redis-cli, si AUTH con --user/--pass falla, la conexión continúa como el usuario default y el comando se ejecuta igualmente; por eso use DRYRUN para las pruebas de permisos.","since":"Redis 7.0","note":"Las ACL llegaron en 6.0; ACL DRYRUN, los selectores y las reglas de subcomandos (client|list) se añadieron en 7.0. aclfile y las líneas user en redis.conf no se pueden usar a la vez. Las contraseñas se guardan como hashes SHA-256; ACL GETUSER no muestra la contraseña en claro.","tags":["acl","setuser","users","permissions","dryrun","security"]},{"id":"acl-log-denied","task":"Investigar errores NOPERM y WRONGPASS (ACL LOG)","category":"Seguridad","command":"# Last 10 permission denials and failed logins (reason: auth/command/key/channel):\nACL LOG 10\n# User of this connection:\nACL WHOAMI\n# Cumulative denial counters:\nINFO stats\n# acl_access_denied_auth, acl_access_denied_cmd, acl_access_denied_key, acl_access_denied_channel\nACL LOG RESET","what":"Lista los accesos denegados a comandos, claves y canales y los intentos AUTH fallidos con nombre de usuario, objeto, número de repeticiones e información del cliente (IP, último comando); muestra los contadores acumulados de denegaciones.","when":"Cuando la aplicación recibe NOPERM o WRONGPASS tras un despliegue, para encontrar el comando que rompió una regla ACL endurecida o para detectar intentos de adivinar contraseñas.","caution":"ACL LOG reside en memoria, se borra al reiniciar y está limitado a acllog-max-len entradas (por defecto 128); cuando el mismo error se repite, aumenta count en lugar de crear una fila nueva. El campo client-info contiene la IP del cliente y el usuario en uso; anote lo que necesite antes de reiniciarlo.","since":"Redis 7.0","note":"Los contadores acl_access_denied_* se añadieron a INFO stats en 7.2; intégrelos en el monitoreo para alertar cuando aumenten. Los campos entry-id y timestamp-created/last-updated llegaron en 7.2. Los intentos con contraseña errónea aparecen como reason=auth, y la falta de permiso de comando como reason=command.","tags":["acl log","noperm","wrongpass","access denied","auth","security"]},{"id":"keyspace-notifications","task":"Monitorear eventos de borrado y expiración de claves con keyspace notifications","category":"Mantenimiento","command":"CONFIG GET notify-keyspace-events\n# E: keyevent channel, x: expire events, g: generic commands (DEL, EXPIRE, RENAME):\nCONFIG SET notify-keyspace-events Exg\n# Listen to expire and del events in db 0:\nredis-cli -h redis1 -p 6379 --csv PSUBSCRIBE '__keyevent@0__:expired' '__keyevent@0__:del'\n# Turn off:\nCONFIG SET notify-keyspace-events \"\"","what":"Publica en canales pub/sub los eventos sobre claves (expire, del, set, etc.) para ver en vivo qué clave se borró o expiró y cuándo.","when":"Para demostrar qué comando o expiración elimina las claves cuando 'desaparecen solas', o para alimentar flujos de trabajo que dependen de eventos de expiración (cierre de sesión).","caution":"Los eventos añaden carga de CPU y de red; 'A' (todas las clases) es especialmente costoso en sistemas con carga, así que habilite solo las clases que necesite y desactívelas después. Pub/sub es fire-and-forget: un suscriptor que no está conectado pierde el evento y nada se persiste. El evento expired se genera cuando la clave se borra realmente (al acceder o por el ciclo de expiración en segundo plano), no en el momento del TTL.","since":"Redis 2.8","note":"Añada la clase e (evicted) para las claves eliminadas por eviction. En un clúster, los eventos solo se publican en el nodo que tiene la clave; suscríbase a cada master por separado. Fije notify-keyspace-events en redis.conf para un ajuste persistente.","tags":["keyspace notifications","notify-keyspace-events","expired","pubsub","events","psubscribe"]}]},"recipes/elasticsearch.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"recipes":[{"id":"flush","task":"Hacer flush de un índice","category":"Ciclo de vida de índices","command":"POST /customer/_flush","what":"Realiza un commit de Lucene: escribe de forma duradera en disco, como segmentos, los datos de indexación en memoria y recorta las entradas del translog que ya no hacen falta.","when":"Para reducir el translog antes de un mantenimiento o un reinicio.","caution":"Normalmente se hace automáticamente; un flush manual provoca un ligero aumento de IO.","tags":["flush","translog","index","customer","maintenance"]},{"id":"force-merge","task":"Fusionar segmentos de índice (force merge)","category":"Ciclo de vida de índices","command":"POST /customer/_forcemerge?max_num_segments=1","what":"Fusiona los segmentos del índice para reducir su número y mejorar el rendimiento de lectura.","when":"Solo en índices en los que ya no se escribe (solo lectura), para mejorar el rendimiento de las consultas.","caution":"Consume mucho IO y CPU; nunca lo ejecute en índices con escritura activa. Puede tardar mucho y no se puede cancelar.","tags":["forcemerge","segment","merge","merge segments","performance"]},{"id":"rollover","task":"Hacer rollover mediante un alias","category":"Ciclo de vida de índices","command":"POST /customer-alias/_rollover\n{\n  \"conditions\": {\n    \"max_age\": \"7d\",\n    \"max_docs\": 100000000,\n    \"max_primary_shard_size\": \"50gb\"\n  }\n}","what":"Cuando el índice de escritura al que apunta el alias cumple las condiciones, crea un índice nuevo y apunta el alias a él.","when":"Para gestionar el tamaño de los índices con datos de series temporales (alternativa/complemento a ILM).","caution":"El alias debe tener un único índice de escritura mediante is_write_index; una configuración errónea bloquea el rollover.","tags":["rollover","alias","index","time series","ilm"]},{"id":"set-refresh-interval","task":"Cambiar el intervalo de refresh","category":"Ciclo de vida de índices","command":"PUT /customer/_settings\n{\n  \"index\": { \"refresh_interval\": \"30s\" }\n}","what":"Fija con qué frecuencia el índice hace buscables los documentos nuevos (refresh).","when":"Al aumentar el intervalo para acelerar la indexación durante cargas masivas.","caution":"-1 deshabilita por completo el refresh; los documentos no pasan a ser buscables. Vuelva a fijar un valor razonable tras la carga.","tags":["refresh","interval","settings","bulk","refresh interval"]},{"id":"set-replicas","task":"Cambiar el número de réplicas","category":"Ciclo de vida de índices","command":"PUT /customer/_settings\n{\n  \"index\": { \"number_of_replicas\": 2 }\n}","what":"Cambia dinámicamente el número de shards réplica del índice.","when":"Para aumentar/reducir la redundancia o la capacidad de lectura.","caution":"Añadir réplicas genera uso de disco y tráfico de red (recovery) adicionales; bajar a 0 elimina la redundancia.","tags":["replica","settings","number_of_replicas","redundancy","capacity"]},{"id":"open-close-index","task":"Abrir o cerrar un índice","category":"Ciclo de vida de índices","command":"POST /customer/_close\nPOST /customer/_open","what":"Cierra el índice a lecturas/escrituras (liberando recursos) o lo vuelve a abrir.","when":"Para reducir el uso de recursos de índices poco usados o para cambiar ciertos ajustes.","caution":"Un índice cerrado no se puede consultar ni escribir; mientras está cerrado no es accesible salvo mediante snapshots.","tags":["open","close","index","open index","close index"]},{"id":"node-join-note","task":"Añadir un nodo al clúster","category":"Clúster/Nodo","command":"GET /_cat/nodes?v","what":"Basta con iniciar el nuevo nodo con el mismo cluster.name y un discovery.seed_hosts correcto en elasticsearch.yml; este comando verifica que el nodo se ha unido.","when":"Para verificar la pertenencia al añadir un nuevo nodo al clúster.","caution":"En 7.0+ no existe minimum_master_nodes; la configuración de votación se gestiona automáticamente. Añada y quite los nodos master-eligible de uno en uno, y nunca quite la mitad o más de ellos a la vez; al quitar de forma permanente más de un nodo master-eligible, use antes POST _cluster/voting_config_exclusions?node_names=....","tags":["node","join","cluster","cat","add node"]},{"id":"node-decommission","task":"Vaciar un nodo (decommission)","category":"Clúster/Nodo","command":"PUT /_cluster/settings\n{\n  \"persistent\": {\n    \"cluster.routing.allocation.exclude._name\": \"node-3\"\n  }\n}","what":"Vacía de forma segura el nodo indicado moviendo sus shards a otros nodos.","when":"Para sacar los datos de un nodo antes de retirarlo del servicio.","caution":"La reubicación de shards genera mucha carga de red/IO; asegúrese de que los nodos restantes tienen suficiente disco y capacidad. Elimine el ajuste con null una vez retirado el nodo. Los ajustes transient están obsoletos desde 7.16; use persistent.","tags":["decommission","exclude","allocation","drain","node"]},{"id":"reroute-shard","task":"Mover un shard manualmente (reroute)","category":"Clúster/Nodo","command":"POST /_cluster/reroute\n{\n  \"commands\": [\n    {\n      \"move\": {\n        \"index\": \"customer\",\n        \"shard\": 0,\n        \"from_node\": \"node-1\",\n        \"to_node\": \"node-2\"\n      }\n    }\n  ]\n}","what":"Mueve manualmente un shard concreto de un nodo a otro.","when":"Para corregir una distribución de shards desequilibrada o para vaciar un nodo concreto.","caution":"Puede entrar en conflicto con el rebalanceo automático; mover shards grandes genera carga de red y de IO.","tags":["reroute","shard","move","relocate","cluster"]},{"id":"allocation-enable","task":"Habilitar o deshabilitar la asignación de shards","category":"Clúster/Nodo","command":"PUT /_cluster/settings\n{\n  \"persistent\": {\n    \"cluster.routing.allocation.enable\": \"primaries\"\n  }\n}","what":"Restringe la asignación de shards en todo el clúster (primaries: solo se asignan primarios, none: nada) o la devuelve al valor por defecto (all / null).","when":"Antes de un reinicio rolling, para evitar reubicaciones innecesarias de réplicas (el valor recomendado oficialmente es \"primaries\").","caution":"Recuerde revertir el ajuste con null (o \"all\") después; si sigue restringido, las réplicas quedan sin asignar. Los ajustes transient están obsoletos; use persistent.","tags":["allocation","enable","restart","shard","cluster"]},{"id":"cluster-settings","task":"Cambiar un ajuste del clúster","category":"Clúster/Nodo","command":"PUT /_cluster/settings\n{\n  \"persistent\": {\n    \"indices.recovery.max_bytes_per_sec\": \"100mb\"\n  }\n}","what":"Cambia un ajuste persistent o transient de todo el clúster.","when":"Al ajustar el comportamiento del clúster, como la velocidad de recovery.","caution":"Los ajustes persistent sobreviven a los reinicios; los transient se pierden y están obsoletos desde 7.16, así que no los use. Para devolver un ajuste a su valor por defecto, fije su valor en null. Un valor erróneo afecta a la estabilidad del clúster.","tags":["cluster","settings","persistent","transient","setting"]},{"id":"snapshot-repo","task":"Registrar un repositorio de snapshots","category":"Snapshot","command":"PUT /_snapshot/backup_repo\n{\n  \"type\": \"fs\",\n  \"settings\": {\n    \"location\": \"/mnt/es_backups\"\n  }\n}","what":"Registra un repositorio donde se almacenarán los snapshots.","when":"Al configurar la infraestructura de backup por primera vez.","caution":"Para un repositorio fs, la ruta debe estar definida en path.repo y ser accesible desde todos los nodos master y de datos.","tags":["snapshot","repository","repo","backup","register"]},{"id":"snapshot-create","task":"Tomar un snapshot","category":"Snapshot","command":"PUT /_snapshot/backup_repo/snap1?wait_for_completion=false\n{\n  \"indices\": \"customer\",\n  \"include_global_state\": false\n}","what":"Toma un backup a un punto en el tiempo (snapshot) de los índices indicados en el repositorio.","when":"Para backups regulares o como backup de seguridad antes de un cambio importante.","caution":"wait_for_completion=true mantiene la conexión HTTP esperando mucho tiempo con índices grandes; use false para snapshots grandes.","tags":["snapshot","create","backup","backup snapshot","take"]},{"id":"snapshot-restore","task":"Restaurar desde un snapshot","category":"Snapshot","command":"POST /_snapshot/backup_repo/snap1/_restore\n{\n  \"indices\": \"customer\",\n  \"rename_pattern\": \"customer\",\n  \"rename_replacement\": \"customer_restored\"\n}","what":"Restaura índices de un snapshot en el clúster (opcionalmente renombrándolos).","when":"Para la recuperación tras una pérdida de datos o al llevar un backup a un entorno de pruebas.","caution":"Para restaurar con el mismo nombre, el índice de destino debe estar cerrado o eliminado; no se puede restaurar sobre un índice abierto.","tags":["snapshot","restore","restore snapshot","recovery","backup"]},{"id":"cache-clear","task":"Borrar la caché","category":"Mantenimiento","command":"POST /customer/_cache/clear","what":"Borra las cachés de query, request y fielddata del índice.","when":"Para reducir la presión de memoria o resolver problemas de fielddata.","caution":"Tras borrar la caché, las primeras consultas van más lentas hasta que vuelve a calentarse.","tags":["cache","clear","clear cache","fielddata","maintenance"]},{"id":"pending-tasks","task":"Ver las tareas pendientes del clúster","category":"Mantenimiento","command":"GET /_cluster/pending_tasks","what":"Lista las tareas a nivel de clúster que esperan ser procesadas en el nodo master.","when":"Para diagnosticar la carga del master cuando el clúster se ralentiza o las actualizaciones se atascan.","caution":"Comando de solo lectura; sin riesgo.","tags":["pending","tasks","master","diagnostics","pending tasks"]},{"id":"hot-threads","task":"Obtener un análisis de hot threads","category":"Mantenimiento","command":"GET /_nodes/hot_threads","what":"Muestra los stack traces de los hilos que más CPU consumen en los nodos.","when":"Al diagnosticar el origen de un uso alto de CPU.","caution":"Comando de solo lectura; la salida puede ser larga, acótela con el parámetro thread_type.","tags":["hotthreads","cpu","diagnostics","thread","performance"]},{"id":"cat-shards","task":"Ver la distribución de shards","category":"Mantenimiento","command":"GET /_cat/shards?v&s=state","what":"Lista todos los shards con índice, tipo (primary/replica), estado y el nodo en el que residen.","when":"Al revisar la ubicación de shards y los shards sin asignar.","caution":"Comando de solo lectura; sin riesgo.","tags":["cat","shards","distribution","state","shard"]},{"id":"allocation-explain","task":"Averiguar por qué un shard está sin asignar","category":"Mantenimiento","command":"GET /_cluster/allocation/explain\n{\n  \"index\": \"customer\",\n  \"shard\": 0,\n  \"primary\": true\n}","what":"Explica en detalle por qué no se pudo asignar un shard concreto (unassigned).","when":"Al buscar el motivo de los shards sin asignar en un clúster yellow/red.","caution":"Comando de diagnóstico de solo lectura; sin riesgo.","tags":["allocation","explain","unassigned","diagnostics","shard"]},{"id":"cluster-health","task":"Ver la salud del clúster","category":"Mantenimiento","command":"GET /_cluster/health","what":"Muestra la salud general del clúster (green/yellow/red) y el número de shards activos y sin asignar.","when":"Al comprobar rápidamente el estado del clúster.","caution":"Comando de solo lectura; sin riesgo.","tags":["cluster","health","health check","status","green"]},{"id":"refresh-index","task":"Hacer refresh manual de un índice","category":"Ciclo de vida de índices","command":"POST /customer/_refresh","what":"Hace buscables de inmediato los cambios pendientes del índice.","when":"Cuando quiere que los datos se puedan consultar justo después de una carga masiva.","caution":"Los refresh manuales frecuentes pueden producir muchos segmentos pequeños y degradar el rendimiento.","tags":["refresh","index","search","segment","customer"]}]},"recipes/elasticsearch-ext.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":2,"recipes":[{"id":"red-yellow-triage","task":"Flujo de triaje de un clúster red/yellow (qué índice, qué shard, por qué)","category":"Clúster/Nodo","command":"GET _cluster/health?filter_path=status,unassigned_shards,initializing_shards,delayed_unassigned_shards,number_of_pending_tasks\nGET _health_report?verbose=false\nGET _cat/indices?v&health=red&h=health,index,pri,rep,docs.count,store.size\nGET _cat/indices?v&health=yellow&h=health,index,pri,rep\nGET _cat/shards?v&h=index,shard,prirep,state,unassigned.reason,unassigned.for,node&s=state\n# Without a body, the call explains the first unassigned shard:\nGET _cluster/allocation/explain","what":"Ofrece un flujo de triaje ordenado desde el estado del clúster hasta los índices con problemas, luego a los shards sin asignar y al motivo de que no se asignen (unassigned.reason, allocation explain); _health_report resume qué indicador (disco, disponibilidad de shards, master) no está sano.","when":"Como primera respuesta cuando el clúster pasa a red o yellow.","caution":"Red: al menos un shard primario está sin asignar; las escrituras en ese índice y las búsquedas que tocan ese shard fallan. Yellow: todos los primarios están presentes pero faltan réplicas; los datos son accesibles pero no hay redundancia. En un clúster de un solo nodo, los índices con replicas > 0 se quedan en yellow de forma permanente (decisión same_shard). Si un nodo salió hace poco, los delayed_unassigned_shards se retienen a propósito hasta que expira index.unassigned.node_left.delayed_timeout (por defecto 1m).","since":"Elasticsearch 8.7","note":"_health_report llegó en 8.7; en versiones anteriores use los cuatro primeros pasos. Valores de unassigned.reason: NODE_LEFT (el nodo salió), ALLOCATION_FAILED (se alcanzó el límite de reintentos, vea retry-failed-allocation), INDEX_CREATED, CLUSTER_RECOVERED. Para un shard concreto, use la receta allocation-explain con un cuerpo index/shard/primary.","tags":["red","yellow","cluster health","unassigned","health report","triage"]},{"id":"retry-failed-allocation","task":"Reasignar shards que alcanzaron el límite de reintentos (reroute retry_failed)","category":"Clúster/Nodo","command":"# Attempt count and last failure details:\nGET _cluster/allocation/explain?filter_path=index,shard,unassigned_info.reason,unassigned_info.failed_allocation_attempts,unassigned_info.details,allocate_explanation\n# After fixing the cause, retry shards that hit the limit:\nPOST _cluster/reroute?retry_failed=true&metric=none\n# Recovery progress:\nGET _cat/recovery?v&active_only=true&h=index,shard,stage,source_node,target_node,bytes_percent,translog_ops_percent","what":"Muestra los detalles de fallo de los shards cuya asignación se detuvo tras fallos consecutivos (ALLOCATION_FAILED, max_retry) y vuelve a disparar la asignación; monitorea el progreso de la recuperación.","when":"Cuando allocation explain muestra 'shard has exceeded the maximum number of retries'; después de resolver un disco lleno, un translog corrupto o un problema de red transitorio.","caution":"Ejecutar retry_failed sin corregir la causa hace que el shard lo intente 5 veces más y vuelva a fallar; resuelva primero el error de unassigned_info.details. Si muchos shards entran en recuperación a la vez, sube la carga de red y de disco; la limitan cluster.routing.allocation.node_concurrent_recoveries e indices.recovery.max_bytes_per_sec.","since":"Elasticsearch 7.0","note":"El límite de reintentos es index.allocation.max_retries (por defecto 5). Elasticsearch también reintenta periódicamente, pero retry_failed elimina la espera. metric=none suprime el gran cluster state en la respuesta.","tags":["reroute","retry_failed","allocation failed","max_retries","unassigned","recovery"]},{"id":"allocate-stale-primary","task":"Asignar un shard primario perdido como último recurso (allocate_stale_primary)","category":"Clúster/Nodo","command":"# Which nodes hold copies of the primary (allocation id, store errors):\nGET _shard_stores/musteri?status=red\n# Last resort: promote a stale copy to primary; data loss is accepted:\nPOST _cluster/reroute\n{\n  \"commands\": [\n    {\n      \"allocate_stale_primary\": {\n        \"index\": \"musteri\",\n        \"shard\": 0,\n        \"node\": \"node-2\",\n        \"accept_data_loss\": true\n      }\n    }\n  ]\n}","what":"Cuando se pierde de forma permanente el nodo con la copia primaria actualizada, asigna como primario una copia más antigua (no in-sync) en otro nodo para sacar el índice de red.","when":"Solo cuando el nodo que aloja el primario no puede recuperarse y restaurar desde un snapshot no es una opción o tardaría más.","caution":"La copia antigua no contiene las últimas escrituras; todas las escrituras posteriores a esa copia se pierden de forma permanente. Si el nodo perdido vuelve después, la copia más reciente que contiene se borra. Considere primero recuperar el nodo y restaurar desde snapshot. allocate_empty_primary crea el shard completamente vacío y borra todos los datos de ese shard; úselo solo cuando no quede ninguna copia.","since":"Elasticsearch 7.0","note":"En la salida de _shard_stores el tipo de asignación aparece como 'primary', 'replica' o 'unused'; no elija copias con store_exception. Puede ejecutar primero el comando con ?dry_run=true&explain=true para ver las decisiones. Tras la operación, cierre el hueco de datos reindexando desde el lado de la aplicación.","tags":["allocate_stale_primary","red","primary","data loss","shard_stores","reroute"],"untested":true},{"id":"disk-watermark","task":"Gestionar los disk watermarks y quitar el bloqueo de escritura de flood-stage","category":"Clúster/Nodo","command":"GET _cat/allocation?v&h=node,shards,disk.indices,disk.used,disk.avail,disk.percent&s=disk.percent:desc\nGET _cluster/settings?include_defaults=true&filter_path=*.cluster.routing.allocation.disk\n# Adjust thresholds temporarily (null returns to the default):\nPUT _cluster/settings\n{\n  \"persistent\": {\n    \"cluster.routing.allocation.disk.watermark.low\": \"85%\",\n    \"cluster.routing.allocation.disk.watermark.high\": \"90%\",\n    \"cluster.routing.allocation.disk.watermark.flood_stage\": \"95%\"\n  }\n}\n# After freeing space, remove the flood-stage write block:\nPUT _all/_settings\n{\n  \"index.blocks.read_only_allow_delete\": null\n}","what":"Muestra el uso de disco por nodo y los valores de watermark efectivos; ajusta los umbrales low/high/flood_stage y quita el bloqueo read_only_allow_delete puesto en los índices en flood stage.","when":"Cuando las escrituras se rechazan con 'TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark, index has read-only-allow-delete block', no se asignan shards nuevos o los shards empiezan a salir de un nodo lleno.","caution":"Por encima de low no se asignan shards nuevos al nodo; por encima de high los shards se mueven fuera del nodo; por encima de flood_stage, los índices con un shard en ese nodo pasan a solo lectura. Subir los umbrales solo gana tiempo; la solución duradera es borrar índices antiguos, reducir réplicas o añadir disco/nodos. Si el disco se llena por completo, los shards pueden corromperse.","since":"Elasticsearch 8.5","note":"En 7.4+ el bloqueo se quita automáticamente cuando el uso de disco baja del high watermark; quitarlo manualmente acorta la espera. En 8.5+ los discos grandes usan max_headroom (low 200GB, high 150GB, flood 100GB) como espacio libre absoluto en lugar del porcentaje; estos headrooms por defecto solo se aplican mientras los watermarks se dejan en sus valores por defecto, por lo que fijar porcentajes explícitamente como arriba los deshabilita. Los watermarks también se aplican en clústeres de un solo nodo de datos.","tags":["disk watermark","flood_stage","read_only_allow_delete","disk full","allocation","cat allocation"]},{"id":"rolling-restart","task":"Reinicio rolling / actualización rolling (nodo a nodo, sin movimientos de shards innecesarios)","category":"Clúster/Nodo","command":"# 1) Disable replica allocation (only primaries are allocated):\nPUT _cluster/settings\n{\n  \"persistent\": { \"cluster.routing.allocation.enable\": \"primaries\" }\n}\n# 2) Optional: stop indexing and flush to speed up recovery:\nPOST _flush\n# 3) Stop one node, do the maintenance, start it; verify it joined:\nGET _cat/nodes?v&h=name,version,node.role,master,heap.percent\n# 4) Re-enable allocation:\nPUT _cluster/settings\n{\n  \"persistent\": { \"cluster.routing.allocation.enable\": null }\n}\n# 5) Wait for green before moving to the next node:\nGET _cluster/health?wait_for_status=green&timeout=10m","what":"Indica el orden para reiniciar o actualizar los nodos de uno en uno evitando que las réplicas se vuelvan a copiar a otros nodos mientras un nodo está caído; cuando el nodo vuelve, los shards se recuperan rápidamente desde la copia local.","when":"Al reiniciar nodos en secuencia por un parche del SO, un cambio de JVM/heap, un cambio en elasticsearch.yml o una actualización de versión menor/mayor.","caution":"No pase al siguiente nodo sin esperar a green; con dos nodos caídos a la vez, pueden faltar todas las copias de un shard. Reinicie los nodos master-eligible al final y de uno en uno; si se pierde la mayoría, el clúster no puede elegir master. Si olvida volver a habilitar allocation.enable, las réplicas quedan sin asignar. Durante una actualización se actualizan primero los nodos de datos (primero frozen/cold, hot al final) y luego los masters.","since":"Elasticsearch 7.16","note":"Si hay jobs de ML, ejecute POST _ml/set_upgrade_mode?enabled=true al principio y enabled=false al final. El synced flush se eliminó en 8.0; basta con un _flush normal. Use persistent, ya que los ajustes transient están obsoletos desde 7.16. Para reinicios cortos, también funciona subir index.unassigned.node_left.delayed_timeout en lugar de deshabilitar la asignación.","tags":["rolling restart","rolling upgrade","allocation","primaries","maintenance","upgrade"],"untested":true},{"id":"shard-sizing","task":"Auditar el tamaño y el número de shards (detección de oversharding / shards demasiado grandes)","category":"Mantenimiento","command":"# Largest shards (GB):\nGET _cat/shards?v&h=index,shard,prirep,state,docs,store,node&s=store:desc&bytes=gb\n# Indices by primary size:\nGET _cat/indices?v&h=index,pri,rep,docs.count,pri.store.size,store.size&s=pri.store.size:desc&bytes=gb\n# Shard count and disk per node:\nGET _cat/allocation?v&h=node,shards,disk.indices,disk.percent&s=shards:desc\nGET _cluster/stats?filter_path=indices.shards.total,indices.shards.primaries,nodes.count.data\nGET _cluster/settings?include_defaults=true&filter_path=*.cluster.max_shards_per_node","what":"Lista los shards más grandes, el tamaño primario de los índices, el número de shards por nodo, el número total de shards y el límite cluster.max_shards_per_node.","when":"Durante la planificación de capacidad, cuando crecen los tiempos de recuperación o sube la presión del heap, y ante el error 'this action would add [x] shards, but this cluster currently has [y]/[z] maximum normal shards open'.","caution":"Muchos shards muy pequeños (oversharding) castigan al master y al heap; los shards muy grandes alargan la recuperación y el rebalanceo hasta horas. El número de shards primarios no se puede cambiar tras crear el índice; para corregirlo hace falta shrink, split o reindex. Subir max_shards_per_node elimina el límite pero no resuelve el problema.","since":"Elasticsearch 8.3","note":"Recomendación general de Elastic: 10-50 GB por shard y como máximo unos 200 millones de documentos. En 8.3+ la antigua regla de '20 shards por GB de heap' se sustituyó por una estimación de heap basada en el número de campos mapeados. Para series temporales, fije el tamaño de shard con max_primary_shard_size en el rollover.","tags":["shard size","oversharding","cat shards","max_shards_per_node","capacity","sizing"]},{"id":"shrink-index","task":"Reducir (shrink) un índice con demasiados shards","category":"Ciclo de vida de índices","command":"# 1) Block writes on the source and gather all shard copies on one node:\nPUT log-2026.09/_settings\n{\n  \"index.number_of_replicas\": 0,\n  \"index.routing.allocation.require._name\": \"node-1\",\n  \"index.blocks.write\": true\n}\nGET _cluster/health/log-2026.09?wait_for_no_relocating_shards=true&timeout=30m\n# 2) Create a new index with fewer shards (the target must be a factor of the source):\nPOST log-2026.09/_shrink/log-2026.09-shrunk\n{\n  \"settings\": {\n    \"index.number_of_shards\": 1,\n    \"index.number_of_replicas\": 1,\n    \"index.routing.allocation.require._name\": null,\n    \"index.blocks.write\": null,\n    \"index.codec\": \"best_compression\"\n  }\n}","what":"Reduce el número de shards primarios de un índice en el que ya no se escribe creando hard links de sus segmentos en un índice nuevo; reduce la sobrecarga de heap y de número de shards.","when":"Cuando los índices diarios se crearon con más shards de los necesarios, el límite de shards está cerca o antes de mover índices antiguos al tier warm.","caution":"El índice de origen queda cerrado a escrituras durante el shrink; aplíquelo solo a índices en los que ya no se escribe. El número de shards de destino debe ser un divisor del de origen (8 -> 4, 2 o 1). El nodo donde se reúnen todas las copias necesita tanto disco libre como el índice de origen. Con replicas 0 el shard de origen tiene una sola copia; no borre el origen hasta que termine la operación y el nuevo índice esté en green.","since":"Elasticsearch 7.0","note":"Tras el shrink, borre el origen o cambie el alias al nuevo índice. La operación suele ser rápida porque el mismo nodo puede crear hard links de los archivos en el mismo sistema de archivos; de lo contrario los archivos se copian. Use _split para aumentar el número de shards, y la acción shrink en la fase warm para automatizarlo con ILM.","tags":["shrink","shard count","oversharding","index","blocks.write","warm"]},{"id":"ilm-policy-bootstrap","task":"Definir una política ILM, asociarla mediante un template e iniciar la cadena de rollover","category":"Ciclo de vida de índices","command":"PUT _ilm/policy/log-policy\n{\n  \"policy\": {\n    \"phases\": {\n      \"hot\": { \"actions\": { \"rollover\": { \"max_primary_shard_size\": \"50gb\", \"max_age\": \"1d\" }, \"set_priority\": { \"priority\": 100 } } },\n      \"warm\": { \"min_age\": \"7d\", \"actions\": { \"shrink\": { \"number_of_shards\": 1 }, \"forcemerge\": { \"max_num_segments\": 1 }, \"set_priority\": { \"priority\": 50 } } },\n      \"delete\": { \"min_age\": \"30d\", \"actions\": { \"delete\": {} } }\n    }\n  }\n}\nPUT _index_template/log\n{\n  \"index_patterns\": [\"log-*\"],\n  \"priority\": 500,\n  \"template\": {\n    \"settings\": {\n      \"number_of_shards\": 1,\n      \"number_of_replicas\": 1,\n      \"index.lifecycle.name\": \"log-policy\",\n      \"index.lifecycle.rollover_alias\": \"log\"\n    }\n  }\n}\n# Create the first index with the write alias (its name must end with a number such as -000001):\nPUT log-000001\n{\n  \"aliases\": { \"log\": { \"is_write_index\": true } }\n}","what":"Crea una política ILM con fases hot (rollover), warm (shrink + force merge) y delete, aplica la política y el alias de rollover a los índices nuevos mediante un template y crea el primer índice de la cadena con el alias de escritura.","when":"Al automatizar el tamaño, la retención y el borrado de índices para datos de series temporales como logs o métricas.","caution":"min_age en las fases warm y delete se cuenta desde el momento del rollover, no desde la creación del índice. Los cambios de política se aplican a los índices existentes solo cuando termina su fase actual. Shrink y forcemerge en la fase warm generan mucho IO; no se debe escribir en el índice hasta que termine el force merge. ILM se ejecuta cada 10 minutos por defecto, así que las condiciones no se evalúan al instante.","since":"Elasticsearch 7.8","note":"Para proyectos nuevos, un data stream consigue lo mismo de forma más sencilla: añada \"data_stream\": {} al index template y no hace falta alias de rollover ni índice inicial. Las condiciones de rollover se evalúan sobre estadísticas refrescadas. Los composable index templates (_index_template) llegaron en 7.8; la API heredada _template está obsoleta.","tags":["ilm","lifecycle","rollover","policy","index template","retention"]},{"id":"ilm-explain-retry","task":"Diagnosticar un paso ILM atascado y reintentarlo (_ilm/explain, _ilm/retry)","category":"Ciclo de vida de índices","command":"# Indices waiting in an error step:\nGET log-*/_ilm/explain?only_errors=true\n# Phase/action/step state and failure reason for one index:\nGET log-000003/_ilm/explain?filter_path=indices.*.phase,indices.*.action,indices.*.step,indices.*.failed_step,indices.*.step_info,indices.*.is_auto_retryable_error\n# After fixing the cause (example: missing rollover alias setting):\nPUT log-000003/_settings\n{\n  \"index.lifecycle.rollover_alias\": \"log\"\n}\nPOST log-000003/_ilm/retry\n# Is ILM running (if STOPPED, POST _ilm/start):\nGET _ilm/status","what":"Muestra los índices que esperan en el paso ERROR, en qué fase/acción/paso están atascados y el motivo del fallo (step_info); vuelve a ejecutar el paso fallido una vez corregida la causa.","when":"Cuando no se produce el rollover, no se borran los índices antiguos o el disco se llena con índices que ILM debería haber eliminado.","caution":"_ilm/retry solo funciona para índices en el paso ERROR; de lo contrario devuelve 'has not encountered an error'. Causas comunes: falta el ajuste rollover_alias o el alias tiene is_write_index en otro índice, no hay disco suficiente en un solo nodo para el shrink, una regla de asignación (_tier_preference) no puede cumplirse. Borrar la política no quita la gestión de ILM de los índices; quite primero el ajuste de política de los índices.","since":"Elasticsearch 7.0","note":"Los errores con is_auto_retryable_error true los reintenta ILM por su cuenta (failed_step_retry_count crece). El intervalo de comprobación de ILM lo fija indices.lifecycle.poll_interval (por defecto 10m). Use POST <index>/_ilm/remove para sacar un índice de ILM.","tags":["ilm","explain","retry","error step","rollover","lifecycle"]},{"id":"index-template-component","task":"Construir un index template a partir de component templates y simular el resultado","category":"Ciclo de vida de índices","command":"PUT _component_template/app-mappings\n{\n  \"template\": {\n    \"mappings\": {\n      \"dynamic\": \"strict\",\n      \"properties\": {\n        \"@timestamp\": { \"type\": \"date\" },\n        \"level\": { \"type\": \"keyword\" },\n        \"msg\": { \"type\": \"text\" }\n      }\n    }\n  }\n}\nPUT _index_template/app-events\n{\n  \"index_patterns\": [\"app-events-*\"],\n  \"priority\": 500,\n  \"composed_of\": [\"app-mappings\"],\n  \"template\": { \"settings\": { \"number_of_shards\": 1, \"refresh_interval\": \"5s\" } }\n}\n# See the final settings and mapping before the index is created:\nPOST _index_template/_simulate_index/app-events-000001\nGET _cat/templates?v&s=name","what":"Define un component template de mapping reutilizable, lo compone en un index template y muestra los settings y el mapping finales que se aplicarían a un nombre de índice dado sin crear el índice.","when":"Cuando los índices nuevos se crean con el mapping o el número de shards equivocados (explosión de dynamic mapping, text en lugar de keyword) y antes de desplegar un cambio de template.","caution":"Un template solo se aplica a los índices recién creados; el mapping de los índices existentes no cambia (vea reindex-alias-swap). Si dos templates coinciden con el mismo patrón, solo se usa el de mayor prioridad; no se fusionan. dynamic: strict rechaza los documentos que contienen campos no definidos; si la aplicación envía un campo nuevo, la indexación falla.","since":"Elasticsearch 7.8","note":"El campo overlapping en la salida de _simulate_index muestra otros templates que coinciden con el mismo patrón y quedan anulados. Mantenga priority por encima de 200 para no chocar con los templates integrados (logs-*-*, metrics-*-*). Los composable templates tienen prioridad sobre la API heredada _template.","tags":["index template","component template","simulate","mapping","priority","dynamic strict"]},{"id":"reindex-alias-swap","task":"Cambiar un mapping sin tiempo de inactividad (reindex + cambio atómico de alias)","category":"Ciclo de vida de índices","command":"# 1) Target index with the new mapping (refresh off and replicas 0 for loading):\nPUT musteri-v2\n{\n  \"settings\": { \"number_of_shards\": 1, \"number_of_replicas\": 0, \"refresh_interval\": \"-1\" },\n  \"mappings\": { \"properties\": { \"kod\": { \"type\": \"keyword\" }, \"adet\": { \"type\": \"long\" } } }\n}\n# 2) Reindex in the background (tracked with the returned task id):\nPOST _reindex?wait_for_completion=false&slices=auto\n{\n  \"source\": { \"index\": \"musteri-v1\", \"size\": 1000 },\n  \"dest\": { \"index\": \"musteri-v2\", \"op_type\": \"create\" },\n  \"conflicts\": \"proceed\"\n}\nGET _tasks/<task_id>\n# 3) Restore settings and compare document counts:\nPUT musteri-v2/_settings\n{\n  \"index\": { \"refresh_interval\": null, \"number_of_replicas\": 1 }\n}\nPOST musteri-v2/_refresh\nGET _cat/indices/musteri-v1,musteri-v2?v&h=index,docs.count,pri.store.size\n# 4) Switch the alias to the new index in one atomic operation:\nPOST _aliases\n{\n  \"actions\": [\n    { \"remove\": { \"index\": \"musteri-v1\", \"alias\": \"musteri\" } },\n    { \"add\": { \"index\": \"musteri-v2\", \"alias\": \"musteri\", \"is_write_index\": true } }\n  ]\n}","what":"Crea un índice con el nuevo mapping, copia los datos en segundo plano con reindex, compara el número de documentos y cambia en una sola operación atómica el alias que usa la aplicación al nuevo índice.","when":"Cuando hay que cambiar el tipo de un campo existente (text -> keyword, integer -> long), cambiar un analyzer o redefinir el número de shards; esto no se puede hacer en el sitio sobre el índice existente.","caution":"La aplicación debe conectarse al alias, no al nombre del índice; de lo contrario el cambio requiere un despliegue. Las escrituras nuevas en el origen durante el reindex no llegan al destino: detenga las escrituras o vuelva a ejecutar el reindex con op_type create + conflicts proceed justo antes del cambio para cerrar el hueco (las actualizaciones y los borrados no se trasladan de este modo). Reindex genera carga de lectura en el origen y de escritura en el destino; limítelo con requests_per_second.","since":"Elasticsearch 7.0","note":"Los documentos cargados mientras refresh_interval es -1 no aparecen en los recuentos hasta un refresh; ejecute _refresh antes de comparar. Si _cat/count recibe varios índices separados por comas, devuelve su total; use _cat/indices para una comparación por índice. La velocidad de un reindex en ejecución se puede cambiar con POST _reindex/<task_id>/_rethrottle?requests_per_second=500. wait_for_completion=false escribe el resultado en el índice .tasks; compruebe response.failures con GET _tasks/<task_id>.","tags":["reindex","alias","mapping change","zero downtime","_aliases","no downtime"]},{"id":"tasks-cancel","task":"Encontrar, cancelar o limitar tareas de búsqueda y reindex de larga duración","category":"Mantenimiento","command":"# Longest running tasks:\nGET _cat/tasks?v&detailed&h=action,task_id,parent_task_id,running_time,node,description&s=running_time:desc\n# Search, reindex and by-query tasks, grouped by parent:\nGET _tasks?actions=*search*,*reindex*,*byquery*&detailed=true&group_by=parents\n# Cancel a single task:\nPOST _tasks/<task_id>/_cancel\n# Cancel all search tasks (with care):\nPOST _tasks/_cancel?actions=*search*\n# Slow down a running reindex/update_by_query:\nPOST _reindex/<task_id>/_rethrottle?requests_per_second=500","what":"Ordena por duración las tareas en ejecución en el clúster, muestra las búsquedas y operaciones bulk con la descripción de su consulta; cancela una tarea seleccionada o todas las búsquedas y limita los jobs de reindex/by-query.","when":"Cuando se llena el thread pool de search, la CPU sube de repente, un dashboard castiga el clúster con una agregación pesada o un update_by_query afecta a producción.","caution":"La cancelación es cooperativa: la tarea se detiene en su siguiente punto de control; algunas fases (p. ej. force merge) no se pueden cancelar. Cancelar todas las búsquedas devuelve errores a los usuarios. Un reindex/by-query cancelado no revierte los cambios hechos hasta entonces; el destino contiene datos parciales. Cuando se cancela una tarea padre, también se cancelan sus tareas hijas.","since":"Elasticsearch 7.0","note":"El parámetro detailed escribe el cuerpo de la consulta de las tareas de búsqueda en el campo description. Para una protección duradera use search.default_search_timeout, un timeout en la petición de búsqueda y search.max_buckets para agregaciones grandes. En 7.x+ las búsquedas cuya conexión HTTP se cierra se cancelan automáticamente.","tags":["tasks","cancel","kill query","rethrottle","long query","search"]},{"id":"search-slowlog","task":"Habilitar el slow log de búsqueda e indexación (umbrales por índice)","category":"Rendimiento","command":"PUT musteri/_settings\n{\n  \"index.search.slowlog.threshold.query.warn\": \"2s\",\n  \"index.search.slowlog.threshold.query.info\": \"500ms\",\n  \"index.search.slowlog.threshold.fetch.warn\": \"1s\",\n  \"index.indexing.slowlog.threshold.index.warn\": \"1s\",\n  \"index.indexing.slowlog.source\": \"200\"\n}\nGET musteri/_settings/index.*slowlog*?flat_settings=true\n# Set thresholds to null to turn it off:\nPUT musteri/_settings\n{\n  \"index.search.slowlog.threshold.query.warn\": null,\n  \"index.search.slowlog.threshold.query.info\": null,\n  \"index.search.slowlog.threshold.fetch.warn\": null,\n  \"index.indexing.slowlog.threshold.index.warn\": null,\n  \"index.indexing.slowlog.source\": null\n}","what":"Para un índice dado, escribe en el slow log las peticiones que superan el umbral en las fases query y fetch y en la indexación, junto con el cuerpo de la consulta.","when":"Para encontrar qué consulta causa búsquedas lentas, o para atrapar consultas pesadas de un dashboard o cliente concreto.","caution":"El umbral se mide por shard: en un índice con muchos shards, cada shard puede escribir su propia línea. Un umbral muy bajo (0ms) aumenta mucho el volumen de log y el IO; desactívelo con null tras el diagnóstico. indexing.slowlog.source escribe en el log el source del documento; fíjelo en false si contiene datos sensibles. El ajuste solo se aplica al índice indicado; añádalo al template para los índices nuevos.","since":"Elasticsearch 8.0","note":"Los archivos de slow log se escriben en el directorio de logs como <cluster>_index_search_slowlog.json y <cluster>_index_indexing_slowlog.json; si log4j2 se dirige a la consola, van a stdout. El ajuste index.search.slowlog.level se eliminó en 8.0; el nivel lo determinan los umbrales (warn/info/debug/trace). En 8.14+, index.search.slowlog.include.user: true también escribe en el log el usuario que hizo la petición. Para inspeccionar en detalle una sola consulta, añada \"profile\": true a la petición de búsqueda.","tags":["slowlog","slow log","slow search","search","indexing","threshold"]},{"id":"rejections-heap-breaker","task":"Diagnosticar rechazos 429 y la presión de heap y circuit breakers","category":"Rendimiento","command":"# Thread pool queue and rejection counts (source of 429):\nGET _cat/thread_pool/write,search,get?v&h=node_name,name,active,queue,rejected,completed,size,queue_size&s=rejected:desc\nGET _cat/nodes?v&h=name,node.role,heap.percent,ram.percent,cpu,load_1m,disk.used_percent,master&s=heap.percent:desc\n# Circuit breaker trips and parent breaker usage:\nGET _nodes/stats/breaker?filter_path=nodes.*.name,nodes.*.breakers.*.tripped,nodes.*.breakers.parent\nGET _nodes/stats/jvm?filter_path=nodes.*.name,nodes.*.jvm.mem.heap_used_percent,nodes.*.jvm.gc.collectors.old\n# Fielddata memory usage (per field):\nGET _nodes/stats/indices/fielddata?fields=*&filter_path=nodes.*.indices.fielddata","what":"Muestra las peticiones rechazadas porque las colas de los thread pools write/search estaban llenas, el estado de heap/CPU por nodo, los disparos de circuit breakers, el número de old GC y el uso de memoria de fielddata.","when":"Cuando los clientes reciben 429 Too Many Requests o es_rejected_execution_exception, aparece CircuitBreakingException ([parent] Data too large) o los nodos sufren pausas de GC largas.","caution":"Los contadores rejected son acumulados desde el arranque del nodo; mire el aumento entre dos muestras. Subir queue_size oculta los rechazos pero aumenta el heap y la latencia; la solución real es reducir el tamaño/la concurrencia de bulk o añadir capacidad. Si el heap se mantiene por encima del 85% y el old GC se ejecuta a menudo, el nodo se acerca a un OOM. Habilitar fielddata en un campo text consume heap rápidamente.","since":"Elasticsearch 7.0","note":"El parent breaker vale por defecto el 95% del heap (real memory circuit breaker, 7.0+). Los rechazos de escritura suelen concentrarse en un único nodo caliente: compruebe la distribución de shards y el routing (_routing). Para liberar fielddata temporalmente, use POST _cache/clear?fielddata=true (vea cache-clear).","tags":["429","rejected","thread pool","circuit breaker","heap","fielddata"]},{"id":"slm-snapshot-monitor","task":"Automatizar snapshots y retención con SLM, monitorear el progreso de los snapshots","category":"Snapshot","command":"# Take a snapshot every night at 01:30, keep for 30 days (at least 7, at most 60 snapshots):\nPUT _slm/policy/gece-yedek\n{\n  \"schedule\": \"0 30 1 * * ?\",\n  \"name\": \"<gece-{now/d}>\",\n  \"repository\": \"yedek_repo\",\n  \"config\": { \"indices\": [\"*\"], \"include_global_state\": true },\n  \"retention\": { \"expire_after\": \"30d\", \"min_count\": 7, \"max_count\": 60 }\n}\n# Run the policy now and see success/failure statistics:\nPOST _slm/policy/gece-yedek/_execute\nGET _slm/stats\n# Snapshot in progress and per-shard progress:\nGET _snapshot/yedek_repo/_current\nGET _snapshot/yedek_repo/_status\nGET _cat/snapshots/yedek_repo?v&s=end_epoch:desc\n# Verify that the repository is writable from all nodes:\nPOST _snapshot/yedek_repo/_verify","what":"Define una política SLM que toma snapshots según una programación cron y borra los antiguos según una regla de retención; muestra el progreso por shard de un snapshot en ejecución, los snapshots pasados y la accesibilidad del repositorio.","when":"Al configurar backups regulares, ante una alerta de que el snapshot nocturno falló o al estimar cuándo terminará un snapshot de larga duración.","caution":"La programación se evalúa en UTC. Si más de un clúster escribe en el mismo repositorio, o el repositorio está registrado como escribible en otro clúster, puede corromperse; use readonly: true en los demás clústeres. La retención solo borra los snapshots tomados por SLM. include_global_state true también puede restaurar templates y ajustes persistentes del clúster durante una restauración.","since":"Elasticsearch 7.5","note":"Los snapshots son incrementales: solo se copian los segmentos que faltan respecto al snapshot anterior, por lo que los snapshots frecuentes no ocupan un espacio proporcional en el repositorio. SLM llegó en 7.4 y la retención en 7.5. Sin nombre de repositorio, _status devuelve todos los snapshots en ejecución del clúster; con el nombre de un snapshot pasado lee del repositorio y puede ser lento.","tags":["slm","snapshot","retention","backup","schedule","snapshot status"],"untested":true},{"id":"security-user-role","task":"Crear un rol y un usuario nativo, cambiar la contraseña, emitir una API key","category":"Seguridad","command":"POST _security/role/log_okuma\n{\n  \"cluster\": [\"monitor\"],\n  \"indices\": [\n    { \"names\": [\"log-*\"], \"privileges\": [\"read\", \"view_index_metadata\"] }\n  ]\n}\nPOST _security/user/rapor_kullanici\n{\n  \"password\": \"Degistir-123!\",\n  \"roles\": [\"log_okuma\"],\n  \"full_name\": \"Rapor Servisi\"\n}\nGET _security/user/rapor_kullanici\n# User making this request and its roles:\nGET _security/_authenticate\nPOST _security/user/rapor_kullanici/_password\n{\n  \"password\": \"Yeni-Sifre-456!\"\n}\n# Time-limited API key with restricted privileges:\nPOST _security/api_key\n{\n  \"name\": \"rapor-key\",\n  \"expiration\": \"30d\",\n  \"role_descriptors\": { \"log_okuma\": { \"indices\": [ { \"names\": [\"log-*\"], \"privileges\": [\"read\"] } ] } }\n}\n# If the elastic user's password is lost (shell on a node):\n# bin/elasticsearch-reset-password -u elastic -i","what":"Crea un rol con acceso de lectura sobre un patrón de índices y un usuario del native realm con ese rol, cambia la contraseña, verifica la identidad de quien llama y emite una API key de duración limitada con privilegios acotados.","when":"Al crear cuentas de informes, aplicación o monitoreo con mínimo privilegio, durante la rotación de contraseñas y al pasar los servicios de usuarios a API keys.","caution":"No use el superusuario elastic en las aplicaciones. Los privilegios de una API key no pueden superar los del usuario que la creó; role_descriptors solo los acota. Una API key queda limitada por una instantánea de los privilegios de su propietario en el momento de la creación; los cambios de roles posteriores no afectan a las claves existentes. Tras un cambio de contraseña, los servicios que usan la contraseña antigua reciben 401; planifique el cambio. El valor encoded de la respuesta de la API key se devuelve solo una vez; guárdelo.","since":"Elasticsearch 8.0","note":"La seguridad está habilitada por defecto en 8.0+ y la contraseña de elastic se genera en el primer arranque. La herramienta elasticsearch-reset-password llegó en 8.0. Liste las API keys con GET _security/api_key?name=rapor-key y revóquelas con DELETE _security/api_key. El role mapping para usuarios de realms LDAP/AD/SAML se hace con _security/role_mapping.","tags":["security","users","roles","api key","password","rbac"],"untested":true}]},"solver/slow-query.json":{"id":"slow-query","title":"Consulta lenta: diagnóstico en profundidad","icon":"🐢","category":"Rendimiento","intro":"Una consulta o todo el sistema va lento. Acótelo paso a paso: ejecute una comprobación en cada nodo y elija la rama según el resultado. Termina con una causa raíz + solución.","start":"scope","nodes":{"scope":{"kind":"step","title":"¿Qué va lento?","detail":"¿Va lenta una o unas pocas consultas concretas, o todo el sistema? Esto divide la investigación en una de dos vías: 'centrada en la consulta' o 'centrada en recursos/cuello de botella'.","choices":[{"label":"Una o unas pocas consultas concretas van lentas","next":"running"},{"label":"Todo el sistema va lento","next":"system"}]},"running":{"kind":"step","title":"¿Se está ejecutando la consulta ahora mismo?","detail":"¿Es una consulta que se está ejecutando/atascada ahora mismo, o una que va 'lenta en general'? Mire las sesiones activas.","command":{"oracle":"SELECT sid, serial#, sql_id, last_call_et AS running_sec, event, blocking_session\nFROM v$session WHERE status='ACTIVE' AND type='USER' ORDER BY last_call_et DESC;","postgres":"SELECT pid, now()-query_start AS dur, state, wait_event, query\nFROM pg_stat_activity WHERE state='active' ORDER BY query_start;","mysql":"SELECT id, time, state, info FROM information_schema.processlist WHERE command='Query' ORDER BY time DESC;","mssql":"SELECT session_id, total_elapsed_time/1000 AS sec, status, wait_type, blocking_session_id\nFROM sys.dm_exec_requests WHERE session_id IN (SELECT session_id FROM sys.dm_exec_sessions WHERE is_user_process = 1) ORDER BY total_elapsed_time DESC;"},"choices":[{"label":"Ejecutándose / atascada ahora mismo","next":"blocked"},{"label":"Lenta en general; quiero ver su plan","next":"plan"}]},"blocked":{"kind":"step","title":"¿Está esperando algo la consulta?","detail":"Si está activa pero no avanza, lo más probable es que esté esperando. ¿Está informado blocking_session / wait_event?","command":{"oracle":"SELECT sid, blocking_session, event, seconds_in_wait\nFROM v$session WHERE sid=&target_sid;","postgres":"SELECT pid, pg_blocking_pids(pid) AS blocked_by, wait_event_type, wait_event\nFROM pg_stat_activity WHERE pid=&target_pid;","mssql":"SELECT session_id, blocking_session_id, wait_type, wait_resource\nFROM sys.dm_exec_requests WHERE session_id=&target;"},"choices":[{"label":"Bloqueada por otra sesión","next":"sol_blocking"},{"label":"Esperando un recurso (IO / log / latch)","next":"wait"},{"label":"Sin espera; consumiendo CPU activamente","next":"plan"}]},"wait":{"kind":"step","title":"¿Qué espera domina?","detail":"El wait event dominante indica el tipo de cuello de botella.","command":{"oracle":"SELECT event, total_waits, time_waited FROM v$session_event WHERE sid=&target_sid ORDER BY time_waited DESC FETCH FIRST 5 ROWS ONLY;","postgres":"SELECT wait_event_type, wait_event, count(*) FROM pg_stat_activity WHERE state='active' GROUP BY 1,2 ORDER BY 3 DESC;","mssql":"SELECT TOP 5 wait_type, waiting_tasks_count, wait_time_ms FROM sys.dm_os_wait_stats ORDER BY wait_time_ms DESC;"},"choices":[{"label":"Espera de IO (db file read / PAGEIOLATCH)","next":"sol_io"},{"label":"Espera de log / commit (log file sync / WRITELOG)","next":"sol_log"},{"label":"Bloqueo / enqueue (enq: TX / LCK)","next":"sol_blocking"}]},"plan":{"kind":"step","title":"Obtener el plan de ejecución (EXPLAIN)","detail":"Extraiga el plan. ¿Hay un full table scan, un orden de join inesperado o una diferencia entre filas estimadas y reales?","command":{"oracle":"SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id', NULL, 'ALLSTATS LAST'));","postgres":"EXPLAIN (ANALYZE, BUFFERS) <query>;","mysql":"EXPLAIN ANALYZE <query>;","mssql":"SET STATISTICS PROFILE ON; <query>; SET STATISTICS PROFILE OFF;","mongodb":"db.collection.find({...}).explain('executionStats')  // COLLSCAN or IXSCAN?"},"choices":[{"label":"Full scan / índice faltante","next":"sol_index"},{"label":"Ayer iba rápida; el plan cambió de repente","next":"sol_planchange"},{"label":"Procesa demasiadas filas / N+1","next":"sol_rewrite"},{"label":"El plan parece bueno pero va lenta","next":"stats"}]},"stats":{"kind":"step","title":"¿Están actualizadas las estadísticas?","detail":"Una gran diferencia entre filas estimadas y reales (E-rows vs A-rows) es señal de estadísticas obsoletas.","command":{"oracle":"SELECT table_name, num_rows, last_analyzed FROM dba_tables WHERE table_name='&TABLE_NAME';","postgres":"SELECT relname, last_analyze, last_autoanalyze, n_live_tup FROM pg_stat_user_tables WHERE relname='&table_name';","mysql":"SELECT table_name, table_rows, update_time FROM information_schema.tables WHERE table_name='&table_name';","mssql":"SELECT name, STATS_DATE(object_id, stats_id) AS last_updated FROM sys.stats WHERE object_id=OBJECT_ID('&table_name');"},"choices":[{"label":"Las estadísticas están obsoletas / son antiguas","next":"sol_stats"},{"label":"Las estadísticas están al día pero sigue lenta","next":"sol_deep"}]},"system":{"kind":"step","title":"Todo el sistema: ¿dónde está el cuello de botella?","detail":"Si va todo lento y no una sola consulta, busque un cuello de botella de recursos: ¿CPU, IO, memoria o bloqueos generalizados?","command":{"oracle":"SELECT metric_name, value FROM v$sysmetric WHERE metric_name IN ('Host CPU Utilization (%)','Average Active Sessions') AND intsize_csec=(SELECT MAX(intsize_csec) FROM v$sysmetric);","postgres":"SELECT state, wait_event_type, count(*) FROM pg_stat_activity GROUP BY 1,2 ORDER BY 3 DESC;","mssql":"SELECT TOP 5 wait_type, wait_time_ms FROM sys.dm_os_wait_stats ORDER BY wait_time_ms DESC;"},"choices":[{"label":"CPU cerca del 100%","next":"sol_cpu"},{"label":"Espera de IO alta","next":"sol_io"},{"label":"Presión de memoria / cache hit bajo","next":"sol_mem"},{"label":"Muchos bloqueos","next":"sol_blocking"}]},"sol_blocking":{"kind":"solution","title":"Causa raíz: bloqueo","detail":"La consulta espera un bloqueo que tiene otra sesión. Encuentre la sesión a la cabeza de la cadena (root blocker) y resuélvala.","steps":["Encuentre la cabeza de la cadena de bloqueos (la sesión que no espera ella misma pero hace esperar a otras).","Entienda qué está haciendo el root blocker: ¿una transacción larga sin commit, o 'idle in transaction'?","Si la aplicación puede hacer commit/rollback, provóquelo; si no y el trabajo está parado, termine el root blocker.","Corrija la causa raíz: un índice faltante puede estar causando bloqueos amplios; acorte las transacciones y revise el nivel de aislamiento."],"related":["blocking-sessions","kill-session"],"playbook":"blocking"},"sol_io":{"kind":"solution","title":"Causa raíz: cuello de botella de IO","detail":"La consulta lee mucho de disco. Normalmente por full scans (índice faltante) o memoria insuficiente (buffer cache).","steps":["Añada un índice adecuado para el filtro que causa el full scan (el plan debería pasar a un index scan, IXSCAN).","Compruebe el hit ratio del buffer cache / buffer pool; aumente la memoria si es bajo.","Reduzca las lecturas innecesarias de columnas/filas (las columnas necesarias en lugar de SELECT *, paginación).","Si el almacenamiento es realmente lento (latencia de IO alta), mejore la capa de disco."],"related":["cache-hit-ratio","create-index"]},"sol_log":{"kind":"solution","title":"Causa raíz: cuello de botella de commit / log","detail":"Si domina 'log file sync' / 'WRITELOG', los commits están esperando las escrituras de redo/WAL.","steps":["Convierta los commits frecuentes de una sola fila en commits por lotes; reduzca los commits innecesarios.","Acelere el disco de redo/WAL (disco separado y rápido); revise el tamaño/número de archivos de log.","Evalúe los ajustes synchronous_commit / commit_write frente a la carga y el compromiso de durabilidad."],"related":["show-config-param"]},"sol_index":{"kind":"solution","title":"Causa raíz: índice faltante","detail":"El plan hace un full scan. Un índice adecuado sobre las columnas de filtro/join da una gran mejora de velocidad.","steps":["Añada en línea un índice adecuado (compuesto si hace falta) sobre las columnas del WHERE/JOIN.","Tras añadir el índice, vuelva a obtener el plan y verifique que pasó a un index scan (IXSCAN).","Si la carga de escritura es alta, sopese el costo del índice; elimine los índices innecesarios.","Un índice no ayuda en columnas con baja selectividad; compruebe la selectividad."],"related":["create-index","explain-plan"]},"sol_planchange":{"kind":"solution","title":"Causa raíz: regresión de plan","detail":"La consulta iba rápida ayer pero hoy no; el plan cambió. Normalmente por estadísticas obsoletas o un cambio de parámetros.","steps":["Primero recopile estadísticas nuevas (las estadísticas obsoletas son la causa más común).","Fije el plan bueno: Oracle SQL Plan Baseline / hint, MSSQL Query Store 'force plan', ajustes de PG.","Si hubo una actualización/parche/cambio de parámetros reciente, revíselo (comportamiento del optimizador)."],"related":["update-statistics"]},"sol_rewrite":{"kind":"solution","title":"Causa raíz: diseño de la consulta","detail":"El plan es razonable, pero la consulta hace por naturaleza demasiado trabajo (demasiadas filas, N+1, joins innecesarios).","steps":["Reduzca el conjunto de resultados: paginación (LIMIT/OFFSET o keyset), filtrado temprano.","Convierta el patrón N+1 en una única consulta por lotes; simplifique los joins/subconsultas innecesarios.","Considere una vista materializada / tabla resumen para informes pesados recurrentes."],"related":["explain-plan","materialized-view"]},"sol_stats":{"kind":"solution","title":"Causa raíz: estadísticas obsoletas","detail":"El optimizador estima mal porque las estadísticas no están actualizadas.","steps":["Recopile manualmente las estadísticas de la(s) tabla(s) afectada(s).","Verifique que el job de recopilación automática (autovacuum/auto stats/gather job) se está ejecutando.","Añada la recopilación de estadísticas al flujo de trabajo tras cargas de datos grandes y repentinas."],"related":["update-statistics"]},"sol_deep":{"kind":"solution","title":"Análisis en profundidad","detail":"Si el plan y las estadísticas están bien pero sigue lenta, profundice.","steps":["Habilite una traza a nivel de sesión: Oracle 10046, PG auto_explain, MySQL profiling, MSSQL Extended Events.","Examine el paralelismo, los memory grants y los spills de sort/hash (tempdb/temp).","Descarte la capa de hardware/SO (CPU steal, latencia de IO, red).","Evalúe la contención causada por el número de conexiones y la concurrencia."],"related":["session-trace","top-wait-events"]},"sol_cpu":{"kind":"solution","title":"Causa raíz: cuello de botella de CPU","detail":"Todo el sistema está limitado por CPU. Normalmente unas pocas consultas mal planificadas están consumiendo la CPU.","steps":["Encuentre las 10 consultas que más CPU consumen y corrija sus planes (índices/estadísticas).","Limite las consultas desbocadas (Resource Manager / Resource Governor).","Si la utilización sigue alta, escale (añada CPU, reparta las lecturas en réplicas de lectura)."],"related":["top-slow-queries"],"playbook":"high-cpu"},"sol_mem":{"kind":"solution","title":"Causa raíz: presión de memoria","detail":"Cache hit bajo y/o swapping: la memoria es insuficiente o está mal asignada.","steps":["Dimensione correctamente el buffer pool / SGA y la memoria de sesión (work_mem/PGA) (Herramientas > Calculadora de memoria).","Encuentre y corrija la consulta desbocada que consume memoria.","Si el sistema está haciendo swapping, salir del swap es la prioridad; añada RAM si hace falta."],"related":["cache-hit-ratio","show-config-param"],"playbook":"memory-pressure"}}},"solver/deadlock.json":{"id":"deadlock","title":"Deadlock / conflicto de bloqueos (ORA-00060, 1213, 40P01)","icon":"♻️","category":"Bloqueos","intro":"Ha sufrido un deadlock (Oracle ORA-00060, MySQL 1213, PG 40P01, MSSQL 1205). Capture el grafo, identifique el patrón y evite que se repita. Los deadlocks se resuelven con el diseño — el objetivo es reducir su frecuencia.","start":"capture","nodes":{"capture":{"kind":"step","title":"Primero capture el deadlock graph","detail":"¿Qué dos (o más) sesiones, qué recursos y qué modos de bloqueo chocaron? No aplique soluciones a ciegas sin ver esto.","command":{"oracle":"-- Check the trace file referenced in the alert log (deadlock graph). For history:\nSELECT * FROM dba_hist_active_sess_history WHERE event LIKE 'enq: TX%' ORDER BY sample_time DESC FETCH FIRST 20 ROWS ONLY;","mysql":"SHOW ENGINE INNODB STATUS\\G  -- 'LATEST DETECTED DEADLOCK' section","postgres":"-- Deadlock errors are always written to postgresql.log as ERROR (log_lock_waits not required); log_lock_waits=on also logs lock waits longer than deadlock_timeout.","mssql":"SELECT XEvent.query('.') FROM (SELECT CAST(target_data AS XML) td FROM sys.dm_xe_session_targets t JOIN sys.dm_xe_sessions s ON s.address=t.event_session_address WHERE s.name='system_health') x CROSS APPLY td.nodes('//event[@name=\"xml_deadlock_report\"]') AS q(XEvent);"},"choices":[{"label":"Tengo el grafo — dos transacciones bloquean recursos en orden inverso","next":"sol_order"},{"label":"Muchas transacciones se acumulan sobre una sola fila/tabla (hotspot)","next":"sol_hotspot"},{"label":"Hay bloqueos de rango amplios (se bloquean muchas filas)","next":"index_check"}]},"index_check":{"kind":"step","title":"¿Usa un índice la consulta que bloquea?","detail":"Sin índice, un UPDATE/DELETE bloquea muchas filas (incluso la tabla entera) para satisfacer la cláusula WHERE → una fuente de deadlocks. Revise su plan.","command":{"mysql":"EXPLAIN <the UPDATE/DELETE involved in the deadlock>;  -- type=ALL means a full scan, no index","postgres":"EXPLAIN <query>;  -- Seq Scan or Index Scan?","oracle":"SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id'));"},"choices":[{"label":"Sin índice / hace un full scan","next":"sol_index"},{"label":"Hay índice, pero sigue habiendo conflictos","next":"sol_order"}]},"sol_order":{"kind":"solution","title":"Causa raíz: orden de bloqueo inverso","detail":"La causa más común de deadlock: dos transacciones acceden a los recursos en órdenes distintos. Cuando A→B y B→A ocurren a la vez, se interbloquean.","steps":["Acceda a los recursos en el MISMO orden en todas las rutas de código (p. ej. siempre del id más bajo → al más alto).","Mantenga las transacciones cortas; saque de la transacción la interacción del usuario/el trabajo largo.","Si hace falta, tome los bloqueos al principio en un orden determinista (SELECT ... FOR UPDATE ordenado).","Añada en la aplicación un reintento automático (retry + backoff) para la víctima del deadlock — es obligatorio."],"related":["blocking-sessions","open-transactions"]},"sol_index":{"kind":"solution","title":"Causa raíz: índice faltante → bloqueos amplios","detail":"Un UPDATE/DELETE sin índice bloquea más filas de las necesarias y hace mucho más probables los deadlocks.","steps":["Añada un índice sobre la columna de la cláusula WHERE; así el bloqueo se limita a las filas afectadas.","Vuelva a obtener el plan y verifique que el full scan (Seq Scan / type=ALL) se ha convertido en acceso por índice.","Ejecute las actualizaciones masivas en lotes pequeños (chunks)."],"related":["create-index","list-indexes"]},"sol_hotspot":{"kind":"solution","title":"Causa raíz: hotspot","detail":"Muchas transacciones se acumulan a la vez sobre un único recurso, como una fila/clave contador.","steps":["Reparta la fila caliente: use una sequence/identity, divida los contadores (sharded counter).","Acorte la duración de la transacción para que el bloqueo se mantenga menos tiempo.","Si es posible, use bloqueo optimista (version/timestamp) o un patrón de cola."],"related":["sequence","open-transactions"]}}},"solver/disk-full.json":{"id":"disk-full","title":"Disco / tablespace lleno","icon":"💽","category":"Capacidad","intro":"Ha recibido 'unable to extend', 'disk full', 'could not extend' o 'log full'. Sin pánico: primero averigüe QUÉ está lleno, recupere espacio de forma segura y luego corríjalo de forma permanente.","start":"what","nodes":{"what":{"kind":"step","title":"¿Qué está lleno?","detail":"Limpiar en el sitio equivocado no sirve. Mida primero el uso: ¿es un datafile, el log de transacciones, archive/redo, temp o el disco del SO?","command":{"oracle":"SELECT d.tablespace_name, ROUND((1-(d.alloc-NVL(f.free,0))/d.maxb)*100,1) AS pct_free\nFROM (SELECT tablespace_name, SUM(bytes) alloc, SUM(GREATEST(bytes,maxbytes)) maxb FROM dba_data_files GROUP BY tablespace_name) d LEFT JOIN (SELECT tablespace_name, SUM(bytes) free FROM dba_free_space GROUP BY tablespace_name) f ON f.tablespace_name = d.tablespace_name ORDER BY pct_free;","postgres":"SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database ORDER BY 2 DESC;  -- OS disk: df -h","mssql":"SELECT name, size/128.0 AS size_mb, size/128.0 - CAST(FILEPROPERTY(name,'SpaceUsed') AS int)/128.0 AS free_mb, type_desc\nFROM sys.database_files;","mysql":"SELECT table_schema, ROUND(SUM(data_length+index_length)/1048576,1) AS mb FROM information_schema.tables GROUP BY table_schema ORDER BY mb DESC;"},"choices":[{"label":"El datafile / tablespace está lleno","next":"data"},{"label":"El log de transacciones / archive-redo está lleno","next":"log"},{"label":"Temp / undo está lleno","next":"sol_temp"},{"label":"El disco del SO está lleno (no la BD)","next":"sol_os"}]},"data":{"kind":"step","title":"¿Cuál es el mayor consumidor?","detail":"La mayoría de las veces la culpable es una sola tabla/segmento desbocado. Encuentre los más grandes.","command":{"oracle":"SELECT segment_name, segment_type, ROUND(bytes/1048576,1) AS mb FROM dba_segments ORDER BY bytes DESC FETCH FIRST 10 ROWS ONLY;","postgres":"SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) FROM pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 10;","mssql":"SELECT TOP 10 OBJECT_NAME(object_id) AS table_name, SUM(reserved_page_count)*8/1024 AS mb FROM sys.dm_db_partition_stats GROUP BY object_id ORDER BY mb DESC;"},"choices":[{"label":"Hay una tabla inesperadamente grande/desbocada","next":"sol_runaway"},{"label":"Crecimiento normal; el espacio realmente se ha agotado","next":"sol_extend"},{"label":"Demasiadas filas muertas / bloat","next":"sol_reclaim"}]},"log":{"kind":"step","title":"¿Por qué crece el log?","detail":"El log/archive no puede limpiarse hasta que termine la transacción/el backup activo más antiguo. ¿Por qué se ha acumulado?","command":{"mssql":"SELECT name, log_reuse_wait_desc FROM sys.databases WHERE name=DB_NAME();  -- why can it not be truncated?","postgres":"SELECT slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained FROM pg_replication_slots ORDER BY 3 DESC;","oracle":"SELECT * FROM v$flash_recovery_area_usage;  -- FRA (archive) usage"},"choices":[{"label":"MSSQL: no se están tomando backups de log (LOG_BACKUP)","next":"sol_logbackup"},{"label":"PG: un replication slot atascado está reteniendo WAL","next":"sol_slot"},{"label":"Oracle: el área de logs archivados (FRA) está llena","next":"sol_archive"},{"label":"Una transacción abierta larga está reteniendo el log","next":"sol_longtxn"}]},"sol_runaway":{"kind":"solution","title":"Causa raíz: tabla/segmento desbocado","detail":"Un único objeto ha crecido de forma inesperada (inserciones descontroladas, una tabla de cola/log que nunca se purga).","steps":["Verifique si el objeto es realmente necesario; si es una tabla de log/cola, aplique retención.","Purgue los datos antiguos innecesarios con un drop de partición / TRUNCATE (tras backup/aprobación).","Detenga el crecimiento descontrolado en la aplicación; configure una alerta de crecimiento."],"related":["largest-tables","create-partition"]},"sol_extend":{"kind":"solution","title":"Causa raíz: necesidad real de capacidad","detail":"Crecimiento normal; hay que añadir espacio.","steps":["Añada espacio al datafile o habilite AUTOEXTEND (Oracle: ALTER DATABASE DATAFILE ... AUTOEXTEND ON MAXSIZE).","Si hace falta, añada un nuevo datafile/tablespace o amplíe el disco.","Configure alertas de uso al 80%/90% para que no haya más sorpresas."],"related":["free-space"]},"sol_reclaim":{"kind":"solution","title":"Causa raíz: bloat / filas muertas","detail":"No se ha recuperado el espacio de las filas borradas/actualizadas.","steps":["Recupere el espacio: PG VACUUM (pg_repack si hace falta), Oracle SHRINK SPACE, MySQL OPTIMIZE TABLE, Mongo compact.","Haga más agresivos los ajustes de autovacuum/mantenimiento automático.","VACUUM FULL / OPTIMIZE bloquean la tabla — ejecútelos fuera de las horas pico."],"related":["reclaim-space","free-space"]},"sol_temp":{"kind":"solution","title":"Causa raíz: Temp / undo lleno","detail":"Un sort/hash grande o una transacción abierta larga llenó el espacio temporal.","steps":["Encuentre la consulta que hizo explotar temp (spill grande de sort/hash) y optimícela.","Encuentre la transacción abierta larga; es la que retiene el undo (Oracle).","Si hace falta, amplíe el espacio de temp/undo; revise el ajuste de retención."],"related":["open-transactions","reclaim-space"]},"sol_os":{"kind":"solution","title":"Causa raíz: disco del SO lleno","detail":"El problema está en el disco del sistema operativo, no en la BD. Pueden estar ocupando espacio logs, core dumps, backups antiguos o archivos de traza.","steps":["Encuentre el sistema de archivos lleno con df -h; busque los directorios más grandes (du -sh *).","Limpie los archivos antiguos de log/traza/core dump/backup (con aprobación).","Ponga en rotación los directorios de diagnóstico de la BD (Oracle ADR, logs de PG)."]},"sol_logbackup":{"kind":"solution","title":"Causa raíz: no se toman backups de log (SQL Server)","detail":"En el modelo de recuperación FULL, el log crece indefinidamente si no se toman backups de log.","steps":["Tome un backup de log (BACKUP LOG) — esto hace que el log sea reutilizable.","Si el modelo de recuperación realmente debería ser SIMPLE, cámbielo.","Reduzca el archivo de log solo si hace falta (DBCC SHRINKFILE) y configure un job regular de backup de log."],"related":["last-backup-status"],"playbook":"log-growth"},"sol_slot":{"kind":"solution","title":"Causa raíz: Replication slot abandonado (PG)","detail":"Un slot atascado (inactivo) impide que se elimine el WAL e infla pg_wal.","steps":["Verifique si el slot está realmente en uso.","Si no es necesario, elimínelo: SELECT pg_drop_replication_slot('slot_name');","Arregle la réplica activa para que el slot avance; configure una alerta de retraso del slot."],"related":["free-space"],"playbook":"log-growth"},"sol_archive":{"kind":"solution","title":"Causa raíz: área de logs archivados (FRA) llena (Oracle)","detail":"Los logs archivados se han acumulado y la FRA está llena; cuando el archiver se detiene, la base de datos puede dejar de aceptar escrituras.","steps":["Borre con RMAN los logs archivados antiguos que ya se han respaldado (NUNCA los borre sin backup).","Aumente el tamaño de la FRA (db_recovery_file_dest_size) si hace falta.","Configure el job de limpieza/backup de logs archivados para que se ejecute regularmente."],"related":["last-backup-status"]},"sol_longtxn":{"kind":"solution","title":"Causa raíz: transacción abierta larga","detail":"La transacción activa más antigua impide recuperar el log/undo.","steps":["Encuentre la transacción abierta más antigua y haga que se confirme/revierta (o hable con su responsable).","Corrija el comportamiento de la aplicación que deja sesiones 'idle in transaction'; fije un timeout.","Mueva los informes largos a una réplica/ventana aparte."],"related":["open-transactions"],"playbook":"log-growth"}}},"solver/connection-storm.json":{"id":"connection-storm","title":"Connection storm (demasiadas conexiones)","icon":"🌊","category":"Capacidad","intro":"Ha recibido 'too many connections', 'remaining connection slots reserved' u ORA-00018; se están rechazando conexiones nuevas. Encuentre el origen, alivie la presión ahora y corríjalo definitivamente.","start":"measure","nodes":{"measure":{"kind":"step","title":"Mida la situación: ¿cuántas conexiones hay y cuántas están activas?","detail":"Mire primero el total de conexiones, el límite y el reparto activas/inactivas.","command":{"oracle":"SELECT status, COUNT(*) FROM v$session WHERE type='USER' GROUP BY status;\nSELECT name, value FROM v$parameter WHERE name IN ('processes','sessions');","postgres":"SELECT state, count(*) FROM pg_stat_activity GROUP BY state;\nSHOW max_connections;","mysql":"SHOW STATUS LIKE 'Threads_connected'; SHOW VARIABLES LIKE 'max_connections';","mssql":"SELECT status, COUNT(*) FROM sys.dm_exec_sessions WHERE is_user_process=1 GROUP BY status;","redis":"INFO clients"},"choices":[{"label":"La mayoría de las conexiones están idle / idle in transaction","next":"sol_idle"},{"label":"La mayoría están activas; necesito encontrar el origen","next":"who"}]},"who":{"kind":"step","title":"¿Quién/desde dónde? Agrupe las conexiones por origen","detail":"Normalmente el pool de un único servidor de aplicaciones se ha disparado o hay una fuga de conexiones. Mire la distribución por host/usuario/aplicación.","command":{"postgres":"SELECT client_addr, usename, application_name, count(*) FROM pg_stat_activity GROUP BY 1,2,3 ORDER BY 4 DESC;","mysql":"SELECT host, user, count(*) FROM information_schema.processlist GROUP BY host, user ORDER BY 3 DESC;","mssql":"SELECT host_name, program_name, login_name, COUNT(*) FROM sys.dm_exec_sessions WHERE is_user_process=1 GROUP BY host_name, program_name, login_name ORDER BY 4 DESC;","oracle":"SELECT machine, program, COUNT(*) FROM v$session WHERE type='USER' GROUP BY machine, program ORDER BY 3 DESC;"},"choices":[{"label":"Una sola aplicación/host ha disparado las conexiones (pool)","next":"sol_pool"},{"label":"La carga ha aumentado de verdad; todas son legítimas y están activas","next":"sol_scale"}]},"sol_idle":{"kind":"solution","title":"Causa raíz: acumulación de conexiones inactivas / fuga","detail":"Las conexiones se abren y nunca se cierran, o se dejan 'idle in transaction'; ocupan slots para nada.","steps":["Limpie las conexiones idle / idle-in-transaction largas; use el slot reservado para administradores.","Fije timeouts en el servidor: PG idle_in_transaction_session_timeout, MySQL wait_timeout, Oracle profile idle_time.","Asegúrese de que la aplicación cierra las conexiones (y las transacciones); corrija la fuga en su origen."],"related":["open-transactions","kill-session","connection-count"],"playbook":"connection-storm"},"sol_pool":{"kind":"solution","title":"Causa raíz: el pool de la aplicación se ha disparado","detail":"El pool de conexiones de un servidor de aplicaciones no tiene límite, es demasiado grande o tiene fugas. Es el que genera las conexiones.","steps":["Inmediato: mate las conexiones inactivas de la aplicación culpable y reinicie su pool.","Fije un tamaño de pool razonable en el lado de la aplicación (Herramientas > Calculadora de Connection Pool).","Ponga un pooler entre el servidor y la BD (PgBouncer / ProxySQL / Oracle DRCP / Connection Manager (CMAN)).","No aumente max_connections a ciegas; consume memoria y solo aplaza el problema."],"related":["connection-count","kill-session"],"playbook":"connection-storm"},"sol_scale":{"kind":"solution","title":"Causa raíz: aumento real de la carga","detail":"Las conexiones son legítimas y están activas; la capacidad es realmente insuficiente.","steps":["Reduzca el número de conexiones que llegan a la BD multiplexándolas a través de un connection pooler.","Reparta la carga de lectura entre réplicas de lectura.","Si hace falta, aumente max_connections de forma controlada según el presupuesto de memoria; escale también la memoria del servidor."],"related":["connection-count"],"playbook":"connection-storm"}}},"solver/replication-lag.json":{"id":"replication-lag","title":"Retraso de replicación / replicación detenida","icon":"🔁","category":"Replicación","intro":"El standby/la réplica se está quedando atrás respecto al primario, o la replicación se ha detenido por completo. Este árbol acota paso a paso el tipo de retraso (creciente, constante o detenido) hasta llegar a la causa raíz, como un error de aplicación, un cuello de botella de un solo hilo, un conflicto de recuperación o corrupción.","start":"root","nodes":{"root":{"kind":"step","title":"¿Cómo se comporta el retraso?","detail":"Mida primero la situación. Mire el flujo de la réplica y la métrica de retraso: ¿el retraso crece con el tiempo, está estancado en un nivel constante o la aplicación se ha detenido por completo? Esto divide la investigación en tres ramas principales.","command":{"postgres":"SELECT client_addr, state, write_lag, flush_lag, replay_lag,\n       pg_wal_lsn_diff(sent_lsn, replay_lsn) AS bytes_behind\nFROM pg_stat_replication;","mysql":"SHOW REPLICA STATUS\\G","oracle":"SELECT name, value, unit FROM v$dataguard_stats WHERE name IN ('apply lag','transport lag');","mssql":"SELECT database_id, is_local, synchronization_state_desc, redo_queue_size, log_send_queue_size\nFROM sys.dm_hadr_database_replica_states;","redis":"INFO replication","elasticsearch":"GET _cluster/health"},"choices":[{"label":"La aplicación se detuvo por completo / hay un error","next":"stopped"},{"label":"El retraso sigue creciendo (no consigue ponerse al día)","next":"growing"},{"label":"El retraso es constante pero se sospecha de una consulta larga","next":"standby_query"},{"label":"Muy retrasada / parece inconsistente","next":"sol_rebuild"}]},"stopped":{"kind":"step","title":"¿Qué dice el error de aplicación?","detail":"Si la replicación se ha detenido, normalmente hay un error en un evento que se está aplicando. Mire el mensaje de error: ¿falta una fila/tabla en el destino, hay un conflicto de clave primaria o una caída de conexión/red?","command":{"mysql":"SHOW REPLICA STATUS\\G  -- Last_Error, Last_SQL_Error, Replica_IO_Running, Replica_SQL_Running","postgres":"SELECT status, last_msg_receipt_time, latest_end_lsn, slot_name\nFROM pg_stat_wal_receiver;","oracle":"SELECT process, status, sequence#, block# FROM v$managed_standby WHERE process LIKE 'MRP%';","elasticsearch":"GET _cat/recovery?v&active_only=true"},"choices":[{"label":"Error de fila o tabla en conflicto/faltante","next":"sol_apply_error"},{"label":"Red/conexión caída, IO thread detenido","next":"sol_connection"}]},"growing":{"kind":"step","title":"¿Por qué no consigue ponerse al día?","detail":"Si el retraso sigue creciendo, la réplica no da abasto con el ritmo de escritura del primario. ¿El cuello de botella es la aplicación en un solo hilo o el disco/IO de la réplica es lento? Mire el estado de los workers de aplicación y las esperas de IO.","command":{"mysql":"SHOW REPLICA STATUS\\G  -- Seconds_Behind_Source and apply thread state\nSELECT * FROM performance_schema.replication_applier_status_by_worker;","postgres":"SELECT pid, wait_event_type, wait_event, state\nFROM pg_stat_activity WHERE backend_type='startup';","mssql":"SELECT database_id, redo_queue_size, redo_rate, log_send_queue_size\nFROM sys.dm_hadr_database_replica_states;"},"choices":[{"label":"Aplicación en un solo hilo + carga de escritura intensa","next":"sol_parallel"},{"label":"El disco/IO de la réplica es lento","next":"sol_io"}]},"standby_query":{"kind":"step","title":"¿Hay una consulta larga en el standby?","detail":"Una consulta larga en un standby de solo lectura puede bloquear la aplicación (recovery). En PostgreSQL, así es como surge un recovery conflict. Mire las consultas activas y los contadores de conflictos en el standby.","command":{"postgres":"SELECT now()-query_start AS dur, pid, state, query\nFROM pg_stat_activity WHERE state='active' ORDER BY query_start;\nSELECT confl_snapshot, confl_lock, confl_bufferpin FROM pg_stat_database_conflicts WHERE datname=current_database();"},"choices":[{"label":"Sí, una consulta larga está bloqueando la aplicación","next":"sol_recovery_conflict"},{"label":"No, no hay consulta, pero sigue retrasada","next":"sol_parallel"}]},"sol_apply_error":{"kind":"solution","title":"Causa raíz: error de aplicación (conflicto de datos)","detail":"La réplica está atascada en un evento que no puede aplicar: falta una fila en el destino, hay un conflicto de clave primaria o falta una tabla. Corrija la causa de la inconsistencia en lugar de saltar a ciegas.","steps":["Examine el error y el evento atascado: ¿qué tabla y qué operación (delete/update/insert) falló?","Encuentre la causa raíz: datos escritos manualmente en el destino, DDL faltante (tabla/columna) o una entrada de binlog/WAL corrupta.","Alinee el esquema: añada la tabla/columna faltante; compare la fila en conflicto con el primario y corríjala.","Si debe saltar el evento (p. ej. un único evento en MySQL), documéntelo y verifique después la consistencia de los datos de la réplica.","Evite que se repita: deshabilite las escrituras directas en el destino y ejecute el DDL a través del primario."],"related":["replication-status"],"playbook":"replication-lag"},"sol_connection":{"kind":"solution","title":"Causa raíz: caída de conexión / red","detail":"El hilo receptor (IO/receiver) no puede conectarse al primario. Pueden ser el problema la red, la autenticación o segmentos de log acumulados/purgados.","steps":["Verifique el acceso de red entre primario y réplica y los privilegios del usuario de replicación.","Compruebe que los segmentos de WAL/binlog necesarios siguen existiendo en el primario (retención/expiración).","En PostgreSQL, verifique que el replication slot existe y está activo; sin slot, el WAL puede haberse eliminado antes de tiempo.","Reinicie la conexión; cuando los logs vuelvan a fluir, el retraso debería cerrarse."],"related":["replication-status","list-replicas"],"playbook":"replication-lag"},"sol_parallel":{"kind":"solution","title":"Causa raíz: cuello de botella de aplicación en un solo hilo","detail":"Mientras el primario escribe en paralelo, la réplica aplica los cambios en una sola secuencia y no da abasto. Paralelice la aplicación.","steps":["Habilite la aplicación en paralelo: en MySQL, replica_parallel_workers + replica_parallel_type=LOGICAL_CLOCK.","En la replicación física de PostgreSQL, la aplicación es un único proceso; reduzca el ritmo de generación en el primario dividiendo las transacciones grandes.","Mueva los jobs batch pesados a horas de poco tráfico o divídalos.","Monitoree la métrica de retraso tras el cambio; debería bajar hasta cero."],"related":["replication-status"],"playbook":"replication-lag"},"sol_io":{"kind":"solution","title":"Causa raíz: cuello de botella de IO en la réplica","detail":"El hilo de aplicación espera al disco, no a la CPU. El almacenamiento de la réplica no da abasto con el ritmo de escritura del primario.","steps":["Mida la latencia/saturación de IO del disco de la réplica; compárela con la del primario.","Si el hardware de la réplica es más débil (disco lento, menos RAM), iguálelo al del primario; el hardware asimétrico es una causa común.","Acelere el disco para la aplicación de redo/WAL; dimensione adecuadamente el buffer/la caché.","Si no mejora, reduzca la carga de escritura o reparta el tráfico de lectura a otra réplica."],"related":["replication-status"],"playbook":"replication-lag"},"sol_recovery_conflict":{"kind":"solution","title":"Causa raíz: Recovery conflict en el standby","detail":"Una consulta larga de solo lectura en el standby está bloqueando la aplicación de los cambios del primario (conflicto de vacuum/bloqueo). PostgreSQL lo resuelve retrasando la aplicación o cancelando la consulta.","steps":["Encuentre la consulta larga que bloquea; si es realmente necesaria, espere a que termine; si no, termínela.","Con hot_standby_feedback=on, haga que el primario respete las consultas del standby (a costa de bloat en el primario).","Como alternativa, ajuste max_standby_streaming_delay a la carga (equilibrio entre tolerancia al retraso y cancelación de consultas).","Mueva los informes largos a una réplica aparte donde no bloqueen la aplicación, o a otro horario."],"related":["replication-status","open-transactions"],"playbook":"replication-lag"},"sol_rebuild":{"kind":"solution","title":"Causa raíz: réplica demasiado retrasada / corrupta - reconstrúyala","detail":"La réplica está demasiado retrasada para recuperarse, los logs necesarios se han eliminado o los datos son inconsistentes. El camino más seguro es reconstruirla desde un backup nuevo.","steps":["Tome un base backup actual del primario: PG pg_basebackup, MySQL clone/xtrabackup, Oracle RMAN duplicate.","Restaure el backup en la réplica y vuelva a establecer la configuración de replicación (slot/usuario/posición).","Inicie la replicación y monitoree el progreso de la sincronización inicial (catch-up).","Cuando el retraso llegue a cero, verifique la consistencia de los datos y el tráfico de lectura y luego póngala en servicio."],"related":["replication-status","list-replicas","last-backup-status"],"playbook":"replication-lag"}}},"solver/ora-01555.json":{"id":"ora-01555","title":"ORA-01555: Snapshot too old (Oracle)","icon":"⏳","category":"Oracle","intro":"El error de Oracle 'ORA-01555: snapshot too old' indica que los datos antiguos (undo) que necesita una consulta para una lectura consistente ya se han sobrescrito. Este árbol acota el origen del error (consulta larga + UNDO insuficiente, alta rotación de undo o LOB) y le lleva a la solución correcta.","start":"root","nodes":{"root":{"kind":"step","title":"¿Dónde se produce el error?","detail":"Determine primero el contexto del error. ¿El ORA-01555 clásico se produce durante un SELECT/informe de larga duración o al acceder a una columna LOB (CLOB/BLOB)? El ORA-01555 relacionado con LOB es un mecanismo distinto (PCTVERSION/RETENTION) y se resuelve por separado.","command":{"oracle":"SELECT sql_id, elapsed_time/1e6 AS sec, sql_text\nFROM v$sql ORDER BY elapsed_time DESC FETCH FIRST 10 ROWS ONLY;"},"choices":[{"label":"Durante una consulta/informe de larga duración","next":"undo_size"},{"label":"Al acceder a una columna LOB","next":"sol_lob"}]},"undo_size":{"kind":"step","title":"¿Es suficiente el UNDO, es adecuada la retención?","detail":"Una consulta larga lee desde UNDO la imagen antigua de los bloques modificados desde que empezó. Si el tablespace de UNDO es pequeño o undo_retention es más corto que la duración de la consulta, el undo antiguo se sobrescribe. Mida el uso de UNDO y la retención.","command":{"oracle":"SHOW PARAMETER undo_retention;\nSELECT tablespace_name, SUM(bytes)/1024/1024 AS mb, status\nFROM dba_undo_extents GROUP BY tablespace_name, status;\nSELECT MAX(maxquerylen) AS longest_query_sec, MAX(tuned_undoretention) AS tuned_retention\nFROM v$undostat;"},"choices":[{"label":"El UNDO es pequeño / la retención es más corta que la consulta","next":"sol_undo"},{"label":"El UNDO es suficiente pero los commits son muy frecuentes","next":"sol_commits"},{"label":"La consulta dura excesivamente","next":"sol_query"}]},"sol_undo":{"kind":"solution","title":"Causa raíz: UNDO / retención insuficientes","detail":"El undo antiguo que necesitaba la consulta larga se sobrescribió porque expiró el periodo de retención o se llenó el tablespace. Amplíe el UNDO y fije una retención que cubra la duración de la consulta.","steps":["Según la duración de la consulta más larga (v$undostat.maxquerylen), suba undo_retention por encima de ella: ALTER SYSTEM SET undo_retention=10800;","Amplíe el tablespace de UNDO lo suficiente para acomodar la retención o habilite autoextend: ALTER DATABASE DATAFILE '...' AUTOEXTEND ON MAXSIZE ...;","Garantícelo para las consultas largas críticas: ALTER TABLESPACE undotbs1 RETENTION GUARANTEE;  (precaución: si el UNDO se llena, el DML puede fallar).","Tras el cambio, vuelva a ejecutar el mismo informe y confirme que ORA-01555 ha desaparecido."],"related":["show-config-param"],"playbook":"ora-01555"},"sol_commits":{"kind":"solution","title":"Causa raíz: alta rotación de undo (commits frecuentes)","detail":"Un DML intenso con commits muy frecuentes recicla rápidamente los bloques de undo y borra la imagen antigua que necesita la consulta larga. El efecto 'commit cleanout' lo empeora.","steps":["Reduzca los commits innecesariamente frecuentes: use commits por lotes en lugar de hacer commit de cada fila dentro de un bucle.","Para frenar la rotación de undo, no programe cargas de DML pesadas a la vez que el informe largo.","Aumente undo_retention y, si hace falta, proteja el undo antiguo con RETENTION GUARANTEE.","Saque el informe de la ventana de escritura intensa o fíjelo a un punto de lectura consistente."],"related":["open-transactions","show-config-param"],"playbook":"ora-01555"},"sol_query":{"kind":"solution","title":"Causa raíz: duración de la consulta excesiva","detail":"La consulta dura tanto que ni siquiera un undo_retention razonable basta. La solución permanente es acelerar la consulta o descargar su trabajo.","steps":["Optimice la consulta: corrija índices faltantes, planes malos o full scans innecesarios; acorte su duración.","Divida el trabajo grande (por fecha/rango) para que cada trozo quepa en la ventana de undo.","Mueva el informe pesado a un standby/réplica o a otro horario de lectura — para que no choque con el DML activo.","Cuando la duración sea aceptable, fije undo_retention en consecuencia."],"related":["explain-plan"],"playbook":"ora-01555"},"sol_lob":{"kind":"solution","title":"Causa raíz: retención de LOB / PCTVERSION","detail":"ORA-01555 al leer un LOB indica que el espacio que guarda las versiones antiguas del LOB (RETENTION o PCTVERSION) es insuficiente. Los LOB tienen un mecanismo de versionado aparte.","steps":["Vea el ajuste actual de la columna LOB: SELECT retention, pctversion FROM dba_lobs WHERE table_name='...' AND column_name='...';","Suba el ajuste adecuado según el modo de undo AUTO/MANUAL: ALTER TABLE t MODIFY LOB (my_column) (RETENTION);  o aumente PCTVERSION en modo MANUAL.","Los LOB que usan RETENTION se basan en el valor undo_retention de la base de datos — manténgalo también suficiente.","Acorte las operaciones largas que acceden al LOB; vuelva a probar el error tras el cambio."],"related":["show-config-param"],"playbook":"ora-01555"}}},"solver/backup-restore.json":{"id":"backup-restore","title":"Decisión de backup y restauración","icon":"💾","category":"Backup y recuperación","intro":"Un árbol de decisión que le guía paso a paso por lo que tiene que hacer con los backups (tomar un backup, restaurar, volver a un punto concreto en el tiempo - PITR, o verificar un backup) y le lleva a los comandos y pasos correctos para cada base de datos.","start":"root","nodes":{"root":{"kind":"step","title":"¿Qué quiere hacer?","detail":"Elija su objetivo. ¿Va a tomar un backup nuevo, restaurar un backup existente, volver a un momento concreto (justo antes de una caída) o verificar que el backup que tiene está en buen estado?","choices":[{"label":"Quiero TOMAR un backup","next":"take"},{"label":"Quiero RESTAURAR un backup","next":"restore"},{"label":"Volver a un punto concreto en el tiempo (PITR)","next":"pitr"},{"label":"Quiero VERIFICAR un backup","next":"verify"}]},"take":{"kind":"step","title":"¿Qué tipo de backup?","detail":"Un backup lógico (dump) es portable y sirve para restauraciones selectivas, pero es lento con datos grandes. Un backup físico (a nivel de archivo/bloque) sirve para bases de datos grandes y una recuperación rápida. Elija según sus necesidades.","choices":[{"label":"Backup lógico (dump / export)","next":"sol_logical_backup"},{"label":"Backup físico / completo","next":"sol_physical_backup"}]},"sol_logical_backup":{"kind":"solution","title":"Tomar un backup lógico (dump)","detail":"Exporta los datos como sentencias SQL/BSON. Ideal para mover entre versiones/plataformas y restaurar una sola tabla/colección.","steps":["PostgreSQL: pg_dump -Fc -d mydb -f backup.dump  (pg_dumpall para todo el clúster).","Oracle: expdp app_user/password directory=DP_DIR dumpfile=backup.dmp schemas=APP_SCHEMA logfile=exp.log","MySQL/MariaDB: mysqldump --single-transaction --routines --triggers mydb > backup.sql","MongoDB: mongodump --db mydb --out /backup/dir","Redis: tome un snapshot RDB en segundo plano con BGSAVE (dump.rdb).","Elasticsearch: registre primero un repositorio de snapshots y luego PUT _snapshot/my_repo/snap1?wait_for_completion=true","Mueva el backup a una ubicación segura y anote su tamaño/estado de finalización."],"related":["logical-dump","last-backup-status"],"playbook":"backup-restore"},"sol_physical_backup":{"kind":"solution","title":"Tomar un backup físico / completo","detail":"Una copia a nivel de bloque/archivo de los datafiles. Rápida para bases de datos grandes y la base del PITR y de la recuperación completa.","steps":["Oracle: RMAN> BACKUP DATABASE PLUS ARCHIVELOG;  (junto con los logs archivados).","PostgreSQL: pg_basebackup -D /backup/base -Fp -Xs -P  (con el archivado de WAL habilitado).","SQL Server: BACKUP DATABASE [mydb] TO DISK='D:\\backup\\db.bak' WITH CHECKSUM, COMPRESSION;","MySQL/MariaDB: xtrabackup --backup --target-dir=/backup/base  (copia física consistente).","MongoDB: tome un snapshot del sistema de archivos o un snapshot consistente desde un miembro del replica set.","Copie el backup a una ubicación offline/remota y anote el estado del último backup."],"related":["last-backup-status"],"playbook":"backup-restore"},"restore":{"kind":"solution","title":"Restaurar un backup","detail":"Restaure en el destino el backup más adecuado que tenga. Verifique primero la integridad y la marca de tiempo del backup y luego cárguelo con el comando adecuado para el motor.","steps":["Verifique la fecha y el buen estado del backup que va a restaurar (conozca su ventana de pérdida de datos).","PostgreSQL: pg_restore -d new_db backup.dump   (para un dump SQL: psql -d new_db -f backup.sql).","Oracle: RMAN> RESTORE DATABASE; RECOVER DATABASE; ALTER DATABASE OPEN;  (import lógico con impdp).","MySQL/MariaDB: mysql mydb < backup.sql   (físico: xtrabackup --copy-back).","SQL Server: RESTORE DATABASE [mydb] FROM DISK='D:\\backup\\db.bak' WITH RECOVERY, REPLACE;","MongoDB: mongorestore --db mydb /backup/dir","Tras la restauración, valide los datos con recuentos de filas/comprobaciones de la aplicación."],"related":["restore-dump","last-backup-status"],"playbook":"backup-restore"},"pitr":{"kind":"solution","title":"Volver a un punto concreto en el tiempo (PITR)","detail":"Quiere volver, con precisión de segundos, a justo antes de un borrado accidental/una corrupción. Necesita un backup completo + los logs de transacciones hasta ese punto (logs archivados / WAL / binlog).","steps":["Determine con precisión la hora objetivo (justo antes del incidente) y verifique que los logs necesarios están disponibles.","Oracle: RMAN> RESTORE DATABASE; RECOVER DATABASE UNTIL TIME \"TO_DATE('2026-07-04 10:00:00','YYYY-MM-DD HH24:MI:SS')\"; ALTER DATABASE OPEN RESETLOGS;","PostgreSQL: restaure el base backup, luego fije recovery_target_time='2026-07-04 10:00:00' y aplique el WAL hasta ese punto.","SQL Server: RESTORE ... WITH NORECOVERY; luego RESTORE LOG ... WITH STOPAT='2026-07-04 10:00:00', RECOVERY;","MySQL/MariaDB: restaure el backup completo y luego aplique el binlog hasta ese punto con mysqlbinlog --stop-datetime='2026-07-04 10:00:00'.","Tras abrir, verifique que los datos están en el punto en el tiempo esperado."],"related":["restore-dump","last-backup-status"],"playbook":"backup-restore"},"verify":{"kind":"solution","title":"Verificar un backup","detail":"Un backup solo es un backup cuando se ha demostrado que es restaurable. Pruebe regularmente la integridad y la restaurabilidad real.","steps":["Oracle: RMAN> RESTORE DATABASE VALIDATE;  y para la consistencia de bloques VALIDATE CHECK LOGICAL DATABASE;","SQL Server: tome los backups WITH CHECKSUM y ejecute RESTORE VERIFYONLY ... WITH CHECKSUM (solo comprueba el archivo de backup); para una prueba real, restaure el backup en un servidor de pruebas y ejecute allí DBCC CHECKDB([mydb]) WITH NO_INFOMSGS; (CHECKDB comprueba una base de datos activa/restaurada, no el archivo de backup).","PostgreSQL: restaure realmente el backup en un servidor aparte/temporal y confirme que pg_restore termina sin errores.","MySQL/MongoDB: restaure el dump en un entorno de pruebas y compare el número de filas/colecciones y datos de muestra.","Programe 'simulacros de restauración' regulares — tomar backups no basta, demuestre también la restauración.","Anote los resultados y la fecha de la última verificación correcta."],"related":["last-backup-status","restore-dump"],"playbook":"backup-restore"}}},"solver/upgrade-wizard.json":{"id":"upgrade-wizard","title":"Asistente de actualización de versión","icon":"⬆️","category":"Ciclo de vida","intro":"¿Parche o versión mayor, en el sitio o mediante migración? Planifique la actualización paso a paso; cada rama cubre las comprobaciones previas, el método y el trabajo posterior a la actualización. Ensáyela aquí antes de tocar producción.","start":"kind","nodes":{"kind":{"kind":"step","title":"¿Qué tipo de actualización está haciendo?","detail":"Una versión de parche/menor (dentro de la misma versión mayor, p. ej., 16.2 -> 16.4, o un CU de SQL Server) es de bajo riesgo y suele hacerse in situ. Una versión mayor (p. ej., 15 -> 16) puede implicar cambios en el formato de los datos y requiere una planificación más cuidadosa.","command":{"oracle":"SELECT * FROM v$version;\nSELECT patch_id, action, status FROM dba_registry_sqlpatch ORDER BY action_time DESC;","postgres":"SELECT version();","mysql":"SELECT VERSION();","mssql":"SELECT SERVERPROPERTY('ProductVersion') AS v, SERVERPROPERTY('ProductUpdateLevel') AS cu;","elasticsearch":"GET /"},"choices":[{"label":"Versión menor / parche (misma versión mayor, CU)","next":"patch_prep"},{"label":"Versión mayor (cambia la versión mayor)","next":"major_method"},{"label":"No conozco la diferencia; explíquela","next":"explain"}]},"explain":{"kind":"step","title":"¿Parche o versión mayor?","detail":"Parche: la versión mayor no cambia (16.2 -> 16.4); el formato de los datafiles no cambia, normalmente se cambian los binarios + reinicio. Mayor: cambia la versión mayor (15 -> 16); el catálogo/formato puede cambiar, se requiere pg_upgrade / AutoUpgrade / dump-restore y hay que probar la compatibilidad de la aplicación.","choices":[{"label":"El mío es un parche (misma versión mayor)","next":"patch_prep"},{"label":"El mío es una versión mayor (cambia la versión mayor)","next":"major_method"}]},"patch_prep":{"kind":"step","title":"¿Están listos el backup y el plan de vuelta atrás?","detail":"Aunque un parche es de bajo riesgo, la regla de oro de toda actualización es: primero un backup completo (y un snapshot si es posible). Necesita un punto al que volver si algo sale mal.","choices":[{"label":"Sí, backup + vuelta atrás están listos","next":"sol_patch"},{"label":"No, todavía no","next":"sol_backupfirst"}]},"sol_backupfirst":{"kind":"solution","title":"Primero backup + plan de vuelta atrás","detail":"Prepare la red de seguridad antes de empezar la actualización.","steps":["Tome un backup completo (y, si es posible, un snapshot del almacenamiento / un guaranteed restore point de Oracle).","Anote los pasos de vuelta atrás: si algo sale mal, ¿cómo vuelvo a la versión antigua?","Cierre la ventana de mantenimiento y el plan de comunicación y luego pase al paso del parche."],"related":["logical-dump","last-backup-status"]},"sol_patch":{"kind":"solution","title":"Aplicar el parche","detail":"Un parche dentro de la misma versión mayor. Aplicarlo de forma rolling en un clúster (empezando por las réplicas) reduce el tiempo de inactividad.","steps":["Oracle: instale la nueva RU con opatch y luego aplique los cambios SQL con 'datapatch -verbose'.","PostgreSQL: actualización menor = instalar los nuevos binarios y reiniciar el servicio (no hace falta initdb/dump).","MySQL: actualice el paquete; en 8.0 el servidor realiza la actualización automáticamente al arrancar.","SQL Server: ejecute el instalador de la CU/GDR correspondiente y luego reinicie.","MongoDB/Redis/Elasticsearch: reinicio rolling; primero los nodos secundarios y el primario al final.","Después verifique la versión y la aplicación; si ve errores, ejecute el plan de vuelta atrás."],"related":["apply-patch","check-version"],"playbook":"upgrade-rollback"},"major_method":{"kind":"step","title":"Método de actualización mayor","detail":"Dos caminos principales: (1) In situ: convertir en el servidor existente (pg_upgrade, Oracle AutoUpgrade/DBUA, MySQL in-place). Rápido pero difícil de revertir. (2) Mediante migración: montar un servidor nuevo con la nueva versión, mover los datos (con dump/restore o replicación) y luego hacer el corte. Más seguro, con un tiempo de inactividad mínimo.","choices":[{"label":"Quiero hacer primero una comprobación de preparación/compatibilidad","next":"precheck"},{"label":"In situ","next":"sol_inplace"},{"label":"Migrando a un servidor nuevo (tiempo de inactividad mínimo)","next":"sol_migrate"}]},"precheck":{"kind":"step","title":"Comprobación de compatibilidad / preparación","detail":"Antes de actualizar, recopile los avisos de la versión de destino: funcionalidades eliminadas (obsoletas), objetos no válidos, nivel de compatibilidad.","command":{"oracle":"-- AutoUpgrade analyze mode: java -jar autoupgrade.jar -config cfg -mode analyze","postgres":"-- pg_upgrade --check   (with the old and new binary paths)","mysql":"-- MySQL Shell: util.checkForServerUpgrade('root@localhost:3306', {targetVersion: '8.4.0'})","mssql":"-- Compatibility report with Data Migration Assistant (DMA)","elasticsearch":"GET _migration/deprecations"},"choices":[{"label":"Salida limpia, continuar","next":"major_method"},{"label":"Hay avisos/incompatibilidades","next":"sol_fixwarn"}]},"sol_fixwarn":{"kind":"solution","title":"Corrija primero los avisos","detail":"Resuelva todos los avisos del informe ANTES de actualizar; de lo contrario la actualización puede fallar a mitad o la aplicación puede romperse.","steps":["Actualice los objetos y consultas que usan funcionalidades/sintaxis eliminadas (obsoletas).","Compile/corrija los objetos no válidos; resuelva las dependencias que faltan.","Planifique los pasos de nivel de compatibilidad / versión de funcionalidades para la versión de destino.","Cuando los avisos estén resueltos, vuelva a ejecutar la comprobación previa y regrese a la elección de método."],"related":["check-upgrade-readiness","set-compatibility"]},"sol_inplace":{"kind":"solution","title":"Actualización de versión mayor in situ","detail":"Convertir en el servidor existente. Rápido, pero la vuelta atrás suele implicar restaurar desde backup, así que un snapshot es imprescindible.","steps":["Tome un backup completo + snapshot/guaranteed restore point (la única vía de vuelta).","Oracle: AutoUpgrade (modo deploy) o DBUA. PostgreSQL: pg_upgrade (--link es rápido pero no tiene vuelta atrás). MySQL: nuevos binarios + actualización automática en el sitio. SQL Server: setup /ACTION=upgrade.","Ejecute las tareas posteriores a la actualización (vea los relacionados abajo).","Valide la aplicación con carga real; si se produce una regresión de plan, fije el comportamiento del optimizador."],"related":["upgrade-major","post-upgrade-tasks"],"playbook":"upgrade-rollback"},"sol_migrate":{"kind":"solution","title":"Actualización mediante migración (tiempo de inactividad mínimo)","detail":"Monte un servidor nuevo con la nueva versión, mueva los datos y luego cambie con un corte breve. El método más seguro; el sistema antiguo queda intacto.","steps":["Monte el nuevo servidor con la versión de destino y prepare el esquema.","Transfiera la carga inicial con dump/restore o replicación; después manténgalo sincronizado con replicación continua (CDC / replicación lógica).","En una ventana de mantenimiento corta: detenga las escrituras, espere al delta restante y apunte la aplicación al nuevo servidor (cutover).","Verifique la migración (recuentos de filas/checksums); si hay un problema, vuelva de inmediato al servidor antiguo."],"related":["setup-cdc-replication","verify-migration","export-for-migration"]}}},"solver/migration-wizard.json":{"id":"migration-wizard","title":"Asistente de migración (migración de datos/esquema)","icon":"🚚","category":"Ciclo de vida","intro":"Va a migrar datos/esquema. ¿Es homogénea o heterogénea, y cuál es su tolerancia al tiempo de inactividad? Planifíquela paso a paso: evaluar → migrar → verificar. Ensaye primero aquí el cutover.","start":"kind","nodes":{"kind":{"kind":"step","title":"¿Desde qué y hacia qué está migrando?","detail":"Homogénea: el mismo motor de base de datos (p. ej., Oracle 19 → Oracle 23, o PostgreSQL a otro servidor). Heterogénea: un motor distinto (p. ej., Oracle → PostgreSQL, MySQL → SQL Server). Las migraciones heterogéneas requieren mucho más trabajo de compatibilidad (tipos de datos, funciones, PL/SQL).","choices":[{"label":"Mismo motor, nuevo servidor/versión (homogénea)","next":"downtime"},{"label":"A un motor distinto (heterogénea)","next":"hetero"},{"label":"Estoy en la fase de planificación; ¿cómo debería abordarlo?","next":"downtime"}]},"downtime":{"kind":"step","title":"¿Cuál es su tolerancia al tiempo de inactividad?","detail":"Esto determina el método de migración. Si hay una ventana de mantenimiento, el camino más sencillo es dump/restore. Si se requiere un tiempo de inactividad casi nulo, carga los datos iniciales, los mantiene sincronizados con replicación continua (CDC) y hace un cutover breve.","choices":[{"label":"Hay una ventana de mantenimiento (el tiempo de inactividad es aceptable)","next":"sol_offline"},{"label":"Se requiere un tiempo de inactividad casi nulo","next":"sol_online"}]},"hetero":{"kind":"step","title":"¿Ha evaluado la compatibilidad de esquema y tipos?","detail":"En una migración heterogénea, el trabajo real no es copiar los datos sino resolver las diferencias: tipos de datos (VARCHAR2↔TEXT), funciones (NVL↔COALESCE), valores autoincrementales, PL/SQL ↔ PL/pgSQL, funcionalidades eliminadas/distintas.","choices":[{"label":"He mapeado las diferencias de esquema/tipos","next":"sol_hetero"},{"label":"Todavía no las he evaluado","next":"sol_assess"}]},"sol_assess":{"kind":"solution","title":"Evalúe primero la compatibilidad","detail":"Antes de migrar, mapee las diferencias entre origen y destino; si se omite este paso, la migración se atasca a mitad de camino.","steps":["Genere el diff de esquema: tablas, restricciones, índices, vistas, procedimientos.","Mapee los tipos de datos y las funciones (el módulo DBAtlas > Tipos y funciones está hecho exactamente para esto).","Decida cómo se tratarán en el destino las funcionalidades específicas del motor (identity/sequence, particionamiento, PL/SQL, triggers).","Elija una herramienta de conversión (ora2pg, pgloader, AWS SCT/DMS) y ensaye con una muestra pequeña."],"related":["compare-schema","heterogeneous-migration"]},"sol_offline":{"kind":"solution","title":"Migración offline (dump / restore)","detail":"El método homogéneo más sencillo y fiable: tomar un backup consistente, cargarlo en el destino, verificar y cambiar.","steps":["Tome un export CONSISTENTE del origen (pg_dump -Fc, expdp FLASHBACK_SCN, mysqldump --single-transaction, BACKUP, mongodump --oplog).","Cárguelo en el destino (pg_restore -j, impdp, mysql <, RESTORE, mongorestore).","Verifique la migración: ¿coinciden los recuentos de filas y los checksums críticos entre origen y destino?","Durante la ventana de mantenimiento, apunte la aplicación al destino (cutover); si algo sale mal, vuelva al origen."],"related":["export-for-migration","import-for-migration","verify-migration"]},"sol_online":{"kind":"solution","title":"Tiempo de inactividad mínimo (CDC / replicación)","detail":"Cargue los datos iniciales y luego mantenga el destino sincronizado con el origen mediante replicación continua; el tiempo de inactividad solo se produce durante el cutover breve.","steps":["Cargue los datos iniciales (baseline) con dump/restore o un snapshot.","Configure la replicación continua: replicación lógica de PG (PUBLICATION/SUBSCRIPTION), replicación por binlog de MySQL, Oracle GoldenGate, replicación transaccional/CDC de MSSQL, change streams/mongosync de Mongo.","Espere y monitoree hasta que el destino alcance al origen y el retraso sea ~0.","Cutover breve: detenga las escrituras en el origen, espere al delta restante, apunte la aplicación al destino y verifique."],"related":["setup-cdc-replication","verify-migration"]},"sol_hetero":{"kind":"solution","title":"Migración heterogénea (a otro motor)","detail":"Convierta el esquema, mueva los datos, adapte la lógica, verifique. Avance por fases; no lo migre todo de una vez.","steps":["Convierta el esquema: con una herramienta (ora2pg, pgloader, AWS SCT) o a mano; adapte los tipos y las restricciones al destino.","Mueva los datos: carga masiva o una herramienta; particione/trocee las tablas grandes.","Adapte la lógica del lado del servidor: PL/SQL → PL/pgSQL, equivalentes de funciones/triggers.","Verifique a fondo: recuentos de filas, datos de muestra, resultados de consultas críticas; pruebe la aplicación sobre el destino.","Si hay que reducir el tiempo de inactividad, mantenga el destino sincronizado con una capa de CDC tras la conversión y luego haga el corte."],"related":["heterogeneous-migration","verify-migration","setup-cdc-replication"]}}},"solver/high-cpu.json":{"id":"high-cpu","title":"CPU alta","icon":"🔥","category":"Rendimiento","intro":"La CPU del servidor está al máximo. Verifique primero que es realmente la base de datos la que la consume y luego siga las ramas hasta la causa raíz: consultas mal planificadas, una regresión de plan, concurrencia excesiva o un proceso ajeno a la BD.","start":"root","nodes":{"root":{"kind":"step","title":"¿Es realmente la base de datos la que consume la CPU?","detail":"Compruebe en el lado del sistema operativo qué proceso está consumiendo CPU. ¿La mayor parte de la carga está en los procesos de la BD o en un proceso vecino (backup, antivirus, otra aplicación)? Esto divide la investigación en dos.","command":{"oracle":"-- OS side: top -o %CPU  (or: top -c)\n-- DB-side verification:\nSELECT ROUND(value,1) AS host_cpu_util_pct FROM v$sysmetric WHERE metric_name='Host CPU Utilization (%)' AND rownum=1;","postgres":"-- OS: top -c  /  ps -eo pcpu,pid,cmd --sort=-pcpu | head\n-- DB active session count:\nSELECT count(*) FROM pg_stat_activity WHERE state='active';","mysql":"-- OS: top -c  /  mysqladmin processlist\nSHOW GLOBAL STATUS LIKE 'Threads_running';","mssql":"-- OS: Task Manager / typeperf\nSELECT record_id, SQLProcessUtilization AS sql_cpu_pct, (100 - SystemIdle - SQLProcessUtilization) AS other_cpu_pct FROM (SELECT record.value('(./Record/@id)[1]','int') AS record_id, record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]','int') AS SystemIdle, record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]','int') AS SQLProcessUtilization FROM (SELECT CONVERT(xml, record) AS record FROM sys.dm_os_ring_buffers WHERE ring_buffer_type='RING_BUFFER_SCHEDULER_MONITOR') AS x) AS y ORDER BY record_id DESC;","mongodb":"// OS: top -c  |  db.serverStatus().metrics\ndb.serverStatus().opcounters","redis":"INFO cpu","elasticsearch":"GET _nodes/stats/os,process"},"choices":[{"label":"Sí, los procesos de la BD consumen la CPU","next":"db_confirm"},{"label":"No, la consume un proceso vecino / un job de backup","next":"sol_os"}]},"db_confirm":{"kind":"step","title":"¿Cómo apareció la carga?","detail":"Si la BD consume la CPU, identifique el patrón: ¿dominan unas pocas consultas pesadas, empezó el problema de repente tras un cambio o hay alta concurrencia/contención de conexiones?","choices":[{"label":"Unas pocas consultas pesadas consumen la CPU","next":"topcpu"},{"label":"Empezó de repente; hubo un despliegue / cambio de parámetros reciente","next":"sol_regression"},{"label":"Muchas conexiones / contención por concurrencia","next":"sol_concurrency"}]},"topcpu":{"kind":"step","title":"Encontrar las consultas que más CPU consumen","detail":"Extraiga las consultas principales por consumo total de CPU. Luego decida si cada una es pesada por llamada (mal plan) o barata por llamada pero invocada con mucha frecuencia.","command":{"oracle":"SELECT sql_id, executions, ROUND(cpu_time/1e6,1) AS cpu_sec, ROUND(cpu_time/GREATEST(executions,1)/1000,2) AS cpu_ms_per_exec, sql_text\nFROM v$sql ORDER BY cpu_time DESC FETCH FIRST 10 ROWS ONLY;","postgres":"SELECT queryid, calls, ROUND(total_exec_time::numeric,1) AS total_ms, ROUND(mean_exec_time::numeric,2) AS mean_ms, query\nFROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;","mysql":"SELECT DIGEST_TEXT, COUNT_STAR AS calls, ROUND(SUM_CPU_TIME/1e12,2) AS cpu_sec, ROUND(SUM_TIMER_WAIT/1e12,2) AS total_sec\nFROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;","mssql":"SELECT TOP 10 qs.execution_count, qs.total_worker_time/1000 AS cpu_ms, qs.total_worker_time/qs.execution_count/1000 AS cpu_ms_per_exec, SUBSTRING(st.text,(qs.statement_start_offset/2)+1,50) AS stmt\nFROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st ORDER BY qs.total_worker_time DESC;","mongodb":"db.setProfilingLevel(1, { slowms: 100 });  // then: db.system.profile.find().sort({ millis: -1 }).limit(10)"},"choices":[{"label":"Pesada por llamada; mal plan / full scan","next":"sol_query"},{"label":"Barata por llamada pero invocada con mucha frecuencia","next":"sol_frequency"}]},"sol_query":{"kind":"solution","title":"Causa raíz: consultas mal planificadas consumen la CPU","detail":"Unas pocas consultas consumen CPU excesiva por llamada por full scans o un mal orden de join. Corregir sus planes es la solución principal.","steps":["Obtenga los planes (EXPLAIN) de las consultas que más CPU consumen; busque full scans, un mal orden de join o diferencias entre filas estimadas y reales.","Añada índices adecuados sobre las columnas de filtro/join; verifique que el plan pasa a un index scan (IXSCAN).","Si el optimizador estima mal, recopile estadísticas nuevas.","Limite las consultas desbocadas (Oracle Resource Manager / MSSQL Resource Governor) y reescriba la consulta si hace falta."],"related":["top-slow-queries","explain-plan","create-index","update-statistics"],"playbook":"high-cpu"},"sol_frequency":{"kind":"solution","title":"Causa raíz: frecuencia de llamadas excesiva","detail":"Las consultas son baratas individualmente pero se invocan miles de veces por segundo; de ahí sale el costo total de CPU. Normalmente un patrón N+1 o una ruta caliente sin caché.","steps":["Identifique las sentencias invocadas con más frecuencia (calls / execution_count) y encuentre la ruta de la aplicación que las llama.","Convierta el patrón N+1 en una consulta por lotes; reduzca las lecturas repetidas con una caché de aplicación/resultados.","Use prepared statements para reducir el costo del parseo repetido (hard parse).","Considere una vista materializada / tabla resumen para consultas pesadas frecuentes que no cambian."],"related":["top-slow-queries","explain-plan"],"playbook":"high-cpu"},"sol_regression":{"kind":"solution","title":"Causa raíz: regresión de plan / estadísticas obsoletas","detail":"Ayer el sistema iba normal; la CPU se disparó tras un despliegue, un cambio de parámetros o una carga de datos. El optimizador puede haber cambiado a un plan peor.","steps":["Identifique el cambio: despliegue reciente, actualización/parche, cambio de parámetros o carga de datos grande.","Recopile estadísticas nuevas de las tablas afectadas (las estadísticas obsoletas son la causa más común).","Fije el plan bueno: Oracle SQL Plan Baseline / hint, MSSQL Query Store 'force plan'.","Si el cambio vino de un parámetro, reviértalo y vuelva a evaluarlo de forma controlada."],"related":["update-statistics","explain-plan","show-config-param"],"playbook":"high-cpu"},"sol_concurrency":{"kind":"solution","title":"Causa raíz: alta concurrencia / contención de conexiones","detail":"Las consultas individuales son aceptables, pero se ejecutan demasiadas sesiones a la vez; más sesiones activas que núcleos de CPU provocan cambios de contexto y contención de latch/spin.","steps":["Compare el número de sesiones activas con el de núcleos de CPU; con más sesiones activas que núcleos se forma una cola de ejecución.","Ponga un connection pooler entre la aplicación y la BD (PgBouncer / ProxySQL) y fije un tamaño de pool razonable.","Si hay paralelismo excesivo, limite el grado de paralelismo (Oracle parallel_degree_limit / parallel_max_servers, MSSQL MAXDOP).","Alivie la CPU del nodo primario repartiendo la carga de lectura entre réplicas de lectura."],"related":["connection-count","active-sessions"],"playbook":"connection-storm"},"sol_os":{"kind":"solution","title":"Causa raíz: un proceso ajeno a la BD consume CPU","detail":"No es la base de datos sino un proceso vecino el que consume la CPU: un job de backup/compresión, un escaneo de antivirus, un enviador de logs u otra aplicación en la misma máquina.","steps":["Localice el proceso culpable con una herramienta del SO (top / Administrador de tareas).","Si es un job de backup, de recopilación de estadísticas o de mantenimiento, reprográmelo fuera de las horas pico.","Añada los directorios de datos a la lista de exclusiones de los procesos de antivirus/escaneo.","Si el conflicto es permanente, mueva la BD a un servidor aparte con sus propios recursos."],"related":["os-resource"],"playbook":"high-cpu"}}},"solver/memory-pressure.json":{"id":"memory-pressure","title":"Presión de memoria / OOM","icon":"🧠","category":"Rendimiento","intro":"La memoria del servidor se está agotando, el sistema hace swapping o el OOM killer está activo. Determine primero quién consume la memoria: ¿la memoria asignada (buffer pool/SGA), la memoria de sesión (work_mem/PGA), el SO en general o una situación propia de Redis?","start":"root","nodes":{"root":{"kind":"step","title":"¿Quién consume la memoria?","detail":"Separe el origen de la presión de memoria por capas. ¿La memoria asignada (fija) es demasiado grande, se dispara la memoria por sesión, la consume un proceso ajeno a la BD o interviene la gestión de memoria propia de Redis?","command":{"oracle":"SELECT component, ROUND(current_size/1024/1024) AS mb FROM v$memory_dynamic_components WHERE current_size>0 ORDER BY current_size DESC;","postgres":"SHOW shared_buffers; SHOW work_mem; SHOW effective_cache_size;\nSELECT count(*) FROM pg_stat_activity WHERE state='active';","mysql":"SHOW VARIABLES LIKE 'innodb_buffer_pool_size';\nSHOW VARIABLES LIKE 'sort_buffer_size'; SHOW VARIABLES LIKE 'join_buffer_size';\nSHOW STATUS LIKE 'Threads_connected';","mssql":"SELECT physical_memory_in_use_kb/1024 AS used_mb, large_page_allocations_kb/1024 AS large_mb FROM sys.dm_os_process_memory;\nSELECT cntr_value/1024 AS target_mb FROM sys.dm_os_performance_counters WHERE counter_name='Target Server Memory (KB)';","mongodb":"db.serverStatus().wiredTiger.cache","redis":"INFO memory","elasticsearch":"GET _nodes/stats/jvm,os"},"choices":[{"label":"Domina la memoria asignada (buffer pool / SGA)","next":"allocated"},{"label":"La memoria de sesión (work_mem / PGA) se dispara","next":"sol_session"},{"label":"Un proceso ajeno a la BD / el SO en general","next":"sol_os"},{"label":"Propio de Redis (maxmemory / eviction / fragmentación)","next":"sol_redis"}]},"allocated":{"kind":"step","title":"¿Supera la memoria total configurada a la RAM física?","detail":"Si la memoria fija asignada es grande, hay dos posibilidades: o el total configurado (buffer pool + sesiones + SO) supera la RAM física (over-commit), o el tamaño es razonable pero el cache hit ratio sigue siendo bajo. Sume los ajustes y compárelos con la RAM.","command":{"oracle":"SHOW PARAMETER sga_target;  SHOW PARAMETER pga_aggregate_target;\nSELECT ROUND(value/1024/1024/1024,1) AS host_ram_gb FROM v$osstat WHERE stat_name='PHYSICAL_MEMORY_BYTES';","postgres":"SELECT name, setting, unit FROM pg_settings WHERE name IN ('shared_buffers','work_mem','maintenance_work_mem','max_connections');","mysql":"SELECT ROUND(@@innodb_buffer_pool_size/1024/1024/1024,1) AS pool_gb, ROUND((@@sort_buffer_size+@@join_buffer_size)/1024/1024,1) AS per_conn_mb, @@max_connections AS max_conn;","mssql":"SELECT value_in_use AS max_server_mem_mb FROM sys.configurations WHERE name='max server memory (MB)';\nSELECT total_physical_memory_kb/1024 AS ram_mb FROM sys.dm_os_sys_memory;"},"choices":[{"label":"Sí, el total configurado supera la RAM (over-commit)","next":"sol_overcommit"},{"label":"No, el tamaño es razonable pero el cache hit ratio es bajo","next":"sol_cache"}]},"sol_overcommit":{"kind":"solution","title":"Causa raíz: Over-commit de memoria","detail":"La suma de buffer pool/SGA + memoria de sesión + necesidades del SO supera la RAM física. El sistema hace swapping o el OOM killer está matando el proceso.","steps":["Rehaga el presupuesto de memoria: reserve una parte para el SO y luego dimensione correctamente la memoria asignada (Herramientas > Calculadora de memoria).","Baje el objetivo del buffer pool/SGA y el techo de memoria de sesión para que quepan dentro del límite de RAM.","Aplique los cambios de forma controlada y verifique que el uso de swap y los eventos OOM han cesado.","Si la capacidad es insuficiente de forma permanente, añada RAM o divida la carga."],"related":["show-config-param","cache-hit-ratio"],"playbook":"memory-pressure"},"sol_cache":{"kind":"solution","title":"Causa raíz: caché insuficiente (cache hit ratio bajo)","detail":"La memoria asignada no supera la RAM pero es pequeña respecto al working set; el hit ratio del buffer cache es bajo y la BD lee constantemente de disco.","steps":["Mida el hit ratio del buffer cache / buffer pool; si es persistentemente bajo (p. ej. por debajo del 90%), la caché es demasiado pequeña.","Si hay RAM disponible, amplíe el buffer pool/SGA de forma controlada y vuelva a medir el hit ratio.","Si los datos calientes son realmente grandes, reduzca las lecturas innecesarias: añada índices, seleccione solo las columnas necesarias en lugar de SELECT *, pagine.","Si ampliarlo presiona la RAM, reequilibre la memoria de sesión frente a la memoria asignada."],"related":["cache-hit-ratio","show-config-param"],"playbook":"memory-pressure"},"sol_session":{"kind":"solution","title":"Causa raíz: explosión de la memoria de sesión","detail":"La memoria por sesión, como work_mem/PGA, multiplicada por una alta concurrencia agota la RAM. Cada operación de sort/hash puede asignar esta memoria por separado.","steps":["Estime el peor caso: memoria de sesión × sesiones concurrentes × número de operaciones; compárelo con la RAM.","Baje el ajuste global de memoria de sesión (work_mem / sort_buffer_size / objetivo de PGA).","Para las consultas pesadas que realmente necesitan sorts/hashes grandes, suba el ajuste solo a nivel de sesión.","Limite la concurrencia con un pooler de conexiones para reducir la demanda total de memoria."],"related":["show-config-param","connection-count"],"playbook":"memory-pressure"},"sol_redis":{"kind":"solution","title":"Causa raíz: gestión de memoria de Redis","detail":"En Redis, used_memory ha alcanzado maxmemory, la expulsión (eviction) está activa o la fragmentación es alta (used_memory_rss >> used_memory). Hay que abordar la política y la fragmentación.","steps":["Lea used_memory, maxmemory y mem_fragmentation_ratio con INFO memory.","Configure una política de expulsión acorde al propósito: CONFIG SET maxmemory-policy allkeys-lru (caché) o noeviction (datos persistentes).","Use TTL para que las claves expiradas se limpien y reduzca las claves grandes (BIG KEYS).","Si la fragmentación es alta, active la desfragmentación activa (CONFIG SET activedefrag yes) o reinicie en una ventana de mantenimiento adecuada."],"related":["show-config-param"],"playbook":"memory-pressure"},"sol_os":{"kind":"solution","title":"Causa raíz: consumo de memoria ajeno a la BD","detail":"La memoria no la consume la base de datos, sino el SO en general o un proceso vecino: otra aplicación, presión de la caché del sistema de archivos o un proceso auxiliar con fuga de memoria.","steps":["Identifique el proceso que más memoria consume con una herramienta del SO (top / free / Task Manager).","Si el culpable es un proceso ajeno a la BD, muévalo, limítelo (cgroup / job object) o corrija la fuga de memoria.","Si el OOM killer está terminando el proceso de la BD, revise la configuración de OOM y garantice memoria para la BD.","Si el conflicto es persistente, mueva la BD a un servidor separado con memoria propia."],"related":["os-resource"],"playbook":"memory-pressure"}}},"solver/install-setup.json":{"id":"install-setup","title":"Nueva instalación y línea base segura","icon":"🛠️","category":"Ciclo de vida","intro":"Los pasos omitidos al preparar una base de datos recién instalada para producción provocan los fallos más costosos. Elija la etapa en la que se encuentra y obtenga una lista de verificación ordenada para la configuración inicial, el endurecimiento de seguridad, la durabilidad y el monitoreo.","start":"root","nodes":{"root":{"kind":"step","title":"¿En qué etapa se encuentra?","detail":"Preparar una nueva instalación para producción tiene cuatro etapas: configuración inicial, endurecimiento de seguridad, durabilidad (backup/replicación) y monitoreo. ¿En cuál está trabajando ahora? Se recomienda recorrerlas todas en orden.","command":{"oracle":"SELECT banner_full FROM v$version; SHOW PARAMETER compatible;","postgres":"SELECT version(); SHOW server_encoding; SHOW data_directory;","mysql":"SELECT VERSION(); SHOW VARIABLES LIKE 'character_set_server'; SHOW VARIABLES LIKE 'datadir';","mssql":"SELECT @@VERSION; SELECT SERVERPROPERTY('Collation') AS collation;","mongodb":"db.version(); db.serverStatus().host","redis":"INFO server","elasticsearch":"GET /"},"choices":[{"label":"Configuración inicial tras la instalación","next":"firstconfig"},{"label":"Endurecimiento de seguridad","next":"sol_security"},{"label":"Configuración de backup y durabilidad","next":"durability"},{"label":"Configuración de monitoreo y alertas","next":"sol_monitoring"}]},"firstconfig":{"kind":"step","title":"¿Cuál es la prioridad en la configuración inicial?","detail":"La configuración inicial se divide en dos temas principales: dimensionamiento de recursos/parámetros (memoria, conexiones) y bases permanentes (juego de caracteres/intercalación, directorio de datos/distribución de archivos). Son las decisiones más difíciles de cambiar después.","command":{"postgres":"SELECT name, setting, unit FROM pg_settings WHERE name IN ('shared_buffers','work_mem','max_connections','wal_level');","oracle":"SHOW PARAMETER sga_target; SHOW PARAMETER pga_aggregate_target; SHOW PARAMETER processes;","mysql":"SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW VARIABLES LIKE 'max_connections';","mssql":"SELECT name, value_in_use FROM sys.configurations WHERE name IN ('max server memory (MB)','max degree of parallelism');"},"choices":[{"label":"Dimensionamiento de memoria / parámetros","next":"sol_sizing"},{"label":"Juego de caracteres, intercalación y distribución de archivos","next":"sol_layout"}]},"durability":{"kind":"step","title":"¿Qué parte de la durabilidad está configurando?","detail":"La durabilidad se apoya en dos pilares: una estrategia de backup verificada (primer backup + prueba de restauración) y la continuidad (modo archive/log, replicación). Decir «tengo un backup» sin haberlo probado es el error más común.","command":{"postgres":"SHOW archive_mode; SHOW wal_level;","oracle":"ARCHIVE LOG LIST; SELECT log_mode FROM v$database;","mysql":"SHOW VARIABLES LIKE 'log_bin'; SHOW VARIABLES LIKE 'server_id';","mssql":"SELECT name, recovery_model_desc FROM sys.databases;"},"choices":[{"label":"Estrategia de backup + primer backup + prueba de restauración","next":"sol_backup"},{"label":"Modo archive/log + replicación","next":"sol_replication"}]},"sol_sizing":{"kind":"solution","title":"Configuración inicial: dimensionamiento de memoria y parámetros","detail":"Los valores por defecto son conservadores en la mayoría de los motores; dimensiónelos según la RAM y la CPU del servidor, pero evite el over-commit.","steps":["Reparta la RAM total: deje una parte para el SO y configure el buffer pool/SGA como caché principal.","Configure la memoria de sesión (work_mem/PGA) de forma conservadora según la concurrencia esperada; no supere la RAM total.","Ajuste max_connections a la necesidad real; planifique un pooler de conexiones para alta concurrencia.","Revise los parámetros principales específicos del motor (WAL/redo, checkpoint, paralelismo) y documente los cambios."],"related":["show-config-param","connect-cli"]},"sol_layout":{"kind":"solution","title":"Configuración inicial: juego de caracteres, intercalación y distribución de archivos","detail":"El juego de caracteres/intercalación y la distribución del directorio de datos se definen en la instalación; cambiarlos después suele requerir una reinstalación o una migración.","steps":["Elija un juego de caracteres Unicode a nivel de servidor (p. ej. UTF-8 / AL32UTF8) y defina una intercalación coherente.","Ubique los directorios de datos, log/WAL y backup en discos/volúmenes separados; reduzca la contención de IO y los puntos únicos de fallo.","Endurezca el sistema de archivos y los permisos: solo el usuario de la BD debe poder leer el directorio de datos.","Documente las decisiones de distribución y juego de caracteres para que los nodos añadidos después coincidan con ellas."],"related":["show-config-param","connect-cli"]},"sol_security":{"kind":"solution","title":"Endurecimiento de seguridad","detail":"Una nueva instalación trae cuentas por defecto, acceso de red permisivo y puertos abiertos. Reducir la superficie de ataque antes de pasar a producción es obligatorio.","steps":["Elimine o bloquee las cuentas por defecto/de ejemplo; deshabilite las cuentas administrativas no utilizadas.","Aplique una política de contraseñas robusta y cambie todas las contraseñas por defecto.","Mínimo privilegio: conceda a cada aplicación solo los privilegios que necesita y evite roles amplios.","Habilite TLS, restrinja el listener/puerto a redes de confianza y active el registro de auditoría."],"related":["create-user-grant","change-password","enable-audit"]},"sol_backup":{"kind":"solution","title":"Durabilidad: estrategia de backup y prueba de restauración","detail":"Un backup vale tanto como su capacidad de ser restaurado. Tome el primer backup, configure la continuidad y realice siempre una prueba de restauración.","steps":["Defina los objetivos de recuperación (RPO/RTO) y establezca una programación de backups completos + incrementales.","Tome el primer backup completo; almacene los backups en una ubicación separada del servidor de la BD.","Pruebe el backup restaurándolo en un entorno separado; un backup cuya integridad no se ha demostrado no cuenta.","Programe el trabajo de backup, envíe su salida al monitoreo y compruebe periódicamente el estado del último backup."],"related":["logical-dump","restore-dump","last-backup-status"]},"sol_replication":{"kind":"solution","title":"Durabilidad: modo archive/log y replicación","detail":"Para la recuperación a un punto en el tiempo y la alta disponibilidad, el modo archive/log debe estar habilitado y debe existir al menos una réplica.","steps":["Habilite el modo archive/log (PG archive_mode/wal_level, Oracle ARCHIVELOG, MySQL binlog, MSSQL FULL recovery).","Configure al menos una réplica (standby/réplica) y complete la sincronización inicial.","Monitoree el retraso de replicación y defina una alerta de retraso.","Valide el failover ejecutando un simulacro controlado de failover/switchover."],"related":["replication-status","list-replicas"],"playbook":"replication-lag"},"sol_monitoring":{"kind":"solution","title":"Configuración de monitoreo y alertas","detail":"El último paso antes de producción: ver los problemas antes que los usuarios. Configure comprobaciones de salud y alertas con los umbrales adecuados.","steps":["Recopile comprobaciones de salud básicas: tasa de aciertos de caché, sesiones activas/en espera, consultas más lentas.","Defina alertas de capacidad: uso de disco, número de conexiones cercano al límite, retraso de replicación.","Envíe las alertas a un canal (correo/chat) y calibre los umbrales según la carga real.","Añada también al monitoreo el éxito de los backups y los logs de errores para detectar fallos silenciosos."],"related":["cache-hit-ratio","connection-count","free-space"]}}},"solver/corruption-recovery.json":{"id":"corruption-recovery","title":"Recuperación ante corrupción de datos","icon":"🧯","category":"Recuperación","intro":"Un árbol de decisión paso a paso para la corrupción de datos (errores de checksum de bloque/página, tablas ilegibles, RDB/AOF corruptos): primero confirme el alcance de la corrupción y luego elija correctamente entre la recuperación a nivel de bloque, una restauración completa/PITR desde backup y las herramientas de reparación de último recurso.","start":"root","nodes":{"root":{"kind":"step","title":"¿Cuál es el alcance de la corrupción?","detail":"Primero ejecute una comprobación de integridad para determinar hasta dónde se ha extendido la corrupción. ¿Solo hay unos pocos bloques/páginas corruptos, está afectado un único objeto (tabla/índice) o la corrupción está generalizada? Analice con los comandos siguientes para determinar el alcance y luego elija la rama adecuada.","command":{"oracle":"RMAN> BACKUP VALIDATE CHECK LOGICAL DATABASE;  -- then: SELECT file#, block#, blocks, corruption_type FROM v$database_block_corruption;","postgres":"pg_amcheck --all --heapallindexed --progress   # single table: pg_amcheck -d mydb -t app_schema.my_table","mysql":"CHECK TABLE app_schema.my_table EXTENDED;   -- for InnoDB also: SHOW ENGINE INNODB STATUS\\G","mssql":"DBCC CHECKDB (N'mydb') WITH NO_INFOMSGS, ALL_ERRORMSGS;","mongodb":"db.my_table.validate({ full: true })","redis":"redis-check-rdb /var/lib/redis/dump.rdb   # AOF (7+, read-only check): redis-check-aof /var/lib/redis/appendonlydir/appendonly.aof.manifest  (--fix modifies the files; only after taking a copy)","elasticsearch":"GET _cluster/health?level=shards   # for a corrupted shard: GET _cat/shards?v&h=index,shard,state,unassigned.reason"},"choices":[{"label":"Solo unos pocos bloques / páginas están corruptos","next":"block_level"},{"label":"Un único objeto (tabla/índice) está afectado","next":"restore"},{"label":"La corrupción está generalizada / muchos objetos","next":"restore"},{"label":"Las herramientas de comprobación no funcionan, la BD no abre","next":"last_resort"},{"label":"La corrupción se repite (se sospecha de una causa raíz)","next":"hardware"}]},"block_level":{"kind":"solution","title":"Causa raíz: corrupción limitada de bloques/páginas → recuperación dirigida","detail":"Si solo unos pocos bloques/páginas están corruptos, no hace falta restaurar toda la base de datos. Minimice la pérdida de datos reparando solo las partes corruptas desde backup/logs con la función de recuperación a nivel de bloque/página del motor.","steps":["Oracle: tras revisar la lista de bloques corruptos en v$database_block_corruption (ejecute antes RMAN> VALIDATE DATABASE; si la lista está desactualizada), recupere un solo bloque con RMAN> RECOVER DATAFILE 5 BLOCK 1234; o todos los bloques listados con RMAN> RECOVER CORRUPTION LIST;.","SQL Server: restauración de una sola página — RESTORE DATABASE [db] PAGE='5:1234' FROM DISK='D:\\backup\\db.bak' WITH NORECOVERY;  luego aplique los backups de log posteriores WITH NORECOVERY y el último WITH RECOVERY.","PostgreSQL: no existe reparación automática a nivel de bloque; restaure parcialmente la tabla afectada desde backup, o habilite zero_damaged_pages=on temporalmente SOLO para extraer datos (las filas corruptas se pierden).","MySQL/InnoDB: no existe reparación de una sola página; restaure la tabla afectada desde el último backup bueno y avance con PITR si es necesario.","Tras la reparación, vuelva a ejecutar el comando de comprobación (VALIDATE / DBCC CHECKDB / pg_amcheck) para confirmar que no queda corrupción.","Verifique los datos de negocio de los bloques recuperados mediante muestreo en el lado de la aplicación."],"related":["restore-dump","last-backup-status"],"playbook":"corruption"},"restore":{"kind":"solution","title":"Causa raíz: corrupción a nivel de objeto/generalizada → restauración completa o PITR desde backup","detail":"Si la corrupción se ha extendido a uno o más objetos, el camino más seguro es restaurar desde un backup bueno y, cuando sea posible, avanzar hasta el momento de la corrupción (PITR). Así se evita la pérdida de datos que causarían las herramientas de reparación.","steps":["Determine el momento aproximado en que empezó la corrupción (alert log / error log) para fijar el objetivo de PITR justo antes.","Verifique el último backup completo BUENO y los logs de transacciones hasta ese punto (logs archivados / WAL / binlog).","Oracle: RMAN> RESTORE DATABASE; RECOVER DATABASE UNTIL TIME \"TO_DATE('2026-07-04 09:59:00','YYYY-MM-DD HH24:MI:SS')\"; ALTER DATABASE OPEN RESETLOGS;","SQL Server: RESTORE DATABASE [db] FROM DISK='...' WITH NORECOVERY; RESTORE LOG [db] FROM DISK='...' WITH STOPAT='2026-07-04 09:59:00', RECOVERY;","PostgreSQL: restaure el base backup, configure recovery_target_time='2026-07-04 09:59:00' y aplique el WAL hasta ese punto.","MySQL/MariaDB: restaure el backup completo y luego aplique el binlog hasta ese punto con mysqlbinlog --stop-datetime='2026-07-04 09:59:00'.","MongoDB: restaure con mongorestore desde una réplica/snapshot buena; Elasticsearch: restaure los índices corruptos con POST _snapshot/my_repo/snap1/_restore.","Tras abrir, vuelva a ejecutar la comprobación de integridad y valide los datos con recuentos de filas/comprobaciones de la aplicación."],"related":["restore-dump","logical-dump","last-backup-status"],"playbook":"corruption"},"last_resort":{"kind":"step","title":"Sin backup / la BD no abre: ¿pasar al último recurso?","detail":"Las herramientas de reparación (REPAIR_ALLOW_DATA_LOSS, innodb_force_recovery, --fix) PUEDEN CAUSAR PÉRDIDA DE DATOS y romper las restricciones de integridad. Son un último recurso solo cuando no existe un backup utilizable y la base de datos no se puede abrir. Primero asegúrese de que realmente no hay ningún backup utilizable.","choices":[{"label":"Encontré un backup bueno, voy a restaurar","next":"restore"},{"label":"No hay ningún backup, una herramienta de reparación es la única opción","next":"repair"}]},"repair":{"kind":"solution","title":"Último recurso: herramientas de reparación con riesgo de pérdida de datos","detail":"Estas herramientas descartan filas/páginas corruptas para que la base de datos se pueda abrir. Úselas solo si no hay backup y ha hecho antes una copia de los archivos existentes. Objetivo: abrirla lo justo para exportar los datos y luego moverlos a una copia limpia.","steps":["PRIMERO haga una copia en bruto de los datafiles existentes (los intentos pueden empeorar la situación).","SQL Server: DBCC CHECKDB (N'db', REPAIR_ALLOW_DATA_LOSS) WITH ALL_ERRORMSGS;  (la base de datos debe estar en modo SINGLE_USER; este comando puede causar pérdida de datos).","MySQL/InnoDB: inicie con innodb_force_recovery=1 bajo [mysqld] en my.cnf; si no abre, aumente a 2, 3... (hasta 6); en cuanto abra, exporte de inmediato los datos con mysqldump, impórtelos en una instalación limpia y elimine el ajuste.","PostgreSQL: como último recurso, pg_resetwal (alto riesgo de pérdida de datos) solo cuando el WAL está corrupto; luego extraiga los datos con pg_dump y cárguelos en un nuevo clúster.","Oracle: sin backup, DBMS_REPAIR / la recuperación basada en eventos se evalúa con Oracle Support; los bloques pueden omitirse con 'skip corrupt' (pérdida de datos).","MongoDB: abra con mongod --repair o el salvage de WiredTiger y luego mueva los datos a un nuevo replica set.","Tras la reparación, mueva SIEMPRE a una instalación limpia, vuelva a ejecutar la comprobación de integridad e informe de los datos perdidos."],"related":["logical-dump","restore-dump"],"playbook":"corruption"},"hardware":{"kind":"solution","title":"Causa raíz: hardware/infraestructura → corrija el origen y habilite los checksums","detail":"Si la corrupción se repite, la causa real es muy probablemente el disco, la controladora, la memoria (RAM) o una capa de almacenamiento inestable. Recuperar los datos no basta; si no corrige el origen, la corrupción volverá. Habilite también la verificación de checksum/integridad para la detección temprana.","steps":["Compruebe la salud del disco: valores SMART (smartctl -a /dev/sdX), logs de la controladora RAID y alertas de almacenamiento.","Ejecute una prueba de memoria (memtest86+ o contadores de errores ECC); los errores de un solo bit son una causa clásica de corrupción.","Revise los logs de errores del SO/sistema de archivos y del hardware (dmesg, Event Viewer, alertas de la cabina de almacenamiento).","PostgreSQL: cree un clúster con checksums mediante initdb --data-checksums (o pg_checksums --enable) para que la corrupción se detecte al escribirse en disco.","Oracle: habilite la verificación de bloques con DB_BLOCK_CHECKSUM=FULL y DB_BLOCK_CHECKING=FULL; añada DB_LOST_WRITE_PROTECT=TYPICAL.","SQL Server: asegúrese de que la base de datos usa PAGE_VERIFY CHECKSUM (ALTER DATABASE [db] SET PAGE_VERIFY CHECKSUM).","MySQL/InnoDB: protéjase de las escrituras parciales con innodb_checksum_algorithm=crc32 y, si es necesario, innodb_doublewrite=ON.","Tras corregir el origen, restaure desde el último backup bueno en una copia limpia y refuerce el monitoreo."],"related":["restore-dump","last-backup-status"],"playbook":"corruption"}}},"solver/failover-drill.json":{"id":"failover-drill","title":"Simulacro de failover / transición de rol planificada","icon":"🔀","category":"Replicación","intro":"Un árbol de decisión que identifica el tipo de cambio de rol (switchover planificado, failover de emergencia o un simulacro que no toca producción) y ofrece la secuencia de comandos y los pasos de verificación correctos para cada escenario. Objetivo: estar listo para la transición sin pérdida de datos cuando es planificada, con pérdida mínima en una emergencia y sin ninguna interrupción en un simulacro.","start":"root","nodes":{"root":{"kind":"step","title":"¿Qué quiere hacer?","detail":"Elija el contexto del cambio de rol. ¿Está haciendo una transición planificada con el primario sano (mantenimiento/migración), ha caído realmente el primario y se necesita una transición de emergencia, o está ejecutando un simulacro/validación de preparación sin tocar producción?","choices":[{"label":"Switchover planificado (el primario está sano)","next":"planned"},{"label":"Failover de emergencia (el primario ha caído)","next":"emergency_check"},{"label":"Simulacro / prueba de preparación (no tocar producción)","next":"drill"}]},"planned":{"kind":"step","title":"Transición planificada: ¿está todo listo?","detail":"El objetivo de un switchover planificado es CERO pérdida de datos. Para ello, antes de la transición debe asegurarse de que la réplica se ha puesto al día con el primario (retraso ~0) y está sana. Compruebe el retraso con los comandos siguientes; si el retraso es cercano a cero, pase a los pasos de transición.","command":{"oracle":"SELECT thread#, MAX(sequence#) FROM v$archived_log GROUP BY thread#;   -- DG: SELECT name, value FROM v$dataguard_stats WHERE name LIKE '%lag%';","postgres":"SELECT client_addr, state, replay_lag, write_lag FROM pg_stat_replication;","mysql":"SHOW REPLICA STATUS\\G   -- check Seconds_Behind_Source and Replica_SQL_Running","mssql":"SELECT ag.name, drs.synchronization_state_desc, drs.log_send_queue_size, drs.redo_queue_size FROM sys.dm_hadr_database_replica_states drs JOIN sys.availability_groups ag ON ag.group_id = drs.group_id;","mongodb":"rs.printSecondaryReplicationInfo()","redis":"redis-cli INFO replication   # check the difference between master_repl_offset and the slave offset","elasticsearch":"GET _cat/recovery?v&active_only=true"},"choices":[{"label":"El retraso es cercano a cero, listo para la transición","next":"sol_planned"},{"label":"El retraso es alto, espere primero a la sincronización","next":"drill"}]},"sol_planned":{"kind":"solution","title":"Switchover planificado (transición controlada sin pérdida de datos)","detail":"Mientras el primario y el standby están sanos y sincronizados, intercambie los roles de forma controlada. El orden importa: detenga las escrituras, asegúrese de que los cambios restantes han llegado al standby, luego transfiera el rol y redirija la aplicación al nuevo primario.","steps":["Anuncie la ventana de mantenimiento y retenga la aplicación de forma controlada para lecturas y escrituras.","Oracle Data Guard (12c+): SQL> ALTER DATABASE SWITCHOVER TO standby_db VERIFY; luego SQL> ALTER DATABASE SWITCHOVER TO standby_db;  (con el Broker: DGMGRL> SWITCHOVER TO 'standby_db'; 11g y anteriores: COMMIT TO SWITCHOVER TO PHYSICAL STANDBY).","PostgreSQL: detenga el primario limpiamente (pg_ctl stop -m fast), promueva el standby con SELECT pg_promote(); y luego vuelva a conectar el antiguo primario como nuevo standby con pg_rewind.","SQL Server AG: en una réplica síncrona, ALTER AVAILABILITY GROUP [ag] FAILOVER;  (failover planificado sin pérdida de datos; se requiere SYNCHRONOUS_COMMIT).","MongoDB: use rs.stepDown(60) para forzar que el primario actual ceda su rol durante 60 s; el secundario más actualizado se elige automáticamente y pasa a ser primario.","Redis: en el candidato a nuevo master, REPLICAOF NO ONE; luego en los demás nodos REPLICAOF new_master port; (con Sentinel, SENTINEL FAILOVER master_name).","Apunte la cadena de conexión / el enrutamiento de la aplicación al nuevo primario (VIP, nombre de servicio o cadena de conexión).","Verifique: el nuevo primario acepta escrituras, el antiguo primario replica en streaming como standby con retraso ~0 y la aplicación no tiene errores."],"related":["promote-failover","replication-status","list-replicas"],"playbook":"replication-lag"},"emergency_check":{"kind":"step","title":"Transición de emergencia: ¿el primario está realmente caído?","detail":"Antes de forzar un failover, verifique que el primario es realmente inaccesible y evalúe el riesgo de split-brain. Si el primario sigue vivo (p. ej. un corte de red transitorio), una transición forzada da lugar a dos copias escribibles separadas (split-brain). Compare también el retraso para elegir el standby más actualizado.","command":{"oracle":"-- try to reach the primary: tnsping primary_db; and on the standby: SELECT thread#, MAX(sequence#) FROM v$archived_log GROUP BY thread#;","postgres":"SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();   -- choose the highest LSN among the standbys","mysql":"SHOW REPLICA STATUS\\G   -- choose the candidate with the largest Exec_Source_Log_Pos","mssql":"SELECT drs.replica_id, drs.last_hardened_lsn FROM sys.dm_hadr_database_replica_states drs;","mongodb":"rs.status()   -- the member with the highest optime is the primary candidate","redis":"redis-cli INFO replication   # choose the one with the highest slave_repl_offset","elasticsearch":"GET _cat/nodes?v   # verify whether the master node is reachable"},"choices":[{"label":"Sí, el primario ha caído: forzar la transición","next":"emergency"},{"label":"No estoy seguro / el primario podría volver","next":"planned"}]},"emergency":{"kind":"solution","title":"Failover de emergencia (el primario ha caído, transición con riesgo de pérdida de datos)","detail":"El primario es inaccesible y no puede esperar a que vuelva. Debe promover el standby de forma forzada; con replicación asíncrona pueden perderse algunas de las últimas transacciones. Su prioridad es restablecer el servicio; después, identifique la ventana de pérdida y recupere de forma segura el antiguo primario.","steps":["Verifique que el primario está realmente caído (aísle de la red / apague el antiguo primario para evitar el split-brain: fencing/STONITH).","Elija el standby más actualizado: compare el retraso/offset antes de actuar para promover el que tenga menor pérdida de datos.","Oracle Data Guard: DGMGRL> FAILOVER TO 'standby_db';  (o SQL> ALTER DATABASE ACTIVATE STANDBY DATABASE;).","PostgreSQL: promueva el standby de inmediato con SELECT pg_promote();; cuando el antiguo primario vuelva, NO lo ABRA como primario sin pg_rewind (riesgo de split-brain).","SQL Server AG: ALTER AVAILABILITY GROUP [ag] FORCE_FAILOVER_ALLOW_DATA_LOSS;  (es posible la pérdida de datos con una réplica asíncrona).","MongoDB: si no queda mayoría, reconfigure los miembros supervivientes con rs.reconfig(cfg, {force:true}).","Redis: sin Sentinel, ejecute REPLICAOF NO ONE en el candidato a nuevo master; conecte los demás a él; nunca permita que el antiguo master se reincorpore automáticamente.","Redirija la aplicación al nuevo primario; luego identifique e informe de la ventana de datos perdidos (transacciones perdidas) y reconstruya limpiamente el antiguo primario como nuevo standby."],"related":["promote-failover","replication-status","list-replicas"],"playbook":"db-down"},"drill":{"kind":"solution","title":"Simulacro / prueba de preparación (validar sin tocar producción)","detail":"Demuestre que está listo para un failover sin realizar una transición real. Verifique la salud de la réplica, que el retraso es aceptable y que la aplicación puede redirigirse, sin afectar a producción. Opcionalmente, ensaye un switchover real en una copia aislada.","steps":["Mida el retraso y verifique que está por debajo del umbral (PG: pg_stat_replication.replay_lag; MySQL: Seconds_Behind_Source; Oracle: v$dataguard_stats; Mongo: rs.printSecondaryReplicationInfo()).","Verifique la salud de las réplicas: todos los standby replican en streaming y el modo síncrono está activo en los miembros esperados (SQL Server: synchronization_state_desc='SYNCHRONIZED').","Prueba de lectura/rol: ejecute una consulta de solo lectura en el standby y confirme que obtiene respuesta sin demora (PG hot standby, Oracle Active Data Guard, réplica de lectura).","Ensayo de enrutamiento de la aplicación: en un entorno de pruebas, apunte la cadena de conexión/nombre de servicio al nuevo primario y verifique que la aplicación se conecta sin problemas (no toque el tráfico de producción).","Ensaye un switchover real de extremo a extremo en una copia aislada y mida el tiempo de transición (RTO).","Ensaye también los pasos de failback: reconstruir el antiguo primario con pg_rewind / DG reinstate / AG resume.","Registre los hallazgos, los tiempos y las carencias; actualice el runbook y programe el simulacro periódicamente."],"related":["replication-status","list-replicas","promote-failover"],"playbook":"replication-lag"}}},"solver/security-incident.json":{"id":"security-incident","title":"Incidente de seguridad / acceso sospechoso","icon":"🚨","category":"Seguridad","intro":"Para un acceso sospechoso, un cambio de privilegios inesperado o una posible fuga de datos, un árbol de decisión que conduce, según el síntoma, a los pasos de respuesta correctos (bloquear la cuenta, revocar privilegios excesivos, cortar conexiones, determinar el alcance) y al endurecimiento permanente posterior al incidente.","start":"root","nodes":{"root":{"kind":"step","title":"¿Qué ha observado?","detail":"Elija el tipo de incidente. ¿El síntoma es un inicio de sesión sospechoso o una ráfaga de inicios de sesión fallidos, un cambio inesperado de privilegios (GRANT/rol), una posible fuga de datos / exportación anómala, o el incidente está cerrado y está aplicando endurecimiento permanente? Elija la rama más adecuada según su primera observación. Primero, anote la hora del incidente y la cuenta/recurso afectado.","choices":[{"label":"Inicio de sesión sospechoso / ráfaga de inicios de sesión fallidos","next":"identity"},{"label":"Cambio inesperado de privilegios (GRANT/rol)","next":"privilege"},{"label":"Fuga de datos / exportación anómala","next":"data"},{"label":"Medidas permanentes tras el incidente (endurecimiento)","next":"harden"}]},"identity":{"kind":"step","title":"Incidente de identidad: ¿qué síntoma?","detail":"Muchos inicios de sesión fallidos apuntan a un ataque de fuerza bruta / credential stuffing. En cambio, un inicio de sesión exitoso pero inesperado (desde una IP inusual/a una hora inusual) indica que la cuenta puede estar comprometida. En ambos casos, recopile primero las evidencias y luego responda.","command":{"oracle":"SELECT username, os_username, userhost, timestamp, returncode FROM dba_audit_session WHERE returncode <> 0 ORDER BY timestamp DESC;","postgres":"-- failed logins in the log file: grep 'password authentication failed' postgresql.log | awk '{print $NF}' | sort | uniq -c | sort -rn","mysql":"SELECT * FROM performance_schema.host_cache ORDER BY COUNT_AUTHENTICATION_ERRORS DESC;   -- also 'Access denied' in the error log","mssql":"-- failed logins in the error log: EXEC xp_readerrorlog 0,1,N'Login failed';  also SELECT login_name, COUNT(*) FROM sys.dm_exec_sessions GROUP BY login_name;","mongodb":"-- audit log: grep 'authenticate' audit.json | grep '\"result\":18'  (18 = AuthenticationFailed)","redis":"ACL LOG   # shows failed authentication/command attempts","elasticsearch":"GET .security-audit-*/_search { \"query\": { \"match\": { \"event.action\": \"authentication_failed\" } } }"},"choices":[{"label":"Ráfaga de inicios de sesión fallidos (fuerza bruta)","next":"id_bruteforce"},{"label":"Inicio de sesión exitoso pero inesperado (compromiso)","next":"id_compromised"}]},"id_bruteforce":{"kind":"solution","title":"Fuerza bruta → bloquear el origen y proteger la cuenta","detail":"Con una ráfaga de inicios de sesión fallidos, puede que aún no se haya obtenido acceso; el objetivo es bloquear el origen, proteger la cuenta con bloqueo/retardo y descartar cualquier intrusión exitosa.","steps":["Identifique las IP atacantes (consultas anteriores) y bloquéelas a nivel de red: firewall / pg_hba.conf reject / ACL del listener / security group.","Bloquee temporalmente la cuenta atacada: Oracle: ALTER USER target_user ACCOUNT LOCK;  PostgreSQL: ALTER ROLE target_user NOLOGIN;  SQL Server: ALTER LOGIN [target_user] DISABLE;  MySQL: ALTER USER 'target_user'@'%' ACCOUNT LOCK;  Redis: ACL SETUSER target_user off.","Aplique un límite de intentos de inicio de sesión / política de bloqueo: Oracle: FAILED_LOGIN_ATTEMPTS en el perfil; PostgreSQL: el módulo auth_delay (shared_preload_libraries; retrasa los intentos fallidos en lugar de bloquear); MySQL: FAILED_LOGIN_ATTEMPTS ... PASSWORD_LOCK_TIME.","Restablezca de todos modos la contraseña de la cuenta atacada con un valor robusto (algún intento podría haber tenido éxito).","Verifique si algún intento tuvo éxito: busque un inicio de sesión exitoso desde la misma IP; si lo hay, siga también la rama de compromiso.","Registre el incidente con una cronología y siga el paso de endurecimiento para las medidas permanentes («Medidas permanentes tras el incidente» desde la raíz)."],"related":["change-password","list-users"],"playbook":"harden-sql"},"id_compromised":{"kind":"solution","title":"Cuenta comprometida → detener, investigar, rotar credenciales","detail":"Un inicio de sesión exitoso inesperado indica que la cuenta está comprometida. Primero detenga la cuenta y sus sesiones, investigue qué hizo, luego restablezca la contraseña y restrinja el origen de acceso.","steps":["Bloquee la cuenta de inmediato (comandos LOCK/DISABLE anteriores) y termine sus sesiones activas: Oracle: ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;  PostgreSQL: SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE usename='app_user';  SQL Server: KILL <spid>;  MySQL: KILL <id>;  Redis: CLIENT KILL.","Investigue qué hizo la cuenta en sus sesiones: las consultas que ejecutó, las tablas a las que accedió y cualquier operación GRANT/CREATE en la pista de auditoría/los logs.","Restablezca la contraseña con un valor robusto y rote todas las credenciales de servicio/aplicación relacionadas.","Busque cuentas nuevas o cambios de privilegios que el atacante haya podido crear (siga también la rama de privilegios).","Restrinja el origen de acceso: permita que la cuenta se conecte solo desde IP conocidas (pg_hba.conf / ACL del listener / firewall).","Siga el paso de endurecimiento para las medidas permanentes («Medidas permanentes tras el incidente» desde la raíz)."],"related":["change-password","list-users","show-privileges"],"playbook":"harden-sql"},"privilege":{"kind":"solution","title":"Escalada de privilegios → auditar y revocar privilegios excesivos","detail":"Un GRANT inesperado, una asignación de rol o una nueva cuenta con privilegios elevados pueden ser la segunda fase de un compromiso (persistencia/escalada de privilegios). Audite los cambios de privilegios recientes, revoque los que no sean legítimos y vuelva al principio de mínimo privilegio.","steps":["Audite los cambios de privilegios recientes: Oracle: SELECT * FROM dba_audit_trail WHERE action_name IN ('GRANT','REVOKE','CREATE USER') ORDER BY timestamp DESC;  SQL Server: eventos GRANT en la default trace / Server Audit y SELECT * FROM sys.server_permissions;  MySQL: entradas GRANT en el audit log y SELECT * FROM mysql.user;  MongoDB: db.getRoles({showPrivileges:true}).","Liste los privilegios actuales: Oracle: SELECT grantee, privilege FROM dba_sys_privs WHERE grantee='APP_USER';  PostgreSQL: SELECT * FROM information_schema.role_table_grants WHERE grantee='app_user';  MySQL: SHOW GRANTS FOR 'app_user'@'%';","Revoque los privilegios excesivos ilegítimos: Oracle: REVOKE DBA FROM app_user;  PostgreSQL: REVOKE ALL ON ... FROM app_user; ALTER ROLE app_user NOSUPERUSER;  SQL Server: ALTER SERVER ROLE [sysadmin] DROP MEMBER [app_user];  MySQL: REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'app_user'@'%';","Busque y elimine cuentas ocultas/de respaldo que el atacante haya podido crear (registros CREATE USER inesperados).","Revise y restrinja los privilegios excesivos concedidos a PUBLIC o a roles amplios.","Verifique que los cambios corresponden a una solicitud de cambio legítima; trate todo lo que no corresponda como parte del incidente y siga el paso de endurecimiento."],"related":["show-privileges","list-users","create-user-grant"],"playbook":"harden-sql"},"data":{"kind":"solution","title":"Fuga de datos → cortar el acceso y determinar el alcance","detail":"Consultas/exportaciones anormalmente grandes o conexiones externas inesperadas apuntan a una fuga de datos. Primero corte las conexiones peligrosas activas y luego determine el alcance con la pista de auditoría/los logs: a qué datos se accedió, quién y cuándo.","steps":["Revise los orígenes de las conexiones activas: PostgreSQL: SELECT pid, usename, client_addr, state, query FROM pg_stat_activity WHERE client_addr IS NOT NULL;  Oracle: SELECT sid, serial#, username, machine, program FROM v$session;  SQL Server: SELECT * FROM sys.dm_exec_connections;  MongoDB: db.currentOp(true);  Redis: CLIENT LIST.","Corte de inmediato las conexiones sospechosas/externas y bloquee esa cuenta (comandos KILL/LOCK).","Bloquee el origen a nivel de red: cierre el bloque de IP sospechoso con firewall / pg_hba.conf / ACL del listener.","Revise el audit log: determine qué tablas fueron objeto de SELECT/EXPORT, el número de filas y la ventana temporal (Oracle unified_audit_trail; SQL Server sys.fn_get_audit_file; logs de pgaudit en PostgreSQL; MongoDB auditLog).","Busque rastros de exportación: conjuntos de resultados grandes, COPY / SELECT ... INTO OUTFILE, mongoexport, patrones de lectura masiva.","Documente el alcance de los datos afectados (qué tablas, qué campos personales/confidenciales) y el posible volumen de datos; evalúe las obligaciones legales/de notificación y siga el paso de endurecimiento."],"related":["show-privileges","list-users"],"playbook":"harden-sql"},"harden":{"kind":"solution","title":"Endurecimiento permanente tras el incidente","detail":"Tras la respuesta de emergencia, aplique controles permanentes para evitar que se repita: habilite la auditoría, aplique el mínimo privilegio, restrinja el acceso de red y registre el incidente por completo.","steps":["Habilite la auditoría: capture los eventos críticos (inicios de sesión, GRANT/REVOKE, acceso a tablas sensibles) en la pista de auditoría; para más detalles, siga el árbol «Configuración de auditoría».","Mínimo privilegio: conceda a cada cuenta solo los privilegios que necesita, separe las cuentas compartidas/de servicio, deshabilite las cuentas no utilizadas.","Política de contraseñas/identidad: contraseñas robustas, rotación periódica, autenticación multifactor cuando sea posible e identidad centralizada (LDAP/Kerberos).","Restricción de red: abra la base de datos solo a los servidores de aplicación (firewall, security group, pg_hba.conf, ACL del listener); bloquee el acceso directo desde internet.","Cifrado: habilite TLS en tránsito y, cuando se requiera, cifrado en reposo.","Monitoreo/alertas: configure alertas para picos de inicios de sesión fallidos, nuevos GRANT y volúmenes de datos anómalos.","Registro del incidente: informe de la cronología, los activos afectados, las acciones realizadas y las lecciones aprendidas; actualice el runbook."],"related":["list-users","show-privileges","change-password"]}}},"solver/audit-setup.json":{"id":"audit-setup","title":"Configuración de auditoría","icon":"🧾","category":"Seguridad","intro":"Un árbol de decisión que, según lo que quiera auditar (inicios de sesión/sesiones, cambios DDL/de esquema, acceso a datos o cambios de privilegios), habilita la auditoría para ese alcance en el motor de base de datos adecuado y luego ofrece los pasos para leer los registros y para su retención/rotación.","start":"root","nodes":{"root":{"kind":"step","title":"¿Qué quiere auditar?","detail":"Elija el alcance de la auditoría. Auditarlo todo genera ruido y costo; limítelo a sus necesidades. ¿Quiere seguir eventos de inicio de sesión/sesión, cambios DDL/de esquema, acceso a datos (DML/SELECT) o cambios de privilegios (GRANT/REVOKE)?","choices":[{"label":"Inicios de sesión / sesiones (login, connect)","next":"login"},{"label":"Cambios DDL / de esquema (CREATE/ALTER/DROP)","next":"ddl"},{"label":"Acceso a datos (DML / SELECT)","next":"dml"},{"label":"Cambios de privilegios (GRANT / REVOKE)","next":"grants"},{"label":"Lectura de registros de auditoría y retención / rotación","next":"retention"}]},"login":{"kind":"solution","title":"Auditoría de inicios de sesión / sesiones","detail":"Registra los inicios de sesión exitosos y fallidos: quién se conectó, desde dónde y cuándo. Es la base para detectar accesos no autorizados e intentos de fuerza bruta. Habilite la política correspondiente en cada motor y luego lea y conserve los registros.","steps":["Oracle (Unified Auditing): CREATE AUDIT POLICY login_pol ACTIONS LOGON, LOGOFF;  AUDIT POLICY login_pol;  (para los fallos: AUDIT POLICY login_pol WHENEVER NOT SUCCESSFUL;).","PostgreSQL: configure log_connections=on y log_disconnections=on en postgresql.conf y recargue; las conexiones se registran sin necesidad de pgaudit.","MySQL/MariaDB: instale el plugin audit_log (INSTALL PLUGIN audit_log ...) y ejecute SET GLOBAL audit_log_policy='LOGINS'; (MariaDB: server_audit_events='CONNECT').","SQL Server: cree un Server Audit (CREATE SERVER AUDIT ...), añada FAILED_LOGIN_GROUP y SUCCESSFUL_LOGIN_GROUP a una Server Audit Specification y aplique ENABLE a ambos.","MongoDB: inicie con --auditDestination file --auditFilter '{ atype: { $in: [\"authenticate\", \"logout\"] } }' (Enterprise/Percona).","Redis: siga los intentos fallidos de autenticación/comandos con ACL LOG (para más detalles use el ACL log, no 'notify').","Elasticsearch: configure xpack.security.audit.enabled: true en elasticsearch.yml y añada authentication_success, authentication_failed a la lista include.","Para leer y conservar/rotar los registros, siga el paso «Lectura de registros de auditoría y retención / rotación» desde la raíz."],"related":["list-users","change-password"]},"ddl":{"kind":"solution","title":"Auditoría de cambios DDL / de esquema","detail":"Registra los cambios de esquema como CREATE/ALTER/DROP. Se usa para detectar cambios no autorizados en objetos, tablas ocultas/puertas traseras y la deriva del esquema.","steps":["Oracle: CREATE AUDIT POLICY ddl_pol ACTIONS CREATE TABLE, ALTER TABLE, DROP TABLE, CREATE USER, ALTER USER;  AUDIT POLICY ddl_pol;","PostgreSQL (pgaudit): cárguelo con shared_preload_libraries='pgaudit' y luego ALTER SYSTEM SET pgaudit.log='ddl'; SELECT pg_reload_conf();","MySQL/MariaDB: con audit_log, SET GLOBAL audit_log_policy='ALL'; luego filtre las sentencias DDL en el log (MariaDB: server_audit_events='QUERY_DDL').","SQL Server: añada SCHEMA_OBJECT_CHANGE_GROUP (y DATABASE_OBJECT_CHANGE_GROUP) a una Database Audit Specification y aplique ENABLE.","MongoDB: --auditFilter '{ atype: { $in: [\"createCollection\",\"dropCollection\",\"createIndex\",\"dropIndex\",\"createDatabase\",\"dropDatabase\"] } }'.","Elasticsearch: añada los eventos relacionados con index_creation/index_deletion a la lista include de auditoría; para los cambios de esquema (mapping), registre las llamadas a _mapping como solicitudes.","Para leer y conservar/rotar los registros, siga el paso «Lectura de registros de auditoría y retención / rotación» desde la raíz."],"related":["show-privileges"]},"dml":{"kind":"solution","title":"Auditoría de acceso a datos (DML / SELECT)","detail":"Registra el acceso de lectura/escritura (SELECT/INSERT/UPDATE/DELETE) a tablas sensibles. Como puede generar un volumen elevado, limite la auditoría a los objetos sensibles. Es fundamental para la detección de fugas y el cumplimiento normativo (KVKK/GDPR).","steps":["Limite el alcance: audite solo las tablas/columnas sensibles, no todos los datos (para controlar el ruido y el costo).","Oracle: CREATE AUDIT POLICY dml_pol ACTIONS SELECT, INSERT, UPDATE, DELETE ON HR.SALARIES;  AUDIT POLICY dml_pol;","PostgreSQL (pgaudit): para un registro amplio use ALTER SYSTEM SET pgaudit.log='read,write'; o, para una auditoría dirigida a nivel de objeto, conceda al rol de auditoría privilegios solo sobre las tablas relevantes y configure pgaudit.role.","MySQL/MariaDB: registre solo las tablas/usuarios relevantes con un filtro de audit_log (audit_log_filter / MariaDB server_audit_events='QUERY_DML').","SQL Server: añada las acciones SELECT, INSERT, UPDATE, DELETE para tablas concretas y un principal (p. ej. public) a una Database Audit Specification y aplique ENABLE.","MongoDB: --auditFilter '{ atype: \"authCheck\", \"param.command\": { $in: [\"find\",\"insert\",\"update\",\"delete\"] }, \"param.ns\": \"db.sensitive\" }' (vigile el volumen).","Elasticsearch: añada access_granted/access_denied a la lista include de auditoría y use emit_request_body para ver a qué índice apunta una consulta (tenga cuidado al registrar cuerpos con datos sensibles).","Para leer y conservar/rotar los registros, siga el paso «Lectura de registros de auditoría y retención / rotación» desde la raíz."],"related":["show-privileges","list-users"]},"grants":{"kind":"solution","title":"Auditoría de cambios de privilegios (GRANT / REVOKE)","detail":"Registra los cambios de privilegios y roles (GRANT/REVOKE, asignación de roles, creación de usuarios). Es la base para detectar escaladas de privilegios y cuentas de puerta trasera; suele ser una auditoría de bajo volumen pero de alto valor.","steps":["Oracle: CREATE AUDIT POLICY priv_pol ACTIONS GRANT, REVOKE, CREATE USER, ALTER USER, CREATE ROLE;  AUDIT POLICY priv_pol;","PostgreSQL (pgaudit): ALTER SYSTEM SET pgaudit.log='role'; SELECT pg_reload_conf();  (captura los eventos GRANT/REVOKE/CREATE ROLE).","MySQL/MariaDB: registre las sentencias GRANT/REVOKE/CREATE USER con audit_log (política 'ALL' o DDL) y mantenga las sentencias de ACL en el filtro.","SQL Server: añada SERVER_PERMISSION_CHANGE_GROUP, SERVER_ROLE_MEMBER_CHANGE_GROUP y DATABASE_PERMISSION_CHANGE_GROUP a una Server Audit Specification y aplique ENABLE.","MongoDB: --auditFilter '{ atype: { $in: [\"grantRolesToUser\",\"revokeRolesFromUser\",\"createUser\",\"dropUser\",\"createRole\"] } }'.","Elasticsearch: para los cambios de roles/usuarios, registre en la pista de auditoría los eventos de solicitudes relacionados con la seguridad (llamadas a las API de realm/role).","Para leer y conservar/rotar los registros, siga el paso «Lectura de registros de auditoría y retención / rotación» desde la raíz."],"related":["show-privileges","create-user-grant","list-users"]},"retention":{"kind":"solution","title":"Lectura de registros de auditoría y retención / rotación","detail":"La auditoría solo es útil si los registros se leen y se conservan. Defina aquí cómo consultar los registros, dónde se guardan y la política de retención/rotación que mantiene su crecimiento bajo control.","steps":["Lea los registros: Oracle: SELECT event_timestamp, dbusername, action_name, object_name, sql_text FROM unified_audit_trail ORDER BY event_timestamp DESC;  PostgreSQL: la salida de pgaudit va al archivo de log del servidor (log_directory); filtre las líneas 'AUDIT:' con grep.","SQL Server: SELECT * FROM sys.fn_get_audit_file('D:\\audit\\*.sqlaudit', DEFAULT, DEFAULT);  MySQL: lea el archivo audit_log (JSON/XML) o el server_audit.log de MariaDB; MongoDB: analice audit.json; Elasticsearch: consulte los índices .security-audit-*.","Defina el periodo de retención (según los requisitos de cumplimiento, p. ej. 90 días / 1 año) y plásmelo en la política.","Rotación/limpieza: Oracle: archive/purgue los registros antiguos con DBMS_AUDIT_MGMT.SET_LAST_ARCHIVE_TIMESTAMP y CLEAN_AUDIT_TRAIL; para los logs en archivo de PostgreSQL/MySQL/MongoDB, rote por tamaño/tiempo con logrotate.","Elasticsearch: defina una política ILM (rollover + delete) para los índices de auditoría.","Envíe los registros de auditoría a una ubicación inmutable (WORM) con un acceso separado del de los DBA (separación de funciones); restrinja el acceso.","Monitoree el crecimiento del almacenamiento: compruebe periódicamente que la auditoría no llena el disco ni afecta excesivamente al rendimiento."],"related":["list-users","show-privileges"]}}},"solver/decide-ha.json":{"id":"decide-ha","title":"Decisión: ¿qué solución de HA/DR?","icon":"🧭","category":"Guía de decisión","intro":"La elección de una solución de alta disponibilidad y recuperación ante desastres no empieza por la tecnología, sino por el equilibrio entre los requisitos de negocio (RTO/RPO), la necesidad de distribución geográfica y el costo. Partiendo de su tolerancia a la interrupción y a la pérdida de datos, este árbol le orienta hacia el enfoque adecuado entre réplicas síncronas, standby asíncronos, clústeres con failover automático y backup+PITR.","start":"root","nodes":{"root":{"kind":"step","title":"¿Cuál es su tolerancia a la pérdida de datos (RPO)?","detail":"Empiece por la restricción más dura: ante un desastre, ¿cuántos segundos de datos puede permitirse perder? Si se exige cero pérdida de datos (RPO=0), la replicación síncrona es obligatoria; si unos segundos/minutos son aceptables, se abren las soluciones asíncronas y enfoques más económicos. El objetivo de RPO suele condicionar todas las demás decisiones.","choices":[{"label":"Cero pérdida de datos es obligatorio (RPO = 0)","next":"rpo_zero"},{"label":"Unos segundos/minutos de pérdida de datos son aceptables","next":"rpo_soft"}]},"rpo_zero":{"kind":"step","title":"¿También se requiere una toma de control automática y rápida (RTO bajo)?","detail":"Quiere cero pérdida de datos; pero ¿con qué rapidez debe volver a estar operativo cuando cae el primario? ¿Failover automático en segundos, o es aceptable una breve ventana de intervención manual? Esto marca la diferencia entre una simple réplica síncrona y un clúster totalmente automatizado.","choices":[{"label":"Se requiere failover automático en segundos","next":"sol_cluster"},{"label":"Basta con una toma de control manual/planificada; lo primero es cero pérdida","next":"sol_sync"}]},"rpo_soft":{"kind":"step","title":"¿Se requiere otra región/geografía (DR)?","detail":"Si cierta pérdida de datos es aceptable, la verdadera pregunta es el alcance de la resiliencia: ¿debe protegerse frente a la pérdida de un centro de datos/región completo, o basta con protegerse frente al fallo de un solo nodo en la misma región? Si se requiere distribución geográfica, cobra protagonismo un standby asíncrono; si no, se evalúan las opciones que priorizan el costo.","choices":[{"label":"Sí, es obligatoria una región separada frente a desastres geográficos","next":"sol_async"},{"label":"No, lo primero son el presupuesto y la simplicidad","next":"budget"}]},"budget":{"kind":"step","title":"¿Puede aceptar un tiempo de recuperación largo (RTO alto)?","detail":"Se queda en una sola región y tolera cierta pérdida de datos. ¿Busca el costo más bajo, o quiere un nodo standby que tome el control rápidamente ante un fallo? Si es aceptable que la recuperación lleve horas, backup+PITR por sí solo es el camino más económico; de lo contrario, necesita un warm standby.","choices":[{"label":"Sí, es aceptable que la recuperación lleve horas (lo más barato)","next":"sol_backup"},{"label":"No, se requiere un warm standby que tome el control en minutos","next":"sol_async"}]},"sol_sync":{"kind":"solution","title":"Recomendación: réplica síncrona (cero pérdida de datos)","detail":"El primario no completa un commit hasta que al menos un standby lo confirma; así, aunque se pierda el primario, no se pierde ninguna transacción confirmada (RPO=0). La solución ideal dentro de una región con una red de baja latencia.","steps":["Por qué: un commit no se completa hasta que las transacciones confirmadas se escriben en el standby, por lo que la pérdida de datos es matemáticamente cero.","Cuándo: OLTP financiero/crítico, en la misma región (misma zona de disponibilidad o adyacente), con una red de RTT bajo disponible.","Ejemplo: PostgreSQL synchronous_commit + synchronous_standby_names, MySQL Group Replication / semisync, Oracle Data Guard SYNC (Maximum Protection/Availability).","Precaución: la confirmación síncrona añade latencia de red a cada commit; si el standby se ralentiza/desconecta y solo hay una copia síncrona, el primario también puede dejar de aceptar escrituras. Diseñe con al menos dos standby o un mecanismo de respaldo adecuado.","Precaución: no replique de forma síncrona a una región remota/de alta latencia; el rendimiento de escritura se desploma. Use replicación asíncrona para el DR geográfico."],"related":["replication-status","promote-failover"]},"sol_async":{"kind":"solution","title":"Recomendación: standby asíncrono (DR geográfico)","detail":"El standby aplica los cambios en streaming sin retrasar los commits del primario. Es la forma estándar de mantener una copia en otra región sin perjudicar el rendimiento de escritura; a cambio, se acepta una pequeña ventana de pérdida de datos (igual al retraso de replicación).","steps":["Por qué: el primario no espera la confirmación del standby, por lo que la replicación a una región remota no añade latencia de escritura.","Cuándo: cuando se necesita recuperación ante desastres a nivel de región/centro de datos y es aceptable una pérdida de datos del orden de segundos.","Ejemplo: PostgreSQL streaming replication (async), replicación asíncrona de MySQL, Oracle Data Guard ASYNC (Maximum Performance).","Precaución: en el failover puede perder tantos datos como el retraso; monitoree continuamente el retraso de replicación y configure alertas por umbral.","Precaución: la toma de control suele hacerse manualmente o mediante orquestación externa; son imprescindibles un procedimiento de failover claro y simulacros periódicos."],"related":["replication-status","promote-failover"]},"sol_cluster":{"kind":"solution","title":"Recomendación: clúster con failover automático","detail":"Una solución de clúster que combina monitoreo de salud, elección de líder y toma de control automática; persigue tanto un RTO bajo (failover automático en segundos) como un RPO bajo/cero con una copia síncrona. La carga operativa es mayor, pero la intervención humana se minimiza.","steps":["Por qué: el gestor del clúster detecta automáticamente un fallo del primario, promueve una copia sana y redirige el tráfico; sin intervención manual a medianoche.","Cuándo: si tanto un RTO bajo como un RPO bajo son obligatorios y el equipo de operaciones puede gestionar la complejidad adicional.","Ejemplo: Patroni/etcd para PostgreSQL, MySQL InnoDB Cluster (Group Replication + MySQL Router), SQL Server Always On Availability Groups, Oracle RAC + Data Guard con Fast-Start Failover (el failover automático requiere FSFO y un observer).","Precaución: el riesgo de split-brain es real; no despliegue sin un fencing/quórum adecuado (un número impar de nodos, un almacén distribuido fiable).","Precaución: el propio clúster es una nueva superficie de fallo; ensaye el failover periódicamente y valide la automatización en condiciones reales."],"related":["promote-failover","replication-status"]},"sol_backup":{"kind":"solution","title":"Recomendación: backup + PITR (lo más económico)","detail":"En lugar de un standby en ejecución, solo se mantienen backups periódicos y la recuperación a un punto en el tiempo (PITR) mediante WAL/logs archivados. Es la solución de menor costo; el precio es que la recuperación puede llevar horas (RTO alto).","steps":["Por qué: no hay costo de un segundo nodo en ejecución continua; solo se mantienen el almacenamiento de backups y los logs archivados.","Cuándo: sistemas no críticos con alta tolerancia a la interrupción; entornos de desarrollo/pruebas; cuando lo primero son el presupuesto y la simplicidad.","Ejemplo: PITR con un backup físico + logs archivados (PG base backup + WAL, Oracle RMAN + archivelog, MySQL xtrabackup + binlog).","Precaución: el RTO es alto: el tiempo de restauración + aplicación de logs crece con el tamaño de los datos; mídalo para confirmar que se ajusta al tiempo que acepta.","Precaución: un backup no probado no es un backup; realice periódicamente simulacros de restauración y verificación de backups."],"related":["last-backup-status","logical-dump"]}}},"solver/decide-isolation.json":{"id":"decide-isolation","title":"Decisión: ¿qué nivel de aislamiento?","icon":"🧭","category":"Guía de decisión","intro":"El nivel de aislamiento de las transacciones determina el equilibrio entre concurrencia y consistencia. Un aislamiento mayor implica menos anomalías, pero más bloqueos/conflictos y reintentos. Este árbol le ayuda a elegir entre Read Committed, Repeatable Read/Snapshot y Serializable según las anomalías de lectura (dirty, non-repeatable, phantom) que pueda tolerar.","start":"root","nodes":{"root":{"kind":"step","title":"¿Puede tolerar lecturas sucias (dirty reads)?","detail":"Empiece por la anomalía más básica: ¿debe una transacción poder leer datos que aún no se han confirmado (y que pueden revertirse)? Casi ninguna aplicación seria lo quiere; en la mayoría de los motores modernos el valor por defecto ya impide las lecturas sucias. Solo se relaja para recuentos aproximados/analíticos y máxima velocidad.","choices":[{"label":"No, solo debo leer datos confirmados","next":"committed"},{"label":"Sí, basta una lectura aproximada (poco frecuente)","next":"sol_read_uncommitted"}]},"committed":{"kind":"step","title":"¿Debe ver el mismo resultado al volver a leer dentro de la misma transacción?","detail":"Descartadas las lecturas sucias, la siguiente pregunta: si lee la misma fila dos veces dentro de una transacción, ¿es un problema ver un valor distinto porque alguien confirmó entre medias (non-repeatable read), o que aparezcan filas nuevas (phantom)? La lógica que exige consistencia en informes/cálculos suele necesitar una instantánea consistente.","choices":[{"label":"No, está bien que cada sentencia vea los últimos datos confirmados","next":"sol_read_committed"},{"label":"Sí, se necesita una instantánea consistente para toda la transacción","next":"consistent"}]},"consistent":{"kind":"step","title":"¿Deben las transacciones concurrentes estar lógicamente ordenadas por completo?","detail":"Quiere lecturas consistentes; ¿es suficiente, o las transacciones concurrentes deben producir un resultado como si se hubieran ejecutado una a una, en secuencia (serializable)? Si puede tolerar anomalías sutiles como el write skew (p. ej. dos transacciones que leen filas distintas y escriben de forma que se invalidan mutuamente), Snapshot es suficiente; de lo contrario, se requiere serialización completa.","choices":[{"label":"No, basta con una instantánea consistente","next":"sol_repeatable_read"},{"label":"Sí, no es aceptable ninguna anomalía de concurrencia","next":"sol_serializable"}]},"sol_read_uncommitted":{"kind":"solution","title":"Recomendación: Read Uncommitted (solo casos especiales)","detail":"El aislamiento más bajo; permite lecturas sucias. Lee casi sin esperar a los bloqueos, pero puede ver datos que pueden revertirse. No se recomienda para aplicaciones de propósito general.","steps":["Por qué: el lector no espera al bloqueo del escritor; máxima concurrencia pero sin garantía de consistencia.","Cuándo: solo en casos como métricas aproximadas/recuentos de progreso donde la exactitud no importa.","Precaución: en PostgreSQL este nivel se comporta como Read Committed, y Oracle no ofrece Read Uncommitted en absoluto; las lecturas sucias reales aparecen sobre todo en SQL Server, basado en bloqueos (NOLOCK).","Precaución: «NOLOCK por velocidad» no debe convertirse en un hábito permanente; conlleva el riesgo de leer filas erróneas/duplicadas/ausentes."]},"sol_read_committed":{"kind":"solution","title":"Recomendación: Read Committed (OLTP por defecto)","detail":"Cada sentencia ve los últimos datos confirmados en ese momento; se impiden las lecturas sucias. Es el valor por defecto en la mayoría de los motores y ofrece el mejor equilibrio entre concurrencia y consistencia para cargas OLTP típicas.","steps":["Por qué: impide las lecturas sucias sin obligar a la transacción a mantener bloqueos más tiempo del necesario; proporciona alta concurrencia.","Cuándo: transacciones OLTP cortas y focalizadas; el valor por defecto correcto para la gran mayoría de las aplicaciones web.","Ejemplo: Read Committed es el valor por defecto en PostgreSQL, SQL Server y Oracle (en Oracle, la consistencia la proporciona una instantánea a nivel de sentencia).","Precaución: dos lecturas en la misma transacción pueden devolver resultados distintos (non-repeatable/phantom); no base en ello la lógica que necesita informes consistentes.","Precaución: el valor por defecto de MySQL/InnoDB es Repeatable Read; tenga en cuenta la diferencia entre motores al migrar."]},"sol_repeatable_read":{"kind":"solution","title":"Recomendación: Repeatable Read / Snapshot (lecturas consistentes)","detail":"La transacción trabaja sobre una instantánea consistente fijada al inicio (o en su primera lectura); las lecturas no cambian durante la transacción. Con MVCC, los lectores no bloquean a los escritores y los escritores no bloquean a los lectores.","steps":["Por qué: proporciona una vista fija durante toda la transacción; impide las lecturas no repetibles (y en la práctica también los phantoms en los motores MVCC), lo que hace consistente la lógica de informes/cálculos con muchas lecturas.","Cuándo: informes, conciliaciones y cálculos por lotes que leen los mismos datos varias veces en una transacción y necesitan resultados consistentes.","Ejemplo: MySQL/InnoDB, Repeatable Read por defecto; SQL Server SNAPSHOT (row versioning); PostgreSQL REPEATABLE READ; el SERIALIZABLE de Oracle es en realidad semántica de snapshot.","Precaución: no impide anomalías de serialización como el write skew; suba a Serializable si la regla de negocio lo exige.","Precaución: Snapshot/row versioning puede hacer crecer tempdb o el undo/version store; las transacciones largas inflan las versiones antiguas."]},"sol_serializable":{"kind":"solution","title":"Recomendación: Serializable (aislamiento completo)","detail":"El aislamiento más alto: las transacciones concurrentes producen un resultado como si se hubieran ejecutado en serie (una a una, en secuencia). Impide todas las anomalías de lectura y el write skew; el precio son más conflictos y la necesidad de reintentos.","steps":["Por qué: garantiza que el resultado siempre equivale a una ejecución en serie válida; elimina todas las anomalías de concurrencia.","Cuándo: lógica con invariantes críticos para la corrección: comprobaciones de saldo/stock, casos en los que nunca debe producirse una doble reserva.","Ejemplo: PostgreSQL SSI (Serializable Snapshot Isolation), SQL Server SERIALIZABLE (bloqueos de rango); en Oracle, la serialización real se gestiona a nivel de aplicación.","Precaución: los fallos de serialización (p. ej. 40001 en PG) son el comportamiento esperado; es OBLIGATORIA una lógica de reintento que vuelva a ejecutar automáticamente la transacción en la aplicación.","Precaución: el throughput puede caer con cargas con muchos conflictos; no lo aplique en todas partes, sino solo a las transacciones en las que la corrección es crítica."]}}},"solver/decide-sqlnosql.json":{"id":"decide-sqlnosql","title":"Decisión: ¿SQL o NoSQL (qué familia de bases de datos)?","icon":"🧭","category":"Guía de decisión","intro":"La elección de la familia de bases de datos adecuada depende de la forma del modelo de datos, de sus prioridades de consistencia y escala y de la complejidad de sus consultas. Generalizaciones como «NoSQL es más moderno» son engañosas; cada familia está diseñada para patrones de acceso concretos. Partiendo de la estructura de los datos, este árbol le orienta hacia la herramienta adecuada entre motores relacionales, documentales, clave-valor y de búsqueda, y le recuerda el enfoque políglota cuando hace falta.","start":"root","nodes":{"root":{"kind":"step","title":"¿Cuál es la naturaleza de los datos y el patrón de acceso predominante?","detail":"Empiece por la pregunta más decisiva: ¿los datos son registros estructurados con relaciones fuertes, documentos con un esquema flexible/variable, búsquedas simples por clave-valor o una necesidad especial como la búsqueda de texto? La forma de los datos y cómo los consulta determinan en gran medida la familia.","choices":[{"label":"Registros relacionales y estructurados, y consultas complejas","next":"relational_check"},{"label":"Documentos anidados con un esquema flexible/variable","next":"sol_document"},{"label":"Clave-valor simple, latencia muy baja/caché","next":"sol_keyvalue"},{"label":"Búsqueda de texto completo / por facetas, analítica de logs","next":"sol_search"}]},"relational_check":{"kind":"step","title":"¿La prioridad es la consistencia fuerte (ACID) o la escala horizontal?","detail":"Los datos parecen relacionales; pero ¿cuál es su prioridad? ¿Son esenciales los joins entre varias tablas, la integridad transaccional y la consistencia fuerte (ACID), o lo es escalar horizontalmente un volumen enorme que no cabe en un solo nodo? El modelo relacional es el valor por defecto correcto para la mayoría de las cargas; solo se ve forzado cuando la escala supera realmente un solo nodo.","choices":[{"label":"Lo primero son ACID, los joins y la integridad de las consultas complejas","next":"sol_relational"},{"label":"La escala por encima de todo, relaciones/joins débiles","next":"sol_document"}]},"sol_relational":{"kind":"solution","title":"Recomendación: base de datos relacional (RDBMS)","detail":"El valor por defecto correcto para cargas con datos estructurados y fuertemente relacionados que requieren integridad transaccional. Las garantías ACID, un motor de consultas SQL maduro y los joins le permiten responder de forma fiable a preguntas complejas.","steps":["Por qué: ofrece transacciones ACID, integridad de claves foráneas y consistencia fuerte, además de un planificador maduro que optimiza consultas complejas que unen varias tablas.","Cuándo: cargas OLTP y de informes en las que las relaciones y la consistencia son críticas, como finanzas, pedidos/inventario, ERP/CRM.","Ejemplo: PostgreSQL, MySQL, Oracle, SQL Server; para necesidades de series temporales, extensiones como PostgreSQL + TimescaleDB.","Precaución: los cambios de esquema (migraciones) requieren disciplina; si los cambios de esquema son frecuentes e impredecibles, evalúe el modelo documental.","Precaución: si alcanza el límite de escritura/datos de un solo nodo, escale con réplicas de lectura, particionamiento y, si es necesario, sharding."],"related":["create-partition"]},"sol_document":{"kind":"solution","title":"Recomendación: base de datos documental","detail":"Almacena datos flexibles y anidados con un esquema que cambia con frecuencia en su forma natural (documentos tipo JSON). Adecuada para aplicaciones que leen/escriben mayoritariamente a través de un único documento, necesitan pocos joins y requieren escala horizontal.","steps":["Por qué: el esquema es flexible; los documentos pueden añadir o quitar campos, obtener estructuras anidadas en una sola lectura y se prestan a la escala horizontal (sharding).","Cuándo: modelos de datos centrados en documentos, de estructura variable y en rápida evolución, como catálogos, perfiles de usuario, contenidos/CMS, registros de eventos.","Ejemplo: MongoDB, Couchbase; en el mundo relacional, JSONB (PostgreSQL) ofrece una flexibilidad similar de forma limitada.","Precaución: los joins complejos entre varios documentos y los informes son débiles; si dominan las relaciones, el modelo relacional es más adecuado.","Precaución: la responsabilidad de la consistencia y de la validación del esquema recae en gran medida en la aplicación; proteja la calidad de los datos en el código."]},"sol_keyvalue":{"kind":"solution","title":"Recomendación: almacén clave-valor / caché","detail":"Diseñado para cargas que necesitan lecturas/escrituras muy rápidas por clave y ninguna consulta compleja. Suele ejecutarse en memoria y ofrece latencias de microsegundos/milisegundos; ideal para tareas como caché, sesiones y contadores.","steps":["Por qué: latencia extremadamente baja y alto throughput para el acceso simple por clave; las estructuras en memoria quitan carga a la base de datos relacional.","Cuándo: capa de caché, almacén de sesiones, contadores de rate limit, clasificaciones (leaderboards), colas temporales/pub-sub.","Ejemplo: Redis, Memcached; Redis ofrece además estructuras ricas como listas/sets/sorted sets y opciones de persistencia.","Precaución: no suele ser la única fuente de verdad; mantenga los datos de origen en una base de datos duradera y almacene aquí los datos derivados/en caché.","Precaución: la memoria es limitada y cara; planifique la política de expulsión, los TTL y los ajustes de persistencia/durabilidad según las necesidades del negocio."]},"sol_search":{"kind":"solution","title":"Recomendación: motor de búsqueda / analítica","detail":"Un motor basado en índice invertido especializado en búsqueda de texto completo, ordenación por relevancia, filtrado por facetas y analítica de logs/texto de gran volumen. Ofrece la experiencia de búsqueda que las consultas LIKE relacionales no pueden dar.","steps":["Por qué: con un índice invertido y puntuación, realiza búsqueda de texto completo, autocompletado, facetas y agregaciones con alto rendimiento.","Cuándo: búsqueda de productos/en el sitio, analítica de logs y observabilidad, aplicaciones que necesitan una experiencia de búsqueda ordenada por relevancia.","Ejemplo: Elasticsearch/OpenSearch, Apache Solr; PostgreSQL tsvector puede bastar para búsqueda de texto completo a escala media.","Precaución: no suele ser el sistema de registro principal; mantenga los datos principales en una BD relacional/documental y alimente el índice de búsqueda desde ella (CDC/sync).","Precaución: las garantías de consistencia fuerte en tiempo real son débiles (indexación casi en tiempo real); tenga en cuenta la latencia de indexación en el diseño."],"related":["setup-cdc-replication"]}}},"solver/decide-shard-replica.json":{"id":"decide-shard-replica","title":"Decisión: ¿sharding o replicación (escalado)?","icon":"🧭","category":"Guía de decisión","intro":"La decisión de escalado de la base de datos depende de si el cuello de botella son las lecturas o las escrituras y de si los datos caben en un solo nodo. Una estrategia equivocada implica complejidad innecesaria o un cuello de botella sin resolver. Este árbol le recuerda primero las opciones más baratas y sencillas (escalado vertical, caché) y luego le ayuda a elegir correctamente entre réplicas de lectura y sharding.","start":"root","nodes":{"root":{"kind":"step","title":"¿Ha probado primero el camino más sencillo: escalado vertical y ajuste de consultas?","detail":"Antes de pasar a una arquitectura distribuida, obtenga la mejora más barata: ampliar el hardware (CPU/RAM/IOPS), añadir los índices que faltan y corregir las consultas costosas resuelve la mayoría de los cuellos de botella. Añada complejidad solo cuando estas opciones se agoten.","choices":[{"label":"No / aún no he llegado al límite","next":"sol_vertical"},{"label":"Sí, el escalado vertical y el ajuste no bastan","next":"bottleneck"}]},"bottleneck":{"kind":"step","title":"¿El cuello de botella son las lecturas o las escrituras/el volumen de datos?","detail":"Aísle el origen del problema de escalado: ¿muchas consultas de lectura saturan un solo nodo (carga de lectura intensiva), o la tasa de escritura o el tamaño total de los datos superan la capacidad de un solo nodo (carga intensiva de escritura/volumen)? Esta distinción es la diferencia fundamental entre réplicas y sharding.","choices":[{"label":"Domina el tráfico de lectura, las escrituras caben en un solo nodo","next":"read_path"},{"label":"La tasa de escritura o el volumen de datos superan un solo nodo","next":"sol_sharding"}]},"read_path":{"kind":"step","title":"¿Las lecturas son repetitivas/cacheables, o recientes y variadas?","detail":"Divida el cuello de botella de lectura un paso más: ¿se leen los mismos resultados una y otra vez (una caché es muy eficaz), o las lecturas piden datos variados, amplios y actuales (se necesita capacidad de lectura adicional)? Esto decide entre una capa de caché y réplicas de lectura.","choices":[{"label":"Los mismos datos se leen repetidamente, es aceptable un ligero desfase","next":"sol_cache"},{"label":"Lecturas variadas/recientes, se necesita capacidad de lectura bruta","next":"sol_read_replica"}]},"sol_vertical":{"kind":"solution","title":"Recomendación: primero escalado vertical + ajuste de consultas","detail":"El primer y más barato camino antes de pasar a una arquitectura distribuida. Ampliar el hardware y corregir consultas/índices resuelve la mayoría de los problemas de escalado sin añadir complejidad operativa.","steps":["Por qué: la menor complejidad y riesgo; si se queda en un solo nodo, su modelo de transacciones/consistencia no cambia y no tiene que dividir el código de la aplicación.","Cuándo: si aún no ha alcanzado el techo de hardware de un solo nodo, o el cuello de botella proviene en realidad de consultas deficientes/índices ausentes.","Paso: perfile las consultas lentas, añada los índices que faltan, corrija planes/estadísticas; luego aumente CPU/RAM/IOPS.","Precaución: el escalado vertical es finito y su costo crece exponencialmente; un solo nodo sigue siendo un punto único de fallo (configure una réplica por separado para HA).","Precaución: planifique con antelación el siguiente paso (réplicas/sharding) a medida que se acerque el techo; las migraciones precipitadas son arriesgadas."]},"sol_read_replica":{"kind":"solution","title":"Recomendación: réplicas de lectura (escalado de lectura)","detail":"Las escrituras permanecen en un solo nodo (el primario) mientras el tráfico de lectura se distribuye entre una o más réplicas. Es la forma estándar de aumentar la capacidad de lectura bruta y la resiliencia en cargas con lectura intensiva.","steps":["Por qué: las consultas de lectura se reparten entre varias copias; el primario solo gestiona las escrituras y el throughput de lectura crece de forma casi lineal.","Cuándo: cargas con una alta relación lectura/escritura; separar las consultas de informes y analítica del primario; obtener al mismo tiempo un standby para HA.","Ejemplo: réplicas de streaming de PostgreSQL, réplicas de MySQL + enrutamiento por proxy, separación de lectura/escritura en la aplicación (read-write split).","Precaución: con réplicas asíncronas, el retraso de replicación crea un problema de read-your-writes (no puede leer de inmediato lo que acaba de escribir); dirija las lecturas críticas al primario.","Precaución: las réplicas no resuelven un cuello de botella de escritura; el único nodo escritor sigue siendo el límite: si las escrituras también son un cuello de botella, se requiere sharding."],"related":["replication-status"]},"sol_cache":{"kind":"solution","title":"Recomendación: capa de caché","detail":"Mantener los resultados que se releen con frecuencia en una capa rápida en memoria reduce drásticamente la carga de la base de datos. Con el patrón adecuado responde sin ir a la base de datos, lo que reduce tanto la latencia como la carga.","steps":["Por qué: las lecturas repetidas se responden a velocidad de memoria sin llegar nunca a la base de datos; es el método de escalado de lectura con la mejor relación beneficio/costo.","Cuándo: los mismos datos se leen muchas veces y es aceptable un ligero desfase; p. ej. detalles de productos, configuraciones, datos de referencia.","Ejemplo: el patrón cache-aside con Redis/Memcached; la aplicación va primero a la caché y, si hay un fallo (miss), a la base de datos.","Precaución: la invalidación de la caché es difícil; diseñe explícitamente el TTL y la estrategia de actualización/purga al escribir.","Precaución: no dependa de una caché desactualizada para lecturas que requieren consistencia fuerte (dinero/stock); sirva esas lecturas desde el origen."]},"sol_sharding":{"kind":"solution","title":"Recomendación: sharding (escalado de escritura/datos)","detail":"Los datos se dividen por una shard key y se distribuyen entre varios nodos independientes; cada nodo es propietario de una porción de los datos y de sus escrituras. Es la forma de escalar la tasa de escritura y el volumen total de datos, pero añade una complejidad considerable.","steps":["Por qué: las escrituras y el almacenamiento se reparten entre varios nodos; desaparecen los límites de escritor único y de datos de un solo nodo, y se abre el escalado horizontal.","Cuándo: cuando el throughput de escritura o el tamaño de los datos ya no caben en un solo nodo (ni en réplicas de lectura); cuando no queda otra opción.","Ejemplo: enrutamiento de shards en la capa de aplicación, Vitess (MySQL), Citus (PostgreSQL), sharding integrado en las BD documentales.","Precaución: la elección de la shard key es crítica y difícil de revertir; una mala clave crea puntos calientes y una distribución desigual. Elija una clave de alta cardinalidad que se ajuste al patrón de acceso.","Precaución: los joins, las transacciones y la unicidad entre shards se vuelven difíciles; la complejidad de la aplicación, el reequilibrio (resharding) y la carga operativa aumentan considerablemente."],"related":["create-partition"]}}},"solver/decide-backup.json":{"id":"decide-backup","title":"Decisión: ¿qué estrategia de backup?","icon":"🧭","category":"Guía de decisión","intro":"Una estrategia de backup se elige equilibrando tres factores: cuánta pérdida de datos puede tolerar (RPO), el tamaño de sus datos y la rapidez con la que debe recuperarse (RTO). Ningún método se ajusta a todos los escenarios. Partiendo del objetivo de RPO, este árbol le orienta entre dumps lógicos, backups físicos + PITR y snapshots, y le recuerda que la regla 3-2-1 y la verificación no son negociables.","start":"root","nodes":{"root":{"kind":"step","title":"¿Cuál es su objetivo de pérdida de datos (RPO)?","detail":"Empiece por la restricción más decisiva: ante un desastre, ¿cuántos minutos/horas de datos puede permitirse perder? Si se requiere una pérdida de datos cercana a cero, el archivado continuo de logs (PITR) es obligatorio; si es aceptable una tolerancia de backup diaria/semanal, bastan métodos más sencillos. El RPO define el marco de la estrategia.","choices":[{"label":"Cercana a cero: debo poder volver a cualquier punto en el tiempo","next":"sol_physical_pitr"},{"label":"Son aceptables backups diarios/periódicos (unas horas de pérdida)","next":"size"},{"label":"Recuérdeme primero las buenas prácticas generales (3-2-1 / verificación)","next":"sol_321"}]},"size":{"kind":"step","title":"¿Cuáles son los requisitos de tamaño de datos y portabilidad?","detail":"Si bastan los backups periódicos, la siguiente pregunta es la escala y el propósito: ¿la base de datos es pequeña/mediana y quiere que el backup sea portable a otro entorno/versión, o los datos son grandes y su principal preocupación es volver a estar operativo en minutos? Esto decide entre un dump lógico y un snapshot.","choices":[{"label":"Pequeña/mediana, portable y compatible entre versiones","next":"sol_logical"},{"label":"Datos grandes, la recuperación debe ser muy rápida (RTO bajo)","next":"sol_snapshot"}]},"sol_logical":{"kind":"solution","title":"Recomendación: dump lógico","detail":"Exporta los datos a nivel lógico (definiciones de esquema + filas); la salida es SQL/texto (formato plain de pg_dump, mysqldump) o un dump binario propio de la herramienta (pg_dump -Fc, Oracle Data Pump). Un método flexible y portable para bases de datos pequeñas y medianas, migraciones, actualizaciones de versión y recuperación de tablas/objetos seleccionados.","steps":["Por qué: la salida es portable (y legible en formato texto); adecuada para moverse entre versiones/motores y para restaurar una sola tabla/objeto.","Cuándo: datos pequeños/medianos, actualizaciones de versión/migraciones, copias para entornos de desarrollo, necesidad de recuperación lógica (a nivel de fila).","Ejemplo: PostgreSQL pg_dump/pg_dumpall, MySQL mysqldump, Oracle Data Pump (expdp/impdp; genera un .dmp binario, use impdp SQLFILE= para ver el DDL como texto).","Precaución: como la restauración vuelve a procesar los datos, tanto tomar el backup como restaurarlo son muy lentos en bases de datos grandes (RTO alto).","Precaución: por sí solo no proporciona un RPO bajo; la recuperación a un punto en el tiempo requiere un backup físico + archivo de logs. Use las opciones de consistencia adecuadas para obtener una instantánea consistente."],"related":["logical-dump","last-backup-status"]},"sol_physical_pitr":{"kind":"solution","title":"Recomendación: backup físico + WAL/logs archivados (PITR)","detail":"Toma base backups físicos periódicos y archiva continuamente los logs de transacciones (WAL/redo/binlog). Esto le permite realizar una recuperación a un punto en el tiempo (Point-in-Time Recovery) a cualquier momento y reducir la pérdida de datos casi a cero.","steps":["Por qué: gracias al archivado continuo de logs, puede avanzar hasta cualquier transacción posterior al último base backup y volver al momento deseado (p. ej. justo antes de un comando erróneo): el RPO más bajo.","Cuándo: bases de datos de producción grandes y críticas; sistemas que necesitan un RPO bajo y un RTO razonable.","Ejemplo: PostgreSQL base backup + archivo de WAL (pgBackRest/barman), Oracle RMAN + archivelog, backup físico de MySQL + binlog (xtrabackup).","Precaución: la configuración y la operación son más complejas; monitoree continuamente que los logs archivados fluyan sin interrupciones ni huecos: un solo hueco rompe la cadena de PITR.","Precaución: el almacén del archivo de logs crece continuamente; sin una política de retención y un plan de almacenamiento, el disco se llena."],"related":["last-backup-status","logical-dump"]},"sol_snapshot":{"kind":"solution","title":"Recomendación: snapshot","detail":"Toma una instantánea a nivel de bloque de los datos en la capa de almacenamiento o de virtualización. El backup y la restauración son muy rápidos incluso para bases de datos muy grandes; sin embargo, depende de la infraestructura y requiere un uso cuidadoso.","steps":["Por qué: se crea una copia a nivel de bloque casi al instante y las restauraciones son muy rápidas; ofrece el RTO más bajo para datos grandes.","Cuándo: terabytes de datos, la recuperación rápida como prioridad; si la capa de almacenamiento/nube admite snapshots.","Ejemplo: snapshot de SAN/LVM, snapshot de disco en la nube (EBS, etc.), snapshot de ZFS/sistema de archivos; lanzado de forma consistente con el motor de base de datos.","Precaución: un snapshot depende del sistema de almacenamiento y suele residir en la misma infraestructura; por sí solo no cumple la regla 3-2-1: cópielo fuera del sitio.","Precaución: para que sea consistente a nivel de aplicación, la base de datos debe estar en el modo adecuado (fsfreeze/backup mode/hot backup) en el momento del snapshot; de lo contrario obtendrá un backup «crash-consistent»."],"related":["last-backup-status"]},"sol_321":{"kind":"solution","title":"Recomendación: regla 3-2-1 + copia fuera del sitio + verificación obligatoria","detail":"Sea cual sea el método elegido, estos principios deben estar por encima de la estrategia. La mera existencia de un backup no basta; debe estar en otro soporte, en otra ubicación y probarse periódicamente. Es un marco que complementa todas las opciones anteriores.","steps":["3-2-1: mantenga al menos 3 copias, en 2 soportes/tipos distintos, con 1 copia fuera del sitio.","Por qué fuera del sitio: un incendio, una inundación, un ransomware o la pérdida de una región pueden destruir a la vez todas las copias de una misma ubicación; al menos una copia debe estar aislada/remota.","La verificación es obligatoria: un backup no probado no es un backup; realice periódicamente simulacros de restauración reales y mida el tiempo de recuperación (RTO).","Inmutabilidad: frente al ransomware, mantenga una copia que no se pueda borrar ni modificar (inmutable/WORM) o aislada (air-gapped).","Precaución: monitoree automáticamente la integridad (checksum) y la recuperabilidad de los backups; configure alertas para backups fallidos/desactualizados. Cifre también los propios backups."],"related":["last-backup-status"]}}},"solver/decide-index.json":{"id":"decide-index","title":"Decisión: ¿qué tipo de índice?","icon":"🧭","category":"Guía de decisión","intro":"El tipo de índice adecuado depende del patrón de acceso de la consulta (igualdad, rango o búsqueda de texto/JSON), de la cardinalidad de la columna y del equilibrio entre lecturas y escrituras. Un índice equivocado desperdicia espacio y además ralentiza las escrituras. Este árbol parte de la forma de la consulta y le orienta entre las opciones B-tree, Hash, Bitmap, GIN/GiST, parcial y covering, y le recuerda que el orden de las columnas es crítico en un índice compuesto.","start":"root","nodes":{"root":{"kind":"step","title":"¿Cómo usa su consulta esta columna?","detail":"El patrón de acceso es el factor más decisivo al elegir un índice. ¿Solo compara la columna con '=', hace rangos/ordenaciones (>, <, BETWEEN, ORDER BY), o busca en texto completo, en un campo JSON o en datos geométricos/arrays? Empiece aquí; no elija un tipo de índice hasta que la necesidad esté clara.","choices":[{"label":"Igualdad y rango/ordenación combinados (=, >, <, BETWEEN, ORDER BY)","next":"cardinality"},{"label":"Solo igualdad (=), sin rango ni ordenación","next":"sol_hash"},{"label":"Búsqueda de texto completo, campo JSON/JSONB, array o geométrica","next":"sol_gin_gist"},{"label":"La consulta siempre apunta al mismo subconjunto pequeño de filas (p. ej. WHERE status='active')","next":"sol_partial"}]},"cardinality":{"kind":"step","title":"¿Cuál es la cardinalidad de la columna (número de valores distintos)?","detail":"Si se necesita rango/ordenación, el valor por defecto es B-tree; sin embargo, para columnas con muy pocos valores distintos (cardinalidad baja: sexo, estado, sí/no) en un data warehouse con lectura intensiva, un índice Bitmap puede ser mejor. ¿La columna tiene muchos valores únicos o unos pocos valores repetidos?","choices":[{"label":"Cardinalidad alta (muchos valores únicos) o carga OLTP","next":"coverage"},{"label":"Cardinalidad baja y lectura intensiva / data warehouse (DWH)","next":"sol_bitmap"}]},"coverage":{"kind":"step","title":"¿La consulta lee solo las columnas del índice?","detail":"El patrón de acceso se ajusta a un B-tree. Una última optimización: si todas las columnas que la consulta devuelve y por las que filtra están en el índice, el motor puede responder solo desde el índice sin visitar la tabla (index-only / covering scan). ¿Su consulta lee unas pocas columnas o necesita la mayor parte de la fila?","choices":[{"label":"Sí, unas pocas columnas; se puede responder sin visitar la tabla","next":"sol_covering"},{"label":"No, basta con el acceso estándar","next":"sol_btree"}]},"sol_btree":{"kind":"solution","title":"Recomendación: B-tree (por defecto)","detail":"Un B-tree es una estructura de árbol equilibrado que admite una amplia gama de patrones de acceso: igualdad, rango, ordenación (ORDER BY), coincidencia de prefijo con LIKE 'abc%' y MIN/MAX. Es el tipo de índice por defecto en la mayoría de los motores relacionales y el punto de partida correcto en caso de duda.","steps":["Por qué: una sola estructura sirve eficientemente tanto a la igualdad como al rango/ordenación; es el índice de propósito más general.","Cuándo: consultas OLTP, WHERE + rango + ORDER BY juntos, claves primarias/únicas.","El ORDEN de las columnas es crítico en un índice compuesto: ponga primero la columna más selectiva usada con igualdad y deje la columna de rango al final. Un índice (a,b) ayuda a un filtro sobre 'a' o 'a,b'; por lo general no ayuda a un filtro solo sobre 'b' (regla del prefijo más a la izquierda).","Ejemplo: el índice B-tree/B+tree por defecto en PostgreSQL/Oracle/MySQL(InnoDB)/MSSQL por igual.","Precaución: cada índice aumenta el costo de escritura (INSERT/UPDATE/DELETE) y el espacio en disco; no mantenga índices innecesarios o duplicados."],"related":["slow-query"]},"sol_hash":{"kind":"solution","title":"Recomendación: índice Hash (solo igualdad)","detail":"Un índice hash almacena el hash del valor de la columna y es muy rápido solo para búsquedas de igualdad exacta ('='). No admite búsquedas por rango, ordenación ni prefijo, por lo que su caso de uso es limitado.","steps":["Por qué: proporciona un acceso en tiempo constante medio (O(1)) para búsquedas de igualdad; puede ser ventajoso para cargas de igualdad muy grandes que caben en memoria.","Cuándo: la columna solo se usa con '=' y nunca se necesita rango/ordenación.","Diferencias entre motores: PostgreSQL tiene índices hash, pero en la práctica B-tree es suficiente y más flexible en la mayoría de los casos; MySQL(InnoDB) gestiona el indexado hash solo internamente como adaptive hash index, mientras que el motor MEMORY usa hash de forma explícita; en Oracle aparece en el contexto de clusters/hash clusters.","Precaución: no se admiten rango, BETWEEN, ORDER BY ni LIKE 'abc%'; elija un B-tree en cuanto surjan esas necesidades.","Precaución: como el B-tree por defecto suele ser suficiente, no cambie a hash sin medir."],"related":["slow-query"]},"sol_bitmap":{"kind":"solution","title":"Recomendación: índice Bitmap (cardinalidad baja / DWH)","detail":"Un índice bitmap mantiene un array de bits para cada valor distinto y combina muy rápidamente varias condiciones de cardinalidad baja con AND/OR a nivel de bits. Es potente en cargas con lectura intensiva como data warehousing e informes.","steps":["Por qué: ocupa muy poco espacio en columnas con pocos valores distintos y combina eficientemente varias condiciones (WHERE gender=.. AND region=..) con operaciones bitmap.","Cuándo: columnas de cardinalidad baja, consultas de esquema en estrella, data warehouse/informes, cargas con lectura intensiva.","Diferencias entre motores: un punto fuerte clásico de Oracle; PostgreSQL no tiene índices bitmap persistentes, pero el planificador puede generar un 'bitmap heap scan' en tiempo de ejecución. MySQL/MSSQL no tienen índices bitmap persistentes como tales.","Precaución: no lo use en tablas actualizadas con frecuencia (OLTP); los bloqueos bitmap reducen gravemente la concurrencia a nivel de fila.","Precaución: la ventaja desaparece en columnas de cardinalidad alta; allí es preferible un B-tree."],"related":["slow-query"]},"sol_gin_gist":{"kind":"solution","title":"Recomendación: GIN / GiST (texto, JSON, array, geometría)","detail":"PostgreSQL tiene familias de índices especializadas para tipos de acceso que un B-tree estándar no puede gestionar. GIN apunta a muchos componentes dentro de un valor (palabras, claves JSONB, elementos de array); GiST se adapta a consultas geométricas/de rango/de vecino más cercano.","steps":["Por qué: para estos tipos de datos, consultas como «contiene/se solapa/cerca de» no pueden servirse eficientemente con un B-tree; GIN/GiST las aceleran.","Cuándo GIN: búsqueda de texto completo (tsvector), búsquedas de campos/claves JSONB (@>, ?), consultas de contención en arrays; lectura intensiva, pocas escrituras.","Cuándo GiST: geometría/datos geoespaciales (PostGIS), tipos de rango, consultas de vecino más cercano (KNN).","Diferencias entre motores: GIN/GiST son específicos de PostgreSQL. Los índices FULLTEXT y SPATIAL (R-tree) de MySQL, Oracle Text y Spatial en Oracle, y Full-Text Index en MSSQL cumplen un propósito similar.","Precaución: GIN es costoso en escritura y lento de construir (se puede mitigar con fastupdate); mida el costo en tablas con escritura intensiva."],"related":["slow-query"]},"sol_partial":{"kind":"solution","title":"Recomendación: índice parcial / filtrado","detail":"Un índice parcial indexa solo las filas que cumplen una condición (WHERE ...) en lugar de toda la tabla. Si las consultas siempre apuntan al mismo subconjunto pequeño, el índice se vuelve mucho más pequeño y rápido, y el costo de escritura baja.","steps":["Por qué: como indexa solo las filas relevantes, bajan tanto el espacio como el costo de mantenimiento; el subconjunto «caliente» consultado con frecuencia se vuelve mucho más rápido.","Cuándo: condiciones estables y acotadas como WHERE status='active', deleted_at IS NULL, priority='high'.","Diferencias entre motores: PostgreSQL lo admite directamente con un 'partial index' (CREATE INDEX ... WHERE ...), MSSQL con un 'filtered index'. En Oracle se usan patrones de índice basado en funciones/condicional como equivalente; MySQL(InnoDB) no lo admite directamente.","Precaución: la condición WHERE del índice debe coincidir con la condición de la consulta; de lo contrario, el planificador no puede usar el índice.","Precaución: si el subconjunto crece, la ventaja disminuye; asegúrese de que la condición sea estable."],"related":["slow-query"]},"sol_covering":{"kind":"solution","title":"Recomendación: índice covering (INCLUDE)","detail":"Un índice covering contiene todas las columnas que necesita la consulta, lo que permite al motor responder solo desde el índice sin visitar la tabla (heap) en absoluto (index-only scan). Ofrece una aceleración considerable en consultas que se ejecutan con frecuencia y devuelven pocas columnas.","steps":["Por qué: se elimina el acceso a la tabla (heap fetch); la E/S de disco y la latencia bajan notablemente.","Cuándo: consultas que se ejecutan con frecuencia, con columnas WHERE + SELECT bien definidas y un conjunto de resultados acotado.","Diferencias entre motores: MSSQL y PostgreSQL añaden columnas no clave a la hoja del índice con 'INCLUDE'; en MySQL(InnoDB), los índices secundarios ya contienen la clave primaria, por lo que el covering se logra con un índice compuesto; en Oracle, el acceso solo por índice se produce cuando todas las columnas necesarias están en el índice.","Precaución: las columnas INCLUDE adicionales/anchas inflan el índice y ralentizan las escrituras; añada solo las columnas realmente necesarias.","Precaución: en PostgreSQL, un index-only scan requiere un visibility map actualizado; el VACUUM periódico es imprescindible."],"related":["slow-query"]}}},"solver/decide-partition.json":{"id":"decide-partition","title":"Decisión: ¿qué estrategia de particionamiento?","icon":"🧭","category":"Guía de decisión","intro":"El particionamiento divide una tabla grande en varias piezas físicas bajo un único nombre lógico, lo que facilita eliminar datos antiguos y permite a las consultas mirar solo las piezas relevantes (partition pruning). Sin embargo, en tablas pequeñas solo añade complejidad. Este árbol cuestiona primero si el particionamiento es realmente necesario y luego le orienta entre Range, List, Hash y Composite según los patrones de acceso y distribución.","start":"root","nodes":{"root":{"kind":"step","title":"¿La tabla es realmente lo bastante grande como para necesitar particionamiento?","detail":"El particionamiento no es magia de rendimiento; es una herramienta de escala y mantenimiento (ciclo de vida). Si la tabla no es lo bastante grande o las consultas ya rinden bien con índices, el particionamiento solo aporta complejidad en los planes y sobrecarga de gestión. ¿Su tabla alcanza decenas de millones o más de filas/cientos de GB, o sigue siendo pequeña/mediana?","choices":[{"label":"Sí, es muy grande y/o hay que borrar/archivar datos antiguos de forma masiva","next":"access"},{"label":"No, tabla pequeña/mediana, o no estoy seguro","next":"sol_no_partition"}]},"access":{"kind":"step","title":"¿Cómo accede a los datos y cómo deben dividirse?","detail":"La clave de partición debe ser la columna por la que filtran la mayoría de las consultas; solo así se activa el pruning. ¿Cuál es el patrón de acceso predominante? ¿Por fechas/rangos secuenciales (últimos 30 días), por valores concretos de categoría/región, o no hay un filtro claro y quiere repartir la carga de forma uniforme?","choices":[{"label":"Rangos secuenciales como fecha/número de secuencia (últimos X días, por mes)","next":"sol_range"},{"label":"Conjuntos de valores discretos como categoría/región/tenant","next":"sol_list"},{"label":"Sin rango/categoría claros; el objetivo es repartir la carga de forma uniforme","next":"sol_hash"},{"label":"Fecha y gran volumen a la vez; hay que dividir en dos dimensiones","next":"sol_composite"}]},"sol_no_partition":{"kind":"solution","title":"Recomendación: no particionar (todavía)","detail":"El particionamiento no aporta ningún beneficio en tablas pequeñas/medianas; bastan índices bien elegidos y el mantenimiento normal. El particionamiento prematuro complica los planes de consulta, pierde el pruning con una clave equivocada y añade sobrecarga operativa.","steps":["Por qué: en una tabla pequeña, un full scan o el acceso por índice ya son rápidos; el particionamiento solo añade gestión adicional y complejidad en los planes.","Cuándo mantenerlo así: por debajo de unos pocos millones de filas, sin necesidad de eliminar datos antiguos de forma masiva, con consultas que rinden bien con índices.","En su lugar: los índices adecuados, una tabla de archivo si es necesario, estadísticas/VACUUM/ANALYZE periódicos.","Precaución: no configure el particionamiento de forma temprana suponiendo que «crecerá»; cambie cuando el crecimiento esté demostrado y la clave adecuada esté clara.","Diferencias entre motores: un principio general válido para todos los motores; el particionamiento declarativo de PostgreSQL, PARTITION BY de MySQL y el particionamiento de Oracle y MSSQL pueden añadirse fácilmente más adelante."],"related":[]},"sol_range":{"kind":"solution","title":"Recomendación: particionamiento por rango (Range)","detail":"El particionamiento por rango divide las filas por rangos secuenciales de una columna (p. ej. rangos de fechas: una partición por mes/trimestre). Es la estrategia más común y potente para series temporales y tablas en crecimiento continuo.","steps":["Por qué: cuando las consultas filtran por fecha/rango, el motor lee solo las particiones relevantes (partition pruning); los datos antiguos se eliminan al instante con un solo comando (DROP de partición).","Cuándo: series temporales, logs, historial de pedidos/transacciones; consultas de «últimos N días» y archivado basado en el tiempo.","Clave de partición: elija la columna de fecha/secuencial presente en la cláusula WHERE de la inmensa mayoría de las consultas; de lo contrario, el pruning no funciona.","Ejemplo: particionamiento RANGE de PostgreSQL, particionamiento range/interval de Oracle (nuevas particiones automáticas), MySQL PARTITION BY RANGE (normalmente RANGE COLUMNS para fechas), partition function/scheme de MSSQL.","Precaución: automatice la creación de nuevas particiones para rangos futuros (interval de Oracle o un job programado); una partición 'default/maxvalue' llena se convierte en un punto caliente."],"related":[]},"sol_list":{"kind":"solution","title":"Recomendación: particionamiento por lista (List)","detail":"El particionamiento por lista agrupa las filas por valores discretos y conocidos de una columna (p. ej. region = 'TR','EU','US' o el ID de tenant). Cuando existen categorías naturales y finitas, hace que el acceso y la gestión sean claros.","steps":["Por qué: cuando las consultas filtran por valores concretos de categoría, solo se lee esa partición; el mantenimiento/borrado por categoría se vuelve más sencillo.","Cuándo: conjuntos de valores discretos y relativamente estables como región/país, tipo de producto, estado, separación multi-tenant.","Clave de partición: el conjunto de valores debe ser finito y predecible; si se añaden valores nuevos con frecuencia, considere Hash en lugar de List.","Ejemplo: particionamiento LIST de PostgreSQL, particionamiento list de Oracle, MySQL PARTITION BY LIST, partition scheme de MSSQL con los límites adecuados.","Precaución: mantenga una partición 'default' para valores no definidos/nuevos; de lo contrario, la fila no se puede insertar y obtendrá un error."],"related":[]},"sol_hash":{"kind":"solution","title":"Recomendación: particionamiento Hash (distribución uniforme)","detail":"El particionamiento hash distribuye las filas de forma relativamente uniforme entre un número predefinido de particiones según el hash de la columna clave. Se usa para evitar puntos calientes y equilibrar la carga cuando no hay un rango/categoría natural.","steps":["Por qué: las filas se distribuyen de forma uniforme; se reduce el punto caliente de escritura que se acumula en una sola partición o al final de la clave primaria.","Cuándo: alto volumen de escritura, sin rango/categoría natural, el objetivo es igualar la E/S y el tamaño (p. ej. un id de cardinalidad alta).","Clave de partición: elija una columna de cardinalidad alta y distribución uniforme (normalmente la clave primaria/única).","Ejemplo: particionamiento HASH de PostgreSQL, particionamiento hash de Oracle, MySQL PARTITION BY HASH/KEY.","Precaución: no proporciona pruning para consultas por rango (el hash destruye el orden); las consultas como «últimos N días» recorren todas las particiones. Cambiar después el número de particiones requiere redistribuir."],"related":[]},"sol_composite":{"kind":"solution","title":"Recomendación: particionamiento compuesto (Composite, multinivel)","detail":"El particionamiento compuesto combina dos estrategias: divide primero en el nivel superior y luego divide cada partición en subparticiones (p. ej. Range-by-Hash o Range-by-List). Se usa cuando se necesitan al mismo tiempo un ciclo de vida basado en el tiempo y una distribución uniforme/separación por categorías.","steps":["Por qué: cuando una sola estrategia no basta, cubre ambas necesidades a la vez; por ejemplo, dividir por mes (archivado) y distribuir por hash dentro del mes (prevención de puntos calientes).","Cuándo: series temporales muy grandes + muchas escrituras concurrentes, o un doble patrón de acceso de fecha + región/tenant.","Ejemplo: Oracle composite (range-hash, range-list, list-hash, etc.); en PostgreSQL se construye manualmente particionando de nuevo las particiones (partition of partition); subparticiones de MySQL (RANGE/LIST + HASH/KEY).","Precaución: es la opción con mayor complejidad de gestión y de esquema; elíjala solo cuando ambas dimensiones sean una necesidad demostrada.","Precaución: una elección equivocada de la clave superior/inferior pierde el pruning; verifique que ambos niveles coinciden con el patrón de consulta."],"related":[]}}},"solver/decide-pooling.json":{"id":"decide-pooling","title":"Decisión: ¿cómo configurar el connection pooling?","icon":"🧭","category":"Guía de decisión","intro":"Cada conexión a la base de datos conlleva un costo de memoria y de planificación; un crecimiento descontrolado del número de conexiones ralentiza el servidor o incluso lo hace caer. Un pool de conexiones lo resuelve reutilizando un pequeño número de conexiones persistentes. Este árbol orienta las decisiones sobre dónde se ubica el pool, qué modo usar (session/transaction/statement) y el tamaño adecuado, e insiste en por qué un pooler externo es obligatorio en entornos serverless.","start":"root","nodes":{"root":{"kind":"step","title":"¿Cómo se conecta la arquitectura de su aplicación?","detail":"La decisión de pooling empieza por la arquitectura. ¿Es un único proceso de aplicación de larga duración (servidor clásico), muchos procesos/instancias independientes (microservicios, muchos pods) o funciones serverless/Lambda que nacen y mueren con cada solicitud? Cada caso requiere una ubicación distinta del pool.","choices":[{"label":"Uno/pocos procesos de aplicación de larga duración","next":"sol_app_pool"},{"label":"Muchos procesos/instancias/pods se conectan a la misma BD","next":"middleware"},{"label":"Serverless / Lambda / funciones de corta duración","next":"sol_serverless"},{"label":"Muéstreme primero cómo elegir el tamaño de pool adecuado","next":"sol_size"}]},"middleware":{"kind":"step","title":"¿Cómo debe elegirse el modo del pooler intermedio?","detail":"Con muchos clientes, un pooler intermedio (PgBouncer, ProxySQL) mantiene bajo el número de conexiones en la BD. Pero el modo del pool depende de las funciones que usa la aplicación: ¿depende de un estado ligado a la sesión (prepared statements, tablas temporales, SET, advisory locks, funciones a nivel de sesión), o todo se completa dentro de una sola transacción?","choices":[{"label":"Dependo del estado de sesión (SET, tablas temporales, prepared statements de sesión)","next":"sol_session_mode"},{"label":"Las transacciones son cortas e independientes; no mantengo estado de sesión","next":"sol_txn_mode"}]},"sol_app_pool":{"kind":"solution","title":"Recomendación: pool de conexiones dentro de la aplicación","detail":"Para uno/pocos procesos de larga duración, una biblioteca de pool dentro de la aplicación (HikariCP, el pool integrado de database/sql de Go, el connection pooling de .NET) cubre la mayoría de las necesidades. Las conexiones se abren una vez y se reutilizan una y otra vez.","steps":["Por qué: desaparece el costo de abrir/cerrar conexiones; un número de conexiones limitado y predecible protege la BD.","Cuándo: un monolito clásico o unas pocas instancias de aplicación; la demanda total de conexiones está por debajo de la capacidad de la BD.","Ejemplo: Java HikariCP, pooling de .NET SqlConnection/Npgsql, Node pg-pool, pool de Python SQLAlchemy.","Para el dimensionamiento: determine el tamaño del pool según los núcleos/discos (siguiente decisión); «cuanto más, mejor» no se aplica.","Precaución: cada instancia de aplicación abre su propio pool; número de instancias × tamaño del pool no debe superar max_connections de la BD. Si lo supera, pase a un pooler intermedio."],"related":["connection-storm"]},"sol_size":{"kind":"solution","title":"Recomendación: dimensione correctamente el pool","detail":"El pool no debe ser ni demasiado pequeño (las solicitudes esperan) ni demasiado grande. Demasiadas conexiones generan cambios de contexto de CPU y contención de bloqueos y reducen el throughput. El tamaño no es un número que haya que aumentar indefinidamente, sino un número pequeño basado en los núcleos y la E/S.","steps":["Fórmula inicial: un punto de partida habitual es tamaño del pool ≈ (número de núcleos × 2) + número efectivo de discos (spindles); con SSD/muchos núcleos, en torno a «núcleos × 2-4» es un buen comienzo.","Por qué mantenerlo pequeño: el paralelismo real de la BD está limitado por el número de núcleos; las conexiones adicionales solo se encolan y lo ralentizan todo por los cambios de contexto/la contención de bloqueos.","Regla: con cientos de clientes concurrentes, la solución no es ampliar el pool sino rotar rápidamente un pool pequeño (transacciones cortas + encolamiento).","Límite total: (número de instancias de aplicación × tamaño del pool) < max_connections de la BD; deje margen para las conexiones de superusuario y de mantenimiento.","Precaución: empiece con la fórmula, pero mida siempre; ajuste con las métricas de tiempo de espera, conexiones activas y profundidad de la cola."],"related":["connection-storm"]},"sol_session_mode":{"kind":"solution","title":"Recomendación: modo de pool session","detail":"En el modo session, una conexión de cliente queda fijada a una conexión de servidor del backend hasta que termina su sesión. El estado a nivel de sesión (SET, tablas temporales, advisory locks, prepared statements de sesión) se conserva, pero la compartición de conexiones es mínima.","steps":["Por qué: es el único modo seguro para aplicaciones que necesitan conservar el estado durante toda la sesión; el comportamiento es el mismo que conectarse directamente a la BD.","Cuándo: aplicaciones que usan variables de sesión con SET, tablas temporales, LISTEN/NOTIFY, prepared statements a nivel de sesión o advisory locks.","Ejemplo: PgBouncer pool_mode=session; en ProxySQL, enrutar por separado el tráfico sensible a la sesión.","Precaución: la compartición de conexiones es baja, por lo que el beneficio se reduce con muchos clientes; cuando sea posible, procure que la aplicación no dependa del estado de sesión y pase al modo transaction.","Diferencias entre motores: PgBouncer para PostgreSQL; en el lado de MySQL, ProxySQL gestiona un comportamiento similar de otra forma, y las variables de sesión requieren cuidado."],"related":["connection-storm"]},"sol_txn_mode":{"kind":"solution","title":"Recomendación: modo de pool transaction","detail":"En el modo transaction, una conexión de servidor se presta al cliente solo durante una transacción y vuelve al pool cuando la transacción termina. Así, miles de clientes pueden trabajar con muy pocas conexiones reales en la BD; es el modo más común a gran escala.","steps":["Por qué: como las conexiones se entregan por transacción, la compartición es máxima; atiende a muchos clientes con pocas conexiones a la BD.","Cuándo: transacciones cortas e independientes; arquitecturas de microservicios/múltiples pods que no mantienen estado de sesión (o se adaptan para no mantenerlo).","Ejemplo: PgBouncer pool_mode=transaction (la configuración más común), el comportamiento de multiplexación por defecto de ProxySQL.","Precaución: las funciones que abarcan la sesión dejan de funcionar: los prepared statements a nivel de sesión, las tablas temporales, SET y los advisory locks no son seguros. Para los prepared statements con nombre a nivel de protocolo, verifique la compatibilidad entre la aplicación y el pooler.","Modo statement: el modo aún más agresivo (statement) entrega la conexión en cada sentencia; solo se considera para escenarios muy concretos de solo lectura sin transacciones y es muy restrictivo."],"related":["connection-storm"]},"sol_serverless":{"kind":"solution","title":"Recomendación: un pooler externo es obligatorio en serverless","detail":"Las funciones serverless/Lambda escalan rápidamente y cada instancia abre su propia conexión; un pool dentro de la aplicación no ayuda aquí porque las instancias son de corta duración y no comparten nada. Cuando surgen de golpe cientos/miles de instancias, se supera el límite de conexiones de la BD. La solución es un pool de conexiones externo delante de la BD.","steps":["Por qué: en instancias de función de corta duración y no compartidas, un pool dentro de la aplicación no puede persistir; se produce una tormenta de conexiones al escalar horizontalmente.","Cuándo: entornos que crean una instancia por solicitud, como AWS Lambda, Cloud Functions, Azure Functions.","Ejemplo: PgBouncer/ProxySQL como capa separada, o un pooler gestionado (p. ej. AWS RDS Proxy, Data API/servicios de pooler externo para PostgreSQL).","Modo: normalmente se prefiere el modo de pool transaction; haga que el código de la función no dependa del estado de sesión.","Precaución: configure también un límite de concurrencia por función (reserved concurrency); dimensione conjuntamente el pooler y max_connections de la BD; de lo contrario, el propio pooler se convierte en el cuello de botella."],"related":["connection-storm"]}}},"solver/decide-cache.json":{"id":"decide-cache","title":"Decisión: ¿qué estrategia de caché?","icon":"🧭","category":"Guía de decisión","intro":"Una caché mantiene los datos leídos con frecuencia en una capa rápida delante de la base de datos (p. ej. Redis), lo que reduce la latencia y la carga de la BD. Pero no todos los datos son aptos para caché, y una invalidación incorrecta crea riesgo de datos desactualizados/incorrectos. Este árbol comprueba primero si realmente necesita una caché, luego le ayuda a elegir entre cache-aside, read-through, write-through y write-behind según el patrón de lectura/escritura, y trata la prevención de stampedes.","start":"root","nodes":{"root":{"kind":"step","title":"¿Cómo es su carga de trabajo: con lectura intensiva o con escritura intensiva?","detail":"Una caché rinde más cuando los mismos datos se leen muchas veces (lectura intensiva) y no cambian a menudo. Establezca primero el patrón general: ¿los datos se leen mucho y cambian poco, se actualizan constantemente, o no está seguro de que merezca la pena añadir una caché?","choices":[{"label":"Lectura muy intensiva, los datos cambian relativamente poco","next":"read_pattern"},{"label":"Las escrituras también son intensas; los datos escritos se leen de inmediato/a menudo","next":"write_pattern"},{"label":"No estoy seguro; ¿realmente necesito una caché?","next":"sol_no_cache"}]},"read_pattern":{"kind":"step","title":"¿Quién debe encargarse de poblar la caché?","detail":"En un escenario de lectura intensiva, la elección clave es quién puebla la caché. ¿Debe su aplicación gestionar la caché de forma explícita y recurrir ella misma a la BD (gran flexibilidad), o debe la capa de caché obtener los datos de la BD subyacente ante un fallo (miss) y devolverlos de forma transparente?","choices":[{"label":"Que la aplicación la gestione explícitamente; quiero flexibilidad y control","next":"sol_cache_aside"},{"label":"Que la capa de caché obtenga los datos de la BD ante un fallo (transparente)","next":"sol_read_through"}]},"write_pattern":{"kind":"step","title":"En las escrituras, ¿la prioridad es la consistencia o la velocidad de escritura?","detail":"Si las escrituras son intensas, debe decidir qué grado de consistencia tendrán los datos escritos entre la caché y la BD. ¿Deben actualizarse juntas la caché y la BD en cada escritura para garantizar la consistencia (escrituras algo más lentas), o deben ir las escrituras primero a la caché y aplicarse a la BD de forma asíncrona en segundo plano (escrituras muy rápidas pero con riesgo de pérdida)?","choices":[{"label":"La consistencia importa; las lecturas deben estar siempre actualizadas","next":"sol_write_through"},{"label":"La velocidad de escritura es crítica; son aceptables una consistencia ligeramente diferida y un riesgo de pérdida","next":"sol_write_behind"}]},"sol_no_cache":{"kind":"solution","title":"Recomendación: no añada una caché por ahora","detail":"Una caché no es gratuita: introduce un problema de consistencia (datos desactualizados), infraestructura adicional y complejidad de invalidación. Si la consulta ya es rápida o los datos cambian en cada solicitud, una caché no aporta ningún beneficio, solo superficie de fallo.","steps":["Por qué: una caché añade sobrecarga de consistencia, invalidación y operación. Sin un beneficio, es una pérdida neta.","Cuándo no es necesaria: la consulta ya es rápida gracias a un índice, los datos difieren en cada lectura/son específicos del usuario y no se reutilizan, o las escrituras superan con mucho a las lecturas.","En su lugar: primero optimización de índices/consultas, una vista materializada o la propia caché de buffers/planes de la BD.","Precaución: no añada una caché «por si acaso»; no la añada sin un cuello de botella de lectura medido y datos reutilizados.","Siguiente: una vez demostrado un punto caliente de lectura real, empiece con cache-aside."],"related":["slow-query","high-cpu"]},"sol_cache_aside":{"kind":"solution","title":"Recomendación: cache-aside (carga diferida)","detail":"Con cache-aside, la aplicación consulta primero la caché; si el dato no está (miss), lo lee de la BD, lo escribe en la caché y lo devuelve. Es el patrón más común y flexible; la caché solo se llena con los datos que realmente se solicitan.","steps":["Por qué: sencillo, flexible y resiliente; aunque la caché falle, la aplicación sigue funcionando recurriendo a la BD. Solo se cachean los datos solicitados.","Cuándo: aceleración de lectura de propósito general; muchas lecturas, escrituras moderadas; el uso clásico con Redis/Memcached.","Invalidación: al escribir, actualice o elimine la clave relacionada (write-invalidate); limite el desfase asignando a cada clave un TTL razonable.","Ejemplo: Redis GET → si no existe, consulta a la BD → SET key value EX <ttl>.","Precaución: la primera solicitud siempre es un fallo (arranque en frío). Si muchos clientes solicitan la misma clave a la vez, puede producirse un cache stampede; consulte la prevención de stampedes (punto siguiente).","Stampede: cuando expira una clave popular, miles de solicitudes llegan a la BD a la vez; evítelo con un lock/single-flight, una actualización anticipada o TTL con jitter."],"related":["high-cpu","slow-query"]},"sol_read_through":{"kind":"solution","title":"Recomendación: caché read-through","detail":"Con read-through, la aplicación solo se comunica con la caché; ante un fallo, la propia capa de caché obtiene los datos de la BD y la puebla (mediante un provider/loader). El código de la aplicación se simplifica y la lógica de poblado reside en un solo lugar.","steps":["Por qué: la lógica de poblado se centraliza en la capa de caché; el código de la aplicación se libera de repetir «gestionar el fallo en todas partes».","Cuándo: cargas con lectura intensiva en las que puede usar una capa de acceso a la caché coherente (biblioteca/servicio).","Ejemplo: una biblioteca/cliente de caché (p. ej. una caché local con un loader definido o una capa de acceso delante de Redis) carga desde la BD ante un fallo.","Invalidación: siguen siendo necesarios los TTL y la invalidación al escribir; read-through por sí solo no garantiza la frescura de los datos.","Precaución: muy similar a cache-aside; la diferencia es «quién la puebla». El riesgo de stampede también existe aquí; aplique las mismas contramedidas.","Nota sobre el motor: Redis no hace read-through por sí mismo; la capa de aplicación/biblioteca proporciona este patrón."],"related":["slow-query"]},"sol_write_through":{"kind":"solution","title":"Recomendación: caché write-through","detail":"Con write-through, cada escritura va tanto a la caché como a la BD (de forma síncrona); ambas se actualizan juntas. Así, la caché nunca se queda por detrás de la BD y las lecturas siempre ven datos actuales.","steps":["Por qué: la caché y la BD se mantienen consistentes; si los datos escritos se leen de inmediato, no hay riesgo de lecturas desactualizadas.","Cuándo: escenarios en los que los datos escritos se leen de inmediato y a menudo, y se requiere una consistencia de lectura fuerte.","Ejemplo: al escribir, la aplicación actualiza primero la BD y luego establece de inmediato la clave de caché con el nuevo valor (o la elimina).","Precaución: cada escritura va a dos destinos, por lo que aumenta la latencia de escritura; también pueden cachearse innecesariamente datos que nunca se leerán. Las dos escrituras no son atómicas (una tiene éxito y la otra falla), lo que puede causar inconsistencias: en caso de error, elimine la entrada de la caché.","Trampa de la doble escritura: en lugar de escribir por separado en la BD y en la caché, el enfoque «escribir en la BD, eliminar (invalidar) la caché» suele ser más seguro."],"related":[]},"sol_write_behind":{"kind":"solution","title":"Recomendación: caché write-behind (write-back)","detail":"Con write-behind, la escritura va primero a la caché y se devuelve al usuario de inmediato; la escritura en la BD se produce en segundo plano, de forma asíncrona (por lotes/diferida). La velocidad de escritura es la máxima, pero conlleva riesgos de durabilidad y consistencia.","steps":["Por qué: la latencia de escritura cae drásticamente y las escrituras pueden agruparse en lotes y transferirse a la BD de forma eficiente; la BD se alivia con cargas de escritura intensiva.","Cuándo: volúmenes de escritura muy altos, como contadores, métricas o feeds de actividad, en los que es aceptable una consistencia ligeramente diferida.","Ejemplo: escritura en Redis → una cola/worker en segundo plano vuelca a la BD por lotes a intervalos.","Precaución: si el nodo de caché cae antes de escribir en la BD, los datos se PIERDEN; no lo use para datos críticos que requieren durabilidad, o protéjalo con una cola duradera/AOF.","Precaución: la BD permanece desactualizada durante un tiempo; otros sistemas que leen directamente de la BD ven datos obsoletos. Acepte de forma consciente el retraso de consistencia y la ventana de pérdida."],"related":[]}}},"solver/decide-datatype.json":{"id":"decide-datatype","title":"Decisión: elegir el tipo de dato adecuado","icon":"🧭","category":"Guía de decisión","intro":"Elegir un tipo de dato es una de las decisiones más costosas de corregir después: un tipo equivocado produce resultados erróneos (p. ej. float para dinero), espacio desperdiciado y consultas lentas. Este árbol le orienta hacia el tipo adecuado para campos numéricos, de texto, de fecha/hora, de clave (PK), semiestructurados (JSON) y de categoría (ENUM), y destaca trampas clásicas como el dinero y los juegos de caracteres.","start":"root","nodes":{"root":{"kind":"step","title":"¿Qué tipo de datos está almacenando?","detail":"El tipo adecuado lo determina la naturaleza de los datos. ¿En qué categoría entra el campo: un número, texto, una fecha/hora, la clave primaria de la tabla, datos flexibles/semiestructurados (JSON) o un conjunto limitado de categorías?","choices":[{"label":"Valor numérico (cantidad, dinero, medida, contador)","next":"numeric"},{"label":"Texto (nombre, descripción, código)","next":"sol_text"},{"label":"Fecha y/u hora","next":"sol_datetime"},{"label":"Estoy diseñando una clave primaria (PK)","next":"sol_pk"},{"label":"Campos flexibles/anidados/variables (datos semiestructurados)","next":"sol_json"},{"label":"Un conjunto limitado y conocido de categorías (estado, tipo, prioridad)","next":"sol_enum"}]},"numeric":{"kind":"step","title":"¿El número es entero o decimal? ¿Y es dinero?","detail":"En los numéricos, la distinción más crítica es la exactitud. ¿El valor es un entero (cantidad, ID, contador) o un decimal? Si es decimal, ¿es DINERO/un importe financiero? La coma flotante binaria (FLOAT/DOUBLE) nunca se usa para dinero ni donde se requiera exactitud, porque produce errores de redondeo.","choices":[{"label":"Entero (cantidad, contador, id)","next":"sol_integer"},{"label":"Dinero o decimal exacto (precio, saldo, tasa)","next":"sol_decimal"}]},"sol_integer":{"kind":"solution","title":"Recomendación: entero (SMALLINT / INT / BIGINT)","detail":"Para los enteros, elija el tipo más pequeño que cubra el rango necesario. Un tipo innecesariamente grande desperdicia espacio y tamaño de índice; pero para los campos que pueden crecer, como contadores/PK, una anchura suficiente es esencial para evitar el desbordamiento.","steps":["Por qué: la anchura adecuada optimiza tanto el almacenamiento como la huella de índice/memoria; la aritmética es rápida.","Cuándo SMALLINT/INT: para un rango limitado (edad, cantidad, contador pequeño), SMALLINT/INT es suficiente.","Cuándo BIGINT: elija BIGINT para ID/contadores siempre crecientes y PK cuyo número de filas pueda llegar a miles de millones; el desbordamiento de INT es una causa grave de caídas en producción.","Diferencias entre motores: en MySQL, INT UNSIGNED duplica el rango; PostgreSQL no tiene UNSIGNED, pase a BIGINT. Oracle NUMBER(p), MSSQL INT/BIGINT.","Precaución: no ponga BIGINT en todas partes «por si crece»; pero para una PK creciente, BIGINT desde el principio es un seguro barato."],"related":[]},"sol_decimal":{"kind":"solution","title":"Recomendación: NUMERIC / DECIMAL; NUNCA FLOAT para dinero","detail":"Para dinero y decimales exactos use NUMERIC/DECIMAL de coma fija (exacta). FLOAT/DOUBLE son de coma flotante binaria y no pueden almacenar con exactitud valores como 0.1; las sumas/multiplicaciones producen desviaciones de céntimos, saldos erróneos y problemas de auditoría.","steps":["Por qué: NUMERIC/DECIMAL almacenan los valores exactamente en decimal; la aritmética monetaria da resultados correctos sin errores de redondeo.","Cuándo: todo campo que requiera exactitud, como precio, saldo, impuesto, tasa o interés. Especifique una escala adecuada: DECIMAL(precision, scale), p. ej. DECIMAL(19,4) para un importe.","Alternativa: algunos sistemas almacenan el dinero en la unidad más pequeña (céntimos) como BIGINT; esto también proporciona exactitud total.","Diferencias entre motores: PostgreSQL NUMERIC, MySQL DECIMAL, Oracle NUMBER(p,s), MSSQL DECIMAL/MONEY (tenga en cuenta las limitaciones de MONEY).","Precaución: FLOAT/DOUBLE son solo para valores aproximados científicos/medidos y casos en los que el rendimiento pesa más que la exactitud; nunca para dinero."],"related":[]},"sol_text":{"kind":"solution","title":"Recomendación: texto: VARCHAR vs TEXT vs CHAR","detail":"Para el texto se usa VARCHAR/TEXT para longitud variable y CHAR para longitud realmente fija. En los motores modernos, la diferencia de almacenamiento entre VARCHAR y TEXT es en general despreciable; la verdadera decisión es si imponer un límite semántico (una restricción de longitud).","steps":["VARCHAR(n): si la longitud es variable y existe un límite superior razonable (p. ej. correo electrónico, nombre), use VARCHAR(n); el límite es una herramienta de validación de datos.","TEXT: para texto libre largo de longitud impredecible (descripción, cuerpo), use TEXT.","CHAR(n): úselo solo para longitudes realmente fijas (p. ej. código de país 'TR', siempre 2 caracteres); para texto corto, CHAR rellena con espacios al final y suele ser innecesario.","Diferencias entre motores: en PostgreSQL, VARCHAR y TEXT rinden igual; el límite es solo una restricción. En MySQL, VARCHAR se almacena dentro de la fila, TEXT puede almacenarse por separado y la indexación tiene un límite de prefijo. En Oracle use VARCHAR2.","Juego de caracteres/intercalación: para emoji y Unicode completo, use siempre utf8mb4 en MySQL (el antiguo 'utf8' es de 3 bytes y rompe los emoji); elija la intercalación (p. ej. utf8mb4_0900_ai_ci) según las necesidades de ordenación/comparación. En PostgreSQL, normalmente una base de datos UTF-8 + una intercalación adecuada."],"related":[]},"sol_datetime":{"kind":"solution","title":"Recomendación: fecha/hora: TIMESTAMP con zona horaria","detail":"Si almacena un instante (la hora de un evento), elija un tipo que tenga en cuenta la zona horaria. Almacenar sin zona horaria provoca cálculos y comparaciones erróneos entre regiones/cambios de horario de verano.","steps":["Por qué: un tipo con zona horaria normaliza el instante a UTC y proporciona comparaciones y conversiones coherentes; imprescindible para sistemas globales.","Cuándo TIMESTAMPTZ: para instantes de eventos/transacciones en PostgreSQL, use TIMESTAMP WITH TIME ZONE (timestamptz); almacene en UTC y convierta a la zona horaria del usuario para mostrarlo.","Solo fecha u hora: DATE para un día del calendario, TIME para una hora no relacionada con zonas horarias; INTERVAL para duraciones (en los motores que lo admiten).","Diferencias entre motores: PostgreSQL timestamptz (en realidad almacena UTC). En MySQL, TIMESTAMP se convierte según la zona horaria pero su rango es limitado (2038); DATETIME no se convierte: imponga en la aplicación la disciplina de almacenar en UTC. Oracle TIMESTAMP WITH TIME ZONE, MSSQL DATETIMEOFFSET.","Precaución: almacenar la hora como texto (VARCHAR) o como INT de epoch rompe las operaciones de ordenación, rango y zona horaria; use un tipo de fecha/hora real."],"related":[]},"sol_pk":{"kind":"solution","title":"Recomendación: clave primaria: BIGINT vs UUID","detail":"Hay dos opciones principales para una PK: un número creciente (BIGINT identity/sequence) o un UUID. Una PK numérica es pequeña y favorable para el índice, pero predecible y requiere generación centralizada; un UUID puede generarse de forma distribuida, pero es más grande y su orden aleatorio puede perjudicar al índice.","steps":["BIGINT (creciente): el índice más pequeño y rápido; las inserciones secuenciales son ideales para un B-tree. La opción por defecto para una sola BD/generación centralizada; BIGINT en lugar de INT desde el principio es seguro frente al desbordamiento.","UUID: puede generarse en clientes/sistemas distribuidos sin colisiones y no revela un número de secuencia cuando se expone el ID. Pero ocupa 16 bytes (2×BIGINT).","El orden del UUID importa: los UUID totalmente aleatorios (v4) no son secuenciales, por lo que provocan inserciones dispersas en el índice (page splits, baja tasa de aciertos de caché); los UUID ordenados por tiempo (v7/ULID) eliminan en gran medida este problema.","Diferencias entre motores: PostgreSQL tiene un tipo uuid nativo; en MySQL almacene el UUID como BINARY(16) (en lugar de texto CHAR(36)) y genérelo de forma ordenada cuando sea posible. MSSQL uniqueidentifier + NEWSEQUENTIALID, Oracle RAW(16).","Precaución: almacenar un UUID como texto VARCHAR/CHAR(36) duplica o triplica aproximadamente el espacio y el tamaño del índice; almacénelo en binario."],"related":[]},"sol_json":{"kind":"solution","title":"Recomendación: JSON / JSONB, con moderación","detail":"El tipo JSON es potente para campos con esquema flexible, anidados o dispersos; pero puede perderse la disciplina relacional (restricciones, índices, seguridad de tipos). Regla: lleve los campos fijos y consultados con frecuencia a columnas normales y mantenga en JSON solo la parte realmente variable.","steps":["Por qué: almacena campos variables/opcionales de forma flexible en una sola columna; se pueden añadir campos sin una migración de esquema.","Cuándo: estructuras impredecibles como payloads de API externas, campos definidos por el usuario o atributos de producto.","Diferencias entre motores: en PostgreSQL use JSONB (binario, indexable, consultas @>/? con GIN); prefiéralo al JSON simple. El tipo JSON de MySQL también es binario y se consulta con una generated column + índice. Oracle 21c+ tiene un tipo JSON/OSON; MSSQL anterior a 2025 guarda el JSON en NVARCHAR y lo procesa con funciones; SQL Server 2025 y Azure SQL tienen un tipo json nativo (binario).","Precaución: indexe los campos JSON filtrados con frecuencia (GIN en PostgreSQL, índice sobre generated column en MySQL); de lo contrario, cada consulta recorre el documento completo.","Precaución: no lo meta todo en JSON: se pierden la integridad relacional, las claves foráneas y el tipado. Los campos que necesitan consultas/restricciones deben ser columnas reales."],"related":["decide-sqlnosql"]},"sol_enum":{"kind":"solution","title":"Recomendación: ENUM vs tabla de búsqueda (referencia)","detail":"Hay dos opciones para conjuntos de categorías limitados: el tipo ENUM del motor, o una tabla de búsqueda (lookup) + clave foránea. ENUM es compacto y sencillo, pero añadir un valor requiere un cambio de esquema; una tabla de búsqueda es flexible y puede llevar atributos adicionales (descripción, indicador de activo, orden).","steps":["Cuándo ENUM: si los valores son muy estables y cambian rara vez (p. ej. sexo, día de la semana) y no se necesitan atributos adicionales; compacto y legible.","Cuándo una tabla de búsqueda: si los valores pueden cambiar con el tiempo, se necesitan campos adicionales como traducciones/descripción/orden/indicador de activo, o los informes hacen join con ella; es más flexible.","Diferencias entre motores: PostgreSQL y MySQL ofrecen un tipo ENUM nativo (en PostgreSQL, añadir un valor a un ENUM requiere ALTER TYPE, y la ordenación sigue el orden de definición). Oracle y MSSQL no tienen ENUM integrado; use una restricción CHECK o una tabla de búsqueda.","Precaución: añadir/quitar un valor de un ENUM es una migración de esquema y puede causar bloqueos/costo en una tabla grande; para conjuntos que cambian con frecuencia, prefiera una tabla de búsqueda.","Alternativa: texto libre + una restricción CHECK también es un término medio práctico para conjuntos pequeños y estables."],"related":[]}}},"solver/decide-capacity.json":{"id":"decide-capacity","title":"Decisión: dimensionamiento del servidor","icon":"🧭","category":"Guía de decisión","intro":"Un dimensionamiento correcto parte del perfil de carga (OLTP, data warehouse, mixto) y exige encontrar el verdadero cuello de botella (CPU, E/S o RAM). Regla de oro: el conjunto de datos de acceso frecuente (working set) debe caber en la RAM; mida primero, luego escale y deje margen para cargas repentinas. Este árbol le ayuda a identificar la carga y a elegir el recurso y la dirección de escalado (vertical/horizontal) según el cuello de botella.","start":"root","nodes":{"root":{"kind":"step","title":"¿Cuál es su perfil de carga?","detail":"El dimensionamiento empieza por la carga, porque las cargas OLTP y analíticas requieren perfiles de recursos completamente distintos. ¿Cuál es el trabajo predominante del sistema: muchas transacciones pequeñas y rápidas (OLTP), informes/analítica con grandes escaneos y agregaciones (DWH), o una mezcla de ambos? Si no está seguro, primero tiene que medir.","choices":[{"label":"OLTP: muchas transacciones pequeñas y cortas (web/aplicación)","next":"bottleneck"},{"label":"Analítica / data warehouse (DWH): grandes escaneos y agregaciones","next":"sol_dwh"},{"label":"Carga mixta, o no estoy seguro","next":"sol_measure"},{"label":"Conozco el cuello de botella; ¿debo escalar vertical u horizontalmente?","next":"sol_scale"}]},"bottleneck":{"kind":"step","title":"¿Dónde está el cuello de botella: RAM, E/S o CPU?","detail":"En OLTP, busque el verdadero cuello de botella en lugar de añadir recursos a ciegas. Mire sus métricas: ¿los datos de acceso frecuente no caben en la RAM y hay muchas lecturas de disco (RAM/E/S), la latencia/IOPS del disco es insuficiente (E/S), o los datos ya están en memoria pero la CPU está saturada (CPU)? Escalar el recurso equivocado desperdicia dinero y tiempo.","choices":[{"label":"Los datos de acceso frecuente no caben en la RAM; las lecturas de disco son altas","next":"sol_ram"},{"label":"Los datos están en memoria, pero la latencia/IOPS del disco es insuficiente (escritura intensiva)","next":"sol_io"},{"label":"Los datos están en memoria y el disco está bien, pero la CPU está saturada de forma constante","next":"sol_cpu"}]},"sol_measure":{"kind":"solution","title":"Recomendación: mida primero y luego dimensione","detail":"Comprar hardware a partir de suposiciones casi siempre produce un tamaño equivocado. El perfil de carga y el cuello de botella surgen de métricas reales. El dimensionamiento no es una suposición, sino una iteración guiada por mediciones.","steps":["Por qué: si es OLTP o DWH, y si el cuello de botella es CPU, E/S o RAM, solo queda claro al medir con carga real; suponer es un error caro.","Mida: uso de CPU y cola de ejecución (run queue), tasa de aciertos de memoria/caché (buffer cache hit), IOPS y latencia de disco, conexiones activas y wait events.","Herramientas por motor: PostgreSQL pg_stat_* y wait events, Oracle AWR/ASH, MySQL Performance Schema, DMV y wait stats de MSSQL.","Enfoque: pruebe con una carga representativa, encuentre el recurso más ajustado, escale solo ese y vuelva a medir. Cambie un recurso cada vez para poder aislar el efecto.","Siguiente: una vez claro el perfil, pase a la decisión correspondiente (cuello de botella OLTP, DWH o escalado horizontal/vertical)."],"related":["high-cpu","memory-pressure","slow-query"]},"sol_ram":{"kind":"solution","title":"Recomendación: añada RAM; el working set debe caber en memoria","detail":"La regla OLTP más crítica: los datos y los índices de acceso frecuente (el working set) deben caber en memoria (buffer/caché). Si no caben, cada consulta va al disco, la latencia se dispara y la E/S se convierte en el cuello de botella. La RAM suele ser la inversión de mayor retorno.","steps":["Por qué: leer de memoria es cientos o miles de veces más rápido que leer de disco; cuando el working set cabe en la RAM, la tasa de aciertos de caché sube y la E/S de disco se desploma.","Cuándo: una tasa de aciertos de caché baja, wait events de 'disk read' frecuentes, un conjunto de datos activo mayor que la RAM disponible.","Cómo: dimensione correctamente el área de memoria de la BD: PostgreSQL shared_buffers + page cache del SO, MySQL innodb_buffer_pool_size (normalmente la mayor parte de la RAM), Oracle SGA/buffer cache, MSSQL max server memory.","Objetivo: idealmente, el working set + los índices caben en memoria; si no cabe todo, al menos debe caber la parte «caliente» de acceso más frecuente.","Precaución: no asigne toda la RAM física al buffer pool; deje espacio para el SO, la memoria por conexión y otros procesos; de lo contrario, el swapping lo ralentiza todo."],"related":["memory-pressure","slow-query"]},"sol_io":{"kind":"solution","title":"Recomendación: refuerce la capa de E/S (IOPS/latencia)","detail":"Aunque los datos estén en memoria, la velocidad del disco es decisiva con una carga de escritura intensiva (commit, WAL/redo/binlog, checkpoint). Aquí el problema no es la capacidad, sino las IOPS y la latencia. Una clase de disco equivocada pone el sistema de rodillas pese a tener CPU/RAM rápidas.","steps":["Por qué: por durabilidad, cada commit escribe el log en disco; un disco con pocas IOPS/alta latencia limita directamente el throughput de transacciones.","Cuándo: latencia de disco alta, esperas largas de 'log/WAL write', un cuello de botella durante los checkpoints, OLTP con escritura intensiva.","Cómo: use NVMe/SSD; mueva el log de transacciones a un disco rápido separado de los archivos de datos; en la nube, elija una clase de disco con suficientes IOPS/throughput aprovisionados (p. ej. provisioned IOPS).","Objetivo: fije objetivos tanto de IOPS como de latencia; no solo importan las IOPS, también la latencia p99. El RAID/la caché de escritura (BBU) puede reducir la latencia de escritura.","Precaución: en la nube, el throughput y las IOPS del disco suelen depender también del tamaño de la instancia; la latencia en discos conectados por red es mayor que en NVMe local."],"related":["disk-full","slow-query"]},"sol_cpu":{"kind":"solution","title":"Recomendación: resuelva el cuello de botella de CPU (primero las consultas, luego los núcleos)","detail":"Si la CPU está saturada mientras los datos están en memoria y el disco está bien, la causa raíz suele ser consultas ineficientes (planes malos, índices ausentes, muchas consultas pequeñas) o una concurrencia excesiva. Reducir la carga antes de añadir núcleos suele ser más barato y eficaz.","steps":["Por qué: lo que más CPU consume son los full scans, las ordenaciones/hashes, la compilación de planes y los cambios de contexto de demasiadas conexiones. Corregirlos es más barato que añadir núcleos.","Optimice primero: encuentre las consultas más costosas (pg_stat_statements / AWR / Performance Schema), añada los índices que faltan, reduzca las consultas N+1, reduzca el pool de conexiones.","Luego escale: si las consultas están sanas y la CPU sigue saturada, aumente el número de núcleos (vertical), sobre todo para OLTP con alta concurrencia.","Nota sobre el motor: más conexiones = más cambios de contexto; un pool pequeño con rotación rápida a menudo supera a tener más núcleos (consulte la decisión sobre el pool de conexiones).","Precaución: si la licencia es por núcleo (Oracle/MSSQL), añadir núcleos es caro; optimizar primero las consultas reduce el costo."],"related":["high-cpu","slow-query"]},"sol_dwh":{"kind":"solution","title":"Recomendación: analítica/DWH: throughput, paralelismo y escala horizontal","detail":"Una carga de data warehouse consiste en un pequeño número de consultas pesadas que recorren y agregan tablas grandes. Aquí la prioridad no es la baja latencia, sino un alto throughput de escaneo, el paralelismo y, por lo general, una arquitectura escalable horizontalmente.","steps":["Por qué: las consultas recorren terabytes; lo decisivo es el ancho de banda de disco/memoria y los núcleos trabajando en paralelo, no la latencia de cada transacción.","Cómo: almacenamiento con alto throughput de E/S, mucha RAM (para agregaciones/hash joins), muchos núcleos y consultas paralelas; un almacén columnar o una arquitectura MPP es muy eficiente a gran escala.","Escala horizontal: para un DWH grande, una arquitectura distribuida/MPP (p. ej. motores analíticos con sharding) suele escalar mejor que ampliar una sola máquina; el particionamiento + partition pruning reduce los escaneos.","Nota sobre el motor: no traslade la configuración de OLTP; para DWH se prefieren work_mem/áreas de ordenación mayores, un grado de paralelismo más alto y bloques/readahead grandes.","Precaución: no mezcle OLTP y DWH en el mismo servidor; los informes pesados ahogan el OLTP. Si es posible, mueva la analítica a una réplica/data warehouse separado."],"related":["decide-shard-replica","decide-partition"]},"sol_scale":{"kind":"solution","title":"Principio general: vertical vs horizontal + margen + medir primero","detail":"Cómo escala importa tanto como qué recurso escala. El escalado vertical (una máquina más grande) es sencillo, pero tiene un techo y un punto único de fallo; el horizontal (más máquinas) escala, pero añade complejidad. En todos los casos, deje margen para cargas repentinas y valide la decisión con mediciones.","steps":["Vertical (scale-up): aumente los recursos (CPU/RAM/IO) de una sola máquina; la opción más sencilla y el primer paso para la mayoría de los OLTP; pero hay un techo físico/de costo y un punto único de fallo.","Horizontal (scale-out): añada réplicas de lectura (para la carga de lectura) o sharding (para el volumen de escritura/datos); escala casi ilimitada, pero aporta complejidad de consistencia y operativa.","Margen: no planifique los recursos al 100%; deje un margen de ~30% en el pico de carga normal para que quepan los picos repentinos, el mantenimiento y el crecimiento.","Mida primero: antes de escalar, confirme el cuello de botella midiendo, actúe sobre el recurso más ajustado y vuelva a medir tras el cambio.","Precaución: el escalado horizontal no resuelve todos los problemas: las transacciones y los joins distribuidos son costosos; cambie solo cuando se hayan agotado el escalado vertical y la optimización."],"related":["decide-shard-replica","decide-ha"]}}},"solver/slow-write.json":{"id":"slow-write","title":"Escrituras lentas (latencia de INSERT/UPDATE)","icon":"🐢","category":"Rendimiento","intro":"Las operaciones INSERT/UPDATE/DELETE son más lentas de lo esperado. Primero determinamos en qué espera la escritura (commit/redo, bloqueos, disco o sobrecarga por fila) y luego profundizamos paso a paso hasta la causa raíz.","start":"root","nodes":{"root":{"kind":"step","title":"¿En qué espera la escritura?","detail":"Clasifique el cuello de botella observando los wait events: ¿predomina la sincronización de commit/redo (WAL), los bloqueos, la latencia de escritura en disco o la sobrecarga por fila de índices/triggers?","command":{"oracle":"SELECT event, total_waits, ROUND(time_waited/100,1) AS sec_waited\nFROM v$system_event\nWHERE event IN ('log file sync','log file parallel write','enq: TX - row lock contention','db file sequential read')\nORDER BY time_waited DESC;","postgres":"SELECT wait_event_type, wait_event, count(*) AS sessions\nFROM pg_stat_activity\nWHERE state='active'\nGROUP BY 1,2 ORDER BY 3 DESC;","mysql":"SHOW GLOBAL STATUS LIKE 'Innodb_row_lock_waits';\nSHOW GLOBAL STATUS LIKE 'Innodb_os_log_pending_fsyncs';\nSHOW ENGINE INNODB STATUS\\G","mssql":"SELECT TOP 10 wait_type, waiting_tasks_count, wait_time_ms\nFROM sys.dm_os_wait_stats\nWHERE wait_type IN ('WRITELOG','PAGEIOLATCH_EX','LCK_M_X','LCK_M_U')\nORDER BY wait_time_ms DESC;"},"choices":[{"label":"Espera en commit / redo (sincronización del WAL)","next":"commit"},{"label":"Espera en un bloqueo","next":"lock"},{"label":"Se sospecha de sobrecarga por fila (índices/triggers)","next":"overhead"},{"label":"La latencia de escritura en disco es alta","next":"sol_disk"}]},"commit":{"kind":"step","title":"¿Patrón de commit o fsync?","detail":"Un commit separado por fila (autocommit) dispara la sincronización de redo/log tantas veces como filas haya. En cambio, si el número de commits es razonable pero cada commit espera en fsync, el problema es la latencia del disco de log.","command":{"oracle":"SELECT name, value FROM v$sysstat WHERE name IN ('user commits','redo writes','redo size');\nSELECT ROUND(time_waited_micro/GREATEST(total_waits,1)/1000,2) AS ms_per_sync FROM v$system_event WHERE event='log file sync';","postgres":"SHOW synchronous_commit;\nSELECT wal_records, wal_fpi, wal_bytes, wal_buffers_full FROM pg_stat_wal; -- wal_sync/wal_sync_time PG14-17; PG18+: pg_stat_io (object = 'wal')","mysql":"SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';\nSHOW VARIABLES LIKE 'sync_binlog';\nSHOW GLOBAL STATUS LIKE 'Com_commit';","mssql":"SELECT io_stall_write_ms, num_of_writes, io_stall_write_ms/NULLIF(num_of_writes,0) AS ms_per_write\nFROM sys.dm_io_virtual_file_stats(DB_ID(), 2);"},"choices":[{"label":"Autocommit / un commit en cada fila","next":"sol_batch"},{"label":"El commit espera la sincronización del log (fsync)","next":"sol_disk"}]},"lock":{"kind":"step","title":"¿Quién retiene el bloqueo?","detail":"Si la escritura espera en un bloqueo: otra sesión puede estar reteniendo la misma fila/índice, o muchas sesiones pueden estar intentando escribir en la misma fila «caliente».","command":{"oracle":"SELECT s.sid, s.serial#, s.username, s.blocking_session, s.event, s.seconds_in_wait\nFROM v$session s WHERE s.blocking_session IS NOT NULL;","postgres":"SELECT pid, pg_blocking_pids(pid) AS blocked_by, wait_event, left(query,60) AS query\nFROM pg_stat_activity\nWHERE cardinality(pg_blocking_pids(pid)) > 0;","mysql":"SELECT requesting_engine_transaction_id AS waiter, blocking_engine_transaction_id AS blocker,\n       object_name, lock_type, lock_mode\nFROM performance_schema.data_lock_waits w\nJOIN performance_schema.data_locks l ON l.engine_lock_id = w.blocking_engine_lock_id;","mssql":"SELECT session_id, blocking_session_id, wait_type, wait_time, wait_resource\nFROM sys.dm_exec_requests\nWHERE blocking_session_id <> 0;"},"choices":[{"label":"Otra sesión retiene el bloqueo","next":"sol_lock"},{"label":"Muchas sesiones escriben en la misma fila caliente","next":"sol_hotrow"}]},"overhead":{"kind":"step","title":"¿De dónde viene la sobrecarga por fila?","detail":"Cada INSERT/UPDATE actualiza también todos los índices de la tabla y dispara los triggers/restricciones existentes. Un número excesivo de índices o un trigger pesado ralentizan considerablemente las escrituras.","command":{"oracle":"SELECT index_name, uniqueness FROM user_indexes WHERE table_name='T';\nSELECT trigger_name, triggering_event FROM user_triggers WHERE table_name='T';","postgres":"SELECT indexname, indexdef FROM pg_indexes WHERE tablename='t';\nSELECT tgname FROM pg_trigger WHERE tgrelid='t'::regclass AND NOT tgisinternal;","mysql":"SHOW INDEX FROM t;\nSHOW TRIGGERS LIKE 't';","mssql":"SELECT name, type_desc FROM sys.indexes WHERE object_id=OBJECT_ID('dbo.t');\nSELECT name, is_disabled FROM sys.triggers WHERE parent_id=OBJECT_ID('dbo.t');"},"choices":[{"label":"La tabla tiene demasiados índices / índices innecesarios","next":"sol_index"},{"label":"Hay un costo elevado de triggers o restricciones","next":"sol_trigger"}]},"sol_batch":{"kind":"solution","title":"Causa raíz: autocommit / commit por fila","detail":"Un commit separado para cada fila repite la sincronización de redo/log tantas veces como filas haya. Agrupando las operaciones puede reducir drásticamente el número de commits (y por tanto de fsync).","steps":["Desactive el autocommit en la aplicación y aplique commits por lotes, confirmando cada N filas (p. ej. 500-5000).","Use INSERT de varias filas / vías de carga masiva en lugar de INSERT de una sola fila (Postgres COPY, Oracle bulk insert, MySQL LOAD DATA, MSSQL BULK INSERT).","Use prepared statements para reducir el costo del parseo repetido (hard parse).","Tras el cambio, verifique que la espera 'log file sync' / WRITELOG ha disminuido."],"related":["active-sessions"]},"sol_disk":{"kind":"solution","title":"Causa raíz: disco de redo/log lento (latencia de fsync)","detail":"Cada commit espera hasta que el registro de redo/WAL se escribe en almacenamiento duradero (fsync). Si el disco de log es lento (alta latencia de escritura, IOPS insuficientes), cada commit paga esa latencia.","steps":["Mueva los archivos de log/redo a un disco dedicado de baja latencia (preferiblemente SSD NVMe); sepárelos físicamente de los datafiles.","Mida la latencia del almacenamiento (estadísticas de E/S de archivos); si los ms por escritura son altos, revise el hardware/la caché de escritura del RAID.","En entornos virtuales/en la nube, aumente los límites de IOPS y throughput aprovisionados.","Si los requisitos de durabilidad lo permiten, considere los ajustes de group commit / retardo de commit (p. ej. Postgres commit_delay); pero sopese el riesgo de pérdida de datos."],"related":[]},"sol_lock":{"kind":"solution","title":"Causa raíz: otra sesión retiene el bloqueo","detail":"La escritura espera un bloqueo de fila/índice retenido por otra sesión. El bloqueador suele ser una transacción de larga duración o una cuyo commit se olvidó.","steps":["Identifique la sesión bloqueadora y examine qué está haciendo (¿un lote largo o idle in transaction?).","Si el bloqueador permanece abierto/largo tiempo sin motivo, corrija el patrón de commit/rollback en la aplicación.","En una emergencia, termine el bloqueador de forma controlada (kill session / pg_terminate_backend / KILL).","Mantenga las transacciones cortas; no espere la interacción del usuario dentro de una transacción."],"related":["blocking-sessions","current-locks"]},"sol_hotrow":{"kind":"solution","title":"Causa raíz: contención en una fila caliente","detail":"Muchas sesiones intentan escribir en la misma fila (p. ej. un contador, un número de secuencia, un saldo); todas hacen cola por el mismo bloqueo y las escrituras se serializan.","steps":["Para el patrón de contador caliente, divida el valor en buckets paralelos (sharded counter) y súmelos al leer.","Traslade la generación de secuencias/ID de una única fila central a un mecanismo de sequence/identity.","Reduzca la frecuencia de actualización: acumule los totales en la aplicación y escríbalos periódicamente.","Reduzca la contención estandarizando el orden de las operaciones y acortando la duración de los bloqueos."],"related":["current-locks"]},"sol_index":{"kind":"solution","title":"Causa raíz: sobrecarga excesiva de índices","detail":"Cada índice de la tabla se actualiza en cada escritura. Muchos índices, o índices no utilizados, ralentizan directamente INSERT/UPDATE.","steps":["Identifique los índices no utilizados/solapados (estadísticas de uso) y elimínelos de forma segura.","Simplifique los índices compuestos anchos; fusione los índices que repiten las mismas columnas iniciales.","Para cargas masivas grandes, considere deshabilitar temporalmente los índices y reconstruirlos tras la carga.","Tras el cambio, vuelva a medir la latencia de escritura y confirme la mejora."],"related":["slow-queries"]},"sol_trigger":{"kind":"solution","title":"Causa raíz: trigger / restricción pesados","detail":"Un trigger que se dispara en cada escritura o una restricción que se valida (sobre todo una comprobación de clave foránea sin índice) añade un costo adicional a cada fila.","steps":["Haga asíncrono/basado en colas el trabajo pesado del cuerpo del trigger (p. ej. escrituras síncronas de log).","Indexe las columnas de clave foránea que hacen referencia (hijas); de lo contrario, cada DELETE/UPDATE en la tabla padre recorre por completo la tabla hija.","Cuando sea posible, pase los triggers a nivel de fila a nivel de sentencia.","Traslade a la capa de aplicación la lógica de triggers innecesaria o la que se ha convertido en reglas de negocio."],"related":[]}}},"solver/io-bottleneck.json":{"id":"io-bottleneck","title":"Alta espera de IO / latencia de disco","icon":"💽","category":"Rendimiento","intro":"El sistema está esperando E/S de disco. Primero distinguimos si la carga es de lectura o de escritura intensiva y luego profundizamos en las causas: buffer cache insuficiente, full table scans, tormentas de checkpoint, límites de IOPS o desbordamientos al espacio temporal.","start":"root","nodes":{"root":{"kind":"step","title":"¿La E/S es de lectura intensiva o de escritura intensiva?","detail":"Use las estadísticas de E/S de archivos para determinar si la espera está en el lado de lectura (datos leídos del disco) o en el de escritura (escrituras de redo/checkpoint/datos). Esto divide el árbol en dos.","command":{"oracle":"SELECT function_name, small_read_reqs+large_read_reqs AS reads, small_write_reqs+large_write_reqs AS writes,\n       small_read_megabytes+large_read_megabytes+small_write_megabytes+large_write_megabytes AS mb\nFROM v$iostat_function ORDER BY mb DESC;","postgres":"SELECT sum(blks_read) AS reads, sum(blks_hit) AS hits FROM pg_stat_database;\nSELECT buffers_checkpoint, buffers_clean, buffers_backend FROM pg_stat_bgwriter;","mysql":"SHOW GLOBAL STATUS LIKE 'Innodb_data_read';\nSHOW GLOBAL STATUS LIKE 'Innodb_data_written';\nSHOW GLOBAL STATUS LIKE 'Innodb_data_reads';","mssql":"SELECT DB_NAME(database_id) AS db, io_stall_read_ms, io_stall_write_ms, num_of_reads, num_of_writes\nFROM sys.dm_io_virtual_file_stats(NULL, NULL)\nORDER BY io_stall_read_ms + io_stall_write_ms DESC;"},"choices":[{"label":"Principalmente E/S de lectura","next":"read"},{"label":"Principalmente E/S de escritura","next":"write"}]},"read":{"kind":"step","title":"¿Por qué es alta la E/S de lectura?","detail":"Las lecturas físicas excesivas provienen de tres causas principales: el buffer cache es demasiado pequeño (el working set no cabe en la RAM), las consultas hacen full table scans o grandes operaciones de ordenación/hash se desbordan al espacio temporal.","command":{"oracle":"SELECT ROUND((1-(pr.value/(dbg.value+cg.value)))*100,2) AS buffer_hit_pct\nFROM v$sysstat pr, v$sysstat dbg, v$sysstat cg\nWHERE pr.name='physical reads' AND dbg.name='db block gets' AND cg.name='consistent gets';\nSELECT sql_id, disk_reads, executions FROM v$sql ORDER BY disk_reads DESC FETCH FIRST 10 ROWS ONLY;","postgres":"SELECT ROUND(sum(blks_hit)*100.0/nullif(sum(blks_hit)+sum(blks_read),0),2) AS cache_hit_pct FROM pg_stat_database;\nSELECT query, shared_blks_read, temp_blks_read FROM pg_stat_statements ORDER BY shared_blks_read DESC LIMIT 10;","mysql":"SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';\nSHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';\nSHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';","mssql":"SELECT CAST(100.0 * a.cntr_value / NULLIF(b.cntr_value, 0) AS decimal(5,2)) AS buffer_cache_hit_ratio\nFROM sys.dm_os_performance_counters a\nJOIN sys.dm_os_performance_counters b ON b.object_name = a.object_name AND b.counter_name = 'Buffer cache hit ratio base'\nWHERE a.counter_name = 'Buffer cache hit ratio' AND a.object_name LIKE '%Buffer Manager%';"},"choices":[{"label":"El buffer cache hit es bajo, el working set no cabe","next":"sol_cache"},{"label":"Las consultas hacen full table scans","next":"sol_scan"},{"label":"Grandes ordenaciones/hashes se desbordan al espacio temporal","next":"sol_spill"}]},"write":{"kind":"step","title":"¿Por qué es alta la E/S de escritura?","detail":"Un cuello de botella en el lado de escritura proviene de checkpoints/flushes periódicos que saturan brevemente el disco (tormenta de checkpoint) o de un almacenamiento sin las IOPS/el throughput necesarios para sostener la carga.","command":{"oracle":"SELECT event, total_waits, ROUND(time_waited/100,1) AS sec\nFROM v$system_event\nWHERE event IN ('log file parallel write','db file parallel write','checkpoint completed')\nORDER BY time_waited DESC;","postgres":"-- PG17+:\nSELECT num_timed, num_requested, buffers_written FROM pg_stat_checkpointer;\n-- PG16 and earlier:\nSELECT checkpoints_timed, checkpoints_req, buffers_checkpoint, maxwritten_clean\nFROM pg_stat_bgwriter;\nSHOW checkpoint_completion_target;","mysql":"SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_dirty';\nSHOW GLOBAL STATUS LIKE 'Innodb_os_log_written';\nSHOW ENGINE INNODB STATUS\\G","mssql":"SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name='Checkpoint pages/sec';\nSELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name='Page writes/sec';"},"choices":[{"label":"Oleadas repentinas de checkpoint/flush (tormenta de checkpoint)","next":"sol_checkpoint"},{"label":"El almacenamiento no aguanta la carga sostenida (límite de IOPS)","next":"sol_iops"}]},"sol_cache":{"kind":"solution","title":"Causa raíz: buffer cache insuficiente","detail":"El conjunto de datos de acceso frecuente (working set) no cabe en el buffer cache, por lo que los bloques se releen constantemente desde el disco. La tasa de aciertos es baja y las lecturas físicas son altas.","steps":["Aumente el tamaño del buffer/caché: Oracle db_cache_size/SGA, Postgres shared_buffers, MySQL innodb_buffer_pool_size, MSSQL max server memory.","Compare la tasa de aciertos y las lecturas físicas antes y después del cambio.","Si la RAM es físicamente insuficiente, añada memoria al servidor o reduzca el working set con archivado/particionamiento.","Si el verdadero cuello de botella proviene de un único patrón de escaneo, pase a la rama de escaneos del árbol de cache hit."],"related":["cache-hit-ratio"]},"sol_scan":{"kind":"solution","title":"Causa raíz: full table scans","detail":"Las consultas recorren tablas grandes de principio a fin porque no hay un índice adecuado; esto provoca muchas lecturas físicas y además contamina la caché con bloques inútiles.","steps":["Obtenga los planes (EXPLAIN) de las consultas con más lecturas físicas y localice los full scans.","Añada índices adecuados en las columnas de filtro y de join; verifique que el plan pasa a acceso por índice.","Para tablas muy grandes, reduzca los datos recorridos con particionamiento por fecha/rango.","Seleccione solo las columnas necesarias en lugar de un 'SELECT *' innecesario para reducir el número de bloques leídos."],"related":["slow-queries"]},"sol_spill":{"kind":"solution","title":"Causa raíz: ordenaciones/hashes que se desbordan al espacio temporal","detail":"Las operaciones grandes de sort, hash join o group by no caben en la memoria de trabajo y escriben en el espacio temporal (temp/tempdb), generando una E/S intensa de lectura/escritura.","steps":["Aumente el ajuste de memoria de trabajo: Oracle pga_aggregate_target, Postgres work_mem, MySQL sort_buffer_size/join_buffer_size, MSSQL memory grant.","Reduzca el volumen de la operación: aplique los filtros pronto, elimine las ordenaciones innecesarias, mejore el orden de los joins.","Evite los desbordamientos a disco añadiendo índices adecuados para las ordenaciones/agrupaciones frecuentes.","Mueva el espacio temporal a un disco rápido; si el problema persiste, profundice con el árbol de presión de undo/temp."],"related":["slow-queries"]},"sol_checkpoint":{"kind":"solution","title":"Causa raíz: tormenta de checkpoint","detail":"Las páginas sucias se acumulan durante mucho tiempo y se escriben en disco de forma masiva a intervalos cortos; estas oleadas repentinas de escritura saturan periódicamente la E/S.","steps":["Reparta los checkpoints en el tiempo: en Postgres aumente checkpoint_completion_target y equilibre checkpoint_timeout.","En MySQL, configure innodb_io_capacity / innodb_io_capacity_max según el almacenamiento y ajuste el adaptive flushing.","En MSSQL, suavice las oleadas repentinas con indirect checkpoints (target_recovery_time).","En Oracle, equilibre la frecuencia de checkpoint con fast_start_mttr_target y amplíe los archivos de redo log."],"related":[]},"sol_iops":{"kind":"solution","title":"Causa raíz: IOPS / throughput insuficientes","detail":"El almacenamiento carece de las IOPS o del ancho de banda para atender las solicitudes de lectura+escritura que requiere la carga; la profundidad de cola y la latencia son persistentemente altas.","steps":["Mida la latencia del disco (ms por solicitud) y compárela con el límite de IOPS del hardware/la nube.","Pase a un almacenamiento más rápido (HDD→SSD/NVMe) o aumente el nivel de IOPS/throughput aprovisionados en la nube.","Reduzca la contención repartiendo los datos, el redo/log y el espacio temporal en discos distintos.","Como solución duradera, reduzca la demanda total de E/S (indexación, archivado, optimización de consultas)."],"related":[]}}},"solver/undo-temp-pressure.json":{"id":"undo-temp-pressure","title":"Presión en undo / TempDB / espacio temporal","icon":"🧯","category":"Capacidad","intro":"El espacio de undo/rollback, el espacio temporal (temp) o TempDB está bajo presión. Determinamos dónde está la presión y profundizamos hasta la causa raíz, como una ordenación/hash grande, una consulta/transacción larga o una fuga de espacio.","start":"root","nodes":{"root":{"kind":"step","title":"¿Dónde está la presión: undo, temp o contención en tempdb?","detail":"Clasifique el origen de la presión observando el uso del espacio: ¿se está llenando el tablespace de undo/rollback, el espacio temporal (temp) general o, en MSSQL, se trata de contención de latches en la asignación de páginas de TempDB?","command":{"oracle":"SELECT tablespace_name, ROUND(used_percent,1) AS used_pct\nFROM dba_tablespace_usage_metrics\nWHERE tablespace_name LIKE '%UNDO%' OR tablespace_name LIKE 'TEMP%';","postgres":"SELECT datname, temp_files, pg_size_pretty(temp_bytes) AS temp_written\nFROM pg_stat_database ORDER BY temp_bytes DESC;","mysql":"SELECT * FROM information_schema.INNODB_TEMP_TABLE_INFO;\nSHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';","mssql":"SELECT SUM(user_object_reserved_page_count)*8/1024 AS user_obj_mb,\n       SUM(internal_object_reserved_page_count)*8/1024 AS internal_obj_mb,\n       SUM(version_store_reserved_page_count)*8/1024 AS version_store_mb\nFROM sys.dm_db_file_space_usage;"},"choices":[{"label":"El espacio de undo/rollback se está llenando (ORA-01555 / ORA-30036)","next":"undo"},{"label":"El espacio temporal (temp) se está llenando (desbordamiento de sort/hash)","next":"temp"},{"label":"Contención de latches de asignación en TempDB (PFS/GAM/SGAM)","next":"tempdb"}]},"undo":{"kind":"step","title":"¿Qué está consumiendo el undo?","detail":"La presión de undo suele ser de uno de dos tipos: una CONSULTA larga necesita undo antiguo para la lectura consistente a partir de su hora de inicio (ORA-01555), o una TRANSACCIÓN larga/abierta genera tanto undo que llena el espacio y provoca ORA-30036.","command":{"oracle":"SELECT s.sid, s.username, t.used_ublk*8/1024 AS undo_mb, s.status, t.start_time\nFROM v$transaction t JOIN v$session s ON s.taddr=t.addr\nORDER BY undo_mb DESC;\nSELECT MAX(maxquerylen) AS longest_query_sec, MAX(tuned_undoretention) AS tuned_retention FROM v$undostat;","postgres":"SELECT pid, now()-xact_start AS xact_age, state, left(query,60) AS query\nFROM pg_stat_activity WHERE xact_start IS NOT NULL ORDER BY xact_start LIMIT 10;"},"choices":[{"label":"Una consulta larga necesita undo antiguo (ORA-01555)","next":"sol_undo_query"},{"label":"Una transacción larga/abierta está acumulando undo (ORA-30036)","next":"sol_undo_tx"}]},"temp":{"kind":"step","title":"¿Qué está llenando el espacio temporal?","detail":"La presión de temp proviene de una gran operación de sort/hash que se desborda a disco (normal pero enorme) o de una sola sesión que retiene temp de forma continua (cursor abierto, fuga, consulta interminable).","command":{"oracle":"SELECT s.sid, s.username, u.tablespace, u.blocks*8/1024 AS temp_mb, u.sqladdr\nFROM v$tempseg_usage u JOIN v$session s ON s.saddr=u.session_addr\nORDER BY temp_mb DESC;","postgres":"SELECT query, temp_blks_written, temp_blks_read\nFROM pg_stat_statements ORDER BY temp_blks_written DESC LIMIT 10;","mysql":"SELECT event_name, current_number_of_bytes_used\nFROM performance_schema.memory_summary_global_by_event_name\nWHERE event_name LIKE '%temptable%' OR event_name LIKE '%sort%';","mssql":"SELECT session_id, request_id, SUM(internal_objects_alloc_page_count)*8/1024 AS internal_mb\nFROM sys.dm_db_task_space_usage GROUP BY session_id, request_id ORDER BY internal_mb DESC;"},"choices":[{"label":"Una gran operación de sort/hash se desborda a disco","next":"sol_temp_sort"},{"label":"Una sola sesión retiene temp (fuga/cursor abierto)","next":"sol_temp_leak"}]},"tempdb":{"kind":"step","title":"¿La contención en TempDB es de asignación o de volumen?","detail":"En MSSQL TempDB se dan dos problemas distintos: contención PAGELATCH en las páginas PFS/GAM/SGAM (muchos objetos temporales pequeños), o una gran ordenación/version store que llena TempDB por volumen.","command":{"mssql":"SELECT session_id, wait_type, wait_duration_ms, resource_description\nFROM sys.dm_os_waiting_tasks\nWHERE wait_type LIKE 'PAGELATCH%' AND resource_description LIKE '2:%';\nSELECT COUNT(*) AS tempdb_data_files FROM sys.master_files WHERE database_id=2 AND type=0;"},"choices":[{"label":"Contención de latches PFS/GAM/SGAM","next":"sol_tempdb_latch"},{"label":"Una gran ordenación/version store está llenando TempDB","next":"sol_temp_sort"}]},"sol_undo_query":{"kind":"solution","title":"Causa raíz: consulta larga + undo insuficiente (ORA-01555)","detail":"El undo antiguo que la consulta larga necesita para una lectura consistente se sobrescribió porque expiró la retención o el espacio era insuficiente.","steps":["Aumente undo_retention a un valor que supere la duración de la consulta más larga (v$undostat.maxquerylen).","Amplíe el tablespace UNDO o habilite autoextend para que el periodo de retención esté respaldado por espacio.","Para los informes críticos, proteja el undo antiguo con ALTER TABLESPACE undotbs RETENTION GUARANTEE (el DML puede fallar si el espacio se llena; tenga cuidado).","Para una solución permanente, optimice la consulta larga o muévala a un standby/réplica."],"related":["slow-queries"]},"sol_undo_tx":{"kind":"solution","title":"Causa raíz: una transacción larga/abierta llenó el undo (ORA-30036)","detail":"Un único DML enorme o una transacción abierta con un commit retrasado generó demasiado undo; el tablespace UNDO no pudo extenderse y se produjo ORA-30036.","steps":["Identifique la transacción activa que retiene más undo (v$transaction.used_ublk) y localice a su propietario.","Ejecute el DML enorme en bloques por fecha/rango con commits intermedios.","Corrija en el lado de la aplicación las sesiones con un commit olvidado / idle-in-transaction; termínelas de forma controlada si es necesario.","Añada temporalmente espacio al tablespace UNDO; de forma permanente, planifique la retención y el tamaño del espacio según la carga."],"related":["active-sessions"]},"sol_temp_sort":{"kind":"solution","title":"Causa raíz: una ordenación/hash grande está llenando el espacio temporal","detail":"Las grandes operaciones de sort/hash/group by que no caben en memoria se desbordan al espacio temporal y pueden llenarlo.","steps":["Aumente la memoria de trabajo: Oracle pga_aggregate_target, Postgres work_mem, MySQL sort_buffer_size, MSSQL memory grant.","Mejore la consulta: elimine ordenaciones innecesarias, aplique los filtros pronto, añada índices en las columnas de ordenación/agrupación.","Divida los trabajos muy grandes en bloques; limite la concurrencia para que no retengan temp todos a la vez.","Amplíe el espacio temporal y muévalo a un disco rápido; configure MSSQL TempDB con varios archivos según el número de núcleos."],"related":["slow-queries"]},"sol_temp_leak":{"kind":"solution","title":"Causa raíz: una sesión retiene espacio temporal (fuga)","detail":"Una sola sesión no libera el espacio temporal por cursores sin cerrar, una consulta interminable o tablas temporales abiertas.","steps":["Identifique la sesión que retiene más temp y el SQL que está ejecutando.","Asegúrese de que los cursores/conjuntos de resultados y las tablas temporales se cierran correctamente en la aplicación.","Termine de forma controlada la sesión atascada/excesivamente larga y verifique que se devuelve el espacio.","Defina las tablas temporales globales para que se limpien automáticamente al final de la sesión/transacción."],"related":["active-sessions"]},"sol_tempdb_latch":{"kind":"solution","title":"Causa raíz: contención de latches PFS/GAM/SGAM en TempDB","detail":"Cuando muchas sesiones crean objetos temporales pequeños al mismo tiempo, surge contención PAGELATCH en las páginas del mapa de asignación de TempDB (PFS/GAM/SGAM).","steps":["Configure un número de archivos de datos de TempDB proporcional al número de núcleos de CPU, con tamaños iguales (p. ej. 4-8 archivos, todos con el mismo tamaño/autogrowth).","En SQL Server moderno, habilite las opciones que reducen la contención de asignación (memory-optimized tempdb metadata).","Reduzca la creación de tablas temporales: sustituya las tablas #temp innecesarias por variables de tabla / una sola consulta.","Tras el cambio, verifique que los tiempos de espera PAGELATCH han disminuido."],"related":["current-locks"]}}},"solver/long-transaction.json":{"id":"long-transaction","title":"Transacción de larga duración / abierta","icon":"⏱️","category":"Bloqueos","intro":"Una transacción de larga duración o abierta está acumulando bloqueos, undo y bloat. Localizamos la transacción causante, determinamos su tipo (idle in transaction, lote largo, impacto) y la resolvemos de forma segura.","start":"root","nodes":{"root":{"kind":"step","title":"¿Cuál es la transacción abierta más antigua?","detail":"Liste las transacciones abiertas por hora de inicio. La más antigua suele ser el origen del problema. Compruebe su estado: ¿se está ejecutando activamente o está 'idle in transaction' esperando un commit?","command":{"oracle":"SELECT s.sid, s.serial#, s.username, s.status, t.start_time, t.used_ublk*8/1024 AS undo_mb\nFROM v$transaction t JOIN v$session s ON s.taddr=t.addr\nORDER BY t.start_time;","postgres":"SELECT pid, state, now()-xact_start AS xact_age, now()-state_change AS since_change, left(query,60) AS query\nFROM pg_stat_activity\nWHERE xact_start IS NOT NULL ORDER BY xact_start LIMIT 20;","mysql":"SELECT trx_id, trx_state, trx_started, TIMESTAMPDIFF(SECOND,trx_started,NOW()) AS secs, trx_mysql_thread_id, trx_rows_locked\nFROM information_schema.INNODB_TRX ORDER BY trx_started;","mssql":"SELECT s.session_id, s.login_name, t.transaction_begin_time,\n       DATEDIFF(second,t.transaction_begin_time,GETDATE()) AS secs\nFROM sys.dm_tran_active_transactions t\nJOIN sys.dm_tran_session_transactions st ON st.transaction_id=t.transaction_id\nJOIN sys.dm_exec_sessions s ON s.session_id=st.session_id\nORDER BY t.transaction_begin_time;"},"choices":[{"label":"idle in transaction (commit olvidado, inactiva)","next":"idle"},{"label":"Lote/DML activo pero de muy larga duración","next":"batch"},{"label":"Acumula bloqueos/bloat/undo mientras la transacción continúa","next":"impact"}]},"idle":{"kind":"step","title":"¿Por qué se produce idle in transaction?","detail":"La sesión abrió una transacción pero permanece inactiva sin ejecutar ninguna consulta; es decir, se olvidó el commit/rollback. Esto retiene bloqueos y undo innecesariamente. ¿Basta con una corrección en la aplicación, o hay que terminarla con urgencia?","command":{"postgres":"SELECT pid, now()-state_change AS idle_age, left(query,80) AS last_query\nFROM pg_stat_activity WHERE state='idle in transaction' ORDER BY state_change;","mysql":"SELECT trx.trx_mysql_thread_id, trx.trx_started, p.time AS idle_secs, p.command\nFROM information_schema.INNODB_TRX trx\nJOIN information_schema.PROCESSLIST p ON p.id=trx.trx_mysql_thread_id\nWHERE p.command='Sleep' ORDER BY trx.trx_started;","oracle":"SELECT sid, serial#, status, last_call_et AS idle_secs\nFROM v$session WHERE status='INACTIVE' AND type='USER' AND last_call_et>300;","mssql":"SELECT s.session_id, s.status, DATEDIFF(second,s.last_request_end_time,GETDATE()) AS idle_secs\nFROM sys.dm_exec_sessions s\nWHERE s.session_id IN (SELECT session_id FROM sys.dm_tran_session_transactions)\n  AND s.status='sleeping';"},"choices":[{"label":"La aplicación/el pool de conexiones no hace commit (solución permanente)","next":"sol_idle"},{"label":"Necesito terminarla ahora mismo","next":"sol_kill"}]},"batch":{"kind":"step","title":"¿Qué está haciendo la transacción larga activa?","detail":"La sesión se está ejecutando realmente (activa). ¿Es un único DML/informe enorme, o un bloqueador que hace esperar a otras sesiones y debe cortarse con urgencia?","command":{"oracle":"SELECT s.sid, s.serial#, sq.sql_text, s.last_call_et AS running_secs\nFROM v$session s JOIN v$sql sq ON sq.sql_id=s.sql_id\nWHERE s.status='ACTIVE' AND s.type='USER' ORDER BY s.last_call_et DESC;","postgres":"SELECT pid, now()-query_start AS run_time, left(query,80) AS query\nFROM pg_stat_activity WHERE state='active' ORDER BY query_start LIMIT 10;","mysql":"SELECT id, time AS run_secs, state, LEFT(info,80) AS query\nFROM information_schema.PROCESSLIST WHERE command<>'Sleep' ORDER BY time DESC;","mssql":"SELECT r.session_id, r.status, r.total_elapsed_time/1000 AS secs, SUBSTRING(t.text,1,80) AS query\nFROM sys.dm_exec_requests r CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t\nORDER BY r.total_elapsed_time DESC;"},"choices":[{"label":"Un único DML/informe enorme; hay que dividirlo","next":"sol_batch"},{"label":"Hace esperar a otros; terminarla con urgencia","next":"sol_kill"}]},"impact":{"kind":"step","title":"¿Qué está acumulando la transacción larga?","detail":"Determine el efecto secundario de la transacción larga: ¿se trata sobre todo de una acumulación de bloqueos que hace esperar a otras sesiones, o de bloat por retener versiones antiguas de filas y bloquear la limpieza (VACUUM/undo)?","command":{"postgres":"SELECT pid, pg_blocking_pids(pid) AS blocked_by FROM pg_stat_activity WHERE cardinality(pg_blocking_pids(pid))>0;\nSELECT max(age(backend_xmin)) AS max_xmin_age FROM pg_stat_activity WHERE backend_xmin IS NOT NULL;","oracle":"SELECT blocking_session, sid, serial#, seconds_in_wait, event\nFROM v$session WHERE blocking_session IS NOT NULL;","mssql":"SELECT session_id, blocking_session_id, wait_type, wait_time\nFROM sys.dm_exec_requests WHERE blocking_session_id<>0;"},"choices":[{"label":"Una acumulación de bloqueos hace esperar a otras sesiones","next":"sol_lock_backlog"},{"label":"La acumulación de versiones antiguas bloquea la limpieza (undo/bloat)","next":"sol_bloat"}]},"sol_idle":{"kind":"solution","title":"Causa raíz: la aplicación no hace commit (idle in transaction)","detail":"El pool de conexiones o la aplicación abre una transacción y luego mantiene la conexión inactiva sin hacer commit/rollback. Esto retiene bloqueos y undo innecesariamente.","steps":["En el código de la aplicación, garantice un commit/rollback en todos los caminos en los que se abre una transacción; no espere la entrada del usuario ni E/S largas dentro de una transacción.","Corte automáticamente las transacciones inactivas: Postgres idle_in_transaction_session_timeout, ajustes de conexión/timeout de MySQL, maxLifetime del pool de la aplicación.","Configure el pool de conexiones según el principio de «autocommit activado, transacciones cortas».","Configure el monitoreo: genere una alerta para las sesiones idle-in-transaction prolongadas."],"related":["active-sessions","blocking-sessions"]},"sol_kill":{"kind":"solution","title":"Solución: terminar de forma segura la sesión causante","detail":"Si una sesión bloqueadora/larga hace esperar a otras, debe terminarse de forma controlada. La terminación desencadena un rollback, que puede necesitar espacio/tiempo; tenga cuidado.","steps":["Verifique primero qué es la sesión (qué usuario/aplicación, qué trabajo) para no cortar un trabajo crítico.","Terminar: Oracle ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE; Postgres SELECT pg_terminate_backend(pid); MySQL KILL thread_id; MSSQL KILL session_id;","Si termina una transacción de escritura grande, espere a que se complete el rollback (reversión de undo/log).","Tras la terminación, verifique que los bloqueos y las sesiones en espera han desaparecido; corrija la causa raíz (inactividad/lote) de forma permanente."],"related":["blocking-sessions"]},"sol_batch":{"kind":"solution","title":"Causa raíz: un único DML/informe enorme","detail":"Un DML que procesa millones de filas en una sola transacción, o un informe muy largo, acumula bloqueos y undo/bloat durante toda su duración.","steps":["Divida el DML grande en bloques por fecha/rango/clave y haga commit tras cada bloque.","Traslade el informe pesado fuera de las horas pico o derívelo a un standby/réplica de lectura.","Optimice la consulta larga para acortar su duración total (y por tanto el tiempo durante el que retiene bloqueos/undo).","Haga observable el trabajo por lotes: registre el progreso y permita reanudarlo desde donde se quedó."],"related":["slow-queries"]},"sol_lock_backlog":{"kind":"solution","title":"Causa raíz: cola de bloqueos acumulados","detail":"Por los bloqueos que retiene, la transacción larga ha creado una cola de espera detrás de ella; muchas sesiones esperan el mismo recurso.","steps":["Identifique la sesión que encabeza la cadena de bloqueos (head blocker); es el único nodo que vaciará la cola.","Acorte el head blocker (commit/rollback) o termínelo de forma controlada; observe cómo se vacía la cola.","Para acortar la duración de los bloqueos, haga transacciones más pequeñas y defina un orden de acceso coherente.","Cuando proceda, pase a enfoques que requieran bloqueos más débiles (a nivel de fila, un nivel de aislamiento adecuado)."],"related":["blocking-sessions","current-locks"]},"sol_bloat":{"kind":"solution","title":"Causa raíz: acumulación de versiones antiguas (undo/bloat)","detail":"La transacción de larga duración impide la limpieza de las versiones antiguas de filas anteriores a ella. En Postgres se manifiesta como bloat de tablas/índices; en Oracle, como presión de undo.","steps":["Encuentre la sesión que retiene el xmin/la transacción más antiguos (Postgres backend_xmin; en Oracle, la transacción activa más antigua).","Acorte/finalice esta transacción para que la limpieza (VACUUM / reciclaje de undo) pueda avanzar de nuevo.","En Postgres, cuando termine la transacción, ejecute VACUUM en las tablas con bloat (VACUUM FULL/pg_repack si es necesario).","Adopte de forma permanente un patrón de aplicación que mantenga las transacciones cortas y haga más agresivo el autovacuum."],"related":[]}}},"solver/plan-regression.json":{"id":"plan-regression","title":"Plan de consulta degradado (lentitud repentina)","icon":"📉","category":"Rendimiento","intro":"Una consulta que ayer era rápida hoy es de repente lenta. Normalmente el optimizador ha cambiado a un plan peor. Buscamos la causa raíz entre estadísticas desactualizadas, sensibilidad a parámetros, crecimiento del volumen de datos o un cambio repentino de plan, y estabilizamos el plan.","start":"root","nodes":{"root":{"kind":"step","title":"¿El plan cambió realmente, y cuándo?","detail":"¿Hay varios planes (plan_hash/plan_generation) para la misma consulta, y cuándo empezó la lentitud? ¿Coincide con un despliegue reciente, una actualización, un cambio de parámetros o una carga de datos grande?","command":{"oracle":"SELECT sql_id, plan_hash_value, executions, ROUND(elapsed_time/GREATEST(executions,1)/1e6,3) AS sec_per_exec, first_load_time\nFROM v$sql WHERE sql_id='&sqlid' ORDER BY first_load_time;","postgres":"EXPLAIN (ANALYZE, BUFFERS) SELECT /* your query */ 1;\n-- Trend: SELECT query, calls, mean_exec_time FROM pg_stat_statements WHERE query LIKE '%...%';","mysql":"EXPLAIN FORMAT=JSON SELECT /* your query */ 1;\nSELECT DIGEST_TEXT, COUNT_STAR, AVG_TIMER_WAIT/1e9 AS avg_ms\nFROM performance_schema.events_statements_summary_by_digest WHERE DIGEST_TEXT LIKE '%...%';","mssql":"SELECT qs.plan_generation_num, qs.execution_count, qs.total_worker_time/qs.execution_count AS avg_cpu_us, qs.creation_time\nFROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t\nWHERE t.text LIKE '%...%';"},"choices":[{"label":"Las estadísticas pueden estar desactualizadas","next":"stats"},{"label":"La misma consulta se comporta de forma distinta según el parámetro (sniffing/peeking)","next":"sniff"},{"label":"El volumen de datos ha crecido considerablemente","next":"sol_datagrowth"},{"label":"El plan cambió de repente tras una actualización/flush","next":"sol_planflip"}]},"stats":{"kind":"step","title":"¿Están actualizadas las estadísticas?","detail":"El optimizador basa sus decisiones en las estadísticas. Si la fecha del último analyze es antigua o el contador de modificaciones es alto, las estimaciones pueden haberse desviado realmente, lo que lleva a un plan malo.","command":{"oracle":"SELECT table_name, num_rows, last_analyzed, stale_stats\nFROM dba_tab_statistics WHERE table_name='T';","postgres":"SELECT relname, n_live_tup, n_mod_since_analyze, last_analyze, last_autoanalyze\nFROM pg_stat_user_tables WHERE relname='t';","mysql":"SELECT table_name, update_time, table_rows FROM information_schema.tables WHERE table_name='t';\nSHOW INDEX FROM t;","mssql":"SELECT s.name, STATS_DATE(s.object_id,s.stats_id) AS last_updated, p.modification_counter, p.rows\nFROM sys.stats s CROSS APPLY sys.dm_db_stats_properties(s.object_id,s.stats_id) p\nWHERE s.object_id=OBJECT_ID('dbo.t');"},"choices":[{"label":"Las estadísticas están desactualizadas/faltan","next":"sol_stats"},{"label":"Las estadísticas están actualizadas, pero el plan sigue siendo sensible a parámetros","next":"sniff"}]},"sniff":{"kind":"step","title":"¿Hay sensibilidad a parámetros?","detail":"La consulta elige y almacena en caché un plan según el valor del parámetro de su primera ejecución (MSSQL parameter sniffing / Oracle bind peeking). Ese plan puede ser malo para otros valores con una distribución sesgada.","command":{"oracle":"SELECT sql_id, child_number, plan_hash_value, is_bind_sensitive, is_bind_aware\nFROM v$sql WHERE sql_id='&sqlid';","mssql":"SELECT qs.query_hash, qs.execution_count, qs.total_worker_time/qs.execution_count AS avg_cpu_us\nFROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t\nWHERE t.text LIKE '%...%';","postgres":"-- For generic vs custom plan behavior:\nPREPARE p AS SELECT /* your query */ 1;\nEXPLAIN EXECUTE p;"},"choices":[{"label":"Sí, el plan alterna entre bueno y malo según el parámetro","next":"sol_sniff"},{"label":"Se conoce el plan correcto; quiero fijarlo de forma permanente","next":"sol_forceplan"}]},"sol_stats":{"kind":"solution","title":"Causa raíz: estadísticas desactualizadas / ausentes","detail":"Basándose en estadísticas antiguas que no se corresponden con la realidad, el optimizador estimó mal el número de filas y eligió un plan malo. Es la causa más común de degradación de planes.","steps":["Recopile estadísticas nuevas para las tablas e índices afectados: Oracle DBMS_STATS.GATHER_TABLE_STATS, Postgres ANALYZE, MySQL ANALYZE TABLE, MSSQL UPDATE STATISTICS ... WITH FULLSCAN.","Cree histogramas/estadísticas extendidas en las columnas con una distribución sesgada.","Tras recopilar las estadísticas, verifique el plan con EXPLAIN y confirme que ha vuelto al plan bueno.","Para evitar que se repita, mantenga habilitada la recopilación automática de estadísticas (autovacuum/auto update stats) con umbrales adecuados."],"related":["slow-queries"]},"sol_sniff":{"kind":"solution","title":"Causa raíz: sensibilidad a parámetros (sniffing / bind peeking)","detail":"El plan en caché se eligió para el primer valor de parámetro que llegó y es malo para otros valores con una distribución sesgada. El objetivo es asegurarse de que se elige un plan adecuado al valor.","steps":["En MSSQL, defina un comportamiento dirigido en la consulta crítica con OPTION (RECOMPILE) u OPTIMIZE FOR; prefiera recompile para valores que cambian con frecuencia.","En Oracle, use adaptive cursor sharing / el comportamiento bind-aware; si es necesario, divida la consulta por rangos de valores.","En Postgres, observe la elección entre plan genérico y plan personalizado (generic vs custom); oriéntela con plan_cache_mode si es necesario.","Si se necesita un único plan permanente y correcto, pase a la solución de fijación de planes."],"related":["slow-queries"]},"sol_forceplan":{"kind":"solution","title":"Solución: fijar el plan correcto (baseline / force plan)","detail":"Si existe un plan bueno conocido, fijarlo para que el optimizador no se desvíe de él detiene la degradación de forma permanente.","steps":["Oracle: cree un SQL Plan Baseline para el plan bueno (DBMS_SPM) o acéptelo mediante SQL Plan Management.","MSSQL: seleccione el plan bueno en Query Store y fuércelo con sp_query_store_force_plan.","MySQL/Postgres: oriente el plan con optimizer hints (p. ej. index hints) o pg_hint_plan.","Monitoree el rendimiento tras la fijación; si el patrón de datos cambia radicalmente, vuelva a evaluar el baseline."],"related":[]},"sol_datagrowth":{"kind":"solution","title":"Causa raíz: el volumen de datos creció y el plan ya no es adecuado","detail":"A medida que crece la tabla, un plan que antes era ideal (p. ej. nested loop) ahora escala mal; el optimizador necesita estadísticas y rutas de acceso que reflejen correctamente el crecimiento.","steps":["Recopile estadísticas actuales para que el optimizador vea el nuevo número de filas.","Añada índices adecuados para las tablas en crecimiento; corrija la ruta de acceso que ahora escala mal (p. ej. full scan/nested loop).","En tablas muy grandes, limite los datos recorridos con particionamiento por fecha/rango.","Reescriba la consulta para adaptarla al crecimiento (filtrado temprano, paginación, vistas de resumen/materializadas)."],"related":["slow-queries"]},"sol_planflip":{"kind":"solution","title":"Causa raíz: cambio repentino de plan (actualización / vaciado de caché)","detail":"El plan cambió de repente tras una actualización de versión, un cambio de parámetros o el vaciado de la caché de planes. El comportamiento del optimizador o las condiciones del entorno han cambiado.","steps":["Identifique el desencadenante del cambio: actualización/parche, parámetros del optimizador, una carga de datos o un reinicio/vaciado de caché.","Si la causa es un parámetro, revierta el cambio y vuelva a evaluarlo de forma controlada.","Fije el plan bueno conocido (baseline / force plan) para que el optimizador no vuelva a desviarse.","Para las degradaciones tras una actualización, revise los ajustes de compatibilidad/preparación del optimizador."],"related":[]}}},"solver/replica-resync.json":{"id":"replica-resync","title":"Réplica rota / se necesita resincronización","icon":"🔗","category":"Replicación","intro":"La sincronización entre la réplica/standby y el primario está rota. Determine si la replicación se ha detenido, cuánto retraso lleva y si la cadena de posición/GTID está rota; luego tome el camino correcto entre omitir un error, esperar o reconstruir (reseed). Incluye una advertencia de split-brain.","start":"root","nodes":{"root":{"kind":"step","title":"¿Cuál es el estado general de la replicación?","detail":"Lea primero la salud de la replicación: ¿se están ejecutando los hilos de aplicación, cuánto retraso hay, hay algún error? Verifique también que los dos nodos no estén aceptando escrituras a la vez (split-brain).","command":{"mysql":"SHOW REPLICA STATUS\\G\n-- Check: Replica_IO_Running, Replica_SQL_Running, Seconds_Behind_Source, Last_Error, Retrieved/Executed_Gtid_Set","postgres":"-- On the primary:\nSELECT client_addr, state, sent_lsn, replay_lsn, pg_wal_lsn_diff(sent_lsn,replay_lsn) AS lag_bytes FROM pg_stat_replication;\n-- On the replica:\nSELECT status, last_msg_receipt_time, latest_end_lsn FROM pg_stat_wal_receiver;","oracle":"SELECT name, value, unit, time_computed FROM v$dataguard_stats WHERE name IN ('transport lag','apply lag');\nSELECT dest_id, status, error, gap_status FROM v$archive_dest_status WHERE status<>'INACTIVE';","mssql":"SELECT ag.name AS ag, drs.synchronization_state_desc, drs.synchronization_health_desc,\n       drs.log_send_queue_size, drs.redo_queue_size\nFROM sys.dm_hadr_database_replica_states drs\nJOIN sys.availability_groups ag ON ag.group_id=drs.group_id;"},"choices":[{"label":"La replicación se ha detenido / está fallando","next":"error"},{"label":"Funciona, pero con mucho retraso (lag grande)","next":"lag"},{"label":"GTID/posición rotos, inconsistente: reconstruir","next":"sol_reseed"},{"label":"Ambos nodos escriben como primario (sospecha de split-brain)","next":"sol_splitbrain"}]},"error":{"kind":"step","title":"¿Se puede reparar el error o hace falta una reconstrucción?","detail":"Examine el error de aplicación. Algunos errores (clave duplicada, fila ausente) pueden resolverse omitiendo de forma segura un único evento; pero si hay inconsistencia/corrupción de datos, el único camino correcto es reconstruir la réplica.","command":{"mysql":"SHOW REPLICA STATUS\\G\n-- Check: Last_SQL_Errno, Last_SQL_Error, Last_IO_Errno, Last_IO_Error","postgres":"-- Examine the replica logs (PostgreSQL log): recovery / WAL apply error messages.\nSELECT status, last_msg_receipt_time FROM pg_stat_wal_receiver;","mssql":"SELECT * FROM sys.dm_hadr_automatic_seeding;\nSELECT database_id, synchronization_state_desc, is_suspended, suspend_reason_desc\nFROM sys.dm_hadr_database_replica_states WHERE is_local=1;"},"choices":[{"label":"Uno o pocos errores de aplicación (omitibles)","next":"sol_skiperror"},{"label":"Inconsistencia / corrupción irreparable","next":"sol_reseed"}]},"lag":{"kind":"step","title":"¿El retraso es temporal o persistente?","detail":"Observe la tendencia del retraso a lo largo del tiempo. ¿Se cierra (se pone al día) tras un pico de carga temporal, o sigue creciendo de forma que la réplica nunca alcanza al primario?","command":{"mysql":"SHOW REPLICA STATUS\\G\n-- Measure Seconds_Behind_Source several times: is it decreasing or increasing?","postgres":"-- Measure repeatedly on the replica:\nSELECT now()-pg_last_xact_replay_timestamp() AS replay_lag;","oracle":"SELECT name, value, time_computed FROM v$dataguard_stats WHERE name='apply lag';","mssql":"SELECT drs.redo_queue_size, drs.redo_rate, drs.log_send_queue_size\nFROM sys.dm_hadr_database_replica_states drs WHERE drs.is_local=0;"},"choices":[{"label":"Pico de carga temporal; el retraso se está cerrando","next":"sol_lag_wait"},{"label":"Sigue creciendo; no consigue ponerse al día","next":"sol_lag_perm"}]},"sol_skiperror":{"kind":"solution","title":"Solución: omitir con cuidado el error de aplicación","detail":"Si la replicación se detuvo por un único error de aplicación (p. ej. una clave duplicada) y está seguro de que los datos son realmente consistentes, puede omitir ese evento y continuar el flujo. Es un último recurso; omitir a ciegas crea una deriva silenciosa de los datos.","steps":["Entienda primero por qué se produjo el error y verifique la consistencia de los datos comparando la fila afectada en ambos lados.","En MySQL sin GTID, omita un único evento: STOP REPLICA; SET GLOBAL sql_replica_skip_counter=1; START REPLICA; (antes de 8.0.26: sql_slave_skip_counter; con cuidado).","Con GTID, para omitir la transacción que falla, marque ese GTID con una transacción vacía y luego inicie la réplica.","Tras la omisión, ejecute una comprobación de consistencia a nivel de fila (checksum) entre ambos lados; si hay deriva, pase a un reseed."],"related":["replication-status"]},"sol_reseed":{"kind":"solution","title":"Solución: reconstruir la réplica (reseed / rebuild)","detail":"Si la cadena de posición/GTID está rota, los datos se han desviado o hay un error irreparable, el único camino fiable es reconstruir la réplica a partir de una copia nueva tomada del primario.","steps":["Tome un base backup consistente del primario: MySQL xtrabackup/mysqldump o CLONE, Postgres pg_basebackup, Oracle RMAN duplicate, MSSQL backup full+log.","Limpie el estado antiguo en la réplica y restaure el backup (Postgres pg_basebackup -R, MSSQL RESTORE ... WITH NORECOVERY).","Inicie la replicación desde la posición/GTID correctos: MySQL CHANGE REPLICATION SOURCE TO ...; START REPLICA; en MSSQL, vuelva a añadir la base de datos al AG (automatic seeding).","Monitoree la puesta al día; cuando el retraso llegue a cero, verifique la consistencia de ambos lados con un checksum."],"related":["replication-status"]},"sol_lag_wait":{"kind":"solution","title":"Causa raíz: pico de carga temporal (el retraso se está cerrando)","detail":"La replicación está sana; el retraso aumentó por una ráfaga temporal de escrituras en el primario o por fluctuaciones de red, pero la réplica se está poniendo al día.","steps":["Confirme con varias mediciones que la tendencia del retraso está disminuyendo realmente.","Hasta que termine la ventana de carga, dirija al primario solo las lecturas sensibles al retraso, si es necesario.","Si se repite, reparta en el tiempo las ráfagas de escritura (trabajos por lotes, ETL) o muévalas fuera de las horas pico.","Añada la latencia de red/aplicación al monitoreo para detectar el retraso temporal antes de que se vuelva persistente."],"related":["replication-status"]},"sol_lag_perm":{"kind":"solution","title":"Causa raíz: la réplica no puede seguir el ritmo del primario","detail":"El lado de aplicación (apply/redo) no puede seguir el ritmo de cambios que produce el primario; el retraso sigue creciendo. Normalmente se debe a una aplicación de un solo hilo, a un hardware lento en la réplica o a un cuello de botella de red.","steps":["Encuentre el cuello de botella de aplicación: ¿lo limita la aplicación de un solo hilo, el disco/CPU de la réplica o el ancho de banda de red?","Habilite/aumente la aplicación en paralelo: varios replica workers en MySQL (replica_parallel_workers); revise los recursos de aplicación en Postgres/Oracle.","Lleve el hardware de la réplica al menos al nivel del primario (sobre todo las IOPS de disco).","Reduzca las consultas de lectura pesadas innecesarias en la réplica para dejar recursos a la aplicación; si aun así no consigue ponerse al día, reevalúe tras un reseed."],"related":["replication-status"]},"sol_splitbrain":{"kind":"solution","title":"ADVERTENCIA: split-brain (dos primarios): riesgo de pérdida de datos","detail":"Si ambos nodos aceptan escrituras con el rol de primario, se acumulan cambios en conflicto en ambos lados; es la situación más peligrosa y puede provocar pérdida de datos. Lo primero es reducir las escrituras a un solo nodo.","steps":["Consolide de inmediato las escrituras en un solo nodo: dirija el tráfico de la aplicación estrictamente a un único primario y ponga el otro en solo lectura/aíslelo.","Identifique el nodo autoritativo (correcto): decida qué datos se consideran válidos desde el punto de vista del negocio.","Compare los cambios en conflicto (divergentes) en ambos lados; concilie manualmente los cambios críticos del lado perdedor.","Reconstruya el otro nodo con un reseed usando el nodo autoritativo como origen; luego configure un mecanismo de fencing/quórum que impida los primarios dobles."],"related":["replication-status"]}}},"solver/cache-hit-low.json":{"id":"cache-hit-low","title":"Tasa de aciertos de caché baja (buffer/cache hit)","icon":"🎯","category":"Rendimiento","intro":"La tasa de aciertos de caché es baja; los datos se leen constantemente del disco (o del origen). Determine si el problema está en el buffer/caché compartida de la BD o en la caché de Redis/aplicación, y luego mídalo y corríjalo.","start":"root","nodes":{"root":{"kind":"step","title":"¿Qué caché tiene una tasa de aciertos baja?","detail":"Mida la tasa de aciertos y aísle el problema: ¿es baja la del buffer/caché compartida de la base de datos (muchas lecturas físicas), o la caché de Redis/aplicación registra muchos fallos y expulsiones?","command":{"oracle":"SELECT ROUND((1-(pr.value/(dbg.value+cg.value)))*100,2) AS buffer_hit_pct\nFROM v$sysstat pr, v$sysstat dbg, v$sysstat cg\nWHERE pr.name='physical reads' AND dbg.name='db block gets' AND cg.name='consistent gets';","postgres":"SELECT ROUND(sum(blks_hit)*100.0/nullif(sum(blks_hit)+sum(blks_read),0),2) AS cache_hit_pct\nFROM pg_stat_database;","mysql":"SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';\nSHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';","mssql":"SELECT cntr_value AS buffer_cache_hit_ratio\nFROM sys.dm_os_performance_counters\nWHERE counter_name='Buffer cache hit ratio' AND object_name LIKE '%Buffer Manager%';","redis":"INFO stats\n# Check the keyspace_hits / keyspace_misses ratio and evicted_keys in INFO memory"},"choices":[{"label":"La tasa de aciertos del buffer/caché compartida de la BD es baja","next":"dbcache"},{"label":"Muchos fallos / expulsiones en la caché de Redis/aplicación","next":"rediscache"}]},"dbcache":{"kind":"step","title":"¿Por qué lee del disco: tamaño o patrón de escaneo?","detail":"Hay dos causas típicas de una tasa de aciertos de buffer baja: los datos de acceso frecuente (working set) no caben en memoria (caché demasiado pequeña), o grandes full table scans contaminan la caché con bloques de un solo uso y expulsan las páginas útiles.","command":{"oracle":"SELECT component, current_size/1024/1024 AS mb FROM v$sga_dynamic_components WHERE component LIKE '%buffer cache%';\nSELECT sql_id, disk_reads, executions FROM v$sql ORDER BY disk_reads DESC FETCH FIRST 10 ROWS ONLY;","postgres":"SHOW shared_buffers;\nSELECT pg_size_pretty(pg_database_size(current_database())) AS db_size;\nSELECT query, shared_blks_read FROM pg_stat_statements ORDER BY shared_blks_read DESC LIMIT 10;","mysql":"SHOW VARIABLES LIKE 'innodb_buffer_pool_size';\nSELECT ROUND(SUM(data_length+index_length)/1024/1024) AS data_mb FROM information_schema.tables;","mssql":"SELECT physical_memory_kb/1024 AS server_ram_mb FROM sys.dm_os_sys_info;\nSELECT COUNT(*)*8/1024 AS buffer_pool_mb FROM sys.dm_os_buffer_descriptors;"},"choices":[{"label":"El working set no cabe en la RAM (caché demasiado pequeña)","next":"sol_sizing"},{"label":"Los full table scans contaminan la caché","next":"sol_scan"}]},"rediscache":{"kind":"step","title":"¿Por qué los fallos en Redis: expulsión por maxmemory o gestión de TTL/claves?","detail":"Si la tasa de aciertos de Redis es baja, o bien la memoria ha alcanzado maxmemory y se están expulsando claves, o bien la gestión de TTL/el patrón de caché de las claves es deficiente (claves frías, poca reutilización).","command":{"redis":"INFO memory\n# used_memory, maxmemory, maxmemory_policy, evicted_keys\nCONFIG GET maxmemory-policy\nINFO keyspace\n# Key count and expires (keys with TTL) ratio per db"},"choices":[{"label":"maxmemory lleno, alta expulsión","next":"sol_maxmem"},{"label":"Patrón de TTL/claves deficiente (poca reutilización)","next":"sol_ttl"}]},"sol_sizing":{"kind":"solution","title":"Causa raíz: la caché es demasiado pequeña para el working set","detail":"Los datos de acceso frecuente son mayores que el buffer/caché asignado, por lo que se expulsan constantemente y se releen del disco. La tasa de aciertos es baja y las lecturas físicas son altas.","steps":["Amplíe la caché para cubrir el working set: Oracle db_cache_size/SGA, Postgres shared_buffers, MySQL innodb_buffer_pool_size, MSSQL max server memory.","Si la RAM física es insuficiente, añada memoria al servidor; es el principal recurso con el que escala la BD.","Reduzca el working set: archive/particione los datos antiguos, elimine columnas/índices innecesarios.","Tras el cambio, compare la tasa de aciertos y las lecturas físicas para confirmar la mejora."],"related":["cache-hit-ratio"]},"sol_scan":{"kind":"solution","title":"Causa raíz: el patrón de escaneo contamina la caché","detail":"Los grandes full table scans llenan la caché de bloques que se usan una vez y se descartan, expulsando las páginas calientes; esto reduce la tasa de aciertos.","steps":["Obtenga los planes de ejecución de las consultas con más lecturas físicas y convierta los full scans en acceso por índice (añada los índices adecuados).","Seleccione solo las columnas necesarias en lugar de 'SELECT *'; reduzca el volumen de bloques recorridos.","En tablas grandes, limite los escaneos al rango relevante mediante particionamiento.","Separe los trabajos analíticos/de escaneo pesados de la caché OLTP (una réplica de lectura o una ventana horaria separada)."],"related":["slow-queries","cache-hit-ratio"]},"sol_maxmem":{"kind":"solution","title":"Causa raíz: expulsión por maxmemory en Redis","detail":"La memoria de Redis ha alcanzado el límite maxmemory; se expulsan claves antiguas para hacer sitio a las nuevas (evicted_keys no deja de subir). Así se pierden entradas que aún se necesitan y baja la tasa de aciertos.","steps":["Aumente maxmemory para ajustarlo al working set real, o reduzca el conjunto de datos.","Elija la política de expulsión según el patrón de acceso (p. ej. LRU/LFU: allkeys-lfu suele ser eficaz para separar lo caliente de lo frío).","Depure las estructuras grandes/poco usadas; reduzca los valores, elimine campos innecesarios, use tipos de datos adecuados.","Si la necesidad es permanente, escale Redis (más RAM o cluster/sharding) y empiece a monitorear evicted_keys."],"related":[]},"sol_ttl":{"kind":"solution","title":"Causa raíz: patrón de TTL / caché deficiente","detail":"O bien las claves no reciben ningún TTL, de modo que la memoria se llena de datos fríos, o bien el patrón de caché tiene poca reutilización, por lo que la mayoría de las solicitudes acaban en fallos.","steps":["Asigne TTL adecuados a las claves en caché para que los datos que se enfrían expiren por sí solos.","Cachee los datos leídos con frecuencia (calientes); no cachee datos poco usados (evite llenar la caché de entradas que nunca aciertan).","En el patrón cache-aside, implemente correctamente el llenado tras un fallo y use bloqueo/deduplicación de solicitudes frente al 'thundering herd'.","Monitoree la relación keyspace_hits/misses y afine el TTL y el diseño de claves según los datos."],"related":[]}}},"solver/arch-oltp.json":{"id":"arch-oltp","title":"Arquitectura de referencia: aplicación OLTP de 3 capas","icon":"🏛️","category":"Arquitectura de referencia","intro":"Las aplicaciones de negocio clásicas (pedidos, pagos, inventario) ejecutan un gran número de transacciones cortas, de una sola fila y críticas para la consistencia (OLTP). Este árbol muestra paso a paso, empezando por identificar el perfil de carga, cuándo introducir la base de datos relacional primaria, un pooler de conexiones, réplicas de lectura, una capa de caché, backup + PITR y monitoreo. El objetivo no es «añadirlo todo desde el principio», sino añadir el componente adecuado a medida que se demuestra cada cuello de botella.","start":"root","nodes":{"root":{"kind":"step","title":"¿Cuál es su perfil de carga: mezcla de transacciones y requisitos de consistencia?","detail":"La primera decisión en una arquitectura OLTP es si la carga es realmente transaccional (una mezcla corta de escrituras y lecturas que requiere ACID). ¿Los datos son relacionales por naturaleza y la consistencia es crítica (dinero, stock, pedidos), la carga es mayoritariamente de lectura, o la carga todavía es muy baja?","choices":[{"label":"Transacciones cortas, consistencia fuerte (dinero/stock/pedidos)","next":"primary_db"},{"label":"Predominan claramente las lecturas, pocas escrituras, y el tráfico crece","next":"scale_reads"},{"label":"La carga es muy baja; ¿basta con la configuración más sencilla?","next":"sol_single_node"}]},"primary_db":{"kind":"step","title":"¿Ha elegido el motor de base de datos primario, y le preocupa la escala?","detail":"Un motor relacional es la base adecuada para el núcleo transaccional (PostgreSQL, MySQL/InnoDB, Oracle, SQL Server). Una vez clara la elección del motor, la siguiente pregunta es el escalado: ¿basta por ahora con una sola instancia, o están aumentando el número de conexiones y la carga de lectura?","choices":[{"label":"El motor está claro; por ahora basta con una sola instancia","next":"sol_single_node"},{"label":"El número de conexiones se ha disparado, los recursos se agotan","next":"sol_pooler"},{"label":"La carga de lectura crece y sobrecarga la instancia primaria de escritura","next":"scale_reads"}]},"scale_reads":{"kind":"step","title":"¿Cómo escalará las lecturas: réplica o caché?","detail":"Si la carga de lectura sobrecarga la instancia primaria, hay dos opciones complementarias. Las lecturas que toleran datos ligeramente retrasados, como informes/listados, pueden dirigirse a una réplica de lectura; si se solicitan una y otra vez los mismos datos «calientes», una caché con latencia de microsegundos (Redis) alivia la BD.","choices":[{"label":"Muchas lecturas de informes/listados; es aceptable un ligero retraso","next":"sol_read_replica"},{"label":"Los mismos datos populares se leen una y otra vez","next":"sol_cache"}]},"sol_single_node":{"kind":"solution","title":"Recomendación: empiece con un único primario + pool + backup","detail":"Mantenga la arquitectura sencilla al principio: basta con una única instancia relacional primaria, un pool de conexiones delante y backups periódicos. Añada complejidad solo cuando un cuello de botella medido lo exija.","steps":["BD primaria: un motor relacional conforme a ACID como PostgreSQL o MySQL/InnoDB; configure correctamente el esquema, las restricciones y los índices.","Pool de conexiones: implante desde el principio un pool en la aplicación (p. ej. HikariCP) o un PgBouncer en el lado del servidor; evita las tormentas de conexiones.","Backup + PITR: backups completos diarios + archivado de WAL/binlog para la recuperación a un punto en el tiempo; almacene los backups en un almacenamiento separado.","Precaución: no añada réplicas/sharding/caché al principio; intentar resolver un problema no medido solo amplía la superficie de fallo.","Monitoreo: recopile desde el principio métricas de latencia, tasa de errores y número de conexiones; no pase por alto la señal de crecimiento."],"related":["decide-pooling","decide-backup"]},"sol_pooler":{"kind":"solution","title":"Recomendación: añada un pooler de conexiones","detail":"En OLTP, cuando cada instancia de aplicación abre sus propias conexiones, la BD primaria se ahoga por la sobrecarga de memoria y del planificador con miles de conexiones. Interponer un pooler permite compartir un pequeño número de conexiones reales a la BD entre muchas solicitudes de la aplicación.","steps":["PostgreSQL: PgBouncer (modo transaction pooling) reduce muchos clientes a un pequeño número de conexiones de backend.","MySQL: ProxySQL proporciona de forma similar multiplexación y enrutamiento de conexiones.","Tamaño del pool: en general, proporcional al número de núcleos de CPU; bastan unas pocas decenas de conexiones de backend. «Cuanto más, mejor» no se aplica.","Precaución: en transaction pooling, las funciones a nivel de sesión (prepared statements, tablas temporales, variables de sesión) están restringidas; escriba la aplicación en consecuencia.","Siguiente: si el pool también se satura, el problema no son las conexiones sino la duración de las consultas/transacciones; localice las consultas lentas y las transacciones largas."],"related":["decide-pooling","connection-storm"]},"sol_read_replica":{"kind":"solution","title":"Recomendación: añada una réplica de lectura","detail":"Las escrituras permanecen en el primario mientras el tráfico de solo lectura (informes, listados, búsquedas) se dirige a una o más réplicas asíncronas alimentadas desde el primario. Esto libera al primario para las escrituras y las lecturas críticas para la consistencia.","steps":["Topología: primario -> una o más réplicas asíncronas; la aplicación separa lecturas/escrituras y envía las lecturas a la réplica.","Motor: PostgreSQL streaming replication, réplicas de MySQL o réplicas de lectura gestionadas.","Cuándo: para lecturas que toleran algo de retraso, como informes/analítica/listados; cuando quiere descargar la CPU/IO del primario.","Precaución: el retraso de replicación produce lecturas desactualizadas; sirva desde el primario los flujos que necesitan «leer de inmediato lo que uno mismo escribió» (read-your-writes).","Siguiente: si las réplicas tampoco bastan, considere una caché para los datos calientes o sharding en el lado de escritura."],"related":["replication-lag","decide-shard-replica"]},"sol_cache":{"kind":"solution","title":"Recomendación: añada una capa de caché (Redis)","detail":"Si los mismos datos populares (sesiones, catálogo, perfiles, fragmentos de página) se leen repetidamente, coloque una caché en memoria delante de la BD y sirva la mayoría de las lecturas sin llegar nunca a la BD. La latencia baja y la BD primaria se alivia.","steps":["Motor: el patrón cache-aside con Redis (o Memcached); la aplicación consulta primero la caché y, ante un fallo, obtiene los datos de la BD y los escribe en la caché.","TTL + invalidación: asigne a cada clave un TTL razonable; al escribir, elimine/actualice la clave relacionada para que no queden datos desactualizados.","Cuándo: muchas lecturas, los datos cambian relativamente poco y se solicitan repetidamente las mismas claves.","Precaución: cache stampede; cuando expira una clave popular, miles de solicitudes pueden llegar a la BD a la vez. Use bloqueo/single-flight y TTL con jitter.","Precaución: no trate la caché como la «fuente de verdad»; la verdad duradera reside siempre en la BD, y la aplicación debe seguir funcionando recurriendo a la BD aunque la caché falle."],"related":["decide-cache","cache-hit-low"]}}},"solver/arch-dwh.json":{"id":"arch-dwh","title":"Arquitectura de referencia: data warehouse / analítica","icon":"🏛️","category":"Arquitectura de referencia","intro":"Las consultas analíticas (informes, agregaciones, escaneos amplios) son fundamentalmente distintas de las transacciones OLTP: pocas consultas, pero muy amplias y de larga duración. Partiendo de la decisión de separar la analítica del OLTP, este árbol recorre paso a paso componentes como el método de carga (ETL vs ELT), el almacenamiento columnar/columnstore, el modelado en esquema en estrella, el particionamiento + compresión y el precálculo con vistas materializadas, sopesando el compromiso entre concurrencia y throughput.","start":"root","nodes":{"root":{"kind":"step","title":"¿Su carga analítica se ejecuta en la misma base de datos que el OLTP?","detail":"El error más común es ejecutar informes pesados sobre la BD transaccional en vivo y degradar la latencia del OLTP. La primera decisión es la separación: ¿la analítica sigue ejecutándose en la instancia OLTP, está construyendo un entorno analítico separado, o está decidiendo cómo trasladar allí los datos?","choices":[{"label":"Los informes siguen ejecutándose sobre el OLTP en vivo; ralentizan el OLTP","next":"sol_separate"},{"label":"Existe un warehouse separado; ¿cómo debo cargar los datos?","next":"load_method"},{"label":"Existe un warehouse; las consultas siguen siendo lentas, ¿cómo debo modelar?","next":"model"}]},"load_method":{"kind":"step","title":"¿Dónde transformará: antes o después de la carga?","detail":"Al trasladar datos desde los orígenes al warehouse, ¿hará la limpieza/transformación en una capa de procesamiento separada antes de cargar (ETL), o cargará primero los datos en bruto en el warehouse y los transformará con SQL dentro del potente motor del warehouse (ELT)? La decisión depende de la capacidad de procesamiento del warehouse y de la necesidad de flexibilidad del esquema.","choices":[{"label":"La transformación es compleja/externa; hacerla antes de cargar","next":"sol_etl"},{"label":"El warehouse es potente; cargar en bruto y transformar dentro con SQL","next":"sol_elt"}]},"model":{"kind":"step","title":"¿El cuello de botella de las consultas está en la disposición del almacenamiento o en el modelo de datos?","detail":"Si la analítica es lenta, hay dos palancas principales. Los escaneos amplios leen la fila completa en el almacenamiento por filas; el almacenamiento columnar lee solo las columnas necesarias y comprime bien. En el lado del modelo, una normalización excesiva implica muchos joins que ralentizan las consultas; un esquema en estrella (hechos + dimensiones) simplifica las cosas para la analítica.","choices":[{"label":"Los escaneos de agregación amplios son lentos; se leen pocas columnas","next":"sol_columnar"},{"label":"Demasiados joins, esquema normalizado complejo","next":"sol_star"},{"label":"El mismo informe pesado produce repetidamente el mismo resultado","next":"sol_matview"}]},"sol_separate":{"kind":"solution","title":"Recomendación: separe la analítica del OLTP","detail":"Las cargas transaccionales y analíticas se ahogan mutuamente en el mismo motor: los informes largos retienen bloqueos/IO y retrasan las transacciones cortas. Trasladar la analítica a un destino separado (una réplica de lectura o un warehouse analítico separado) alivia ambos lados.","steps":["Primer paso: dirija los informes ligeros a una réplica de lectura del OLTP y la analítica pesada a un motor analítico separado.","Motor analítico: un motor columnar/analítico (p. ej. SQL Server con columnstore, ClickHouse o un warehouse columnar) es mucho más rápido que el almacenamiento por filas en escaneos amplios.","Concurrencia vs throughput: el OLTP se ajusta para muchas transacciones pequeñas (alta concurrencia) y el warehouse para pocas consultas enormes (alto throughput); no puede optimizar ambos en la misma máquina.","Precaución: el warehouse no es una «copia en tiempo real de producción»; son datos cargados a intervalos, con retraso pero organizados para el análisis.","Siguiente: tras separarlos, decida el método de carga (ETL/ELT) y el modelo (esquema en estrella)."],"related":["decide-sqlnosql","slow-query"]},"sol_etl":{"kind":"solution","title":"Recomendación: ETL (transformar antes de cargar)","detail":"En ETL, los datos se extraen de los orígenes (Extract), se limpian y transforman en una capa de procesamiento separada (Transform) y solo después se cargan en el warehouse (Load). Solo entran en el warehouse datos limpios y conformes al modelo.","steps":["Cuándo: la transformación es compleja, necesita combinar varios orígenes, o quiere que los datos en bruto/sensibles nunca entren en el warehouse.","Flujo: origen (OLTP/archivo/API) → capa de transformación → carga por lotes en el warehouse.","Ventaja: mantiene limpio el almacenamiento del warehouse; los datos sensibles pueden enmascararse antes de cargar.","Inconveniente: la transformación depende de la potencia de un sistema separado; no puede aprovechar la flexibilidad SQL del warehouse.","Precaución: cargue en una ventana de lotes (p. ej. por la noche); planifique para evitar choques con consultas pesadas durante la carga."],"related":["decide-capacity"]},"sol_elt":{"kind":"solution","title":"Recomendación: ELT (transformar dentro del warehouse)","detail":"En ELT, los datos en bruto se cargan primero en el warehouse y la transformación se realiza dentro del propio y potente motor SQL del warehouse. Como los warehouses columnares modernos tienen una gran capacidad de procesamiento, se mueven los datos una vez y se procesan dentro, en lugar de llevar la transformación a los datos.","steps":["Cuándo: el motor del warehouse es potente y escalable; además quiere conservar los datos en bruto (auditoría/reprocesamiento).","Flujo: origen → capa en bruto (staging) → vistas SQL/de transformación → capa modelada de hechos/dimensiones.","Ventaja: un solo motor, flexible; como se conservan los datos en bruto, puede cambiar la transformación más tarde y regenerar.","Inconveniente: los datos en bruto (posiblemente sensibles) entran en el warehouse; el control de acceso y el enmascaramiento son obligatorios.","Precaución: modele por capas (raw → staging → mart); escriba las transformaciones de forma idempotente para que las reejecuciones sean seguras."],"related":["decide-capacity"]},"sol_columnar":{"kind":"solution","title":"Recomendación: almacenamiento columnar / columnstore + compresión","detail":"Las consultas analíticas suelen recorrer unas pocas columnas en todas las filas. El almacenamiento columnar guarda los datos columna a columna; lee solo las columnas necesarias, comprime muy bien porque los valores similares están contiguos, y la E/S cae drásticamente.","steps":["Motor/función: SQL Server columnstore index, ClickHouse o warehouses columnares; extensiones columnares en el lado de PostgreSQL.","Compresión: la disposición columnar ofrece una alta compresión; menos disco, menos bytes leídos, escaneos más rápidos.","Particionamiento: particione las tablas de hechos grandes por fecha/rango para que el partition pruning omita por completo las particiones irrelevantes.","Precaución: el almacenamiento columnar es malo para muchas actualizaciones de una sola fila/lecturas puntuales; no traslade el OLTP a él.","Concurrencia vs throughput: el almacenamiento columnar brilla con unas pocas consultas de escaneo enormes, no con miles de consultas concurrentes pequeñas."],"related":["decide-partition","io-bottleneck"]},"sol_star":{"kind":"solution","title":"Recomendación: esquema en estrella (hechos + dimensiones)","detail":"En un modelo analítico, una tabla de hechos con las medidas se sitúa en el centro, rodeada de tablas de dimensiones descriptivas. En comparación con un esquema sobrenormalizado, el número de joins baja, las consultas se simplifican y el motor genera mejores planes.","steps":["Estructura: la tabla de hechos (medidas + claves de dimensión) en el centro; las dimensiones (tiempo, producto, cliente) forman una estrella a su alrededor.","Cuándo: los informes unen muchas tablas y el esquema normalizado se ahoga en joins.","Desnormalización: la desnormalización deliberada es normal en analítica; la consistencia se garantiza en el proceso de carga (ETL/ELT).","Siguiente: precalcule con vistas materializadas las agregaciones pesadas que se ejecutan con frecuencia.","Precaución: no convierta el OLTP en un esquema en estrella; este modelo es para analítica, no para escrituras transaccionales."],"related":["decide-index"]},"sol_matview":{"kind":"solution","title":"Recomendación: precalcule con vistas materializadas","detail":"Si la misma agregación pesada (ingresos diarios, resumen mensual) se ejecuta repetidamente y produce el mismo resultado, almacene físicamente el resultado en una vista materializada y haga que los informes lean el resultado precalculado. El cálculo pesado se ejecuta una vez y las consultas responden al instante.","steps":["Cuándo: determinadas consultas de resumen recorren con frecuencia y de forma repetida la misma tabla grande.","Actualización: actualice la vista materializada según una programación (p. ej. lote nocturno o incremental); fije la frecuencia según las necesidades de frescura.","Motor: PostgreSQL materialized view (REFRESH), Oracle materialized view (incluso query rewrite), SQL Server indexed view.","Precaución: la vista conlleva una ventana de frescura; donde se requieren datos en tiempo real, es obligatoria una consulta en vivo.","Siguiente: si el costo de actualización es alto, considere la actualización incremental o por particiones."],"related":["decide-partition"]}}},"solver/arch-microservice.json":{"id":"arch-microservice","title":"Arquitectura de referencia: capa de datos de microservicios","icon":"🏛️","category":"Arquitectura de referencia","intro":"En microservicios, la decisión más crítica es la propiedad de los datos: cada servicio debe ser propietario de sus datos (database-per-service) y no debe acceder directamente a las tablas de otro servicio. Este árbol recorre paso a paso el antipatrón de BD compartida, cómo lograr consistencia entre servicios sin transacciones distribuidas (saga + outbox), la sincronización de datos con CDC y las consultas entre servicios (API composition vs un modelo de lectura CQRS). El objetivo es mantener los servicios realmente independientes y desplegables por separado.","start":"root","nodes":{"root":{"kind":"step","title":"¿Cómo son propietarios de los datos los servicios: una BD compartida o una por servicio?","detail":"El aislamiento de datos es la columna vertebral de una arquitectura de microservicios. ¿Los servicios usan una única base de datos compartida (una dependencia oculta), cada servicio tiene su propia base de datos (el patrón correcto), o el verdadero reto es la consistencia/las consultas entre servicios? Establezca primero el estado actual.","choices":[{"label":"Todos los servicios usan una única BD compartida","next":"sol_db_per_service"},{"label":"Cada servicio tiene su propia BD, pero las operaciones necesitan consistencia entre servicios","next":"consistency"},{"label":"Cada servicio tiene su propia BD, pero se necesitan consultas/informes combinados","next":"query"}]},"consistency":{"kind":"step","title":"¿Cómo mantendrá consistente un flujo de trabajo entre servicios?","detail":"Para una operación que abarca varios servicios (crear pedido → cobrar el pago → descontar stock), una transacción distribuida clásica (2PC) es frágil y no escala. En su lugar, divida el flujo en pasos de transacción local + evento (saga) y publique los eventos de forma fiable (outbox). ¿Con qué problema está lidiando?","choices":[{"label":"Intento que un flujo de trabajo multiservicio sea atómico (¿2PC?)","next":"sol_saga"},{"label":"No consigo que la escritura local y la publicación del evento sean consistentes (dual write)","next":"sol_outbox"}]},"query":{"kind":"step","title":"¿Cómo servirá una consulta que muestra datos de varios servicios?","detail":"Una vez repartidos los datos entre servicios, no se puede construir una pantalla combinada con un único join. Hay dos enfoques principales: llamar a las API de los servicios correspondientes en el momento de la solicitud y combinar los resultados (API composition), o mantener un modelo de lectura precombinado alimentado por eventos (CQRS). La frecuencia y la complejidad de las consultas determinan la elección.","choices":[{"label":"Join simple e infrecuente; basta con llamar a unos pocos servicios y combinar","next":"sol_api_composition"},{"label":"Consulta combinada compleja/frecuente; se necesita un modelo de lectura ya preparado","next":"sol_cqrs"}]},"sol_db_per_service":{"kind":"solution","title":"Recomendación: database-per-service (divida la BD compartida)","detail":"Que varios servicios compartan una base de datos es una dependencia oculta y peligrosa: el cambio de esquema de un servicio rompe a los demás, la propiedad de las tablas se vuelve difusa y el despliegue independiente resulta imposible. Cada servicio debe ser propietario de sus datos.","steps":["Principio: cada servicio lee/escribe solo en su propia base de datos; no puede acceder directamente a las tablas de otro servicio, solo solicitar datos a través de la API de ese servicio.","Independencia del motor: cada servicio puede elegir un motor según sus necesidades (p. ej. PostgreSQL para pedidos, MongoDB para el catálogo, Elasticsearch para la búsqueda, Redis para el carrito).","Antipatrón: la «base de datos compartida»: tablas comunes, joins cruzados y un único esquema; esto no son microservicios, sino un monolito distribuido.","Descomposición: primero separación lógica (esquema/usuario separados), luego separación física; elimine los joins entre tablas de distintos servicios.","Precaución: tras la división surgen problemas de consistencia (saga/outbox) y de consultas combinadas (composition/CQRS); son el costo natural de esta transición."],"related":["decide-isolation","decide-sqlnosql"]},"sol_saga":{"kind":"solution","title":"Recomendación: patrón saga (evite las transacciones distribuidas)","detail":"No intente hacer atómica con una única transacción distribuida (2PC) una operación que abarca varios servicios; 2PC retiene bloqueos y conlleva fragilidad de punto único y baja disponibilidad. En su lugar, divida el flujo en pasos que sean cada uno una transacción local en su propio servicio, y deshágalos con pasos de compensación en caso de fallo.","steps":["Principio: modele el flujo de trabajo como una cadena de transacciones locales + eventos; cada paso desencadena el siguiente.","Compensación: si un paso falla, ejecute acciones compensatorias que deshagan los pasos anteriores (p. ej. cancelar el pedido si el pago falla).","Coreografía vs orquestación: en flujos sencillos, los servicios se desencadenan entre sí con eventos (coreografía); en flujos complejos, un orquestador central gestiona los pasos.","Precaución: una saga no es atómica; proporciona consistencia eventual. Los estados intermedios pueden ser visibles desde fuera, así que diseñe las reglas de negocio en consecuencia.","Precaución: no use 2PC/XA en microservicios a gran escala; el bloqueo y la duración de los locks paralizarán el sistema."],"related":["decide-isolation","long-transaction"]},"sol_outbox":{"kind":"solution","title":"Recomendación: transactional outbox (la trampa del dual write)","detail":"Cuando un servicio quiere escribir en su propia BD y además publicar un evento, hacer ambas cosas por separado es la trampa del «dual write»: si una tiene éxito y la otra falla, el sistema queda inconsistente. La solución es escribir el evento en una tabla outbox en la MISMA transacción local que los datos de negocio y luego publicarlo con un proceso separado.","steps":["Principio: el registro de negocio + el registro del evento se escriben en la tabla outbox en una sola transacción local; o se producen ambos o ninguno (atómico).","Publicación: un proceso relay/CDC separado lee las filas de outbox, las publica en un message broker (p. ej. Kafka) y las marca.","Con CDC: una herramienta de CDC como Debezium lee el log de la BD (WAL/binlog) y traslada de forma fiable los cambios de outbox a Kafka.","Idempotencia: los consumidores pueden recibir el mismo evento más de una vez; procese de forma idempotente usando el id del evento (asuma una entrega at-least-once).","Precaución: no use directamente el enfoque «escribir primero en la BD y luego publicar en Kafka»; una caída en el momento de la publicación provoca la pérdida de eventos."],"related":["replication-lag"]},"sol_api_composition":{"kind":"solution","title":"Recomendación: API composition (combinar en el momento de la solicitud)","detail":"Cuando se necesitan datos combinados, el llamante (p. ej. un gateway/BFF) llama a las API de los servicios correspondientes y combina los resultados en memoria. Es la solución más sencilla para joins simples que abarcan pocos servicios; no se mantiene ninguna copia adicional de los datos.","steps":["Principio: un compositor (API gateway/BFF) llama a service-A y service-B, combina el resultado y lo devuelve.","Cuándo: el join es sencillo, abarca pocos servicios y la frecuencia de consulta es moderada; no quiere la carga de mantenimiento de un modelo de lectura adicional.","Ventaja: sin almacén de datos ni sincronización adicionales; cada servicio devuelve sus propios datos actuales.","Precaución: para joins entre muchos servicios o grandes conjuntos de datos, la latencia se acumula y los joins en memoria son costosos.","Siguiente: si el join se vuelve complejo o muy frecuente, pase a un modelo de lectura CQRS."],"related":[]},"sol_cqrs":{"kind":"solution","title":"Recomendación: modelo de lectura CQRS (alimentado por eventos)","detail":"Para consultas combinadas complejas y frecuentes, mantenga un modelo de lectura preunido y optimizado para consultas (vista materializada/copia desnormalizada) alimentado por los eventos que emiten los servicios. El lado de escritura permanece en los servicios; el lado de lectura sirve rápidamente datos listos desde un solo lugar.","steps":["Principio: los servicios publican los cambios de estado como eventos; una proyección escucha estos eventos y actualiza el modelo de lectura.","Elección del almacén: elija el almacén del modelo de lectura según el tipo de consulta: Elasticsearch para búsqueda, MongoDB para documentos flexibles, Redis para acceso rápido por clave, PostgreSQL para informes relacionales.","Cuándo: la consulta combinada es compleja/frecuente, se requiere baja latencia y el costo de la API composition ha crecido.","Precaución: el modelo de lectura tiene consistencia eventual; va ligeramente por detrás hasta que se procesan los eventos.","Idempotencia: la proyección puede ver el mismo evento dos veces; actualice de forma idempotente con el id del evento y controle el orden/la versión si es necesario."],"related":["decide-cache"]}}},"solver/arch-eventdriven.json":{"id":"arch-eventdriven","title":"Arquitectura de referencia: orientada a eventos / CQRS","icon":"🏛️","category":"Arquitectura de referencia","intro":"En una arquitectura orientada a eventos, los servicios publican eventos en un log de eventos (p. ej. Kafka) en lugar de llamarse directamente entre sí, y los consumidores los procesan de forma asíncrona. Partiendo de la construcción de la columna vertebral de eventos, este árbol recorre paso a paso el event sourcing (derivar el estado a partir de los eventos), la separación de comandos/consultas con CQRS, la proyección del modelo de lectura, la idempotencia, la tolerancia a la consistencia eventual y la falacia de la «entrega exactly-once». El objetivo es un sistema débilmente acoplado y escalable cuyos compromisos de consistencia se entiendan bien.","start":"root","nodes":{"root":{"kind":"step","title":"¿Qué decisión afronta en su arquitectura orientada a eventos?","detail":"La base de un sistema orientado a eventos es un log de eventos duradero que conecta los servicios mediante eventos asíncronos. ¿Sigue construyendo la columna vertebral, quiere derivar el estado a partir de los eventos (event sourcing), o está separando las rutas de lectura y escritura (CQRS)?","choices":[{"label":"Estoy construyendo la columna vertebral de eventos que conecta los servicios","next":"sol_event_log"},{"label":"Quiero derivar el estado a partir del historial de eventos","next":"sol_event_sourcing"},{"label":"Quiero separar las rutas de lectura y escritura (CQRS)","next":"cqrs_split"}]},"cqrs_split":{"kind":"step","title":"¿Cómo construirá el lado de lectura en CQRS y cuál es su tolerancia de consistencia?","detail":"En CQRS, los lados de comando (escritura) y de consulta (lectura) están separados; los eventos de escritura actualizan la proyección que alimenta el modelo de lectura. La pregunta clave: ¿puede tolerar que el modelo de lectura vaya por detrás de las escrituras (consistencia eventual), o espera que cada evento se procese exactamente una vez?","choices":[{"label":"Alimentaré el modelo de lectura a partir de los eventos mediante una proyección","next":"sol_read_model"},{"label":"Las lecturas deben estar actualizadas; el retraso es inaceptable","next":"sol_consistency"},{"label":"Quiero que los eventos se procesen «exactamente una vez»","next":"sol_idempotency"}]},"sol_event_log":{"kind":"solution","title":"Recomendación: una columna vertebral de log de eventos duradero","detail":"En el centro de una arquitectura orientada a eventos hay un log de eventos duradero y ordenado en el que escriben los productores y que los consumidores leen a su propio ritmo. Esta columna vertebral desacopla los servicios en el tiempo y en la escala.","steps":["Motor: Apache Kafka (o una plataforma similar basada en logs): un log de eventos particionado, persistente y reproducible.","Particiones y orden: el orden solo está garantizado dentro de una partición; preserve el orden enviando los eventos de una misma entidad a la misma clave.","Independencia de los consumidores: cada consumer group mantiene su propio offset; puede releer/rebobinar sin afectar al productor.","Retención: conserve el log el tiempo suficiente para que los nuevos consumidores y el reprocesamiento puedan leer el historial.","Precaución: Kafka no es una cola, sino un log reproducible; asuma una entrega at-least-once y escriba consumidores idempotentes."],"related":[]},"sol_event_sourcing":{"kind":"solution","title":"Recomendación: event sourcing (derivar el estado a partir de los eventos)","detail":"En event sourcing no se almacena el estado actual de una entidad, sino todos los eventos de cambio (inmutables) que se le han aplicado; el estado actual se deriva aplicando estos eventos en orden. Proporciona un historial de auditoría completo y la capacidad de volver a cualquier punto del pasado.","steps":["Principio: el flujo de eventos es la fuente de verdad; el estado se reconstruye reproduciendo los eventos desde el principio (o un snapshot + los eventos posteriores).","Snapshots: para flujos largos, tome snapshots periódicos para no reproducir desde el principio cada vez, y aplique encima los eventos posteriores.","Ventaja: una pista de auditoría completa, una respuesta a «cómo llegamos a este estado» a lo largo del tiempo y la capacidad de derivar distintos modelos de lectura.","Precaución: los esquemas de eventos evolucionan; leer eventos antiguos requiere versionado/upcasting, porque los eventos se conservan para siempre.","Precaución: no es necesario en todos los escenarios; si basta con el estado actual, el CRUD clásico es más sencillo. Suele usarse junto con CQRS."],"related":[]},"sol_read_model":{"kind":"solution","title":"Recomendación: proyección del modelo de lectura","detail":"En CQRS, el lado de escritura produce eventos; una proyección escucha estos eventos y actualiza un modelo de lectura optimizado para consultas (vista desnormalizada). Las lecturas se sirven desde este modelo ya preparado, completamente separado de la ruta de escritura, y ambos lados escalan de forma independiente.","steps":["Principio: eventos → proyección → almacén de lectura; las consultas de lectura solo acceden a este modelo desnormalizado.","Elección del almacén: según el tipo de consulta: Elasticsearch para búsqueda, MongoDB para documentos flexibles, Redis para acceso rápido, PostgreSQL para lo relacional.","Reconstruibilidad: como el modelo de lectura se deriva de los eventos, puede eliminarse y reconstruirse desde cero volviendo a leer el log.","Idempotencia: la proyección puede ver el mismo evento más de una vez; actualice de forma idempotente usando el id/versión del evento.","Precaución: el modelo de lectura tiene consistencia eventual; va por detrás de las escrituras hasta que se procesa el evento, así que diseñe la interfaz en consecuencia."],"related":["decide-cache"]},"sol_consistency":{"kind":"solution","title":"Recomendación: diseñe para tolerar la consistencia eventual","detail":"En los sistemas orientados a eventos/CQRS, el modelo de lectura va ligeramente por detrás de las escrituras (consistencia eventual). En lugar de intentar «eliminarlo», diseñe para tolerarlo; mantenga separados los pocos flujos que requieren consistencia inmediata estricta.","steps":["Principio: la mayoría de los flujos de negocio toleran una consistencia con un breve retraso; ofrezca al usuario una respuesta del tipo «solicitud recibida, en proceso».","Read-your-writes: si los usuarios deben ver de inmediato sus propias escrituras, sirva esa lectura desde el lado de escritura/de forma síncrona.","Núcleo que requiere consistencia estricta: mantenga el trabajo que necesita consistencia inmediata absoluta, como un saldo monetario, dentro de un único servicio/BD con una transacción ACID; no lo distribuya mediante eventos.","Precaución: no oculte la consistencia eventual; gestione explícitamente los estados intermedios/transitorios en las pantallas y las reglas de negocio.","Precaución: el objetivo de «hacer que todo el sistema tenga consistencia fuerte» destruye las ventajas de la arquitectura orientada a eventos (escala, bajo acoplamiento)."],"related":["decide-ha","replication-lag"]},"sol_idempotency":{"kind":"solution","title":"Recomendación: idempotencia (la falacia del exactly-once)","detail":"En la mensajería distribuida, la «entrega exactly-once» de extremo a extremo es en la práctica una falacia; los mensajes duplicados son inevitables por la red y los reintentos. El objetivo correcto es el procesamiento idempotente: procesar dos veces el mismo evento debe dar el mismo resultado que procesarlo una vez.","steps":["Principio: acepte la entrega at-least-once; haga que los duplicados sean inofensivos en el consumidor.","Método: asigne a cada evento un id único; registre los id procesados y omítalos si vuelven a llegar (dedup), o use operaciones idempotentes por naturaleza (upsert, set).","Semántica exactly-once: la función 'exactly-once' de Kafka solo se aplica dentro del límite de transacción propio de Kafka; los efectos secundarios en una BD externa no están garantizados sin idempotencia.","Orden: evite la reordenación enviando los eventos de una misma entidad a la misma clave de partición.","Precaución: la suposición «configuré la entrega exactly-once, no necesito dedup» produce inconsistencias en producción; procese siempre de forma idempotente."],"related":[]}}},"solver/arch-multiregion.json":{"id":"arch-multiregion","title":"Arquitectura de referencia: multirregión / global","icon":"🏛️","category":"Arquitectura de referencia","intro":"Cuando los usuarios están distribuidos geográficamente o se requiere resiliencia ante un desastre regional, los datos se reparten entre varias regiones. Este árbol recorre paso a paso la localidad de lectura (una réplica de lectura en cada región), la topología de escritura (single-leader vs multi-leader vs quórum), la residencia de datos/cumplimiento normativo (GDPR), la resolución de conflictos y los compromisos entre RPO/RTO y latencia. La verdad básica: por las leyes de la física, la baja latencia, la consistencia fuerte y las escrituras multirregión no pueden lograrse plenamente a la vez; es obligatorio un compromiso deliberado.","start":"root","nodes":{"root":{"kind":"step","title":"¿Por qué opta por multirregión: localidad de lectura, escrituras/resiliencia o cumplimiento normativo?","detail":"Una arquitectura multirregión es costosa y compleja; aclare la motivación. ¿Quiere baja latencia de lectura para usuarios lejanos, escrituras entre regiones/resiliencia ante desastres, o el verdadero requisito es que los datos permanezcan en un país concreto (residencia legal)?","choices":[{"label":"Lecturas rápidas para usuarios lejanos; las escrituras pueden quedarse en una región","next":"sol_read_locality"},{"label":"Se requieren escrituras entre regiones / resiliencia ante la pérdida de una región","next":"write_topology"},{"label":"Los datos deben permanecer legalmente en un país/región concretos","next":"sol_data_residency"}]},"write_topology":{"kind":"step","title":"¿Cuál debe ser la topología de escritura: single-leader, multi-leader o quórum?","detail":"Para las escrituras entre regiones, la decisión clave es el modelo de liderazgo. ¿Todas las escrituras van a una sola región (sencillo, consistente, pero las escrituras remotas son lentas), puede escribir cada región (baja latencia de escritura pero con conflictos), o se acepta una escritura con la confirmación de la mayoría de las regiones (quórum: consistente pero con latencia añadida)?","choices":[{"label":"Una región escribe, las demás leen (sencillo y consistente)","next":"sol_single_leader"},{"label":"Cada región puede escribir localmente (baja latencia de escritura)","next":"sol_multi_leader"},{"label":"Escribir con confirmación de la mayoría; lo primero es la consistencia","next":"sol_quorum"}]},"sol_read_locality":{"kind":"solution","title":"Recomendación: una réplica de lectura por región (localidad de lectura)","detail":"Las escrituras permanecen en una única región primaria mientras coloca en cada región de usuarios una réplica de lectura alimentada desde ella. Los usuarios leen de la réplica más cercana y la latencia de lectura baja; como las escrituras siguen yendo a un único centro, el modelo de consistencia sigue siendo sencillo.","steps":["Topología: una región primaria (escrituras) → una réplica de lectura asíncrona en cada región de usuarios.","Motores: PostgreSQL streaming replication, réplicas de MySQL entre regiones o réplicas de lectura globales gestionadas.","Enrutamiento: la aplicación dirige las lecturas a la réplica más cercana y las escrituras a la región primaria (geo DNS / enrutador inteligente).","Precaución: el retraso de replicación entre regiones provoca lecturas desactualizadas; haga que los flujos que necesitan read-your-writes lean del primario.","RPO/RTO: si cae la región primaria, puede promoverse una réplica asíncrona con cierta pérdida de datos (RPO > 0); prepare el plan de failover con antelación."],"related":["replication-lag","decide-shard-replica"]},"sol_single_leader":{"kind":"solution","title":"Recomendación: escrituras single-leader","detail":"Todas las escrituras van a una única región líder; las demás regiones son réplicas de lectura alimentadas de forma asíncrona desde ese líder. Es el modelo más sencillo y con consistencia más fuerte; no hay conflictos porque existe un único punto de escritura. El precio es una alta latencia de escritura desde regiones remotas.","steps":["Principio: una única autoridad de escritura → sin conflictos, modelo de consistencia sencillo.","Cuándo: el volumen de escritura puede gestionarse en una región y la latencia de escritura remota es tolerable.","Failover: si cae la región líder, se promueve una réplica a líder; son obligatorios un failover automático/controlado y la protección frente al split-brain.","RPO/RTO: con replicación asíncrona puede perderse una pequeña cantidad de datos en el failover (RPO); la replicación síncrona da RPO=0, pero aumenta la latencia de escritura.","Precaución: las escrituras de los usuarios remotos son lentas; si la localidad de escritura es crítica, considere multi-leader o quórum."],"related":["decide-ha","failover-drill"]},"sol_multi_leader":{"kind":"solution","title":"Recomendación: escrituras multi-leader + resolución de conflictos","detail":"Como cada región puede escribir localmente, la latencia de escritura es baja y, aunque una región quede aislada, las demás siguen escribiendo. El precio es alto: dos regiones pueden actualizar el mismo registro de forma distinta, y debe diseñar la resolución de conflictos.","steps":["Principio: cada región es líder; las escrituras se propagan de forma asíncrona a las demás regiones y se fusionan.","Resolución de conflictos: last-write-wins (sencillo pero con riesgo de pérdida de datos), fusión específica de la aplicación o tipos de datos libres de conflictos como los CRDT.","Ejemplos de motores: configuraciones multi-master/multi-leader (p. ej. EDB Postgres Distributed (BDR)/pgEdge para PostgreSQL, Redis Enterprise Active-Active (CRDT), CouchDB). Cassandra no es multi-leader sino leaderless; con LOCAL_ONE/LOCAL_QUORUM permite escrituras locales en cada región con un resultado similar y resuelve los conflictos con last-write-wins (marcas de tiempo).","Cuándo: la latencia de escritura local y la independencia regional importan más que la consistencia fuerte.","Precaución: diseñe la resolución de conflictos desde el principio; la suposición de que «no habrá conflictos» produce pérdidas de datos silenciosas. Vigile las colisiones entre regiones en la generación de ID únicos."],"related":["decide-ha"]},"sol_quorum":{"kind":"solution","title":"Recomendación: escrituras basadas en quórum (confirmación de la mayoría)","detail":"Una escritura se acepta cuando la confirma una mayoría (quórum) de regiones/réplicas; cuando las lecturas también van a una mayoría, se garantiza el valor más reciente. Es resiliente ante la pérdida de una región y tiene consistencia fuerte; el precio es la latencia de que cada escritura llegue a una mayoría.","steps":["Principio: confirmación de la mayoría con la regla W + R > N; aunque caiga una región minoritaria, el sistema sigue escribiendo de forma consistente.","Ejemplos de motores: BD distribuidas basadas en consenso (p. ej. sistemas que usan Raft/Paxos), el nivel de consistencia QUORUM en Cassandra.","Cuándo: se requieren tanto resiliencia ante desastres regionales como consistencia fuerte, y es aceptable una mayor latencia de escritura.","RPO/RTO: como una mayoría escribe de forma duradera, RPO≈0; con la reconstitución automática de la mayoría tras la pérdida de una región, el RTO puede ser bajo.","Precaución: el quórum entre regiones geográficamente distantes añade decenas de ms a cada escritura por la latencia de la velocidad de la luz; elija con cuidado el número y la ubicación de las regiones."],"related":["decide-ha","decide-shard-replica"]},"sol_data_residency":{"kind":"solution","title":"Recomendación: residencia de datos / cumplimiento normativo (GDPR)","detail":"Algunos datos (personales, sanitarios, financieros) deben permanecer legalmente dentro de las fronteras de un país/región concretos (p. ej. GDPR). En este caso, la arquitectura no se diseña en torno a «copiar en todas partes», sino para que los datos queden anclados geográficamente.","steps":["Principio: mantenga los datos personales/regulados en la región donde se originan; restrinja o prohíba la replicación fuera de la región.","Particionamiento regional: dirija a los usuarios a la región en la que están registrados (geo-partitioning); cada región guarda los datos de sus propios residentes.","Datos globales vs locales: los datos globales, como un catálogo, pueden replicarse en todas partes; los datos personales permanecen en la región.","Precaución: los backups y las réplicas de lectura también mueven datos; la región de almacenamiento de backups y la región de DR también entran en el ámbito del cumplimiento.","Precaución: una decisión como «copiémoslo en todas partes solo por la baja latencia» puede ser una infracción legal con datos regulados; incorpore las restricciones de residencia a la arquitectura desde el principio."],"related":["security-incident","audit-setup"]}}},"solver/arch-highscale.json":{"id":"arch-highscale","title":"Arquitectura de referencia: gran escala / alto tráfico","icon":"🏛️","category":"Arquitectura de referencia","intro":"A medida que crece el tráfico, hay un orden correcto para proteger la base de datos: primero las palancas sencillas y baratas, luego las complejas. Este árbol recorre paso a paso el orden escalado vertical → réplica de lectura → sharding, los patrones de caché (cache-aside, write-back), el buffering con una cola + backpressure, el rate limiting, el aislamiento de la base de datos con bulkheads/circuit breakers y la prevención de puntos calientes. Principio básico: deje para el final las decisiones difíciles de revertir, como el sharding, y agote primero las mejoras baratas.","start":"root","nodes":{"root":{"kind":"step","title":"¿Dónde está el cuello de botella: una sola instancia saturada, lecturas o escrituras intensas, o tráfico a ráfagas?","detail":"Para resolver un problema de escalado en el lugar adecuado, identifique primero el cuello de botella. ¿Una sola instancia está saturada de CPU/memoria, predominan las lecturas, las escrituras llegan al límite, o picos repentinos de tráfico aplastan la base de datos? Cada caso apunta a un componente distinto.","choices":[{"label":"Una sola instancia está saturada; aún no he probado las palancas sencillas","next":"scale_path"},{"label":"Las lecturas son muy intensas y van en aumento","next":"sol_cache"},{"label":"Picos repentinos de tráfico / carga a ráfagas aplastan la BD","next":"protect"}]},"scale_path":{"kind":"step","title":"¿En qué paso de escalado está: vertical, réplicas de lectura o sharding?","detail":"El escalado tiene un orden correcto. El escalado vertical (hardware más potente) es el primer paso más sencillo; luego reparte las lecturas entre réplicas; solo cuando un único primario ya no puede con las escrituras pasa al particionamiento horizontal (sharding). ¿Dónde está usted?","choices":[{"label":"Aún no he ampliado el hardware / agotado el ajuste básico","next":"sol_vertical"},{"label":"Las escrituras caben en una instancia, pero necesito repartir la carga de lectura","next":"sol_read_replica"},{"label":"Un único primario ya no puede con la carga de escritura","next":"sol_sharding"}]},"protect":{"kind":"step","title":"¿Cómo protegerá la base de datos de la carga: buffering, limitación o aislamiento?","detail":"Escalar no siempre consiste en hacer más grande la BD; a menudo consiste en protegerla de la sobrecarga. ¿Suavizará los picos repentinos de escritura con una cola, limitará la tasa de solicitudes entrantes (rate limiting), o impedirá que una sola dependencia haga caer todo el sistema (bulkhead/circuit breaker)?","choices":[{"label":"Quiero suavizar los picos repentinos de escritura","next":"sol_queue"},{"label":"Quiero limitar/repartir de forma equitativa la tasa de solicitudes entrantes","next":"sol_ratelimit"},{"label":"No quiero que una dependencia que falla haga caer todo el sistema","next":"sol_bulkhead"}]},"sol_vertical":{"kind":"solution","title":"Recomendación: primero escalado vertical y ajuste básico","detail":"El paso más barato y menos arriesgado es el escalado vertical: añadir más CPU/RAM/discos más rápidos y corregir lo básico (índices, connection pooling, parámetros de memoria). Agote estas mejoras antes de decisiones complejas como el sharding.","steps":["Primer paso: amplíe el hardware (CPU/RAM/NVMe) y ajuste los parámetros de memoria de buffer/caché a la carga.","Mejoras baratas: añada los índices que faltan, corrija las consultas pesadas, coloque delante un pooler de conexiones (PgBouncer/ProxySQL); la mayoría de los problemas de «escala» son en realidad problemas de ajuste.","Cuándo basta: si la instancia única ampliada cumple la latencia/el throughput objetivo, deténgase; no añada más complejidad.","Precaución: el escalado vertical tiene un techo físico y de costo; a medida que se acerque a él, pase a réplicas de lectura y caché.","Siguiente: si predominan las lecturas, réplicas + caché; si predominan las escrituras y se supera el techo, sharding."],"related":["decide-capacity","high-cpu"]},"sol_read_replica":{"kind":"solution","title":"Recomendación: reparta las lecturas con réplicas de lectura","detail":"Las escrituras permanecen en un único primario mientras el tráfico de solo lectura se reparte entre una o más réplicas. Es mucho más sencillo que el sharding y resuelve la mayoría de las cargas con lectura intensiva; el primario queda solo para las escrituras y las lecturas críticas para la consistencia.","steps":["Topología: primario (escrituras) → varias réplicas de lectura asíncronas; las lecturas se distribuyen a las réplicas mediante un balanceador de carga.","Motores: PostgreSQL streaming replication, réplicas de MySQL, réplicas de lectura gestionadas.","Cuándo: la relación lectura/escritura está muy sesgada hacia las lecturas y las escrituras aún pueden gestionarse en una sola instancia.","Precaución: el retraso de replicación provoca lecturas desactualizadas; sirva desde el primario las lecturas que necesitan read-your-writes.","Siguiente: si ni siquiera las réplicas bastan, o el verdadero cuello de botella son las escrituras, añada una caché y luego considere el sharding."],"related":["replication-lag","decide-shard-replica"]},"sol_sharding":{"kind":"solution","title":"Recomendación: sharding (particionamiento horizontal) como último recurso","detail":"Cuando un único primario no puede con la carga de escritura, se dividen los datos por una clave en varias piezas independientes (shards); cada shard asume parte de las escrituras. Es potente pero difícil de revertir; complica las consultas y transacciones entre shards, por lo que se deja para el final.","steps":["Shard key: elija una clave que reparta uniformemente la carga y los datos y permita que la mayoría de las consultas caigan en un solo shard (p. ej. customer_id).","Método: particionamiento basado en hash (distribución uniforme) o en rangos (consultas por rango sencillas, pero con riesgo de puntos calientes).","Ejemplos de motores: sharding de MongoDB, Citus (PostgreSQL), Vitess (MySQL) o sharding a nivel de aplicación.","Precaución: los joins/transacciones entre shards son costosos y complejos; diseñe el esquema para que la mayoría de las transacciones se completen dentro de un solo shard.","Precaución: una clave equivocada crea puntos calientes (la carga se acumula en un shard); evite claves monótonas como id/fechas crecientes, y recuerde que el resharding es difícil."],"related":["decide-shard-replica","decide-partition"]},"sol_cache":{"kind":"solution","title":"Recomendación: patrones de caché (cache-aside + write-back)","detail":"Con alto tráfico y lectura intensiva, se coloca una caché en memoria (Redis) delante de la BD y se sirven la mayoría de las lecturas sin que lleguen nunca a la BD. Para escrituras de gran volumen, write-back acumula las escrituras y las vuelca a la BD por lotes, aliviando el primario.","steps":["Cache-aside: la aplicación consulta primero Redis; ante un fallo, obtiene los datos de la BD y los escribe en la caché. Esto quita a la BD la mayor parte de la carga de lectura.","Write-back (write-behind): para volúmenes de escritura muy altos, como contadores/métricas, escriba primero en la caché y vuelque a la BD por lotes en segundo plano.","TTL + invalidación: asigne a las claves un TTL razonable; al escribir, actualice/elimine la clave relacionada para que no queden datos desactualizados.","Precaución: cache stampede: cuando expira una clave popular, miles de solicitudes llegan a la BD a la vez; use bloqueo/single-flight, actualización anticipada y TTL con jitter.","Precaución: con write-back, si el nodo de caché cae antes de escribir en la BD, se pierden datos; proteja los datos críticos con una cola duradera/AOF."],"related":["decide-cache","cache-hit-low"]},"sol_queue":{"kind":"solution","title":"Recomendación: buffering con una cola (backpressure)","detail":"Para evitar que los picos repentinos de escritura lleguen directamente a la BD, se interpone una cola duradera (p. ej. Kafka/una cola de mensajes) y se consumen las escrituras al ritmo que la BD puede sostener. La cola absorbe el pico; en lugar de fallar de forma síncrona, el sistema funciona con una latencia controlada (backpressure).","steps":["Principio: el productor escribe el evento en la cola; el consumidor escribe en la BD a su ritmo sostenible (posiblemente por lotes).","Motor: Kafka o una cola de mensajes; actúa como un buffer que aísla la BD del tráfico pico.","Backpressure: si la cola no deja de crecer, ralentice/avise al productor; no permita una acumulación ilimitada, o la latencia se disparará.","Cuándo: el tráfico de escritura es muy irregular y no es obligatorio el procesamiento síncrono inmediato (el procesamiento asíncrono es aceptable).","Precaución: una cola introduce asincronía; el resultado tiene consistencia eventual. Haga idempotente al consumidor (los mensajes pueden volver a entregarse)."],"related":["connection-storm"]},"sol_ratelimit":{"kind":"solution","title":"Recomendación: rate limiting (limitar la tasa de solicitudes)","detail":"Limitando las solicitudes entrantes por origen (rate limiting), evita tanto el abuso como que un solo cliente consuma todos los recursos. Acotar la carga que llega a la BD mantiene el sistema predecible y equitativo.","steps":["Ubicación: aplique la limitación antes de la BD (API gateway / borde de la aplicación); solo deben llegar a la BD las solicitudes aceptadas.","Método: tasa por cliente o por usuario con token bucket / sliding window; devuelva 429 cuando se supere.","Reparto equitativo: use cuotas para impedir que un «vecino ruidoso» acapare toda la capacidad.","Cuándo: el tráfico puede dispararse de repente, o existe riesgo de abuso/clientes automatizados.","Precaución: el rate limiting es una medida de protección, no de escalado; si hay una necesidad real de capacidad, resuelva también el cuello de botella real (vertical/réplica/shard)."],"related":["connection-storm"]},"sol_bulkhead":{"kind":"solution","title":"Recomendación: bulkhead + circuit breaker (aislamiento)","detail":"Para evitar que una dependencia (p. ej. una BD que se ralentiza o un servicio externo) haga caer todo el sistema, se dividen los recursos en compartimentos (bulkheads) y se cortan temporalmente las llamadas a una dependencia que falla continuamente (circuit breaker). Así, un fallo parcial no se propaga a todo el sistema.","steps":["Bulkhead: asigne pools de conexiones/hilos separados para los distintos tipos de trabajo; una ruta lenta no debe agotar todo el pool y dejar sin recursos a las demás.","Circuit breaker: cuando una dependencia supera un umbral de errores/latencia, «abra» el circuito y falle rápido durante un tiempo; luego compruebe la recuperación con llamadas de prueba (half-open).","Timeouts: ponga un timeout razonable en cada llamada a la BD/servicio; las llamadas colgadas no deben bloquear el pool.","Cuándo: en un sistema con muchas dependencias, en el que la ralentización de un componente conlleva riesgo de fallos en cascada.","Precaución: diseñe una degradación controlada para cuando el circuito esté abierto: devuelva datos desactualizados de la caché o un resultado parcial en lugar de rechazar toda la solicitud."],"related":["connection-storm","high-cpu"]}}},"types/types-functions.json":{"schemaVersion":1,"sections":[{"id":"datatypes","title":"Tipos de datos","icon":"🔤","rows":[{"id":"var-text","concept":"Texto de longitud variable","engines":{"oracle":"VARCHAR2(n)","postgres":"VARCHAR(n) / TEXT","mysql":"VARCHAR(n)","mssql":"NVARCHAR(n)","mongodb":"string","redis":"string (value)","elasticsearch":"text / keyword"},"note":"En ES, text se usa para la búsqueda de texto completo y keyword para coincidencia exacta/agregaciones."},{"id":"fixed-text","concept":"Texto de longitud fija","engines":{"oracle":"CHAR(n)","postgres":"CHAR(n)","mysql":"CHAR(n)","mssql":"NCHAR(n)","mongodb":"string","redis":"string","elasticsearch":"keyword"},"note":"Los sistemas documentales/clave-valor no tienen el concepto de longitud fija; los valores se almacenan como cadenas."},{"id":"large-text","concept":"Texto grande (CLOB/TEXT)","engines":{"oracle":"CLOB","postgres":"TEXT","mysql":"LONGTEXT / TEXT","mssql":"NVARCHAR(MAX)","mongodb":"string","redis":"string (up to 512 MB)","elasticsearch":"text"}},{"id":"int32","concept":"Entero (32 bits)","engines":{"oracle":"NUMBER(10)","postgres":"INTEGER / INT4","mysql":"INT","mssql":"INT","mongodb":"int (NumberInt / int32)","redis":"string (interpreted as an integer)","elasticsearch":"integer"},"note":"Oracle no tiene un tipo INT separado; se usa NUMBER."},{"id":"int64","concept":"Entero grande (64 bits)","engines":{"oracle":"NUMBER(19)","postgres":"BIGINT / INT8","mysql":"BIGINT","mssql":"BIGINT","mongodb":"long (NumberLong / int64)","redis":"string (INCR 64-bit integer)","elasticsearch":"long"}},{"id":"decimal","concept":"Número decimal/exacto (NUMERIC)","engines":{"oracle":"NUMBER(p,s)","postgres":"NUMERIC(p,s) / DECIMAL","mysql":"DECIMAL(p,s)","mssql":"DECIMAL(p,s) / NUMERIC","mongodb":"decimal (Decimal128)","redis":"none, stored as a string","elasticsearch":"scaled_float"},"note":"Para valores exactos como el dinero, se prefieren estos tipos a float."},{"id":"float","concept":"Coma flotante (FLOAT/DOUBLE)","engines":{"oracle":"BINARY_DOUBLE / FLOAT","postgres":"REAL / DOUBLE PRECISION","mysql":"FLOAT / DOUBLE","mssql":"FLOAT / REAL","mongodb":"double","redis":"none, string (INCRBYFLOAT)","elasticsearch":"float / double"}},{"id":"boolean","concept":"Lógico (BOOLEAN)","engines":{"oracle":"NUMBER(1) / CHAR(1) (BOOLEAN in 23c)","postgres":"BOOLEAN","mysql":"TINYINT(1) / BOOL","mssql":"BIT","mongodb":"bool","redis":"none, 0/1 string or bit","elasticsearch":"boolean"},"note":"El tipo de columna BOOLEAN llegó a Oracle SQL solo con 23c; antes se simula con NUMBER/CHAR."},{"id":"date","concept":"Fecha","engines":{"oracle":"DATE (includes time)","postgres":"DATE","mysql":"DATE","mssql":"DATE","mongodb":"Date (ISODate)","redis":"none (epoch string/number)","elasticsearch":"date (with format)"},"note":"El tipo DATE de Oracle contiene fecha y hora; no es solo fecha."},{"id":"datetime","concept":"Fecha-hora (timestamp/datetime)","engines":{"oracle":"TIMESTAMP","postgres":"TIMESTAMP","mysql":"DATETIME / TIMESTAMP","mssql":"DATETIME2","mongodb":"Date (ISODate)","redis":"none (epoch string/number)","elasticsearch":"date"}},{"id":"datetime-tz","concept":"Fecha-hora con zona horaria","engines":{"oracle":"TIMESTAMP WITH TIME ZONE","postgres":"TIMESTAMPTZ","mysql":"TIMESTAMP (stored as UTC)","mssql":"DATETIMEOFFSET","mongodb":"Date (stored as UTC)","redis":"none (epoch UTC)","elasticsearch":"date (UTC, time zone via format)"},"note":"Mongo y ES almacenan las fechas en UTC; la zona horaria se aplica al mostrarlas."},{"id":"binary","concept":"Datos binarios (BLOB/binary)","engines":{"oracle":"BLOB / RAW","postgres":"BYTEA","mysql":"BLOB / VARBINARY","mssql":"VARBINARY(MAX)","mongodb":"BinData (binary)","redis":"string (binary-safe)","elasticsearch":"binary (base64, not searchable)"},"note":"Las cadenas de Redis son binary-safe y pueden contener cualquier secuencia de bytes."},{"id":"uuid","concept":"UUID/GUID","engines":{"oracle":"RAW(16) / SYS_GUID()","postgres":"UUID","mysql":"BINARY(16) / CHAR(36)","mssql":"UNIQUEIDENTIFIER","mongodb":"UUID (BinData subtype 4) / ObjectId","redis":"none, stored as a string","elasticsearch":"keyword"},"note":"El _id nativo de Mongo es ObjectId; un UUID también puede almacenarse como BinData."},{"id":"json","concept":"JSON","engines":{"oracle":"JSON (21c+) / CLOB","postgres":"JSON / JSONB","mysql":"JSON","mssql":"JSON (2025) / NVARCHAR","mongodb":"object (native BSON)","redis":"not native / RedisJSON module","elasticsearch":"object / nested / flattened"},"note":"En Postgres se prefiere JSONB para consultas/indexación; MongoDB es documental de por sí."},{"id":"auto-increment","concept":"Autoincremento (identity/serial)","engines":{"oracle":"GENERATED AS IDENTITY / SEQUENCE","postgres":"SERIAL / GENERATED ... IDENTITY","mysql":"AUTO_INCREMENT","mssql":"IDENTITY(1,1)","mongodb":"none (ObjectId or a counter pattern)","redis":"counter with INCR","elasticsearch":"none (automatic _id)"}},{"id":"enum","concept":"Enum/lista fija","engines":{"oracle":"via a CHECK constraint","postgres":"ENUM (CREATE TYPE)","mysql":"ENUM(...)","mssql":"via a CHECK constraint","mongodb":"none (validation with JSON Schema enum)","redis":"none, stored as a string","elasticsearch":"keyword"}},{"id":"array","concept":"Array","engines":{"oracle":"VARRAY / nested table / collection","postgres":"ARRAY (e.g. INTEGER[])","mysql":"none (via JSON array)","mssql":"none (JSON / table variable)","mongodb":"array (native)","redis":"List / Set (native)","elasticsearch":"any field can be an array (native)"},"note":"En ES, cualquier campo puede contener implícitamente un array; no hay un tipo array separado."},{"id":"ip-address","concept":"Dirección IP","engines":{"oracle":"VARCHAR2 / UTL_INADDR","postgres":"INET / CIDR","mysql":"VARBINARY(16) / INET6_ATON","mssql":"none, via VARCHAR","mongodb":"none, stored as a string","redis":"none, stored as a string","elasticsearch":"ip"},"note":"Los tipos IP nativos de Postgres y ES admiten consultas por rango/subred."},{"id":"money","concept":"Dinero","engines":{"oracle":"NUMBER(p,2)","postgres":"MONEY / NUMERIC(p,2)","mysql":"DECIMAL(p,2)","mssql":"MONEY / DECIMAL(p,2)","mongodb":"decimal (Decimal128)","redis":"none, an integer in minor units (cents) is recommended","elasticsearch":"scaled_float"},"note":"Para evitar errores de redondeo, el dinero se almacena con DECIMAL/Decimal128 en lugar de float."},{"id":"spatial","concept":"Geográfico/espacial","engines":{"oracle":"SDO_GEOMETRY (Oracle Spatial)","postgres":"GEOMETRY / GEOGRAPHY (PostGIS)","mysql":"GEOMETRY / POINT","mssql":"GEOGRAPHY / GEOMETRY","mongodb":"GeoJSON object (2dsphere index)","redis":"Geo (GEOADD, sorted set based)","elasticsearch":"geo_point / geo_shape"}}]},{"id":"functions","title":"Funciones comunes","icon":"🧩","rows":[{"id":"coalesce","concept":"Devolver un valor por defecto si es NULL (COALESCE/NVL)","engines":{"oracle":"NVL(x, y) / COALESCE","postgres":"COALESCE(x, y)","mysql":"COALESCE(x, y) / IFNULL","mssql":"COALESCE(x, y) / ISNULL","mongodb":"$ifNull: [x, y]","redis":"none (application side)","elasticsearch":"?.field / painless ternary"}},{"id":"nullif-replace","concept":"Sustituir si es NULL (ISNULL/IFNULL)","engines":{"oracle":"NVL(x, y)","postgres":"COALESCE(x, y)","mysql":"IFNULL(x, y)","mssql":"ISNULL(x, y)","mongodb":"$ifNull: [x, y]","redis":"none","elasticsearch":"painless: doc[f].size()==0 ? y : x"}},{"id":"now","concept":"Fecha-hora actual (NOW/SYSDATE)","engines":{"oracle":"SYSDATE / SYSTIMESTAMP","postgres":"NOW() / CURRENT_TIMESTAMP","mysql":"NOW()","mssql":"GETDATE() / SYSDATETIME()","mongodb":"$$NOW / new Date()","redis":"TIME command","elasticsearch":"now (date math)"}},{"id":"current-date","concept":"Fecha actual","engines":{"oracle":"TRUNC(SYSDATE) / CURRENT_DATE","postgres":"CURRENT_DATE","mysql":"CURDATE()","mssql":"CAST(GETDATE() AS DATE)","mongodb":"$dateTrunc / new Date()","redis":"TIME (epoch)","elasticsearch":"now/d"}},{"id":"concat","concept":"Concatenar texto (concat)","engines":{"oracle":"a || b / CONCAT","postgres":"a || b / CONCAT","mysql":"CONCAT(a, b)","mssql":"CONCAT(a, b) / a + b","mongodb":"$concat: [a, b]","redis":"APPEND","elasticsearch":"painless: a + b"},"note":"En MySQL, || significa OR por defecto; use CONCAT para concatenar."},{"id":"substring","concept":"Subcadena","engines":{"oracle":"SUBSTR(s, p, n)","postgres":"SUBSTRING(s, p, n)","mysql":"SUBSTRING(s, p, n)","mssql":"SUBSTRING(s, p, n)","mongodb":"$substrCP: [s, p, n]","redis":"GETRANGE key start end","elasticsearch":"painless: s.substring(p, e)"}},{"id":"length","concept":"Longitud de cadena","engines":{"oracle":"LENGTH(s)","postgres":"LENGTH(s) / CHAR_LENGTH","mysql":"CHAR_LENGTH(s)","mssql":"LEN(s)","mongodb":"$strLenCP: s","redis":"STRLEN key","elasticsearch":"painless: s.length()"}},{"id":"upper-lower","concept":"Mayúsculas/minúsculas","engines":{"oracle":"UPPER(s) / LOWER(s)","postgres":"UPPER(s) / LOWER(s)","mysql":"UPPER(s) / LOWER(s)","mssql":"UPPER(s) / LOWER(s)","mongodb":"$toUpper / $toLower","redis":"none (application side)","elasticsearch":"painless: s.toUpperCase() / lowercase analyzer"}},{"id":"trim","concept":"Eliminar espacios (TRIM)","engines":{"oracle":"TRIM(s) / LTRIM / RTRIM","postgres":"TRIM(s)","mysql":"TRIM(s)","mssql":"TRIM(s)","mongodb":"$trim: { input: s }","redis":"none","elasticsearch":"painless: s.trim() / trim token filter"}},{"id":"replace","concept":"Reemplazar texto (REPLACE)","engines":{"oracle":"REPLACE(s, a, b)","postgres":"REPLACE(s, a, b)","mysql":"REPLACE(s, a, b)","mssql":"REPLACE(s, a, b)","mongodb":"$replaceAll / $replaceOne","redis":"SETRANGE (positional) / none","elasticsearch":"painless: s.replace(a, b)"}},{"id":"cast","concept":"Conversión de tipo (CAST/CONVERT)","engines":{"oracle":"CAST(x AS t) / TO_NUMBER","postgres":"CAST(x AS t) / x::t","mysql":"CAST(x AS t) / CONVERT","mssql":"CAST(x AS t) / CONVERT","mongodb":"$convert / $toInt, $toString","redis":"none (everything is a string)","elasticsearch":"painless: Integer.parseInt(...) / runtime field"}},{"id":"date-to-char","concept":"Formatear una fecha como texto (TO_CHAR/FORMAT)","engines":{"oracle":"TO_CHAR(d, 'YYYY-MM-DD')","postgres":"TO_CHAR(d, 'YYYY-MM-DD')","mysql":"DATE_FORMAT(d, '%Y-%m-%d')","mssql":"FORMAT(d, 'yyyy-MM-dd') / CONVERT","mongodb":"$dateToString: { format, date }","redis":"none","elasticsearch":"date field format / painless SimpleDateFormat"}},{"id":"parse-date","concept":"Convertir texto en fecha (TO_DATE/PARSE)","engines":{"oracle":"TO_DATE(s, 'YYYY-MM-DD')","postgres":"TO_DATE(s, 'YYYY-MM-DD')","mysql":"STR_TO_DATE(s, '%Y-%m-%d')","mssql":"CONVERT(date, s) / PARSE","mongodb":"$dateFromString: { dateString: s }","redis":"none","elasticsearch":"automatic parsing via date mapping"}},{"id":"date-add","concept":"Sumar a una fecha (interval/DATEADD)","engines":{"oracle":"d + n / ADD_MONTHS / INTERVAL","postgres":"d + INTERVAL '1 day'","mysql":"DATE_ADD(d, INTERVAL 1 DAY)","mssql":"DATEADD(day, 1, d)","mongodb":"$dateAdd: { startDate, unit, amount }","redis":"none (add to the epoch)","elasticsearch":"date math: now+1d"}},{"id":"date-diff","concept":"Diferencia entre dos fechas (DATEDIFF)","engines":{"oracle":"d1 - d2 (days) / MONTHS_BETWEEN","postgres":"d1 - d2 / AGE(d1, d2)","mysql":"DATEDIFF(d1, d2)","mssql":"DATEDIFF(day, d2, d1)","mongodb":"$dateDiff: { startDate, endDate, unit }","redis":"none (epoch difference)","elasticsearch":"painless: ChronoUnit.DAYS.between(...)"}},{"id":"extract","concept":"Extraer una parte de la fecha (EXTRACT)","engines":{"oracle":"EXTRACT(YEAR FROM d)","postgres":"EXTRACT(YEAR FROM d) / DATE_PART","mysql":"EXTRACT(YEAR FROM d) / YEAR(d)","mssql":"DATEPART(year, d) / YEAR(d)","mongodb":"$year / $month / $dayOfMonth","redis":"none","elasticsearch":"painless: doc[f].value.year"}},{"id":"round","concept":"Redondear (ROUND)","engines":{"oracle":"ROUND(x, n)","postgres":"ROUND(x, n)","mysql":"ROUND(x, n)","mssql":"ROUND(x, n)","mongodb":"$round: [x, n]","redis":"none","elasticsearch":"painless: Math.round(x)"}},{"id":"mod","concept":"Módulo (resto)","engines":{"oracle":"MOD(a, b)","postgres":"MOD(a, b) / a % b","mysql":"MOD(a, b) / a % b","mssql":"a % b","mongodb":"$mod: [a, b]","redis":"none","elasticsearch":"painless: a % b"}},{"id":"case","concept":"Condicional (CASE/DECODE)","engines":{"oracle":"CASE ... / DECODE(x, ...)","postgres":"CASE WHEN ... END","mysql":"CASE WHEN ... END / IF()","mssql":"CASE WHEN ... END / IIF()","mongodb":"$cond / $switch","redis":"none","elasticsearch":"painless: if/else"}},{"id":"greatest-least","concept":"Mayor/menor (GREATEST/LEAST)","engines":{"oracle":"GREATEST(...) / LEAST(...)","postgres":"GREATEST(...) / LEAST(...)","mysql":"GREATEST(...) / LEAST(...)","mssql":"GREATEST(...) / LEAST(...) (2022+)","mongodb":"$max / $min (expression)","redis":"none","elasticsearch":"painless: Math.max / Math.min"}},{"id":"string-agg","concept":"Concatenar dentro de un grupo (STRING_AGG/GROUP_CONCAT/LISTAGG)","engines":{"oracle":"LISTAGG(x, ',')","postgres":"STRING_AGG(x, ',')","mysql":"GROUP_CONCAT(x)","mssql":"STRING_AGG(x, ',')","mongodb":"$push + $reduce / $concatArrays","redis":"none","elasticsearch":"terms agg / scripted_metric"}},{"id":"row-number","concept":"Número de fila (ROW_NUMBER)","engines":{"oracle":"ROW_NUMBER() OVER (...)","postgres":"ROW_NUMBER() OVER (...)","mysql":"ROW_NUMBER() OVER (...)","mssql":"ROW_NUMBER() OVER (...)","mongodb":"$setWindowFields: { $documentNumber }","redis":"none","elasticsearch":"none (top_hits / bucket_sort)"}},{"id":"limit","concept":"Primeras N filas (LIMIT/TOP/ROWNUM)","engines":{"oracle":"FETCH FIRST n ROWS ONLY / ROWNUM","postgres":"LIMIT n","mysql":"LIMIT n","mssql":"TOP n / OFFSET-FETCH","mongodb":"$limit: n / .limit(n)","redis":"LRANGE key 0 n-1","elasticsearch":"size: n"}},{"id":"current-user","concept":"Usuario actual","engines":{"oracle":"USER / SYS_CONTEXT","postgres":"CURRENT_USER","mysql":"CURRENT_USER() / USER()","mssql":"CURRENT_USER / SUSER_SNAME()","mongodb":"connectionStatus / $$USER (none, application side)","redis":"ACL WHOAMI","elasticsearch":"_security/_authenticate API"}},{"id":"gen-uuid","concept":"Generar un UUID","engines":{"oracle":"SYS_GUID()","postgres":"gen_random_uuid()","mysql":"UUID()","mssql":"NEWID()","mongodb":"UUID() / ObjectId()","redis":"none (application side)","elasticsearch":"automatic _id / application side"}},{"id":"json-extract","concept":"Extraer un campo JSON","engines":{"oracle":"JSON_VALUE(j, '$.a')","postgres":"j->>'a' / j #>> '{a}'","mysql":"JSON_EXTRACT(j, '$.a') / j->>'$.a'","mssql":"JSON_VALUE(j, '$.a')","mongodb":"doc.a (native access)","redis":"JSON.GET key $.a (RedisJSON)","elasticsearch":"doc['a'] / field access (native)"}},{"id":"regexp","concept":"Coincidencia de expresión regular (REGEXP)","engines":{"oracle":"REGEXP_LIKE(s, p)","postgres":"s ~ p / REGEXP_MATCHES","mysql":"s REGEXP p / REGEXP_LIKE","mssql":"REGEXP_LIKE (2025+) / before: LIKE / CLR","mongodb":"$regex / $regexMatch","redis":"none (SCAN MATCH glob pattern)","elasticsearch":"regexp query"},"note":"SQL Server 2025 (y Azure SQL) añadieron funciones regex nativas como REGEXP_LIKE, REGEXP_REPLACE y REGEXP_SUBSTR (REGEXP_LIKE requiere el nivel de compatibilidad 170); las versiones anteriores requieren LIKE o una función CLR."}]}]},"reference/params-oracle.json":{"engineId":"oracle","engine":"Oracle","order":1,"params":[{"name":"sga_target","default":"0","recommended":"~60% de la RAM en un servidor dedicado","unit":"bytes (K/M/G)","dynamic":"dynamic","desc":"Establece el tamaño total de la SGA para Automatic Shared Memory Management (ASMM); distribuye automáticamente los subpools.","tags":["memory","memory","sga"]},{"name":"sga_max_size","default":"Same as sga_target","recommended":"Igual o ligeramente superior a sga_target","unit":"bytes (K/M/G)","dynamic":"restart","desc":"Establece el límite superior hasta el que puede crecer la SGA en tiempo de ejecución; aumentarlo requiere reiniciar la instancia.","tags":["memory","memory","sga"]},{"name":"pga_aggregate_target","default":"10M or 20% of the SGA","recommended":"~20% de la RAM","unit":"bytes (K/M/G)","dynamic":"dynamic","desc":"Establece la memoria PGA objetivo total que usan todos los procesos de servidor; afecta a las operaciones de ordenación y hash.","tags":["memory","memory","pga","sort"]},{"name":"pga_aggregate_limit","default":"Greatest of: 2G, 200% of pga_aggregate_target, 3M x processes (not exceeding 120% of physical RAM minus SGA)","recommended":"El valor por defecto suele ser suficiente","unit":"bytes (K/M/G)","dynamic":"dynamic","desc":"Límite superior estricto de la memoria PGA de toda la instancia; cuando se supera, se abortan las llamadas de las sesiones que más consumen (excepto SYS y los procesos en segundo plano). Introducido en Oracle 12c.","tags":["memory","memory","pga","limit"]},{"name":"memory_target","default":"0","recommended":"0 (se prefiere la gestión separada de SGA/PGA)","unit":"bytes (K/M/G)","dynamic":"dynamic","desc":"Combina SGA y PGA bajo un único objetivo con Automatic Memory Management (AMM); no se recomienda en sistemas grandes.","tags":["memory","memory","amm"]},{"name":"db_cache_size","default":"0 (automatic)","recommended":"Dejar en 0 si se usa sga_target","unit":"bytes (K/M/G)","dynamic":"dynamic","desc":"Define el tamaño del buffer cache para el tamaño de bloque predeterminado; mantiene los bloques de datos en memoria.","tags":["memory","memory","cache","buffer"]},{"name":"shared_pool_size","default":"0 (automatic)","recommended":"Dejar en 0 si se usa sga_target","unit":"bytes (K/M/G)","dynamic":"dynamic","desc":"Tamaño del shared pool, que contiene los resultados del análisis de SQL/PLSQL, el dictionary cache y el library cache.","tags":["memory","memory","shared","pool"]},{"name":"processes","default":"100 / 300 (depending on version)","recommended":"Aumentar según las conexiones concurrentes esperadas","unit":"integer","dynamic":"restart","desc":"Número máximo de procesos del sistema operativo que pueden conectarse a la instancia; cambiarlo requiere un reinicio.","tags":["connection","connection","process","process"]},{"name":"sessions","default":"Derived from processes (1.5x+22)","recommended":"La derivación predeterminada suele ser suficiente","unit":"integer","dynamic":"restart","desc":"Número máximo de sesiones que pueden existir al mismo tiempo; se deriva automáticamente del valor de processes.","tags":["connection","connection","session","session"]},{"name":"open_cursors","default":"50","recommended":"300-1000 según las necesidades de la aplicación","unit":"integer","dynamic":"dynamic","desc":"Define el número máximo de cursores que una sesión puede mantener abiertos al mismo tiempo.","tags":["cursor","cursor","session","session"]},{"name":"cursor_sharing","default":"EXACT","recommended":"EXACT (usar bind variables)","unit":"EXACT | FORCE","dynamic":"dynamic","desc":"Determina si las sentencias SQL que contienen literales se comparten mediante bind variables; FORCE reemplaza los literales.","tags":["cursor","cursor","sql","parse"]},{"name":"optimizer_mode","default":"ALL_ROWS","recommended":"ALL_ROWS","unit":"ALL_ROWS | FIRST_ROWS_n","dynamic":"dynamic","desc":"Define el objetivo del optimizador basado en costos; si se prioriza el rendimiento total o el tiempo de respuesta de la primera fila.","tags":["optimizer","optimize","plan","sql"]},{"name":"undo_retention","default":"900","recommended":"Según la duración de la consulta/flashback más larga","unit":"seconds","dynamic":"dynamic","desc":"Define cuánto tiempo se conservan, si es posible, los datos de undo confirmados; afecta al riesgo de ORA-01555.","tags":["undo","rollback","retention","flashback"]},{"name":"undo_tablespace","default":"UNDOTBS1","recommended":"Un tablespace de undo dedicado y con tamaño adecuado","unit":"string (tablespace name)","dynamic":"dynamic","desc":"Define el tablespace de undo activo que usa la gestión automática de undo; puede cambiarse en línea.","tags":["undo","rollback","tablespace"]},{"name":"db_recovery_file_dest_size","default":"0","recommended":"Lo bastante grande para contener archivelogs + backups + flashback logs","unit":"bytes (K/M/G)","dynamic":"dynamic","desc":"Define el tamaño máximo en disco de la Fast Recovery Area (FRA); contiene los logs archivados y los backups.","tags":["recovery","recovery","fra","archive"]},{"name":"db_writer_processes","default":"1 or based on CPU count","recommended":"Aproximadamente CPU/8 (aumentar con carga de escritura intensa)","unit":"integer","dynamic":"restart","desc":"Define el número de procesos database writer (DBWn) que escriben los bloques sucios en disco; paraleliza la carga de escritura.","tags":["io","write","dbwr","writer"]},{"name":"parallel_max_servers","default":"parallel_threads_per_cpu x cpu_count x concurrent_parallel_users (1/2/4) x 5; capped at processes - 15","recommended":"Definir según la carga de trabajo y el número de CPU","unit":"integer","dynamic":"dynamic","desc":"Limita el número máximo de servidores de ejecución en paralelo (PX) que pueden ejecutarse al mismo tiempo en la instancia.","tags":["parallel","parallel","px","query"]},{"name":"statistics_level","default":"TYPICAL","recommended":"TYPICAL","unit":"BASIC | TYPICAL | ALL","dynamic":"dynamic","desc":"Define el nivel de AWR/ADDM y de la recopilación automática de estadísticas; BASIC desactiva muchos diagnósticos.","tags":["statistics","statistics","awr","monitoring"]},{"name":"deferred_segment_creation","default":"TRUE","recommended":"TRUE (ahorra espacio en tablas vacías)","unit":"TRUE | FALSE","dynamic":"dynamic","desc":"Pospone la creación del primer segmento de una tabla/índice vacío hasta que se inserta la primera fila; ahorra espacio.","tags":["segment","deferred-segment","space","storage"]},{"name":"audit_trail","default":"NONE (DB in databases created with DBCA)","recommended":"Usar DB o unified auditing","unit":"NONE | DB | DB,EXTENDED | OS | XML","dynamic":"restart","desc":"Determina dónde y cómo se escriben los registros de auditoría tradicionales; cambiarlo requiere un reinicio.","tags":["audit","audit","security","security"]},{"name":"compatible","default":"Installed version (e.g., 19.0.0)","recommended":"Elevar una vez que las funcionalidades de la nueva versión sean estables","unit":"version string","dynamic":"restart","desc":"Determina qué funcionalidades de versión están habilitadas; no puede reducirse una vez elevado y requiere un reinicio.","tags":["compatible","compatibility","version","version"]},{"name":"db_block_size","default":"8192","recommended":"8K (OLTP); solo se define al crear la base de datos","unit":"bytes","dynamic":"restart","desc":"Define el tamaño de bloque estándar de la base de datos; solo se define al crear la base de datos y no puede cambiarse después.","tags":["block","block","storage","io"]},{"name":"filesystemio_options","default":"none (platform-dependent)","recommended":"SETALL (Direct + Async I/O)","unit":"none | asynch | directio | setall","dynamic":"restart","desc":"Controla el uso de I/O asíncrona y directa para los datafiles en sistemas de archivos.","tags":["io","io","async","directio"]},{"name":"log_archive_max_processes","default":"4","recommended":"Aumentar según el número de destinos de archivado","unit":"integer","dynamic":"dynamic","desc":"Define el número máximo de procesos ARCn que archivan los redo logs; se aumenta para reducir el retraso de archivado.","tags":["archive","archive","redo","log"]}]},"reference/params-postgres.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"params":[{"name":"shared_buffers","default":"128MB","recommended":"~25% de la RAM","unit":"bytes (kB/MB/GB)","dynamic":"restart","desc":"Cantidad de memoria compartida que PostgreSQL asigna para almacenar en caché los bloques de datos; cambiarla requiere un reinicio.","tags":["memory","memory","cache","buffer"]},{"name":"effective_cache_size","default":"4GB","recommended":"~50-75% de la RAM","unit":"bytes (kB/MB/GB)","dynamic":"dynamic","desc":"Indica al planificador el tamaño total estimado de la caché del SO + PostgreSQL; influye en el uso de índices.","tags":["memory","memory","planner","cache"]},{"name":"work_mem","default":"4MB","recommended":"16-64MB (tener en cuenta el número de conexiones)","unit":"bytes (kB/MB/GB)","dynamic":"dynamic","desc":"Memoria que pueden usar las operaciones de ordenación y hash antes de volcar a disco; se asigna por nodo del plan de consulta.","tags":["memory","memory","sort","query"]},{"name":"maintenance_work_mem","default":"64MB","recommended":"256MB-1GB","unit":"bytes (kB/MB/GB)","dynamic":"dynamic","desc":"Define la memoria máxima que pueden usar operaciones de mantenimiento como VACUUM, CREATE INDEX y ALTER TABLE.","tags":["memory","memory","vacuum","maintenance"]},{"name":"max_connections","default":"100","recommended":"100-200 (usar un pooler más allá de eso)","unit":"integer","dynamic":"restart","desc":"Número máximo de conexiones de cliente concurrentes aceptadas; cambiarlo requiere un reinicio.","tags":["connection","connection","limit"]},{"name":"wal_buffers","default":"-1 (1/32 of shared_buffers)","recommended":"16MB","unit":"bytes (kB/MB/GB)","dynamic":"restart","desc":"Define el tamaño del buffer de memoria compartida que contiene los registros WAL antes de escribirse en disco. El valor -1 (automático) se limita a un mínimo de 64kB y un máximo de un segmento WAL (normalmente 16MB).","tags":["wal","memory","memory","buffer"]},{"name":"max_wal_size","default":"1GB","recommended":"4-16GB según la carga de trabajo","unit":"bytes (MB/GB)","dynamic":"dynamic","desc":"Define el límite superior flexible hasta el que puede crecer el WAL antes de que se dispare un checkpoint automático.","tags":["wal","checkpoint","disk"]},{"name":"min_wal_size","default":"80MB","recommended":"1-2GB","unit":"bytes (MB/GB)","dynamic":"dynamic","desc":"Límite inferior por debajo del cual los archivos WAL se conservan para reciclarlos; reduce las reasignaciones frecuentes.","tags":["wal","checkpoint","disk"]},{"name":"checkpoint_completion_target","default":"0.9","recommended":"0.9","unit":"ratio (0-1)","dynamic":"dynamic","desc":"Define la fracción del intervalo de checkpoint a lo largo de la cual se reparten las escrituras del checkpoint; suaviza los picos de I/O. El valor predeterminado pasó a ser 0.9 en PG14; antes era 0.5.","tags":["checkpoint","io","wal"]},{"name":"checkpoint_timeout","default":"5min","recommended":"15-30min","unit":"time (s/min)","dynamic":"dynamic","desc":"Define el tiempo máximo entre checkpoints automáticos; aumentarlo reduce la carga de escritura pero alarga el tiempo de recuperación.","tags":["checkpoint","wal","recovery"]},{"name":"random_page_cost","default":"4.0","recommended":"1.1 para SSD","unit":"decimal","dynamic":"dynamic","desc":"Define el costo relativo del acceso aleatorio a disco para el planificador; reducirlo en SSD favorece los index scans.","tags":["planner","optimize","io","index"]},{"name":"effective_io_concurrency","default":"1","recommended":"200 para SSD","unit":"integer","dynamic":"dynamic","desc":"Número estimado de operaciones de I/O concurrentes que el disco puede atender a la vez; afecta al prefetching en los bitmap scans. El valor predeterminado es 1 en PG17 y anteriores, y 16 en PG18.","tags":["io","gc","ssd","prefetch"]},{"name":"default_statistics_target","default":"100","recommended":"100 (elevar por columna en tablas grandes)","unit":"integer","dynamic":"dynamic","desc":"Define el nivel de detalle de las estadísticas que recopila ANALYZE; elevarlo mejora la precisión del plan y aumenta el tiempo de analyze.","tags":["statistics","statistics","analyze","planner"]},{"name":"autovacuum","default":"on","recommended":"on","unit":"on | off","dynamic":"dynamic","desc":"Habilita el proceso de vacuum automático que elimina las tuplas muertas y recopila estadísticas; desactivarlo provoca bloat.","tags":["autovacuum","vacuum","maintenance","bloat"]},{"name":"autovacuum_vacuum_scale_factor","default":"0.2","recommended":"0.05-0.1 en tablas grandes","unit":"ratio (0-1)","dynamic":"dynamic","desc":"Define la fracción de filas de una tabla que debe cambiar antes de que se le aplique vacuum; reducirlo limpia las tablas grandes con más frecuencia.","tags":["autovacuum","vacuum","bloat","maintenance"]},{"name":"autovacuum_max_workers","default":"3","recommended":"3-6 (elevar en sistemas con muchas tablas)","unit":"integer","dynamic":"restart","desc":"Define el número máximo de procesos worker de autovacuum que pueden ejecutarse a la vez; en PG17 y anteriores cambiarlo requiere un reinicio. En PG18 puede cambiarse con un reload hasta el límite de autovacuum_worker_slots.","tags":["autovacuum","vacuum","worker","maintenance"]},{"name":"synchronous_commit","default":"on","recommended":"on (datos críticos); off (rendimiento primero)","unit":"on | off | local | remote_write | remote_apply","dynamic":"dynamic","desc":"Determina si un commit espera a que el WAL se escriba en disco; off conlleva un riesgo de pequeña pérdida de datos ante una caída.","tags":["wal","commit","durability","performance"]},{"name":"wal_level","default":"replica","recommended":"replica (para replicación/PITR)","unit":"minimal | replica | logical","dynamic":"restart","desc":"Determina cuánta información se escribe en el WAL; es necesario para la replicación y la replicación lógica, y cambiarlo requiere un reinicio.","tags":["wal","replication","replication","pitr"]},{"name":"max_wal_senders","default":"10","recommended":"Según el número de standbys + backups","unit":"integer","dynamic":"restart","desc":"Define el número máximo de procesos que pueden transmitir WAL de forma concurrente (para standbys y backups).","tags":["wal","replication","replication","standby"]},{"name":"wal_keep_size","default":"0","recommended":"Suficiente para cubrir el retraso del standby","unit":"bytes (MB/GB)","dynamic":"dynamic","desc":"Cantidad mínima de WAL que se conserva en pg_wal para los standbys; protege a un standby retrasado cuando no hay replication slot. Introducido en PG13; antes se usaba wal_keep_segments (número de segmentos).","tags":["wal","replication","replication","standby"]},{"name":"log_min_duration_statement","default":"-1 (disabled)","recommended":"1000ms para detectar consultas lentas","unit":"milliseconds","dynamic":"dynamic","desc":"Registra en el log las sentencias que se ejecutan durante más tiempo del especificado; se usa para detectar consultas lentas, -1 lo desactiva.","tags":["logging","log","slow-query","slow-query"]},{"name":"idle_in_transaction_session_timeout","default":"0 (disabled)","recommended":"60000ms para evitar bloqueos retenidos","unit":"milliseconds","dynamic":"dynamic","desc":"Termina las sesiones que permanecen inactivas dentro de una transacción abierta durante el tiempo especificado; reduce el riesgo de bloqueos y bloat.","tags":["timeout","transaction","transaction","lock"]},{"name":"statement_timeout","default":"0 (disabled)","recommended":"Depende de la aplicación (bajo para OLTP)","unit":"milliseconds","dynamic":"dynamic","desc":"Cancela automáticamente las sentencias que superan el tiempo especificado; evita que las consultas descontroladas consuman recursos.","tags":["timeout","query","query","limit"]},{"name":"shared_preload_libraries","default":"'' (empty)","recommended":"pg_stat_statements (para monitoreo)","unit":"string (comma-separated list)","dynamic":"restart","desc":"Especifica las bibliotecas compartidas que se cargan al iniciar el servidor; extensiones como pg_stat_statements se configuran aquí.","tags":["extension","extension","preload","monitoring"]},{"name":"max_parallel_workers_per_gather","default":"2","recommended":"2-4 (según el número de CPU)","unit":"integer","dynamic":"dynamic","desc":"Define el número máximo de workers paralelos que puede iniciar un solo nodo Gather/GatherMerge; 0 desactiva el paralelismo.","tags":["parallel","parallel","worker","query"]}]},"reference/params-mysql.json":{"engineId":"mysql","engine":"MySQL","order":3,"params":[{"name":"innodb_buffer_pool_size","default":"128MB","recommended":"~70-80% de la RAM en un servidor dedicado","unit":"bytes (M/G)","dynamic":"dynamic","desc":"El pool de memoria principal donde InnoDB almacena en caché los datos y los índices; en 8.0 puede cambiarse en línea, pero los cambios grandes requieren cuidado.","tags":["memory","memory sizing","innodb","buffer"]},{"name":"innodb_buffer_pool_instances","default":"1 (or 8 if size>1GB)","recommended":"8 para pools grandes","unit":"integer","dynamic":"restart","desc":"Define el número de instancias en que se divide el buffer pool; reduce la contención de mutex con concurrencia, cambiarlo requiere un reinicio.","tags":["memory","memory sizing","innodb","concurrency"]},{"name":"innodb_redo_log_capacity","default":"100MB (8.0.30+)","recommended":"1-8GB (según la carga de escritura; ~1 hora de redo en el pico)","unit":"bytes (M/G)","dynamic":"dynamic","desc":"Define el espacio total en disco que ocupa el redo log de InnoDB; aumentarlo suaviza la presión de checkpoint y los picos de escritura. Introducido en MySQL 8.0.30 y modificable sin reinicio mediante SET GLOBAL/SET PERSIST. Los antiguos innodb_log_file_size (predeterminado 48MB) e innodb_log_files_in_group están obsoletos desde 8.0.30; antes de 8.0.30 el tamaño del redo se define con estos dos y requiere un reinicio.","tags":["innodb","redo","log","io"]},{"name":"innodb_flush_log_at_trx_commit","default":"1","recommended":"1 (ACID completo); 2 (rendimiento)","unit":"0 | 1 | 2","dynamic":"dynamic","desc":"Controla si el redo log se escribe y sincroniza en disco en cada commit; 1 ofrece durabilidad completa, 2/0 conllevan riesgo de pérdida.","tags":["innodb","commit","durability","acid"]},{"name":"innodb_flush_method","default":"8.0: fsync (Linux) / 8.4: O_DIRECT (if supported); Windows: unbuffered","recommended":"O_DIRECT (Linux)","unit":"fsync | O_DIRECT | O_DIRECT_NO_FSYNC","dynamic":"restart","desc":"Define cómo InnoDB vacía los archivos de datos y de log en disco; O_DIRECT evita el doble buffering, cambiarlo requiere un reinicio.","tags":["innodb","io","flush","gc"]},{"name":"innodb_io_capacity","default":"8.0: 200 / 8.4: 10000","recommended":"Según los IOPS reales del almacenamiento (1000-2000 para SSD en 8.0)","unit":"integer (IOPS)","dynamic":"dynamic","desc":"Define la capacidad estimada de IOPS del disco que usan las tareas en segundo plano de InnoDB (flush, merge). En MySQL 8.4 los valores predeterminados se elevaron a innodb_io_capacity=10000 e innodb_io_capacity_max=20000 (200/2000 en 8.0).","tags":["innodb","io","iops","ssd"]},{"name":"innodb_read_io_threads","default":"8.0: 4 / 8.4: half the number of logical CPUs (minimum 4)","recommended":"4-8 (según la carga de lectura)","unit":"integer","dynamic":"restart","desc":"Define el número de threads en segundo plano de InnoDB que atienden las solicitudes de I/O de lectura; cambiarlo requiere un reinicio.","tags":["innodb","io","thread","read"]},{"name":"innodb_write_io_threads","default":"4","recommended":"4-8 (según la carga de escritura)","unit":"integer","dynamic":"restart","desc":"Define el número de threads en segundo plano de InnoDB que atienden las solicitudes de I/O de escritura; cambiarlo requiere un reinicio.","tags":["innodb","io","thread","write"]},{"name":"max_connections","default":"151","recommended":"200-500 según la carga de trabajo","unit":"integer","dynamic":"dynamic","desc":"Define el número máximo de conexiones de cliente simultáneas permitidas; puede cambiarse en línea.","tags":["connection","connections","limit"]},{"name":"thread_cache_size","default":"-1 (autosized)","recommended":"Automático; aumentar con muchas conexiones de corta duración","unit":"integer","dynamic":"dynamic","desc":"Define cuántos threads se mantienen en caché para reutilizarlos; reduce la creación de threads para nuevas conexiones.","tags":["connection","connections","thread","cache"]},{"name":"table_open_cache","default":"4000","recommended":"4000-8000 según el número de tablas abiertas","unit":"integer","dynamic":"dynamic","desc":"Define el tamaño de la caché de handles de tablas abiertas para todos los threads; reduce la sobrecarga de reabrir tablas.","tags":["cache","table","table cache","open"]},{"name":"tmp_table_size","default":"16MB","recommended":"64-256MB","unit":"bytes (M/G)","dynamic":"dynamic","desc":"Define el tamaño máximo de una tabla temporal en memoria antes de convertirse en una tabla en disco; funciona junto con max_heap_table_size.","tags":["memory","memory sizing","temp","temporary table"]},{"name":"max_heap_table_size","default":"16MB","recommended":"Igual que tmp_table_size","unit":"bytes (M/G)","dynamic":"dynamic","desc":"Define el tamaño máximo hasta el que pueden crecer las tablas del motor MEMORY y las tablas temporales en memoria.","tags":["memory","memory sizing","temp","heap"]},{"name":"sort_buffer_size","default":"256KB","recommended":"256KB-2MB (tener en cuenta el costo por conexión)","unit":"bytes (K/M)","dynamic":"dynamic","desc":"Buffer asignado por sesión para las ordenaciones de ORDER BY y GROUP BY; hacerlo demasiado grande consume memoria.","tags":["memory","memory sizing","sort","buffer"]},{"name":"join_buffer_size","default":"256KB","recommended":"256KB-1MB (preferir resolverlo con un índice)","unit":"bytes (K/M)","dynamic":"dynamic","desc":"Buffer asignado por sesión para los joins que no pueden usar un índice; la solución duradera es añadir un índice adecuado.","tags":["memory","memory sizing","join","buffer"]},{"name":"sync_binlog","default":"1","recommended":"1 (seguridad total); 100-1000 con carga intensa","unit":"integer","dynamic":"dynamic","desc":"Define tras cuántos commits se sincroniza el binary log en disco; 1 es lo más seguro, los valores más altos sacrifican durabilidad a cambio de rendimiento.","tags":["binlog","replication","durability","commit"]},{"name":"binlog_expire_logs_seconds","default":"2592000 (30 days)","recommended":"Según la ventana de backup/replicación","unit":"seconds","dynamic":"dynamic","desc":"Define cuánto tiempo se conservan los archivos de binary log antes de la purga automática; controla el uso de disco.","tags":["binlog","log","retention","replication"]},{"name":"long_query_time","default":"10","recommended":"1 (detectar consultas lentas)","unit":"seconds","dynamic":"dynamic","desc":"Define la duración que debe superar una consulta para registrarse en el slow query log; acepta valores fraccionarios.","tags":["slow-query","slow-query-log","log","performance"]},{"name":"slow_query_log","default":"OFF","recommended":"ON (para diagnóstico)","unit":"ON | OFF","dynamic":"dynamic","desc":"Controla si las consultas que superan long_query_time se escriben en el slow query log; habilítelo para el análisis de rendimiento.","tags":["slow-query","slow-query-log","log","monitoring"]},{"name":"wait_timeout","default":"28800 (8 hours)","recommended":"Según el pool de la aplicación (p. ej. 600)","unit":"seconds","dynamic":"dynamic","desc":"Define el tiempo máximo que una conexión no interactiva puede permanecer inactiva antes de cerrarse; limpia las conexiones inactivas.","tags":["timeout","connection","connections","idle"]},{"name":"max_allowed_packet","default":"64MB","recommended":"64-256MB para BLOBs/filas grandes","unit":"bytes (M/G)","dynamic":"dynamic","desc":"Define el tamaño máximo de un solo paquete o de cualquier fila generada/intermedia; auméntelo para transferencias de BLOB grandes.","tags":["packet","packet size","blob","network"]},{"name":"innodb_lock_wait_timeout","default":"50","recommended":"10-50 para OLTP","unit":"seconds","dynamic":"dynamic","desc":"Define cuánto tiempo espera una transacción de InnoDB un bloqueo de fila antes de agotar el tiempo de espera.","tags":["innodb","lock","lock wait","timeout"]},{"name":"character_set_server","default":"utf8mb4","recommended":"utf8mb4","unit":"text (character set)","dynamic":"dynamic","desc":"Define el juego de caracteres predeterminado del servidor; utf8mb4 ofrece soporte completo de Unicode (incluidos los emoji).","tags":["charset","character-set","unicode","utf8mb4"]}]},"reference/params-mssql.json":{"engineId":"mssql","engine":"SQL Server","order":4,"params":[{"name":"max server memory (MB)","default":"2147483647","recommended":"RAM total menos la parte reservada para el SO y otros servicios (~10-25%)","unit":"MB","dynamic":"dynamic","desc":"Define la cantidad máxima de memoria que pueden usar el buffer pool de SQL Server y otros consumidores de memoria. El valor predeterminado es prácticamente ilimitado; si en el servidor se ejecutan otros servicios, debe reducirse para dejar memoria suficiente al sistema operativo. Se define con sp_configure; surte efecto de inmediato con RECONFIGURE.","tags":["memory","memory limit","buffer pool"]},{"name":"min server memory (MB)","default":"0","recommended":"En instancias críticas, un límite inferior razonable respecto a max","unit":"MB","dynamic":"dynamic","desc":"La cantidad mínima de memoria que SQL Server no devolverá al sistema operativo una vez asignada. Se usa para evitar que SQL Server libere demasiada memoria bajo presión de memoria. Se define con sp_configure; surte efecto con RECONFIGURE.","tags":["memory","memory limit"]},{"name":"max degree of parallelism","default":"0","recommended":"Normalmente 8 o el número de núcleos físicos por nodo NUMA","unit":"count","dynamic":"dynamic","desc":"Limita el número máximo de procesadores (núcleos) que una sola consulta puede usar en la ejecución en paralelo. 0 significa que pueden usarse todos los núcleos; se limita para evitar un paralelismo excesivo en cargas de trabajo OLTP. Surte efecto de inmediato con sp_configure + RECONFIGURE.","tags":["parallelism","parallel query","maxdop","cpu"]},{"name":"cost threshold for parallelism","default":"5","recommended":"25-50 en hardware moderno","unit":"cost","dynamic":"dynamic","desc":"El umbral mínimo de costo estimado que debe alcanzar un plan de consulta para ejecutarse en paralelo. El valor predeterminado de 5 es muy bajo y hace que consultas pequeñas se paralelicen innecesariamente; elevarlo mejora el comportamiento del paralelismo en todo el servidor. Surte efecto de inmediato con sp_configure + RECONFIGURE.","tags":["parallelism","parallel query","cpu"]},{"name":"max worker threads","default":"0","recommended":"0 (dejar que SQL Server lo calcule)","unit":"count","dynamic":"dynamic","desc":"Define el número máximo de worker threads que SQL Server puede crear para procesar las solicitudes de los clientes. 0 hace que se calcule automáticamente según el número de procesadores y la arquitectura. Es una opción avanzada; se aplica con sp_configure + RECONFIGURE sin reinicio (sys.configurations.is_dynamic = 1).","tags":["threads","worker","cpu","concurrency"]},{"name":"optimize for ad hoc workloads","default":"0","recommended":"1 (activado)","unit":"0/1","dynamic":"dynamic","desc":"Para las consultas ad hoc que se ejecutan una sola vez, almacena en caché un pequeño stub del plan compilado en la primera ejecución en lugar del plan completo. Evita que la plan cache se llene de planes de un solo uso y mejora el uso de memoria. Surte efecto de inmediato con sp_configure + RECONFIGURE.","tags":["plan cache","memory","memory usage"]},{"name":"backup compression default","default":"0","recommended":"1 (activado)","unit":"0/1","dynamic":"dynamic","desc":"Hace que los nuevos backups se compriman de forma predeterminada. La compresión reduce notablemente el tamaño y la duración del backup a costa de algo de CPU. Surte efecto de inmediato con sp_configure + RECONFIGURE.","tags":["backup","backup size","compression","backup compression"]},{"name":"fill factor (%)","default":"0","recommended":"0 o 90-100 (según la carga de trabajo)","unit":"%","dynamic":"restart","desc":"Define, como porcentaje, cuánto se llenan las páginas de nivel hoja cuando se crea o reconstruye (REBUILD) un índice. 0 y 100 llenan las páginas por completo; los valores más bajos reducen las divisiones de página pero aumentan el uso de disco y memoria. El valor predeterminado a nivel de servidor se cambia con sp_configure, pero no surte efecto hasta que se reinicia el servicio (is_dynamic = 0); la opción FILLFACTOR por índice puede usarse de inmediato.","tags":["index","page split","storage","disk usage"]},{"name":"recovery interval (min)","default":"0","recommended":"0 (automático)","unit":"minutes","dynamic":"dynamic","desc":"Define la frecuencia de los checkpoints automáticos en función del tiempo máximo de recuperación objetivo. 0 permite que SQL Server gestione automáticamente el tiempo de recuperación objetivo por base de datos. Surte efecto de inmediato con sp_configure + RECONFIGURE.","tags":["checkpoint","recovery","crash recovery"]},{"name":"remote query timeout (s)","default":"600","recommended":"600 o 0 (ilimitado), según la carga de trabajo","unit":"seconds","dynamic":"dynamic","desc":"El tiempo que el servidor espera una respuesta a las consultas remotas (a través de un linked server) antes de agotar el tiempo de espera. 0 significa esperar indefinidamente. Surte efecto de inmediato con sp_configure + RECONFIGURE.","tags":["timeout","remote","linked server"]},{"name":"network packet size (B)","default":"4096","recommended":"4096 (no cambiar salvo en casos especiales)","unit":"bytes","dynamic":"dynamic","desc":"El tamaño de paquete usado para la transferencia de datos por la red. Se recomienda el valor predeterminado salvo en operaciones masivas especiales que transfieren datos muy grandes; un ajuste incorrecto puede degradar el rendimiento. Surte efecto con sp_configure + RECONFIGURE.","tags":["network","network packet","packet"]},{"name":"nivel de compatibilidad de la base de datos (ALTER DATABASE)","default":"Depends on server version","recommended":"El último nivel probado","unit":"level","dynamic":"dynamic","desc":"Determina con qué comportamientos y modelo de optimizador de consultas de qué versión de SQL Server se ejecuta una base de datos. Se define por base de datos con ALTER DATABASE ... SET COMPATIBILITY_LEVEL y surte efecto de inmediato; afecta a funcionalidades del optimizador como la estimación de cardinalidad.","tags":["database","compatibility level","optimizer","compatibility"]},{"name":"MAXDOP (database scoped configuration)","default":"0 (inherits the server setting)","recommended":"Un valor adecuado a la carga de trabajo de la base de datos","unit":"count","dynamic":"dynamic","desc":"Sobrescribe a nivel de base de datos la configuración max degree of parallelism de todo el servidor. Se define con ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP y surte efecto de inmediato; permite un comportamiento de paralelismo distinto para cada base de datos.","tags":["parallelism","parallel query","maxdop","database scoped"]},{"name":"número de archivos de tempdb (recomendación)","default":"SQL Server 2016+ setup: equal to the number of logical cores, at most 8 (1 before)","recommended":"Igual al número de núcleos lógicos, como máximo 8 (aumentar en pasos de 4 si es necesario)","unit":"count","dynamic":"dynamic","desc":"El número de archivos de datos de tempdb es importante para reducir la contención en las páginas de asignación. La recomendación general es usar archivos de igual tamaño hasta el número de núcleos (máx. 8). Un archivo añadido con ALTER DATABASE tempdb ADD FILE se usa de inmediato sin reinicio; sin embargo, si los archivos existentes están llenos, iguale el tamaño de todos los archivos para equilibrar el llenado proporcional. Eliminar un archivo de tempdb (REMOVE FILE) a menudo solo es posible tras un reinicio, porque el archivo no se vacía.","tags":["tempdb","storage","data files","contention"]},{"name":"Trace Flag 1117 / 1118 (nota)","default":"Default behavior for tempdb in SQL 2016+","recommended":"Generalmente innecesario en SQL 2016+; activado en versiones anteriores","unit":"flag","dynamic":"restart","desc":"TF 1117 obliga a que todos los archivos de un filegroup crezcan juntos; TF 1118 fuerza la asignación de extents uniformes en lugar de extents mixtos. Desde SQL Server 2016 estos comportamientos son los predeterminados para tempdb, por lo que los flags suelen ser innecesarios. Requiere un reinicio cuando se añade como flag de inicio.","tags":["trace flag","tempdb","note"]},{"name":"Trace Flag 3226","default":"Off","recommended":"Activado (suprime los mensajes de backup correcto)","unit":"flag","dynamic":"dynamic","desc":"Evita que las operaciones de backup correctas se escriban en el error log de SQL Server y en el registro de eventos de Windows. En sistemas con log backups frecuentes, evita que los logs se llenen de mensajes innecesarios (log spam). Puede habilitarse de inmediato en tiempo de ejecución con DBCC TRACEON(3226, -1); para que persista tras los reinicios, añada también -T3226 como parámetro de inicio.","tags":["trace flag","backup","backup messages","log"]},{"name":"lock pages in memory (nota sobre la directiva de Windows)","default":"Off","recommended":"Se concede a la cuenta de servicio en escenarios específicos","unit":"policy","dynamic":"restart","desc":"Una directiva de derechos de usuario de Windows que permite a la cuenta de servicio de SQL Server evitar que el sistema operativo pagine a disco sus páginas de memoria. Puede reducir las fluctuaciones de rendimiento bajo presión de memoria, pero debe configurarse con cuidado. Cambiar la directiva requiere reiniciar el servicio.","tags":["memory","memory paging","windows","policy","note"]}]},"reference/params-mongodb.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"params":[{"name":"storage.wiredTiger.engineConfig.cacheSizeGB","default":"(RAM - 1GB) * 0.5","recommended":"Mantener el valor predeterminado o ~50% de la RAM disponible","unit":"GB","dynamic":"restart","desc":"Define, en gigabytes, la cantidad de memoria asignada a la caché interna del motor de almacenamiento WiredTiger. De forma predeterminada se usa la mitad de (RAM total - 1GB). Definir el valor manualmente solo es necesario en casos especiales y requiere reiniciar mongod.","tags":["memory","memory","cache","wiredtiger"]},{"name":"net.maxIncomingConnections","default":"1000000 (effectively limited by OS limits)","recommended":"Según la carga de trabajo y el ulimit del sistema operativo","unit":"count","dynamic":"restart","desc":"Limita el número máximo de conexiones entrantes simultáneas que acepta una instancia mongod o mongos. Si el valor es mayor que el límite de descriptores de archivo/conexiones del sistema operativo (ulimit), no tiene efecto; el SO determina el techo real (visible como current + available en db.serverStatus().connections). El cambio se realiza en el archivo de configuración y requiere un reinicio.","tags":["network","network","connections","connection"]},{"name":"operationProfiling.slowOpThresholdMs","default":"100","recommended":"100 o menos según la carga de trabajo","unit":"ms","dynamic":"dynamic","desc":"Define, en milisegundos, el umbral de duración para que una operación se considere lenta y la registren el profiler y el log. Las operaciones que superan este umbral se registran para facilitar el diagnóstico. Puede cambiarse de inmediato en tiempo de ejecución con db.setProfilingLevel().","tags":["profiling","slow query","slow query","monitoring"]},{"name":"replication.oplogSizeMB","default":"~5% of disk size (max 50GB)","recommended":"Lo bastante grande para cubrir la ventana de mantenimiento","unit":"MB","dynamic":"dynamic","desc":"Define, en megabytes, el tamaño máximo de la colección oplog que almacena las operaciones en los miembros del replica set. Un oplog suficientemente grande permite que los miembros retrasados se pongan al día sin necesidad de un resync completo. Puede cambiarse de inmediato en un replica set en ejecución con el comando replSetResizeOplog.","tags":["replication","oplog","replica set"]},{"name":"security.authorization","default":"disabled","recommended":"enabled (control de acceso basado en roles)","unit":"enabled/disabled","dynamic":"restart","desc":"Habilita el control de acceso basado en roles (RBAC); cuando está habilitado, los clientes deben autenticarse y estar autorizados. Siempre debe estar habilitado en entornos de producción. Se define en el archivo de configuración y requiere un reinicio.","tags":["security","security","authorization","authorization","rbac"]},{"name":"net.compression.compressors","default":"snappy,zstd,zlib","recommended":"snappy,zstd (el valor predeterminado es adecuado)","unit":"list","dynamic":"restart","desc":"Define los algoritmos de compresión, y su orden de preferencia, que pueden usarse para el tráfico de red de clientes y entre miembros del clúster. Reduce el uso de ancho de banda de red a costa de algo de CPU. Se define en el archivo de configuración y requiere un reinicio.","tags":["network","network","compression","compression"]},{"name":"storage.wiredTiger.collectionConfig.blockCompressor","default":"snappy","recommended":"snappy (velocidad) o zstd (alta compresión)","unit":"algorithm","dynamic":"restart","desc":"Define el algoritmo de compresión de bloques que usa WiredTiger al escribir los datos de las colecciones en disco. snappy prioriza la velocidad, mientras que zlib y zstd ofrecen tasas de compresión más altas. Solo se aplica a las colecciones creadas después del ajuste y requiere un reinicio.","tags":["storage","storage","compression","compression","wiredtiger"]},{"name":"storage.directoryPerDB","default":"false","recommended":"true si desea separar discos","unit":"true/false","dynamic":"restart","desc":"Almacena los datos de cada base de datos en un subdirectorio independiente, de modo que distintas bases de datos pueden ubicarse en distintos volúmenes de disco. Solo puede habilitarse en un directorio de datos vacío. El cambio se realiza en el archivo de configuración y requiere un reinicio.","tags":["storage","storage","directory"]},{"name":"setParameter.cursorTimeoutMillis","default":"600000","recommended":"El valor predeterminado (10 minutos), según la carga de trabajo","unit":"ms","dynamic":"dynamic","desc":"Define, en milisegundos, cuánto esperan los cursores inactivos antes de que el servidor los cierre automáticamente. Evita que los cursores abandonados consuman recursos. Puede cambiarse de inmediato en tiempo de ejecución con db.adminCommand({setParameter:1}).","tags":["cursor","timeout","setparameter"]},{"name":"setParameter.transactionLifetimeLimitSeconds","default":"60","recommended":"El valor predeterminado, salvo que se necesiten transacciones largas","unit":"seconds","dynamic":"dynamic","desc":"Define, en segundos, el tiempo máximo que una transacción multidocumento puede permanecer abierta antes de abortarse automáticamente. Evita que las transacciones de larga duración retengan recursos y la caché de WiredTiger. Puede cambiarse de inmediato con setParameter.","tags":["transaction","transaction","setparameter"]},{"name":"setParameter.maxTransactionLockRequestTimeoutMillis","default":"5","recommended":"El valor predeterminado, según la contención de bloqueos","unit":"ms","dynamic":"dynamic","desc":"Define, en milisegundos, el tiempo máximo que una transacción espera para adquirir un bloqueo que necesita antes de agotar el tiempo de espera. Ajustar el comportamiento de espera de bloqueos evita que las transacciones queden bloqueadas demasiado tiempo. Puede cambiarse de inmediato con setParameter.","tags":["transaction","lock","lock","setparameter"]},{"name":"net.tls.mode","default":"disabled","recommended":"requireTLS (en producción)","unit":"mode","dynamic":"restart","desc":"Determina el uso de conexiones cifradas TLS/SSL por parte del servidor. requireTLS obliga a cifrar todas las conexiones; preferTLS y allowTLS ofrecen modos intermedios para periodos de transición. Se usa junto con la configuración de certificados y requiere un reinicio.","tags":["security","security","tls","ssl","encryption"]},{"name":"storage.syncPeriodSecs","default":"60","recommended":"60 (no cambiar el valor predeterminado)","unit":"seconds","dynamic":"dynamic","desc":"Define, en segundos, con qué frecuencia WiredTiger realiza checkpoints, es decir, el intervalo entre volcados a disco de los datos en memoria. El valor predeterminado se adapta a la mayoría de cargas de trabajo y en general no se recomienda cambiarlo. Se define en el archivo de configuración; en tiempo de ejecución también puede cambiarse sin reinicio con db.adminCommand({ setParameter: 1, syncdelay: <seconds> }).","tags":["storage","storage","checkpoint","sync"]},{"name":"replication.enableMajorityReadConcern","default":"true","recommended":"true (no puede cambiarse en 5.0+)","unit":"true/false","dynamic":"restart","desc":"Permite usar el nivel de read concern majority; este nivel garantiza leer datos que se han escrito en la mayoría de los miembros y que no se revertirán. Desde MongoDB 5.0 este ajuste no puede cambiarse y siempre es true; en versiones anteriores (p. ej. en una arquitectura PSA) podía establecerse en false. Se define en el archivo de configuración y requiere un reinicio.","tags":["replication","read concern","replica set"]},{"name":"processManagement.fork","default":"false","recommended":"false con systemd, true para un daemon manual","unit":"true/false","dynamic":"restart","desc":"Hace que el proceso mongod haga fork y se ejecute en segundo plano como daemon. Con systemd o un gestor de servicios similar normalmente se deja en false, porque el gestor de servicios se encarga de la gestión del proceso. Se define en el archivo de configuración y afecta al comportamiento de inicio.","tags":["process","process","daemon","fork"]}]},"reference/params-redis.json":{"engineId":"redis","engine":"Redis","order":6,"params":[{"name":"maxmemory","default":"0 (unlimited)","recommended":"~70-75% de la RAM física","unit":"bytes","dynamic":"dynamic","desc":"Define la cantidad máxima de memoria que Redis puede usar para datos. Un valor de 0 significa uso ilimitado y puede provocar situaciones de falta de memoria; se recomienda encarecidamente establecer un límite. Puede cambiarse de inmediato con CONFIG SET maxmemory.","tags":["memory","memory"]},{"name":"maxmemory-policy","default":"noeviction","recommended":"allkeys-lru para una caché; de lo contrario, noeviction","unit":"policy","dynamic":"dynamic","desc":"La política que determina qué claves desaloja Redis, y cómo, cuando se alcanza el límite de maxmemory. noeviction devuelve errores de escritura, mientras que allkeys-lru elimina las claves usadas menos recientemente. Puede cambiarse de inmediato con CONFIG SET.","tags":["memory","memory","eviction","cache"]},{"name":"maxclients","default":"10000","recommended":"Según la carga de trabajo y el ulimit del SO","unit":"count","dynamic":"dynamic","desc":"Define el número máximo de conexiones de cliente concurrentes aceptadas. El límite de descriptores de archivo del sistema operativo puede restringir este valor. Puede cambiarse de inmediato con CONFIG SET maxclients.","tags":["connections","connection","clients"]},{"name":"appendonly","default":"no","recommended":"yes si se requiere persistencia","unit":"yes/no","dynamic":"dynamic","desc":"Habilita el mecanismo de persistencia AOF (Append Only File); cuando está habilitado, cada operación de escritura se añade a un archivo de log. Ofrece menos riesgo de pérdida de datos que los snapshots RDB. Puede habilitarse de inmediato con CONFIG SET appendonly yes.","tags":["persistence","persistence","aof"]},{"name":"appendfsync","default":"everysec","recommended":"everysec (equilibrado)","unit":"policy","dynamic":"dynamic","desc":"Define con qué frecuencia el archivo AOF se sincroniza en disco (fsync). always sincroniza en cada escritura y es lo más seguro pero lento; everysec sincroniza una vez por segundo, equilibrando rendimiento y durabilidad; no deja la sincronización al sistema operativo. Puede cambiarse de inmediato con CONFIG SET.","tags":["persistence","persistence","aof","fsync"]},{"name":"save","default":"3600 1 300 100 60 10000","recommended":"Según la carga de trabajo, o desactivado (solo AOF)","unit":"rule","dynamic":"dynamic","desc":"Define las condiciones (un número dado de cambios en un tiempo dado) bajo las cuales se guarda un snapshot RDB en disco. Establecer un valor vacío desactiva por completo los snapshots RDB. Puede cambiarse de inmediato con CONFIG SET save.","tags":["persistence","persistence","rdb","snapshot"]},{"name":"tcp-keepalive","default":"300","recommended":"300 (el valor predeterminado es adecuado)","unit":"seconds","dynamic":"dynamic","desc":"Define, en segundos, el intervalo con el que Redis envía ACK de TCP keepalive a las conexiones de clientes. Garantiza que las conexiones muertas se detecten y cierren y que las conexiones semiabiertas se limpien. Puede cambiarse de inmediato con CONFIG SET.","tags":["network","network","tcp","keepalive"]},{"name":"timeout","default":"0","recommended":"0 (no cerrar nunca las conexiones) o un tiempo de inactividad razonable","unit":"seconds","dynamic":"dynamic","desc":"Define, en segundos, el tiempo máximo que una conexión de cliente puede permanecer inactiva; la conexión se cierra cuando se supera. Un valor de 0 significa que los clientes pueden permanecer inactivos indefinidamente. Puede cambiarse de inmediato con CONFIG SET timeout.","tags":["connections","connection","timeout","idle"]},{"name":"hz","default":"10","recommended":"10 (hasta 100 en sistemas con mucha actividad)","unit":"Hz","dynamic":"dynamic","desc":"Define cuántas veces por segundo Redis ejecuta sus tareas en segundo plano (expiración de claves, comprobación de timeouts de clientes, etc.). Los valores más altos dan un control más preciso pero aumentan el uso de CPU. Puede cambiarse de inmediato con CONFIG SET hz.","tags":["performance","cpu","background"]},{"name":"activedefrag","default":"no","recommended":"yes si la fragmentación de memoria es alta","unit":"yes/no","dynamic":"dynamic","desc":"Habilita la desfragmentación activa de memoria; reorganiza los datos durante la ejecución para reducir la fragmentación de memoria. Útil en instancias de larga duración con fragmentación alta. Puede habilitarse de inmediato con CONFIG SET activedefrag yes.","tags":["memory","memory","defragmentation"]},{"name":"maxmemory-samples","default":"5","recommended":"5 (puede elevarse para un desalojo más preciso)","unit":"count","dynamic":"dynamic","desc":"Define el número de claves que muestrean los algoritmos de desalojo LRU y LFU al elegir una clave. Los valores más altos se acercan más al comportamiento LRU/LFU real, pero consumen algo más de CPU. Puede cambiarse de inmediato con CONFIG SET.","tags":["memory","memory","eviction","lru"]},{"name":"io-threads","default":"1","recommended":"2-4 en servidores multinúcleo (según el número de núcleos)","unit":"count","dynamic":"restart","desc":"Define el número de threads de I/O usados para escribir y leer en los sockets de los clientes. Puede aumentar el throughput en sistemas multinúcleo con tráfico de red intenso. Este ajuste solo puede cambiarse en el archivo de configuración y requiere un reinicio.","tags":["performance","threads","io","cpu"]},{"name":"repl-backlog-size","default":"1mb","recommended":"Mayor, según la tasa de escritura y la duración de las interrupciones","unit":"bytes","dynamic":"dynamic","desc":"Define el tamaño del buffer de replicación que se mantiene en el primario para la resincronización parcial cuando se cae la conexión de una réplica. Un buffer suficientemente grande permite que la réplica se ponga al día tras interrupciones cortas sin una sincronización completa. Puede cambiarse de inmediato con CONFIG SET.","tags":["replication","replication","backlog"]},{"name":"repl-diskless-sync","default":"yes","recommended":"yes (en redes rápidas)","unit":"yes/no","dynamic":"dynamic","desc":"Durante una sincronización completa, envía los datos RDB a las réplicas directamente por el socket sin escribirlos en disco. Acelera la sincronización en sistemas con un cuello de botella de I/O de disco. Puede cambiarse de inmediato con CONFIG SET repl-diskless-sync.","tags":["replication","replication","diskless","sync"]},{"name":"requirepass","default":"(empty)","recommended":"Establecer una contraseña segura","unit":"password","dynamic":"dynamic","desc":"Define la contraseña con la que los clientes deben autenticarse mediante AUTH antes de ejecutar comandos. Debe establecerse una contraseña segura para evitar el acceso no autorizado a Redis. Puede cambiarse de inmediato con CONFIG SET requirepass.","tags":["security","security","authentication","password"]},{"name":"cluster-enabled","default":"no","recommended":"yes si se requiere escalado horizontal","unit":"yes/no","dynamic":"restart","desc":"Habilita el modo Redis Cluster; cuando está habilitado, los datos se distribuyen entre varios nodos mediante hash slots. Se usa para el escalado horizontal y el particionamiento automático de datos. Este ajuste solo puede cambiarse en el archivo de configuración y requiere un reinicio.","tags":["cluster","cluster","scaling","scaling"]},{"name":"cluster-node-timeout","default":"15000","recommended":"15000 o un valor ajustado a la latencia de red","unit":"ms","dynamic":"dynamic","desc":"En modo clúster, define en milisegundos el tiempo máximo que un nodo puede pasar sin responder antes de considerarse inalcanzable (fail). Afecta directamente al comportamiento del failover y a la estabilidad del clúster. Puede cambiarse de inmediato con CONFIG SET.","tags":["cluster","cluster","timeout","failover"]},{"name":"aof-use-rdb-preamble","default":"yes","recommended":"yes (recarga rápida)","unit":"yes/no","dynamic":"dynamic","desc":"Habilita el formato de persistencia híbrido en el que una reescritura de AOF escribe un snapshot RDB compacto al inicio del archivo seguido de comandos AOF incrementales. Esto acorta el tiempo de carga del AOF al reiniciar. Puede cambiarse de inmediato con CONFIG SET.","tags":["persistence","persistence","aof","rdb"]}]},"reference/params-elasticsearch.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"params":[{"name":"-Xms / -Xmx (heap de la JVM)","default":"Automatic depending on version (half of RAM)","recommended":"50% de la RAM física, como máximo ~30-31GB (Xms=Xmx)","unit":"GB","dynamic":"restart","desc":"Define el tamaño inicial (-Xms) y máximo (-Xmx) del heap de la JVM; ambos deben ser iguales. Se recomienda no superar la mitad de la RAM total ni el límite de ~32GB a partir del cual dejan de aplicarse los punteros de objeto comprimidos. Se define en jvm.options y requiere reiniciar el nodo.","tags":["memory","memory","jvm","heap"]},{"name":"indices.memory.index_buffer_size","default":"10%","recommended":"10% (puede aumentarse con carga de indexación intensa)","unit":"% / bytes","dynamic":"restart","desc":"Define la parte del heap asignada al buffer de memoria que contiene los nuevos documentos durante la indexación. Aumentar este buffer puede mejorar el throughput de indexación en cargas de trabajo con mucha escritura. Se define por nodo en elasticsearch.yml y requiere un reinicio.","tags":["memory","memory","indexing","buffer"]},{"name":"thread_pool.write.queue_size","default":"10000","recommended":"10000 (aumentar con cuidado si se producen rechazos en la cola)","unit":"count","dynamic":"restart","desc":"Define la longitud de la cola del thread pool para las operaciones de escritura index, delete y bulk. Cuando la cola está llena, las solicitudes se rechazan. Elevar demasiado el valor puede causar presión de memoria. Se define en elasticsearch.yml y requiere un reinicio.","tags":["thread pool","write","queue","queue"]},{"name":"thread_pool.search.queue_size","default":"1000","recommended":"1000 (aumentar con cuidado si se producen rechazos de búsqueda)","unit":"count","dynamic":"restart","desc":"Define la longitud de la cola del thread pool para las operaciones de búsqueda. Cuando las búsquedas concurrentes superan la capacidad del pool, las solicitudes esperan en esta cola y se rechazan si se llena. Se define en elasticsearch.yml y requiere un reinicio.","tags":["thread pool","search","search","queue"]},{"name":"indices.fielddata.cache.size","default":"unbounded","recommended":"Un límite como 20-40% (para protección de memoria)","unit":"% / bytes","dynamic":"restart","desc":"Define el tamaño máximo de la caché de fielddata usada para ordenaciones y agregaciones en campos text. Como el valor predeterminado no tiene límite, se recomienda establecer uno para evitar el agotamiento de memoria. Se define en elasticsearch.yml y requiere un reinicio.","tags":["memory","memory","fielddata","cache"]},{"name":"index.number_of_shards","default":"1","recommended":"Según el volumen de datos; objetivo de 10-50GB por shard","unit":"count","dynamic":"restart","desc":"Define el número de shards primarios en que se divide un índice. Es un ajuste estático del índice (no requiere reinicio, pero solo se indica al crear el índice): no puede cambiarse directamente en un índice existente; se cambia generando un nuevo índice con un índice nuevo + _reindex, o con las API _split (multiplicar) / _shrink (reducir a un divisor). Demasiados shards aumentan la sobrecarga del clúster, mientras que muy pocos limitan el escalado.","tags":["index","index","shard","scaling"]},{"name":"index.number_of_replicas","default":"1","recommended":"Al menos 1 en producción (para alta disponibilidad)","unit":"count","dynamic":"dynamic","desc":"Define el número de shards réplica que se mantienen para cada shard primario. Las réplicas proporcionan tanto alta disponibilidad como rendimiento de lectura. Este ajuste puede cambiarse al instante en un índice activo con la API _settings.","tags":["index","index","replica","availability"]},{"name":"index.refresh_interval","default":"1s","recommended":"30s o -1 (temporalmente) con indexación intensa","unit":"duration","dynamic":"dynamic","desc":"Define con qué frecuencia se ejecuta la operación refresh para que los documentos recién indexados sean visibles para la búsqueda. Aumentar el valor, o establecerlo en -1 durante las cargas masivas, mejora notablemente el throughput de indexación. Puede cambiarse al instante con la API _settings.","tags":["index","index","refresh","indexing"]},{"name":"indices.breaker.total.limit","default":"95%","recommended":"95% (predeterminado; cambiar con cuidado)","unit":"%","dynamic":"dynamic","desc":"Evita que el nodo se caiga por OutOfMemory definiendo la parte máxima del heap que pueden usar en conjunto todos los circuit breakers. Puede cambiarse al instante en todo el clúster con la API _cluster/settings.","tags":["memory","memory","circuit breaker","stability"]},{"name":"cluster.routing.allocation.disk.watermark.low","default":"85%","recommended":"85% (el valor predeterminado es adecuado)","unit":"% / bytes","dynamic":"dynamic","desc":"Cuando el uso de disco de un nodo supera este umbral, Elasticsearch deja de asignar nuevos shards a ese nodo (los shards existentes permanecen). Es el primer nivel de advertencia para evitar un disco lleno. Puede cambiarse al instante con la API _cluster/settings.","tags":["disk","allocation","watermark","storage"]},{"name":"cluster.routing.allocation.disk.watermark.high","default":"90%","recommended":"90% (el valor predeterminado es adecuado)","unit":"% / bytes","dynamic":"dynamic","desc":"Cuando el uso de disco de un nodo supera este umbral, Elasticsearch empieza a reubicar shards de este nodo en otros nodos. Alivia los nodos cuyos discos se están llenando. Puede cambiarse al instante con la API _cluster/settings.","tags":["disk","allocation","watermark","storage"]},{"name":"cluster.routing.allocation.disk.watermark.flood_stage","default":"95%","recommended":"95% (el valor predeterminado es adecuado)","unit":"% / bytes","dynamic":"dynamic","desc":"Cuando el uso de disco supera este umbral crítico, los índices con un shard en el nodo afectado pasan al modo de solo lectura (read-only-allow-delete) y se bloquean las escrituras. Evita la corrupción de datos antes de que el disco se llene por completo; en 7.4+ el bloqueo se libera automáticamente cuando el uso de disco baja del high watermark. En 8.x, en discos grandes es decisivo max_headroom (predeterminado 100GB libres) en lugar del porcentaje. Puede cambiarse al instante con la API _cluster/settings.","tags":["disk","allocation","watermark","read-only"]},{"name":"index.translog.durability","default":"request","recommended":"request (durabilidad) o async (rendimiento)","unit":"mode","dynamic":"dynamic","desc":"Define cuándo el log de transacciones (translog) se sincroniza (fsync) en disco. request sincroniza tras cada solicitud y minimiza la pérdida de datos; async sincroniza periódicamente y mejora el rendimiento, pero conlleva riesgo de pérdida de datos ante una caída. Puede cambiarse al instante con la API _settings.","tags":["translog","durability","durability","indexing"]},{"name":"indices.recovery.max_bytes_per_sec","default":"40mb","recommended":"Puede aumentarse según la capacidad de red y disco","unit":"bytes/s","dynamic":"dynamic","desc":"Define la velocidad máxima de transferencia de datos por nodo durante la recuperación de shards. Aumentar el valor acelera la recuperación, pero puede consumir los recursos de red y disco de la carga de trabajo normal. Puede cambiarse al instante con la API _cluster/settings.","tags":["recovery","recovery","throttle","network"]},{"name":"action.destructive_requires_name","default":"true","recommended":"true (bloquea las operaciones destructivas con comodines)","unit":"true/false","dynamic":"dynamic","desc":"Bloquea operaciones destructivas como eliminar índices con un comodín (*) o _all, y exige indicar explícitamente el nombre del índice. Un ajuste de seguridad importante para evitar eliminar por accidente todos los índices. Puede cambiarse al instante con la API _cluster/settings.","tags":["security","security","destructive","safety"]},{"name":"discovery.seed_hosts","default":"(empty / 127.0.0.1)","recommended":"Direcciones de los nodos master-eligible del clúster","unit":"list","dynamic":"restart","desc":"Define la lista de direcciones de seed hosts (nodos master-eligible) con las que contacta un nodo para unirse al clúster. Necesario para el descubrimiento del clúster y para que los nodos se encuentren entre sí. Se define en elasticsearch.yml y requiere un reinicio.","tags":["discovery","cluster","cluster","network"]}]},"reference/waits-oracle.json":{"engineId":"oracle","engine":"Oracle","order":1,"waits":[{"name":"log file sync","kind":"wait","meaning":"La sesión emitió un COMMIT (o ROLLBACK) y espera a que el proceso LGWR escriba los registros de redo en los online redo log files y lo confirme.","causes":"I/O lenta del redo log, commits pequeños muy frecuentes, un proceso LGWR sobrecargado, LGWR retrasado por falta de CPU, redo logs en discos lentos.","action":"Reducir la frecuencia de commit (commits por lotes), mover los redo log files a discos rápidos/de baja latencia (SSD), eliminar commits innecesarios, considerar COMMIT NOWAIT/BATCH solo para trabajos donde la pérdida sea tolerable (ADVERTENCIA: reduce la durabilidad; las transacciones que parecen confirmadas pueden perderse en una caída) y medir la latencia de I/O del log writer (log file parallel write).","tags":["commit","redo","io","lgwr"]},{"name":"db file sequential read","kind":"wait","meaning":"La sesión realiza una lectura física de un solo bloque; espera a que el bloque se traiga del disco al buffer cache, normalmente durante un acceso por índice o una lectura de bloque de tabla por ROWID.","causes":"Index scans, planes de acceso ineficientes, un buffer cache pequeño, un subsistema de disco lento, bloques no encontrados en la caché por un alto volumen de lecturas físicas.","action":"Ajustar el SQL para reducir lecturas innecesarias, añadir índices adecuados, aumentar el tamaño del buffer cache, mejorar el subsistema de I/O e identificar las consultas que más leen con AWR/ASH.","tags":["io","index","single-block","read"]},{"name":"db file scattered read","kind":"wait","meaning":"La sesión realiza una lectura física de varios bloques; espera a que se lean varios bloques del disco, normalmente durante un full table scan o un fast full index scan.","causes":"Full table scans, índices ausentes o no usados, planes de consulta ineficientes, escaneos de tablas grandes, estadísticas del optimizador incorrectas.","action":"Evitar full scans innecesarios añadiendo índices, revisar los planes de consulta, actualizar las estadísticas, examinar el ajuste db_file_multiblock_read_count y considerar paralelizar los full scans realmente necesarios.","tags":["io","full-scan","multi-block","read"]},{"name":"direct path read","kind":"wait","meaning":"La sesión lee bloques directamente en la PGA, sin pasar por el buffer cache; suele verse en consultas paralelas, escaneos de tablas grandes o lecturas de LOB.","causes":"Full table scans paralelos, una decisión de direct path read en serie (adaptive direct read), escaneos de tablas grandes, lecturas del tablespace temporal (temp), acceso a LOB.","action":"Reducir la necesidad de escaneos grandes mediante el ajuste de consultas, evaluar el tamaño de la PGA, optimizar las operaciones de sort/hash si el uso de temp es alto y aumentar el ancho de banda de I/O.","tags":["io","direct-path","parallel","read"]},{"name":"direct path write","kind":"wait","meaning":"La sesión escribe bloques directamente en disco sin pasar por el buffer cache; suele verse en inserciones direct-path, volcados de ordenación o escrituras de LOB.","causes":"Operaciones INSERT direct-path (APPEND), volcados de sort/hash al tablespace temporal, DML paralelo, escrituras de LOB, discos temp o de datos lentos.","action":"Aumentar la PGA para las operaciones de sort/hash a fin de reducir los volcados a temp, colocar el tablespace temp en discos rápidos, revisar las cargas direct-path innecesarias y mejorar el rendimiento de I/O.","tags":["io","direct-path","temp","write"]},{"name":"buffer busy waits","kind":"wait","meaning":"La sesión quiere acceder a un bloque del buffer cache, pero otra sesión lo está usando en un modo incompatible, por lo que espera a que se libere.","causes":"Acceso concurrente intenso al mismo bloque (hot block), freelists/ASSM insuficientes, contención en el bloque del extremo derecho de un índice secuencial (right-hand index growth), segmentos con alta intensidad de DML.","action":"Repartir los hot blocks (hash partitioning, reverse key index), usar ASSM, aumentar freelists/freelist groups y mejorar el cacheo y la distribución de secuencias en tablas que generan claves secuenciales.","tags":["concurrency","buffer","hot-block","contention"]},{"name":"read by other session","kind":"wait","meaning":"Otra sesión ya está leyendo del disco el bloque que necesita esta sesión; esta sesión espera a que se complete esa lectura del mismo bloque.","causes":"Solicitudes de lectura física concurrentes para los mismos hot blocks, un buffer cache pequeño, I/O lenta, muchas sesiones accediendo al mismo tiempo a bloques populares de índices o tablas.","action":"Aumentar el buffer cache para reducir las lecturas físicas, identificar los segmentos calientes y mejorar sus planes de acceso, reducir la latencia de I/O y eliminar los full scans innecesarios.","tags":["io","concurrency","read","hot-block"]},{"name":"latch: cache buffers chains","kind":"wait","meaning":"La sesión espera para adquirir el latch que protege una cadena hash del buffer cache; se produce serialización al localizar bloques en el buffer cache.","causes":"SQL ineficiente que genera un I/O lógico excesivo, acceso concurrente intenso a hot blocks, acceso repetido a los mismos bloques mediante nested loops, bloques root/branch de índices muy populares.","action":"Ajustar el SQL para reducir las lecturas lógicas, eliminar los accesos nested loop innecesarios, repartir los hot blocks y encontrar y corregir con AWR/ASH las consultas que producen buffer gets excesivos.","tags":["latch","concurrency","logical-io","buffer"]},{"name":"cursor: pin S wait on X","kind":"wait","meaning":"La sesión quiere fijar (pin) un cursor con un mutex en modo compartido (S), pero espera porque otra sesión mantiene el mismo cursor en modo exclusivo (X).","causes":"Una tasa alta de hard/soft parse, SQL popular analizado con mucha frecuencia, consultas con literales que no usan bind variables, proliferación de child cursors, muchas sesiones analizando el mismo SQL de forma concurrente.","action":"Reducir la carga de parse usando bind variables, cachear los cursores en la aplicación (session_cached_cursors), reducir los re-parse innecesarios y corregir el SQL con un version count alto.","tags":["mutex","parse","cursor","concurrency"]},{"name":"library cache lock","kind":"wait","meaning":"La sesión espera para adquirir un bloqueo sobre el handle de un objeto del library cache (paquete, vista, definición de tabla); suele verse durante el acceso concurrente a la definición del objeto o sus cambios.","causes":"DDL sobre objetos en uso, paquetes/vistas que se recompilan constantemente, objetos inválidos, conexiones y parse concurrentes intensos, cambios de privilegios (grant).","action":"Ejecutar el DDL en horas de baja actividad, evitar compilaciones innecesarias, corregir los objetos inválidos y reducir los bucles excesivos de connect/parse en el lado de la aplicación.","tags":["library-cache","concurrency","ddl","parse"]},{"name":"library cache: mutex X","kind":"wait","meaning":"La sesión espera para adquirir un mutex que proteja un objeto del library cache en modo exclusivo (X); suele verse con mucho parse y contención en los handles de objetos.","causes":"Parse excesivo (hard/soft), gran variedad de SQL con literales sin binds, un número alto de child cursors, invalidación frecuente de objetos, muchas sesiones accediendo al mismo objeto de forma concurrente.","action":"Usar bind variables y cacheo de cursores para reducir el parse, estandarizar los literales SQL, corregir los problemas de version count alto y mejorar el comportamiento de conexión de la aplicación.","tags":["mutex","library-cache","parse","concurrency"]},{"name":"enq: TX - row lock contention","kind":"wait","meaning":"La sesión quiere modificar una fila bloqueada por otra transacción y espera a que esa transacción haga commit/rollback y libere el bloqueo de fila.","causes":"UPDATE/DELETE concurrentes de las mismas filas, transacciones de larga duración que no hacen commit, un flujo de trabajo de la aplicación mal diseñado, bloqueos de fila indirectos mediante claves únicas/foráneas, sesiones colgadas.","action":"Mantener las transacciones cortas y hacer commit con prontitud, rediseñar el patrón de acceso en contención, imponer un acceso ordenado con SELECT FOR UPDATE si es necesario, y encontrar y resolver la sesión bloqueante con ASH.","tags":["lock","transaction","row-lock","contention"]},{"name":"enq: TX - allocate ITL entry","kind":"wait","meaning":"La sesión necesita un slot ITL (Interested Transaction List) en un bloque para trabajar en él, pero tiene que esperar porque no queda ningún slot ITL libre en el bloque.","causes":"Un ajuste de INITRANS bajo, un tamaño de bloque pequeño, muchas transacciones concurrentes en el mismo bloque, bloques muy llenos que reciben DML intenso.","action":"Aumentar INITRANS de la tabla/índice, elevar PCTFREE para poner menos filas por bloque, repartir los hot blocks y reconstruir el objeto si es necesario.","tags":["lock","transaction","itl","block"]},{"name":"gc buffer busy acquire","kind":"wait","meaning":"En un entorno RAC, la sesión quiere obtener un bloque a través de la global cache, pero espera porque otra sesión de la misma instancia ya ha iniciado una solicitud global para el mismo bloque.","causes":"Compartición de hot blocks entre instancias en RAC, rendimiento insuficiente del interconnect, contención en el extremo derecho de un índice secuencial, una carga de trabajo mal particionada, tráfico intenso de global cache.","action":"Repartir la carga de trabajo de forma razonable entre instancias (afinidad de aplicación/services), repartir los segmentos calientes (reverse key/partición), mejorar la latencia del interconnect y analizar la contención gc con la sección RAC de AWR y las vistas GV$.","tags":["rac","global-cache","concurrency","interconnect"]},{"name":"gc cr block busy","kind":"wait","meaning":"En un entorno RAC, la sesión solicitó un bloque para lectura consistente (CR) a otra instancia y espera a que esa instancia prepare el bloque y lo envíe por el interconnect.","causes":"Compartición de hot blocks entre instancias, un interconnect lento o saturado, tráfico intenso de redo/commit, una carga de trabajo mal particionada, solicitudes CR excesivas a la global cache.","action":"Mejorar la afinidad de la carga de trabajo, optimizar el ancho de banda y la latencia del interconnect, reducir los hot blocks, revisar los patrones de commit y DML, y ajustar RAC.","tags":["rac","global-cache","cr","interconnect"]},{"name":"log file switch (checkpoint incomplete)","kind":"wait","meaning":"La sesión quiere cambiar a un nuevo redo log, pero el checkpoint del siguiente log file aún no ha terminado, por lo que LGWR no puede reutilizar ese log y se detiene toda la generación de redo.","causes":"Redo log files demasiado pequeños o escasos, I/O lenta de DBWR/checkpoint, una tasa alta de generación de redo, un número insuficiente de grupos de log.","action":"Ampliar los redo log files y añadir grupos, mejorar el rendimiento de I/O de DBWR/checkpoint, aplicar optimizaciones en la aplicación que reduzcan la generación de redo y monitorear la frecuencia de log switch.","tags":["redo","checkpoint","log-switch","io"]},{"name":"free buffer waits","kind":"wait","meaning":"La sesión busca un buffer limpio/libre en el buffer cache para leer un bloque nuevo, pero espera un buffer libre porque DBWR no puede escribir los buffers sucios en disco con suficiente rapidez.","causes":"I/O de escritura lenta de DBWR, un buffer cache pequeño, generación rápida de buffers sucios por DML intenso, velocidad de checkpoint insuficiente, un subsistema de disco saturado.","action":"Acelerar la I/O de escritura de los datafiles, aumentar el tamaño del buffer cache, evaluar el número de procesos DBWR (db_writer_processes) y revisar la intensidad de DML y los ajustes de checkpoint.","tags":["buffer","dbwr","io","checkpoint"]},{"name":"Buffer Cache Hit Ratio","kind":"metric","meaning":"La proporción de bloques solicitados que se sirven desde el buffer cache sin ir a disco; muestra qué porcentaje de las lecturas lógicas no requirió una lectura física.","causes":"Una proporción baja suele deberse a un buffer cache pequeño, full scans ineficientes, una caché fría o un alto volumen de lecturas físicas. Por sí sola puede ser engañosa; una proporción alta también puede ocultar SQL deficiente.","action":"No tome la proporción como objetivo por sí sola; evalúela junto con los wait events y las tendencias de lecturas físicas. Ajuste el SQL ineficiente, reduzca las lecturas lógicas innecesarias y dimensione la caché según la carga de trabajo.","tags":["metric","buffer-cache","ratio","memory"]},{"name":"Average Active Sessions","kind":"metric","meaning":"El número medio de sesiones activas al mismo tiempo (ejecutándose en CPU o esperando un evento) durante un intervalo; resume la carga general y la intensidad de los cuellos de botella de la base de datos.","causes":"Un AAS alto puede deberse a saturación de CPU, esperas de I/O, contención de bloqueos/latches o un aumento de la concurrencia. Los valores que se acercan al número de núcleos de CPU o lo superan apuntan a un cuello de botella de recursos.","action":"Comparar el AAS con el número de núcleos de CPU, encontrar la wait class dominante (CPU/IO/concurrencia) con ASH, ajustar el SQL y las sesiones que generan más carga, y resolver el problema de recursos o de concurrencia.","tags":["metric","load","ash","sessions"]}]},"reference/waits-postgres.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"waits":[{"name":"LWLock:WALWrite","kind":"wait","meaning":"La sesión espera el lightweight lock necesario para escribir los buffers de WAL en disco; normalmente se debe a la serialización de las escrituras de WAL durante el commit.","causes":"I/O lenta del disco de WAL, commits muy frecuentes/pequeños, alta carga de escritura, ancho de banda de escritura de WAL insuficiente, muchos backends escribiendo WAL al mismo tiempo.","action":"Mover el WAL a un disco rápido/de baja latencia, agrupar commits (evaluar synchronous_commit, commit_delay), aumentar wal_buffers, reducir las operaciones de escritura innecesarias.","tags":["wal","lwlock","commit","io"]},{"name":"LWLock:BufferContent","kind":"wait","meaning":"La sesión espera el lightweight lock de un buffer del shared buffer pool que protege el contenido de la página; indica contención por acceso concurrente a la página.","causes":"Lecturas/escrituras concurrentes intensas en la misma página (hot page), contención en el borde derecho de índices secuenciales, tablas pequeñas que reciben muchas actualizaciones, alta concurrencia.","action":"Repartir las hot pages (ajuste de fillfactor, particionamiento), reducir la contención de claves secuenciales, reestructurar las filas muy actualizadas, revisar el patrón de acceso concurrente.","tags":["lwlock","buffer","concurrency","contention"]},{"name":"Lock:transactionid","kind":"wait","meaning":"La sesión espera a que termine (commit/rollback) otra transacción que ha bloqueado la fila que quiere modificar; la espera de bloqueo es sobre el ID de la transacción objetivo.","causes":"UPDATE/DELETE concurrentes sobre las mismas filas, transacciones de larga duración que no hacen commit, sesiones idle-in-transaction, un flujo de trabajo con contención mal diseñado.","action":"Mantener las transacciones cortas y hacer commit con prontitud, limitar las sesiones idle-in-transaction (idle_in_transaction_session_timeout), rediseñar el patrón de acceso en contención, encontrar la consulta bloqueante con pg_stat_activity.","tags":["lock","transaction","row-lock","contention"]},{"name":"Lock:tuple","kind":"wait","meaning":"La sesión espera para adquirir un bloqueo sobre una tupla (fila) específica; se ve cuando varias sesiones hacen cola para actualizar/bloquear la misma fila.","causes":"SELECT FOR UPDATE/UPDATE concurrentes sobre la misma fila, contención en filas calientes (p. ej. filas contador), bloqueos de fila retenidos durante mucho tiempo, alta concurrencia.","action":"Repartir o encolar las actualizaciones de filas calientes, acortar la duración de las transacciones, rediseñar patrones con contención como los contadores (p. ej. actualizaciones por lotes), considerar FOR UPDATE SKIP LOCKED.","tags":["lock","tuple","row-lock","contention"]},{"name":"Lock:relation","kind":"wait","meaning":"La sesión espera para adquirir un bloqueo a nivel de tabla sobre una tabla/índice (relation); suele verse con contención entre modos de bloqueo incompatibles de DDL y DML.","causes":"DDL como ALTER/DROP/TRUNCATE mientras se ejecutan consultas, VACUUM FULL que requiere un bloqueo agresivo, acceso concurrente a la misma tabla en modos incompatibles, consultas de larga duración.","action":"Ejecutar el DDL en periodos tranquilos con un lock_timeout corto, usar el autovacuum regular en lugar de VACUUM FULL, monitorear las consultas largas, revisar las operaciones que serializan el acceso a la tabla.","tags":["lock","relation","ddl","table-lock"]},{"name":"IO:DataFileRead","kind":"wait","meaning":"La sesión espera a que una página de un archivo de datos se lea del disco en shared buffers; es la espera de lectura física que se produce cuando la página solicitada no está en caché.","causes":"shared_buffers pequeño, planes de consulta ineficientes y escaneos grandes, una caché fría, un subsistema de disco lento, uso insuficiente de índices.","action":"Ajustar shared_buffers y la page cache del SO a la carga de trabajo, corregir las consultas ineficientes y los índices ausentes, mejorar el rendimiento de I/O, configurar correctamente effective_cache_size.","tags":["io","read","buffer","disk"]},{"name":"IO:WALSync","kind":"wait","meaning":"La sesión espera a que el WAL se escriba de forma duradera en disco (fsync/sincronización); para la durabilidad del commit espera a que el WAL se vacíe físicamente.","causes":"Latencia de fsync del almacenamiento lenta, commits muy frecuentes, un disco de WAL de bajo rendimiento, un disco sin caché write-back, alta carga de escritura.","action":"Mover el disco de WAL a un almacenamiento rápido con baja latencia de fsync, agrupar commits (commit_delay/commit_siblings), evaluar synchronous_commit frente a los requisitos del negocio, mejorar la capa de almacenamiento.","tags":["wal","io","fsync","commit"]},{"name":"IO:DataFileWrite","kind":"wait","meaning":"La sesión espera a que una página de datos sucia se escriba en disco; suele verse cuando el propio backend escribe para encontrar una página limpia en el buffer pool.","causes":"I/O de escritura lenta en el disco de datos, congestión de checkpoint, un background writer insuficiente, un buffer pool pequeño, el propio backend escribiendo debido a una alta carga de escritura.","action":"Suavizar los checkpoints (checkpoint_completion_target, max_wal_size), ajustar el background writer (bgwriter_lru_maxpages), mejorar el rendimiento de escritura en disco, revisar shared_buffers.","tags":["io","write","checkpoint","bgwriter"]},{"name":"Client:ClientRead","kind":"wait","meaning":"El backend espera el siguiente comando/datos del cliente; se ve cuando el servidor está inactivo, esperando a que el cliente envíe datos.","causes":"Una red lenta, sesiones idle-in-transaction, la aplicación cliente enviando/recibiendo datos lentamente, grandes conjuntos de resultados consumidos con lentitud, problemas del pool de conexiones.","action":"Examinar la latencia del lado de la aplicación y de la red, limitar las sesiones idle-in-transaction, consumir los resultados de forma eficiente (p. ej. cursor/lotes), mejorar el pool de conexiones. Esta espera normalmente no es un cuello de botella de la base de datos.","tags":["client","network","idle","application"]},{"name":"IPC:ProcArrayGroupUpdate","kind":"wait","meaning":"La sesión espera a que el backend líder del grupo marque en grupo el final de su transacción en el ProcArray; es la serialización de las actualizaciones del ProcArray durante el commit.","causes":"Tasa de commit/transacciones muy alta, exceso de transacciones cortas concurrentes, número alto de conexiones, acceso intenso a snapshot/ProcArray.","action":"Gestionar la tasa de transacciones y el número de conexiones concurrentes (pool de conexiones), reducir las transacciones cortas innecesarias, limitar el número de conexiones con concurrencia muy alta, equilibrar la carga de trabajo.","tags":["ipc","commit","procarray","concurrency"]},{"name":"LWLock:LockManager","kind":"wait","meaning":"La sesión espera el lightweight lock del lock manager que protege la tabla de heavyweight locks; se debe a la serialización de las operaciones de adquisición/liberación de bloqueos.","causes":"Consultas que tocan muchas tablas/particiones (superando el límite de bloqueos fast-path), alta concurrencia, tablas sobreparticionadas, una carga de trabajo que adquiere/libera muchos bloqueos.","action":"Reducir el número de particiones/tablas tocadas por consulta (partition pruning), revisar el sobreparticionamiento, gestionar la concurrencia con un pool de conexiones, simplificar las operaciones que toman bloqueos innecesarios.","tags":["lwlock","lock-manager","concurrency","partitioning"]},{"name":"Timeout:VacuumDelay","kind":"wait","meaning":"El proceso vacuum/autovacuum se pausa deliberadamente por el cost-based vacuum delay; una espera intencionada para limitar la carga de I/O.","causes":"Limitación debida a los ajustes vacuum_cost_delay/autovacuum_vacuum_cost_delay, un cost_limit bajo, vacuum limpiando muchas tablas, ajustes de autovacuum poco agresivos.","action":"Si vacuum es demasiado lento y se acumulan bloat/tuplas muertas, aumentar cost_limit y reducir cost_delay, ajustar el número de workers y los umbrales de autovacuum, equilibrar frente a la capacidad de I/O.","tags":["vacuum","timeout","throttle","maintenance"]},{"name":"tasa de aciertos de caché (cache hit ratio)","kind":"metric","meaning":"La proporción de solicitudes de bloques que se sirven desde shared buffers sin ir a disco; se calcula a partir de blks_hit y blks_read en pg_stat_database.","causes":"Una proporción baja suele indicar shared_buffers pequeño, escaneos grandes ineficientes, una caché fría o memoria insuficiente. Como la page cache del SO también atiende lecturas, la proporción por sí sola puede ser engañosa.","action":"Interpretar la proporción junto con los wait events y las tendencias de I/O, ajustar shared_buffers y effective_cache_size, corregir las consultas ineficientes; si es persistentemente baja, examinar la memoria y los planes de consulta.","tags":["metric","cache","ratio","memory"]},{"name":"retraso de replicación (replication lag)","kind":"metric","meaning":"Cuánto se retrasa un servidor standby/réplica al aplicar el WAL generado por el primario; se mide en bytes (diferencia de LSN) o en segundos de retraso.","causes":"Una red lenta, carga de consultas intensa en el standby (especialmente conflictos de replay), I/O de disco lenta en el standby, una tasa de escritura alta en el primario, consultas de lectura de larga duración que retrasan la recuperación.","action":"Monitorear el retraso de write/flush/replay con pg_stat_replication, mejorar el rendimiento de red y de I/O del standby, gestionar las consultas largas en conflicto (hot_standby_feedback/max_standby_streaming_delay), revisar la necesidad de replicación síncrona.","tags":["metric","replication","lag","standby"]}]},"reference/waits-mysql.json":{"engineId":"mysql","engine":"MySQL","order":3,"waits":[{"name":"wait/synch/mutex/innodb/buf_pool_LRU_list_mutex","kind":"wait","meaning":"El thread espera el mutex que protege la lista LRU del buffer pool de InnoDB; indica serialización durante la lectura/desalojo de páginas. En 8.0 se dividió el antiguo buf_pool_mutex único; los otros instrumentos relacionados son buf_pool_free_list_mutex, buf_pool_flush_state_mutex y buf_pool_zip_mutex.","causes":"Acceso intenso al buffer pool con alta concurrencia, una sola instancia de buffer pool, lectura/desalojo frecuente de páginas, un buffer pool subdimensionado.","action":"Dividir el mutex aumentando innodb_buffer_pool_instances, dimensionar innodb_buffer_pool_size según el working set, ajustar las consultas para reducir las lecturas físicas y gestionar la concurrencia.","tags":["innodb","mutex","buffer-pool","concurrency"]},{"name":"wait/io/file/innodb/innodb_data_file","kind":"wait","meaning":"El thread espera a que se complete la I/O física de archivo (lectura/escritura) sobre los archivos de datos de InnoDB (ibd/ibdata).","causes":"Un subsistema de disco lento, muchas lecturas físicas por un buffer pool pequeño, escaneos de tablas grandes, presión de checkpoint/flush, capacidad de I/O insuficiente.","action":"Ampliar el buffer pool, corregir las consultas ineficientes y los índices ausentes, pasar a un almacenamiento rápido (SSD/NVMe), ajustar innodb_io_capacity al almacenamiento y revisar el comportamiento de flush.","tags":["innodb","io","datafile","disk"]},{"name":"wait/lock/table/sql/handler","kind":"wait","meaning":"El thread espera para adquirir el handler lock a nivel de tabla (bloqueo de tabla) en la capa SQL; se ve con acceso concurrente a una tabla en modos incompatibles.","causes":"Operaciones que requieren un bloqueo de tabla, motores de almacenamiento con bloqueo a nivel de tabla como MyISAM, sentencias LOCK TABLES, contención entre DDL y DML, consultas de larga duración.","action":"Usar InnoDB con bloqueo a nivel de fila, reducir el uso de LOCK TABLES, ejecutar el DDL en periodos de baja actividad, optimizar las consultas largas y revisar los patrones que serializan el acceso a la tabla.","tags":["lock","table-lock","sql-layer","contention"]},{"name":"wait/synch/mutex/sql/LOCK_open","kind":"wait","meaning":"El thread espera el mutex LOCK_open, que protege las definiciones de tablas abiertas/el diccionario de objetos; es la serialización de las operaciones de apertura/cierre de tablas.","causes":"Acceso constante a muchas tablas diferentes, un table_open_cache/table_definition_cache pequeño, apertura y cierre frecuentes de tablas, alta concurrencia, demasiadas tablas/particiones.","action":"Aumentar table_open_cache y table_open_cache_instances, ajustar table_definition_cache, revisar un número excesivo de tablas/particiones y reducir las aperturas de tablas innecesariamente frecuentes.","tags":["mutex","table-cache","sql-layer","concurrency"]},{"name":"esperas de bloqueo de fila (row lock waits)","kind":"wait","meaning":"Una transacción espera a que se libere un bloqueo de fila de InnoDB que mantiene otra transacción; indica contención de bloqueos a nivel de fila.","causes":"UPDATE/DELETE concurrentes sobre las mismas filas, transacciones de larga duración que no hacen commit, rangos de bloqueo amplios (gap locks) por índices ausentes, contención en filas calientes, ciclos de deadlock.","action":"Mantener las transacciones cortas y hacer commit con prontitud, acotar el rango de filas bloqueadas añadiendo un índice adecuado, rediseñar los patrones en contención, monitorear los deadlocks (SHOW ENGINE INNODB STATUS) y revisar el nivel de aislamiento.","tags":["innodb","row-lock","transaction","contention"]},{"name":"log file waits","kind":"wait","meaning":"El thread espera a que se libere espacio en el redo log buffer de InnoDB o a que el redo log se escriba en disco; suele verse con presión de flush del log buffer/checkpoint.","causes":"Un redo log pequeño (8.0.30+ innodb_redo_log_capacity; antes innodb_log_file_size × innodb_log_files_in_group), un disco de log lento, una tasa de escritura alta, flushes frecuentes (innodb_flush_log_at_trx_commit=1 con commits frecuentes), un log buffer insuficiente.","action":"Aumentar la capacidad de redo (8.0.30+ innodb_redo_log_capacity, dinámico; antes innodb_log_file_size, requiere reinicio) e innodb_log_buffer_size, mover el redo log a un disco rápido, revisar el patrón de commit y evaluar el ajuste de flush frente a los requisitos de durabilidad.","tags":["innodb","redo","log","io"]},{"name":"wait/io/file/innodb/innodb_log_file","kind":"wait","meaning":"El thread espera a que se complete la I/O física (escritura y fsync) sobre los redo log files de InnoDB; espera a que el redo llegue a disco para la durabilidad del commit.","causes":"Latencia de fsync del almacenamiento lenta, commits muy frecuentes con innodb_flush_log_at_trx_commit=1, un disco de log de bajo rendimiento, una alta carga de escritura.","action":"Mover el disco del redo log a un almacenamiento rápido de baja latencia, agrupar commits cuando sea posible, evaluar innodb_flush_log_at_trx_commit frente a los requisitos del negocio y mejorar la capa de almacenamiento.","tags":["innodb","redo","fsync","commit"]},{"name":"wait/synch/mutex/innodb/log_writer_mutex","kind":"wait","meaning":"El thread espera el mutex que protege las estructuras del thread log writer del redo log de InnoDB. El subsistema de redo se rediseñó en 8.0; en lugar del antiguo log_sys_mutex único hay instrumentos separados como log_writer_mutex, log_flusher_mutex y log_checkpointer_mutex.","causes":"Una tasa de escritura/commit muy alta, generación intensa de redo, contención en los threads log writer/flusher, un disco de log lento, un log buffer pequeño.","action":"Equilibrar la carga de escritura y la frecuencia de commit, considerar operaciones por lotes que reduzcan la generación de redo, ajustar el log buffer y aprovechar las mejoras de escalabilidad del redo log de su versión de MySQL.","tags":["innodb","mutex","redo","concurrency"]},{"name":"Innodb_buffer_pool_wait_free","kind":"metric","meaning":"El número de veces que un thread no encontró ninguna página libre en el buffer pool y tuvo que esperar a que se vaciaran páginas sucias para obtener una página libre; un aumento constante indica presión en el buffer pool.","causes":"Un buffer pool pequeño, I/O de flush/escritura lenta, un innodb_io_capacity bajo, generación rápida de páginas sucias con alta carga de escritura, retraso de checkpoint.","action":"Aumentar innodb_buffer_pool_size, ajustar innodb_io_capacity/io_capacity_max al almacenamiento, mejorar el rendimiento de I/O de escritura y revisar la proporción de páginas sucias (innodb_max_dirty_pages_pct).","tags":["metric","innodb","buffer-pool","flush"]},{"name":"Threads_running","kind":"metric","meaning":"El número de threads que no están inactivos y ejecutan consultas activamente en ese momento; muestra la ejecución concurrente instantánea en el servidor y la probable intensidad de la contención.","causes":"Un pico repentino de consultas, acumulación de consultas lentas, threads detenidos por contención de bloqueos/mutex, saturación de CPU, un efecto thundering herd.","action":"Cuando Threads_running se dispara, encontrar las consultas lentas/bloqueadas (SHOW PROCESSLIST), ajustar las consultas, limitar la concurrencia con un pool de conexiones y un thread pool, y eliminar la fuente de contención.","tags":["metric","concurrency","threads","load"]},{"name":"Innodb_row_lock_time_avg","kind":"metric","meaning":"El tiempo medio (milisegundos) de espera para adquirir un bloqueo de fila; muestra la gravedad de la contención de bloqueos a nivel de fila.","causes":"Acceso concurrente intenso a las mismas filas, transacciones largas, rangos de bloqueo amplios por índices ausentes, esperas que se acercan al deadlock/lock wait timeout.","action":"Acortar la duración de las transacciones, acotar el alcance de los bloqueos con índices adecuados, rediseñar el patrón de acceso en contención y revisar innodb_lock_wait_timeout y el nivel de aislamiento.","tags":["metric","innodb","row-lock","contention"]},{"name":"history list length","kind":"metric","meaning":"El número de transacciones en el undo log de InnoDB que aún no se han purgado (conservan versiones antiguas de filas); muestra la acumulación de versiones antiguas mantenidas para MVCC.","causes":"Transacciones abiertas de larga duración (especialmente transacciones de lectura largas), un purge thread lento, una tasa de DML alta, transacciones sin commit/inactivas.","action":"Identificar y terminar las transacciones de larga duración e inactivas, ajustar el número de purge threads (innodb_purge_threads) y mantener las transacciones cortas en la aplicación; un HLL alto provoca problemas de rendimiento y bloat.","tags":["metric","innodb","undo","mvcc"]},{"name":"Innodb_data_reads","kind":"metric","meaning":"El número total de solicitudes de lectura física que InnoDB realizó desde disco; muestra la carga de lectura que el buffer pool no pudo atender y que fue a disco.","causes":"Un buffer pool pequeño, una caché fría, consultas ineficientes y escaneos grandes, índices ausentes, un working set mayor que la memoria.","action":"Aumentar el tamaño del buffer pool, corregir las consultas ineficientes y los índices ausentes, monitorear la tendencia de lecturas físicas junto con el hit ratio del buffer pool y mejorar el rendimiento de I/O.","tags":["metric","innodb","io","read"]},{"name":"Innodb_buffer_pool_read_requests","kind":"metric","meaning":"El número total de solicitudes de lectura lógica atendidas desde el buffer pool de InnoDB; se usa junto con Innodb_buffer_pool_reads (el número de páginas no encontradas en el buffer pool y leídas desde disco) para calcular el hit ratio del buffer pool.","causes":"Un valor alto indica una actividad de consultas intensa. Si la proporción de Innodb_buffer_pool_reads respecto a este valor es alta (hit ratio bajo), hay escasez de memoria o un acceso ineficiente.","action":"Monitorear el hit ratio del buffer pool (1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests); si es bajo, ampliar el buffer pool y optimizar las consultas, y ajustar las consultas ineficientes que generan lecturas lógicas excesivas.","tags":["metric","innodb","buffer-pool","logical-io"]}]},"reference/waits-mssql.json":{"engineId":"mssql","engine":"SQL Server","order":4,"waits":[{"name":"CXPACKET","kind":"wait","meaning":"Espera que se produce mientras los threads se sincronizan durante la ejecución de consultas en paralelo; se ve mientras está pendiente la distribución/coordinación del trabajo entre los operadores del plan paralelo.","causes":"Un grado de paralelismo alto, distribución desigual del trabajo (skew), MAXDOP ilimitado/alto (0) y un cost threshold for parallelism bajo (predeterminado 5), grandes escaneos paralelos por índices ausentes, planes ineficientes.","action":"Ajustar cost threshold for parallelism y MAXDOP a la carga de trabajo, corregir los índices ausentes y las estadísticas, y no tratar CXPACKET como un problema por sí solo; separar la espera real usando CXCONSUMER y centrarse en los planes paralelos ineficientes.","tags":["parallelism","cxpacket","query","cpu"]},{"name":"CXCONSUMER","kind":"wait","meaning":"En un plan paralelo, el thread consumidor que espera a que lleguen datos de los threads productores; normalmente una espera de paralelismo inofensiva.","causes":"El thread consumidor esperando durante la ejecución paralela normal, flujo de datos desigual, retrasos en el lado productor, planes paralelos ineficientes.","action":"CXCONSUMER es mayormente benigno y no requiere acción por sí solo; para encontrar el cuello de botella real, céntrese en los wait types subyacentes del lado productor (IO, LCK, etc.) y en la eficiencia del plan.","tags":["parallelism","cxconsumer","query","benign"]},{"name":"PAGEIOLATCH_SH","kind":"wait","meaning":"El thread espera un latch para proteger una página de datos en modo compartido (SH) mientras se lee del disco al buffer pool; una espera de I/O física para lecturas.","causes":"I/O de disco lenta, memoria insuficiente (buffer pool pequeño), escaneos grandes e índices ausentes, una caché fría, carga intensa de lecturas físicas.","action":"Evaluar la memoria (max server memory) y el buffer pool, corregir las consultas ineficientes y los índices ausentes, mejorar la latencia de I/O del almacenamiento, e identificar y ajustar las consultas que más leen.","tags":["io","pageiolatch","read","buffer-pool"]},{"name":"PAGEIOLATCH_EX","kind":"wait","meaning":"El thread espera un latch para proteger en modo exclusivo (EX) una página de datos que se está leyendo del disco; normalmente espera la I/O física de una página que se leerá para modificarla.","causes":"I/O de disco lenta, una carga de escritura intensa, memoria insuficiente, presión de checkpoint/lazy writer, operaciones que realizan grandes cambios de datos.","action":"Mejorar el rendimiento de escritura/lectura del almacenamiento, aumentar la memoria del buffer pool, hacer que las operaciones con mucha escritura sean por lotes y eficientes, y separar y medir la I/O de tempdb y del disco de datos.","tags":["io","pageiolatch","write","buffer-pool"]},{"name":"PAGELATCH_UP","kind":"wait","meaning":"El thread espera un latch para proteger en modo update (UP) una página que ya está en memoria; no es I/O física sino contención por acceso concurrente a una página en memoria.","causes":"Contención en páginas calientes en memoria, especialmente contención en las páginas de sistema de tempdb (PFS/GAM/SGAM), creación concurrente intensa de objetos en tempdb, contención de inserción en la última página (hot page).","action":"Dividir tempdb en archivos de datos de igual tamaño según el número de núcleos, reducir la contención en tempdb, repartir los patrones de hot page y eliminar el uso innecesario de tempdb (spills, etc.).","tags":["pagelatch","tempdb","concurrency","contention"]},{"name":"PAGELATCH_EX","kind":"wait","meaning":"El thread espera un latch para proteger en modo exclusivo (EX) una página en memoria; suele verse con contención de inserción en la última página en tablas con claves ascendentes (secuenciales).","causes":"Contención de inserción en la última página en índices con claves secuenciales/ascendentes, contención en las páginas de sistema de tempdb, escrituras concurrentes intensas en hot pages, una tasa de inserción alta.","action":"Para la contención en la última página, usar OPTIMIZE_FOR_SEQUENTIAL_KEY = ON (SQL Server 2019+) o distribución de claves (hash/GUID/índice inverso), dividir tempdb en varios archivos y rediseñar el patrón de inserción intensa.","tags":["pagelatch","last-page-insert","concurrency","contention"]},{"name":"LCK_M_X","kind":"wait","meaning":"El thread espera para adquirir un bloqueo exclusivo sobre un recurso (fila/página/tabla); normalmente espera a que se liberen otros bloqueos para modificar datos (UPDATE/DELETE/INSERT).","causes":"Transacciones de larga duración, cambios concurrentes en los mismos recursos, lock escalation, alcance de bloqueo amplio por índices ausentes, sesiones bloqueantes.","action":"Mantener las transacciones cortas y hacer commit con prontitud, acotar el alcance de los bloqueos con índices adecuados, encontrar la consulta bloqueante (sys.dm_exec_requests/blocking), revisar la lock escalation y el nivel de aislamiento, y considerar RCSI si es necesario.","tags":["lock","exclusive","blocking","transaction"]},{"name":"LCK_M_U","kind":"wait","meaning":"El thread espera para adquirir un update lock sobre un recurso; mientras un UPDATE escanea, leyendo una fila y preparándose para modificarla, espera a que se liberen otros bloqueos.","causes":"Actualizaciones concurrentes de las mismas filas, transacciones largas, muchas filas escaneadas y bloqueadas por índices ausentes, sesiones bloqueantes, contención de bloqueos.","action":"Añadir índices adecuados para las sentencias UPDATE a fin de reducir el número de filas escaneadas/bloqueadas, acortar la duración de las transacciones, analizar la cadena de bloqueo y evaluar el nivel de aislamiento y las opciones de RCSI.","tags":["lock","update-lock","blocking","transaction"]},{"name":"WRITELOG","kind":"wait","meaning":"El thread espera a que los registros del log de transacciones se escriban en disco (log flush); normalmente espera en el momento del commit a que el log sea duradero.","causes":"I/O lenta del disco de log, commits muy frecuentes/pequeños, alta carga de escritura, contención entre el disco de log y el disco de datos, el efecto de la replicación síncrona de Always On.","action":"Mover el log de transacciones a un disco rápido de baja latencia, agrupar commits por lotes, reducir los commits innecesariamente frecuentes, considerar delayed durability (si procede) y medir la latencia de I/O del log.","tags":["log","writelog","commit","io"]},{"name":"ASYNC_NETWORK_IO","kind":"wait","meaning":"SQL Server está listo para enviar resultados, pero espera en el lado de la red/cliente porque el cliente no consume los datos con suficiente rapidez; normalmente un cuello de botella del lado de la aplicación.","causes":"La aplicación cliente procesando los resultados con lentitud (consumo fila a fila), grandes conjuntos de resultados, una red lenta, procesamiento RBAR en la aplicación, recursos insuficientes en el cliente.","action":"Consumir los resultados con rapidez y en bloque en la aplicación, reducir los conjuntos de resultados innecesariamente grandes (solo las columnas/filas necesarias) y comprobar la latencia de red. Normalmente es un problema del cliente/red, no de SQL Server.","tags":["network","client","application","async"]},{"name":"SOS_SCHEDULER_YIELD","kind":"wait","meaning":"Espera que se produce cuando un thread agota su porción de tiempo de CPU (quantum) y cede voluntariamente el scheduler; en general indica presión de CPU o procesamiento intensivo en CPU.","causes":"Consultas intensivas limitadas por CPU, alta concurrencia y saturación de CPU, contención de spinlocks, consultas ineficientes que realizan muchas lecturas lógicas.","action":"Encontrar y ajustar las consultas ineficientes que consumen CPU (índice ausente, mal plan), reducir las lecturas lógicas, evaluar la capacidad de CPU y la concurrencia y, si sigue alto, investigar el cuello de botella de hardware/plan.","tags":["cpu","scheduler","cpu-bound","yield"]},{"name":"RESOURCE_SEMAPHORE","kind":"wait","meaning":"La consulta espera el memory grant que necesita para ejecutarse; la ejecución no puede iniciarse hasta que haya suficiente memoria de consultas libre.","causes":"Consultas que solicitan memoria excesiva (grandes sorts/hashes), estimaciones de cardinalidad erróneas (infladas), memoria del servidor insuficiente, muchas consultas concurrentes intensivas en memoria, estadísticas obsoletas.","action":"Actualizar las estadísticas para corregir las estimaciones excesivas de memory grant, optimizar las consultas que necesitan grandes sorts/hashes (índice, filtro), revisar los ajustes de Resource Governor y max server memory, y limitar la carga concurrente intensiva en memoria.","tags":["memory","memory-grant","query","resource"]},{"name":"THREADPOOL","kind":"wait","meaning":"Tareas que esperan un thread libre del thread pool porque no hay ningún worker thread disponible para ejecutar una nueva tarea; un wait type crítico que indica un agotamiento grave de worker threads.","causes":"Threads retenidos por bloqueos largos, un número muy alto de conexiones concurrentes, max worker threads insuficiente, paralelismo intenso, deadlocks/cadenas de bloqueo largas.","action":"Resolver con urgencia la cadena de bloqueo, gestionar las conexiones concurrentes con un pool de conexiones, eliminar los bloqueos retenidos durante mucho tiempo, revisar el ajuste max worker threads; conectarse mediante la DAC para encontrar la causa raíz.","tags":["threadpool","worker-thread","blocking","critical"]},{"name":"HADR_SYNC_COMMIT","kind":"wait","meaning":"En una réplica síncrona de Always On, el primario espera a que el registro de log se consolide (harden) en la réplica secundaria antes de confirmar la transacción; latencia de commit síncrono.","causes":"Una red lenta/de alta latencia hacia la réplica secundaria, I/O lenta del disco de log en la secundaria, una secundaria sobrecargada, una réplica geográficamente remota, una tasa de escritura alta.","action":"Mejorar la latencia y el ancho de banda de red entre réplicas, aumentar el rendimiento del disco de log de la secundaria, equilibrar la carga de la secundaria y, si la latencia es crítica, revisar la topología de réplicas síncronas/asíncronas.","tags":["alwayson","replication","sync-commit","hadr"]},{"name":"Page Life Expectancy","kind":"metric","meaning":"Métrica que muestra el número medio de segundos que una página de datos permanece en el buffer pool (sin ser desalojada de la memoria); un indicador importante de presión de memoria.","causes":"Un PLE bajo o que cae rápidamente indica presión de memoria, escaneos grandes ineficientes, un working set mayor que la memoria o memoria insuficiente. Los nodos NUMA también deben evaluarse por separado.","action":"Observar la tendencia del PLE y las caídas repentinas en lugar de un umbral fijo, evaluar la memoria del buffer pool (max server memory), ajustar las consultas ineficientes que consumen mucha memoria y monitorear el PLE por nodo NUMA.","tags":["metric","memory","buffer-pool","ple"]},{"name":"Tasa de aciertos del buffer cache","kind":"metric","meaning":"La proporción de solicitudes de páginas de datos satisfechas desde el buffer cache sin ir a disco; muestra el porcentaje de páginas leídas desde memoria.","causes":"En los sistemas modernos esta proporción suele ser muy alta (gracias al read-ahead), y un valor bajo indica una presión de memoria significativa. Una proporción alta por sí sola no garantiza un buen rendimiento.","action":"Interpretar esta métrica no de forma aislada sino junto con el PLE, las esperas PAGEIOLATCH y las tendencias de lecturas físicas; si hay signos de presión de memoria, aumentar la memoria del buffer pool y corregir las consultas ineficientes.","tags":["metric","memory","buffer-pool","ratio"]}]},"reference/waits-mongodb.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"waits":[{"name":"% de caché de WiredTiger usada","kind":"metric","meaning":"Cuánto de la caché interna asignada al motor de almacenamiento WiredTiger está en uso; muestra la presión de mantener los datos en memoria (el umbral objetivo predeterminado ronda el 80%).","causes":"Un working set mayor que la caché, una caché subdimensionada, escaneos grandes, lecturas/escrituras intensas, muchas transacciones abiertas.","action":"Evaluar el tamaño de la caché de WiredTiger (storage.wiredTiger.engineConfig.cacheSizeGB) y la RAM, añadir índices que reduzcan el working set, y monitorear la presión de desalojo y la latencia a medida que el uso se acerca al objetivo del 80% y al umbral de desalojo del 95%.","tags":["metric","wiredtiger","cache","memory"]},{"name":"% de caché de WiredTiger sucia","kind":"metric","meaning":"El porcentaje de datos de la caché de WiredTiger que aún no se han escrito en disco (sucios); muestra la presión de escritura y la carga de checkpoint/desalojo (el umbral de desalojo de datos sucios suele rondar el 20%).","causes":"Alta carga de escritura, I/O de disco lenta, escrituras grandes/de larga duración, presión de checkpoint, la caché sin poder seguir el ritmo de la tasa de escritura.","action":"Si el porcentaje de datos sucios se acerca al umbral de desalojo (20%), mejorar el rendimiento de escritura en disco, equilibrar la carga de escritura, evaluar el tamaño de la caché y revisar las escrituras largas y el intervalo de checkpoint.","tags":["metric","wiredtiger","cache","write"]},{"name":"tickets disponibles (lectura/escritura)","kind":"metric","meaning":"El número restante de tickets de concurrencia que WiredTiger concede para operaciones de lectura y escritura concurrentes; si llega a cero, las nuevas operaciones hacen cola esperando un ticket.","causes":"Operaciones lentas que retienen tickets durante mucho tiempo, un cuello de botella de I/O de disco, bloqueos/contención, picos de carga repentinos, tickets agotados por consultas de larga duración.","action":"Si los tickets disponibles se acercan a cero y se forma una cola, optimizar las consultas lentas (añadir índices), mejorar la I/O de disco, equilibrar la carga; el agotamiento de tickets suele ser síntoma de un cuello de botella más profundo de I/O/contención.","tags":["metric","wiredtiger","concurrency","tickets"]},{"name":"cola de bloqueo global (global lock queue)","kind":"metric","meaning":"La longitud de la cola de operaciones de lectura (readers) y escritura (writers) que esperan un bloqueo global/de base de datos/de colección; muestra el número de operaciones en espera por contención de bloqueos.","causes":"Bloqueos retenidos durante mucho tiempo, contención de escritura intensa, operaciones de larga duración, operaciones DDL y el breve bloqueo exclusivo de colección que se toma al inicio/final de una construcción de índice (builds híbridos en 4.2+; los builds en foreground ya no existen), bloqueos retenidos mucho tiempo por I/O lenta.","action":"Si la cola está persistentemente llena, encontrar y optimizar las operaciones de larga duración (currentOp), programar las construcciones de índices y otros DDL fuera de las horas pico, mejorar la I/O y revisar los patrones de escritura en contención.","tags":["metric","lock","queue","concurrency"]},{"name":"retraso de replicación (secundario)","kind":"metric","meaning":"Cuánto (en segundos) se retrasa un miembro secundario al aplicar el oplog del primario; muestra el retraso de datos entre los miembros del replica set.","causes":"Red lenta, I/O de disco lenta en el secundario, carga de lectura intensa en el secundario, una tasa de escritura alta en el primario, hardware insuficiente en el secundario, operaciones de larga duración.","action":"Monitorear el retraso con rs.printSecondaryReplicationInfo, mejorar el rendimiento de red y de I/O del secundario, equilibrar la carga del secundario, mantener un tamaño de oplog suficiente y revisar el requisito de write concern.","tags":["metric","replication","lag","secondary"]},{"name":"ventana del oplog (oplog window)","kind":"metric","meaning":"El intervalo de tiempo (horas) entre las operaciones más antiguas y más recientes que contiene el oplog; muestra cuánto puede retrasarse un secundario sin perder la sincronización.","causes":"Un tamaño de oplog pequeño, una tasa de escritura alta repentina, secundarios fuera de línea durante mucho tiempo, aumento de la carga de replicación.","action":"Si la ventana del oplog se ha reducido, aumentar el tamaño del oplog, mantener el retraso del secundario por debajo de la ventana y revisar la carga de escritura y los requisitos de backup/sincronización; si se supera la ventana, se requiere un resync completo.","tags":["metric","replication","oplog","window"]},{"name":"fallos de página (page faults)","kind":"metric","meaning":"El número de fallos de página (especialmente hard faults) en los que el sistema operativo debe traer desde disco páginas de datos que no están en memoria; muestra datos que se desbordan de la memoria a disco.","causes":"Un working set mayor que la RAM, memoria insuficiente, escaneos grandes, presión de caché, consumo de memoria por otros procesos.","action":"Evaluar la memoria del servidor/el working set, reducir los datos escaneados añadiendo índices, equilibrar la caché de WiredTiger y la memoria del SO, y monitorear los aumentos de hard page faults junto con la latencia de I/O.","tags":["metric","memory","page-fault","io"]},{"name":"conexiones actuales (connections current)","kind":"metric","meaning":"El número de conexiones de cliente abiertas actualmente en el servidor; muestra el uso del pool de conexiones y lo cerca que está del máximo.","causes":"Pools de conexiones mal configurados, fugas de conexiones, un aumento repentino de clientes, conexiones retenidas durante mucho tiempo, conexiones excesivas desde microservicios.","action":"Comparar el número de conexiones con el límite del servidor (maxIncomingConnections), dimensionar correctamente el pool de conexiones en el lado de la aplicación, corregir las fugas de conexiones y monitorear los picos repentinos.","tags":["metric","connections","pool","capacity"]},{"name":"proporción de scanAndOrder / COLLSCAN","kind":"metric","meaning":"La proporción de consultas que escanean una colección completa sin índice (COLLSCAN) u ordenan en memoria (scanAndOrder); un indicador de ineficiencia de índices.","causes":"Índices ausentes o inadecuados, claves de ordenación que no coinciden con la consulta, consultas amplias sin filtrar, patrones de consulta deficientes.","action":"Revisar las consultas lentas y sus planes (explain), añadir índices compuestos que coincidan con los patrones de consulta y ordenación, corregir las consultas que producen COLLSCAN y scanAndOrder, y monitorear la proporción de documentos examinados/devueltos.","tags":["metric","index","collscan","query"]},{"name":"operaciones en cola (queued operations)","kind":"metric","meaning":"El número de operaciones de lectura y escritura en cola (que aún no pueden ejecutarse activamente) porque esperan un bloqueo o recurso; muestra que el sistema no puede atender la demanda.","causes":"Contención de bloqueos, agotamiento de tickets, un cuello de botella de I/O, consultas de larga duración, picos de carga repentinos, discos lentos.","action":"Si la cola crece, inspeccionar y optimizar las operaciones activas y de larga duración (currentOp), mejorar los índices y la I/O, equilibrar la carga y encontrar la causa raíz junto con las métricas de tickets y de contención de bloqueos.","tags":["metric","queue","concurrency","load"]}]},"reference/waits-redis.json":{"engineId":"redis","engine":"Redis","order":6,"waits":[{"name":"keyspace hit ratio","kind":"metric","meaning":"La proporción de búsquedas de claves encontradas en memoria, calculada a partir de keyspace_hits y keyspace_misses; muestra la eficacia de Redis como caché.","causes":"Una proporción baja indica desalojo por memoria insuficiente, TTL cortos, una caché fría, muchas búsquedas de claves únicas que nunca se cachearon o un mal diseño de claves.","action":"Monitorear el hit ratio como hits/(hits+misses); si es bajo, revisar la memoria/política de desalojo y los TTL, precalentar los datos de acceso frecuente y mejorar el diseño de claves y la estrategia de caché.","tags":["metric","cache","hit-ratio","keyspace"]},{"name":"evicted_keys","kind":"metric","meaning":"El número de claves eliminadas (borradas) de la memoria por la política de desalojo cuando se alcanza el límite de maxmemory; un indicador directo de presión de memoria.","causes":"Superar el límite de maxmemory, memoria insuficiente, acumulación de claves sin TTL, un volumen de datos mayor que la memoria, una política de desalojo inadecuada.","action":"Con un desalojo en aumento constante, añadir memoria o reducir el volumen de datos, elegir maxmemory-policy (p. ej. allkeys-lru/volatile-lru) según la carga de trabajo y revisar el uso de TTL y las big keys.","tags":["metric","memory","eviction","maxmemory"]},{"name":"blocked_clients","kind":"metric","meaning":"El número de clientes que esperan un evento/datos con comandos bloqueantes como BLPOP, BRPOP, WAIT y XREAD BLOCK; normal en patrones de consumidores de colas, pero un aumento anómalo es una señal de advertencia.","causes":"Consumidores que usan comandos bloqueantes de listas/streams, ausencia de datos desde el lado productor, espera de confirmación de réplicas con WAIT, desequilibrio entre consumidores y productores.","action":"Comparar el número de clientes bloqueados con el modelo de consumidores esperado; ante un aumento anómalo, investigar el lado productor y las confirmaciones de replicación, y equilibrar la tasa de consumo de la cola.","tags":["metric","clients","blocking","queue"]},{"name":"mem_fragmentation_ratio","kind":"metric","meaning":"La proporción entre la memoria física que el sistema operativo ha asignado a Redis (RSS) y la memoria que Redis usa realmente; muestra fragmentación de memoria o swapping.","causes":"Los valores claramente superiores a 1 indican fragmentación de memoria, mientras que los inferiores a 1 indican que Redis se ha enviado a swap (a disco). Las claves de tamaño variable y los ciclos de borrado/inserción aumentan la fragmentación.","action":"Con fragmentación alta, considerar habilitar activedefrag o reiniciar; si es inferior a 1, investigar el swapping y añadir memoria; revisar el comportamiento del allocator (jemalloc) y los patrones de big keys.","tags":["metric","memory","fragmentation","swap"]},{"name":"instantaneous_ops_per_sec","kind":"metric","meaning":"El número de comandos que Redis procesa por segundo en este momento (throughput instantáneo); muestra la intensidad de la carga de trabajo y los cambios repentinos de tráfico.","causes":"Picos repentinos de tráfico, carga de trabajos batch, patrones de acceso de clientes ineficientes, no usar pipelining en lugar de comandos individuales, ráfagas intensas de lectura/escritura.","action":"Monitorear el throughput junto con la latencia y la CPU, correlacionar las caídas/picos repentinos con el tráfico y los comandos lentos, mejorar la eficiencia con pipelining/lotes y planificar la capacidad.","tags":["metric","throughput","ops","load"]},{"name":"rdb_last_bgsave_status","kind":"metric","meaning":"El estado de éxito del último guardado RDB en segundo plano (BGSAVE); muestra si el snapshot de persistencia se tomó correctamente.","causes":"Un estado fallido (err) puede deberse a un disco lleno/espacio insuficiente, memoria insuficiente para el fork, un error de I/O de disco, problemas de permisos o una carga excesiva.","action":"Si el estado es 'err', comprobar el espacio en disco, la memoria necesaria para el fork (ajuste de overcommit) y el estado del disco, y revisar los logs; un BGSAVE fallido genera un riesgo de pérdida de datos, así que revise la configuración de persistencia.","tags":["metric","persistence","rdb","bgsave"]},{"name":"master_link_status","kind":"metric","meaning":"El estado (up/down) del enlace de replicación de una réplica con el master; muestra si la conexión entre la réplica y el master está sana.","causes":"Un estado 'down' puede deberse a un corte de red, a que el master sea inalcanzable, a un error de sincronización de la replicación, a un problema de autenticación o a una sobrecarga del master.","action":"Si el estado es 'down', comprobar la conectividad de red, la accesibilidad del master y los ajustes de auth, revisar los logs de la réplica, monitorear el retraso de master_repl_offset y verificar que la sincronización se haya completado.","tags":["metric","replication","master-link","availability"]},{"name":"latencia (slowlog)","kind":"metric","meaning":"La latencia de los comandos lentos que superan un umbral definido, detectada mediante slowlog y el monitoreo de latencia; en Redis, de un solo thread, muestra el riesgo de que un comando bloquee a los demás.","causes":"Comandos con alta complejidad temporal (KEYS, SORT grandes, SMEMBERS/HGETALL en colecciones grandes), big keys, scripts Lua lentos, pausas por fork/persistencia, swapping.","action":"Encontrar los comandos lentos con los comandos SLOWLOG y LATENCY, eliminar los comandos O(N) (SCAN en lugar de KEYS) y las big keys, optimizar los scripts y monitorear las pausas causadas por fork/AOF/RDB.","tags":["metric","latency","slowlog","performance"]},{"name":"expired_keys","kind":"metric","meaning":"El número total de claves eliminadas porque su TTL expiró; un indicador del comportamiento de expiración basado en TTL y de la recuperación de memoria.","causes":"Que expiren claves con TTL es normal. Los cambios repentinos pueden indicar un cambio en la política de TTL, una expiración masiva de claves o patrones de borrado inesperados.","action":"Distinguir expired de evicted (expired es un comportamiento sano de TTL, evicted es presión de memoria), monitorear el impacto de las oleadas repentinas de expiración en los cache misses y la carga, y repartir la distribución de TTL.","tags":["metric","ttl","expiration","keyspace"]},{"name":"connected_clients vs maxclients","kind":"metric","meaning":"Lo cerca que está el número actual de clientes conectados del límite maxclients configurado; muestra el riesgo de agotar la capacidad de conexiones.","causes":"Fugas de conexiones, un pool de conexiones mal configurado, un aumento repentino de clientes, conexiones inactivas retenidas durante mucho tiempo, muchas conexiones de microservicios.","action":"Comparar los clientes conectados con maxclients; al acercarse al límite, dimensionar correctamente el pool de conexiones, corregir las fugas de conexiones, definir un timeout para conexiones inactivas y aumentar maxclients si es necesario.","tags":["metric","connections","maxclients","capacity"]}]},"reference/waits-elasticsearch.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"waits":[{"name":"% de heap de la JVM usado","kind":"metric","meaning":"El porcentaje del heap de la JVM de un nodo que está en uso; un heap alto y persistentemente lleno implica presión de memoria y riesgo de GC intenso.","causes":"Memoria excesiva de fielddata/agregaciones, demasiados shards, carga intensa de bulk/consultas, mappings grandes, un heap subdimensionado, patrones de fuga de memoria.","action":"Monitorear el uso del heap (intervenir especialmente si se mantiene por encima del 75% tras el GC), reducir el número de shards, limitar las agregaciones intensas y el uso de fielddata, y mantener el heap en no más de la mitad de la RAM y por debajo de 32GB.","tags":["metric","jvm","heap","memory"]},{"name":"duración/frecuencia de GC","kind":"metric","meaning":"La duración y la frecuencia del garbage collection de la JVM; las pausas de GC largas/frecuentes hacen que el nodo no responda y degradan el rendimiento.","causes":"Alta presión de heap, gran acumulación en la generación antigua (old gen), agregaciones/fielddata intensas, un heap subdimensionado, demasiados shards, consultas grandes de larga duración.","action":"Eliminar la presión de heap que es la causa raíz de las pausas largas (especialmente old GC), optimizar las consultas que consumen mucha memoria, reducir el número de shards; un GC frecuente y largo es una alerta temprana de inestabilidad del nodo.","tags":["metric","jvm","gc","pause"]},{"name":"thread_pool write rechazados","kind":"metric","meaning":"El número de solicitudes de indexación rechazadas porque la cola del thread pool de escritura (indexación/bulk) estaba llena; muestra que el nodo no puede soportar la carga de escritura.","causes":"Tasa excesiva de bulk/indexación, I/O de disco lenta, muchas solicitudes pequeñas en lugar de bulk, capacidad insuficiente del nodo, presión de refresh.","action":"Enviar las solicitudes de indexación en bulks de tamaño adecuado, limitar la tasa de escritura a la capacidad del nodo (backoff), mejorar la I/O de disco, aumentar refresh_interval, escalar la capacidad.","tags":["metric","thread-pool","indexing","rejection"]},{"name":"thread_pool search rechazados","kind":"metric","meaning":"El número de solicitudes de búsqueda rechazadas porque la cola del thread pool de búsqueda estaba llena; muestra que el nodo no puede soportar la carga de consultas.","causes":"Exceso de consultas concurrentes, agregaciones pesadas/lentas, tocar demasiados shards, consultas ineficientes, capacidad insuficiente de nodos/CPU.","action":"Limitar la concurrencia de consultas en el lado del cliente, optimizar las consultas y agregaciones pesadas, reducir el fan-out innecesario de shards (routing/filtrado), añadir nodos y aumentar el número de shards réplica (las búsquedas también las atienden los shards réplica), escalar la capacidad.","tags":["metric","thread-pool","search","rejection"]},{"name":"latencia de indexación/búsqueda","kind":"metric","meaning":"La latencia media de las operaciones de indexación y búsqueda; derivada de las métricas index_time_in_millis y query_time_in_millis, refleja el tiempo de respuesta que perciben los usuarios.","causes":"I/O de disco lenta, consultas/agregaciones pesadas, pausas de GC, un número alto de shards, presión de refresh/merge, recursos insuficientes.","action":"Encontrar y optimizar las consultas lentas con el slow log, ajustar bulk y refresh_interval para la indexación, mejorar el rendimiento de disco y heap, e interpretar la latencia junto con los rechazos del thread pool y el GC.","tags":["metric","latency","indexing","search"]},{"name":"tiempo de refresh/flush","kind":"metric","meaning":"Tiempo dedicado a refresh, que hace que los nuevos documentos se puedan buscar, y a flush, que persiste el translog en Lucene; muestra la sobrecarga de indexación.","causes":"Refresh demasiado frecuentes (refresh_interval bajo), I/O de disco lenta, indexación intensa, un translog grande, muchos segmentos pequeños.","action":"Durante la indexación intensa, aumentar refresh_interval (p. ej. 30s) o desactivarlo temporalmente, mejorar la I/O de disco, ajustar el tamaño de bulk, revisar el comportamiento de merge de segmentos.","tags":["metric","refresh","flush","indexing"]},{"name":"shards sin asignar (unassigned shards)","kind":"metric","meaning":"El número de shards no asignados a ningún nodo; un primario sin asignar significa que los datos son inaccesibles (estado red), una réplica sin asignar implica un riesgo de durabilidad/capacidad.","causes":"Fallo/salida de un nodo, disk watermark superado, un ajuste de réplicas demasiado alto para el número de nodos, reglas/filtros de asignación, retrasos durante la recuperación de shards.","action":"Encontrar el motivo con la API cluster allocation explain, recuperar los nodos fallidos, corregir los problemas de espacio en disco/watermark, revisar el número de réplicas y las reglas de asignación; priorizar el estado red.","tags":["metric","shards","allocation","cluster-health"]},{"name":"% de disk watermark","kind":"metric","meaning":"Uso de disco del nodo respecto a los umbrales low/high/flood-stage (predeterminados 85%/90%/95%) que afectan a la ubicación de shards de Elasticsearch.","causes":"Disco llenándose, crecimiento de datos, almacenamiento insuficiente, distribución de shards desequilibrada, índices antiguos que no se limpian.","action":"En el low watermark no se asignan nuevos shards al nodo; en el high watermark los shards se reubican en otros nodos; en flood-stage los índices pasan a solo lectura. Liberar espacio en disco (eliminar índices antiguos/usar ILM), ampliar el almacenamiento, equilibrar la distribución de shards y monitorear los umbrales de watermark.","tags":["metric","disk","watermark","storage"]},{"name":"desalojos de fielddata/query cache","kind":"metric","meaning":"El número de entradas desalojadas de la caché de fielddata y de la query (filter) cache por límites de memoria; indica ineficiencia de la caché y presión de memoria.","causes":"Uso alto de fielddata (ordenaciones/agregaciones en campos text), límites de caché pequeños, filtros de consulta muy variados, presión de memoria, filtros que nunca se reutilizan.","action":"Usar keyword/doc_values en lugar de fielddata en campos text, limitar las agregaciones pesadas de fielddata, revisar los tamaños de caché; los desalojos frecuentes deben evaluarse junto con la presión de heap.","tags":["metric","cache","fielddata","eviction"]},{"name":"circuit breaker disparado","kind":"metric","meaning":"El número de veces que los circuit breakers (parent/fielddata/request breaker) previeron que una solicitud superaría la memoria y la rechazaron; un mecanismo de seguridad que protege al nodo de OOM.","causes":"Agregaciones/consultas que solicitan memoria excesiva, uso alto de fielddata, solicitudes bulk grandes, presión general de heap, conjuntos de resultados muy grandes.","action":"Tratar los disparos de circuit breaker como un signo de presión de heap, optimizar las consultas que consumen mucha memoria, reducir el uso de fielddata/agregaciones, reducir el tamaño de bulk y revisar el heap y el número de shards.","tags":["metric","circuit-breaker","memory","oom"]},{"name":"tareas pendientes (pending tasks)","kind":"metric","meaning":"La cola de tareas de actualización del cluster state que esperan ser procesadas por el nodo master; muestra que el master no puede seguir el ritmo de la carga de gestión del clúster.","causes":"Un gran número de índices/shards, cambios frecuentes de mapping/settings, recursos insuficientes en el nodo master, un cluster state grande, problemas de red/elección de master.","action":"Si la cola está persistentemente llena, reducir el número de shards/índices, limitar las actualizaciones frecuentes de dynamic mapping, usar nodos master dedicados y revisar el tamaño del cluster state y los recursos del master.","tags":["metric","master","cluster-state","queue"]}]},"reference/alerts-oracle.json":{"engineId":"oracle","engine":"Oracle Database","order":1,"alerts":[{"metric":"Tasa de aciertos del buffer cache","warn":"< 95%","crit":"< 90%","tool":"V$SYSSTAT / V$BUFFER_POOL_STATISTICS","note":"El buffer cache hit ratio muestra el porcentaje de bloques de datos leídos desde memoria sin ir a disco. Los valores bajos indican un aumento de lecturas físicas y una SGA subdimensionada. Por sí solo puede ser engañoso; evalúelo siempre junto con las lecturas físicas del informe AWR.","tags":["memory","cache","sga"]},{"metric":"Uso del tablespace (%used)","warn":"> 85%","crit":"> 95%","tool":"DBA_TABLESPACE_USAGE_METRICS","note":"Si el uso del tablespace llega al 100%, no pueden asignarse nuevos extents y las operaciones se detienen con errores como ORA-01653. Incluso con autoextend habilitado, vigile el límite del disco. La vista DBA_TABLESPACE_USAGE_METRICS da el porcentaje correcto tanto para archivos autoextensibles como de tamaño fijo.","tags":["disk","tablespace","capacity"]},{"metric":"Uso de memoria PGA","warn":"> 90% pga_aggregate_target","crit":"> 100% (sobreasignación)","tool":"V$PGASTAT / V$PGA_TARGET_ADVICE","note":"Cuando se supera el objetivo de PGA, las operaciones de sort y hash se vuelcan a disco (temp) y las consultas se ralentizan. Un over allocation count en aumento constante muestra que el objetivo es insuficiente. Revise el ajuste pga_aggregate_target frente a la memoria del servidor.","tags":["memory","pga","temp"]},{"metric":"Uso del tablespace TEMP","warn":"> 80%","crit":"> 95%","tool":"V$TEMPSEG_USAGE / V$SORT_SEGMENT / DBA_TEMP_FREE_SPACE","note":"El espacio TEMP se usa para ordenaciones grandes, hash joins y operaciones con global temporary tables. Cuando se llena, se produce ORA-01652 y las consultas fallan. Los llenados repentinos suelen deberse a una sola consulta con un mal plan o que ordena demasiados datos.","tags":["temp","disk","sort"]},{"metric":"Average Active Sessions (AAS)","warn":"> número de núcleos de CPU","crit":"> 2x el número de núcleos de CPU","tool":"V$ACTIVE_SESSION_HISTORY / OEM","note":"Average Active Sessions resume en un solo número la carga actual de la base de datos. Si el valor supera el número de núcleos de CPU, las sesiones están haciendo cola por CPU o por wait events. Si se mantiene en un múltiplo del número de núcleos, es necesario un análisis de cuellos de botella.","tags":["cpu","load","session"]},{"metric":"Data Guard apply lag","warn":"> 60 s","crit":"> 300 s","tool":"V$DATAGUARD_STATS (apply lag)","note":"El apply lag es cuánto va retrasada la base de datos standby respecto a la primaria. Un apply lag alto aumenta la cantidad de redo por aplicar durante el failover y, por tanto, alarga el tiempo de recuperación (RTO); si el redo ha llegado al standby, no se pierden datos. La posible pérdida de datos (RPO) la determina el transport lag. Revíselo junto con el transport lag para determinar si la lentitud está en la red o en la aplicación del redo.","tags":["replication","dataguard","lag"]},{"metric":"Uso de la Fast Recovery Area (FRA)","warn":"> 85%","crit":"> 95%","tool":"V$RECOVERY_FILE_DEST / V$RECOVERY_AREA_USAGE","note":"Cuando la FRA se llena, se detiene la generación de archivelogs y toda la base de datos se cuelga. Es una de las causas más comunes de caídas en sistemas de producción. Compruebe con regularidad las políticas de borrado de RMAN y el valor de db_recovery_file_dest_size.","tags":["disk","fra","archivelog"]},{"metric":"Número de sesiones bloqueantes","warn":">= 1 (> 30 s)","crit":">= 1 (> 120 s)","tool":"V$LOCK / V$SESSION (blocking_session)","note":"Cuando un bloqueo mantenido por una sesión hace esperar a otras sesiones, se produce una ralentización en cascada en toda la aplicación. Los bloqueos de larga duración suelen deberse a transacciones sin commit o a errores de la aplicación. Encuentre la sesión raíz de la cadena de bloqueo y termínela si es necesario.","tags":["lock","blocking","concurrency"]},{"metric":"Número de objetos inválidos","warn":"> 0","crit":"> 50","tool":"DBA_OBJECTS (status='INVALID')","note":"Los paquetes, vistas o triggers inválidos pueden provocar errores inesperados en tiempo de ejecución. Suelen aparecer tras un cambio de DDL. Resuélvalos recompilando con UTL_RECOMP.RECOMP_PARALLEL o utlrp.sql, e investigue la causa raíz de los objetos persistentemente inválidos.","tags":["object","compile","maintenance"]},{"metric":"Consulta de mayor duración","warn":"> 5 min","crit":"> 30 min","tool":"V$SQL / V$SESSION_LONGOPS","note":"Una sola consulta de larga duración puede monopolizar los recursos y ralentizar también otras operaciones. Salvo en los trabajos batch, las consultas que se ejecutan durante minutos en sistemas OLTP suelen deberse a un índice ausente o a un mal plan. Examine el plan y el número real de filas con un informe de SQL Monitor.","tags":["query","performance","sql"]},{"metric":"Errores ORA en el alert log","warn":">= 1 (no críticos)","crit":"ORA-00600 / ORA-07445 / ORA-04031","tool":"alert log / V$DIAG_ALERT_EXT","note":"El alert log es el diario de salud de la base de datos. ORA-00600 y ORA-07445 son errores internos que pueden indicar corrupción de datos, mientras que ORA-04031 indica agotamiento de memoria del shared pool. Si aparecen estos errores, investíguelos de inmediato y compártalos con Oracle Support si es necesario.","tags":["error","alertlog","monitoring"]},{"metric":"Frecuencia de cambio de redo log","warn":"> 6 / hora","crit":"> 12 / hora","tool":"V$LOG_HISTORY","note":"Los cambios de redo log muy frecuentes indican log files subdimensionados y presión de checkpoint. Lo ideal es un cambio cada 15-20 minutos en horas pico. Los cambios excesivamente frecuentes aumentan la carga de IO y de archivado; considere ampliar los grupos de redo log.","tags":["redo","io","checkpoint"]},{"metric":"Antigüedad del último backup RMAN correcto","warn":"> 24 horas","crit":"> 48 horas","tool":"V$RMAN_BACKUP_JOB_DETAILS","note":"La antigüedad del backup determina el punto más reciente al que puede recuperar y refleja directamente el riesgo de pérdida de datos. Si los backups fallidos u omitidos pasan desapercibidos, no hay vuelta atrás ante un desastre. Monitoree también por separado los backups de archivelogs para garantizar la capacidad de recuperación a un punto en el tiempo.","tags":["backup","rman","recovery"]},{"metric":"Latencia de IO de disco (db file sequential read)","warn":"> 10 ms","crit":"> 20 ms","tool":"V$SYSTEM_EVENT / AWR","note":"El tiempo de espera medio de las lecturas de un solo bloque muestra el rendimiento de la capa de almacenamiento. En los sistemas flash/SSD modernos este valor debe ser de unos pocos milisegundos. Una latencia persistentemente alta indica un cuello de botella de almacenamiento o lecturas físicas excesivas; evalúe la SGA y la capa de almacenamiento en conjunto.","tags":["io","disk","latency"]}]},"reference/alerts-postgres.json":{"engineId":"postgres","engine":"PostgreSQL","order":2,"alerts":[{"metric":"Tasa de aciertos de caché (cache hit ratio)","warn":"< 99%","crit":"< 95%","tool":"pg_stat_database / postgres_exporter","note":"El cache hit ratio es el porcentaje de bloques servidos únicamente desde shared_buffers; las lecturas atendidas por la caché del sistema operativo también se cuentan como blks_read, por lo que la proporción puede hacer que las lecturas reales de disco parezcan peores de lo que son. En sistemas OLTP de PostgreSQL sanos esta proporción debe superar el 99%. Los valores bajos indican memoria insuficiente o una caché fría; se calcula a partir de blks_hit y blks_read.","tags":["memory","cache"]},{"metric":"Número de conexiones (conexiones vs max_connections)","warn":"> 80%","crit":"> 90%","tool":"pg_stat_activity / max_connections","note":"Las conexiones activas que se acercan al límite de max_connections provocan fallos en las nuevas conexiones. Como cada conexión consume memoria, un número alto también genera presión de memoria. Se recomienda mantener bajo el número de backends con un connection pooler como PgBouncer.","tags":["connection","limit","pool"]},{"metric":"Retraso de replicación (replication lag)","warn":"> 10 MB","crit":"> 100 MB","tool":"pg_stat_replication (replay_lag / lsn diff)","note":"El retraso de replicación es la cantidad de WAL, en bytes, por la que el standby va por detrás del primario. Un retraso alto implica datos obsoletos en las réplicas de lectura y riesgo de pérdida de datos en un failover. Monitoree la diferencia de LSN en bytes junto con la duración de replay_lag.","tags":["replication","lag","standby"]},{"metric":"Antigüedad de la transacción más antigua","warn":"> 5 min","crit":"> 30 min","tool":"pg_stat_activity (xact_start)","note":"Las transacciones abiertas de larga duración impiden que VACUUM elimine las tuplas muertas y provocan bloat en las tablas. Las sesiones en estado 'idle in transaction' también mantienen bloqueos innecesariamente. Identifique esas sesiones y limítelas con idle_in_transaction_session_timeout.","tags":["query","vacuum","transaction"]},{"metric":"Deadlocks","warn":"> 1 / min","crit":"> 5 / min","tool":"pg_stat_database (deadlocks)","note":"Un deadlock se produce cuando dos transacciones esperan cada una los bloqueos de la otra, y PostgreSQL lo resuelve cancelando una de ellas. Pueden producirse deadlocks ocasionales, pero los frecuentes indican un orden de bloqueo inconsistente en la aplicación. Habilite log_lock_waits para ver qué consultas entran en conflicto.","tags":["lock","deadlock","concurrency"]},{"metric":"Uso de archivos temporales","warn":"> 1 GB / hora","crit":"> 10 GB / hora","tool":"pg_stat_database (temp_bytes)","note":"Los archivos temporales se crean cuando las operaciones de sort y hash que no caben en work_mem se vuelcan a disco. Un uso alto de temp provoca tanto lentitud como presión sobre el disco. Para las consultas que vuelcan con frecuencia, considere aumentar work_mem u optimizar la consulta.","tags":["temp","disk","work_mem"]},{"metric":"Retraso de autovacuum (tuplas muertas)","warn":"> 10% de tuplas muertas","crit":"> 20% de tuplas muertas","tool":"pg_stat_user_tables (n_dead_tup)","note":"Una proporción creciente de tuplas muertas muestra que autovacuum no da abasto y que el bloat de las tablas crece. Las tablas con bloat ocupan espacio y ralentizan los escaneos. En tablas con mucha actividad, reduzca autovacuum_vacuum_scale_factor para que la limpieza se ejecute con más frecuencia.","tags":["vacuum","bloat","maintenance"]},{"metric":"Uso de disco (directorio de datos)","warn":"> 85%","crit":"> 95%","tool":"df / node_exporter","note":"PostgreSQL no ofrece ningún mecanismo de autoprotección antes de que el disco se llene. Cuando el disco de datos se llena, las escrituras fallan con 'could not extend file' / 53100; si se llena el disco del WAL (pg_wal), el servidor se detiene con PANIC. El WAL y los archivos temporales pueden provocar un crecimiento repentino; la intervención temprana es crítica.","tags":["disk","capacity"]},{"metric":"Retención de WAL (retraso del replication slot)","warn":"> 1 GB","crit":"> 10 GB","tool":"pg_replication_slots (restart_lsn)","note":"Un replication slot no usado o retrasado impide que se eliminen los archivos WAL y hace crecer el directorio pg_wal. Esto puede terminar con el disco lleno. Identifique y elimine los slots muertos, y establezca un límite superior con max_slot_wal_keep_size.","tags":["wal","slot","disk"]},{"metric":"Frecuencia de checkpoint (checkpoints solicitados)","warn":"> 10% de checkpoints solicitados","crit":"> 30% de checkpoints solicitados","tool":"PG16 y anteriores: pg_stat_bgwriter (checkpoints_req) / PG17+: pg_stat_checkpointer (num_requested)","note":"Cuando aumenta la proporción de checkpoints solicitados frente a los programados, max_wal_size es demasiado pequeño. Los checkpoints frecuentes provocan picos de IO y degradación del rendimiento. Revise los ajustes max_wal_size y checkpoint_completion_target para repartir la carga.","tags":["checkpoint","wal","io"]},{"metric":"Antigüedad del último backup correcto","warn":"> 24 horas","crit":"> 48 horas","tool":"pgBackRest / barman info","note":"La antigüedad del backup determina el último punto al que puede volver y expresa directamente su RPO. Para la recuperación a un punto en el tiempo se requieren una cadena de backups ininterrumpida y el archivo de WAL. Los backups no solo deben realizarse; su validez también debe verificarse con pruebas de restauración periódicas.","tags":["backup","recovery","rpo"]},{"metric":"Latencia de IO de disco","warn":"> 10 ms","crit":"> 20 ms","tool":"node_exporter / iostat","note":"La latencia de lectura/escritura del almacenamiento afecta directamente a los tiempos de las consultas. En los sistemas SSD modernos este valor debe ser de unos pocos milisegundos. Una latencia alta sostenida indica un cuello de botella de disco o lecturas físicas excesivas; evalúe shared_buffers y la capa de almacenamiento en conjunto.","tags":["io","disk","latency"]},{"metric":"Transaction ID wraparound (antigüedad)","warn":"> 1000 millones","crit":"> 1500 millones","tool":"pg_database (age(datfrozenxid))","note":"Si la antigüedad del transaction ID se acerca a 2000 millones, PostgreSQL detiene todas las escrituras para evitar la pérdida de datos (protección contra wraparound). Es uno de los fallos más peligrosos de PostgreSQL. Monitoree la antigüedad de datfrozenxid y asegúrese de que autovacuum no se esté quedando atrás en el trabajo de freeze.","tags":["wraparound","vacuum","critical"]}]},"reference/alerts-mysql.json":{"engineId":"mysql","engine":"MySQL","order":3,"alerts":[{"metric":"Tasa de aciertos del buffer pool de InnoDB","warn":"< 99%","crit":"< 95%","tool":"SHOW ENGINE INNODB STATUS / mysqld_exporter","note":"El buffer pool hit ratio es el porcentaje de páginas de datos e índices leídas desde memoria sin ir a disco. Los valores bajos indican que innodb_buffer_pool_size es demasiado pequeño para el working set. En un servidor dedicado se asigna normalmente ~70-80% de la memoria al buffer pool (menos en un servidor compartido).","tags":["memory","cache","innodb"]},{"metric":"Número de conexiones (Threads_connected vs max_connections)","warn":"> 80%","crit":"> 90%","tool":"SHOW STATUS / max_connections","note":"A medida que el número de conexiones se acerca al límite de max_connections, las nuevas conexiones se rechazan con 'Too many connections'. Max_used_connections muestra el pico histórico. Mantenga bajo control las conexiones concurrentes con un pool de conexiones en el lado de la aplicación.","tags":["connections","limit","thread"]},{"metric":"Retraso de la réplica (Seconds_Behind_Source)","warn":"> 30 s","crit":"> 300 s","tool":"SHOW REPLICA STATUS","note":"Seconds_Behind_Source indica, en segundos, cuánto va retrasada la réplica respecto al source. Un retraso alto implica datos obsoletos en las réplicas de lectura y riesgo en el failover. Con la aplicación en un solo thread, el valor puede ser engañoso durante transacciones grandes; confírmelo junto con la diferencia de GTID.","tags":["replication","lag","replica"]},{"metric":"Tiempo de espera de bloqueo de fila de InnoDB","warn":"media > 1 s","crit":"media > 5 s","tool":"SHOW STATUS (Innodb_row_lock_time_avg)","note":"El tiempo medio de espera de bloqueo de fila muestra cuánto se hacen esperar las transacciones entre sí. Los valores altos apuntan a actualizaciones con mucho conflicto o a transacciones de larga duración. Revíselo junto con Innodb_row_lock_waits y encuentre las consultas en conflicto en performance_schema.","tags":["lock","blocking","innodb"]},{"metric":"History list length (undo)","warn":"> 100000","crit":"> 1000000","tool":"SHOW ENGINE INNODB STATUS","note":"La history list length es el número de registros de undo sin purgar y crece por las transacciones abiertas de larga duración. Un valor alto muestra que la purga se está quedando atrás y que el rendimiento de lectura se degrada por MVCC. Encuentre y termine las transacciones de larga duración para que la lista pueda reducirse.","tags":["undo","transaction","mvcc"]},{"metric":"Conexiones fallidas (Aborted_connects)","warn":"> 5 / min","crit":"> 20 / min","tool":"SHOW STATUS (Aborted_connects)","note":"Aborted_connects cuenta las conexiones que fallan durante la autenticación o el handshake. Los picos repentinos pueden indicar contraseñas incorrectas, problemas de red o un posible intento de ataque. Revíselo junto con Aborted_clients para determinar dónde se cae la conexión.","tags":["connections","error","security"]},{"metric":"Tablas temporales escritas en disco","warn":"> 25% de proporción","crit":"> 50% de proporción","tool":"SHOW STATUS (Created_tmp_disk_tables)","note":"La proporción de tablas temporales en memoria que se vuelcan a disco muestra que las consultas superan el límite de tmp_table_size. Las tablas temporales en disco ralentizan notablemente las consultas. Optimice las consultas con GROUP BY y columnas BLOB/TEXT y considere aumentar tmp_table_size y max_heap_table_size.","tags":["temp","disk","query"]},{"metric":"Consultas lentas","warn":"> 5 / min","crit":"> 20 / min","tool":"slow query log / estado Slow_queries","note":"Las consultas que superan el umbral de long_query_time se escriben en el slow query log y son la principal fuente de problemas de rendimiento. Las consultas lentas frecuentes apuntan a índices ausentes o a consultas mal escritas. Analice el log con pt-query-digest y priorice las consultas más costosas.","tags":["query","performance","slowlog"]},{"metric":"Uso de CPU","warn":"> 80%","crit":"> 90%","tool":"node_exporter / top","note":"Un uso alto y sostenido de CPU puede deberse a consultas pesadas, índices ausentes o demasiadas conexiones. Los picos cortos son normales, pero una saturación sostenida provoca colas de consultas. Identifique las consultas que más CPU consumen con performance_schema o el slow log.","tags":["cpu","load"]},{"metric":"Uso de disco (directorio de datos)","warn":"> 85%","crit":"> 95%","tool":"df / node_exporter","note":"Un directorio de datos lleno impide que MySQL escriba y provoca errores en las tablas. El crecimiento de los archivos binlog, los undo tablespaces y el tablespace temporal (ibtmp1) puede llenar el disco de repente; el redo log tiene un tamaño fijo (8.0.30+ #innodb_redo, innodb_redo_log_capacity). Asegúrese de que los binlogs antiguos se purguen mediante binlog_expire_logs_seconds.","tags":["disk","capacity","binlog"]},{"metric":"Consulta de mayor duración","warn":"> 60 s","crit":"> 300 s","tool":"SHOW PROCESSLIST / information_schema.PROCESSLIST","note":"Una sola consulta de larga duración puede afectar a otras transacciones al retener bloqueos y consumir recursos. En los sistemas OLTP, las consultas que se ejecutan durante minutos suelen deberse a un mal plan. Examine el plan con EXPLAIN y, si es necesario, termine la consulta (kill) y optimícela.","tags":["query","performance"]},{"metric":"Antigüedad del último backup correcto","warn":"> 24 horas","crit":"> 48 horas","tool":"xtrabackup / log del job de mysqldump","note":"La antigüedad del backup determina el último punto al que puede volver y, por tanto, el RPO. Junto con los backups de binlog, la recuperación a un punto en el tiempo es posible. No basta con realizar backups; verifique su validez con pruebas de restauración periódicas.","tags":["backup","recovery","rpo"]},{"metric":"Latencia de IO de disco","warn":"> 10 ms","crit":"> 20 ms","tool":"iostat / node_exporter","note":"La latencia del almacenamiento se traduce directamente en los tiempos de las consultas y es crítica especialmente para cargas de trabajo que no caben en el buffer pool. En los sistemas SSD modernos se esperan unos pocos milisegundos. Una latencia alta indica un cuello de botella de disco o lecturas físicas excesivas; revise también el tamaño del buffer pool.","tags":["io","disk","latency"]}]},"reference/alerts-mssql.json":{"engineId":"mssql","engine":"SQL Server","order":4,"alerts":[{"metric":"Page Life Expectancy (PLE)","warn":"< 900 s","crit":"< 300 s","tool":"sys.dm_os_performance_counters","note":"Page Life Expectancy muestra, en segundos, el tiempo estimado que una página de datos puede permanecer en memoria antes de ser desalojada del buffer pool. Los valores bajos indican presión de memoria y que las páginas se vacían rápidamente de la caché. Monitoréelo por separado en cada nodo NUMA y trate las caídas repentinas como escasez de memoria.","tags":["memory","cache","ple"]},{"metric":"Tasa de aciertos del buffer cache","warn":"< 98%","crit":"< 95%","tool":"sys.dm_os_performance_counters","note":"El buffer cache hit ratio es el porcentaje de páginas leídas desde memoria sin ir a disco. En SQL Server este valor suele ser muy alto; evalúelo junto con el PLE. Como por sí solo puede ser engañoso, use el PLE como métrica principal al analizar un cuello de botella de memoria.","tags":["memory","cache"]},{"metric":"Procesos bloqueados","warn":">= 1 (> 5 s)","crit":">= 1 (> 30 s)","tool":"sys.dm_exec_requests (blocking_session_id)","note":"Cuando un bloqueo mantenido por una sesión hace esperar a otras, se forma una cadena de bloqueo y sobreviene una ralentización en toda la aplicación. Los bloqueos de larga duración suelen deberse a transacciones sin commit o a un nivel de aislamiento inadecuado. Identifique el head blocker con el blocked process report.","tags":["lock","blocking","concurrency"]},{"metric":"Uso del log de transacciones (%used)","warn":"> 75%","crit":"> 90%","tool":"sys.dm_db_log_space_usage / DBCC SQLPERF(LOGSPACE)","note":"Cuando el log de transacciones se llena, la base de datos no puede escribir y se detiene con el error 9002. En el modelo de recuperación FULL, el log sigue creciendo si no se realizan log backups. Monitoree la frecuencia de los log backups y el número de VLF para asegurarse de que el log se trunca y se reutiliza.","tags":["log","disk","recovery"]},{"metric":"Uso de tempdb (%used)","warn":"> 80%","crit":"> 95%","tool":"sys.dm_db_file_space_usage","note":"tempdb se comparte para tablas temporales, ordenaciones, el version store y operaciones hash. Cuando se llena, fallan las consultas de todo el servidor. El crecimiento repentino suele deberse a una consulta grande o a una transacción snapshot de larga duración; monitoree por separado el tamaño del version store.","tags":["tempdb","disk","sort"]},{"metric":"Consulta de mayor duración","warn":"> 60 s","crit":"> 300 s","tool":"sys.dm_exec_requests / Query Store","note":"Una sola consulta de larga duración puede afectar a otros trabajos al retener bloqueos y consumir recursos. En los sistemas OLTP, las consultas que se ejecutan durante minutos suelen deberse a un índice ausente, a parameter sniffing o a un mal plan. Investigue las regresiones de plan con Query Store.","tags":["query","performance"]},{"metric":"Redo queue del Availability Group","warn":"> 1 MB","crit":"> 100 MB","tool":"sys.dm_hadr_database_replica_states","note":"La redo queue muestra la cantidad de log en la réplica secundaria que aún espera ser aplicada. Una redo queue alta alarga el tiempo de recuperación tras el failover (RTO) e implica datos obsoletos en las réplicas legibles. Examínela junto con redo_rate para prever cuándo se cerrará el retraso.","tags":["replication","ag","lag"]},{"metric":"Tasa de deadlocks","warn":"> 1 / min","crit":"> 5 / min","tool":"XEvent system_health / contadores de rendimiento","note":"Un deadlock se produce cuando dos transacciones esperan cada una los bloqueos de la otra, y SQL Server lo resuelve eligiendo una como víctima. Pueden producirse deadlocks ocasionales, pero los frecuentes indican un orden de bloqueo inconsistente. Extraiga el deadlock graph de la sesión de extended events system_health y corrija las consultas en conflicto.","tags":["lock","deadlock","concurrency"]},{"metric":"Uso de worker threads","warn":"> 80%","crit":"> 90%","tool":"sys.dm_os_schedulers / max worker threads","note":"Cuando se agota el pool de worker threads, las nuevas solicitudes hacen cola en la espera THREADPOOL y el servidor puede dejar de responder. Normalmente lo provocan bloqueos de larga duración o un exceso de conexiones concurrentes. Reduzca la presión sobre los threads resolviendo las cadenas de bloqueo.","tags":["cpu","thread","load"]},{"metric":"Uso de CPU","warn":"> 80%","crit":"> 90%","tool":"sys.dm_os_ring_buffers / Performance Monitor","note":"Una CPU alta sostenida puede deberse a consultas pesadas, índices ausentes o malos planes. Los picos cortos son normales, pero una saturación sostenida provoca colas de consultas. Identifique las consultas que más CPU consumen con sys.dm_exec_query_stats.","tags":["cpu","load"]},{"metric":"Antigüedad del último backup correcto","warn":"> 24 horas (full)","crit":"> 48 horas (full)","tool":"msdb.dbo.backupset","note":"La antigüedad del backup determina el último punto al que puede volver y su RPO. En el modelo de recuperación FULL, la recuperación a un punto en el tiempo es posible con log backups. Monitoree la cadena de backups full, diferenciales y de log para garantizar la recuperabilidad, y realice pruebas de restauración periódicas.","tags":["backup","recovery","rpo"]},{"metric":"Latencia de IO de disco (media de lectura/escritura)","warn":"> 15 ms","crit":"> 30 ms","tool":"sys.dm_io_virtual_file_stats","note":"La latencia media de lectura/escritura por archivo muestra el rendimiento del almacenamiento. Se esperan unos 15 ms para los archivos de datos y valores más bajos para los archivos de log. Una latencia alta sostenida indica un cuello de botella de almacenamiento; identifique los archivos más lentos con sys.dm_io_virtual_file_stats.","tags":["io","disk","latency"]}]},"reference/alerts-mongodb.json":{"engineId":"mongodb","engine":"MongoDB","order":5,"alerts":[{"metric":"Proporción de caché sucia de WiredTiger","warn":"> 5%","crit":"> 20%","tool":"db.serverStatus().wiredTiger.cache","note":"La proporción de caché sucia es el porcentaje de páginas modificadas en la caché de WiredTiger que aún no se han escrito en disco. Superar el 20% implica presión de desalojo y un cuello de botella de escritura; MongoDB obliga a los threads de la aplicación a realizar el desalojo. Evalúe en conjunto el tamaño de la caché y el throughput de escritura en disco.","tags":["memory","cache","wiredtiger"]},{"metric":"Uso de la caché de WiredTiger (used %)","warn":"> 80%","crit":"> 95%","tool":"db.serverStatus().wiredTiger.cache","note":"Cuando el uso de la caché supera el objetivo de desalojo del 80%, WiredTiger empieza a desalojar páginas de forma agresiva y el rendimiento cae. Si el working set no cabe en la caché, aumentan las lecturas físicas. Revise el ajuste wiredTigerCacheSizeGB y la memoria del servidor.","tags":["memory","cache","wiredtiger"]},{"metric":"Tickets disponibles (tickets de lectura/escritura)","warn":"available/totalTickets < 20% o queueLength > 0 (sostenido)","crit":"available = 0 y queueLength sigue creciendo","tool":"db.serverStatus().wiredTiger.concurrentTransactions","note":"WiredTiger limita las operaciones de lectura y escritura concurrentes con un sistema de tickets. Antes de 7.0 el valor predeterminado era fijo, de 128 tickets; en 7.0+ el número de tickets se ajusta dinámicamente (storageEngineConcurrencyAdjustmentAlgorithm: throughputProbing), así que monitoree la proporción available/totalTickets y queueLength en lugar del número absoluto. Que se agoten los tickets y las operaciones hagan cola es signo de un cuello de botella grave; normalmente lo causan consultas lentas o la saturación de I/O de disco.","tags":["concurrency","ticket","bottleneck"]},{"metric":"Retraso de replicación (replication lag)","warn":"> 10 s","crit":"> 60 s","tool":"rs.printSecondaryReplicationInfo()","note":"El retraso de replicación indica, en segundos, cuánto va retrasado un miembro secundario respecto al primario. Un retraso alto implica datos obsoletos según la read preference y riesgo de pérdida de datos en un failover. Suele deberse a cuellos de botella de I/O de disco o de red en el secundario, o a operaciones de larga duración.","tags":["replication","lag","replicaset"]},{"metric":"Uso de conexiones (connections %)","warn":"> 80%","crit":"> 90%","tool":"db.serverStatus().connections","note":"Las conexiones activas que se acercan al límite superior configurado provocan que se rechacen nuevas conexiones. Como cada conexión consume memoria y un thread, un número alto también genera presión sobre los recursos. Evite las tormentas de conexiones dimensionando correctamente el pool de conexiones en el lado de la aplicación.","tags":["connection","limit","pool"]},{"metric":"Tasa de page faults","warn":"> 100 / s","crit":"> 1000 / s","tool":"db.serverStatus() / extra_info page_faults","note":"Un page fault se produce cuando los datos no están en memoria y deben traerse desde disco. Una tasa de faults alta muestra que el working set no cabe en memoria y que el sistema funciona limitado por disco. Los valores altos sostenidos requieren ampliar la memoria u optimizar consultas/índices.","tags":["memory","disk","pagefault"]},{"metric":"Operaciones en cola","warn":"> 10","crit":"> 50","tool":"db.serverStatus().globalLock.currentQueue","note":"globalLock currentQueue muestra el número de lecturas/escrituras en cola mientras esperan un bloqueo o recurso. Una cola persistentemente llena indica que el servidor no puede seguir el ritmo de la carga entrante. Examínela junto con el agotamiento de tickets y la I/O de disco para determinar el origen del cuello de botella.","tags":["concurrency","queue","lock"]},{"metric":"Ventana del oplog (oplog window)","warn":"< 24 horas","crit":"< 6 horas","tool":"rs.printReplicationInfo()","note":"La ventana del oplog es el intervalo de tiempo que cubre el oplog y determina cuánto tiempo puede permanecer desconectado un secundario y aun así resincronizarse. Si la ventana se estrecha, un miembro que se retrase requerirá un resync completo. La ventana se acorta a medida que aumenta la carga de escritura; amplíe el tamaño del oplog en consecuencia.","tags":["oplog","replication","recovery"]},{"metric":"Consulta de mayor duración","warn":"> 30 s","crit":"> 120 s","tool":"db.currentOp() / slow query log","note":"Las consultas de larga duración afectan a otras operaciones al consumir recursos y retener bloqueos. Suelen deberse a consultas que hacen un collection scan (COLLSCAN) por un índice ausente. Encuentre las operaciones lentas con el profiler y currentOp y añada índices adecuados.","tags":["query","performance","index"]},{"metric":"Uso de CPU","warn":"> 80%","crit":"> 90%","tool":"node_exporter / mongostat","note":"Una CPU alta sostenida puede deberse a consultas sin índice, operaciones de agregación pesadas o un exceso de conexiones. Los picos cortos son normales, pero una saturación sostenida hace que las operaciones se encolen. Use el profiler para identificar las consultas que más CPU consumen y optimícelas.","tags":["cpu","load"]},{"metric":"Uso de disco (dbPath)","warn":"> 85%","crit":"> 95%","tool":"df / node_exporter","note":"Un directorio de datos lleno deja a MongoDB sin poder escribir y puede provocar corrupción. Aunque la compresión de WiredTiger ayuda, el journal y las colecciones en crecimiento pueden llenar el disco rápidamente. Realice una planificación de capacidad y purgue automáticamente los datos innecesarios con índices TTL.","tags":["disk","capacity"]},{"metric":"Antigüedad del último backup correcto","warn":"> 24 horas","crit":"> 48 horas","tool":"mongodump / Ops Manager / registro de snapshot","note":"La antigüedad del backup determina el último punto al que puede volver y su RPO. Combinada con el oplog, la recuperación a un punto en el tiempo es posible. No basta con realizar backups; valídelos con pruebas de restauración periódicas y asegúrese de que se toman snapshots consistentes en los clústeres con sharding.","tags":["backup","recovery","rpo"]}]},"reference/alerts-redis.json":{"engineId":"redis","engine":"Redis","order":6,"alerts":[{"metric":"Uso de memoria (used_memory vs maxmemory)","warn":"> 80%","crit":"> 95%","tool":"INFO memory / redis_exporter","note":"La proporción entre used_memory y maxmemory muestra cuánta memoria de Redis está en uso. Cuando se alcanza el límite, las claves se desalojan o las escrituras se rechazan según maxmemory-policy. Siga el crecimiento de la memoria para evitar OOM inesperados y oleadas de desalojo.","tags":["memory","maxmemory","capacity"]},{"metric":"Keyspace hit ratio","warn":"< 90%","crit":"< 80%","tool":"INFO stats (keyspace_hits/misses)","note":"El hit ratio es el porcentaje de claves solicitadas que se encuentran en la caché y se calcula a partir de keyspace_hits y keyspace_misses. Una proporción baja apunta a un uso ineficaz de la caché, TTL incorrectos o una caché fría. En cargas de trabajo de caché esta proporción debe ser lo más alta posible.","tags":["cache","hit","performance"]},{"metric":"Claves desalojadas (evicted_keys)","warn":"> 1 / s","crit":"> 100 / s","tool":"INFO stats (evicted_keys)","note":"evicted_keys es el número de claves eliminadas según maxmemory-policy porque la memoria está llena. Un desalojo continuo muestra que la memoria es insuficiente y que pueden perderse datos importantes. Reduzca el desalojo ampliando la memoria o replanteando la estrategia de TTL o el modelo de datos.","tags":["memory","eviction","maxmemory"]},{"metric":"Clientes bloqueados (blocked_clients)","warn":"> 50","crit":"> 200","tool":"INFO clients (blocked_clients)","note":"blocked_clients es el número de clientes que esperan en comandos bloqueantes como BLPOP/BRPOP/WAIT. Los valores superiores a lo esperado apuntan a consumidores que no dan abasto o a una acumulación creciente en la cola. En cargas de trabajo basadas en colas, compruebe el equilibrio entre productores y consumidores.","tags":["client","blocking","queue"]},{"metric":"Proporción de fragmentación de memoria (mem_fragmentation_ratio)","warn":"> 1.5","crit":"> 2.0","tool":"INFO memory","note":"La proporción de fragmentación es la memoria obtenida del sistema operativo dividida por la memoria que usa Redis. Por encima de 1.5 indica una fragmentación excesiva; por debajo de 1.0 indica memoria desbordada a swap, lo cual es muy peligroso. Puede reducir la fragmentación habilitando activedefrag o reiniciando.","tags":["memory","fragmentation"]},{"metric":"Estado del enlace con el master (master_link_status)","warn":"down (< 10 s)","crit":"down (> 30 s)","tool":"INFO replication (master_link_status)","note":"master_link_status muestra el estado (up/down) de la conexión de la réplica con el master. 'down' significa que la replicación está rota y la réplica sirve datos obsoletos. Las desconexiones largas requieren un resync completo; compruebe de inmediato la red y el estado del master.","tags":["replication","master","connection"]},{"metric":"Retraso de replicación (diferencia de master_repl_offset)","warn":"> 1 MB","crit":"> 10 MB","tool":"INFO replication (diferencia de offset)","note":"La diferencia entre los offsets del master y de la réplica muestra, en bytes, cuánto va retrasada la réplica. Una diferencia grande implica riesgo de pérdida de datos en un failover e inconsistencia en las réplicas de lectura. Examine la latencia de red y la carga de la réplica para asegurarse de que la diferencia se reduce.","tags":["replication","lag","offset"]},{"metric":"Estado del último bgsave RDB (rdb_last_bgsave_status)","warn":"err (último intento)","crit":"err (> 24 horas)","tool":"INFO persistence","note":"rdb_last_bgsave_status muestra si el último snapshot en segundo plano tuvo éxito (ok). Un bgsave fallido significa que la persistencia no funciona y que hay riesgo de pérdida de datos al reiniciar. Suele deberse a un disco lleno o a memoria insuficiente para el fork.","tags":["backup","persistence","rdb"]},{"metric":"Uso de conexiones (connected_clients)","warn":"> 80% maxclients","crit":"> 90% maxclients","tool":"INFO clients / maxclients","note":"Que connected_clients se acerque al límite de maxclients provoca que se rechacen nuevas conexiones. Las fugas de conexiones o un pool mal configurado pueden causar picos repentinos. Configure correctamente el pool de conexiones en el lado de la aplicación y cierre las conexiones inactivas.","tags":["connection","limit","client"]},{"metric":"Latencia instantánea de comandos","warn":"> 1 ms","crit":"> 10 ms","tool":"redis-cli --latency / LATENCY DOCTOR","note":"Como Redis es de un solo thread, la latencia de los comandos suele estar en el rango de los microsegundos. El aumento de la latencia puede deberse a comandos lentos (KEYS, SORT grandes), bloqueos durante el fork o uso de swap. Encuentre el origen con slowlog y los comandos LATENCY.","tags":["latency","performance","slowlog"]},{"metric":"Uso de CPU","warn":"> 80% (un solo núcleo)","crit":"> 90% (un solo núcleo)","tool":"top / redis_exporter","note":"Como el thread principal de Redis usa un solo núcleo, saturar un núcleo ralentiza todo el servidor. Una CPU alta puede deberse a comandos costosos, demasiadas conexiones o scripts Lua. Encuentre los comandos costosos con SLOWLOG y, si es necesario, reparta la carga de trabajo entre varias instancias.","tags":["cpu","load"]},{"metric":"Conexiones rechazadas (rejected_connections)","warn":"> 0","crit":"> 10 / min","tool":"INFO stats (rejected_connections)","note":"rejected_connections es el número de conexiones rechazadas porque se alcanzó el límite de maxclients. Un valor superior a cero significa que no se está atendiendo a los clientes y que se producen errores en la aplicación. Investigue de inmediato el valor de maxclients y las fugas de conexiones.","tags":["connection","error","limit"]}]},"reference/alerts-elasticsearch.json":{"engineId":"elasticsearch","engine":"Elasticsearch","order":7,"alerts":[{"metric":"Uso del heap de la JVM","warn":"> 75%","crit":"> 85%","tool":"_nodes/stats/jvm / elasticsearch_exporter","note":"El uso del heap de la JVM muestra la presión de memoria del nodo; los valores sostenidos por encima del 75% implican GC frecuentes y un posible OutOfMemory. Si el heap supera el 85%, el nodo puede dejar de responder o salir del clúster. Dimensione el heap a aproximadamente la mitad de la memoria del servidor y no más de 30-31 GB.","tags":["memory","jvm","heap"]},{"metric":"Número de shards sin asignar (unassigned shards)","warn":">= 1 (réplica)","crit":">= 1 (primario)","tool":"_cluster/health / _cat/shards","note":"Los shards sin asignar significan que parte de los datos es inaccesible o no está replicada. Un shard primario sin asignar implica riesgo de pérdida de datos; una réplica sin asignar implica pérdida de redundancia, y el clúster pasa a yellow/red. Encuentre el motivo con _cluster/allocation/explain.","tags":["shard","cluster","health"]},{"metric":"Uso respecto al disk watermark","warn":"> 85% (low)","crit":"> 90% (high) / > 95% (flood_stage)","tool":"_nodes/stats/fs / _cat/allocation","note":"Elasticsearch actúa de forma escalonada según el uso de disco: en el low watermark del 85% deja de asignar nuevos shards al nodo, en el high watermark del 90% reubica los shards existentes en otros nodos y en el flood_stage del 95% aplica un bloqueo read_only_allow_delete a los índices. En 8.x, en discos grandes los valores de max_headroom (low 200GB / high 150GB / flood 100GB libres) tienen prioridad sobre los porcentajes. En 7.4+ el bloqueo se libera automáticamente cuando el uso de disco baja del high watermark; aun así, libere disco antes de que se superen los watermarks.","tags":["disk","watermark","capacity"]},{"metric":"Operaciones rechazadas del thread pool (rejected)","warn":"> 0 (write/search)","crit":"> 100 / min","tool":"_cat/thread_pool / _nodes/stats/thread_pool","note":"Cuando la cola de un thread pool está llena, las solicitudes entrantes se rechazan; un rechazo en el pool de escritura significa indexación perdida y, en el pool de búsqueda, una consulta fallida. Los rechazos sostenidos indican que el nodo no puede seguir el ritmo de la carga entrante. Equilibre la carga reduciendo el tamaño de bulk y la concurrencia.","tags":["threadpool","rejected","load"]},{"metric":"Tiempo de pausa de GC (pausa de GC de old gen)","warn":"> 1 s","crit":"> 5 s","tool":"Logs de GC / _nodes/stats/jvm","note":"Durante las pausas largas de GC el nodo no puede responder y puede perder la conexión con el master, lo que dispara la reasignación de shards. Un GC de la generación antigua frecuente y largo es síntoma de presión de heap. Revise el tamaño del heap y el uso de agregaciones y fielddata que consumen mucha memoria.","tags":["jvm","gc","pause"]},{"metric":"Latencia de indexación","warn":"> 50 ms","crit":"> 200 ms","tool":"_nodes/stats/indices (index_time/index_total)","note":"El tiempo medio de indexación muestra el rendimiento de escritura de documentos y se calcula dividiendo index_time_in_millis entre index_total. Los valores crecientes indican un cuello de botella de IO de disco, una cadena de análisis pesada o presión de refresh. Optimice el tamaño de bulk y el ajuste refresh_interval.","tags":["indexing","latency","performance"]},{"metric":"Latencia de búsqueda","warn":"> 100 ms","crit":"> 500 ms","tool":"_nodes/stats/indices (query_time/query_total)","note":"El tiempo medio de consulta refleja directamente el rendimiento de búsqueda que perciben los usuarios y se calcula dividiendo query_time_in_millis entre query_total. Los valores crecientes pueden deberse a agregaciones pesadas, demasiados shards o una caché fría. Habilite el slow log para encontrar las consultas costosas.","tags":["search","latency","performance"]},{"metric":"Tareas pendientes (pending tasks)","warn":"> 10","crit":"> 100","tool":"_cluster/pending_tasks","note":"Las pending tasks son cambios del cluster state que el nodo master aún no ha aplicado (mapping, asignación de shards, etc.). Una acumulación constante indica que el master está sobrecargado o que el cluster state ha crecido demasiado. Alivie la presión sobre el master reduciendo un número excesivo de shards.","tags":["cluster","master","queue"]},{"metric":"Disparos de circuit breaker","warn":"> 0 (disparado)","crit":"> 10 / min","tool":"_nodes/stats/breakers","note":"Los circuit breakers cancelan una solicitud para evitar que una operación consuma memoria excesiva y lleve al nodo a OutOfMemory. Los disparos frecuentes indican presión de memoria por agregaciones pesadas o solicitudes grandes. Monitoree el tripped count y aligere las consultas problemáticas.","tags":["memory","breaker","jvm"]},{"metric":"Estado de salud del clúster (cluster status)","warn":"yellow","crit":"red","tool":"_cluster/health","note":"El estado del clúster puede ser green (todos los shards asignados), yellow (faltan réplicas) o red (faltan primarios, datos inaccesibles). Red requiere acción inmediata porque hay datos perdidos o inaccesibles. Yellow indica pérdida de redundancia y no debe ignorarse.","tags":["cluster","health","shard"]},{"metric":"Uso de CPU","warn":"> 80%","crit":"> 90%","tool":"_nodes/stats/os / node_exporter","note":"Una CPU alta sostenida puede deberse a consultas pesadas, indexación excesiva u operaciones de merge. Los picos cortos son normales, pero una saturación sostenida provoca colas de consultas e indexación. Identifique las operaciones que más CPU consumen con la API hot threads (_nodes/hot_threads).","tags":["cpu","load"]},{"metric":"Antigüedad del último snapshot correcto","warn":"> 24 horas","crit":"> 48 horas","tool":"_snapshot/_status / SLM","note":"La antigüedad del snapshot determina el último punto al que puede volver y su RPO. Deben garantizarse backups automáticos y periódicos con Snapshot Lifecycle Management (SLM). Si los snapshots fallidos u omitidos pasan desapercibidos, los datos no podrán recuperarse ante un desastre; verifique con regularidad el estado de los snapshots.","tags":["backup","snapshot","rpo"]}]},"cloud/cloud-ops-1.json":{"category":"Conexión, backup y réplica","categoryId":"cloud-core","order":1,"providers":[{"id":"aws-rds","name":"AWS RDS","color":"#B85C00"},{"id":"aws-aurora","name":"Aurora","color":"#7B3FE4"},{"id":"gcp-cloudsql","name":"Cloud SQL","color":"#C5372C"},{"id":"azure-sql","name":"Azure SQL","color":"#0067B8"},{"id":"mongodb-atlas","name":"Atlas","color":"#00843D"}],"concepts":[{"id":"list-instances","title":"Listar instancias / bases de datos","aliases":["list","instances","list all"],"providers":{"aws-rds":{"command":"aws rds describe-db-instances --query 'DBInstances[].DBInstanceIdentifier'","note":"Lista los identificadores de todas las instancias RDS. Añada --region para apuntar a una región.","danger":"safe"},"aws-aurora":{"command":"aws rds describe-db-clusters --query 'DBClusters[].DBClusterIdentifier'","note":"Lista los identificadores de los clústeres Aurora; consulte el campo DBClusterMembers para ver los miembros del clúster.","danger":"safe"},"gcp-cloudsql":{"command":"gcloud sql instances list","note":"Lista todas las instancias de Cloud SQL del proyecto. Añada --project para apuntar a un proyecto.","danger":"safe"},"azure-sql":{"command":"az sql db list -g <rg> -s <server>","note":"Lista las bases de datos del grupo de recursos (-g) y del servidor lógico (-s) indicados. Use 'az sql server list' para listar los servidores.","danger":"safe"},"mongodb-atlas":{"command":"atlas clusters list","note":"Lista todos los clústeres del proyecto de Atlas. Añada --projectId para apuntar a un proyecto.","danger":"safe"}}},{"id":"connect","title":"Conectarse a una instancia (encontrar el endpoint + conectar)","aliases":["connect","endpoint","connection string","connect to"],"providers":{"aws-rds":{"command":"aws rds describe-db-instances --db-instance-identifier <id> --query 'DBInstances[0].Endpoint.Address'","note":"Devuelve la dirección de conexión (host) de la instancia. Obtenga el puerto de Endpoint.Port y conéctese con el cliente psql/mysql.","danger":"safe"},"aws-aurora":{"command":"aws rds describe-db-clusters --db-cluster-identifier <id> --query 'DBClusters[0].Endpoint'","note":"Devuelve el endpoint de escritura del clúster. Use el campo ReaderEndpoint para el acceso de solo lectura.","danger":"safe"},"gcp-cloudsql":{"command":"gcloud sql instances describe <instance> --format='value(ipAddresses[].ipAddress)'","note":"Devuelve la(s) dirección(es) IP de la instancia. Se recomienda Cloud SQL Auth Proxy para conexiones seguras.","danger":"safe"},"azure-sql":{"command":"az sql db show-connection-string -s <server> -n <db> -c ado.net","note":"Genera la cadena de conexión para el tipo de cliente indicado (-c: ado.net, jdbc, odbc, php, etc.); usted completa el usuario/contraseña.","danger":"safe"},"mongodb-atlas":{"command":"atlas clusters connectionStrings describe <clusterName>","note":"Devuelve las cadenas de conexión estándar y SRV del clúster. Se requieren una entrada en la lista de acceso IP y un usuario de base de datos.","danger":"safe"}}},{"id":"take-snapshot","title":"Realizar un backup / snapshot manual","aliases":["snapshot","backup","backup"],"providers":{"aws-rds":{"command":"aws rds create-db-snapshot --db-instance-identifier <id> --db-snapshot-identifier <snap-id>","note":"Toma un snapshot manual de la instancia (se conserva hasta que se elimina manualmente). Puede producirse una breve suspensión de I/O.","danger":"caution"},"aws-aurora":{"command":"aws rds create-db-cluster-snapshot --db-cluster-identifier <id> --db-cluster-snapshot-identifier <snap-id>","note":"Toma un snapshot manual del clúster Aurora. Como los backups de Aurora son continuos, es una copia fija.","danger":"caution"},"gcp-cloudsql":{"command":"gcloud sql backups create --instance=<instance>","note":"Crea un backup bajo demanda de la instancia. Es posible un breve impacto en el rendimiento durante la operación.","danger":"caution"},"azure-sql":{"command":"az sql db export -s <server> -n <db> -u <user> -p <pwd> --admin-user <adm> --admin-password <pwd> --storage-key <key> --storage-key-type StorageAccessKey --storage-uri https://<acct>.blob.core.windows.net/<container>/<file>.bacpac","note":"Azure SQL se respalda automáticamente; para un backup manual portable, este comando exporta la base de datos como BACPAC. La restauración a un punto en el tiempo es una funcionalidad aparte.","danger":"caution"},"mongodb-atlas":{"command":"atlas backups snapshots create <clusterName>","note":"Inicia un snapshot de backup bajo demanda del clúster. Cloud Backup debe estar habilitado.","danger":"caution"}}},{"id":"restore-pitr","title":"Restaurar a un punto en el tiempo (point-in-time restore)","aliases":["pitr","restore","point-in-time","roll back"],"providers":{"aws-rds":{"command":"aws rds restore-db-instance-to-point-in-time --source-db-instance-identifier <src> --target-db-instance-identifier <new> --restore-time 2026-07-03T10:00:00Z","note":"Crea una NUEVA instancia a partir de la instancia de origen en el momento indicado; no sobrescribe la instancia existente. Use --use-latest-restorable-time para restaurar al último punto.","danger":"caution"},"aws-aurora":{"command":"aws rds restore-db-cluster-to-point-in-time --source-db-cluster-identifier <src> --db-cluster-identifier <new> --restore-to-time 2026-07-03T10:00:00Z","note":"Restaura el clúster Aurora al momento indicado como un clúster nuevo. Después debe añadir instancias con create-db-instance.","danger":"caution"},"gcp-cloudsql":{"command":"gcloud sql instances clone <src> <new> --point-in-time '2026-07-03T10:00:00Z'","note":"Crea un clon de la instancia de origen en el momento indicado como una instancia nueva. El registro binario/WAL debe estar habilitado en el origen.","danger":"caution"},"azure-sql":{"command":"az sql db restore -g <rg> -s <server> -n <db> --dest-name <new-db> --time '2026-07-03T10:00:00'","note":"Restaura la base de datos al momento indicado como una NUEVA base de datos; la base de datos de origen se conserva. La hora está en UTC.","danger":"caution"},"mongodb-atlas":{"command":"atlas backups restores start pointInTime --clusterName <cluster> --pointInTimeUTCMillis <ms> --targetClusterName <target> --targetProjectId <projId>","note":"Inicia una restauración a la hora UTC indicada usando Continuous Cloud Backup. El clúster de destino se sobrescribe; proceda con cuidado.","danger":"danger"}}},{"id":"read-replica","title":"Crear una réplica de lectura","aliases":["replica","read replica","read replica"],"providers":{"aws-rds":{"command":"aws rds create-db-instance-read-replica --db-instance-identifier <replica-id> --source-db-instance-identifier <src>","note":"Crea una réplica de lectura asíncrona a partir de la instancia de origen. Use --source-region para una copia entre regiones.","danger":"caution"},"aws-aurora":{"command":"aws rds create-db-instance --db-instance-identifier <reader-id> --db-cluster-identifier <cluster> --engine aurora-postgresql --db-instance-class db.r6g.large","note":"En Aurora, un reader es una instancia añadida al clúster; comparte el almacenamiento del clúster. Defina el valor de engine según el clúster (aurora-mysql / aurora-postgresql).","danger":"caution"},"gcp-cloudsql":{"command":"gcloud sql instances create <replica> --master-instance-name=<src> --region=<region>","note":"Crea una réplica de lectura a partir de la instancia primaria (master). El registro binario debe estar habilitado en el origen.","danger":"caution"},"azure-sql":{"command":"az sql db replica create -g <rg> -s <server> -n <db> --partner-server <target-server>","note":"Crea en otro servidor una secundaria legible con geo-replicación activa de la base de datos. El servidor de destino debe existir.","danger":"caution"},"mongodb-atlas":{"command":"atlas clusters update <clusterName> --file cluster.json","note":"Los clústeres de Atlas ya son replica sets replicados. Para capacidad de lectura adicional, añada nodos readOnly o analytics en replicationSpecs y actualice.","danger":"caution"}}},{"id":"failover","title":"Failover","aliases":["failover","fail over","switch to standby"],"providers":{"aws-rds":{"command":"aws rds reboot-db-instance --db-instance-identifier <id> --force-failover","note":"En una instancia Multi-AZ, fuerza un reinicio que dispara el failover al standby. Cuente con una breve interrupción.","danger":"danger"},"aws-aurora":{"command":"aws rds failover-db-cluster --db-cluster-identifier <cluster> --target-db-instance-identifier <reader>","note":"Promueve un reader del clúster Aurora a nuevo writer. Si no se indica un destino, se elige una réplica adecuada; cuente con una breve interrupción.","danger":"danger"},"gcp-cloudsql":{"command":"gcloud sql instances failover <instance>","note":"Realiza el failover de una instancia HA (alta disponibilidad) a la zona standby. Solo funciona en instancias con HA habilitada; se produce una interrupción.","danger":"danger"},"azure-sql":{"command":"az sql failover-group set-primary -g <rg> -s <secondary-server> -n <fog-name>","note":"Traslada el rol primario al servidor secundario de un auto-failover group (failover planificado). Puede forzarse con --allow-data-loss, lo que conlleva riesgo de pérdida de datos.","danger":"danger"},"mongodb-atlas":{"command":"atlas clusters failover <clusterName>","note":"Atlas realiza el failover automáticamente; este comando dispara un failover manual para pruebas. El nodo primario cambia y la aplicación debe reconectarse.","danger":"danger"}}}]},"cloud/cloud-ops-2.json":{"category":"Configuración, escalado y monitoreo","categoryId":"cloud-config","order":2,"providers":[{"id":"aws-rds","name":"AWS RDS","color":"#B85C00"},{"id":"aws-aurora","name":"Aurora","color":"#7B3FE4"},{"id":"gcp-cloudsql","name":"Cloud SQL","color":"#C5372C"},{"id":"azure-sql","name":"Azure SQL","color":"#0067B8"},{"id":"mongodb-atlas","name":"Atlas","color":"#00843D"}],"concepts":[{"id":"scale-instance","title":"Cambiar el tamaño de la instancia (escalado vertical)","aliases":["scale","size","resize","scale up","vertical"],"providers":{"aws-rds":{"command":"aws rds modify-db-instance --db-instance-identifier <id> --db-instance-class db.r6g.xlarge --apply-immediately","note":"--apply-immediately aplica el cambio ahora y reinicia la instancia (breve interrupción). Sin este flag el cambio se aplica en la siguiente ventana de mantenimiento. En instancias Multi-AZ la interrupción se reduce aproximadamente al tiempo del failover.","danger":"caution"},"aws-aurora":{"command":"aws rds modify-db-instance --db-instance-identifier <writer-or-reader-id> --db-instance-class db.r6g.2xlarge --apply-immediately","note":"En Aurora cada instancia (writer y readers) se escala por separado; cambia el tamaño de la instancia, no el del clúster. Escale primero los readers y luego haga failover para reemplazar el writer y minimizar la interrupción. Con Serverless v2 se escala mediante min/max ACU.","danger":"caution"},"gcp-cloudsql":{"command":"gcloud sql instances patch <id> --tier db-custom-4-16384","note":"Un tipo de máquina personalizado se especifica como db-custom-CPU-MEMORY (ejemplo: 4 vCPU, 16384 MB de RAM). El cambio reinicia la instancia y provoca una breve interrupción. También pueden usarse niveles predefinidos (p. ej. db-n1-standard-4).","danger":"caution"},"azure-sql":{"command":"az sql db update -g <rg> -s <server> -n <db> --service-objective S3","note":"En el modelo DTU use un service objective como S3/P2; en el modelo vCore use --edition GeneralPurpose --family Gen5 --capacity 4. El escalado se realiza en línea; puede producirse un breve corte de conexión de unos segundos cerca del final de la operación.","danger":"caution"},"mongodb-atlas":{"command":"atlas clusters update <name> --tier M30","note":"Atlas redimensiona los nodos del replica set de forma escalonada (rolling), por lo que no hay interrupción. Requiere el flag --projectId o un proyecto predeterminado. Al reducir el tamaño, vigile los límites de disco y memoria.","danger":"caution"}}},{"id":"parameter-group","title":"Cambiar parámetros (parameter group / flags / configuración del servidor)","aliases":["parameter","parameter group","flags","config","setting"],"providers":{"aws-rds":{"command":"aws rds modify-db-parameter-group --db-parameter-group-name <pg> --parameters \"ParameterName=max_connections,ParameterValue=500,ApplyMethod=pending-reboot\"","note":"Use ApplyMethod=immediate para los parámetros dinámicos y pending-reboot para los estáticos. El parameter group debe estar asociado a la instancia; los cambios de parámetros estáticos surten efecto tras un reinicio. Use create-db-parameter-group para crear un grupo.","danger":"caution"},"aws-aurora":{"command":"aws rds modify-db-cluster-parameter-group --db-cluster-parameter-group-name <cpg> --parameters \"ParameterName=innodb_lock_wait_timeout,ParameterValue=100,ApplyMethod=immediate\"","note":"En Aurora, los parámetros de todo el clúster se gestionan con el DB cluster parameter group y los específicos de cada instancia con el DB parameter group (modify-db-parameter-group). Los parámetros estáticos requieren un reinicio en todo el clúster.","danger":"caution"},"gcp-cloudsql":{"command":"gcloud sql instances patch <id> --database-flags max_connections=500,log_min_duration_statement=1000","note":"--database-flags define TODOS los flags a la vez; los flags definidos previamente que no estén en la lista se restablecen, así que especifíquelos todos juntos. Algunos flags requieren reiniciar la instancia. Consulte los flags actuales con 'gcloud sql instances describe <id>'.","danger":"caution"},"azure-sql":{"command":"az sql db configure -g <rg> -s <server> -n <db> ...   # DB-level settings via T-SQL: ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP = 4;","note":"En Azure SQL, en lugar de la configuración clásica del servidor, los ajustes se aplican sobre todo con T-SQL: ALTER DATABASE SCOPED CONFIGURATION ...; sp_configure a nivel de servidor es limitado. Para las opciones de elastic pool/base de datos use los flags de 'az sql db update'.","danger":"caution"},"mongodb-atlas":{"command":"atlas clusters advancedSettings update <name> --oplogMinRetentionHours 24 --oplogSizeMB 20480","note":"En Atlas, los argumentos de bajo nivel del proceso MongoDB solo pueden definirse de forma limitada mediante 'advancedSettings' (oplog, búsqueda de texto completo, profundidad de índices, etc.). La mayoría de los parámetros del servidor los gestiona Atlas y no pueden cambiarse directamente.","danger":"caution"}}},{"id":"monitoring","title":"Ver métricas y rendimiento","aliases":["monitoring","metric","observability","performance","insights"],"providers":{"aws-rds":{"command":"aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name CPUUtilization --dimensions Name=DBInstanceIdentifier,Value=<id> --start-time 2026-07-05T00:00:00Z --end-time 2026-07-05T01:00:00Z --period 300 --statistics Average","note":"Las métricas básicas (CPU, conexiones, IOPS, latencia) están en el namespace AWS/RDS de CloudWatch. Para el análisis a nivel de consulta habilite Performance Insights: 'aws pi get-resource-metrics --service-type RDS ...' o use el panel de Performance Insights en la consola.","danger":"safe"},"aws-aurora":{"command":"aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name AuroraReplicaLag --dimensions Name=DBInstanceIdentifier,Value=<reader-id> --start-time 2026-07-05T00:00:00Z --end-time 2026-07-05T01:00:00Z --period 60 --statistics Maximum","note":"Las métricas específicas de Aurora (AuroraReplicaLag, VolumeReadIOPs, CommitLatency, buffer cache hit) también están en el namespace AWS/RDS. Se recomienda Performance Insights para Aurora; ofrece análisis de top SQL y de wait events.","danger":"safe"},"gcp-cloudsql":{"command":"gcloud monitoring time-series list --filter='metric.type=\"cloudsql.googleapis.com/database/cpu/utilization\" AND resource.labels.database_id=\"<project>:<id>\"' --interval-start-time=2026-07-05T00:00:00Z --interval-end-time=2026-07-05T01:00:00Z","note":"Las métricas de Cloud SQL están en Cloud Monitoring bajo cloudsql.googleapis.com/... Para el análisis a nivel de consulta habilite Cloud SQL Query Insights (consola o API); muestra las consultas más lentas y los wait events.","danger":"safe"},"azure-sql":{"command":"az monitor metrics list --resource <db-resource-id> --metric \"cpu_percent\" --interval PT5M --start-time 2026-07-05T00:00:00Z --end-time 2026-07-05T01:00:00Z","note":"Métricas como cpu_percent, dtu_consumption_percent, storage_percent y connection_successful provienen de Azure Monitor. Para el rendimiento de las consultas use los paneles Query Performance Insight e Intelligent Insights (Azure SQL Analytics).","danger":"safe"},"mongodb-atlas":{"command":"atlas metrics processes <host>:<port> --granularity PT5M --period P1D --type SYSTEM_CPU_USER,QUERY_TARGETING_SCANNED_OBJECTS_PER_RETURNED","note":"Las mediciones a nivel de proceso se obtienen con 'atlas metrics processes' y las de disco con 'atlas metrics disks'. Para recomendaciones sobre consultas lentas use Performance Advisor: 'atlas performanceAdvisor slowQueryLogs list' y 'atlas performanceAdvisor suggestedIndexes list'.","danger":"safe"}}},{"id":"maintenance-window","title":"Ventana de mantenimiento y gestión de actualizaciones","aliases":["maintenance","maintenance window","window","update","patch"],"providers":{"aws-rds":{"command":"aws rds modify-db-instance --db-instance-identifier <id> --preferred-maintenance-window Sun:03:00-Sun:04:00   # apply pending maintenance now: aws rds apply-pending-maintenance-action --resource-identifier <arn> --apply-action system-update --opt-in-type immediate","note":"La ventana está en UTC, en formato ddd:hh24:mi-ddd:hh24:mi, y debe ser de al menos 30 minutos. Liste el mantenimiento pendiente con 'aws rds describe-pending-maintenance-actions' y aplíquelo de inmediato o en la siguiente ventana con apply-pending-maintenance-action.","danger":"caution"},"aws-aurora":{"command":"aws rds modify-db-cluster --db-cluster-identifier <cluster> --preferred-maintenance-window Sun:03:00-Sun:04:00","note":"En Aurora, la ventana de mantenimiento a nivel de clúster se define con modify-db-cluster y la de nivel de instancia con modify-db-instance. Las actualizaciones pendientes de motor/SO se gestionan con apply-pending-maintenance-action; las actualizaciones de versión del motor pueden requerir una interrupción.","danger":"caution"},"gcp-cloudsql":{"command":"gcloud sql instances patch <id> --maintenance-window-day SUN --maintenance-window-hour 3 --maintenance-release-channel production","note":"Define el día (MON..SUN), la hora (0-23, hora local de la instancia/UTC) y el canal de versiones (production o preview). Con los flags --deny-maintenance-period-* también puede definir un periodo durante el cual no se realiza mantenimiento.","danger":"caution"},"azure-sql":{"command":"az sql db update -g <rg> -s <server> -n <db> --maint-config-id SQL_Default   # or custom: /subscriptions/<sub>/providers/Microsoft.Maintenance/publicMaintenanceConfigurations/SQL_<Region>_DB_1","note":"La ventana de mantenimiento de Azure SQL se gestiona asignando una Maintenance Configuration predefinida (ventanas nocturnas entre semana/fin de semana en lugar de la predeterminada). Liste las configuraciones con 'az maintenance public-configuration list'.","danger":"caution"},"mongodb-atlas":{"command":"atlas maintenanceWindows update --dayOfWeek 1 --hourOfDay 3 --projectId <projectId>   # defer: atlas maintenanceWindows defer","note":"dayOfWeek 1=domingo ... 7=sábado, hourOfDay 0-23 (UTC). Use 'atlas maintenanceWindows defer' para posponer una semana el mantenimiento pendiente y 'atlas maintenanceWindows start' para iniciarlo de inmediato.","danger":"caution"}}},{"id":"encryption","title":"Ver/configurar el estado del cifrado (at-rest)","aliases":["encryption","encryption at rest","at-rest","kms","tde","cmek"],"providers":{"aws-rds":{"command":"aws rds describe-db-instances --db-instance-identifier <id> --query 'DBInstances[0].[StorageEncrypted,KmsKeyId]'   # at creation: aws rds create-db-instance ... --storage-encrypted --kms-key-id <kms-arn>","note":"El cifrado at-rest de RDS solo puede habilitarse AL CREAR la instancia; para habilitarlo en una instancia existente sin cifrar, haga una copia cifrada de un snapshot y restaure a partir de ella. La clave la gestiona AWS KMS.","danger":"caution"},"aws-aurora":{"command":"aws rds describe-db-clusters --db-cluster-identifier <cluster> --query 'DBClusters[0].[StorageEncrypted,KmsKeyId]'   # at creation: aws rds create-db-cluster ... --storage-encrypted --kms-key-id <kms-arn>","note":"En Aurora, el cifrado se define a nivel de clúster y solo en el momento de la creación. Para cifrar un clúster existente sin cifrar, copie un snapshot como cifrado y restáurelo. Todas las instancias y backups usan la misma clave KMS.","danger":"caution"},"gcp-cloudsql":{"command":"gcloud sql instances create <id> --disk-encryption-key projects/<project>/locations/<region>/keyRings/<ring>/cryptoKeys/<key>   # to view: gcloud sql instances describe <id> --format='value(diskEncryptionConfiguration.kmsKeyName)'","note":"Cloud SQL está cifrado de forma predeterminada con una clave gestionada por Google; una clave gestionada por el cliente (CMEK) solo puede asignarse al crear la instancia con --disk-encryption-key. Debe concederse a la cuenta de servicio de Cloud SQL el permiso de encrypt/decrypt sobre la clave.","danger":"caution"},"azure-sql":{"command":"az sql db tde set -g <rg> -s <server> -d <db> --status Enabled   # or T-SQL: ALTER DATABASE <db> SET ENCRYPTION ON;   # status: az sql db tde show -g <rg> -s <server> -d <db>","note":"En Azure SQL, el cifrado at-rest lo proporciona Transparent Data Encryption (TDE) y está habilitado de forma predeterminada en las bases de datos nuevas. Para usar su propia clave, configure 'az sql server tde-key set' (Azure Key Vault / BYOK) a nivel de servidor.","danger":"caution"},"mongodb-atlas":{"command":"atlas clusters describe <name> --output json | jq '.encryptionAtRestProvider'   # enable BYOK: atlas encryptionAtRest enable AWS --awsKmsMasterKeyID <keyId> --awsKmsRegion <region> --awsKmsRoleID <roleId>","note":"En Atlas, los datos están cifrados at-rest de forma predeterminada. Además, el cifrado envelope con una clave del cliente (BYOK) mediante AWS KMS / Azure Key Vault / GCP KMS se habilita en todo el proyecto con 'atlas encryptionAtRest' y se aplica al clúster.","danger":"caution"}}},{"id":"storage-autoscale","title":"Ampliar el almacenamiento / habilitar el autoescalado","aliases":["storage","disk space","autoscale","grow","disk"],"providers":{"aws-rds":{"command":"aws rds modify-db-instance --db-instance-identifier <id> --allocated-storage 200 --max-allocated-storage 1000 --apply-immediately","note":"--allocated-storage define el tamaño actual; --max-allocated-storage define el límite superior del autoescalado de almacenamiento. Ampliar el almacenamiento se hace en línea; sin embargo, debe esperar al menos 6 horas entre modificaciones del almacenamiento y el almacenamiento no puede reducirse.","danger":"caution"},"aws-aurora":{"command":"# Aurora storage is automatic; no manual setting required\naws rds describe-db-clusters --db-cluster-identifier <cluster> --query 'DBClusters[0].AllocatedStorage'","note":"El almacenamiento de Aurora escala de forma totalmente automática (en incrementos de 10 GB, hasta 128 TB según el motor) y crece a medida que se usa; --allocated-storage / --max-allocated-storage no se definen. Solo monitorea el uso; no se necesita gestionar el tamaño.","danger":"safe"},"gcp-cloudsql":{"command":"gcloud sql instances patch <id> --storage-auto-increase --storage-size 200GB   # upper limit: --storage-auto-increase-limit 1000","note":"--storage-auto-increase habilita el crecimiento automático cuando el disco se acerca a lleno; --storage-auto-increase-limit define el límite superior (0 = ilimitado). El almacenamiento solo puede aumentarse, NO PUEDE REDUCIRSE; elija con cuidado el tamaño inicial y el límite.","danger":"caution"},"azure-sql":{"command":"az sql db update -g <rg> -s <server> -n <db> --max-size 500GB","note":"En Azure SQL el tamaño máximo de la base de datos se define con --max-size; la base de datos crece automáticamente dentro del espacio asignado. El tamaño máximo depende del nivel de servicio/límite de vCore seleccionado. En el nivel Hyperscale el almacenamiento escala automáticamente y casi sin límite.","danger":"caution"},"mongodb-atlas":{"command":"atlas clusters update <name> --diskSizeGB 200   # for autoscaling, diskGBEnabled=true via extended configuration (advancedSettings / API)","note":"El tamaño del disco se aumenta con --diskSizeGB. En Atlas, el autoescalado de almacenamiento (Auto-scale storage) está habilitado de forma predeterminada en clústeres M10 y superiores; el disco crece automáticamente cuando está lleno al 90%. La habilitación/deshabilitación se gestiona en la configuración del clúster (autoScaling.diskGBEnabled).","danger":"caution"}}}]},"guide/best-practices.json":{"topics":[{"id":"indexing-strategy","title":"Estrategia de indexación","category":"Rendimiento","summary":"Los índices aceleran las consultas de lectura, pero añaden costo de escritura y de almacenamiento; añádalos según la necesidad y la medición.","points":["Apunte a las columnas de WHERE, JOIN y ORDER BY; ponga la columna más selectiva primero en un índice compuesto (regla del prefijo izquierdo).","Use covering indexes: incluya en el índice todas las columnas que necesita la consulta para eliminar el acceso a la tabla (lookup).","Identifique y elimine los índices no usados y redundantes (duplicados o prefijos de otro índice); cada índice ralentiza INSERT/UPDATE/DELETE.","Evite índices B-tree independientes en columnas de baja cardinalidad (p. ej. género, booleanos); use un índice parcial/filtrado si es necesario.","Verifique con EXPLAIN/EXPLAIN ANALYZE que el índice se usa realmente; una función o una conversión de tipo inhabilita el índice (como en WHERE UPPER(name))."],"level":"junior"},{"id":"normalization","title":"Normalización frente a desnormalización","category":"Diseño","summary":"La normalización garantiza la integridad de los datos y la ausencia de redundancia; la desnormalización es un compromiso deliberado a favor del rendimiento de lectura.","points":["Apunte a 3NF de forma predeterminada en sistemas OLTP; evite la redundancia de datos y las anomalías de actualización.","Desnormalice solo cuando haya un cuello de botella de rendimiento medido, y justifíquelo.","Garantice la consistencia de los datos desnormalizados con triggers, vistas materializadas o la capa de aplicación.","Para cargas de trabajo de informes/analítica, considere un esquema desnormalizado aparte (star schema) o una réplica de lectura.","La desnormalización prematura es deuda técnica; intente primero resolver el problema con el índice y la consulta adecuados."],"level":"senior"},{"id":"data-types","title":"Elegir el tipo de dato adecuado","category":"Diseño","summary":"El tipo de dato correcto y lo más pequeño posible mejora directamente el almacenamiento, el tamaño de los índices y el rendimiento.","points":["Elija el tipo más pequeño que cubra la necesidad: SMALLINT para la edad, DECIMAL/NUMERIC para importes monetarios (nunca FLOAT).","Almacene siempre los datos de tiempo con zona horaria (TIMESTAMPTZ / UTC); evite almacenar la hora local.","Use un ENUM o una tabla de búsqueda para conjuntos de valores fijos y limitados; restrinja en lugar de permitir texto libre.","Para texto de longitud variable, dimensione según la necesidad real o use TEXT en lugar de un VARCHAR(255) innecesariamente grande.","Si usa UUID como PK, vigile la fragmentación del índice; considere UUID secuenciales (v7) o BIGINT identity.","Elija la semántica de NULL de forma deliberada; haga explícita en el diseño la diferencia entre 'desconocido' y 'vacío'."],"level":"junior"},{"id":"naming-conventions","title":"Convenciones de nombres","category":"Diseño","summary":"Una nomenclatura coherente facilita la legibilidad, el mantenimiento y la comunicación del equipo; defina el estándar desde el principio.","points":["Elija una convención y aplíquela en todas partes: snake_case está muy extendido y es robusto frente a problemas de sensibilidad a mayúsculas/minúsculas.","Decida entre singular y plural para los nombres de tablas; prefiera nombres de columnas significativos y sin abreviaturas.","Nombre las claves foráneas de modo que el origen sea evidente (p. ej. customer_id) y dé prefijos estándar a índices/restricciones (fk_, ix_, uq_).","Evite usar palabras reservadas (order, user, group) como nombres de tablas/columnas.","Haga explícitas las columnas booleanas con los prefijos is_/has_; añada sufijos coherentes como _at, _date a las columnas de fecha."],"level":"junior"},{"id":"partitioning-strategy","title":"Estrategia de particionamiento","category":"Rendimiento","summary":"El particionamiento divide las tablas grandes en partes manejables; mejora el mantenimiento, la purga y el rendimiento de las consultas.","points":["Elija la clave de partición según los patrones de consulta y de archivado; el particionamiento RANGE por fecha es el más común para series temporales.","Verifique que el partition pruning funciona: las consultas deben incluir la clave de partición en la cláusula WHERE; de lo contrario, se escanean todas las particiones.","En lugar de borrar los datos antiguos, púrguelos al instante y casi sin bloqueos con DROP/DETACH de particiones.","Un número excesivo de particiones añade costo de planificación y de metadatos; elija una granularidad razonable (mensual/semanal).","Configure un job de mantenimiento que cree automáticamente las particiones futuras; evite errores por escrituras sin partición correspondiente."],"level":"senior"},{"id":"connection-pooling","title":"Connection pooling","category":"Operaciones","summary":"Un pool de conexiones elimina el costoso establecimiento de conexiones y protege la base de datos de avalanchas de conexiones.","points":["No abra una conexión nueva por solicitud; use un pool en la aplicación o en una capa intermedia (PgBouncer, HikariCP).","Dimensione el pool según los núcleos de CPU y la carga de trabajo; un pool sobredimensionado ahoga la base de datos (normalmente es mejor un pool pequeño).","Defina de forma deliberada los valores de vida útil de la conexión, idle y acquire timeout; habilite la detección de fugas.","Planifique el pool frente al límite max_connections de la base de datos; tenga en cuenta el total de varias instancias de la aplicación.","Use transacciones cortas para que las conexiones vuelvan pronto al pool; no espere I/O externa dentro de una transacción."],"level":"senior"},{"id":"transaction-design","title":"Diseño de transacciones (cortas y atómicas)","category":"Rendimiento","summary":"Las transacciones deben ser cortas, atómicas y abarcar una sola unidad lógica de trabajo; así se minimiza la duración de los bloqueos.","points":["Mantenga las transacciones lo más cortas posible; no incluya en ellas interacción con el usuario, llamadas a API externas ni cálculos largos.","Agrupe en una transacción solo las operaciones que deben tener éxito o fallar juntas.","Actualice siempre las filas en el mismo orden para reducir los conflictos de bloqueo (la base de la prevención de deadlocks).","Evite bloqueos innecesariamente amplios; no ponga operaciones de solo lectura en bloques de transacción innecesarios.","Defina una ruta de rollback clara ante errores; no deje commits parciales ni transacciones abiertas.","Haga commit de las operaciones masivas largas por lotes; una sola transacción gigante provoca bloat de WAL/undo."],"level":"senior"},{"id":"isolation-levels","title":"Elegir un nivel de aislamiento","category":"Rendimiento","summary":"El nivel de aislamiento define el equilibrio entre consistencia y concurrencia; elíjalo de forma deliberada según las reglas de negocio.","points":["Conozca los tipos de anomalías: dirty read, non-repeatable read, phantom read; entienda qué nivel evita cada una.","Read Committed es suficiente para la mayoría de los sistemas OLTP; suba de nivel solo cuando sea realmente necesario.","Use Serializable o bloqueo explícito (SELECT ... FOR UPDATE) para operaciones críticas de dinero/inventario.","Con Serializable, añada lógica de reintento automático para los fallos de serialización.","Conozca la diferencia de comportamiento entre los motores MVCC y los basados en bloqueos; el mismo nivel puede dar garantías distintas en motores distintos."],"level":"expert"},{"id":"deadlock-prevention","title":"Prevención de deadlocks","category":"Rendimiento","summary":"Un deadlock se produce cuando las transacciones esperan los recursos de las otras; en gran medida puede evitarse mediante el diseño.","points":["Acceda siempre a los recursos/filas en un orden coherente e idéntico; las diferencias de orden son la fuente más común de deadlocks.","Mantenga las transacciones cortas y reduzca el número de bloqueos; evite actualizaciones innecesariamente amplias.","Cuando sea necesario, haga que los conflictos sean predecibles con un bloqueo temprano (SELECT ... FOR UPDATE).","Capture los errores de deadlock en la aplicación y reintente la transacción automáticamente; eliminar los deadlocks por completo suele ser imposible.","Revise con regularidad los logs/grafos de deadlocks; elimine los patrones recurrentes cambiando los índices o el orden de acceso."],"level":"expert"},{"id":"sharding-strategy","title":"Estrategia de sharding / escalado horizontal","category":"Diseño","summary":"El sharding supera los límites de un solo servidor distribuyendo los datos entre varios nodos; como añade una complejidad considerable, debe ser el último recurso.","points":["Agote primero el escalado vertical, la indexación, el caching y las réplicas de lectura; haga sharding solo cuando sea realmente necesario.","Elija la shard key con cuidado: debe dar pocas consultas entre shards y una distribución uniforme, sin crear hotspots.","Use consistent hashing o shards virtuales para el reequilibrio (resharding); planifique el crecimiento desde el principio.","Los JOIN entre shards y las transacciones distribuidas son costosos; diseñe el esquema para minimizarlos.","Resuelva la generación de ID globalmente únicos (ID secuenciales al estilo snowflake, etc.) y la capa de enrutamiento de shards."],"level":"expert"},{"id":"backup-strategy","title":"Estrategia de backup (3-2-1)","category":"Backup y HA","summary":"Una estrategia de backup sólida es la última línea de defensa ante los desastres; un backup no cuenta hasta que se ha probado.","points":["Aplique la regla 3-2-1: 3 copias, 2 soportes distintos, 1 copia fuera del sitio (offsite/otra región).","Combine backups completos + incrementales con el archivado de WAL/binlog para obtener la recuperación a un punto en el tiempo (PITR).","Pruebe los backups con regularidad restaurándolos REALMENTE; un backup no probado no es un backup.","Cifre los backups tanto en reposo como en tránsito; restrinja el acceso y defina el periodo de retención por política.","Mantenga una copia inmutable o air-gapped frente al ransomware."],"level":"junior"},{"id":"hadr-design","title":"Diseño de HA/DR y RPO/RTO","category":"Backup y HA","summary":"El diseño de alta disponibilidad y recuperación ante desastres empieza por definir los objetivos de negocio como métricas de RPO y RTO.","points":["Defina junto con el negocio los objetivos de RPO (pérdida de datos aceptable) y RTO (tiempo de inactividad aceptable).","La replicación síncrona ofrece un RPO cercano a cero pero añade latencia; la asíncrona rinde bien pero conlleva riesgo de pérdida de datos.","Configure el failover automático y verifique que realmente funciona con simulacros de failover periódicos.","Use un mecanismo de quórum/witness para evitar el split-brain; nunca permita que ambos nodos se conviertan en master.","Mantenga el entorno de DR en una región física distinta; sea resiliente al fallo de una sola región/centro de datos."],"level":"expert"},{"id":"capacity-planning","title":"Planificación de capacidad y crecimiento","category":"Operaciones","summary":"La planificación de capacidad evita caídas inesperadas al aprovisionar recursos por delante de la tendencia de crecimiento.","points":["Mida a lo largo del tiempo la tasa de crecimiento de los datos, IOPS, CPU, memoria y uso de conexiones, y obtenga tendencias.","Defina alertas tempranas por umbral para el uso de disco (p. ej. 75%/85%); actúe antes de que se agote el espacio.","Planifique el crecimiento según previsiones; tenga en cuenta la carga pico y la fluctuación estacional.","Revise con regularidad el número de conexiones, el bloat de tablas/índices y las necesidades de archivado.","Decida la vía de escalado (vertical, réplicas, particionamiento/sharding) antes de llegar al cuello de botella."],"level":"senior"},{"id":"migration-strategy","title":"Estrategia de migración (mínima inactividad)","category":"Operaciones","summary":"Las migraciones de esquema y de datos deben seguir un plan reversible que minimice la inactividad, en línea cuando sea posible.","points":["Diseñe cada migración para que sea reversible e idempotente; nunca pase a producción sin un plan de rollback.","Evite el DDL bloqueante en tablas grandes; use herramientas de cambio de esquema en línea (PG: pg-osc/pgroll; MySQL: gh-ost, pt-osc).","Aplique el patrón expand-contract para cambios compatibles hacia atrás/adelante: primero añadir, desplegar y luego eliminar lo antiguo.","Mueva los grandes volúmenes de datos en lotes pequeños; no genere bloqueos y bloat de WAL con un único UPDATE gigante.","Ensaye primero la migración en un entorno similar a producción con un volumen de datos realista y mida su duración.","Plasme de antemano en un plan escrito el momento del cutover, los pasos de validación y la decisión de rollback."],"level":"senior"},{"id":"schema-versioning","title":"Versionado de esquemas (Flyway/Liquibase)","category":"Operaciones","summary":"Los cambios de esquema deben gestionarse con migraciones que convivan con el código, estén versionadas y se apliquen automáticamente; la intervención manual es propensa a errores.","points":["Versione todos los cambios de esquema con herramientas como Flyway/Liquibase y manténgalos en el control de código fuente.","No ejecute DDL manualmente en producción; cada cambio debe pasar por un archivo de migración revisado.","Mantenga las migraciones ordenadas y únicas; nunca modifique un archivo de migración ya aplicado, corríjalo con una migración nueva.","Aplique y valide las migraciones automáticamente en el pipeline de CI/CD; garantice la consistencia del esquema entre entornos.","Haga avanzar a la par el código de la aplicación y la versión del esquema; adopte el patrón expand-contract para transiciones compatibles hacia atrás."],"level":"senior"},{"id":"least-privilege","title":"Principio de mínimo privilegio","category":"Seguridad","summary":"Cada usuario y aplicación debe tener solo los privilegios necesarios para hacer su trabajo; el exceso de privilegios amplía la superficie de ataque.","points":["Use cuentas separadas y con privilegios limitados para la aplicación; nunca se conecte como superuser/admin.","Conceda los privilegios a roles en lugar de directamente a usuarios; gestione el acceso mediante la pertenencia a roles.","Permita solo las tablas y operaciones necesarias (SELECT/INSERT/UPDATE); evite un GRANT ALL amplio.","Revise los privilegios periódicamente y revoque los no usados; retire el acceso de inmediato cuando un empleado/servicio se va.","No incruste secretos (contraseñas, cadenas de conexión) en el código; use un gestor de secretos (vault) y rótelos con regularidad."],"level":"junior"},{"id":"encryption-basics","title":"Fundamentos de cifrado (at-rest/in-transit)","category":"Seguridad","summary":"Los datos deben cifrarse tanto al almacenarse como al transmitirse; el cifrado es una protección básica para el cumplimiento normativo y frente a fugas de datos.","points":["Imponga TLS en tránsito; rechace las conexiones sin cifrar y habilite la verificación de certificados.","Aplique cifrado en reposo (TDE o cifrado de disco/volumen) para que un disco/backup robado no pueda leerse.","Tome en serio la gestión de claves: mantenga las claves en un KMS/HSM separado y rótelas con regularidad.","Cifre además en la capa de aplicación (a nivel de campo) los campos muy sensibles, como números de tarjeta de crédito y documentos de identidad.","Cifre también los backups; extienda la protección de producción a los backups y las réplicas.","Enmascare o anonimice los datos sensibles en los entornos de prueba/desarrollo (data masking)."],"level":"senior"},{"id":"monitoring-basics","title":"Fundamentos de monitoreo (golden signals)","category":"Operaciones","summary":"Un monitoreo eficaz implica recopilar las métricas adecuadas y alertar con umbrales significativos; detecta los problemas antes de que los usuarios los noten.","points":["Monitoree las golden signals: latencia, tráfico, tasa de errores y saturación.","Añada métricas específicas de la base de datos: consultas lentas, tiempos de bloqueo/espera, número de conexiones, cache hit ratio, retraso de replicación.","Vincule las alertas al impacto y a umbrales accionables en lugar de a síntomas; las alertas ruidosas provocan fatiga de alertas.","Mantenga permanentemente habilitados el slow query log y las estadísticas de consultas (como pg_stat_statements) y revíselos con regularidad.","Conserve el histórico de métricas; construya una línea base para las tendencias y la planificación de capacidad.","Alerte siempre sobre condiciones críticas como el espacio en disco restante y el éxito de los backups."],"level":"junior"},{"id":"query-design","title":"Diseño de consultas y prevención de N+1","category":"Rendimiento","summary":"Una consulta eficiente obtiene solo los datos necesarios con el mínimo de idas y vueltas; N+1 es el error de rendimiento más común y más insidioso.","points":["Seleccione solo las columnas necesarias en lugar de SELECT *; esto mejora la red, la memoria y las oportunidades de covering index.","Evite el problema N+1: use un JOIN o una consulta por lotes (IN) en lugar de consultas individuales en un bucle; habilite el eager loading en el ORM.","Pagine los grandes conjuntos de resultados; prefiera la paginación keyset (seek) frente a OFFSET en datos grandes.","Deje a la consulta la lógica de negocio tanto como sea posible; no traiga toda la tabla para filtrar/agregar en la aplicación.","Examine el plan real y las estimaciones de filas con EXPLAIN ANALYZE; elimine los full table scans y las estimaciones erróneas.","Respalde las consultas de agregación frecuentes y costosas con una vista materializada o una tabla de resumen precalculada."],"level":"junior"},{"id":"large-table-management","title":"Gestión de tablas grandes (archivado/particionamiento)","category":"Rendimiento","summary":"Las tablas que crecen continuamente ralentizan con el tiempo las consultas, el mantenimiento y los backups; manténgalas bajo control con archivado y particionamiento.","points":["Separe los datos calientes (activos) de los fríos (históricos); mueva los datos antiguos a una tabla de archivo o a almacenamiento barato.","Particione la tabla por fecha/rango; purgue los datos antiguos al instante con DROP de partición en lugar de DELETE.","Realice los borrados/actualizaciones grandes en lotes pequeños; un único DELETE gigante genera bloqueos, bloat de WAL y bloat de tabla.","Monitoree con regularidad el bloat y el tamaño de tablas/índices; aplique una reorganización en línea (VACUUM/reorg) cuando sea necesario.","Mantenga los índices adecuados para las consultas frecuentes; mantener índices innecesarios en una tabla grande es muy costoso."],"level":"senior"},{"id":"caching-strategy","title":"Estrategia de caching","category":"Rendimiento","summary":"Una caché reduce la latencia y la carga al alejar de la base de datos las lecturas repetidas; el verdadero desafío es la invalidación.","points":["Use la caché para datos costosos, de lectura frecuente y que cambian poco; no intente cachearlo todo.","Elija una estrategia de invalidación: TTL, actualizar al escribir (write-through) o eliminar al escribir (write-invalidate).","Defina la tolerancia a datos obsoletos según las reglas de negocio; no dependa de la caché donde se requiera consistencia estricta.","Evite las cache stampedes (muchas solicitudes llenando a la vez una caché vacía) con bloqueo o request coalescing.","Monitoree el cache hit ratio; un hit ratio bajo muestra que la caché no aporta valor.","Una caché es una capa de aceleración, no un estado duradero; el sistema debe seguir funcionando correctamente cuando se pierde la caché."],"level":"senior"},{"id":"statistics-maintenance","title":"Disciplina de estadísticas y mantenimiento","category":"Operaciones","summary":"Unas estadísticas actualizadas y un mantenimiento regular garantizan que el planificador de consultas tome las decisiones correctas y que el rendimiento no se degrade con el tiempo.","points":["Mantenga las estadísticas al día (ANALYZE / auto-analyze); las estadísticas obsoletas empujan al planificador hacia un plan erróneo.","Ejecute VACUUM/limpieza con regularidad para eliminar filas muertas y bloat; es crítico en los motores MVCC.","Monitoree la fragmentación de índices y reconstruya en línea cuando sea necesario (REINDEX/rebuild).","Actualice las estadísticas manualmente tras una carga masiva; evite que el planificador trabaje con la distribución antigua.","Programe los jobs de mantenimiento en ventanas de bajo tráfico, monitoree su duración/impacto y automatícelos.","Vigile las regresiones de plan (una consulta que de repente se ralentiza); la actualidad de las estadísticas es lo primero que hay que comprobar."],"level":"senior"}]},"guide/best-practices-2.json":{"topics":[{"id":"iac","title":"Gestionar la infraestructura como código (IaC)","category":"Operaciones","summary":"Los servidores de base de datos, la red y la configuración deben definirse en código versionado, no a mano, para que los entornos sean reproducibles, auditables y reversibles.","points":["Mantenga como código, bajo control de versiones, las definiciones de servidores, parameter groups, red y usuarios con herramientas como Terraform/Pulumi/CloudFormation.","Prohíba los cambios manuales en la consola (click-ops); ejecute la detección de drift y corrija los recursos que se desvían del código.","Gestione las diferencias entre entornos (dev/stage/prod) con variables; construya entornos similares a producción a partir del mismo módulo.","Mantenga el state file remoto y bloqueado (p. ej. S3 + bloqueo con DynamoDB); no filtre secretos en el state como texto plano.","Revise los cambios mediante un flujo plan/apply; ponga puertas de aprobación delante de producción."],"level":"senior"},{"id":"automation-idempotency","title":"Automatización e idempotencia","category":"Operaciones","summary":"Las tareas operativas deben pasar de comandos repetidos manualmente a una automatización idempotente que produzca el mismo resultado sin importar cuántas veces se ejecute.","points":["Escriba cada script de forma idempotente para que volver a ejecutarlo no falle ni corrompa el estado (p. ej. CREATE IF NOT EXISTS en lugar de CREATE).","Vincule las tareas de mantenimiento recurrentes (backup, limpieza, estadísticas) a una automatización programada y registrada; no las deje a ejecuciones manuales.","Incorpore en los scripts comprobaciones explícitas de precondiciones y salidas seguras; volver a ejecutar una operación a medio terminar debe completarla desde donde se detuvo.","Ejecute la automatización desde código (Ansible/scripts + CI); no dependa de que alguien escriba comandos en una terminal en producción.","Ofrezca soporte de dry-run para las operaciones críticas; muestre el impacto antes de aplicarlo."],"level":"senior"},{"id":"observability","title":"Observabilidad (métricas/logs/trazas)","category":"Operaciones","summary":"La observabilidad combina métricas, logs y trazas distribuidas para pasar de 'qué ocurrió' a 'por qué ocurrió'.","points":["Recopile las tres señales juntas: métricas (tendencias), logs (contexto), trazas (recorrido de la solicitud); ninguna por sí sola revela la causa raíz.","Trace las consultas de extremo a extremo: correlacione las trazas de la aplicación con la latencia de la base de datos (usando estándares como OpenTelemetry).","Emita logs estructurados (JSON) con un correlation/trace id; los logs de texto libre dificultan la búsqueda.","Evite las etiquetas de alta cardinalidad; mantenga bajo control la explosión de cardinalidad y el costo.","Defina SLO y haga seguimiento del error budget; vincule la observabilidad a los objetivos de negocio."],"level":"senior"},{"id":"alerting-strategy","title":"Estrategia de alertas (poco ruido, accionable)","category":"Operaciones","summary":"Una buena estrategia de alertas despierta a una persona solo ante condiciones reales y accionables; el ruido provoca fatiga de alertas y agotamiento.","points":["Alerte sobre el impacto y la experiencia del usuario (violaciones de SLO) en lugar de sobre síntomas; no envíe un page por cada fluctuación de una métrica.","Cada alerta necesita un responsable, una condición y un runbook claros; una alerta sin una acción definida no vale nada.","Use histéresis y duración (for: 5m) en los umbrales; evite que los picos momentáneos disparen alertas.","Separe el paging de la notificación (ticket/email); envíe pages solo para lo que requiere intervención inmediata.","Revise las alertas con regularidad; corrija o elimine las que se disparan constantemente y se ignoran.","Cubra siempre los desastres silenciosos, como los fallos de backup, los discos llenos y la replicación detenida."],"level":"senior"},{"id":"dr-testing","title":"Disciplina de simulacros de DR/failover","category":"Backup y HA","summary":"Un plan de recuperación ante desastres solo es fiable cuando se valida con simulacros periódicos y realistas; un plan no probado es solo una suposición.","points":["Programe simulacros de failover y de restauración y ejecútelos periódicamente; un plan en papel revisado una vez al año no basta.","Mida el RTO y el RPO reales en cada simulacro; informe de la desviación respecto al objetivo y mejore.","Ensaye el failover planificado en un entorno similar a producción con tráfico real; no se limite a arrancar y detener el standby.","Simule escenarios como la pérdida de una región, el fallo de un disco y la corrupción de datos con game days.","Registre las lecciones aprendidas en el runbook tras cada simulacro; convierta en automatización los pasos que se recuerdan de memoria."],"level":"expert"},{"id":"blue-green-deploy","title":"Despliegue blue-green / sin inactividad","category":"Operaciones","summary":"Los despliegues blue-green y canary ejecutan la nueva versión junto a la antigua y desplazan el tráfico gradualmente, minimizando la inactividad y el riesgo.","points":["Mantenga dos entornos de producción (blue/green); despliegue la nueva versión en el inactivo, valídela y luego cambie el tráfico en un solo paso.","Si algo sale mal, devuelva el tráfico al entorno antiguo de inmediato; el rollback no debe requerir un nuevo despliegue.","Con canary, envíe un pequeño porcentaje del tráfico a la nueva versión, observe las métricas y aumente gradualmente.","Desacople el esquema de la base de datos del despliegue: ambas versiones del código deben funcionar con el mismo esquema (expand-contract).","Defina de antemano los health checks y los umbrales de rollback automático; acorte el tiempo de espera de decisiones humanas manuales."],"level":"senior"},{"id":"expand-contract","title":"Cambios de esquema compatibles hacia atrás (expand-contract)","category":"Diseño","summary":"El patrón expand-contract divide un cambio de esquema en añadir (expand), luego migrar y finalmente eliminar lo antiguo (contract), lo que permite una transición sin inactividad.","points":["Primero expanda: añada la nueva columna/tabla de forma compatible hacia atrás (nullable o con un valor predeterminado) sin romper la antigua.","Despliegue la aplicación de modo que funcione tanto con el esquema antiguo como con el nuevo, usando dual-write o dual-read.","Migre los datos a la nueva estructura en segundo plano en lotes pequeños y valídelos; no bloquee con una única migración gigante.","Contraiga solo cuando todas las instancias hayan pasado a la nueva versión y la validación haya concluido: elimine la columna/código antiguos.","Haga cada paso en un despliegue distinto; nunca 'añada y elimine lo antiguo al mismo tiempo', o el rollback se vuelve imposible.","Añada NOT NULL y las demás restricciones al final, una vez que los datos sean consistentes."],"level":"expert"},{"id":"secrets-management","title":"Gestión de secretos (contraseñas/claves)","category":"Seguridad","summary":"Las contraseñas de bases de datos, las claves de API y las cadenas de conexión no deben incrustarse en el código ni en la configuración; deben almacenarse de forma segura y rotarse con un gestor de secretos central.","points":["No incruste secretos en el código, repositorios, archivos env ni imágenes; use un almacén de secretos como Vault, AWS Secrets Manager o KMS.","Inyecte los secretos en las aplicaciones en tiempo de ejecución; no los deje en disco como texto plano.","Rote con regularidad las contraseñas y las claves; cuando sea posible, use credenciales de corta duración generadas automáticamente.","Conceda el acceso con mínimo privilegio y un log de auditoría; debe poder rastrearse quién accedió a qué secreto y cuándo.","Escanee el código en busca de secretos filtrados (git-secrets, trufflehog); revoque y rote de inmediato un secreto filtrado.","Haga que el acceso humano sea temporal y basado en aprobación (just-in-time) en lugar de personal y permanente."],"level":"senior"},{"id":"cost-optimization","title":"Optimización de costos en la nube","category":"Operaciones","summary":"El costo de las bases de datos en la nube puede reducirse notablemente sin sacrificar rendimiento mediante el dimensionamiento adecuado, el escalonamiento del almacenamiento y la limpieza de recursos no usados.","points":["Dimensione las instancias según las métricas de uso reales; reduzca los servidores con una utilización constantemente baja (right-sizing).","Use capacidad reservada/savings plans para la carga base predecible; deje la carga variable en on-demand.","Mueva los datos fríos a un nivel de almacenamiento más barato; separe los datos calientes de los de acceso poco frecuente.","Limpie los recursos no usados: los snapshots huérfanos, las réplicas inactivas y las bases de datos de prueba abandonadas son fugas de costo.","Asigne los costos a sus responsables con etiquetas y configure alertas de presupuesto; vea las sorpresas de inmediato, no a fin de mes.","Revise la retención de backups y el número de réplicas frente a las necesidades del negocio; el exceso es tanto riesgo como costo."],"level":"senior"},{"id":"multi-tenancy","title":"Diseño multi-tenant","category":"Diseño","summary":"En los sistemas multi-tenant, el modelo de aislamiento (tabla compartida, esquema separado, base de datos separada) es un compromiso deliberado entre seguridad, costo y escalabilidad.","points":["Elija el modelo de aislamiento: tabla compartida (tenant_id), un esquema por tenant o una base de datos por tenant; cada uno tiene un equilibrio distinto entre costo y aislamiento.","En el modelo compartido, aplique un filtro tenant_id a cada consulta; garantice a nivel del motor que no haya fugas con row-level security (RLS).","Ponga tenant_id en primer lugar en los índices relevantes; evite los escaneos entre tenants y los puntos calientes.","Aplique límites de recursos y cuotas frente al efecto noisy neighbor; un tenant no debe dejar sin recursos a los demás.","Diseñe desde el principio una vía (migración de tenants) para trasladar los tenants grandes o premium a un esquema/base de datos separados.","Piense en el backup, el borrado y la portabilidad de datos por tenant; debe poder atender las solicitudes de supresión de KVKK/GDPR."],"level":"expert"},{"id":"time-series-design","title":"Diseño de datos de series temporales","category":"Diseño","summary":"Los datos de series temporales son especiales por su alto volumen de escritura y sus patrones de consulta basados en el tiempo; deben diseñarse con particionamiento por fecha, rollups y una política de retención.","points":["Divida los datos con particionamiento RANGE basado en timestamp; purgue los datos antiguos a bajo costo con DROP de partición.","En lugar de acumular los datos brutos tal cual, genere resúmenes a intervalos regulares (rollup/continuous aggregate); deje que las consultas se ejecuten rápido sobre los resúmenes.","Ponga en primer lugar en el índice los componentes de tiempo y etiqueta (tag/origen); el patrón de consulta típico es 'origen concreto, rango concreto'.","Elija la herramienta adecuada: con volúmenes altos, los motores de series temporales como TimescaleDB y ClickHouse pueden ser mucho más eficientes que los relacionales.","Defina la política de retención desde el principio; conserve poco tiempo los datos brutos y mucho tiempo los agregados.","Almacene siempre el tiempo en UTC; gestione la hora local y los cambios de horario de verano en la capa de consulta."],"level":"senior"},{"id":"event-sourcing","title":"Fundamentos de event sourcing / CQRS","category":"Diseño","summary":"El event sourcing no registra el estado sino los eventos que lo cambian; CQRS separa los modelos de lectura y escritura para que cada uno escale de forma independiente.","points":["En lugar de sobrescribir el estado actual, acumule un flujo de eventos inmutable; obtenga el estado actual reproduciendo los eventos.","Con CQRS, separe el modelo de escritura (comandos) del modelo de lectura (consultas); acelere el lado de lectura con proyecciones desnormalizadas.","Versione el esquema de eventos y hágalo evolucionar de forma compatible hacia atrás; nunca elimine eventos antiguos, conviértalos a la nueva versión con upcasting.","Acepte que las proyecciones de lectura serán eventualmente consistentes y tenga en cuenta el retraso en el diseño.","Tome snapshots para evitar el costo de reproducir cada vez desde el principio un flujo de eventos largo.","Use este patrón no en todas partes sino en dominios que necesiten una pista de auditoría, reglas de negocio complejas e historial a lo largo del tiempo; no subestime la complejidad que conlleva."],"level":"expert"},{"id":"data-retention","title":"Política de retención y archivado de datos","category":"Operaciones","summary":"No todos los datos se conservan para siempre; una política de retención y archivado definida según los requisitos legales, de negocio y de costo aporta tanto cumplimiento como rendimiento.","points":["Defina el periodo de retención de cada tipo de dato según los requisitos del negocio y legales (KVKK/GDPR); un vago 'guardarlo por si acaso' no es una política.","Archive o elimine automáticamente los datos vencidos; no lo deje a una limpieza manual, que se olvida mientras las tablas crecen.","Mantenga los datos calientes en almacenamiento rápido y los archivos en almacenamiento barato/frío; escalone según la frecuencia de acceso.","Diseñe desde el principio el mecanismo para atender las solicitudes de borrado/anonimización de datos personales (derecho de supresión).","Mantenga los datos archivados recuperables y consultables cuando sea necesario; archivado no debe significar inaccesible.","Registre los borrados en el log de auditoría; debe poder rastrearse qué se borró, cuándo y por qué."],"level":"senior"},{"id":"change-management","title":"Gestión de cambios y seguridad en producción","category":"Operaciones","summary":"Todo cambio en una base de datos de producción debe ser revisado, reversible y trazable; la intervención manual sin control es la causa más común de inactividad.","points":["No ejecute DDL/DML ad hoc a mano en producción; cada cambio debe pasar por una migración o un script revisados por pares.","Aplique los cambios en una ventana de bajo tráfico con un plan de rollback preparado; no toque producción sin planificación.","Ensaye primero los comandos de riesgo en un entorno similar a producción con volúmenes de datos reales; mida su duración y su impacto en los bloqueos.","Restrinja y audite el acceso a producción: debe registrarse quién ejecutó qué comando y cuándo (acceso just-in-time).","Antes de un borrado/actualización, verifique la cláusula WHERE y el número de filas afectadas; ejecute dentro de una transacción y confirme primero con un SELECT.","Vincule los cambios a un calendario y a la comunicación; no sorprenda a otros equipos con cambios en producción no anunciados."],"level":"senior"}]},"guide/best-practices-3.json":{"topics":[{"id":"plan-reading-expert","title":"Cómo leer un plan de ejecución","category":"Rendimiento","summary":"Un plan de ejecución muestra las estimaciones del optimizador y los operadores físicos que eligió; el cuello de botella se esconde en la desviación entre el número de filas estimado y el real.","points":["Distinga los métodos de acceso: Full/Table Scan es para lecturas amplias en tablas grandes, Index Seek/Range Scan para predicados selectivos, Index Full Scan para lecturas ordenadas; Rows Removed by Filter o la división predicate/access muestra dónde se pierde la selectividad.","Adapte el método de join a la carga de trabajo: Nested Loop es ideal para un conjunto externo pequeño + un conjunto interno indexado, Hash Join para equi-joins grandes, Merge Join para dos conjuntos ya ordenados; una mala elección suele ser consecuencia de una mala estimación de cardinalidad.","Busque los errores de estimación de cardinalidad: compare las filas estimadas con las reales con EXPLAIN ANALYZE (PG), DBMS_XPLAN ALLSTATS LAST (Oracle) o SET STATISTICS PROFILE (MSSQL); una desviación superior a 10x sugiere que la elección del plan puede ser fundamentalmente errónea.","Lea el plan por su costo real: encuentre el operador que consume más tiempo/lecturas lógicas (logical reads, buffers); la unidad de 'cost' del optimizador es una estimación, no el cuello de botella real.","Detecte las advertencias de spill/espacio temp: un operador Hash/Sort que no cabe en memoria y se vuelca a disco (PG 'Sort Method: external merge', Sort/Hash Warning de MSSQL con SpillToTempDb en el plan) indica un work_mem/área de ordenación insuficiente.","Lea el árbol de operadores de dentro hacia fuera y de abajo hacia arriba: los nodos internos se ejecutan primero y las filas fluyen hacia arriba; calcule el costo total por operador multiplicando los loops por el tiempo real."],"level":"expert"},{"id":"parameter-sniffing-deep","title":"Parameter sniffing y bind peeking","category":"Rendimiento","summary":"El optimizador construye y cachea un plan según el valor del parámetro de la primera llamada; cuando valores posteriores con una distribución distinta heredan ese plan, el rendimiento puede cambiar drásticamente.","points":["Reconozca el síntoma: la misma consulta es rápida con unos parámetros y muy lenta con otros; muestra que el plan cacheado se compiló para un 'primer' valor atípico.","El riesgo es alto en columnas con distribución sesgada; los histogramas hacen que el optimizador sea sensible al valor, mientras que un único plan cacheado lo mantiene insensible al valor, y esa tensión es el origen del problema.","En MSSQL, OPTION (RECOMPILE) construye un plan en cada ejecución, OPTIMIZE FOR UNKNOWN elige un plan equilibrado basado en la densidad media y OPTIMIZE FOR (@p = value) fija un valor típico.","En Oracle, adaptive cursor sharing y los bind-aware cursors generan planes distintos para valores sesgados; en PG, la elección entre plan custom y generic se controla con plan_cache_mode.","Para una solución permanente, separe los planes por valor: diseñe statement splitting, índices filtrados/parciales o una ruta de acceso dedicada para los valores muy atípicos.","En OLTP, un exceso de RECOMPILE añade costo de CPU y de compilación (hard parse); mida y mantenga el equilibrio en las consultas que se ejecutan con frecuencia."],"level":"expert"},{"id":"plan-stability","title":"Estabilidad de planes: baselines, hints y plan guides","category":"Rendimiento","summary":"Se usan mecanismos de persistencia específicos de cada motor para congelar los planes verificados de las consultas críticas y evitar regresiones de plan inesperadas ante cambios de estadísticas/versión.","points":["En Oracle, fije los planes aprobados con SQL Plan Baselines (SPM); los planes nuevos solo se recopilan como candidatos hasta que se 'aceptan', y se verifican de forma controlada con evolve.","En MSSQL, detecte la regresión de plan con Query Store y fuerce el plan conocido como bueno con 'Force Plan'; Automatic Plan Correction puede automatizarlo.","PostgreSQL no tiene congelación de planes integrada; la estabilidad proviene de la extensión pg_hint_plan o de reescrituras de consultas, estadísticas extendidas y el diseño de índices.","Use los hints como último recurso, con justificación y comentarios; un hint presupone el esquema físico y la distribución de los datos, y se vuelve perjudicial cuando estos cambian.","Audite periódicamente el inventario de baselines/planes forzados; los planes fijados pueden perder optimalidad con el tiempo a medida que crecen los datos, así que no caiga en la trampa de 'estable pero lento'.","Capture los planes de las consultas críticas antes de una actualización de versión; prepare baselines como red de seguridad frente a cambios de comportamiento del optimizador."],"level":"expert"},{"id":"latch-mutex-contention","title":"Contención de latches, mutex y spinlocks","category":"Rendimiento","summary":"Los latches/mutex son bloqueos ligeros que protegen estructuras en memoria; con alta concurrencia, la contención sobre ellos quema CPU en bucles de spinlock y detiene el escalado.","points":["Distinga los locks de los latches: los locks protegen datos lógicos (fila/tabla) durante una transacción, los latches protegen estructuras físicas de memoria (buffers, cadenas hash) durante microsegundos; sus métodos de diagnóstico son distintos.","En Oracle, la contención del latch 'cache buffers chains' suele apuntar a un hot block; una tabla pequeña leída con frecuencia o el extremo creciente de un índice monótonamente creciente (right-hand index) es una causa común.","Las inserciones con claves secuencialmente crecientes (identity/sequence) provocan contención en la última página/bloque (hot page, PAGELATCH); repártalas con hash partitioning, un índice reverse-key o una clave aleatorizada.","En MSSQL, monitoree las estadísticas de spinlocks y las esperas PAGELATCH_*; para la contención de asignación en tempdb, use varios archivos de datos y una configuración adecuada de tempdb.","La contención de spinlocks se manifiesta como CPU alta con bajo throughput; la solución es reducir la contención a nivel de código/arquitectura, y añadir hardware a menudo la empeora.","Tener en cuenta NUMA y limitar el número de conexiones (pooling) reduce la contención de latches; un paralelismo excesivo puede aumentar la presión sobre los latches."],"level":"expert"},{"id":"wait-event-methodology","title":"Metodología de análisis de wait events","category":"Rendimiento","summary":"Gestione el rendimiento midiendo dónde pasa el sistema su tiempo esperando, no adivinando; el análisis de esperas top-down es el método científico que acota sistemáticamente el cuello de botella.","points":["Descomponga el DB time en sus componentes: separe el tiempo en CPU del tiempo de espera; empiece por el componente mayor y no se desvíe hacia la microoptimización.","Encuentre el wait event principal con ASH/AWR y 'Top Wait Events' en Oracle, pg_stat_activity + pg_wait_sampling en PG, y sys.dm_os_wait_stats y Query Store en MSSQL.","Interprete la wait class: las esperas de I/O apuntan al almacenamiento/los planes, las de bloqueo al diseño de la concurrencia, las de latch a puntos calientes y las de red/commit a problemas de configuración.","Profundice hasta el nivel de sesión/consulta: parta de las esperas totales, vaya al SQL que más contribuye y luego al plan de ese SQL para aislar la causa (drill-down).","Vincule la medición a la ventana de la carga de trabajo: tome la diferencia (delta) de los contadores acumulados entre dos puntos; los totales son engañosos, lo que importa son las tasas y los intervalos de tiempo.","Filtre del ruido las esperas idle/benignas; no toda espera es un problema, lo que importa son las esperas en primer plano que contribuyen al tiempo de respuesta del usuario."],"level":"expert"},{"id":"plan-regression-detection","title":"Detectar regresiones de plan de ejecución","category":"Rendimiento","summary":"Una consulta que se ralentiza de repente suele deberse a un cambio de plan, no a un cambio de código; detectar las regresiones de forma proactiva es más barato que apagar incendios de forma reactiva.","points":["Conserve y compare el historial de planes: haga seguimiento de los cambios de plan y de las tendencias de duración/recursos para el mismo sql_id/query_hash con Query Store de MSSQL y AWR/SQL Monitor de Oracle.","Identifique el desencadenante de la regresión: las actualizaciones de estadísticas, el crecimiento del volumen de datos, las actualizaciones de versión, los cambios de parámetros o los índices eliminados son las causas más comunes.","Configure alertas automáticas: genere una alerta cuando cambie el plan hash y la duración media/las lecturas lógicas superen un umbral; haga seguimiento de métricas por ejecución, no acumuladas.","Prepare una vía de reversión rápida: fije temporalmente el plan conocido como bueno con Force Plan (MSSQL) o una SQL Plan Baseline (Oracle) y corrija la causa raíz después.","En las regresiones causadas por estadísticas, examine las estadísticas bloqueadas/actuales y la actualidad de los histogramas; la recopilación automática de estadísticas puede estar programada en horas críticas.","Ejecute pruebas de regresión antes de una actualización: reproduzca una carga de trabajo representativa con el optimizador antiguo y el nuevo e informe de las diferencias de plan (un enfoque similar a Oracle SPA)."],"level":"expert"},{"id":"mvcc-vacuum-internals","title":"Funcionamiento interno de MVCC y vacuum/undo","category":"Funcionamiento interno","summary":"MVCC evita el bloqueo entre lectores y escritores conservando versiones antiguas de las filas; cómo y dónde se guardan estas versiones (undo frente a dentro de la tabla) determina la estrategia de mantenimiento.","points":["En PostgreSQL, las versiones antiguas permanecen dentro de la propia tabla como tuplas muertas; VACUUM las limpia; de lo contrario, se producen bloat y crecimiento de los índices.","Oracle y MySQL/InnoDB guardan las versiones antiguas en segmentos de undo/rollback; si una lectura de larga duración agota el undo, aparecen errores como 'ORA-01555 snapshot too old'.","Ajuste autovacuum a la carga de trabajo: reduzca el scale factor y el cost delay en tablas con muchos update/delete; dé ajustes por tabla a las tablas agresivas.","Mantenga sana la visibility map; los index-only scans y el seguimiento de tuplas congeladas dependen de ella, y el rendimiento se degrada si se descuida.","Monitoree el riesgo de transaction ID wraparound: haga seguimiento de la métrica datfrozenxid/age en PG y evite que el vacuum agresivo/anti-wraparound se active en horario laboral.","Favorezca las actualizaciones HOT (Heap-Only Tuple): las actualizaciones que no cambian una columna indexada se realizan in situ sin hacer crecer el índice; un fillfactor adecuado lo facilita."],"level":"expert"},{"id":"wal-redo-log-mechanics","title":"Mecánica de WAL/redo/log de transacciones","category":"Funcionamiento interno","summary":"La durabilidad se logra escribiendo el cambio en el log antes de que las páginas de datos se escriban en disco; la tasa y el tamaño del flujo de log determinan tanto la durabilidad como el rendimiento de escritura.","points":["Entienda el principio write-ahead: para que un commit retorne basta con que los registros de log correspondientes estén en almacenamiento duradero; las páginas de datos sucias se escriben más tarde de forma diferida.","Ajuste el group commit y el comportamiento de log flush: equilibre la frecuencia de fsync con los requisitos de durabilidad de la carga de trabajo mediante commit_delay/synchronous_commit en PG y el comportamiento del log writer en Oracle.","Monitoree la tasa de generación de log (generación de WAL/redo); un aumento repentino apunta a actualizaciones masivas, imágenes de página completa tras un checkpoint (full_page_writes/FPI) o mantenimiento de índices, y aumenta el retraso de DR.","El log no solo es una fuente para la recuperación, sino también para la replicación y CDC; la replicación lógica y el WAL shipping se alimentan de este flujo, así que diseñe la política de archivado/limpieza en consecuencia.","Separe el disco de log del disco de datos y póngalo en almacenamiento de baja latencia; las escrituras de log son secuenciales y sensibles a la latencia, y el throughput de commit depende directamente de ellas.","Protéjase del crecimiento descontrolado del log: en MSSQL use el modelo de recuperación correcto y log backups periódicos; en PG asegúrese de que el archivado/los replication slots no se queden atrás."],"level":"expert"},{"id":"buffer-pool-management","title":"Gestión del buffer/shared pool y desalojo","category":"Funcionamiento interno","summary":"El buffer pool reduce la I/O de disco manteniendo los datos calientes en memoria; el algoritmo que decide qué página se desaloja y cuándo determina el hit ratio y la estabilidad.","points":["No tome como objetivo solo el buffer cache hit ratio; incluso una proporción alta puede ocultar la latencia de las lecturas físicas, así que observe también la I/O absoluta y el tiempo de espera.","Entienda el algoritmo de desalojo: frente a un LRU puro contaminado por los escaneos secuenciales, PG usa clock-sweep, Oracle una variante touch-count/LRU e InnoDB un LRU con midpoint insertion.","Evite que los grandes escaneos puntuales expulsen los datos calientes del pool (cache pollution); protéjalos con buffer pools separados o estrategias de acceso adecuadas para escaneos.","Gestione la proporción de páginas sucias y la presión de escritura: demasiadas páginas sucias provocan picos de I/O en el checkpoint, y una escritura demasiado agresiva genera I/O innecesaria; mida el equilibrio.","Dimensione la memoria según el working set; el objetivo es cubrir el conjunto al que se accede activamente, no el total de los datos, y el sobredimensionamiento ofrece rendimientos decrecientes.","En el shared pool de Oracle, monitoree la contención del library/dictionary cache y los hard parses frecuentes; usar bind variables en lugar de SQL con literales reduce la presión sobre el shared pool."],"level":"expert"},{"id":"checkpoint-tuning","title":"Ajuste de checkpoints","category":"Funcionamiento interno","summary":"Un checkpoint acorta el tiempo de recuperación escribiendo las páginas sucias en disco; los checkpoints demasiado frecuentes generan picos de I/O y los demasiado espaciados una recuperación larga, y este compromiso debe ajustarse.","points":["Equilibre el tiempo de recuperación frente a la I/O en ejecución: checkpoints espaciados implican menos carga de escritura pero una recuperación tras caída más larga; los frecuentes, lo contrario.","En PG, reparta los checkpoints en el tiempo con checkpoint_timeout y max_wal_size; suavice el pico de I/O distribuyendo las escrituras a lo largo del intervalo con checkpoint_completion_target.","El indirect checkpoint de MSSQL (target recovery time) proporciona una escritura continua en segundo plano; hace predecible la latencia al evitar grandes oleadas repentinas de checkpoint.","Correlacione la frecuencia de checkpoint con la tasa de generación de log/WAL; en periodos de escritura intensa, monitoree si se activa el disparador por tamaño en lugar del disparador por tiempo.","full_page_writes (PG) registra una página completa en el primer cambio tras un checkpoint; si los checkpoints son demasiado frecuentes, el volumen de WAL se dispara y aumentan el costo de DR y de disco.","Detecte los síntomas de 'checkpoint sink': aumento de la latencia de escritura y esperas de fsync durante los checkpoints; revise la cola y la distribución del almacenamiento."],"level":"expert"},{"id":"statistics-strategy","title":"Estrategia de recopilación de estadísticas","category":"Funcionamiento interno","summary":"La calidad de las decisiones del optimizador depende de la actualidad y la resolución de las estadísticas; la tasa de muestreo, los histogramas y las estadísticas extendidas determinan las estimaciones de cardinalidad.","points":["Elija la tasa de muestreo según el tamaño y el sesgo de los datos: una muestra demasiado pequeña produce estimaciones erróneas en distribuciones sesgadas, y una demasiado grande genera un costo de recopilación innecesario.","Modele el sesgo con histogramas: default_statistics_target de PG, los histogramas FREQUENCY/HYBRID de Oracle y los histogramas de estadísticas de MSSQL exponen al optimizador los valores frecuentes y desequilibrados.","Capture la correlación entre columnas con estadísticas extendidas: CREATE STATISTICS (ndistinct/dependencies) de PG y las extended stats de Oracle corrigen los errores de estimación en columnas interdependientes.","Desacople los calendarios de recopilación de la carga de trabajo: evite que el job automático de estadísticas/gather se ejecute en horas críticas y provoque I/O e inestabilidad de planes.","Gestione el riesgo de estadísticas obsoletas en tablas de crecimiento rápido; las actualizaciones automáticas por umbral pueden retrasarse, así que añada una recopilación manual/programada para las tablas críticas.","Tenga en cuenta que los cambios de estadísticas pueden causar regresiones de plan; en entornos críticos, bloquee las estadísticas (locked stats) o publíquelas de forma controlada con pending publish."],"level":"expert"},{"id":"partition-pruning-join","title":"Partition pruning y partition-wise joins","category":"Escala/Arquitectura","summary":"El particionamiento no solo divide una tabla; la ganancia real es que las consultas nunca leen particiones irrelevantes (pruning) y que los joins se ejecutan en paralelo por partición.","points":["Elija la clave de partición según el filtro más frecuente y selectivo; el pruning solo funciona cuando el predicado de la consulta incluye la clave de partición; de lo contrario, se escanean todas las particiones.","Distinga el pruning estático del dinámico: los predicados constantes eliminan particiones en tiempo de compilación, y los valores bind/join en tiempo de ejecución (runtime pruning); verifique el plan en consecuencia.","Para los partition-wise joins, particione ambas tablas por la misma clave con un esquema compatible; el optimizador reduce un join grande a pequeños joins partición a partición y los paraleliza.","Equilibre el número y la granularidad de las particiones: demasiadas particiones implican costo de metadatos y de planificación, y muy pocas un pruning débil; dimensiónelas según el patrón de acceso.","Combine la gestión del ciclo de vida con el particionamiento: haga DETACH/DROP o archive las particiones antiguas; esto ofrece una limpieza instantánea y con poco logging en lugar de DELETE.","Recuerde que las funciones/conversiones de tipo o las expresiones que ocultan la clave rompen el pruning; verifique con EXPLAIN el número de particiones escaneadas."],"level":"expert"},{"id":"shard-key-selection","title":"Elección de la shard key y puntos calientes","category":"Escala/Arquitectura","summary":"La shard key determina el destino de un sistema distribuido; una mala elección acumula la carga en un solo nodo (hot shard), detiene el escalado y encarece las consultas entre shards.","points":["Elija una clave de alta cardinalidad que reparta la carga de forma uniforme; una clave de baja cardinalidad o temporal/monótonamente creciente acumula la carga en un solo shard y crea un punto caliente.","Diseñe la clave para que las consultas más frecuentes se resuelvan en un solo shard; las consultas entre shards (scatter-gather) multiplican la latencia y el costo de coordinación.","El hash sharding iguala la carga pero dispersa las consultas por rango; el range sharding es bueno para consultas por rango pero aumenta el riesgo de puntos calientes; elija según el patrón de acceso.","Minimice la redistribución al añadir/quitar nodos con consistent hashing; un hash por módulo fijo mueve casi todos los datos cuando cambia el número de nodos.","Diseñe el plan de resharding desde el principio; cambiar la clave más adelante es costoso y arriesgado, así que modele de antemano el escenario de crecimiento.","Al dividir por tenant/cliente, prepare un shard dedicado o una estrategia de subparticionamiento (split) para que un tenant gigante (whale) no llene un solo shard."],"level":"expert"},{"id":"read-write-splitting","title":"Separación de lecturas y escrituras y enrutamiento a réplicas","category":"Escala/Arquitectura","summary":"Enrutar las lecturas a réplicas alivia al primario de escritura; pero como el retraso de replicación puede romper las garantías de consistencia, el enrutamiento debe hacerse de forma deliberada.","points":["Al repartir la carga de lectura entre réplicas, monitoree continuamente el retraso de replicación; una réplica desactualizada puede mostrar datos antiguos a los usuarios.","Enrute al primario los flujos que requieren read-your-writes (el usuario que escribe lee de inmediato), o garantice una réplica actualizada con causal reads/tokens.","Haga el enrutamiento en la capa de infraestructura, no en la aplicación: distribuya de forma transparente con un proxy/router (ProxySQL para MySQL; para PG, Pgpool-II o HAProxy con endpoints de lectura/escritura separados, ya que PgBouncer no separa lecturas y escrituras; en MSSQL, read-only routing mediante application intent).","Retire una réplica del pool cuando se supere el umbral de retraso; enviar lecturas a una réplica con mucho retraso produce inconsistencia de datos bajo la ilusión de 'escalar'.","Aísle en una réplica dedicada las lecturas pesadas y tolerantes al retraso, como analítica/informes; proteja de esa carga a las réplicas de OLTP.","Pruebe que el enrutamiento de lecturas se actualiza automáticamente en un escenario de failover; un router que no conoce al nuevo primario provoca split-brain o inactividad."],"level":"expert"},{"id":"connection-pool-sizing","title":"Cálculo del tamaño del pool de conexiones","category":"Escala/Arquitectura","summary":"El tamaño del pool se determina mediante un cálculo basado en la ley de Little y el número de núcleos, no por intuición; demasiadas conexiones reducen el throughput por contención y cambios de contexto.","points":["Aplique la ley de Little: concurrencia necesaria ≈ tasa de solicitudes (throughput) × tiempo medio de procesamiento; obtenga la concurrencia objetivo a partir de valores medidos, no de suposiciones.","Un pool pequeño suele ser más rápido: cuando las conexiones activas superan el paralelismo físico de núcleos/disco, los cambios de contexto de CPU y la contención de latches reducen el throughput.","Como punto de partida aproximado, use un pool base de conexiones cercano al número de núcleos (p. ej. un número reducido de conexiones reales detrás de PgBouncer) y ajústelo midiendo bajo carga.","Planifique el pool del lado de la aplicación junto con max_connections de la base de datos; N instancias de la aplicación × tamaño del pool no debe superar el límite de la base de datos.","En motores con un alto costo de proceso por conexión como PG, use PgBouncer/transaction pooling; cada conexión cuesta memoria y planificación.","Monitoree la saturación del pool, el tiempo de espera en cola y los timeouts; una cola que crece constantemente indica un cuello de botella o un subdimensionamiento, así que no amplíe el pool a ciegas."],"level":"expert"},{"id":"lock-escalation-deadlock","title":"Lock escalation y patrones de prevención de deadlocks","category":"Fiabilidad","summary":"Muchos bloqueos de fila pueden escalar a un bloqueo de tabla y acabar con la concurrencia; un deadlock es el resultado evitable de un orden de bloqueo inconsistente.","points":["Divida las operaciones masivas grandes que disparan la lock escalation (lotes/fragmentos); bloquear millones de filas en una sola transacción provoca escalation y bloqueos.","El patrón básico para prevenir deadlocks es un orden de bloqueo coherente: todas las rutas de código deben bloquear los recursos en el mismo orden (p. ej. por orden de clave primaria).","Mantenga las transacciones cortas y realice una sola unidad atómica de trabajo; las transacciones que contienen interacción con el usuario, llamadas remotas o cálculos largos retienen los bloqueos innecesariamente.","Elija el nivel de aislamiento de forma deliberada: un aislamiento innecesariamente alto causa exceso de bloqueos, y uno innecesariamente bajo causa anomalías; en los motores MVCC las lecturas ya evitan la mayoría de los bloqueos.","Recopile grafos/trazas de deadlocks (deadlock graph de MSSQL, log_lock_waits de PG, trace de Oracle); registre la víctima y rastree los patrones recurrentes hasta la causa raíz.","Implemente lógica de reintento: reintente de forma idempotente la transacción víctima del deadlock; es una red de seguridad para la contención que no puede evitarse por diseño."],"level":"expert"},{"id":"long-transaction-bloat","title":"Transacciones largas y gestión de bloat/undo","category":"Fiabilidad","summary":"Una transacción larga que se deja abierta bloquea la limpieza de versiones antiguas en los motores MVCC y desencadena una cadena de bloat, presión de undo y colapso del rendimiento.","points":["Monitoree y alerte sobre las transacciones abiertas/de larga duración: idle in transaction en PG y las consultas largas que retienen undo activo en Oracle detienen la limpieza (vacuum/recuperación de undo).","En PG, una sola transacción larga impide que VACUUM elimine las tuplas muertas; tablas e índices sufren bloat, y una sola sesión 'zombi' puede ralentizar toda la base de datos.","Termine automáticamente las transacciones atascadas con idle_in_transaction_session_timeout y statement_timeout; construya una línea de defensa contra las fugas de la aplicación.","En Oracle/InnoDB, una lectura larga agota el undo y produce 'snapshot too old'; dimensione el espacio/la retención de undo según la consulta más larga de la carga de trabajo.","Divida los trabajos batch con intervalos de commit cortos; una sola transacción gigante acumula undo/WAL y prolonga peligrosamente el tiempo de rollback.","Mida y recupere el bloat existente: mida el bloat con pgstattuple y recupere espacio y eficiencia de índices con VACUUM FULL/pg_repack o una reorganización en línea."],"level":"expert"},{"id":"dr-game-day","title":"Simulacros de DR (game day) y validación de RPO/RTO","category":"Fiabilidad","summary":"Un plan de backup y failover no probado es una suposición; la capacidad de DR solo se demuestra midiendo el RPO/RTO en simulacros realistas.","points":["Derive el RPO y el RTO de los requisitos de negocio y mídalos en la práctica: el retraso de backup/replicación determina el RPO, y el tiempo de recuperación + validación determina el RTO; compare el objetivo con la realidad.","Restaure los backups de forma periódica y automática: un backup que nunca se ha restaurado es un backup de Schrödinger; sin una prueba de restauración, la afirmación de que existe un backup no está verificada.","Planifique simulacros de game day en un entorno casi de producción con escenarios de fallo realistas (pérdida del primario, caída de una región, corrupción de datos).","Ejecute de extremo a extremo los procedimientos de failover y failback; incluya el enrutamiento de DNS/conexiones, la reconexión de la aplicación y la promoción de réplicas.","Demuestre la capacidad de recuperación a un punto en el tiempo (PITR): pruebe que puede volver a un momento concreto con archivos de log + backups completos en un escenario de corrupción lógica.","Obtenga de cada simulacro el RTO/RPO medidos, las sorpresas y las acciones a realizar; actualice el runbook y verifique la mejora en el siguiente simulacro."],"level":"expert"},{"id":"observability-slo","title":"Observabilidad y SLO/golden signals","category":"Fiabilidad","summary":"Para gestionar la base de datos según el impacto en el negocio, vincule las golden signals (latencia, tráfico, errores, saturación) a SLO y priorice con un error budget.","points":["Monitoree las cuatro golden signals: latencia, tráfico (throughput), tasa de errores (errors) y saturación; resumen el estado de salud sin profundizar en la causa raíz.","Mida la latencia con percentiles (p95/p99), no con promedios; el promedio oculta la latencia de cola y la peor experiencia de usuario.","Defina SLO y un error budget; alerte sobre síntomas (violaciones de SLO), no sobre cada fluctuación de una métrica de bajo nivel, para evitar la fatiga de alertas.","Aplique el método USE (Utilization, Saturation, Errors) a los recursos: observe por separado la saturación de CPU, memoria, I/O de disco y pool de conexiones.","Correlacione el monitoreo entre capas: vea la cadena consulta -> wait event -> saturación de recursos -> métrica de negocio en un único eje temporal.","Configure métricas + logs + trazas en conjunto; las métricas por sí solas responden al 'qué', los logs/trazas al 'por qué', y sin ellos el MTTR crece."],"level":"expert"},{"id":"least-privilege-roles","title":"Mínimo privilegio y diseño de roles","category":"Seguridad","summary":"Que cada cuenta tenga solo los privilegios mínimos necesarios para su trabajo reduce el radio de impacto de una brecha; los roles ofrecen una abstracción manejable en lugar de conceder privilegios directamente.","points":["Conceda los privilegios a roles, no a usuarios; diseñe los roles por función (lector, escritor, DDL, operaciones) y asigne los usuarios a roles, evitando los grants directos.","Dé a las cuentas de aplicación solo los privilegios de objetos y operaciones necesarios; DDL, SUPERUSER/DBA y los privilegios amplios de esquema no deben existir en una cuenta de aplicación de producción.","Personalice y audite el acceso de los administradores humanos: use identidades individuales + acceso elevado bajo demanda (JIT/break-glass) en lugar de una cuenta de administrador compartida.","Revoque los privilegios amplios de PUBLIC/predeterminados; configure los default privileges y los controles a nivel de esquema para evitar que los derechos predeterminados se filtren a los objetos nuevos.","Revise con regularidad las cuentas privilegiadas (recertificación de accesos); limpie periódicamente las cuentas no usadas, las de empleados que se han ido y las que tienen privilegios excesivos.","Imponga la separación de funciones; la misma persona no debe poder cambiar producción y además borrar la pista de auditoría, así que proteja la escritura de auditoría con un privilegio separado."],"level":"expert"},{"id":"encryption-decision","title":"Decisiones de cifrado: at-rest, in-transit, TDE","category":"Seguridad","summary":"El cifrado se elige según el modelo de amenazas; cada capa protege frente a un ataque distinto, y la gestión de claves suele ser más crítica que el algoritmo de cifrado.","points":["Aclare la amenaza: at-rest (discos/backups robados), in-transit (escucha en la red) y a nivel de aplicación (no expuesto a nadie, DBA incluidos) son protecciones distintas; decida cuál necesita.","Para in-transit, imponga TLS en las conexiones cliente-servidor; no desactive la validación de certificados, ya que le deja expuesto a ataques man-in-the-middle (MITM).","Para at-rest, compare TDE con las opciones de cifrado del sistema de archivos/disco; TDE protege los datafiles y los backups, pero no los datos en memoria ni el acceso autorizado de los DBA.","Para los campos más sensibles, use cifrado a nivel de aplicación/columna o un enfoque del lado del cliente como Always Encrypted, de modo que ni siquiera el DBA pueda ver el texto plano.","Tome en serio la gestión de claves: almacene las claves en un KMS/HSM separado de los datos y planifique la rotación y el control de acceso; si la clave se filtra, el cifrado no vale nada.","Mida el impacto funcional y en el rendimiento: la indexación/búsqueda está restringida en las columnas cifradas y TDE añade costo de CPU; tome la decisión como un compromiso medido."],"level":"expert"},{"id":"audit-strategy","title":"Estrategia de auditoría","category":"Seguridad","summary":"La pista de auditoría aporta evidencia de los eventos de seguridad y de los requisitos de cumplimiento; qué se registra, adónde va la pista y cuánto tiempo se conserva determinan el equilibrio entre ruido y evidencia.","points":["Derive qué auditar de las amenazas y del cumplimiento normativo: priorice las operaciones privilegiadas, los inicios de sesión/fallos de inicio de sesión, los cambios de esquema (DDL) y el acceso a datos sensibles.","No caiga en la trampa de registrarlo todo; una auditoría excesiva degrada el rendimiento y ahoga la señal en el ruido, así que apunte a los eventos críticos de forma selectiva y con un propósito.","Escriba la pista de auditoría en un destino separado y seguro que el DBA de producción no pueda modificar (un recolector de logs externo/SIEM); evite la manipulación de la pista mediante la separación de funciones.","Use las capacidades del motor: ajuste el nivel de detalle a sus necesidades con Oracle Unified/Fine-Grained Audit, MSSQL SQL Audit y PG pgAudit.","Defina la retención según los requisitos de cumplimiento (KVKK/GDPR/PCI-DSS, etc.) y automatice la política de archivado/limpieza; preserve la integridad de la pista.","No se limite a recopilar la pista; revísela con regularidad y vincúlela a alertas: la auditoría solo aporta valor cuando señala anomalías y las convierte en acciones, no como un archivo muerto."],"level":"expert"}]},"guide/best-practices-4.json":{"topics":[{"id":"oracle-rac-cache-fusion","title":"Oracle RAC: Cache Fusion y el interconnect","category":"Oracle","summary":"En RAC, varias instancias comparten la misma base de datos; Cache Fusion mantiene la coherencia enviando bloques entre nodos por el interconnect en lugar de a través del disco.","points":["Cuando una instancia necesita un bloque que reside en el buffer cache de otra instancia, Cache Fusion lo transfiere de RAM a RAM por el interconnect privado; no lo escribe en disco para volver a leerlo (disk ping).","Global Cache Service (procesos GCS/LMS) gestiona la propiedad y el modo (shared/exclusive) de los bloques, mientras que Global Enqueue Service (GES) gestiona los bloqueos y los enqueues.","Cada bloque tiene un nodo master; la ubicación del bloque se registra en el Global Resource Directory y puede remasterizarse con DRM (Dynamic Resource Mastering) según la intensidad de acceso.","El interconnect requiere una red dedicada de baja latencia; use Jumbo Frames (MTU 9000) y, si es necesario, RDMA/RoCE o InfiniBand, y nunca lo comparta con la red pública.","Los wait events 'gc buffer busy' y 'gc cr/current block busy' indican contención de hot blocks; puede ayudar aumentar el CACHE de las secuencias, usar índices reverse key o repartir la carga con particionamiento.","Si la estadística gc blocks lost sigue aumentando, hay pérdida de paquetes en el interconnect; revise los tamaños de la NIC, del buffer del switch y de los socket buffers (rmem/wmem)."],"level":"expert"},{"id":"oracle-rac-node-eviction","title":"Oracle RAC: node eviction y split-brain","category":"Oracle","summary":"Clusterware expulsa del clúster a un nodo que no responde para evitar el split-brain; decide según el heartbeat de red, el heartbeat de disco y la mayoría de los voting disks.","points":["CSSD monitorea dos heartbeats separados: el heartbeat de red por el interconnect (misscount, predeterminado 30 s) y el heartbeat de disco a través de los voting disks (disktimeout, predeterminado 200 s).","En un split-brain, el subclúster superviviente (cohort) se determina por la mayoría de los voting disks; por ello el número de voting disks debe ser impar (3 o 5) y deben residir en failure groups separados.","La expulsión suele realizarse mediante un reinicio del nodo o un fencing sin reinicio (reiniciando solo el stack); el objetivo es impedir que el nodo defectuoso escriba en el almacenamiento compartido.","Causas comunes de expulsión: pérdida del interconnect, CSSD sin CPU bajo carga intensa, latencia de I/O de los voting disks y problemas de acceso a OCR/voting disks.","Para el análisis de la causa raíz, revise ocssd.log, las trazas de CSSD, el alert log y los mensajes del SO junto con los datos de CHM/oclumon (Cluster Health Monitor)."],"level":"expert"},{"id":"oracle-asm-redundancy-rebalance","title":"Oracle ASM: redundancia de disk groups y rebalance","category":"Oracle","summary":"ASM protege los datos distribuyéndolos (striping) entre los discos de un disk group y replicándolos (mirroring) por failure group; reequilibra automáticamente los extents cuando se añaden o eliminan discos.","points":["Niveles de redundancia: EXTERNAL (sin mirroring, la protección queda en manos de la cabina de almacenamiento), NORMAL (2 copias, tolera la pérdida de 1 disco), HIGH (3 copias, tolera 2 pérdidas simultáneas).","ASM replica failure groups, no discos; el extent primario reside en un failure group y sus copias espejo en failure groups distintos, por lo que no se pierden datos aunque falle un failure group completo.","Añadir o eliminar un disco dispara un rebalance que redistribuye los extents; ASM_POWER_LIMIT (0-1024) define el equilibrio entre velocidad y consumo de recursos.","El tamaño de AU (Allocation Unit) se define al crear el disk group; para bases de datos grandes, un AU de 4 MB mejora la eficiencia de I/O.","Los voting disks y el OCR pueden almacenarse en un disk group con redundancia normal/high; un quorum failure group se usa solo para voting disks y no contiene extents de datos."],"level":"expert"},{"id":"oracle-data-guard-modes","title":"Oracle Data Guard: tipos de standby y modos de protección","category":"Oracle","summary":"Data Guard proporciona recuperación ante desastres enviando el redo a un standby; la elección entre standby físico/lógico y los tres modos de protección definen el equilibrio entre pérdida de datos y disponibilidad.","points":["Un standby físico es una copia idéntica bloque a bloque del primario (Redo Apply/MRP); con Active Data Guard puede consultarse en solo lectura mientras está abierto. Un standby lógico convierte el redo en SQL y lo aplica (SQL Apply) y puede alojar estructuras/índices distintos.","Maximum Performance (predeterminado): el redo se envía de forma asíncrona (ASYNC) y el primary no espera confirmación; impacto nulo en el rendimiento, pero con un pequeño riesgo de pérdida de datos.","Maximum Availability: el redo se envía de forma síncrona (SYNC/AFFIRM); si el standby no está accesible, el primary no se detiene, sino que pasa al comportamiento de MAX PERFORMANCE y se resincroniza cuando el standby vuelve.","Maximum Protection: el primary no confirma el commit hasta que al menos un standby lo confirma; garantiza cero pérdida de datos, pero el primary se apaga si se pierden todos los standby.","Fast-Start Failover (FSFO) usa un proceso observer para hacer failover automáticamente cuando falla el primary; se apunta a RPO=0 con FastStartFailoverThreshold y transporte síncrono.","Supervise el apply lag en el standby con v$dataguard_stats y las métricas 'apply lag'/'transport lag'; real-time apply aplica el redo antes de que se archive."],"level":"expert"},{"id":"oracle-exadata-offload","title":"Oracle Exadata: Smart Scan y storage indexes","category":"Oracle","summary":"En Exadata, las storage cells asumen parte del procesamiento de consultas (offload) y devuelven al nodo de base de datos solo los datos relevantes; Smart Scan, los storage indexes y HCC reducen drásticamente la E/S.","points":["En consultas con full scan, Smart Scan realiza el filtrado de predicados y la proyección de columnas en la storage cell; al nodo de base de datos se devuelven solo las filas/columnas necesarias, no los bloques, lo que reduce el tráfico del interconnect.","Smart Scan funciona solo con direct path reads; no se activa para bloques leídos desde el buffer cache ni para ciertas operaciones; mida el beneficio con las estadísticas 'cell physical IO bytes eligible for predicate offload' y 'cell physical IO interconnect bytes returned by smart scan' ('cell physical IO bytes saved by storage index' muestra solo el ahorro del storage index).","Un storage index mantiene en memoria los valores mín./máx. de las columnas para cada región de almacenamiento de 1 MB; si el predicado de la consulta queda fuera del rango, esa región se omite sin leerse (funciona como un índice negativo).","Los storage indexes no son persistentes; residen en memoria y se reconstruyen cuando la cell se reinicia. Los datos ordenados físicamente (clustering) aumentan la tasa de omisión.","HCC (Hybrid Columnar Compression) comprime los datos por columnas dentro de Compression Units; QUERY LOW/HIGH es para datos con muchas consultas, mientras que ARCHIVE LOW/HIGH es para datos de archivo poco consultados y ofrece una compresión muy alta a un alto costo de CPU."],"level":"expert"},{"id":"oracle-awr-ash-analysis","title":"Análisis de rendimiento con Oracle AWR y ASH","category":"Oracle","summary":"AWR recopila periódicamente snapshots de rendimiento, mientras que ASH muestrea las sesiones activas cada segundo; juntos responden a '¿en qué se fue el tiempo?' en términos de wait events.","points":["Por defecto, AWR toma un snapshot cada 60 minutos y los conserva 8 días; un informe AWR entre dos snapshots muestra qué SQL y wait events consumieron DB Time (requiere la licencia de Diagnostics Pack).","Comience siempre el análisis por 'Top 10 Foreground Events by Total Wait Time' y DB Time; observe ahí el equilibrio entre CPU, E/S y concurrencia en lugar de perderse en estadísticas individuales.","ASH muestrea v$active_session_history una vez por segundo y captura problemas breves y repentinos (picos); expone tormentas momentáneas de lock/latch que AWR no detecta, por SQL_ID y sesión bloqueante.","Average Active Sessions (AAS = DB Time / tiempo transcurrido) es un resumen en un solo número de la carga del sistema; un AAS superior al número de núcleos de CPU indica saturación.","Para el tuning de SQL, tome el SQL_ID más costoso de la sección 'SQL ordered by Elapsed Time/Gets' de AWR, confirme con ASH en qué paso espera ese SQL y luego examine el plan línea por línea con un informe de SQL Monitor."],"level":"expert"},{"id":"oracle-scn-read-consistency","title":"Oracle SCN y consistencia de lectura","category":"Oracle","summary":"El System Change Number (SCN) es el reloj lógico de la base de datos; Oracle aísla a los lectores de los escritores construyendo una imagen consistente (read consistency) a partir del undo según el SCN en el que comenzó la consulta.","points":["El SCN aumenta de forma monótona con cada commit y se registra en redo/undo, el control file y las cabeceras de bloque; si el SCN del último cambio de un bloque es mayor que el SCN de la consulta, el bloque se 'revierte'.","Para la consistencia de lectura, Oracle genera una copia CR (Consistent Read) del bloque actual y la revierte hasta el SCN de inicio de la consulta usando undo; así, 'los lectores no bloquean a los escritores y los escritores no bloquean a los lectores'.","Si el undo es insuficiente o se supera undo_retention, una consulta de larga duración recibe 'ORA-01555 snapshot too old'; la solución es ampliar el undo tablespace, ajustar la retención o acortar la consulta.","La consistencia a nivel de sentencia usa un SCN distinto para cada sentencia SQL; una transacción SERIALIZABLE o de solo lectura mantiene para todas las sentencias el único SCN del inicio de la transacción.","En RAC, el SCN se mantiene sincronizado entre todas las instancias (Lamport o broadcast-on-commit); el avance del SCN se coordina entre nodos para lograr consistencia global."],"level":"expert"},{"id":"postgres-patroni-failover","title":"PostgreSQL: failover automático con Patroni","category":"PostgreSQL","summary":"Patroni proporciona elección de líder y failover automático manteniendo el estado del clúster PostgreSQL en un almacén de consenso distribuido (DCS); evita el split-brain con un watchdog.","points":["El estado del clúster y el leader lock se guardan en un DCS como etcd/Consul/ZooKeeper; el líder renueva periódicamente un lock con TTL y, si no puede renovarlo, el lock expira y comienza una nueva elección de líder.","En un failover, Patroni elige la réplica más actualizada (la de LSN más alto); el nuevo líder se promueve y las demás réplicas se reconectan a él (reinitialize/rewind).","El watchdog (softdog) es una red de seguridad a nivel de hardware que reinicia la máquina cuando el líder anterior pierde contacto con el DCS, evitando que dos nodos sean líder al mismo tiempo (split-brain).","maximum_lag_on_failover impide que una réplica muy retrasada se convierta en líder y cause pérdida de datos; synchronous_mode puede apuntar a RPO=0, pero aumenta la latencia de escritura.","Los clientes no deben conectarse directamente a un nodo, sino a través de HAProxy usando el health check REST de Patroni, o mediante una VIP; así el tráfico se redirige automáticamente al nuevo líder cuando cambia el líder.","La mayoría (quórum) del DCS es obligatoria; use un clúster etcd de 3 o 5 miembros. Si el DCS no está accesible, Patroni actúa con prudencia y puede degradar al líder actual a solo lectura."],"level":"expert"},{"id":"postgres-logical-replication-upgrade","title":"PostgreSQL: actualización de versión mayor sin tiempo de inactividad con replicación lógica","category":"PostgreSQL","summary":"Como la replicación lógica puede transportar cambios de filas entre versiones, permite actualizaciones de versión mayor con un tiempo de inactividad casi nulo; la versión antigua y la nueva funcionan en paralelo durante un tiempo.","points":["La replicación lógica decodifica lógicamente los cambios del WAL (INSERT/UPDATE/DELETE); a diferencia de la replicación física, funciona entre distintas versiones mayores y distintas plataformas.","Flujo: cree un esquema vacío en la nueva versión, defina una PUBLICATION en la versión antigua y una SUBSCRIPTION en la nueva; primero se copian las tablas (sincronización inicial) y luego empiezan a fluir los cambios en vivo.","Toda tabla debe tener una REPLICA IDENTITY; en tablas sin clave primaria, replicar UPDATE/DELETE requiere REPLICA IDENTITY FULL; de lo contrario, se produce un error.","Los cambios DDL no se replican automáticamente y los valores actuales de las secuencias no se trasladan; en el cutover, avance manualmente las secuencias en la nueva versión; de lo contrario, aparecerán claves en conflicto.","En el cutover, detenga brevemente la aplicación que escribe en la versión antigua y, cuando el lag llegue a cero (verifíquelo con pg_stat_replication), apunte la aplicación a la nueva versión; si es necesario, tenga preparada una suscripción inversa para un rollback rápido.","Limitaciones: los large objects y parte del DDL no se replican; deben configurarse wal_level=logical y valores suficientes de max_replication_slots/max_wal_senders."],"level":"expert"},{"id":"postgres-pgvector-similarity","title":"PostgreSQL: búsqueda por similitud con pgvector","category":"PostgreSQL","summary":"pgvector almacena embeddings vectoriales y realiza búsquedas aproximadas de vecinos más cercanos (ANN); los índices HNSW e IVFFlat definen el equilibrio entre precisión, velocidad y memoria.","points":["Elija el operador de distancia según el caso de uso: <-> L2 (euclidiana), <=> coseno, <#> producto interno; el índice debe usar la misma operator class que la consulta; de lo contrario, no se utiliza.","HNSW construye un grafo multicapa; ofrece alto recall y consultas rápidas, pero necesita más memoria y se construye lentamente. m (número de conexiones) y ef_construction ajustan la calidad de construcción; ef_search ajusta la precisión en tiempo de consulta.","IVFFlat divide los vectores en lists clústeres y en cada consulta escanea solo probes clústeres; usa menos memoria, pero el índice debe crearse DESPUÉS de cargar los datos (los clústeres no tienen sentido en una tabla vacía).","Para IVFFlat, comience con lists ≈ filas/1000 hasta 1M de filas y ≈ sqrt(filas) por encima de 1M; aumentar probes eleva el recall pero ralentiza la consulta, así que ajuste este equilibrio midiendo.","ANN devuelve resultados aproximados; si se requiere un 100 % de exactitud, hace falta un escaneo exacto sin índice. En búsquedas filtradas (WHERE) el recall del índice puede caer; si es necesario, considere iterative scan o un índice parcial.","Las dimensiones de los vectores y el número de filas determinan los requisitos de memoria; mantenga maintenance_work_mem suficientemente grande durante la construcción; de lo contrario, la construcción de HNSW se vuelca a disco y se vuelve muy lenta."],"level":"expert"},{"id":"postgres-citus-distributed","title":"PostgreSQL: escalado distribuido con Citus","category":"PostgreSQL","summary":"Citus escala PostgreSQL horizontalmente dividiendo las tablas en shards según una columna de distribución y repartiéndolos entre nodos worker; la elección de la columna de distribución determina el rendimiento.","points":["El nodo coordinator recibe la consulta y la enruta a los shards correspondientes según la columna de distribución; los nodos worker almacenan los shards y los procesan en paralelo. Los shards son tablas PostgreSQL estándar (divididas por rangos de hash).","La columna de distribución debe ser el filtro común de la mayoría de las consultas y JOIN (p. ej., tenant_id); las filas con el mismo valor caen en el mismo shard, de modo que los JOIN y las claves foráneas permanecen locales en el mismo nodo (colocation).","La colocation es crítica: las tablas que se unen entre sí se ejecutan sin redistribución de datos entre nodos (repartition) si se distribuyen con la misma columna de distribución y el mismo número de shards.","Convierta en reference tables las tablas pequeñas que se unen con frecuencia con todos los shards; se copian completas en cada worker y eliminan el costo de red en los JOIN distribuidos.","Las consultas que no incluyen la columna de distribución se propagan a todos los shards y son costosas; en SaaS multi-tenant, filtrar por tenant_id reduce la consulta a un único shard (router execution)."],"level":"expert"},{"id":"postgres-timescaledb-hypertable","title":"PostgreSQL: hypertables y continuous aggregates de TimescaleDB","category":"PostgreSQL","summary":"TimescaleDB escala los datos de series temporales con hypertables que los dividen automáticamente en chunks por intervalos de tiempo y con continuous aggregates precalculados.","points":["Una hypertable parece una sola tabla desde fuera, pero internamente se divide en chunks por la columna de tiempo (y opcionalmente de espacio); las consultas tocan solo los chunks del rango de tiempo correspondiente (chunk exclusion).","Configure chunk_time_interval para que los chunks más activos quepan en memoria (shared_buffers); un valor demasiado pequeño causa una sobrecarga excesiva de chunks/planificación y uno demasiado grande, una exclusión débil.","Un continuous aggregate es una vista materializada que se actualiza de forma incremental; se mantiene al día en segundo plano mediante una refresh policy que recalcula solo los time buckets que cambiaron.","Con real-time aggregation, un continuous aggregate combina los datos antiguos materializados con los datos más recientes de la tabla original para devolver resultados actualizados en tiempo de consulta.","Los chunks antiguos pueden reducirse considerablemente con compresión columnar; una retention policy elimina automáticamente con drop_chunks los chunks más antiguos que cierta edad (mucho más barato que un DELETE fila por fila)."],"level":"expert"},{"id":"postgres-vacuum-xid-wraparound","title":"PostgreSQL: VACUUM y XID wraparound","category":"PostgreSQL","summary":"MVCC deja versiones de filas muertas; VACUUM las limpia y marca las filas como 'frozen', evitando la pérdida de datos causada por el wraparound del identificador de transacción (XID) de 32 bits.","points":["PostgreSQL usa XID de 32 bits, que son circulares; cada versión de fila lleva el sello xmin. Si las filas suficientemente antiguas no se congelan, en el XID wraparound las transacciones 'pasadas' parecen estar en el 'futuro' y las filas pueden volverse invisibles de repente.","autovacuum hace dos trabajos: la limpieza regular (dead tuples, bloat) y el congelamiento; para las tablas que se acercan a autovacuum_freeze_max_age se dispara un vacuum 'anti-wraparound' obligatorio aunque autovacuum esté desactivado.","A medida que se acerca el wraparound se registran advertencias; si el límite está muy cerca, la base de datos rechaza nuevas escrituras y pasa a solo lectura para evitar la pérdida de datos; la solución es ejecutar un VACUUM agresivo (FREEZE).","Supervise el XID no congelado más antiguo con pg_database.datfrozenxid y age(); las transacciones abiertas de larga duración (idle in transaction) y los replication slots sin uso bloquean el avance de VACUUM, aumentando el bloat y el riesgo de wraparound.","Con alta carga de escritura, un autovacuum retrasado provoca bloat; aumente autovacuum_vacuum_cost_limit, reduzca naptime y aplique ajustes de autovacuum por tabla a las tablas grandes/calientes.","VACUUM por lo general no devuelve espacio al sistema operativo; lo marca para reutilizarlo. La reducción física requiere VACUUM FULL (bloquea la tabla) o pg_repack (en línea)."],"level":"expert"},{"id":"mysql-innodb-cluster","title":"MySQL: Group Replication e InnoDB Cluster","category":"MySQL","summary":"InnoDB Cluster es un clúster por mayoría (quórum) basado en Group Replication que proporciona alta disponibilidad presentando a la aplicación un único punto de conexión a través de MySQL Router.","points":["Group Replication propaga las transacciones al grupo mediante una capa de comunicación de grupo basada en Paxos; una transacción se confirma solo después de que la mayoría del grupo (quórum) está de acuerdo, lo que proporciona failover automático y consistencia.","El modo single-primary (predeterminado) permite escrituras solo en un nodo y el grupo elige automáticamente un nuevo primary cuando el primary falla; en modo multi-primary todos los nodos aceptan escrituras, pero la gestión de conflictos (certification) se vuelve más compleja.","MySQL Router lee los metadatos del clúster y ofrece a la aplicación dos puertos: las escrituras van al primary actual (6446) y las lecturas a las réplicas (6447); en un failover, el enrutamiento se actualiza sin cambiar la conexión de la aplicación.","La pérdida de la mayoría (p. ej., 2 de 3 nodos caídos) bloquea el grupo; para evitar el split-brain, el clúster no acepta nuevas escrituras y la recuperación se hace manualmente con group_replication_force_members.","Group Replication requiere binlog basado en filas (formato ROW), GTID e InnoDB; es obligatoria una clave primaria en cada tabla; de lo contrario, el nodo no puede unirse al clúster.","Un nodo que se une primero se sincroniza mediante state transfer desde un donor existente (distributed recovery: clone plugin o binlog) y luego pasa al flujo en vivo."],"level":"expert"},{"id":"mysql-semisync-replication","title":"MySQL: replicación semi-sync","category":"MySQL","summary":"La replicación semisíncrona reduce la pérdida de datos haciendo que el primary espere, antes de confirmar un commit, hasta que al menos una réplica haya recibido la transacción en su relay log.","points":["Con replicación asíncrona, el primary confirma el commit sin esperar a la réplica; si el primary se cae, se pierden las transacciones aún no enviadas. Semi-sync espera un acuse de recibo de al menos N réplicas (rpl_semi_sync_source_wait_for_replica_count).","AFTER_SYNC (predeterminado, recomendado): el primary primero recibe el acuse de la réplica y luego devuelve el commit al cliente; esto garantiza que toda transacción que el cliente ve como 'exitosa' existe en al menos una réplica.","En el modo AFTER_COMMIT el primary primero hace commit localmente y luego espera a la réplica; si la réplica se retrasa, otras sesiones pueden ver datos que aún no se han replicado (similar a un phantom read).","Si se supera rpl_semi_sync_source_timeout, el primary vuelve automáticamente a asíncrono y no se detiene; regresa a semi-sync cuando la réplica vuelve, así que la 'garantía' es condicional.","Semi-sync garantiza 'recibido', no 'aplicado en la réplica'; para un RPO=0 estricto, Group Replication o una capa de consenso externa es más adecuada."],"level":"expert"},{"id":"mysql-undo-purge","title":"MySQL InnoDB: undo tablespaces y purge","category":"MySQL","summary":"InnoDB guarda las versiones antiguas de las filas en el undo log para MVCC; los purge threads limpian los registros de undo y las versiones muertas que ningún lector necesita ya.","points":["Cada cambio genera un registro de undo log para el rollback y las lecturas consistentes; una transacción de larga duración mantiene vivas read views antiguas, bloqueando el avance del purge y haciendo crecer el undo (aumenta el history list length).","Si el history list length (SHOW ENGINE INNODB STATUS) sigue creciendo, el purge no da abasto; la causa principal suele ser transacciones abiertas durante mucho tiempo o lecturas antiguas mantenidas activas (p. ej., mysqldump --single-transaction).","innodb_purge_threads controla el paralelismo del purge e innodb_max_purge_lag añade un retraso artificial al DML cuando el purge se queda muy atrás, evitando que la carga lo desborde.","En MySQL 8, el undo se guarda en undo tablespaces separados y los archivos de undo que crecen pueden reducirse con el truncado automático (innodb_undo_log_truncate); en versiones antiguas el undo permanecía en el system tablespace y nunca se reducía.","Un undo inflado obliga a las lecturas consistentes a recorrer cadenas de versiones más largas (E/S y CPU); las transacciones cortas y la disciplina de autocommit minimizan la presión sobre el undo."],"level":"expert"},{"id":"mysql-buffer-pool-lru","title":"MySQL InnoDB: instancias del buffer pool y LRU","category":"MySQL","summary":"El buffer pool de InnoDB mantiene en memoria las páginas de datos e índices; el algoritmo LRU de inserción en el punto medio (midpoint insertion) evita que los escaneos de tablas expulsen de memoria los datos calientes.","points":["En un servidor de base de datos dedicado, el buffer pool suele configurarse en ~70-80 % de la RAM (innodb_buffer_pool_size; menos en un servidor compartido); el objetivo es mantener en memoria el working set de acceso frecuente y minimizar la E/S de disco.","La lista LRU se divide en las sublistas 'young' (caliente) y 'old' (fría); una página recién leída no se inserta al inicio de la lista, sino al inicio de la región old (midpoint, ~37 %), de modo que un escaneo puntual no expulsa las páginas calientes.","Una página de la región old se promueve a young solo si se vuelve a acceder después de que haya transcurrido innodb_old_blocks_time; esto evita que los full scans grandes contaminen el buffer pool.","innodb_buffer_pool_instances divide el pool en varias instancias independientes; como cada instancia tiene su propio mutex, la contención del mutex del buffer pool disminuye con alta concurrencia (útil en pools grandes).","Supervise la tasa de aciertos del buffer pool y el equilibrio entre páginas libres y sucias; un gran número de páginas sucias y un flushing agresivo pueden causar una 'checkpoint storm'; innodb_io_capacity fija la velocidad de flush según el almacenamiento."],"level":"expert"},{"id":"sqlserver-always-on-ag","title":"SQL Server: grupos de disponibilidad Always On","category":"SQL Server","summary":"Un Availability Group replica un grupo de bases de datos en réplicas síncronas/asíncronas; sobre WSFC proporciona failover automático, réplicas legibles y read-only routing.","points":["Con una réplica synchronous-commit, el primary espera a que el secundario persista (harden) el registro de log antes de hacer commit; esto proporciona RPO=0, pero si la réplica se retrasa aumenta la latencia de commit en el primary.","Una réplica asynchronous-commit no hace esperar al primary; es adecuada para sitios remotos de recuperación ante desastres (DR), pero el failover puede perder algunos datos (RPO>0) y solo se admite el failover manual forzado (FORCE_FAILOVER_ALLOW_DATA_LOSS).","El failover automático ocurre solo entre un par de réplicas en modo synchronous-commit y 'automatic failover', mientras se mantiene el quórum de WSFC; los health checks se realizan con sp_server_diagnostics y database health detection.","Una réplica secundaria legible descarga los informes del primary; gracias a read-only routing, los clientes que se conectan con ApplicationIntent=ReadOnly se enrutan automáticamente a través del listener a una réplica legible.","Las consultas en una réplica legible usan automáticamente snapshot isolation; para ello se añade una etiqueta de versión de 14 bytes a las filas modificadas en el primary y se carga el version store de tempdb en el secundario; además, si el redo apply se retrasa, los datos de la réplica pueden quedar desactualizados.","El AG listener presenta a los clientes un único punto de conexión con un nombre de red virtual + IP; en un failover, el listener pasa al nuevo primary y la aplicación sigue funcionando sin cambiar su connection string."],"level":"expert"},{"id":"sqlserver-columnstore","title":"SQL Server: índices columnstore y batch mode","category":"SQL Server","summary":"Un índice columnstore almacena los datos por columnas con alta compresión; el procesamiento en batch mode y la segment elimination aceleran mucho las consultas analíticas.","points":["Los datos se dividen en segmentos de columna dentro de rowgroups (~1 millón de filas) y cada segmento se comprime; solo se leen las columnas que necesita la consulta, lo que reduce significativamente la E/S.","Cada segmento guarda metadatos mín./máx.; si el predicado de la consulta queda fuera del rango de valores del segmento, este se omite sin leerse (segment elimination), por lo que cargar los datos ordenados es muy importante.","Batch mode procesa las filas en grupos de ~900 en lugar de una por una, aumentando el trabajo realizado por ciclo de CPU; es muchas veces más rápido que row mode en agregaciones y joins (con SQL 2019+, batch mode también es posible sobre rowstore).","Un clustered columnstore es ideal para tablas de hechos analíticas/DW grandes; para cargas mixtas OLTP + analítica, puede construirse real-time operational analytics (HTAP) con un nonclustered columnstore sobre una tabla rowstore.","Las filas calientes (aún no comprimidas) se guardan por filas en el deltastore y el tuple mover las comprime cuando se llena; muchas cargas pequeñas causan fragmentación, por lo que debe preferirse la carga masiva (bulk load)."],"level":"expert"},{"id":"sqlserver-inmemory-oltp","title":"SQL Server: In-Memory OLTP (Hekaton)","category":"SQL Server","summary":"In-Memory OLTP elimina cuellos de botella en cargas OLTP de alta concurrencia procesando tablas memory-optimized con estructuras sin locks/latches y procedimientos compilados de forma nativa.","points":["Las tablas memory-optimized se mantienen íntegramente en memoria y todas las versiones de fila residen en memoria; la concurrencia se gestiona con MVCC optimista sin locks, eliminando la contención clásica de latches y locks.","Para la durabilidad, los cambios se siguen escribiendo en el log y se vuelcan a checkpoint files (data/delta); las tablas SCHEMA_ONLY no se registran en el log y se vacían al reiniciar (ideal, p. ej., para datos transitorios de staging/sesión).","Un procedimiento almacenado compilado de forma nativa compila T-SQL a código máquina (DLL), eliminando la sobrecarga de interpretación; solo puede acceder a tablas memory-optimized y admite un subconjunto limitado de T-SQL.","Como la concurrencia es optimista, los conflictos se detectan en tiempo de ejecución; un conflicto write-write revierte la transacción con un error (que requiere reintento), por lo que la aplicación debe incluir lógica de reintento.","Escenarios más adecuados: tasas de inserción muy altas (p. ej., ingesta de IoT/telemetría), tablas calientes con cuellos de botella de lock/latch y tablas temporales que usan tempdb; todos los datos deben caber en memoria y hay que vigilar de cerca la presión de memoria."],"level":"expert"},{"id":"sqlserver-query-store","title":"SQL Server: gestión de planes con Query Store","category":"SQL Server","summary":"Query Store persiste los textos de las consultas, los planes de ejecución y las estadísticas de ejecución; permite detectar regresiones de planes y forzar un plan concreto.","points":["Query Store guarda, dentro de la base de datos, todos los planes que cada consulta ha usado a lo largo del tiempo junto con métricas de duración/CPU/lecturas por plan; responde con datos históricos a '¿por qué esta consulta era rápida ayer y hoy es lenta?'.","Las regresiones de plan (un plan recién compilado peor que el anterior) se detectan con los informes 'Regressed Queries' y 'Top Resource Consumers'; la estabilidad se restablece forzando el plan bueno conocido con sys.sp_query_store_force_plan.","Con los Query Store hints de SQL Server 2022, se pueden aplicar hints (p. ej., RECOMPILE, MAXDOP) sin forzar un plan ni cambiar el texto de la consulta; es muy útil para ajustar consultas de aplicaciones de terceros.","La corrección automática de planes (automatic tuning, FORCE_LAST_GOOD_PLAN) detecta regresiones, fuerza automáticamente el plan bueno anterior y lo revierte si no hay mejora; actívela en producción bajo supervisión.","Configure correctamente el capture mode (ALL/AUTO/CUSTOM) y el tamaño de almacenamiento; AUTO filtra las consultas poco frecuentes/insignificantes para reducir la sobrecarga y el espacio, y un Query Store lleno pasa a modo de solo lectura y deja de recopilar datos."],"level":"expert"},{"id":"mongodb-sharding-deep","title":"MongoDB: sharding, chunks y el balancer","category":"MongoDB","summary":"MongoDB divide los datos en chunks según la shard key y los distribuye entre shards; el balancer equilibra los chunks y la elección de la shard key determina la distribución y el enrutamiento de consultas.","points":["Los datos se dividen en chunks por rangos de shard key y los chunks se distribuyen entre shards; el router mongos mira la shard key y enruta la consulta solo al shard correspondiente (targeted); de lo contrario, la difunde a todos los shards (scatter-gather).","Una buena shard key tiene alta cardinalidad, baja frecuencia y no es monótonamente creciente; una clave monótonamente creciente (p. ej., timestamp/ObjectId) acumula todas las escrituras nuevas en un solo shard (hotspot), y el hashed sharding las reparte.","Cuando el número de chunks se desequilibra entre shards, el balancer migra chunks en segundo plano; durante la migración se preserva la consistencia entre origen y destino, pero tiene un costo de E/S y puede programarse en una ventana de mantenimiento.","Un jumbo chunk es un chunk que supera el tamaño máximo de chunk y no puede dividirse porque la shard key tiene baja cardinalidad; no puede migrarse y crea desequilibrio; la solución es una shard key más selectiva (compuesta).","Zone sharding (tag-aware) fija rangos concretos de shard key a shards concretos; se usa para dirigir la ubicación de los datos geográficamente o por hardware (p. ej., datos de la UE en un shard de la región UE).","Aunque las versiones modernas introducen el balanceo por tamaño y la desfragmentación automática en lugar del número de chunks, el principio es el mismo: para consultas targeted, la shard key debe estar presente en cada lectura/escritura."],"level":"expert"},{"id":"mongodb-oplog-election","title":"MongoDB: oplog del replica set y elecciones","category":"MongoDB","summary":"En un replica set, el primary registra las operaciones de escritura en el oplog y los secundarios las aplican; cuando el primary falla, se elige un nuevo primary mediante una elección similar a Raft.","points":["El oplog es una capped collection que registra cada cambio del primary de forma idempotente (reaplicable con seguridad); los secundarios leen (tail) y aplican el oplog para mantenerse sincronizados.","El tamaño del oplog determina la ventana de replicación; si un secundario permanece desconectado más tiempo del que cubre el oplog, queda 'stale' y requiere una resincronización completa (initial sync), por lo que el oplog debe dimensionarse según la carga de trabajo.","Cuando el primary falla, los secundarios votan con un protocolo similar a Raft; el candidato que obtiene la mayoría de los votos se convierte en primary, por lo que se mantiene un número impar de votos (con un arbiter si es necesario) para preservar la mayoría.","writeConcern 'majority' garantiza que una escritura ha llegado a la mayoría de los nodos, de modo que no se revierte en un failover (protección contra rollback); w:1 es más rápido, pero la escritura puede perderse si el primary falla.","readConcern y read preference controlan la frescura de las lecturas; leer desde secundarios reparte la carga, pero puede devolver datos desactualizados por el retraso de replicación, mientras que 'majority'/'linearizable' ofrecen garantías más fuertes.","Si el primary anterior queda en minoría tras una partición de red, pasa a secundario; al reincorporarse con el nuevo primary, mueve sus escrituras no confirmadas a un archivo de rollback (el write concern majority lo evita)."],"level":"expert"},{"id":"mongodb-wiredtiger-checkpoint","title":"MongoDB: checkpoints y journal de WiredTiger","category":"MongoDB","summary":"WiredTiger proporciona durabilidad mediante checkpoints periódicos y un journal write-ahead; ofrece alto rendimiento con concurrencia a nivel de documento y compresión.","points":["Por defecto, WiredTiger toma un checkpoint aproximadamente cada 60 segundos, escribiendo en disco un snapshot consistente de la memoria; entre checkpoints, la durabilidad se preserva registrando cada escritura en el journal (WAL).","Sin el journal, un crash pierde las escrituras posteriores al último checkpoint; con el journal, la recuperación carga el último checkpoint y vuelve a aplicar desde el journal las operaciones posteriores.","El control de concurrencia a nivel de documento permite actualizar simultáneamente distintos documentos de la misma colección; los snapshots MVCC proporcionan lecturas consistentes.","La caché de WiredTiger (por defecto ~50 % de la RAM) guarda los datos e índices de acceso frecuente; si el working set no cabe en la caché, aumentan el eviction y la E/S de disco, y la saturación de la caché degrada gravemente el rendimiento.","La compresión de almacenamiento usa snappy por defecto (equilibrio velocidad/ratio); zstd/zlib ofrecen ratios más altos pero necesitan más CPU. Los índices se reducen además con prefix compression."],"level":"expert"},{"id":"redis-cluster-hashslot","title":"Redis Cluster: hash slots y resharding","category":"Redis","summary":"Redis Cluster escala horizontalmente dividiendo el espacio de claves en 16384 hash slots y distribuyéndolos entre nodos; los clientes encuentran el nodo correcto mediante redirecciones MOVED/ASK.","points":["Cada clave se asigna a un slot mediante CRC16(key) mod 16384; cada master posee rangos de slots concretos, por lo que el clúster distribuye los datos sin un router central.","Si un cliente va al nodo equivocado, el nodo responde con MOVED para indicar el propietario permanente del slot y el cliente actualiza su mapa de slots. Durante el resharding, se usa una redirección temporal ASK para el slot que se está migrando.","El resharding mueve slots (y sus claves) en vivo de un nodo a otro; la migración se hace clave por clave y el clúster sigue dando servicio durante todo el proceso.","Las operaciones multiclave (MSET, transacciones, Lua) funcionan solo con claves del mismo slot; los hash tags ({...}) se usan para agrupar claves relacionadas en el mismo slot, p. ej., user:{123}:name y user:{123}:email.","Para alta disponibilidad, asigne a cada master al menos una réplica; cuando un master falla, su réplica se promueve. El clúster decide por mayoría de masters y las escrituras se detienen si se pierde la mayoría.","Si algún slot no está asignado o no tiene master, el clúster rechaza todas las escrituras (cuando cluster-require-full-coverage está activado); los 16384 slots deben estar asignados."],"level":"expert"},{"id":"redis-replication-psync","title":"Redis: replicación y PSYNC","category":"Redis","summary":"En la replicación asíncrona de Redis, el master transfiere un snapshot de datos a la réplica y luego transmite los comandos posteriores; PSYNC realiza una resincronización parcial tras desconexiones breves.","points":["En la sincronización inicial, el master genera un snapshot RDB y lo envía a la réplica (full resync); mientras tanto, guarda las escrituras entrantes en un replication backlog buffer y las transmite después del RDB.","PSYNC sigue el replication offset de la réplica y el master replication ID; tras una desconexión breve, si los datos perdidos siguen en el backlog buffer, solo se envía la parte que falta (partial resync), evitando un full resync costoso.","repl-backlog-size determina cuánto tiempo de desconexión se puede tolerar; con altas tasas de escritura, un backlog pequeño fuerza un full resync en las desconexiones, lo que implica generar un RDB en el master y costo de red.","La replicación es asíncrona: el master confirma una escritura sin esperar a las réplicas, por lo que si el master se cae de repente pueden perderse las últimas escrituras; el comando WAIT permite esperar a que un número dado de réplicas la haya recibido, pero no es una verdadera garantía síncrona.","Con diskless replication (repl-diskless-sync), el master puede transmitir el RDB directamente al socket sin escribirlo en disco; esto acelera la sincronización inicial en entornos con discos lentos pero redes rápidas."],"level":"expert"},{"id":"redis-memory-fragmentation","title":"Redis: fragmentación de memoria y jemalloc","category":"Redis","summary":"Redis usa jemalloc por defecto; las asignaciones y liberaciones de tamaño variable causan fragmentación de memoria con el tiempo, haciendo que el RSS parezca mayor que los datos realmente usados.","points":["mem_fragmentation_ratio = used_memory_rss / used_memory; un valor cercano a 1 es ideal, claramente superior a 1 (p. ej., >1.5) indica fragmentación y uno inferior a 1 indica que los datos se están enviando a swap en disco (malo).","La fragmentación surge cuando se crean y eliminan constantemente claves de distintos tamaños, dejando huecos inutilizables en las arenas de jemalloc; los cambios frecuentes en valores grandes la empeoran.","La desfragmentación activa (activedefrag yes) compacta los bloques de memoria en tiempo de ejecución con ayuda de jemalloc para reducir la fragmentación; sus umbrales (active-defrag-ignore-bytes, -threshold-lower/upper) se ajustan para limitar la sobrecarga.","Configure correctamente maxmemory y maxmemory-policy; como el RSS real asignado por jemalloc puede superar maxmemory, mantenga maxmemory por debajo de la RAM para evitar un OOM del sistema operativo.","Si used_memory es bajo pero el RSS sigue alto, un reinicio (o un nodo reiniciado mediante failover) suele restablecer la fragmentación de forma duradera; active defrag reduce la necesidad de reinicios en la mayoría de los casos."],"level":"expert"},{"id":"elasticsearch-shard-sizing","title":"Elasticsearch: dimensionamiento de shards","category":"Elasticsearch","summary":"Cada shard es un índice de Lucene y conlleva un costo fijo de recursos; demasiados shards pequeños (oversharding) o shards demasiado grandes degradan el rendimiento y la estabilidad.","points":["Regla general: mantenga un shard en el rango de 10-50 GB; los shards muy grandes ralentizan la recuperación y el rebalanceo, mientras que los muy pequeños (oversharding) cargan innecesariamente el cluster state y el heap.","Cada shard, y sobre todo cada campo mapeado, consume heap. La antigua regla de \"~20 shards por GB de heap\" ya no se aplica a partir de 8.3 (la sobrecarga de heap por shard bajó drásticamente); la recomendación actual es reservar ~1 KB de heap por campo mapeado por índice en los data nodes más la sobrecarga, y mantener los shards por nodo por debajo de cluster.max_shards_per_node (predeterminado 1000). Demasiados shards siguen haciendo crecer el cluster state y aumentando la sobrecarga de coordinación.","La latencia de las consultas está relacionada con el número de shards: una consulta se propaga a todos los shards relevantes y los resultados se combinan; más shards de los necesarios ralentizan incluso las consultas pequeñas por la sobrecarga de coordinación.","Para datos de series temporales/logs, use índices basados en tiempo (data streams) en lugar de un único índice gigante; así mover/eliminar datos antiguos por índice (ILM) resulta barato y los shards siguen siendo manejables.","El número de primary shards se fija al crear el índice y no puede cambiarse después (solo mediante reindex/split/shrink); por ello, planifíquelo bien desde el principio según las estimaciones de crecimiento, mientras que el número de réplicas puede ajustarse dinámicamente."],"level":"expert"},{"id":"elasticsearch-ilm-tiers","title":"Elasticsearch: ILM y niveles hot-warm-cold-frozen","category":"Elasticsearch","summary":"Index Lifecycle Management gestiona de forma económica los datos de series temporales moviendo automáticamente los índices entre niveles de costo/rendimiento según su fase de ciclo de vida.","points":["Una política ILM mueve un índice por fases: hot (escrituras activas + consultas frecuentes, SSD rápido), warm (escrituras terminadas, consultas ocasionales), cold (consultas poco frecuentes, hardware más barato), frozen (muy poco frecuentes, servidas desde almacenamiento de objetos mediante searchable snapshots).","Rollover abre un nuevo índice cuando el índice hot alcanza cierto tamaño/edad/número de documentos; así un único índice no crece indefinidamente y cada fase se gestiona por índice.","El nivel frozen mantiene los datos en almacenamiento de objetos (S3/blob) mediante searchable snapshots y los trae parcialmente a memoria solo cuando se necesitan; reduce mucho el costo de disco, pero aumenta la latencia de las consultas.","Al pasar a warm/cold, el almacenamiento y el heap se optimizan con force merge, shrink y menos réplicas; como los datos ya no cambian en estas fases, los segmentos pueden fusionarse en un único segmento.","Asocie los data tiers a roles de nodo (data_hot/data_warm/data_cold/data_frozen); ILM usa shard allocation filters para colocar el índice en nodos con el hardware adecuado."],"level":"expert"},{"id":"elasticsearch-segment-merge","title":"Elasticsearch: fusión de segmentos y force merge","category":"Elasticsearch","summary":"Lucene crea nuevos segmentos inmutables en cada refresh; la fusión en segundo plano combina y elimina segmentos, mientras que force merge es una operación manual que debe usarse con cuidado.","points":["Cada refresh (1 s por defecto) produce un nuevo segmento y las eliminaciones se marcan como 'tombstones' en el segmento; muchos segmentos pequeños ralentizan las lecturas, por lo que la fusión en segundo plano los combina.","Los documentos eliminados/actualizados se borran físicamente del disco solo durante una fusión; con una carga intensa de actualizaciones, los 'deleted docs' se acumulan en los segmentos y el uso de disco/memoria se dispara.","En índices que ya no reciben escrituras (solo lectura), un force merge a 1 segmento mejora la velocidad de las consultas y la eficiencia del disco; es un paso típico de la fase warm de ILM.","NUNCA ejecute force merge sobre un índice con escrituras activas; el segmento muy grande que produce no entra en el ciclo normal de fusión, las eliminaciones posteriores no pueden limpiarse y satura la E/S y el heap durante mucho tiempo.","Aumentar temporalmente refresh_interval durante las cargas masivas (p. ej., -1 o 30s) produce menos segmentos y más grandes, reduciendo la carga de fusión y mejorando el rendimiento de escritura."],"level":"expert"},{"id":"elasticsearch-node-roles","title":"Elasticsearch: roles de nodo y topología del clúster","category":"Elasticsearch","summary":"Los nodos de Elasticsearch asumen roles como master, data, coordinating e ingest; separar los roles en clústeres grandes aporta estabilidad y escalabilidad.","points":["Los nodos master-eligible gestionan el cluster state (índices, ubicación de shards, mappings); para elecciones estables, use un número impar (normalmente 3) de nodos master dedicados y confíe en un quórum (mayoría) para evitar el split-brain.","Los data nodes alojan shards y se encargan de la indexación y la E/S de consultas; son los que más CPU/RAM/disco consumen y pueden dividirse en data tiers (hot/warm/cold/frozen).","Un nodo coordinating-only no tiene otros roles: distribuye las solicitudes a los shards correspondientes y combina los resultados (scatter-gather); protege a los data nodes asumiendo la combinación pesada de búsquedas/agregaciones y actúa como balanceador de carga.","Un nodo ingest procesa los datos mediante pipelines (transformación, enriquecimiento) antes de indexarlos; mover los ingest pipelines pesados a nodos ingest dedicados protege el rendimiento de consultas de los data nodes.","Nunca haga que los nodos master compartan carga pesada de consultas/datos; un master que se ralentiza bajo carga retrasa las actualizaciones del cluster state y causa inestabilidad en todo el clúster (asignación de shards retrasada, elecciones)."],"level":"expert"}]},"guide/glossary.json":{"terms":[{"term":"ACID","full":"Atomicidad, Consistencia, Aislamiento, Durabilidad","definition":"Las cuatro propiedades fundamentales que hacen fiables las transacciones: atomicidad (todo o nada), consistencia (se preservan las reglas), aislamiento (las transacciones concurrentes no se corrompen entre sí) y durabilidad (los datos confirmados no se pierden). Es la garantía transaccional de las bases de datos relacionales.","tags":["transaction","transaction","acid","consistency","consistency"]},{"term":"BASE","full":"Básicamente disponible, estado flexible, consistencia eventual","definition":"Un enfoque de consistencia relajada, alternativo a ACID, preferido en sistemas distribuidos y NoSQL. El sistema prioriza estar siempre disponible y acepta que los datos se vuelvan consistentes con el tiempo (consistencia eventual). Sacrifica la consistencia estricta a cambio de escalabilidad.","tags":["base","distributed","distributed","eventual-consistency","nosql"]},{"term":"MVCC","full":"Control de concurrencia multiversión","definition":"Un método de concurrencia que mantiene varias versiones de la misma fila para que lectores y escritores trabajen sin bloquearse entre sí. Cada transacción ve su propio snapshot de los datos, de modo que las lecturas no bloquean las escrituras y las escrituras no bloquean las lecturas. Se usa en sistemas como PostgreSQL y Oracle.","tags":["concurrency","concurrency","lock","mvcc","snapshot"]},{"term":"WAL","full":"Write-Ahead Log","definition":"El principio de que un registro de log se escribe de forma duradera en disco ANTES de que el cambio se escriba en los archivos de datos. La recuperación ante caídas y la durabilidad se garantizan gracias a él. Se llama WAL en PostgreSQL y transaction log en SQL Server.","tags":["wal","log","recovery","recovery","durability"]},{"term":"Redo Log","full":"","definition":"El log que registra los cambios hechos en la base de datos y se usa para volver a aplicar (redo) el trabajo perdido en una caída. Durante la recuperación, aplica hacia adelante (roll forward) los cambios confirmados que aún no se habían escrito en disco. El redo log de Oracle corresponde al concepto general de WAL.","tags":["redo","log","recovery","recovery","oracle"]},{"term":"Undo / Rollback Segment","full":"","definition":"El área que almacena la imagen anterior de los datos modificados por una transacción. Cumple dos funciones: deshacer un cambio con rollback y servir la versión antigua a los lectores bajo MVCC. Se llama undo/rollback segment en Oracle y undo/version store en otros sistemas.","tags":["undo","rollback","rollback","mvcc","segment"]},{"term":"Buffer Pool / Buffer Cache","full":"","definition":"La caché en memoria donde se mantienen las páginas de datos e índices de uso frecuente. Como las lecturas de disco son costosas, el rendimiento de la base de datos depende en gran medida de la tasa de aciertos aquí (cache hit ratio). Se llama buffer pool en MySQL InnoDB y buffer cache en SQL Server.","tags":["buffer-pool","buffer-cache","cache","cache","memory"]},{"term":"Página sucia (dirty page)","full":"","definition":"Una página de datos modificada en memoria pero aún no escrita en disco. La durabilidad está protegida por el WAL; la propia página sigue sucia hasta que el checkpoint o el background writer la vuelca a disco. Demasiadas páginas sucias alargan el tiempo de recuperación.","tags":["dirty-page","dirty-page","memory","checkpoint","io"]},{"term":"LSN","full":"Log Sequence Number","definition":"Un número de secuencia único y siempre creciente asignado a cada registro del WAL/transaction log. Se usa para determinar hasta qué registro de log está actualizada una página y desde dónde debe continuar la recuperación. También sirve para seguir el progreso en la replicación.","tags":["lsn","log","sequence-number","recovery","replication"]},{"term":"SCN","full":"System Change Number","definition":"En Oracle, una marca de tiempo lógica monótonamente creciente asignada a cada punto de commit de la base de datos. Se usa para la consistencia de lectura, MVCC y para ordenar los eventos durante la recuperación. Puede considerarse el equivalente de Oracle al LSN.","tags":["scn","oracle","timestamp","read-consistency","commit"]},{"term":"Nivel de aislamiento","full":"Nivel de aislamiento","definition":"El ajuste que determina hasta qué punto las transacciones concurrentes pueden ver los cambios de las demás. Los niveles son Read Uncommitted, Read Committed, Repeatable Read y Serializable; a medida que sube el nivel, aumenta la consistencia y disminuye la concurrencia. Controla las anomalías de dirty/non-repeatable/phantom read.","tags":["isolation","isolation-level","transaction","concurrency","concurrency"]},{"term":"Lectura sucia (dirty read)","full":"","definition":"Una transacción que lee un cambio que otra transacción aún no ha confirmado (y que puede revertir). Como los datos leídos pueden no ser válidos, es una anomalía de consistencia. Solo es posible en el nivel de aislamiento Read Uncommitted.","tags":["dirty-read","anomaly","isolation","transaction","consistency"]},{"term":"Lectura no repetible (non-repeatable read)","full":"","definition":"Ver un valor distinto al leer dos veces la misma fila dentro de la misma transacción, porque otra transacción hizo commit entretanto. Puede ocurrir en Read Committed; se evita en Repeatable Read y niveles superiores.","tags":["non-repeatable-read","anomaly","isolation","transaction","non-repeatable"]},{"term":"Lectura fantasma (phantom read)","full":"","definition":"El conjunto de filas cambia al volver a ejecutar una consulta con la misma condición dentro de la misma transacción, debido a un insert/delete intermedio (aparecen nuevas filas 'fantasma'). Según el estándar SQL, se evita en Serializable; PostgreSQL también lo evita en Repeatable Read.","tags":["phantom-read","phantom","anomaly","serializable","isolation"]},{"term":"Deadlock","full":"","definition":"Una situación en la que dos o más transacciones esperan cada una un bloqueo que tiene la otra, de modo que ninguna puede avanzar. La base de datos lo detecta y rompe el ciclo, normalmente eligiendo la transacción más barata como 'víctima' y revirtiéndola. Adquirir siempre los bloqueos en el mismo orden reduce el riesgo.","tags":["deadlock","deadlock","lock","lock","concurrency"]},{"term":"Escalado de bloqueos (lock escalation)","full":"","definition":"Cuando una transacción mantiene muchos bloqueos de fila/página, la base de datos los convierte en un único bloqueo más grueso (por ejemplo, un bloqueo de tabla). Reduce la memoria de gestión de bloqueos, pero disminuye la concurrencia y puede aumentar el bloqueo entre sesiones. Se ve sobre todo en SQL Server.","tags":["lock-escalation","lock","lock","concurrency","sql-server"]},{"term":"Latch","full":"","definition":"Un bloqueo de concurrencia ligero y de bajo costo que se usa para proteger estructuras en memoria (por ejemplo, páginas del buffer pool) durante muy poco tiempo. A diferencia de los bloqueos de transacción, protege la estructura física de memoria y no los datos lógicos, y se mantiene solo un instante.","tags":["latch","lock","memory","concurrency","internal"]},{"term":"Índice B-Tree","full":"Balanced Tree Index","definition":"El tipo de índice más común, que mantiene los datos en una estructura de árbol ordenada y equilibrada. Es eficiente para consultas de igualdad y de rango, ordenación y búsquedas por prefijo. Es el tipo de índice predeterminado en la mayoría de las bases de datos.","tags":["btree","index","index","search","range"]},{"term":"Índice hash","full":"","definition":"Un tipo de índice que localiza una clave pasándola por una función hash. Es muy rápido solo para búsquedas de igualdad exacta (=); no puede usarse para consultas de rango ni ordenación. Disponible, por ejemplo, en PostgreSQL y en las tablas Memory de MySQL.","tags":["hash-index","index","index","equality","search"]},{"term":"Índice bitmap","full":"","definition":"Un índice que mantiene arrays de bits para cada valor distinto, adecuado para columnas de baja cardinalidad (pocos valores distintos). Permite combinar rápidamente varias condiciones con AND/OR; eficaz en cargas de data warehouse/OLAP, pero ineficiente con escrituras intensas.","tags":["bitmap-index","index","index","olap","cardinality"]},{"term":"Índice de cobertura (covering index)","full":"","definition":"Un índice que contiene todas las columnas que necesita una consulta; la consulta se resuelve solo con el índice, sin visitar la tabla (heap/clustered). Mejora el rendimiento al eliminar costosas búsquedas de filas. Puede crearse con columnas INCLUDE.","tags":["covering-index","index","index","performance","lookup"]},{"term":"Índice agrupado (clustered index)","full":"","definition":"Un índice que mantiene las filas de la tabla ordenadas físicamente por la clave del índice; la propia tabla se almacena en ese orden. Una tabla solo puede tener uno. Es rápido para consultas por rango; en InnoDB la clave primaria siempre es agrupada.","tags":["clustered-index","index","index","physical","primary-key"]},{"term":"Cardinalidad","full":"","definition":"El número de valores distintos en una columna. Una cardinalidad alta (p. ej., email) significa muchos valores diferentes; una cardinalidad baja (p. ej., género), pocos. Es una medida crítica para el optimizador al elegir índices y estimar el costo del plan.","tags":["cardinality","cardinality","statistics","optimizer","distinct"]},{"term":"Selectividad","full":"","definition":"Indica qué fracción del total de filas devuelve una condición de filtro. Una selectividad alta (pocas filas) hace atractivo el uso de índices; con una selectividad baja (muchas filas) un full scan puede ser más barato. El optimizador la usa al elegir un plan.","tags":["selectivity","selectivity","optimizer","index","statistics"]},{"term":"Plan de ejecución","full":"","definition":"La hoja de ruta que muestra los pasos (tipo de escaneo, método de join, ordenamiento, etc.) que eligió el optimizador para ejecutar una consulta. Examinarlo con EXPLAIN/EXPLAIN ANALYZE revela la causa raíz de los problemas de rendimiento. Es importante comparar los costos estimados con los reales.","tags":["execution-plan","query-plan","explain","optimizer","performance"]},{"term":"Optimizador basado en costos","full":"CBO","definition":"El optimizador de consultas que estima el costo de los posibles planes de ejecución a partir de las estadísticas y elige el más barato. Las estadísticas actualizadas son esenciales para tomar decisiones correctas; las estadísticas obsoletas generan malos planes. Es el enfoque estándar en las bases de datos modernas.","tags":["cbo","optimizer","cost","statistics","query-plan"]},{"term":"Estadísticas","full":"Estadísticas","definition":"Metadatos recopilados para una tabla/índice para que el optimizador pueda estimar el número de filas, la cardinalidad y la distribución de valores. Estadísticas actualizadas significan buenos planes de consulta; cuando quedan obsoletas deben renovarse con ANALYZE/UPDATE STATISTICS. Son una de las causas más comunes de bajo rendimiento.","tags":["statistics","statistics","optimizer","analyze","query-plan"]},{"term":"Histograma","full":"","definition":"Una estructura de estadísticas que resume la distribución de los valores de una columna dividiéndolos en rangos (buckets). Permite al optimizador estimar el número de filas con mayor precisión, especialmente en distribuciones sesgadas. Es un componente de las estadísticas.","tags":["histogram","statistics","statistics","optimizer","distribution"]},{"term":"Partición","full":"","definition":"Una técnica que divide físicamente una tabla grande en piezas más pequeñas (por ejemplo, por fecha o rango) mientras lógicamente sigue siendo una sola tabla. Las consultas escanean solo la pieza relevante (partition pruning), y el mantenimiento y el archivado se simplifican. La diferencia con el sharding: permanece dentro del mismo servidor/instancia.","tags":["partition","partitioning","scaling","pruning","big-data"]},{"term":"Sharding","full":"","definition":"Un método para distribuir datos horizontalmente entre varios servidores/bases de datos separados según una shard key. Ofrece escalado horizontal más allá de los límites de una sola máquina, pero complica la gestión de consultas y transacciones. La diferencia con el particionamiento: los datos se reparten entre distintos nodos.","tags":["sharding","scaling","distributed","distributed","horizontal-scaling"]},{"term":"Replicación (síncrona/asíncrona)","full":"","definition":"Copiar datos de un nodo primario a una o más copias (réplicas). En la replicación síncrona el commit espera la confirmación de la réplica (sin pérdida de datos, mayor latencia); en la asíncrona no espera (rápida, pero es posible perder datos si la réplica va atrasada). Se usa para HA y escalado de lecturas.","tags":["replication","replication","synchronous","asynchronous","ha"]},{"term":"Replicación lógica vs. física","full":"","definition":"La replicación física crea una copia exacta a nivel de bloque de disco/WAL (misma versión, base de datos completa). La replicación lógica transfiere los cambios a nivel de fila/SQL; permite replicar tablas de forma selectiva y transferir entre versiones y esquemas distintos. La replicación lógica es la preferida para CDC y escenarios de actualización de versión.","tags":["logical-replication","physical-replication","replication","cdc","wal"]},{"term":"Failover","full":"","definition":"Transferir el servicio, de forma automática o manual, a un nodo standby cuando falla el nodo primario. Minimiza el tiempo de inactividad; el RPO determina cuántos datos se pueden perder y el RTO cuánto tarda la recuperación. Entra en juego ante fallos no planificados.","tags":["failover","ha","high-availability","standby","rpo"]},{"term":"Switchover","full":"","definition":"Un intercambio planificado y controlado de los roles primario y standby (por ejemplo, por mantenimiento). La diferencia con el failover: es una operación planificada, no un fallo, y normalmente se realiza sin pérdida de datos.","tags":["switchover","planned","ha","maintenance","standby"]},{"term":"RPO","full":"Recovery Point Objective","definition":"La cantidad máxima aceptable de pérdida de datos tras un desastre (expresada en tiempo). Por ejemplo, con un RPO de 5 minutos se tolera como máximo una pérdida de 5 minutos de datos. La frecuencia de backup y el tipo de replicación se eligen para cumplir este objetivo.","tags":["rpo","data-loss","dr","disaster-recovery","backup"]},{"term":"RTO","full":"Recovery Time Objective","definition":"El tiempo máximo objetivo para que el sistema vuelva a estar operativo tras una interrupción. Por ejemplo, con un RTO de 1 hora, el servicio debe restablecerse en 1 hora como máximo. La arquitectura de HA/DR y el nivel de automatización se diseñan en torno a este objetivo.","tags":["rto","recovery-time","dr","disaster-recovery","ha"]},{"term":"HA","full":"Alta disponibilidad (High Availability)","definition":"El objetivo de diseño de mantener un sistema en servicio sin interrupción, o con muy poca, a pesar de los fallos. Se logra mediante redundancia, failover automático y comprobaciones de estado. Suele expresarse como porcentaje de uptime (p. ej., 99.99%).","tags":["ha","high-availability","high-availability","failover","redundancy"]},{"term":"DR","full":"Recuperación ante desastres (Disaster Recovery)","definition":"Los planes y procesos para restablecer sistemas y datos, normalmente en una región geográficamente separada, tras grandes desastres como incendios, inundaciones o una caída de toda una región. Se mide con objetivos de RPO y RTO. La HA se centra en los fallos cotidianos; el DR, en los grandes desastres.","tags":["dr","disaster-recovery","disaster-recovery","rpo","rto"]},{"term":"Quórum","full":"","definition":"La mayoría mínima de nodos (normalmente N/2 + 1) necesaria para que una decisión (por ejemplo, la elección de líder o la confirmación de una escritura) sea válida en un sistema distribuido. Se usa para evitar el split-brain y preservar la consistencia. Por eso los tamaños de clúster suelen ser números impares.","tags":["quorum","majority","distributed","distributed","split-brain"]},{"term":"Split-Brain","full":"","definition":"Una situación en la que, tras una partición de red, ambas mitades de un clúster creen ser el primario y siguen escribiendo de forma independiente; esto provoca datos en conflicto y corrupción. Se previene con mecanismos de quórum y fencing. Es uno de los escenarios de fallo más peligrosos en los clústeres de HA.","tags":["split-brain","distributed","quorum","ha","consistency"]},{"term":"TDE","full":"Transparent Data Encryption","definition":"Un método de cifrado, transparente para la aplicación, que mantiene cifrados en disco los archivos de la base de datos (datos y log). Protege los datos incluso si se roban los discos o los archivos de backup (datos en reposo). No protege los datos en la red (en tránsito); para eso se necesita TLS.","tags":["tde","encryption","encryption","security","security"]},{"term":"RBAC","full":"Control de acceso basado en roles (Role-Based Access Control)","definition":"Un modelo de control de acceso en el que los privilegios se asignan a roles en lugar de directamente a usuarios, y los usuarios se hacen miembros de los roles. Simplifica la gestión de privilegios y facilita aplicar el principio de mínimo privilegio. Mantiene la administración escalable a medida que crece el número de usuarios.","tags":["rbac","role","privilege","security","security"]},{"term":"Pool de conexiones","full":"","definition":"Un pool que crea conexiones por adelantado y las reutiliza en lugar de abrir y cerrar repetidamente las conexiones de la aplicación a la base de datos. Reduce el costo de establecer conexiones, limita el consumo de recursos y controla la carga. Protege la base de datos evitando un exceso de conexiones.","tags":["connection-pool","connection-pool","performance","resource","application"]},{"term":"Sentencia preparada (prepared statement)","full":"","definition":"Una construcción en la que una sentencia SQL se analiza y planifica una sola vez y luego se ejecuta repetidamente con distintos valores de parámetros. Mejora el rendimiento gracias a la reutilización del plan y ofrece protección contra la inyección SQL. Se usa junto con bind variables.","tags":["prepared-statement","bind","security","sql-injection","performance"]},{"term":"Bind variable","full":"","definition":"Un parámetro que se pasa mediante un marcador de posición en lugar de incrustar el valor en el texto SQL. Aprovecha eficientemente la caché de planes al evitar que la misma consulta se vuelva a planificar para distintos valores, y previene la inyección SQL. Es crítica para consultas cuyos valores literales cambian con frecuencia.","tags":["bind-variable","parameter","prepared-statement","plan-cache","security"]},{"term":"Parameter sniffing","full":"","definition":"El optimizador genera y almacena en caché el plan de una consulta preparada/parametrizada según el valor del parámetro de su primera ejecución. Si ese valor es atípico, puede persistir un plan que rinde mal con valores posteriores distintos. Las soluciones incluyen la recompilación del plan (recompile) y los hints del optimizador.","tags":["parameter-sniffing","plan-cache","optimizer","performance","sql-server"]},{"term":"VACUUM / Bloat","full":"","definition":"Debido a MVCC, las versiones muertas de filas eliminadas/actualizadas se acumulan en la tabla; esta hinchazón se llama bloat. VACUUM recupera el espacio de estas filas muertas y actualiza las estadísticas (PostgreSQL). Si no se ejecuta con regularidad, el disco se hincha y el rendimiento cae.","tags":["vacuum","bloat","bloated","mvcc","postgresql"]},{"term":"Compactación (compaction)","full":"","definition":"En los almacenes basados en LSM-tree (p. ej., Cassandra, RocksDB), el proceso de fusionar muchos archivos de datos pequeños y purgar los registros muertos/obsoletos. Mejora el rendimiento de lectura y recupera espacio en disco. Es el equivalente de VACUUM en el mundo LSM.","tags":["compaction","lsm-tree","merge","cassandra","rocksdb"]},{"term":"Oplog","full":"Operations Log","definition":"En MongoDB, una colección especial de tamaño fijo (capped) en la que se registran en orden todas las operaciones de escritura aplicadas en el primario. Los miembros del replica set la leen para mantenerse sincronizados con el primario. Es la base de CDC y de la replicación.","tags":["oplog","mongodb","replication","cdc","log"]},{"term":"Binlog","full":"Binary Log","definition":"En MySQL, el log binario que registra todas las operaciones (o cambios de filas) que modifican datos. Se usa para replicación, recuperación a un punto en el tiempo y CDC. Puede mantenerse en formato row, statement o mixed.","tags":["binlog","mysql","replication","cdc","recovery"]},{"term":"CDC","full":"Change Data Capture","definition":"Una técnica que captura los cambios de insert/update/delete en una base de datos y los transmite a otros sistemas (data warehouse, motor de búsqueda, cola de mensajes). Normalmente funciona con baja sobrecarga leyendo el log de transacciones (binlog, WAL, oplog). Es la base de la integración de datos en tiempo real.","tags":["cdc","change-data-capture","data-streaming","binlog","integration"]},{"term":"Vista materializada","full":"","definition":"Una vista que almacena físicamente en disco el resultado de una consulta; a diferencia de una vista normal, no se recalcula en cada consulta. Acelera agregaciones y joins costosos, pero sus datos quedan desactualizados y deben actualizarse con un refresh. Común en OLAP/reporting.","tags":["materialized-view","view","cache","olap","reporting"]},{"term":"Clave foránea","full":"","definition":"Una restricción de integridad referencial que permite que una columna de una tabla haga referencia a la clave primaria/candidata de otra tabla. Impide referencias a filas inexistentes y preserva las relaciones. Reglas como ON DELETE/UPDATE CASCADE definen el comportamiento de las filas dependientes.","tags":["foreign-key","foreign-key","integrity","relationship","constraint"]},{"term":"Normalización","full":"Normalización","definition":"El proceso de dividir los datos en tablas relacionadas para reducir la redundancia y el riesgo de inconsistencia (formas normales como 1NF, 2NF, 3NF). Previene anomalías de actualización y se prefiere en sistemas OLTP. A cambio, las consultas pueden requerir más joins.","tags":["normalization","normalization","design","oltp","redundancy"]},{"term":"Desnormalización","full":"Desnormalización","definition":"Una técnica que duplica datos deliberadamente para reducir el número de joins y mejorar el rendimiento de lectura. Acelera las consultas, pero introduce duplicación de datos y complejidad en las actualizaciones. Se prefiere para reporting/OLAP y cargas de trabajo con muchas lecturas.","tags":["denormalization","denormalization","performance","olap","read"]},{"term":"OLTP","full":"Online Transaction Processing","definition":"Un tipo de sistema que procesa un gran número de transacciones cortas, frecuentes y concurrentes (insert/update/delete, lecturas de una sola fila); p. ej., banca, comercio electrónico. Requiere baja latencia y alta concurrencia y suele diseñarse normalizado. Es la contraparte operativa de OLAP.","tags":["oltp","transaction","transaction","operational","normalization"]},{"term":"OLAP","full":"Online Analytical Processing","definition":"Un tipo de sistema que ejecuta consultas complejas de agregación y análisis sobre grandes conjuntos de datos; p. ej., reporting, inteligencia de negocios. Se ejecutan pocas consultas pero pesadas, y suele diseñarse desnormalizado y columnar. Es la contraparte analítica de OLTP.","tags":["olap","analytics","reporting","data-warehouse","columnar"]},{"term":"ETL","full":"Extract, Transform, Load","definition":"El proceso de extraer datos de las fuentes (Extract), transformarlos según las reglas de negocio (Transform) y cargarlos en el sistema destino, normalmente un data warehouse (Load). La variante en la que la transformación se hace en el destino tras la carga se llama ELT. Es el enfoque clásico de la integración de datos.","tags":["etl","data-integration","data-warehouse","elt","transformation"]},{"term":"Datos calientes/fríos","full":"","definition":"Los datos actuales y de acceso frecuente se llaman 'calientes'; los datos antiguos/de archivo de acceso poco frecuente se llaman 'fríos'. Los datos calientes se mantienen en almacenamiento rápido/caro y los fríos en almacenamiento barato/lento (data tiering). Es una estrategia para equilibrar costo y rendimiento.","tags":["hot-data","cold-data","hot-cold","archive","tiering"]},{"term":"Fragmentación de índices","full":"","definition":"Con el tiempo, los inserts/deletes/updates hacen que el orden lógico de las páginas del índice se aleje de su orden físico y que el llenado de las páginas disminuya. Genera I/O innecesario y espacio desperdiciado, degradando el rendimiento. Se corrige con REBUILD o REORGANIZE.","tags":["index-fragmentation","fragmentation","index","maintenance","performance"]},{"term":"Commit en dos fases (two-phase commit)","full":"2PC","definition":"Un protocolo que proporciona un commit atómico para una transacción distribuida que abarca varias bases de datos/recursos. Funciona en dos fases: primero el coordinador pregunta a todos los participantes si están listos (prepare); si todos aceptan hace commit, y si uno se niega todos hacen rollback. Es lento y sensible a la caída del coordinador.","tags":["two-phase-commit","2pc","distributed","distributed","atomic"]}]},"guide/glossary-2.json":{"terms":[{"term":"LSM Tree","full":"Log-Structured Merge Tree","definition":"Una estructura de almacenamiento que primero acumula las escrituras en una estructura en memoria (memtable) y las vuelca a disco como bloques ordenados. Convierte escrituras aleatorias en secuenciales para lograr un alto throughput de escritura; las lecturas recorren varios niveles. Es la base de RocksDB, Cassandra y LevelDB.","tags":["storage","storage engine","lsm","write"]},{"term":"B+ Tree","full":"","definition":"Una estructura de árbol balanceado en la que todos los datos se guardan en los nodos hoja y los nodos internos contienen solo claves de enrutamiento. Las hojas enlazadas aceleran los escaneos por rango. Es la estructura más común para los índices de bases de datos relacionales.","tags":["index","index structure","storage","storage engine","btree"]},{"term":"Filtro de Bloom","full":"","definition":"Una estructura de datos probabilística que comprueba con poca memoria si un elemento pertenece a un conjunto. Responde \"definitivamente no\" o \"probablemente sí\"; son posibles los falsos positivos, pero no los falsos negativos. Se usa en motores basados en LSM para evitar lecturas de disco innecesarias.","tags":["storage","storage engine","probabilistic","filter","performance"]},{"term":"Hashing consistente","full":"","definition":"Un método de distribución que garantiza que solo una pequeña fracción de las claves se mueva al agregar o quitar nodos. Las claves y los nodos se ubican en un anillo virtual. Se usa para la distribución de datos en cachés distribuidas y sistemas NoSQL.","tags":["distributed systems","distributed","hashing","sharding","scaling"]},{"term":"Reloj vectorial (vector clock)","full":"","definition":"Una estructura que mantiene un contador por nodo para seguir el orden causal de los eventos en sistemas distribuidos. Determina si dos actualizaciones descienden una de otra o son concurrentes (en conflicto). Se usa en la detección de conflictos y en la consistencia eventual.","tags":["distributed systems","distributed","clock","causality","consistency"]},{"term":"Protocolo gossip","full":"","definition":"Un método de comunicación en el que cada nodo difunde periódicamente su estado a unos pocos pares elegidos al azar, de modo que la información llega a todo el clúster como una epidemia. No requiere un coordinador central, es escalable y tolerante a fallos. Se usa para la gestión de membresía en Cassandra y Consul.","tags":["distributed systems","distributed","gossip","cluster","membership"]},{"term":"Raft","full":"","definition":"Un algoritmo de consenso diseñado para ser más fácil de entender que Paxos. Elige un líder, añade todas las escrituras a un log replicado a través del líder y las hace durables con la confirmación de la mayoría. Lo usan etcd y Consul.","tags":["distributed systems","distributed","consensus","raft","replication"]},{"term":"Paxos","full":"","definition":"El algoritmo de consenso clásico que garantiza que una mayoría acuerde un único valor sobre una red no confiable. Es correcto, pero se considera complejo de implementar; constituye la base teórica de muchos sistemas distribuidos. Se usa en sistemas como Google Chubby.","tags":["distributed systems","distributed","consensus","paxos","agreement"]},{"term":"Elección de líder","full":"","definition":"El proceso de seleccionar un único nodo (el líder) para coordinar una tarea específica en un clúster distribuido. Si el líder actual falla, se elige automáticamente uno nuevo. Normalmente requiere la aprobación de la mayoría (quórum) para evitar el split-brain.","tags":["distributed systems","distributed","leader","election","coordination"]},{"term":"Escritura/lectura por quórum","full":"","definition":"El número mínimo de nodos necesario para que una lectura o escritura se considere válida. Cuando se cumple W + R > N, se garantiza obtener los datos más recientes. Permite ajustar el equilibrio entre consistencia y disponibilidad.","tags":["distributed systems","distributed","quorum","consistency","replication"]},{"term":"WAL Shipping","full":"Write-Ahead Log Shipping","definition":"Enviar los registros WAL generados por el servidor primario a los servidores standby y reproducirlos allí. Así el standby mantiene una copia casi actualizada de la base de datos primaria. Es la base de la streaming replication de PostgreSQL.","tags":["replication","replication","wal","high-availability","standby"]},{"term":"Log Shipping","full":"","definition":"Un método de replicación sencillo que consiste en copiar los backups del log de transacciones a un servidor standby a intervalos regulares y restaurarlos. Su retraso es mayor que el del streaming, pero es fácil de configurar. Se usa comúnmente para recuperación ante desastres en SQL Server.","tags":["replication","replication","log","backup","standby"]},{"term":"Snapshot Isolation","full":"","definition":"Un nivel de aislamiento en el que cada transacción ve una instantánea consistente de los datos tal como estaban en el momento en que comenzó. Los lectores no bloquean a los escritores; sin embargo, está expuesto a algunas anomalías como el write skew. Se implementa junto con MVCC.","tags":["isolation level","isolation","mvcc","snapshot","concurrency"]},{"term":"Serializable Snapshot Isolation (SSI)","full":"Serializable Snapshot Isolation","definition":"Un método que proporciona serializabilidad real sobre snapshot isolation mediante el seguimiento de dependencias peligrosas de lectura-escritura. Cuando se detecta un conflicto, se hace rollback de una de las transacciones. Se usa en el nivel SERIALIZABLE de PostgreSQL.","tags":["isolation level","isolation","serializable","ssi","concurrency"]},{"term":"Gap Lock","full":"","definition":"Un bloqueo sobre el hueco entre registros de índice (un rango que aún no existe) que impide insertar nuevas filas en ese rango. Se usa para evitar las lecturas fantasma. Entra en juego en el nivel REPEATABLE READ de InnoDB.","tags":["locking","lock","innodb","phantom","concurrency"]},{"term":"Next-Key Lock","full":"","definition":"La combinación de un bloqueo sobre un registro de índice y un bloqueo sobre el hueco anterior a él (gap lock). Evita las inserciones fantasma protegiendo tanto la fila existente como el rango. Es el comportamiento de bloqueo de filas por defecto de InnoDB.","tags":["locking","lock","innodb","gap","concurrency"]},{"term":"Bloqueo optimista","full":"","definition":"Un método que asume que los conflictos son raros y funciona sin tomar bloqueos, comprobando solo en el momento del commit si los datos han cambiado. Normalmente se implementa con un número de versión o una marca de tiempo. Si se detecta un conflicto, la operación se reintenta.","tags":["locking","lock","optimistic","concurrency","concurrency"]},{"term":"Bloqueo pesimista","full":"","definition":"Un método que asume que habrá conflictos y toma un bloqueo de inmediato al leer o modificar datos. Las demás transacciones esperan hasta que se libere el bloqueo. Protege la integridad de los datos en entornos de alta contención, pero reduce la concurrencia.","tags":["locking","lock","pessimistic","concurrency","concurrency"]},{"term":"Idempotencia","full":"","definition":"La propiedad de que ejecutar una operación una o muchas veces produzca el mismo resultado. Permite reintentos seguros tras errores de red. En sistemas distribuidos evita que las solicitudes duplicadas causen efectos secundarios.","tags":["distributed systems","distributed","idempotent","retry","reliability"]},{"term":"Circuit Breaker","full":"","definition":"Un patrón de diseño que protege el sistema cortando temporalmente las llamadas a un servicio que falla repetidamente. Mientras el circuito está \"abierto\", las solicitudes se rechazan rápidamente, y al cabo de un tiempo se hace una llamada de prueba. Evita los fallos en cascada.","tags":["distributed systems","distributed","fault tolerance","resilience","pattern"]},{"term":"Patrón Saga","full":"","definition":"Un patrón que gestiona transacciones de larga duración que abarcan varios servicios haciendo de cada paso una transacción local y usando transacciones compensatorias ante fallos. Proporciona consistencia eventual en lugar de ACID en un entorno distribuido. Común en arquitecturas de microservicios.","tags":["distributed systems","distributed","saga","microservices","pattern"]},{"term":"Patrón Outbox","full":"","definition":"Un patrón que escribe el mensaje en una tabla \"outbox\" dentro de la misma transacción para que la actualización de la base de datos y la publicación del mensaje sean atómicas. Un proceso separado lee esta tabla y publica el mensaje. Así se evita la inconsistencia entre los datos y la publicación de eventos.","tags":["distributed systems","distributed","outbox","messaging","pattern"]},{"term":"Fan-out","full":"","definition":"Distribuir una única operación entre muchas suboperaciones o destinos; por ejemplo, escribir una publicación en los feeds de miles de seguidores. Puede hacerse en el momento de la escritura (write fan-out) o de la lectura (read fan-out). Es una decisión de diseño importante para escalar.","tags":["distributed systems","distributed","scaling","fanout","architecture"]},{"term":"Hotspot","full":"","definition":"Concentración excesiva de carga en un nodo, partición o clave determinados. Ese punto se convierte en un cuello de botella y limita el rendimiento de todo el sistema. Una sharding key mal elegida es una causa común.","tags":["distributed systems","distributed","hotspot","performance","sharding"]},{"term":"Skew (desequilibrio de datos)","full":"","definition":"Distribución desigual de los datos o de la carga entre particiones. Algunas particiones se desbordan mientras otras quedan vacías, y la eficiencia del paralelismo cae. Se equilibra mediante la elección de la clave o salting.","tags":["distributed systems","distributed","skew","imbalance","sharding"]},{"term":"Índice parcial","full":"","definition":"Un índice que cubre solo las filas de la tabla que cumplen una condición WHERE. Reduce el tamaño del índice, abarata el mantenimiento y acelera el subconjunto consultado con frecuencia. Se usa, por ejemplo, para indexar solo las filas activas.","tags":["index","index type","postgresql","partial","performance"]},{"term":"Índice de expresión","full":"","definition":"Un índice construido sobre el resultado de una expresión o función en lugar de una columna. Por ejemplo, construido sobre LOWER(email), acelera las búsquedas que no distinguen mayúsculas y minúsculas. La consulta debe usar la misma expresión.","tags":["index","index type","expression","function","performance"]},{"term":"Índice GIN","full":"Generalized Inverted Index","definition":"Un tipo de índice invertido que indexa varios componentes dentro de un mismo valor. Ideal cuando un campo contiene muchas claves, como arrays, JSONB y búsqueda de texto completo. Se usa en PostgreSQL.","tags":["index","index type","postgresql","gin","jsonb"]},{"term":"Índice GiST","full":"Generalized Search Tree","definition":"Un framework de índices extensible y basado en árboles; se pueden definir estrategias de búsqueda personalizadas para distintos tipos de datos. Muy adecuado para consultas geoespaciales (geometry), de rangos y de vecino más cercano. Es la base de PostGIS.","tags":["index","index type","postgresql","gist","geospatial"]},{"term":"TOAST","full":"Técnica de almacenamiento de atributos de gran tamaño (The Oversized-Attribute Storage Technique)","definition":"La técnica de PostgreSQL para comprimir valores de campos grandes que no caben en una página y almacenarlos en un área separada. Los valores grandes de texto y JSONB se dividen de forma transparente. Preserva el rendimiento manteniendo compacta la tabla principal.","tags":["storage","storage engine","postgresql","toast","large-values"]},{"term":"Index-Only Scan","full":"","definition":"Un escaneo que produce el resultado solo a partir del índice, sin leer la tabla en absoluto, porque todos los datos necesarios están en el índice. Reduce significativamente el acceso a disco. En PostgreSQL funciona junto con el visibility map.","tags":["index","index scan","scan","performance","postgresql"]},{"term":"HTAP","full":"Procesamiento híbrido transaccional/analítico","definition":"Un enfoque que ejecuta conjuntamente cargas de trabajo transaccionales (OLTP) y analíticas (OLAP) en un único sistema. Elimina el retraso de mover los datos a un almacén separado para analizarlos (ETL). Normalmente combina almacenamiento por filas y por columnas.","tags":["analytics","oltp","olap","htap","architecture"]},{"term":"Sharding key","full":"","definition":"La columna o columnas que determinan en qué shard cae una fila. Una clave bien elegida distribuye la carga de forma uniforme y evita los hotspots. Una mala elección provoca desequilibrio de datos y costosas consultas entre shards.","tags":["distributed systems","distributed","sharding","key","scaling"]},{"term":"Rebalanceo","full":"","definition":"Redistribuir los datos entre particiones al agregar o quitar nodos o cuando la carga se desequilibra. El objetivo es igualar la carga y usar los recursos de forma eficiente. Mientras se ejecuta genera costo de red e I/O.","tags":["distributed systems","distributed","rebalancing","scaling","sharding"]},{"term":"Fencing","full":"","definition":"Un mecanismo que evita la corrupción de datos impidiendo que un nodo obsoleto o aislado escriba en un recurso compartido. Normalmente se usa un \"fencing token\" creciente para que se rechacen las solicitudes obsoletas. Limita el impacto del split-brain.","tags":["distributed systems","distributed","fencing","safety","split-brain"]},{"term":"Watermark","full":"","definition":"En los sistemas de procesamiento de streams, un marcador de progreso que indica que se considera que han llegado todos los eventos hasta un momento determinado. Se usa para manejar los eventos que llegan tarde y para decidir cuándo cerrar los cálculos de ventana.","tags":["stream processing","streaming","watermark","event time","event"]},{"term":"Tombstone","full":"","definition":"Un marcador especial que se escribe para indicar que un registro ha sido eliminado. En sistemas LSM y distribuidos, donde los datos no se eliminan físicamente de inmediato, garantiza que la eliminación se propague a todas las réplicas. La limpieza real ocurre durante la compactación.","tags":["storage","storage engine","lsm","tombstone","delete"]},{"term":"Compactación (LSM)","full":"","definition":"Fusionar y reescribir los archivos ordenados (SSTables) en disco en los LSM trees. Mejora el rendimiento de lectura y el uso del disco purgando versiones antiguas y tombstones. Se ejecuta en segundo plano y genera carga de escritura.","tags":["storage","storage engine","lsm","compaction","maintenance"]},{"term":"Read Repair","full":"","definition":"Una técnica que, cuando se detecta una inconsistencia entre réplicas durante una lectura, la repara escribiendo el valor más reciente en los nodos atrasados. El sistema se vuelve consistente a través del tráfico normal de lecturas. Se usa en sistemas como Cassandra.","tags":["distributed systems","distributed","repair","consistency","replication"]},{"term":"Hinted Handoff","full":"","definition":"Almacenar una escritura como \"hint\" en otro nodo mientras el nodo destino está temporalmente inaccesible y entregarla cuando el nodo vuelve. Garantiza que no se pierdan escrituras durante fallos transitorios. Respalda la consistencia eventual.","tags":["distributed systems","distributed","handoff","availability","replication"]},{"term":"Consistencia eventual","full":"","definition":"Un modelo de consistencia débil que garantiza que todas las réplicas acabarán convergiendo al mismo valor si cesan las nuevas actualizaciones. Permite inconsistencias de corta duración, pero ofrece alta disponibilidad y baja latencia. Es el comportamiento por defecto en muchos sistemas NoSQL.","tags":["distributed systems","distributed","consistency","eventual","nosql"]},{"term":"Consistencia fuerte","full":"","definition":"Un modelo de consistencia en el que todos los lectores ven inmediatamente el valor más reciente una vez que se completa una escritura. Es intuitivo de usar, pero como requiere coordinación su costo en latencia y disponibilidad es alto. Se prefiere en sistemas financieros.","tags":["distributed systems","distributed","consistency","strong","strong consistency"]},{"term":"Linealizabilidad (linearizability)","full":"","definition":"El nivel de consistencia más fuerte: garantiza que cada operación parezca surtir efecto en un único instante entre su inicio y su fin, y que todas las operaciones encajen en un único orden de tiempo real. El sistema se comporta como si fuera una sola copia. Es costoso de implementar.","tags":["distributed systems","distributed","consistency","linearizability","strong consistency"]},{"term":"Two-Phase Locking (2PL)","full":"Two-Phase Locking","definition":"Una regla de bloqueo en dos fases en la que una transacción primero solo adquiere bloqueos (fase de crecimiento) y luego solo los libera (fase de contracción). Esta disciplina garantiza la serializabilidad. En su forma estricta (Strict 2PL), los bloqueos se mantienen hasta el commit.","tags":["locking","lock","2pl","serializable","concurrency"]},{"term":"Detección de deadlocks","full":"","definition":"Un mecanismo que detecta un deadlock, en el que dos o más transacciones esperan unas a otras por bloqueos que mantienen. Normalmente se detecta buscando un ciclo en el grafo de espera (wait-for graph), y se elige una transacción como víctima y se le hace rollback. Así el sistema vuelve a avanzar.","tags":["locking","lock","deadlock","deadlock detection","concurrency"]}]},"guide/glossary-3.json":{"terms":[{"term":"Amplificación de escritura","full":"Amplificación de escritura","definition":"Una sola escritura lógica que provoca muchas más escrituras físicas en la capa de almacenamiento (compactación LSM, reescritura de páginas, WAL, borrado de bloques SSD). Los valores altos desgastan los discos y reducen el throughput de escritura.","tags":["storage","lsm","performance","ssd"]},{"term":"Amplificación de lectura","full":"Amplificación de lectura","definition":"Una sola lectura lógica que en realidad requiere varias lecturas físicas/internas. Buscar una clave a través de muchos niveles en un LSM tree es el ejemplo típico; los filtros de Bloom y la compactación la reducen.","tags":["storage","lsm","performance"]},{"term":"LSM-tree","full":"Log-Structured Merge Tree","definition":"Una estructura de datos que primero absorbe las escrituras en una estructura en memoria (memtable), las vuelca a disco como bloques ordenados (SSTables) y las fusiona en segundo plano (compactación). Ideal para cargas de trabajo con muchas escrituras; la usan RocksDB y Cassandra.","tags":["storage","lsm","write-heavy"]},{"term":"B+tree","full":"B+ Tree","definition":"La estructura clásica de los índices de bases de datos relacionales. Todos los datos se guardan en los nodos hoja y las hojas se encadenan en una lista enlazada; ofrece un rendimiento de lectura equilibrado para consultas puntuales y por rango.","tags":["index","btree","storage"]},{"term":"Fill factor","full":"Fill factor","definition":"La proporción que indica cuán llena se rellena inicialmente una página de índice/tabla. Dejar espacio libre reduce las divisiones de página (page splits) en futuras actualizaciones/inserciones; fillfactor en PG, fill factor en MSSQL.","tags":["index","storage","tuning"]},{"term":"HOT Update","full":"Heap-Only Tuple Update","definition":"En PostgreSQL, una actualización que no modifica ninguna columna indexada crea la nueva versión de la fila en la misma página y no actualiza los índices. Reduce el bloat de índices y abarata las actualizaciones.","tags":["postgres","mvcc","update","performance"]},{"term":"Bloat","full":"Bloat de tablas/índices","definition":"Una tabla/índice que ocupa más espacio del necesario en motores MVCC debido a versiones de filas muertas no limpiadas o a páginas vacías. Aumenta el costo de los escaneos; el espacio se recupera con VACUUM/reorg.","tags":["mvcc","postgres","maintenance","bloat"]},{"term":"Autovacuum","full":"VACUUM automático","definition":"El proceso en segundo plano de PostgreSQL que realiza automáticamente la limpieza de dead tuples, la recopilación de estadísticas y el freezing de los ID de transacción. Se ajusta a la carga de trabajo mediante parámetros de umbral y de costo.","tags":["postgres","vacuum","maintenance"]},{"term":"Freeze / Wraparound","full":"Transaction ID Freeze / Wraparound","definition":"En PostgreSQL, marcar las filas antiguas como 'frozen' para evitar que los ID de transacción de 32 bits den la vuelta (wraparound). Si se descuida, la base de datos deja de aceptar escrituras para evitar la pérdida de datos.","tags":["postgres","mvcc","vacuum","wraparound"]},{"term":"Checkpoint","full":"Checkpoint","definition":"El evento en el que las páginas sucias en memoria se escriben en disco y se registra el punto desde el que comenzará la recuperación. Acorta el tiempo de recuperación tras una caída; su frecuencia es un equilibrio entre los picos de I/O y el tiempo de recuperación.","tags":["recovery","wal","internals","checkpoint"]},{"term":"Fuzzy Checkpoint","full":"Fuzzy Checkpoint","definition":"Un tipo de checkpoint que escribe las páginas sucias de forma continua en segundo plano, repartidas en el tiempo, sin detener el sistema. Mantiene predecibles los tiempos de respuesta al evitar ráfagas repentinas de I/O (checkpoint spikes).","tags":["recovery","checkpoint","internals"]},{"term":"Redo","full":"Redo Log / Roll Forward","definition":"El registro que se mantiene para volver a aplicar (roll forward) los cambios realizados por una transacción durante la recuperación. Restaura los cambios confirmados que no habían llegado a disco antes de una caída.","tags":["recovery","oracle","wal","redo"]},{"term":"Undo","full":"Undo Log / Rollback","definition":"El registro que se mantiene para hacer rollback de una transacción y proporcionar versiones antiguas de las filas para MVCC. En Oracle/InnoDB, las versiones antiguas se leen de los segmentos de undo.","tags":["mvcc","oracle","innodb","undo","rollback"]},{"term":"Snapshot MVCC","full":"Snapshot MVCC","definition":"La vista lógica de un punto en el tiempo que determina qué versiones de filas son visibles para que una transacción obtenga una lectura consistente. Se toma al inicio de la transacción o de la sentencia (según el nivel de aislamiento).","tags":["mvcc","concurrency","isolation","snapshot"]},{"term":"Anomalía de aislamiento","full":"Anomalías de aislamiento","definition":"Inconsistencias producidas por transacciones concurrentes con un aislamiento insuficiente: dirty read (leer datos no confirmados), non-repeatable read (la misma fila cambia), phantom (aparecen filas nuevas) y write skew (escrituras aparentemente independientes que en conjunto rompen una regla).","tags":["isolation","concurrency","anomaly","acid"]},{"term":"2PL","full":"Two-Phase Locking","definition":"Un protocolo que garantiza la serializabilidad mediante bloqueos: una transacción primero solo adquiere bloqueos (fase de crecimiento) y, a partir de cierto punto, solo los libera (fase de contracción). Strict 2PL mantiene los bloqueos hasta el commit.","tags":["concurrency","locking","serializable","2pl"]},{"term":"MVTO","full":"Multi-Version Timestamp Ordering","definition":"Un método de control de concurrencia que asigna marcas de tiempo a las transacciones y ordena según ellas las lecturas/escrituras sobre datos multiversión. Resuelve los conflictos con marcas de tiempo en lugar de bloqueos.","tags":["concurrency","mvcc","timestamp","protocol"]},{"term":"Mutex","full":"Exclusión mutua (Mutual Exclusion)","definition":"Una primitiva de exclusión mutua que garantiza que solo un hilo acceda a un recurso a la vez. En las bases de datos cumple un papel similar al de un latch en la protección de estructuras de memoria compartida.","tags":["concurrency","internals","mutex"]},{"term":"Spinlock","full":"Spinlock","definition":"Un bloqueo de muy corta duración que hace que el hilo itere (busy-wait) en lugar de dormir hasta que se libere el bloqueo. Es barato para secciones críticas muy cortas; bajo contención consume CPU.","tags":["concurrency","internals","spinlock","cpu"]},{"term":"Buffer Pool","full":"Buffer Pool / Buffer Cache","definition":"Una caché que reduce el I/O de disco manteniendo en memoria las páginas de datos de acceso frecuente. El algoritmo de desalojo (LRU, clock-sweep) decide qué página se desaloja; su tamaño se fija según el working set.","tags":["memory","cache","internals","buffer"]},{"term":"Working set","full":"Working set","definition":"El conjunto de datos al que una carga de trabajo accede activamente en una ventana de tiempo determinada. Cuando el buffer pool es lo bastante grande para contener este conjunto, el I/O físico se minimiza; es distinto del tamaño total de los datos.","tags":["memory","cache","sizing","performance"]},{"term":"Skew","full":"Sesgo de datos (data skew)","definition":"Una situación en la que los valores no se distribuyen de manera uniforme y algunos aparecen con mucha más frecuencia. Es la principal causa de estimaciones erróneas de cardinalidad, problemas de parameter sniffing y hotspots en sharding; se maneja con histogramas.","tags":["statistics","distribution","skew","performance"]},{"term":"Caché de planes","full":"Caché de planes","definition":"El área de memoria donde se almacenan los planes de ejecución compilados para su reutilización. Evita el costo del hard parse, pero conlleva riesgos de parameter sniffing y planes obsoletos; plan cache en MSSQL, library cache en Oracle.","tags":["optimizer","plan-cache","parsing"]},{"term":"Hard/Soft Parse","full":"Hard Parse / Soft Parse","definition":"Hard parse: el SQL se analiza, se optimiza y se genera un plan desde cero (costoso). Soft parse: se encuentra un plan existente en la caché y se reutiliza (barato). Las bind variables aumentan la proporción de soft parse.","tags":["parsing","oracle","performance","plan-cache"]},{"term":"Connection Storming","full":"Tormenta de conexiones","definition":"Apertura simultánea de un gran número de conexiones nuevas a la base de datos en poco tiempo. Satura la base de datos con el costo de establecer conexiones y con presión de memoria; suele deberse al agotamiento del pool o a una ola de reconexiones.","tags":["connections","pooling","overload"]},{"term":"Thundering Herd","full":"Problema de Thundering Herd","definition":"Muchos clientes accediendo al mismo recurso al mismo tiempo tras un evento (expiración de caché, liberación de un bloqueo, recuperación de un servicio). Se mitiga con backpressure, jitter y patrones single-flight.","tags":["concurrency","cache","overload","resilience"]},{"term":"Backpressure","full":"Backpressure","definition":"Un sistema que ralentiza o rechaza al productor en lugar de aceptar una carga que no puede manejar (límites de cola, límites de tasa). Permite una degradación controlada en lugar de colapsar por sobrecarga.","tags":["resilience","flow-control","overload"]},{"term":"Lease","full":"Lease","definition":"Derecho de liderazgo/recurso otorgado a un nodo por un período fijo que deja de ser válido automáticamente al expirar. Al estar limitado en el tiempo, evita que un nodo bloqueado siga siendo líder indefinidamente.","tags":["distributed","leader-election","lease"]},{"term":"Read-Your-Writes","full":"Consistencia Read-Your-Writes","definition":"Consistencia de sesión que garantiza que un usuario siempre vea su propio cambio en la siguiente lectura. Al enrutar a réplicas con lag, se logra con enrutamiento/tokens especiales.","tags":["distributed","consistency","session","replication"]},{"term":"Consistencia causal","full":"Consistencia causal","definition":"Modelo de consistencia más fuerte que la eventual que garantiza que las operaciones con relación causal se vean en el mismo orden en todos los nodos. Las operaciones no relacionadas (concurrentes) pueden verse en órdenes distintos.","tags":["distributed","consistency","causal"]},{"term":"CAP","full":"Consistencia, disponibilidad, tolerancia a particiones","definition":"Teorema que establece que, durante una partición de red, un sistema distribuido solo puede elegir entre consistencia o disponibilidad. Sin partición, este compromiso no es obligatorio.","tags":["distributed","cap","consistency","theory"]},{"term":"PACELC","full":"Teorema PACELC","definition":"Modelo que extiende CAP: ante una partición (P) se elige entre disponibilidad (A) y consistencia (C); en caso contrario (E), entre latencia (L) y consistencia (C). También cubre el compromiso latencia-consistencia en operación normal.","tags":["distributed","cap","pacelc","latency","theory"]},{"term":"Partición caliente","full":"Partición caliente / Hot Spot","definition":"Carga que se acumula de forma desproporcionada en una sola partición/shard. Se debe a una clave de shard de baja cardinalidad, basada en tiempo o monótonamente creciente; convierte ese nodo en cuello de botella e impide escalar.","tags":["distributed","sharding","hot-spot","skew"]},{"term":"Columnstore","full":"Almacenamiento orientado a columnas","definition":"Formato en el que los datos se almacenan por columna en lugar de por fila. Las consultas analíticas leen solo las columnas necesarias, con alta compresión y ejecución vectorizada; ideal para OLAP.","tags":["olap","columnar","analytics","compression"]},{"term":"Ejecución vectorizada","full":"Ejecución vectorizada","definition":"Operadores de consulta que procesan un bloque (lote) de valores de columna a la vez en lugar de fila por fila. Aumenta mucho el rendimiento de las consultas analíticas al aprovechar mejor las cachés de CPU y SIMD.","tags":["olap","execution","performance","columnar"]},{"term":"Zone Map / Min-Max","full":"Zone Map / índice Min-Max","definition":"Metadatos ligeros que almacenan el valor mínimo y máximo de la columna para cada bloque de datos. Reducen el costo del escaneo al omitir, sin leerlos, los bloques que no pueden cumplir el predicado de la consulta (block skipping).","tags":["olap","index","pruning","columnar"]},{"term":"Replicación lógica","full":"Replicación lógica","definition":"Replicación que publica y suscribe cambios a nivel de fila (eventos lógicos) en lugar de bloques físicos. Permite tablas selectivas, escenarios entre versiones y bidireccionales; PG lo ofrece con publication/subscription.","tags":["replication","postgres","logical","cdc"]},{"term":"GTID","full":"Global Transaction Identifier","definition":"Identificador globalmente único asignado a cada transacción en la replicación de MySQL. Al rastrear la posición de la réplica por ID de transacción en lugar de archivo/offset, hace que el failover y la reconfiguración sean seguros y sencillos.","tags":["mysql","replication","gtid","failover"]},{"term":"High Water Mark","full":"High Water Mark","definition":"El límite de bloques más alto utilizado alguna vez en un segmento/tabla. En Oracle, un full table scan lee hasta este límite; como no baja tras borrados masivos, puede provocar el escaneo de bloques vacíos y bloat.","tags":["oracle","storage","internals","scan"]}]},"playbooks/playbooks.json":{"schemaVersion":1,"playbooks":[{"id":"db-slow","title":"La base de datos está lenta","icon":"🐢","symptom":"Lentitud en toda la aplicación; las consultas tardan mucho más de lo habitual y los usuarios se quejan.","steps":[{"title":"Primero aclare qué es exactamente lo que está lento","detail":"¿Está todo lento o una consulta/pantalla concreta? ¿Cuándo empezó y coincide con un despliegue/parche/crecimiento de datos? ¿Afecta a todo el sistema o a una sola BD? Esto reduce la búsqueda a un eje: CPU, I/O, bloqueos o un cambio de plan."},{"title":"Revise las sesiones activas y los wait events","detail":"¿Qué se está ejecutando y qué está esperando ahora mismo? Un wait event dominante (I/O, bloqueo, log) señala el cuello de botella.","engines":{"oracle":"SELECT sid, username, status, event, sql_id FROM v$session WHERE status='ACTIVE' AND type='USER';","postgres":"SELECT pid, state, wait_event_type, wait_event, query FROM pg_stat_activity WHERE state='active';","mysql":"SELECT id, user, command, time, state, info FROM information_schema.processlist WHERE command<>'Sleep' ORDER BY time DESC;","mssql":"SELECT session_id, status, wait_type, wait_time, blocking_session_id FROM sys.dm_exec_requests WHERE session_id IN (SELECT session_id FROM sys.dm_exec_sessions WHERE is_user_process = 1);","mongodb":"db.currentOp({ active: true, secs_running: { $gt: 1 } })","redis":"SLOWLOG GET 10"}},{"title":"Compruebe si hay bloqueos","detail":"La causa más común de lentitud es una sesión que bloquea a otras. Encuentre la sesión bloqueadora e identifique la cabeza de la cadena.","engines":{"oracle":"SELECT blocking_session, sid, seconds_in_wait, event FROM v$session WHERE blocking_session IS NOT NULL;","postgres":"SELECT pid, pg_blocking_pids(pid) AS blocked_by, query FROM pg_stat_activity WHERE cardinality(pg_blocking_pids(pid))>0;","mysql":"SELECT * FROM sys.innodb_lock_waits\\G","mssql":"SELECT session_id, blocking_session_id, wait_type FROM sys.dm_exec_requests WHERE blocking_session_id<>0;"}},{"title":"Extraiga las consultas más costosas","detail":"Encuentre las 10 consultas que más recursos consumen. Normalmente el 90% del problema proviene de unas pocas consultas.","engines":{"oracle":"SELECT sql_id, elapsed_time/1e6 sec, executions, sql_text FROM v$sql ORDER BY elapsed_time DESC FETCH FIRST 10 ROWS ONLY;","postgres":"SELECT calls, total_exec_time, mean_exec_time, query FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;","mysql":"SELECT digest_text, count_star, sum_timer_wait/1e12 sec FROM performance_schema.events_statements_summary_by_digest ORDER BY sum_timer_wait DESC LIMIT 10;","mssql":"SELECT TOP 10 total_elapsed_time/execution_count avg_us, execution_count, SUBSTRING(t.text,1,200) FROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t ORDER BY total_elapsed_time DESC;"}},{"title":"Examine el plan de la consulta sospechosa","detail":"Obtenga el EXPLAIN de la consulta lenta. Busque full table scans, índices faltantes, un mal orden de joins o estadísticas obsoletas. Si el plan cambió de repente, considere recopilar estadísticas.","engines":{"oracle":"SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id', NULL, 'ALLSTATS LAST'));","postgres":"EXPLAIN (ANALYZE, BUFFERS) <query>;","mysql":"EXPLAIN ANALYZE <query>;","mssql":"SET STATISTICS PROFILE ON; <query>; SET STATISTICS PROFILE OFF;","mongodb":"db.my_collection.find({...}).explain('executionStats')  // COLLSCAN or IXSCAN?"}},{"title":"Verifique los recursos del sistema","detail":"A nivel de SO, revise CPU, espera de I/O (iowait), memoria y uso de disco. Revise el cache hit ratio; si es bajo, puede haber un cuello de botella de memoria. El problema puede estar fuera de la BD (disco lleno, VM vecina ruidosa)."},{"title":"Decida: solución rápida o solución permanente","detail":"Para un alivio inmediato: termine la consulta descontrolada, agregue el índice faltante, recopile estadísticas. Para una solución permanente: rediseñe la consulta/índice, corrija la configuración de memoria, asegure la estabilidad del plan (baseline/hint)."}],"related":["active-sessions","blocking-sessions","top-slow-queries","explain-plan","cache-hit-ratio"]},{"id":"disk-full","title":"Disco / tablespace lleno","icon":"💽","symptom":"Errores 'unable to extend', 'disk full', 'log file full'; las escrituras se están deteniendo.","steps":[{"title":"No entre en pánico; primero averigüe qué está lleno","detail":"¿Es un datafile, el log de transacciones, el área de archive/redo, temp o el disco del SO? Limpiar el lugar equivocado no sirve. Revise el disco con df -h a nivel de SO y las vistas de espacio dentro de la BD."},{"title":"Mida el uso","detail":"¿Qué tablespace/archivo/directorio está lleno y en qué porcentaje?","engines":{"oracle":"SELECT d.tablespace_name, ROUND((1-(d.alloc-NVL(f.free,0))/d.maxb)*100,1) pct_free FROM (SELECT tablespace_name, SUM(bytes) alloc, SUM(GREATEST(bytes,maxbytes)) maxb FROM dba_data_files GROUP BY tablespace_name) d LEFT JOIN (SELECT tablespace_name, SUM(bytes) free FROM dba_free_space GROUP BY tablespace_name) f ON f.tablespace_name = d.tablespace_name;","postgres":"SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database ORDER BY 2 DESC; -- use OS df for the disk","mysql":"SELECT table_schema, ROUND(SUM(data_length+index_length)/1048576,1) mb FROM information_schema.tables GROUP BY table_schema ORDER BY mb DESC;","mssql":"SELECT name, size/128.0 size_mb, size/128.0 - CAST(FILEPROPERTY(name,'SpaceUsed') AS int)/128.0 free_mb FROM sys.database_files;","redis":"INFO memory  # used_memory vs maxmemory"}},{"title":"Encuentre los mayores consumidores","detail":"Identifique las tablas/segmentos más grandes, los archivos de log/archive más grandes y los backups antiguos innecesarios. La mayoría de las veces el culpable es una sola tabla descontrolada o logs archivados acumulados.","engines":{"oracle":"SELECT segment_name, bytes/1048576 mb FROM dba_segments ORDER BY bytes DESC FETCH FIRST 10 ROWS ONLY;","postgres":"SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) FROM pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 10;","mssql":"SELECT TOP 10 OBJECT_NAME(object_id), SUM(reserved_page_count)*8/1024 mb FROM sys.dm_db_partition_stats GROUP BY object_id ORDER BY mb DESC;"}},{"title":"Recupere espacio de forma segura (rápido)","detail":"Para logs/archives: haga backup y luego purgue los logs archivados/backups antiguos (Oracle: RMAN delete archivelog; MSSQL: tome un backup de log y reduzca el log). En Redis: maxmemory-policy y eviction. NUNCA borre manualmente un datafile en uso; NUNCA borre logs archivados sin backup."},{"title":"Aumente la capacidad de forma permanente","detail":"Agregue espacio al datafile o habilite autoextend (Oracle: ALTER DATABASE DATAFILE ... AUTOEXTEND ON MAXSIZE), amplíe el disco, agregue un tablespace. Corrija la causa raíz: rotación de logs, job de limpieza de archive, política de retención."},{"title":"Evite que se repita","detail":"Configure alertas de uso de disco (umbrales de 80%/90%), automatice la limpieza de archive/logs y defina claramente la política de retención. Evite tener que volver a abrir este playbook."}],"related":["free-space","largest-tables","database-size","reclaim-space","last-backup-status"]},{"id":"blocking","title":"Crisis de bloqueos","icon":"🔒","symptom":"Las consultas se cuelgan, 'lock wait timeout', muchas sesiones en espera pero la CPU está baja.","steps":[{"title":"Encuentre la cabeza de la cadena de bloqueo (root blocker)","detail":"Determine quién bloquea a quién; el verdadero culpable es la sesión que no espera nada pero hace esperar a las demás. Concéntrese en la sesión que está en la cima de la cadena.","engines":{"oracle":"SELECT blocking_session, sid, serial#, username, seconds_in_wait, sql_id FROM v$session WHERE blocking_session IS NOT NULL ORDER BY seconds_in_wait DESC;","postgres":"SELECT pid, usename, pg_blocking_pids(pid) blocked_by, state, query FROM pg_stat_activity WHERE cardinality(pg_blocking_pids(pid))>0;","mysql":"SELECT waiting_pid, waiting_query, blocking_pid, blocking_query FROM sys.innodb_lock_waits;","mssql":"SELECT session_id, blocking_session_id, wait_type, wait_resource FROM sys.dm_exec_requests WHERE blocking_session_id<>0;","mongodb":"db.currentOp({ waitingForLock: true })"}},{"title":"Entienda qué está haciendo el root blocker","detail":"¿Es una transacción abierta pero sin commit? ¿La aplicación la dejó 'idle in transaction'? ¿Es un batch largo? Entienda qué es antes de terminarla a ciegas: puede estar a punto de finalizar.","engines":{"oracle":"SELECT s.sid, s.prev_sql_id, t.start_time, t.used_ublk FROM v$transaction t JOIN v$session s ON t.ses_addr=s.saddr;","postgres":"SELECT pid, state, xact_start, now()-xact_start age, query FROM pg_stat_activity WHERE pid=<root_pid>;"}},{"title":"Decida: esperar, resolver desde la aplicación o terminar","detail":"Si está a punto de finalizar, espere. Si la aplicación puede hacer commit/rollback, provóquelo. De lo contrario, si el negocio está detenido, termine el root blocker. Antes de terminarlo, considere el impacto (tiempo de rollback, datos)."},{"title":"Termine el root blocker si es necesario","detail":"Termine solo la sesión en la cabeza de la cadena; las sesiones debajo de ella se liberan automáticamente.","engines":{"oracle":"ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;","postgres":"SELECT pg_terminate_backend(<root_pid>);","mysql":"KILL <root_id>;","mssql":"KILL <root_session_id>;","mongodb":"db.killOp(<opid>)"}},{"title":"Corrija la causa raíz","detail":"Un índice faltante puede provocar bloqueos amplios; la aplicación puede mantener transacciones abiertas demasiado tiempo; el nivel de aislamiento puede ser incorrecto. Agregue índices, acorte las transacciones, configure un timeout para 'idle in transaction' y elija un nivel de aislamiento adecuado."}],"related":["blocking-sessions","kill-session","open-transactions"]},{"id":"connection-storm","title":"Connection storm (demasiadas conexiones)","icon":"🌊","symptom":"'too many connections', 'ORA-00018', 'remaining connection slots reserved': se están rechazando conexiones nuevas.","steps":[{"title":"Mida el estado actual","detail":"¿Cuántas conexiones hay, cuál es el límite y cuántas están activas frente a inactivas?","engines":{"oracle":"SELECT COUNT(*) total, SUM(CASE WHEN status='ACTIVE' THEN 1 ELSE 0 END) active FROM v$session WHERE type='USER';","postgres":"SELECT count(*), state FROM pg_stat_activity WHERE backend_type = 'client backend' GROUP BY state;","mysql":"SHOW STATUS LIKE 'Threads_connected'; SHOW VARIABLES LIKE 'max_connections';","mssql":"SELECT COUNT(*), status FROM sys.dm_exec_sessions WHERE is_user_process=1 GROUP BY status;","redis":"INFO clients"}},{"title":"Identifique el origen: quién/desde dónde","detail":"¿Qué aplicación/host/usuario está abriendo las conexiones? Normalmente el pool de un servidor de aplicaciones se ha desbordado o hay una fuga de conexiones (un montón de sesiones idle in transaction).","engines":{"postgres":"SELECT client_addr, usename, state, count(*) FROM pg_stat_activity WHERE backend_type = 'client backend' GROUP BY 1,2,3 ORDER BY 4 DESC;","mysql":"SELECT host, user, count(*) FROM information_schema.processlist GROUP BY host, user ORDER BY 3 DESC;","mssql":"SELECT host_name, program_name, login_name, COUNT(*) FROM sys.dm_exec_sessions WHERE is_user_process=1 GROUP BY host_name, program_name, login_name ORDER BY 4 DESC;"}},{"title":"Gane margen de inmediato","detail":"Limpie las conexiones inactivas durante mucho tiempo/idle in transaction. Si es necesario, cierre en bloque las conexiones de la aplicación problemática. Use el slot reservado para el inicio de sesión de administración (PG superuser_reserved_connections)."},{"title":"La solución correcta: connection pooling","detail":"Aumentar max_connections sin límite consume memoria y solo pospone el problema. La solución correcta es un pool de conexiones en el lado de la aplicación (HikariCP, PgBouncer, ProxySQL) con un tamaño de pool razonable. El tamaño del pool suele rondar (núcleos*2 + número de discos); consulte la calculadora Herramientas > Connection Pool."},{"title":"Evite fugas y recurrencias","detail":"Asegúrese de que la aplicación cierre las conexiones (finally/using). Configure idle_in_transaction_session_timeout y wait_timeout. Configure una alerta sobre el número de conexiones."}],"related":["connection-count","kill-session","open-transactions"]},{"id":"replication-lag","title":"Retraso de replicación / replicación detenida","icon":"🔁","symptom":"El standby/réplica se está quedando atrás o se ha detenido por completo; las réplicas de lectura muestran datos obsoletos.","steps":[{"title":"¿Qué tan grande es el lag y está creciendo o es estable?","detail":"¿Cuál es el lag en segundos/bytes? ¿Es un desfase estable o crece continuamente? Si crece, la réplica no puede seguir el ritmo del apply; si es estable, hubo una detención momentánea.","engines":{"oracle":"SELECT name, value, unit FROM v$dataguard_stats WHERE name LIKE '%lag%';","postgres":"SELECT client_addr, state, write_lag, flush_lag, replay_lag FROM pg_stat_replication;","mysql":"SHOW REPLICA STATUS\\G  -- Seconds_Behind_Source","mssql":"SELECT ar.replica_server_name, drs.redo_queue_size, drs.log_send_queue_size FROM sys.dm_hadr_database_replica_states drs JOIN sys.availability_replicas ar ON drs.replica_id=ar.replica_id;","redis":"INFO replication  # master_repl_offset vs slave offset"}},{"title":"¿Está funcionando la replicación? ¿Hay errores?","detail":"¿Cuál es el estado del hilo IO/SQL, del proceso de apply? ¿Se cortó la conexión? Si hay un error (p. ej., fila en conflicto, tabla faltante), el apply se ha detenido; lea el mensaje de error.","engines":{"mysql":"SHOW REPLICA STATUS\\G  -- Replica_IO_Running / Replica_SQL_Running / Last_Error","postgres":"SELECT status, last_msg_receipt_time, latest_end_lsn FROM pg_stat_wal_receiver;  -- on the standby","oracle":"SELECT process, status, sequence#, thread# FROM v$managed_standby WHERE process LIKE 'MRP%' OR process LIKE 'RFS%';"}},{"title":"Encuentre el cuello de botella","detail":"¿Por qué no puede seguir el ritmo? ¿Apply de un solo hilo más una carga de escritura intensa (PG/MySQL aplican mayormente en serie), un cuello de botella de I/O en el standby, la red o una consulta larga que bloquea el apply (PG hot_standby_feedback / recovery conflict)?"},{"title":"Reduzca el lag a cero","detail":"Acelere el apply: habilite el apply paralelo (MySQL replica_parallel_workers; slave_parallel_workers antes de 8.0.26; PG no lo tiene, pero hay parámetros de recovery), mejore el I/O del standby, resuelva la consulta que bloquea el apply. Si se detuvo con un error: corrija el error (resuelva manualmente la fila en conflicto) y reanude la replicación."},{"title":"Reconstruya si está roto","detail":"Si la réplica está muy atrasada/rota, lo más limpio es resincronizar: reconstruya el standby a partir de un backup reciente (PG pg_basebackup, MySQL clone/xtrabackup, Oracle RMAN duplicate). Crítico: encuentre la causa antes de reconstruir, o volverá a ocurrir."}],"related":["replication-status","list-replicas","promote-failover"]},{"id":"high-cpu","title":"CPU alta","icon":"🔥","symptom":"CPU del servidor al 90%+; todo el sistema está lento y el load average es alto.","steps":[{"title":"Verifique que la BD es realmente la que consume la CPU","detail":"Use top/mpstat a nivel de SO para verificar que el proceso que consume CPU es realmente la BD. A veces el consumidor es un proceso vecino o un backup/antivirus. Si el iowait es alto, el problema es el disco, no la CPU."},{"title":"Encuentre las consultas que más CPU usan","detail":"Extraiga las consultas principales ordenadas por tiempo de CPU. Normalmente unas pocas consultas (mal plan, full scan, índice faltante) consumen la CPU.","engines":{"oracle":"SELECT sql_id, cpu_time/1e6 cpu_sec, executions, sql_text FROM v$sql ORDER BY cpu_time DESC FETCH FIRST 10 ROWS ONLY;","postgres":"SELECT query, calls, total_exec_time, mean_exec_time FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;","mysql":"SELECT digest_text, count_star, sum_timer_wait/1e12 sec FROM performance_schema.events_statements_summary_by_digest ORDER BY sum_timer_wait DESC LIMIT 10;","mssql":"SELECT TOP 10 total_worker_time/execution_count avg_cpu, execution_count, SUBSTRING(t.text,1,200) FROM sys.dm_exec_query_stats q CROSS APPLY sys.dm_exec_sql_text(q.sql_handle) t ORDER BY total_worker_time DESC;"}},{"title":"Compruebe si hay un problema de plan / índice","detail":"Obtenga el plan de la consulta problemática. Los full table scans, la explosión de nested loops, los derrames (spills) de hash join o los malos planes causados por parameter sniffing/estadísticas obsoletas consumen CPU. Agregue un índice o recopile estadísticas."},{"title":"Alivie de inmediato","detail":"Termine la consulta descontrolada y, si es necesario, limítela con Resource Manager/Resource Governor. Ante una carga que empezó de repente, revise el último despliegue/cambio de parámetros."},{"title":"Corrija de forma permanente","detail":"Corrija la consulta/índice, asegure la estabilidad del plan y programe el job de recopilación de estadísticas. Si la CPU está constantemente al 100%, puede ser necesario escalar (agregar CPU, distribuir lecturas en réplicas de lectura)."}],"related":["top-slow-queries","explain-plan","active-sessions","update-statistics"]},{"id":"memory-pressure","title":"Presión de memoria / OOM","icon":"🧠","symptom":"El OOM killer termina el proceso, 'out of memory', cache hit ratio bajo, swapping excesivo, Redis 'OOM command not allowed'.","steps":[{"title":"¿Quién está consumiendo memoria?","detail":"¿Es la memoria asignada de la BD (buffer pool/SGA), la memoria por sesión (PGA/sort/work_mem) o el SO en su conjunto? Revise free/vmstat a nivel de SO y las vistas de memoria dentro de la BD.","engines":{"oracle":"SELECT component, current_size/1048576 mb FROM v$memory_dynamic_components WHERE current_size>0;","postgres":"SHOW shared_buffers; SHOW work_mem; -- work_mem * connections * number of sorts can blow up in total","mysql":"SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW STATUS LIKE 'Innodb_buffer_pool%';","redis":"INFO memory  # used_memory, maxmemory, mem_fragmentation_ratio"}},{"title":"Separe la configuración de la carga","detail":"¿La suma de los parámetros de memoria supera la RAM física (over-commit)? Calcule shared_buffers + (work_mem * concurrencia). ¿O es una carga momentánea (un sort/hash enorme, muchas conexiones)?"},{"title":"Específico de Redis: eviction y fragmentación","detail":"Si used_memory ha alcanzado maxmemory, revise maxmemory-policy (con noeviction, se rechazan las escrituras). Si mem_fragmentation_ratio es alto, use MEMORY PURGE / activedefrag. Encuentre claves grandes con --bigkeys.","engines":{"redis":"CONFIG GET maxmemory-policy\nMEMORY DOCTOR\nredis-cli --bigkeys"}},{"title":"Alivie de inmediato","detail":"Termine la consulta/sesión descontrolada que consume memoria, reduzca temporalmente la memoria de sesión como work_mem, cierre las conexiones innecesarias. Si el sistema está haciendo swapping, salir del swap es la prioridad."},{"title":"Dimensione correctamente","detail":"Distribuya la RAM correctamente con la calculadora Herramientas > Memory/Buffer Pool. Regla general: en un servidor dedicado, buffer pool/SGA ~70-80% de la RAM; la memoria total de sesión multiplicada por la concurrencia no debe superar la RAM. Agregue RAM si es necesario."}],"related":["cache-hit-ratio","show-config-param","reclaim-space"]},{"id":"log-growth","title":"Crecimiento del log de transacciones / undo","icon":"📈","symptom":"El archivo de log de transacciones o el área de undo/redo crece rápidamente; 'log full', 'ORA-01555 snapshot too old', 'unable to extend undo'.","steps":[{"title":"Encuentre la transacción abierta que hace crecer el log","detail":"El log/undo no se puede liberar hasta que finalice la transacción activa más antigua. Una sola transacción larga abierta puede estar reteniendo todo el log.","engines":{"oracle":"SELECT s.sid, s.username, t.start_time, t.used_ublk FROM v$transaction t JOIN v$session s ON t.ses_addr=s.saddr ORDER BY t.start_time;","postgres":"SELECT pid, state, xact_start, now()-xact_start age, query FROM pg_stat_activity WHERE xact_start IS NOT NULL ORDER BY xact_start;","mysql":"SELECT trx_id, trx_started, trx_mysql_thread_id FROM information_schema.innodb_trx ORDER BY trx_started;","mssql":"DBCC OPENTRAN;"}},{"title":"Entienda la causa raíz específica del motor","detail":"MSSQL: en el modelo de recuperación FULL, el log crece indefinidamente si no se toman backups de log. Oracle: ORA-01555 = retención/tamaño de undo insuficiente + una consulta larga. PG: si un replication slot no avanza, se acumula WAL; una transacción larga bloquea VACUUM."},{"title":"La acción correcta para el log de MSSQL","detail":"Tome un backup de log (BACKUP LOG): esto permite reutilizar el log. Si el modelo de recuperación realmente debe ser SIMPLE, cámbielo. Luego, si es necesario (y solo si es necesario), reduzca el log.","engines":{"mssql":"BACKUP LOG [DB] TO DISK='C:\\backup\\DB_log.trn';\n-- Then, if needed: DBCC SHRINKFILE (N'DB_log', 512);"}},{"title":"Revisión de WAL / replication slot en PG","detail":"Un replication slot que no avanza (inactivo) provoca acumulación de WAL. Antes de eliminarlo, verifique el propietario del slot (slot_type, database, plugin): puede ser un standby desconectado temporalmente o un suscriptor lógico/CDC (p. ej., Debezium). Si se elimina el slot, el standby asociado debe reconstruirse y el lado del suscriptor/CDC debe resincronizarse. Elimínelo solo si realmente no se usa; para una protección duradera, establezca un límite superior con max_slot_wal_keep_size (PG13+).","engines":{"postgres":"SELECT slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) retained FROM pg_replication_slots ORDER BY 3 DESC;\n-- If unneeded: SELECT pg_drop_replication_slot('slot_name');"}},{"title":"Ajuste undo/retención y evite que se repita","detail":"Oracle: amplíe el tablespace UNDO, ajuste undo_retention según las consultas largas (o RETENTION GUARANTEE). Evite transacciones largas abiertas en la aplicación. Supervise que el job de backup de log se ejecute."}],"related":["open-transactions","reclaim-space","last-backup-status"]},{"id":"deadlocks","title":"Análisis de deadlocks","icon":"♻️","symptom":"'deadlock detected', 'ORA-00060', 'chosen as deadlock victim': las transacciones se bloquean entre sí y una se aborta.","steps":[{"title":"Los deadlocks son normales, pero no deberían ser frecuentes","detail":"La BD detecta el deadlock y lo resuelve eligiendo una víctima: esto es por diseño. El objetivo es reducir la frecuencia. La aplicación debe reintentar la transacción víctima. Primero capture el deadlock graph."},{"title":"Obtenga el deadlock graph/detalles","detail":"¿Qué dos (o más) sesiones, qué recursos y qué modos de bloqueo entraron en conflicto?","engines":{"oracle":"-- Check the trace file referenced in the alert log (deadlock graph). Also:\nSELECT * FROM dba_hist_active_sess_history WHERE event LIKE 'enq: TX%' ORDER BY sample_time DESC;","postgres":"-- log_lock_waits=on; deadlock details are in postgresql.log. Check the deadlock_timeout setting.","mysql":"SHOW ENGINE INNODB STATUS\\G  -- LATEST DETECTED DEADLOCK section","mssql":"-- Deadlock XML in the system_health Extended Events session (ring_buffer, recent events):\nSELECT x.ev.value('@timestamp','datetime2') AS event_time, x.ev.query('.') AS deadlock_xml\nFROM (SELECT CAST(t.target_data AS XML) td FROM sys.dm_xe_session_targets t\n      JOIN sys.dm_xe_sessions s ON s.address = t.event_session_address\n      WHERE s.name = N'system_health' AND t.target_name = N'ring_buffer') r\nCROSS APPLY r.td.nodes('//RingBufferTarget/event[@name=\"xml_deadlock_report\"]') AS x(ev);\n-- For longer history, the event_file target:\n-- SELECT CAST(event_data AS XML) FROM sys.fn_xe_file_target_read_file(N'system_health*.xel', NULL, NULL, NULL) WHERE object_name = N'xml_deadlock_report';"}},{"title":"Resuelva el patrón de conflicto","detail":"La causa más común: dos transacciones que acceden a los recursos en orden OPUESTO. Acceda a los recursos en el MISMO orden en todas las rutas de código. La segunda causa más común: bloqueos de rango amplios por un índice faltante; agregue el índice necesario."},{"title":"Reduzca la duración y el alcance de los bloqueos","detail":"Mantenga las transacciones cortas (no incluya interacción del usuario dentro de una transacción). Elija un nivel de aislamiento adecuado. Cuando sea necesario, adquiera los bloqueos de fila temprano (SELECT ... FOR UPDATE con un orden determinista)."},{"title":"El reintento en la aplicación es obligatorio","detail":"Los deadlocks no se pueden eliminar por completo; implemente el reintento automático (exponential backoff) para la transacción víctima. Así el usuario no ve un error."}],"related":["blocking-sessions","open-transactions","list-indexes"]},{"id":"db-down","title":"La base de datos no abre / no está accesible","icon":"🚨","symptom":"No es posible conectarse al servicio, 'connection refused', la instancia no arranca, el listener no responde.","steps":[{"title":"¿Dónde falla: red, servicio o instancia?","detail":"¿Es accesible el servidor (ping/ssh)? ¿Se está ejecutando el proceso/servicio de la BD (ps/systemctl/services.msc)? ¿Está escuchando el puerto (ss -ltnp / telnet host port)? Esto ubica el problema en una capa: red / servicio / instancia de base de datos."},{"title":"Revise el servicio y el listener","detail":"Si el proceso de la BD no está levantado, intente iniciarlo; si está levantado pero las conexiones fallan, revise el listener/puerto y la capa de autenticación.","engines":{"oracle":"-- OS: lsnrctl status  (listener)\n-- SQL*Plus: STARTUP;  (if the instance is down)\nSELECT status FROM v$instance;","postgres":"-- OS: systemctl status postgresql ; pg_isready -h host\n-- Logs: the last lines tell why it did not start","mysql":"-- OS: systemctl status mysqld ; mysqladmin ping","mssql":"-- Services / SQL Server Configuration Manager; SQL Server Error Log","redis":"redis-cli ping  # expect PONG"}},{"title":"Lea los logs: la respuesta está ahí","detail":"Si la BD no abre, el motivo está escrito en el log: alert log (Oracle), postgresql.log / arranque, error log de mysqld, ERRORLOG de SQL Server, log de redis. Disco lleno, archivo corrupto, permisos, conflicto de puerto, archivo faltante: todo aparece aquí."},{"title":"Atienda las causas comunes: disco, permisos, puerto, corrupción","detail":"¿Está el disco lleno al 100% (la causa más común)? ¿Están dañados los permisos del directorio de datos? ¿Ocupa el puerto otro proceso? ¿Se necesita recovery (tras un crash)? ¿Hay un control file/redo log corrupto? Aborde cada uno según el mensaje del log."},{"title":"Recupere y mantenga un registro del incidente","detail":"El crash recovery normalmente se completa automáticamente; tenga paciencia. Si hay corrupción, recupere desde backup/PITR (consulte el playbook de Corrupción). Después, redacte un análisis de causa raíz (RCA): qué pasó, por qué y cómo evitar que se repita."}],"related":["version-uptime","connect-cli","last-backup-status"]},{"id":"corruption","title":"Corrupción de datos","icon":"🧯","symptom":"'ORA-01578', 'page corruption', 'checksum mismatch', 'MISCONF', errores de consistencia de I/O durante las lecturas.","steps":[{"title":"Mantenga la calma y detenga la propagación","detail":"Una intervención apresurada empeora las cosas. Primero determine el alcance y, si es posible, detenga las escrituras en el objeto afectado. Si está relacionado con el hardware (disco/controladora), revise también los logs del SO/hardware: arreglar la BD no arregla el disco."},{"title":"Determine el alcance: cuánto está corrupto","detail":"¿Es un solo bloque/página/objeto o está extendido? Ejecute una verificación de integridad.","engines":{"oracle":"RMAN> VALIDATE DATABASE;  -- or: SELECT * FROM v$database_block_corruption;","postgres":"-- With data_checksums enabled it shows up in the logs; check indexes/heap with pg_amcheck:\n-- pg_amcheck --heapallindexed -d mydb","mysql":"CHECK TABLE customer;  -- for InnoDB, innodb_force_recovery is the last resort","mssql":"DBCC CHECKDB ('MyDB') WITH NO_INFOMSGS, ALL_ERRORMSGS;","redis":"redis-check-rdb dump.rdb\n# AOF (Redis 7+ multi-part: under appendonlydir/, pass the manifest file):\nredis-check-aof appendonlydir/appendonly.aof.manifest\n# --fix TRUNCATES everything after the corrupted point (writes in that part are lost); back up the directory first:\n# redis-check-aof --fix appendonlydir/appendonly.aof.manifest"}},{"title":"Prioridad: recuperación desde backup","detail":"Ante una corrupción, el camino más seguro es restaurar desde un backup bueno. Si se admite la recuperación a nivel de bloque/página, recupere solo la parte corrupta (Oracle RMAN BLOCKRECOVER, MSSQL page restore). Las herramientas de reparación (DBCC REPAIR_ALLOW_DATA_LOSS, innodb_force_recovery) CAUSAN PÉRDIDA DE DATOS: son el último recurso."},{"title":"Verifique la integridad","detail":"Tras la recuperación, vuelva a ejecutar la comprobación (DBCC CHECKDB / RMAN VALIDATE / CHECK TABLE) y confirme que el resultado es limpio. Verifique también la consistencia de los datos en el lado de la aplicación (conteo de filas, totales críticos)."},{"title":"Causa raíz: normalmente el hardware","detail":"La corrupción suele deberse a fallos de disco, memoria o controladora. Pruebe/sustituya el hardware, habilite checksums (data_checksums, PAGE_VERIFY CHECKSUM) y refuerce la rutina de backup + verificación. Programe DBCC CHECKDB / RMAN VALIDATE de forma periódica."}],"related":["last-backup-status","restore-dump","db-down"]},{"id":"long-running-query","title":"Consulta / job de larga duración","icon":"⏱️","symptom":"Una sola consulta o job lleva horas ejecutándose sin terminar; consume recursos o afecta a otros.","steps":[{"title":"Identifique la consulta y su duración","detail":"¿Qué consulta es, cuánto tiempo lleva ejecutándose, qué usuario/aplicación?","engines":{"oracle":"SELECT sid, serial#, sql_id, last_call_et/60 running_min, username FROM v$session WHERE status='ACTIVE' AND type='USER' ORDER BY last_call_et DESC;","postgres":"SELECT pid, now()-query_start dur, usename, query FROM pg_stat_activity WHERE state='active' ORDER BY query_start;","mysql":"SELECT id, user, time, state, info FROM information_schema.processlist WHERE command='Query' ORDER BY time DESC;","mssql":"SELECT session_id, total_elapsed_time/1000 sec, status, command FROM sys.dm_exec_requests WHERE session_id IN (SELECT session_id FROM sys.dm_exec_sessions WHERE is_user_process = 1) ORDER BY total_elapsed_time DESC;","mongodb":"db.currentOp({ active: true, secs_running: { $gt: 60 } })"}},{"title":"¿Avanza o está atascada?","detail":"¿Se está ejecutando realmente o espera un bloqueo? Revise los indicadores de progreso (Oracle v$session_longops, vistas pg_stat_progress_* de PG). Si está en espera, pase al playbook de bloqueos.","engines":{"oracle":"SELECT sid, opname, sofar, totalwork, ROUND(sofar/NULLIF(totalwork,0)*100,1) pct, time_remaining FROM v$session_longops WHERE sofar<totalwork;","postgres":"SELECT * FROM pg_stat_progress_create_index;  -- or pg_stat_progress_vacuum / _cluster / _analyze"}},{"title":"Decida: esperar o cancelar","detail":"Si avanza y terminará pronto, espere (el costo del rollback al cancelar puede hacer que tarde aún más). Si está atascada, se ejecuta incorrectamente o afecta a producción, termínela. Tenga en cuenta el tiempo de rollback antes de cancelar."},{"title":"Termine si es necesario","detail":"Prefiera cancelar solo la consulta (manteniendo la sesión); si no funciona, mate la sesión.","engines":{"oracle":"ALTER SYSTEM CANCEL SQL 'sid,serial#';  -- 18c+; otherwise KILL SESSION","postgres":"SELECT pg_cancel_backend(pid);  -- otherwise pg_terminate_backend(pid)","mysql":"KILL QUERY <id>;","mssql":"KILL <session_id>;","mongodb":"db.killOp(<opid>)"}},{"title":"Corrija de forma permanente","detail":"Optimice la consulta (índice, plan, paginación/lotes), traslade los jobs pesados a horas de baja carga y, si es necesario, ejecútelos por partes. Para informes recurrentes, considere una vista materializada / réplica de lectura."}],"related":["active-sessions","kill-session","explain-plan","top-slow-queries"]},{"id":"xid-wraparound","title":"Postgres: XID wraparound / crisis de autovacuum","icon":"🧬","symptom":"'database is not accepting commands to avoid wraparound data loss'; autovacuum no da abasto, datfrozenxid envejece.","steps":[{"title":"Evalúe la urgencia","detail":"Los ID de transacción de PostgreSQL son de 32 bits; cuando se acercan al agotamiento, el servidor DETIENE LAS ESCRITURAS para evitar pérdida de datos. Es una situación crítica y urgente. La buena noticia: tiene solución; requiere disciplina, no pánico."},{"title":"Mida qué tan cerca está","detail":"Obtenga la edad del XID no congelado más antiguo de cada base de datos; la cercanía a autovacuum_freeze_max_age (200M por defecto) indica el riesgo.","engines":{"postgres":"SELECT datname, age(datfrozenxid) AS xid_age\nFROM pg_database ORDER BY xid_age DESC;"}},{"title":"Encuentre las tablas más antiguas","detail":"El problema suele provenir de unas pocas tablas grandes o con escritura frecuente; céntrese en los valores de relfrozenxid más antiguos.","engines":{"postgres":"SELECT relname, age(relfrozenxid) AS xid_age\nFROM pg_class WHERE relkind IN ('r','m','t')\nORDER BY xid_age DESC LIMIT 20;"}},{"title":"Elimine lo que bloquea VACUUM","detail":"Las transacciones abiertas durante mucho tiempo, los replication slots abandonados y las transacciones preparadas impiden la limpieza (freeze). Encuéntrelos y resuélvalos; de lo contrario VACUUM no avanza por mucho que se ejecute.","engines":{"postgres":"SELECT pid, now()-xact_start AS age, state FROM pg_stat_activity WHERE xact_start IS NOT NULL ORDER BY age DESC;\nSELECT slot_name, active FROM pg_replication_slots WHERE NOT active;\nSELECT * FROM pg_prepared_xacts;"}},{"title":"Ejecute un FREEZE agresivo","detail":"Una vez eliminados los bloqueadores, fuerce el congelamiento empezando por las tablas más antiguas. Aunque el servidor haya dejado de asignar nuevos ID de transacción, no pase a modo monousuario (postgres --single): la documentación actual de PostgreSQL indica que ya no es necesario y que debe evitarse. Basta con conectarse como superusuario en modo normal y ejecutar VACUUM; en PG14+, el mecanismo failsafe de VACUUM se activa al acercarse el wraparound.","engines":{"postgres":"VACUUM (FREEZE, VERBOSE) oldest_table;\n-- Entire database: vacuumdb --all --freeze --jobs=4"}},{"title":"Permanente: refuerce y monitoree autovacuum","detail":"Aumente autovacuum_vacuum_cost_limit, reduzca autovacuum_naptime y defina parámetros de almacenamiento específicos en las tablas con mucha escritura. Configure una alerta sobre age(datfrozenxid); no vuelva a pasar por esta crisis."}],"related":["open-transactions","reclaim-space"]},{"id":"tempdb-contention","title":"Contención en tempdb (SQL Server)","icon":"🌀","symptom":"Esperas PAGELATCH_UP/EX, contención en las páginas de asignación de tempdb (PFS/GAM/SGAM), lentitud general.","steps":[{"title":"Confirme la contención","detail":"Las esperas PAGELATCH sobre páginas de sistema de tempdb (como 2:1:1) indican contención de asignación.","engines":{"mssql":"SELECT session_id, wait_type, wait_duration_ms, resource_description\nFROM sys.dm_os_waiting_tasks\nWHERE wait_type LIKE 'PAGELATCH%' AND resource_description LIKE '2:%'\nORDER BY wait_duration_ms DESC;"}},{"title":"Desglose el uso de tempdb","detail":"¿Quién usa cuánto tempdb? ¿Objetos internos (spills de sort/hash), el version store u objetos de usuario?","engines":{"mssql":"SELECT * FROM sys.dm_db_file_space_usage;\nSELECT TOP 10 session_id, user_objects_alloc_page_count, internal_objects_alloc_page_count\nFROM sys.dm_db_task_space_usage ORDER BY internal_objects_alloc_page_count DESC;"}},{"title":"Corrija la configuración de archivos","detail":"Use varios archivos de datos de IGUAL tamaño para tempdb, tantos como núcleos de CPU (hasta 8). SQL 2016+ lo recomienda durante la instalación; en versiones anteriores use los trace flags 1117/1118. Coloque los archivos en disco rápido (NVMe local)."},{"title":"Encuentre la consulta descontrolada","detail":"Las consultas con grandes spills de sort/hash hacen crecer tempdb sin control. Apunte a las que tienen memory grants insuficientes o planes malos; reduzca los spills con índices/planes.","engines":{"mssql":"SELECT TOP 10 t.text, mg.requested_memory_kb, mg.granted_memory_kb\nFROM sys.dm_exec_query_memory_grants mg CROSS APPLY sys.dm_exec_sql_text(mg.sql_handle) t\nORDER BY mg.requested_memory_kb DESC;"}},{"title":"Equivalentes en otras bases de datos","detail":"La misma 'contención de espacio temporal' corresponde al tablespace TEMP en Oracle (v$tempseg_usage / v$sort_usage) y a temp_files y el desbordamiento de work_mem en PostgreSQL. La solución de fondo es siempre la misma: corregir la consulta que desborda y acelerar el espacio temporal."}],"related":["free-space","top-slow-queries","reclaim-space"]},{"id":"cert-expiry","title":"TLS / certificado caducado","icon":"🔐","symptom":"'certificate has expired', 'SSL handshake failed', los clientes de repente no pueden conectarse (sin cambios de código).","steps":[{"title":"¿Dónde falla: servidor, cliente o CA?","detail":"¿Caducó el certificado del servidor, un certificado de cliente o una CA de la cadena? El síntoma suele ser 'ayer funcionaba, hoy no, y no cambió el código'."},{"title":"Compruebe la caducidad","detail":"Lea la fecha de caducidad del certificado directamente desde el servidor (método rápido e independiente de la base de datos).","engines":{"postgres":"openssl s_client -connect host:5432 -starttls postgres </dev/null 2>/dev/null | openssl x509 -noout -dates","mongodb":"openssl s_client -connect host:27017 </dev/null 2>/dev/null | openssl x509 -noout -enddate","redis":"openssl s_client -connect host:6379 </dev/null 2>/dev/null | openssl x509 -noout -enddate"}},{"title":"Instale y cargue el nuevo certificado","detail":"Sustituya el certificado y, en la mayoría de los casos, recargue el servicio sin reiniciarlo.","engines":{"postgres":"-- Renew ssl_cert_file/ssl_key_file, then:\nSELECT pg_reload_conf();","mysql":"ALTER INSTANCE RELOAD TLS;","mssql":"-- SQL Server Configuration Manager > Certificate; a service restart is required","mongodb":"// Renew the net.tls.certificateKeyFile file (at the same path), then load it without a restart (5.0+):\ndb.adminCommand({rotateCertificates: 1})","redis":"CONFIG SET tls-cert-file /path/new.crt\nCONFIG SET tls-key-file /path/new.key","elasticsearch":"# ES watches the certificate/key files (PEM, PKCS#12) defined under xpack.security.*.ssl and\n# reloads them automatically when the file at the SAME path changes (no restart needed).\n# If the path, file name or keystore password changes, a rolling restart is required.\n# Note: POST _nodes/reload_secure_settings is only for reloadable keystore settings, not for TLS certificates.\n# Verify: check the new expiry date with openssl s_client and look in the ES logs for\n# an entry showing the certificate was reloaded."}},{"title":"Verifique","detail":"Confirme con el comando openssl anterior que el handshake TLS se completa con el nuevo certificado y que la caducidad se ha extendido; conéctese de extremo a extremo con un cliente."},{"title":"Permanente: que no vuelva a ocurrir","detail":"Configure una alerta 30 días antes de la caducidad del certificado; use renovación automática (ACME/Let's Encrypt, HashiCorp Vault, cert-manager). Automatice la recarga tras la renovación."}],"related":["connect-cli"]},{"id":"upgrade-rollback","title":"Actualización de versión / reversión de parche","icon":"⏪","symptom":"Problemas tras una actualización o parche: regresión de rendimiento, incompatibilidad con la aplicación o la base de datos no abre.","steps":[{"title":"Primero, alto: ¿tiene un plan de vuelta atrás?","detail":"Si no tomó un backup completo + (si es posible) un snapshot de almacenamiento ANTES de la actualización en producción, el camino más seguro es corregir hacia adelante (fix forward). En la mayoría de las bases de datos, revertir significa restaurar desde backup. Este paso debería haberse planificado ANTES de la actualización."},{"title":"Clasifique el problema","detail":"¿Es una regresión de planes del optimizador (consultas más lentas), una incompatibilidad de aplicación/driver o un fallo de apertura/corrupción? La vía de solución varía mucho según el caso."},{"title":"Vía de reversión por base de datos","detail":"Oracle: reversión del parche con datapatch, o Flashback Database (si creó un guaranteed restore point). PostgreSQL: reversión tras pg_upgrade = binarios antiguos + backup del directorio de datos antiguo. MySQL: el DOWNGRADE in-place en general NO ESTÁ SOPORTADO; restaure desde backup. SQL Server: no hay downgrade; restaure desde el backup previo a la actualización. En todos los casos: el snapshot/backup es su seguro."},{"title":"Si es una regresión de planes: corrija sin revertir","detail":"Normalmente no es necesario revertir toda la versión; solo han cambiado los planes.","engines":{"oracle":"ALTER SYSTEM SET optimizer_features_enable='19.1.0';  -- pin to the old behavior\n-- or lock in the good plan with a SQL Plan Baseline","postgres":"ANALYZE;  -- fresh statistics first\n-- if needed, temporarily: steer the plan with settings such as SET enable_nestloop=off;"}},{"title":"Permanente: la próxima vez","detail":"Pruebe primero la actualización en staging con una carga real, aplique actualizaciones canary/rolling, tome un guaranteed restore point / snapshot y documente el procedimiento de vuelta atrás."}],"related":["last-backup-status","restore-dump","version-uptime"]},{"id":"es-red-cluster","title":"Clúster Elasticsearch en red/yellow y shards sin asignar","icon":"🟥","symptom":"_cluster/health 'red' o 'yellow'; shards sin asignar; lecturas/escrituras restringidas o datos inaccesibles.","steps":[{"title":"Color y causa","detail":"yellow = hay shards réplica sin asignar (los datos están, la durabilidad se reduce). red = falta al menos un shard PRIMARIO (esos datos son inaccesibles). Determine primero cuál es el caso y por qué.","engines":{"elasticsearch":"GET _cluster/health?level=indices\nGET _cluster/allocation/explain"}},{"title":"Liste los shards sin asignar y el motivo","detail":"La columna unassigned.reason indica la causa raíz (NODE_LEFT, ALLOCATION_FAILED, DISK, etc.).","engines":{"elasticsearch":"GET _cat/shards?v&h=index,shard,prirep,state,unassigned.reason&s=state"}},{"title":"Corrija las causas habituales","detail":"1) Disk watermark → por encima de low, el nodo no recibe nuevos shards; en flood_stage, los índices pasan a solo lectura. 2) Un nodo cayó / salió. 3) La asignación quedó deshabilitada. 4) Número de réplicas > número de nodos elegibles."},{"title":"Corrija","detail":"Desbloquee según la causa que corresponda.","engines":{"elasticsearch":"# If disk space was freed, remove the read-only block (7.4+ also lifts it automatically once below the high watermark):\nPUT _all/_settings {\"index.blocks.read_only_allow_delete\": null}\n# If allocation was left restricted, return it to the default (all):\nPUT _cluster/settings {\"persistent\":{\"cluster.routing.allocation.enable\":null}}\n# Retry failed allocations:\nPOST _cluster/reroute?retry_failed=true"}},{"title":"Si sigue en rojo y el primario realmente se ha perdido","detail":"Restaure el índice afectado desde un snapshot o reindexe desde los datos de origen (_reindex). Permanente: alertas de disk watermark, un número de réplicas adecuado (≥1) y monitoreo de la salud de los nodos."}],"related":["replication-status","list-replicas","free-space"]},{"id":"time-skew","title":"Desfase de reloj / problema de NTP","icon":"⏱️","symptom":"Diferencias de reloj entre los nodos del clúster; inconsistencias de replicación/elección, 'certificate not yet valid', saltos de tiempo en los logs.","steps":[{"title":"Reconozca el síntoma","detail":"El desfase de reloj es traicionero: elecciones inestables (flapping) en Mongo/Redis Sentinel, errores TLS 'not yet valid/expired', errores de Kerberos, marcas de tiempo que retroceden en los logs. En sistemas distribuidos es la raíz de la mayoría de las rarezas 'inexplicables'."},{"title":"Mida la diferencia","detail":"En cada nodo, compare la fuente de tiempo y el offset: `timedatectl`, `chronyc tracking` (o `ntpq -p`) y `date -u` entre nodos. ¿Cuántos ms/s de diferencia hay?"},{"title":"Corrija NTP/chrony","detail":"¿Se está ejecutando chronyd/ntpd, alcanza sus fuentes de tiempo, está sincronizado? Inicie el servicio y corrija la fuente; ante offsets grandes, ajuste con cuidado (un salto brusco puede afectar a algunas BD)."},{"title":"Verifique el componente afectado","detail":"Tras corregir el reloj, compruebe: ¿la elección del replica set de MongoDB es estable, Redis Sentinel/Cluster está sano, los handshakes TLS se completan, se han recuperado las métricas de lag de replicación?"},{"title":"Permanente","detail":"Haga NTP obligatorio en todos los nodos y monitoree el offset (alerta: >100ms). En máquinas virtuales, defina claramente la sincronización de hora host-invitado; preferiblemente use una única jerarquía de fuentes de tiempo fiable."}],"related":["replication-status"]}]},"playbooks/hadr.json":{"schemaVersion":1,"playbooks":[{"id":"hadr-oracle-dataguard","title":"Configuración de Oracle Data Guard (physical standby)","icon":"🛡️","symptom":"Objetivo: configurar recuperación ante desastres y alta disponibilidad para Oracle con un physical standby (Data Guard); poder cambiar al standby si el primario falla.","steps":[{"title":"Ponga el primario en modo FORCE LOGGING y ARCHIVELOG","detail":"Para que el standby se mantenga consistente con el primario, todos los cambios deben escribirse en redo. FORCE LOGGING registra incluso las operaciones NOLOGGING. Para el modo ARCHIVELOG, la base de datos se levanta en estado MOUNT.","engines":{"oracle":"ALTER DATABASE FORCE LOGGING;\n-- For ARCHIVELOG:\nSHUTDOWN IMMEDIATE;\nSTARTUP MOUNT;\nALTER DATABASE ARCHIVELOG;\nALTER DATABASE OPEN;"}},{"title":"Agregue archivos de standby redo log","detail":"Los standby redo logs (SRL) deben tener un grupo más que los online redo logs y el mismo tamaño. Se crean tanto en el primario como en el standby; son necesarios para real-time apply. En el ejemplo, si el online redo es de 200M, los SRL también son de 200M.","engines":{"oracle":"ALTER DATABASE ADD STANDBY LOGFILE ('/u01/oradata/srl01.log') SIZE 200M;\nALTER DATABASE ADD STANDBY LOGFILE ('/u01/oradata/srl02.log') SIZE 200M;\nALTER DATABASE ADD STANDBY LOGFILE ('/u01/oradata/srl03.log') SIZE 200M;\nALTER DATABASE ADD STANDBY LOGFILE ('/u01/oradata/srl04.log') SIZE 200M;"}},{"title":"Configure log_archive_dest y los parámetros relacionados","detail":"En el primario, defina dest_2 para enviar redo al standby. Configure DB_UNIQUE_NAME, LOG_ARCHIVE_CONFIG (el db_unique_name de ambos lados), FAL_SERVER y STANDBY_FILE_MANAGEMENT=AUTO. Así los archivos se crean automáticamente en el standby.","engines":{"oracle":"ALTER SYSTEM SET LOG_ARCHIVE_CONFIG='DG_CONFIG=(orcl_prim,orcl_stby)';\nALTER SYSTEM SET LOG_ARCHIVE_DEST_2='SERVICE=orcl_stby ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=orcl_stby';\nALTER SYSTEM SET FAL_SERVER='orcl_stby';  -- for role changes; on the standby FAL_SERVER='orcl_prim'\nALTER SYSTEM SET STANDBY_FILE_MANAGEMENT=AUTO;"}},{"title":"Cree el standby con RMAN DUPLICATE FOR STANDBY","detail":"Una vez configurados el password file y el listener estático/tnsnames en el servidor standby, RMAN se conecta al primario y al standby (AUXILIARY). FROM ACTIVE DATABASE copia por la red sin backup; DORECOVER aplica el redo.","engines":{"oracle":"rman TARGET sys/password@orcl_prim AUXILIARY sys/password@orcl_stby\nRUN {\n  DUPLICATE TARGET DATABASE FOR STANDBY FROM ACTIVE DATABASE\n  DORECOVER\n  NOFILENAMECHECK;\n}"}},{"title":"Inicie redo apply (MRP) y verifique","detail":"Inicie la recuperación gestionada con el standby en estado MOUNT; use USING CURRENT LOGFILE para real-time apply. Verifique con v$managed_standby que el proceso de aplicación (MRP0) se está ejecutando.","engines":{"oracle":"ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT FROM SESSION;\nSELECT process, status, sequence# FROM v$managed_standby WHERE process LIKE 'MRP%';"}},{"title":"Habilite Data Guard Broker","detail":"El broker centraliza la configuración y reduce failover/switchover a un solo comando. Configure DG_BROKER_START=TRUE en ambas instancias y luego cree y habilite la configuración con dgmgrl. En 12c+ el broker gestiona el transporte de redo: si LOG_ARCHIVE_DEST_2 se configuró manualmente, ADD DATABASE falla con ORA-16698; límpielo primero con ALTER SYSTEM SET LOG_ARCHIVE_DEST_2=''.","engines":{"oracle":"ALTER SYSTEM SET DG_BROKER_START=TRUE;\n-- In dgmgrl:\nCREATE CONFIGURATION dgconf AS PRIMARY DATABASE IS orcl_prim CONNECT IDENTIFIER IS orcl_prim;\nADD DATABASE orcl_stby AS CONNECT IDENTIFIER IS orcl_stby;\nENABLE CONFIGURATION;"}},{"title":"Monitoree el desfase de sincronización y complete las pruebas","detail":"La diferencia de secuencia entre primario y standby debe monitorearse continuamente; un desfase creciente indica un cuello de botella de red/aplicación. dgmgrl SHOW CONFIGURATION debe mostrar 'SUCCESS'. Opcionalmente, pruebe un cambio de rol con un switchover.","engines":{"oracle":"-- dgmgrl (SHOW DATABASE shows the Transport Lag / Apply Lag values):\nSHOW CONFIGURATION;\nSHOW DATABASE orcl_stby;\n-- SQL (standby): SELECT name, value FROM v$dataguard_stats WHERE name LIKE '%lag';"}}],"related":["replication-status","promote-failover","last-backup-status"]},{"id":"hadr-mssql-alwayson","title":"Configuración de SQL Server Always On Availability Group","icon":"🔁","symptom":"Objetivo: configurar failover automático y una réplica secundaria legible para SQL Server con un Always On Availability Group (AG).","steps":[{"title":"Configure un Windows Server Failover Cluster (WSFC)","detail":"Always On se apoya en un WSFC subyacente. Instale la característica Failover Clustering en todos los nodos y configure la red y el testigo (witness). Cree el clúster con PowerShell.","engines":{"mssql":"-- PowerShell (the Failover-Clustering feature must be installed on every node):\nNew-Cluster -Name sqlclu -Node node1,node2 -StaticAddress 10.0.0.50\n-- Witness:\nSet-ClusterQuorum -NodeAndFileShareMajority \\\\fs01\\quorum"}},{"title":"Habilite Always On en cada instancia","detail":"Habilite la característica Always On Availability Groups con SQL Server Configuration Manager o PowerShell. Después reinicie el servicio de SQL Server. Esto vincula la pertenencia al clúster con la instancia.","engines":{"mssql":"-- PowerShell:\nEnable-SqlAlwaysOn -ServerInstance node1\\SQL01 -Force\nEnable-SqlAlwaysOn -ServerInstance node2\\SQL01 -Force"}},{"title":"Haga backup de la base de datos y restáurela en el secundario WITH NORECOVERY","detail":"La base de datos que se agregará al AG debe estar en modelo de recuperación FULL. Tome un backup completo y uno de log en el primario; restáurelos en el secundario WITH NORECOVERY (para que pueda seguir recibiendo redo). Esto es seeding manual; la alternativa es el automatic seeding.","engines":{"mssql":"-- On the primary:\nBACKUP DATABASE app TO DISK='\\\\fs01\\bak\\app.bak';\nBACKUP LOG app TO DISK='\\\\fs01\\bak\\app.trn';\n-- On the secondary:\nRESTORE DATABASE app FROM DISK='\\\\fs01\\bak\\app.bak' WITH NORECOVERY;\nRESTORE LOG app FROM DISK='\\\\fs01\\bak\\app.trn' WITH NORECOVERY;"}},{"title":"Cree los endpoints y defina el Availability Group","detail":"Cree un endpoint HADR en el puerto 5022 para el tráfico de redo entre nodos. El modo síncrono (SYNCHRONOUS_COMMIT) ofrece failover automático sin pérdida de datos; el asíncrono (ASYNCHRONOUS_COMMIT) tiene retraso pero rinde bien para DR remoto.","engines":{"mssql":"CREATE ENDPOINT hadr_endpoint STATE=STARTED\n  AS TCP (LISTENER_PORT=5022)\n  FOR DATA_MIRRORING (ROLE=ALL, ENCRYPTION=REQUIRED ALGORITHM AES);\n\nCREATE AVAILABILITY GROUP appag\n  FOR DATABASE app\n  REPLICA ON\n    'NODE1\\SQL01' WITH (ENDPOINT_URL='TCP://node1:5022', AVAILABILITY_MODE=SYNCHRONOUS_COMMIT, FAILOVER_MODE=AUTOMATIC, SECONDARY_ROLE(ALLOW_CONNECTIONS=ALL)),\n    'NODE2\\SQL01' WITH (ENDPOINT_URL='TCP://node2:5022', AVAILABILITY_MODE=SYNCHRONOUS_COMMIT, FAILOVER_MODE=AUTOMATIC, SECONDARY_ROLE(ALLOW_CONNECTIONS=ALL));"}},{"title":"Una el secundario al AG y una la base de datos","detail":"Ejecute JOIN del AG en la instancia secundaria y luego agregue la base de datos restaurada al AG. Este paso es necesario si se usó seeding manual; con automatic seeding se gestiona en el primario mediante SET(SEEDING_MODE).","engines":{"mssql":"-- On the secondary:\nALTER AVAILABILITY GROUP appag JOIN;\nALTER DATABASE app SET HADR AVAILABILITY GROUP = appag;"}},{"title":"Cree el Availability Group Listener","detail":"El listener es el nombre y la IP virtuales a los que se conectan las aplicaciones; tras un failover, las conexiones se redirigen automáticamente al nuevo primario. Use este nombre y MultiSubnetFailover=True en la cadena de conexión de la aplicación.","engines":{"mssql":"ALTER AVAILABILITY GROUP appag\n  ADD LISTENER 'appag-listener' (WITH IP ((N'10.0.0.60', N'255.255.255.0')), PORT=1433);"}},{"title":"Pruebe el failover y verifique el estado de sincronización","detail":"Cuando la sincronización esté 'HEALTHY', pruebe un cambio de rol con un failover planificado. Para el failover automático, el modo debe ser síncrono y FAILOVER_MODE=AUTOMATIC. Monitoree el estado con sys.dm_hadr_availability_replica_states.","engines":{"mssql":"SELECT ag.name, ars.role_desc, ars.synchronization_health_desc\nFROM sys.dm_hadr_availability_replica_states ars\nJOIN sys.availability_groups ag ON ag.group_id=ars.group_id;\n-- Planned failover (on the node that will become the new primary):\nALTER AVAILABILITY GROUP appag FAILOVER;"}}],"related":["replication-status","promote-failover","list-replicas"]},{"id":"hadr-postgres-streaming","title":"Streaming replication de PostgreSQL + failover automático","icon":"🐘","symptom":"Objetivo: configurar un hot standby para PostgreSQL con streaming replication y agregar Patroni/repmgr para el failover automático.","steps":[{"title":"Configure los parámetros de WAL y de conexión en el primario","detail":"Para streaming replication, wal_level debe ser al menos 'replica'. max_wal_senders debe cubrir el número de réplicas + conexiones de backup, y la eliminación prematura de WAL debe evitarse con wal_keep_size o un replication slot. Los cambios requieren reinicio.","engines":{"postgres":"ALTER SYSTEM SET wal_level = 'replica';\nALTER SYSTEM SET max_wal_senders = 10;\nALTER SYSTEM SET max_replication_slots = 10;\nALTER SYSTEM SET hot_standby = on;\n-- To apply the postgresql.conf change:\n-- systemctl restart postgresql"}},{"title":"Defina el rol de replicación y el acceso en pg_hba","detail":"Cree un rol con el atributo REPLICATION para que el standby se conecte. Agregue a pg_hba.conf una línea que permita el acceso a la base de datos 'replication' desde la IP del standby y recargue con pg_reload_conf().","engines":{"postgres":"CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'secret';\n-- Add to pg_hba.conf:\n-- host replication replicator 10.0.0.20/32 scram-sha-256\nSELECT pg_reload_conf();"}},{"title":"Cree un replication slot persistente en el primario","detail":"Un physical replication slot impide que el primario elimine el WAL necesario aunque el standby se quede atrás; gestionar slots es más seguro que wal_keep_size. Usaremos el nombre del slot en la configuración del standby.","engines":{"postgres":"SELECT pg_create_physical_replication_slot('standby1_slot');\nSELECT slot_name, active, restart_lsn FROM pg_replication_slots;"}},{"title":"Tome un base backup en el servidor standby con pg_basebackup","detail":"Con el directorio de datos del standby vacío, tome una copia completa del primario. El flag -R escribe automáticamente el archivo standby.signal y primary_conninfo; -C también puede crear el slot. El nombre del slot se pasa con -S.","engines":{"postgres":"pg_basebackup -h 10.0.0.10 -U replicator -D /var/lib/pgsql/data \\\n  -R -X stream -S standby1_slot -P -v"}},{"title":"Verifique primary_conninfo y hot_standby, inicie el standby","detail":"Compruebe primary_conninfo (host, user, slot) en el postgresql.auto.conf escrito por -R; la presencia del archivo standby.signal determina el modo standby. Con hot_standby=on el standby acepta consultas de solo lectura. Inicie el servicio.","engines":{"postgres":"-- Expected in postgresql.auto.conf:\n-- primary_conninfo = 'host=10.0.0.10 user=replicator password=secret'\n-- primary_slot_name = 'standby1_slot'\n-- systemctl start postgresql\nSELECT pg_is_in_recovery();  -- should return true on the standby"}},{"title":"Monitoree el lag de replicación desde el primario","detail":"En el primario, pg_stat_replication muestra el LSN enviado/reproducido y el lag de cada standby. El lag de write/flush/replay debe mantenerse bajo; sync_state es 'sync' si se usa una réplica síncrona.","engines":{"postgres":"SELECT client_addr, state, sync_state,\n       pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytes,\n       write_lag, flush_lag, replay_lag\nFROM pg_stat_replication;"}},{"title":"Agregue Patroni o repmgr para el failover automático","detail":"La streaming replication por sí sola requiere un promote manual. El failover automático requiere un gestor de clúster: Patroni (elección de líder sobre etcd/Consul + promote automático) o repmgr (monitoreo/failover con repmgrd). En el lado de la aplicación, ofrezca un único endpoint con HAProxy/pgBouncer. Si se necesita un promote manual: pg_ctl promote. Antes de promover, asegúrese de que el primario antiguo esté realmente detenido y de que las aplicaciones no puedan escribir en él (riesgo de split-brain). Reincorporar el primario antiguo como standby del nuevo primario requiere pg_rewind; para ello, wal_log_hints = on o los data checksums deben estar habilitados.","engines":{"postgres":"-- Manual promote (if there is no cluster manager):\npg_ctl promote -D /var/lib/pgsql/data\n-- or via SQL:\nSELECT pg_promote();\n-- With Patroni: patronictl -c /etc/patroni.yml switchover mycluster"}}],"related":["replication-status","list-replicas","promote-failover"]},{"id":"hadr-mysql-innodb-cluster","title":"Configuración de MySQL InnoDB Cluster (Group Replication)","icon":"🐬","symptom":"Objetivo: configurar alta disponibilidad con failover automático para MySQL usando InnoDB Cluster (Group Replication + MySQL Shell + MySQL Router).","steps":[{"title":"Cumpla los requisitos previos","detail":"InnoDB Cluster requiere MySQL 8.0+, un server_id único, modo GTID y el motor de almacenamiento InnoDB en cada nodo. Todas las instancias deben ser accesibles entre sí por red, y las tablas deben tener claves primarias. Prepare un usuario clusterAdmin para la administración.","engines":{"mysql":"-- my.cnf on each instance:\n-- server_id unique, gtid_mode=ON, enforce_gtid_consistency=ON\nCREATE USER 'icadmin'@'%' IDENTIFIED BY 'secret';\nGRANT ALL PRIVILEGES ON *.* TO 'icadmin'@'%' WITH GRANT OPTION;"}},{"title":"Configure cada instancia con MySQL Shell","detail":"En mysqlsh, dba.configureInstance() prepara cada nodo para Group Replication; corrige los parámetros que faltan y solicita un reinicio si es necesario. Repita en todos los nodos.","engines":{"mysql":"-- mysqlsh:\ndba.configureInstance('icadmin@node1:3306')\ndba.configureInstance('icadmin@node2:3306')\ndba.configureInstance('icadmin@node3:3306')"}},{"title":"Cree el clúster (nodo semilla)","detail":"Conéctese a un nodo e inicie el clúster con dba.createCluster(); este nodo se convierte en el primer primario. Se devuelve un objeto cluster que se usa para las adiciones posteriores.","engines":{"mysql":"-- mysqlsh, while connected to node1:\nvar cluster = dba.createCluster('appCluster')"}},{"title":"Agregue las demás instancias al clúster","detail":"Agregue los nodos secundarios con cluster.addInstance(). Elija clone (clonación física, rápida para un nodo nuevo) o incremental (vía GTID) como recoveryMethod. Al menos 3 nodos proporcionan una mayoría impar y failover automático.","engines":{"mysql":"-- mysqlsh:\ncluster.addInstance('icadmin@node2:3306', {recoveryMethod: 'clone'})\ncluster.addInstance('icadmin@node3:3306', {recoveryMethod: 'clone'})"}},{"title":"Haga el bootstrap de MySQL Router","detail":"MySQL Router enruta entre la aplicación y el clúster; expone puertos de lectura/escritura (6446) y de solo lectura (6447) y redirige el tráfico automáticamente cuando cambia el primario. El bootstrap genera la configuración a partir de los metadatos del clúster.","engines":{"mysql":"mysqlrouter --bootstrap icadmin@node1:3306 --user=mysqlrouter --directory /etc/mysqlrouter\n# Then: mysqlrouter -c /etc/mysqlrouter/mysqlrouter.conf &"}},{"title":"Compruebe el estado del clúster y verifique el failover","detail":"cluster.status() muestra el rol de cada nodo (R/W primary, R/O secondary) y su estado (ONLINE). clusterName y la topología deben estar sanos. Pruebe el failover apagando el primario y observando la elección automática de un nuevo primario.","engines":{"mysql":"-- mysqlsh:\nvar cluster = dba.getCluster('appCluster')\ncluster.status()\n-- Members via SQL:\nSELECT member_host, member_role, member_state FROM performance_schema.replication_group_members;"}}],"related":["replication-status","list-replicas","promote-failover"]},{"id":"hadr-mongodb-replicaset","title":"Configuración de MongoDB replica set + sharding","icon":"🍃","symptom":"Objetivo: configurar un replica set con failover automático para MongoDB y ampliarlo a un clúster particionado (sharded cluster) para escalado horizontal.","steps":[{"title":"Inicie los procesos mongod con el mismo nombre de replSet","detail":"Inicie cada miembro del replica set con el mismo nombre --replSet y un --bind_ip accesible. En producción se recomienda autenticación interna con keyFile o TLS. Procure un número impar de miembros con voto para tener una mayoría clara.","engines":{"mongodb":"mongod --replSet rs0 --port 27017 --dbpath /data/n1 --bind_ip localhost,10.0.0.11\nmongod --replSet rs0 --port 27017 --dbpath /data/n2 --bind_ip localhost,10.0.0.12\nmongod --replSet rs0 --port 27017 --dbpath /data/n3 --bind_ip localhost,10.0.0.13"}},{"title":"Inicialice el replica set","detail":"Conéctese a un miembro con mongosh, llame a rs.initiate() y defina todos los miembros. Esto desencadena la primera elección y se elige un primario. Verifique los roles PRIMARY/SECONDARY con rs.status().","engines":{"mongodb":"rs.initiate({\n  _id: 'rs0',\n  members: [\n    { _id: 0, host: '10.0.0.11:27017' },\n    { _id: 1, host: '10.0.0.12:27017' },\n    { _id: 2, host: '10.0.0.13:27017' }\n  ]\n})\nrs.status()"}},{"title":"Agregue miembros, defina prioridades y un árbitro si es necesario","detail":"Agregue miembros más adelante con rs.add(). priority determina qué miembro tiende a convertirse en primario (0 = nunca se convierte en primario). Un árbitro solo vota y no almacena datos; se usa para desempatar con un número par de nodos de datos, pero para la seguridad de los datos es preferible un miembro real. En topologías con árbitro (especialmente PSA), el write concern majority no puede satisfacerse cuando cae un nodo de datos y las escrituras quedan en espera; en 5.0+ puede ser necesario definir explícitamente el write concern por defecto de todo el clúster con setDefaultRWConcern antes de agregar un árbitro.","engines":{"mongodb":"rs.add({ host: '10.0.0.14:27017', priority: 0.5 })\n// Arbiter only if really needed (a data-bearing member is preferred):\nrs.addArb('10.0.0.15:27017')\n// change priority:\ncfg = rs.conf(); cfg.members[0].priority = 2; rs.reconfig(cfg)"}},{"title":"Configure el replica set de config servers para sharding","detail":"Los metadatos del sharded cluster se guardan en un replica set de config servers (--configsvr). Se inicializa a su vez como replica set; se recomiendan 3 miembros para alta disponibilidad.","engines":{"mongodb":"mongod --configsvr --replSet cfgrs --port 27019 --dbpath /data/cfg1 --bind_ip localhost,10.0.0.21\n-- mongosh (on the config server):\nrs.initiate({ _id: 'cfgrs', configsvr: true, members: [\n  { _id: 0, host: '10.0.0.21:27019' },\n  { _id: 1, host: '10.0.0.22:27019' },\n  { _id: 2, host: '10.0.0.23:27019' } ] })"}},{"title":"Inicie los replica sets de shard y el router mongos","detail":"Cada shard es su propio replica set iniciado con --shardsvr (la durabilidad de los datos proviene de la replicación dentro del shard). El router mongos se inicia apuntando a los config servers; las aplicaciones se conectan a mongos.","engines":{"mongodb":"mongod --shardsvr --replSet shard1rs --port 27018 --dbpath /data/s1 --bind_ip localhost,10.0.0.31\n# initiate shard1rs with rs.initiate, then mongos:\nmongos --configdb cfgrs/10.0.0.21:27019,10.0.0.22:27019,10.0.0.23:27019 --port 27017 --bind_ip localhost,10.0.0.40"}},{"title":"Agregue los shards y particione la colección","detail":"Conéctese a mongos y agregue cada replica set de shard con sh.addShard(). Habilite el sharding en la base de datos y distribuya la colección por una shard key con sh.shardCollection(). La elección de la clave es crítica: debe tener alta cardinalidad y una distribución uniforme. Monitoree la distribución de chunks con sh.status().","engines":{"mongodb":"sh.addShard('shard1rs/10.0.0.31:27018')\nsh.addShard('shard2rs/10.0.0.32:27018')\nsh.enableSharding('appdb')\nsh.shardCollection('appdb.events', { customer_id: 'hashed' })\nsh.status()"}}],"related":["replication-status","list-replicas","promote-failover"]},{"id":"hadr-redis-sentinel","title":"Alta disponibilidad con Redis Sentinel + Cluster","icon":"⚡","symptom":"Objetivo: configurar un despliegue master-réplica con failover automático para Redis usando Sentinel; pasar al modo Redis Cluster si se necesita escalado horizontal.","steps":[{"title":"Configure el master y las réplicas","detail":"Ejecute un master y al menos dos réplicas. Las réplicas siguen al master con REPLICAOF ('replicaof <host> <port>' también puede escribirse en redis.conf). Habilite AOF o RDB para la persistencia; se recomienda autenticación con requirepass/masterauth.","engines":{"redis":"# at runtime on the replica nodes:\nREPLICAOF 10.0.0.10 6379\n# to make it persistent, redis.conf:\n# replicaof 10.0.0.10 6379\n# masterauth secret\nINFO replication"}},{"title":"Configure Sentinel con monitor y quorum","detail":"Defina el master a monitorear y el quorum en el sentinel.conf de cada nodo Sentinel. quorum es el número mínimo de Sentinels necesarios para considerar el master 'caído'. En producción se recomiendan 3 Sentinels en 3 servidores distintos (contra split-brain).","engines":{"redis":"# sentinel.conf:\nsentinel monitor mymaster 10.0.0.10 6379 2\nsentinel down-after-milliseconds mymaster 5000\nsentinel failover-timeout mymaster 60000\nsentinel parallel-syncs mymaster 1"}},{"title":"Inicie los procesos Sentinel","detail":"Inicie Sentinel en cada nodo. Los Sentinels se descubren automáticamente entre sí y a las réplicas; escriben la configuración en el archivo en tiempo de ejecución. Por eso sentinel.conf debe ser escribible.","engines":{"redis":"redis-sentinel /etc/redis/sentinel.conf\n# or:\nredis-server /etc/redis/sentinel.conf --sentinel"}},{"title":"Consulte el estado mediante Sentinel y pruebe el failover","detail":"Consulte el master y las réplicas actuales con los comandos SENTINEL. Las aplicaciones deben conocer el master actual preguntando a Sentinel con 'get-master-addr-by-name'. Verifique el cambio automático con un failover planificado.","engines":{"redis":"SENTINEL master mymaster\nSENTINEL get-master-addr-by-name mymaster\nSENTINEL replicas mymaster\n# planned switchover:\nSENTINEL failover mymaster"}},{"title":"Alternativa: particione los datos con Redis Cluster","detail":"Si la memoria/throughput de un solo master no basta, use Redis Cluster: los datos se dividen en 16384 hash slots y se distribuyen entre varios masters; cada master tiene su propia réplica. La aplicación debe usar un cliente compatible con clúster. Configúrelo con redis-cli --cluster create.","engines":{"redis":"redis-cli --cluster create \\\n  10.0.0.11:6379 10.0.0.12:6379 10.0.0.13:6379 \\\n  10.0.0.21:6379 10.0.0.22:6379 10.0.0.23:6379 \\\n  --cluster-replicas 1"}},{"title":"Verifique el estado del clúster","detail":"Tras crear el clúster, verifique que todos los slots estén asignados y que los nodos estén conectados. cluster_state debe ser 'ok' y los 16384 slots deben aparecer asignados. Monitoree la distribución de nodos y slots con los comandos CLUSTER.","engines":{"redis":"redis-cli --cluster check 10.0.0.11:6379\nredis-cli -c -h 10.0.0.11 CLUSTER INFO\nredis-cli -c -h 10.0.0.11 CLUSTER NODES"}}],"related":["replication-status","list-replicas","promote-failover"]},{"id":"hadr-elasticsearch-ha","title":"Resiliencia de Elasticsearch: réplica + snapshot + CCR","icon":"🔎","symptom":"Objetivo: dotar de resiliencia a Elasticsearch con replica shards, nodos master/data dedicados, snapshots de backup (SLM) y replicación entre clústeres (CCR).","steps":[{"title":"Separar roles: nodos master y data dedicados","detail":"Para la estabilidad del clúster, los nodos master-eligible con voto (preferiblemente 3, una mayoría impar) se separan de la carga de datos. Asigne los roles con node.roles en elasticsearch.yml y defina los seed hosts para discovery. 3 masters evitan el split-brain.","engines":{"elasticsearch":"# elasticsearch.yml on master nodes:\n# node.roles: [ master ]\n# on data nodes:\n# node.roles: [ data, ingest ]\n# discovery.seed_hosts: [ m1, m2, m3 ]\n# cluster.initial_master_nodes: [ m1, m2, m3 ]"}},{"title":"Definir el número de réplicas del índice","detail":"number_of_replicas es el número de copias que se mantienen de cada primary shard; debe ser al menos 1 para que los datos sigan disponibles si se pierde un nodo data. Puede cambiarse dinámicamente en un índice en uso; el número de réplicas debe ser menor que el número de nodos data.","engines":{"elasticsearch":"PUT /orders/_settings\n{\n  \"index\": { \"number_of_replicas\": 1 }\n}\n# default via template (composable template; the old _template API is legacy):\n# PUT _index_template/order-tpl { \"index_patterns\": [\"order*\"], \"template\": { \"settings\": { \"number_of_replicas\": 1 } } }"}},{"title":"Definir un repositorio de snapshots","detail":"Los backups se toman en un repositorio: un sistema de archivos compartido (fs) o los plugins de S3/GCS/Azure. Para poder recuperar los datos incluso si se elimina el clúster, el repositorio debe estar en almacenamiento duradero fuera del clúster.","engines":{"elasticsearch":"PUT /_snapshot/backup_repo\n{\n  \"type\": \"fs\",\n  \"settings\": { \"location\": \"/mnt/es_backups\", \"compress\": true }\n}"}},{"title":"Programar snapshots automáticos con SLM","detail":"Snapshot Lifecycle Management (SLM) toma snapshots periódicos y aplica una política de retención. Una expresión cron define la frecuencia; también se definen los índices incluidos y cuántos snapshots se conservan.","engines":{"elasticsearch":"PUT /_slm/policy/daily_backup\n{\n  \"schedule\": \"0 30 1 * * ?\",\n  \"name\": \"<daily-{now/d}>\",\n  \"repository\": \"backup_repo\",\n  \"config\": { \"indices\": [\"*\"] },\n  \"retention\": { \"expire_after\": \"30d\", \"min_count\": 5, \"max_count\": 50 }\n}"}},{"title":"Verificar el snapshot y la restauración","detail":"Ejecute manualmente la política SLM para verificar que funciona; el estado del snapshot debe ser SUCCESS. Realice pruebas de restauración con regularidad, porque un backup no probado no es un backup.","engines":{"elasticsearch":"POST /_slm/policy/daily_backup/_execute\nGET /_snapshot/backup_repo/_all\n# restore:\nPOST /_snapshot/backup_repo/<snapshot_name>/_restore"}},{"title":"Configurar DR remoto con Cross-Cluster Replication (CCR)","detail":"Para la recuperación ante desastres entre regiones, un segundo clúster (follower) sigue los índices del clúster primario remoto (leader). Primero defina el clúster remoto y luego vincule el índice follower al leader; los cambios en el leader se propagan automáticamente. CCR es una funcionalidad Platinum (comercial).","engines":{"elasticsearch":"# define the remote cluster on the follower cluster:\nPUT /_cluster/settings\n{ \"persistent\": { \"cluster\": { \"remote\": { \"primary\": { \"seeds\": [\"leader1:9300\"] } } } } }\n# create the follower index:\nPUT /orders-follower/_ccr/follow\n{ \"remote_cluster\": \"primary\", \"leader_index\": \"orders\" }"}}],"related":["replication-status","list-replicas","last-backup-status"]}]},"playbooks/security.json":{"schemaVersion":1,"playbooks":[{"id":"harden-sql","title":"Hardening de seguridad: bases de datos SQL (Oracle, PostgreSQL, MySQL, SQL Server)","icon":"🔒","symptom":"Objetivo: aplicar hardening estilo CIS a las bases de datos relacionales: cuentas predeterminadas, política de contraseñas robusta, mínimo privilegio, restricción de red, TLS, auditoría y cifrado (TDE).","steps":[{"title":"Eliminar o bloquear cuentas predeterminadas/innecesarias, eliminar esquemas de ejemplo","detail":"Las cuentas predeterminadas y los esquemas de ejemplo que trae la instalación son una superficie de ataque bien conocida. Bloquee o elimine los que no se usan; renombre las cuentas de máximo nivel/de servicio.","engines":{"oracle":"-- Lock unused default accounts and expire their passwords:\nALTER USER SCOTT ACCOUNT LOCK PASSWORD EXPIRE;\nALTER USER HR ACCOUNT LOCK PASSWORD EXPIRE;\n-- Audit locked/open accounts:\nSELECT username, account_status FROM dba_users ORDER BY account_status;\n-- Remove sample schemas: $ORACLE_HOME/demo/schema/drop_sch.sql","postgres":"-- Keep the default superuser 'postgres' role; do NOT drop/rename it.\n-- Audit roles:\nSELECT rolname, rolsuper, rolcanlogin FROM pg_roles ORDER BY rolsuper DESC;\n-- Remove unneeded login roles:\nDROP ROLE IF EXISTS test_user;\n-- Drop sample/test databases: DROP DATABASE IF EXISTS test;","mysql":"# Interactive hardening wizard (removes anonymous users, the test DB, remote root):\nmysql_secure_installation\n-- Manual: remove anonymous and test accounts:\nDROP USER IF EXISTS ''@'localhost';\nDROP USER IF EXISTS ''@'%';\nDROP DATABASE IF EXISTS test;\nDELETE FROM mysql.db WHERE Db LIKE 'test%'; FLUSH PRIVILEGES;","mssql":"-- Disable and rename the 'sa' account:\nALTER LOGIN sa DISABLE;\nALTER LOGIN sa WITH NAME = [sa_disabled_acct];\n-- Restrict the guest user — only in USER databases\n-- (guest must stay enabled in master, tempdb and msdb; disabling it in tempdb/msdb causes problems):\nUSE appdb;\nREVOKE CONNECT FROM guest;\n-- Remove sample DBs: DROP DATABASE AdventureWorks;"}},{"title":"Aplicar una política de contraseñas robusta y su expiración","detail":"Las contraseñas débiles o que nunca cambian son el punto de entrada más común. Imponga límites de intentos de login, complejidad y vigencia de contraseñas; almacene las contraseñas con un hash robusto.","engines":{"oracle":"-- Attempt limit, lifetime and complexity via a profile:\nCREATE PROFILE app_profile LIMIT\n  FAILED_LOGIN_ATTEMPTS 5\n  PASSWORD_LIFE_TIME 90\n  PASSWORD_REUSE_MAX 10\n  PASSWORD_VERIFY_FUNCTION ora12c_verify_function;\nALTER USER app_user PROFILE app_profile;","postgres":"-- Strong hash algorithm (SCRAM):\nSHOW password_encryption;              -- should be 'scram-sha-256'\nALTER SYSTEM SET password_encryption = 'scram-sha-256';\n-- Load the passwordcheck extension for complexity:\n-- shared_preload_libraries = 'passwordcheck'\n-- Lifetime: ALTER ROLE app_user VALID UNTIL '2026-12-31';","mysql":"-- Install the validate_password component and set the policy:\nINSTALL COMPONENT 'file://component_validate_password';\nSET PERSIST validate_password.policy = 'STRONG';\nSET PERSIST validate_password.length = 14;\n-- Attempt limit and lifetime:\nALTER USER app_user PASSWORD EXPIRE INTERVAL 90 DAY\n  FAILED_LOGIN_ATTEMPTS 5 PASSWORD_LOCK_TIME 1;","mssql":"-- Enforce the Windows password policy and expiration on the login:\nALTER LOGIN app_user WITH CHECK_POLICY = ON, CHECK_EXPIRATION = ON;\n-- Audit policy/expiration status:\nSELECT name, is_policy_checked, is_expiration_checked\nFROM sys.sql_logins;"}},{"title":"Mínimo privilegio: revocar privilegios PUBLIC/amplios, otorgar privilegios basados en roles","detail":"Los privilegios amplios otorgados a todos (PUBLIC) facilitan el movimiento lateral. Revóquelos, cree roles y otorgue solo lo necesario.","engines":{"oracle":"-- Revoke dangerous PUBLIC privileges:\nREVOKE EXECUTE ON UTL_HTTP FROM PUBLIC;\nREVOKE EXECUTE ON UTL_FILE FROM PUBLIC;\nREVOKE EXECUTE ON DBMS_SCHEDULER FROM PUBLIC;\n-- Role-based privileges:\nCREATE ROLE app_read;\nGRANT SELECT ON app.orders TO app_read;\nGRANT app_read TO app_user;","postgres":"-- Revoke the default PUBLIC privileges on the schema:\nREVOKE ALL ON DATABASE appdb FROM PUBLIC;\nREVOKE CREATE ON SCHEMA public FROM PUBLIC;\n-- Role-based, read-only access:\nCREATE ROLE app_read NOLOGIN;\nGRANT USAGE ON SCHEMA app TO app_read;\nGRANT SELECT ON ALL TABLES IN SCHEMA app TO app_read;\nGRANT app_read TO app_user;","mysql":"-- Audit and revoke broad global privileges:\nSHOW GRANTS FOR app_user;\nREVOKE ALL PRIVILEGES, GRANT OPTION FROM app_user;\n-- Role-based, object-level privileges (MySQL 8+):\nCREATE ROLE app_read;\nGRANT SELECT ON appdb.* TO app_read;\nGRANT app_read TO app_user;\nSET DEFAULT ROLE app_read TO app_user;","mssql":"-- Revoke excessive privileges granted to the public role:\nREVOKE SELECT ON SCHEMA::dbo FROM public;\n-- Role-based privileges:\nCREATE ROLE app_read;\nGRANT SELECT ON SCHEMA::app TO app_read;\nALTER ROLE app_read ADD MEMBER app_user;\n-- At server level: limit fixed server roles (sysadmin)."}},{"title":"Restringir la red/el acceso: solo pueden conectarse clientes conocidos","detail":"Evite que la base de datos escuche abiertamente a todo el mundo. Restrinja la dirección de escucha, aplique reglas de acceso por host/IP y limite el puerto con un firewall.","engines":{"oracle":"# listener.ora / sqlnet.ora — valid node checking:\nTCP.VALIDNODE_CHECKING = YES\nTCP.INVITED_NODES = (10.0.0.5, 10.0.0.6)\nTCP.EXCLUDED_NODES = (0.0.0.0/0)\n# After the change: lsnrctl reload","postgres":"# postgresql.conf — listen only on specific interfaces:\nlisten_addresses = '10.0.0.10,localhost'\n# pg_hba.conf — host/IP + strong authentication:\nhostssl appdb app_user 10.0.0.0/24 scram-sha-256\nhost    all      all       0.0.0.0/0      reject\n# Reload: SELECT pg_reload_conf();","mysql":"# my.cnf — bind only to a specific interface:\n[mysqld]\nbind-address = 10.0.0.10\n-- Host-based user (only from the application server):\nCREATE USER 'app_user'@'10.0.0.5'\n  IDENTIFIED BY '****' REQUIRE SSL;\n-- Never give remote root the '%' host.","mssql":"# Limit the TCP port with the firewall (application subnet only):\nNew-NetFirewallRule -DisplayName 'SQL-1433' -Direction Inbound `\n  -Protocol TCP -LocalPort 1433 -RemoteAddress 10.0.0.0/24 -Action Allow\n-- Audit listening addresses:\nSELECT * FROM sys.dm_tcp_listener_states;\n-- Disable the SQL Browser service if not used."}},{"title":"Exigir conexiones TLS/cifradas","detail":"El tráfico de red está expuesto a la captura pasiva (sniffing). Configure un certificado de servidor y exija que los clientes se conecten de forma cifrada.","engines":{"oracle":"# sqlnet.ora — TCPS/TLS (server certificate in the wallet):\nWALLET_LOCATION = (SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY=/etc/oracle/wallet)))\nSSL_CLIENT_AUTHENTICATION = FALSE\nSSL_VERSION = 1.2\n# Listen only on TCPS (port 2484) in listener.ora. Note: SQLNET.ENCRYPTION_SERVER is Native Network Encryption, not TLS.","postgres":"# postgresql.conf:\nssl = on\nssl_cert_file = 'server.crt'\nssl_key_file  = 'server.key'\nssl_min_protocol_version = 'TLSv1.2'\n# Use 'hostssl' lines in pg_hba.conf; reject unencrypted connections over plain 'host'.","mysql":"# my.cnf:\n[mysqld]\nrequire_secure_transport = ON\nssl_cert = /etc/mysql/server-cert.pem\nssl_key  = /etc/mysql/server-key.pem\nssl_ca   = /etc/mysql/ca.pem\n-- At user level: ALTER USER app_user REQUIRE SSL;","mssql":"-- Force connection encryption (SQL Server Configuration Manager):\n-- Protocols for MSSQLSERVER > Properties > Force Encryption = Yes\n-- Assign a certificate; then restart the service.\n-- Check the status:\nSELECT session_id, encrypt_option FROM sys.dm_exec_connections;"}},{"title":"Habilitar la auditoría: hacer visible quién hizo qué","detail":"Un registro de auditoría es imprescindible para supervisar el acceso privilegiado y la actividad sospechosa. Registre los logins, los cambios de privilegios y el acceso a objetos sensibles.","engines":{"oracle":"-- Create and enable a policy with Unified Auditing:\nCREATE AUDIT POLICY sec_pol\n  ACTIONS LOGON, LOGOFF, ALTER USER, GRANT, CREATE USER, DROP USER;\nAUDIT POLICY sec_pol;\n-- View the records:\nSELECT * FROM unified_audit_trail ORDER BY event_timestamp DESC;","postgres":"# With the pgaudit extension:\n# postgresql.conf:\nshared_preload_libraries = 'pgaudit'\npgaudit.log = 'ddl, role, write'\n-- Create the extension:\nCREATE EXTENSION pgaudit;\n-- log_connections = on and log_disconnections = on should also be enabled.","mysql":"-- Load the Enterprise/Percona audit_log plugin:\nINSTALL PLUGIN audit_log SONAME 'audit_log.so';\nSET PERSIST audit_log_policy = 'ALL';\n-- MariaDB: server_audit plugin\n-- INSTALL SONAME 'server_audit'; SET GLOBAL server_audit_logging=ON;","mssql":"-- Create and start a Server Audit:\nCREATE SERVER AUDIT sec_audit\n  TO FILE (FILEPATH = 'C:\\SQLAudit\\');\nALTER SERVER AUDIT sec_audit WITH (STATE = ON);\nCREATE SERVER AUDIT SPECIFICATION sec_spec\n  FOR SERVER AUDIT sec_audit\n  ADD (FAILED_LOGIN_GROUP), ADD (DATABASE_ROLE_MEMBER_CHANGE_GROUP)\n  WITH (STATE = ON);"}},{"title":"Aplicar cifrado de datos (en reposo / TDE)","detail":"Si se roba un disco, un backup o un archivo, los datos deben ser ilegibles. Habilite el cifrado de datos en reposo (TDE) a nivel de columna/tablespace/base de datos.","engines":{"oracle":"-- Transparent Data Encryption (keystore + tablespace):\nADMINISTER KEY MANAGEMENT CREATE KEYSTORE '/etc/oracle/wallet'\n  IDENTIFIED BY \"WalletPwd1\";\nADMINISTER KEY MANAGEMENT SET KEY IDENTIFIED BY \"WalletPwd1\" WITH BACKUP;\nCREATE TABLESPACE sec_ts DATAFILE '/u01/sec01.dbf' SIZE 100M\n  ENCRYPTION USING 'AES256' DEFAULT STORAGE(ENCRYPT);","postgres":"-- There is no built-in block-level TDE; two common approaches:\n-- 1) Column-level pgcrypto:\nCREATE EXTENSION pgcrypto;\nINSERT INTO t(ssn) VALUES (pgp_sym_encrypt('123-45', 'my_key'));\nSELECT pgp_sym_decrypt(ssn, 'my_key') FROM t;\n-- 2) File system/block encryption (LUKS) or an EDB/Cybertec TDE build.","mysql":"# InnoDB tablespace encryption — keyring COMPONENT (8.0.24+; the keyring_file plugin was removed in 8.4)\n# 1) Global manifest in the mysqld binary directory: mysqld.my\n{ \"components\": \"file://component_keyring_file\" }\n# 2) In plugin_dir: component_keyring_file.cnf\n{ \"path\": \"/var/lib/mysql-keyring/component_keyring_file\", \"read_only\": false }\n# 3) Restart the server; verify:\n-- SELECT * FROM performance_schema.keyring_component_status;\n-- Encrypt the table:\nALTER TABLE app.orders ENCRYPTION = 'Y';\n-- For new tables: SET PERSIST default_table_encryption = ON;\n# Before 8.0.24 (8.0 only): early-plugin-load = keyring_file.so + keyring_file_data\n# WARNING: back up the keyring file SEPARATELY from the data directory and securely;\n# if the keyring is lost, encrypted tables and their backups become permanently unreadable.","mssql":"-- Transparent Data Encryption (DEK + certificate) — the key and certificate must be in master:\nUSE master;\nCREATE MASTER KEY ENCRYPTION BY PASSWORD = '<StrongPassword1>';\nCREATE CERTIFICATE tde_cert WITH SUBJECT = 'TDE Cert';\n-- MANDATORY: back up the certificate with its private key immediately and store it off the server.\n-- Without this backup, if the server is lost the encrypted DB and its backups CANNOT be restored.\nBACKUP CERTIFICATE tde_cert TO FILE = 'D:\\secure\\tde_cert.cer'\n  WITH PRIVATE KEY (FILE = 'D:\\secure\\tde_cert.pvk',\n    ENCRYPTION BY PASSWORD = '<CertBackupPassword1>');\nUSE appdb;\nCREATE DATABASE ENCRYPTION KEY\n  WITH ALGORITHM = AES_256 ENCRYPTION BY SERVER CERTIFICATE tde_cert;\nALTER DATABASE appdb SET ENCRYPTION ON;\n-- Progress: SELECT DB_NAME(database_id), encryption_state, percent_complete FROM sys.dm_database_encryption_keys;"}},{"title":"Mantener los parches al día y deshabilitar funcionalidades innecesarias","detail":"Las vulnerabilidades conocidas son el riesgo más explotado. Aplique los parches de seguridad con regularidad; deshabilite los protocolos, plugins y funcionalidades no usados (reduzca la superficie de ataque).","engines":{"oracle":"-- Check the installed version and patches:\nSELECT version_full FROM v$instance;\n-- Apply the quarterly Critical Patch Update (CPU): OPatch/opatchauto\n-- Remove unused components (e.g. XML DB, APEX) and\n-- disable the external procedure (extproc) listener.","postgres":"# Check the version and minor patch level:\nSELECT version();\n# Upgrade to the current minor release with the package manager (e.g. latest 16.x).\n# Remove unused extensions: DROP EXTENSION IF EXISTS ...;\n# postgresql.conf: do not load unused language/PL extensions.","mysql":"-- Check the version: SELECT VERSION();\n-- Upgrade to the LTS/security release (8.0.x/8.4 LTS).\n-- Disable local file reading:\nSET PERSIST local_infile = OFF;\n-- Remove unused plugins: UNINSTALL PLUGIN ...;","mssql":"-- Check the product version/patch level:\nSELECT SERVERPROPERTY('ProductVersion'), SERVERPROPERTY('ProductLevel');\n-- Apply the latest Cumulative Update (CU).\n-- Disable unused features (with sp_configure):\nEXEC sp_configure 'xp_cmdshell', 0; RECONFIGURE;\nEXEC sp_configure 'Ole Automation Procedures', 0; RECONFIGURE;"}}],"related":["list-users","create-user-grant","enable-audit","change-password"]},{"id":"harden-nosql","title":"Hardening de seguridad: MongoDB / Redis / Elasticsearch","icon":"🛡️","symptom":"Objetivo: aplicar hardening estilo CIS a los almacenes NoSQL/de datos: autenticación, restricción de red, bloqueo de comandos peligrosos, TLS, roles de mínimo privilegio y auditoría.","steps":[{"title":"HABILITAR la autenticación: cortar el acceso no autenticado","detail":"Estos productos suelen venir con la autenticación DESHABILITADA de forma predeterminada y terminan comprometidos cuando quedan expuestos a internet. Habilite la autenticación y cree un usuario administrador.","engines":{"mongodb":"// mongod.conf:\nsecurity:\n  authorization: enabled\n// Create an administrator user (first time, via the localhost exception):\nuse admin\ndb.createUser({ user: \"admin\", pwd: passwordPrompt(),\n  roles: [ { role: \"userAdminAnyDatabase\", db: \"admin\" } ] })\n// Then connect with: mongosh -u admin --authenticationDatabase admin","redis":"# redis.conf — ACL (Redis 6+): disable the default user, define named users:\nuser default off\nuser admin on >AdminPassword789! ~* &* +@all\nuser app on >AppPassword456! ~app:* +@read +@write\n# Note: requirepass is a legacy compatibility setting that only sets the 'default' user's password;\n# do not confuse it with ACL users. You can also keep users in an aclfile (persisted with ACL SAVE).","elasticsearch":"# 8.x: security (authentication + TLS) is configured automatically on first start; elasticsearch.yml:\nxpack.security.enabled: true\n# Set/reset the password of the 'elastic' user:\nbin/elasticsearch-reset-password -u elastic\n# (elasticsearch-setup-passwords is deprecated since 8.0)\n# Create separate users with dedicated roles for Kibana/clients instead of 'elastic'."}},{"title":"Restringir la red: abrir solo las interfaces y puertos necesarios","detail":"Evite que el servicio escuche a todo el mundo en 0.0.0.0. Fije la dirección de bind a una interfaz interna/loopback y habilite el protected mode.","engines":{"mongodb":"# mongod.conf — internal interface + loopback only:\nnet:\n  bindIp: 127.0.0.1,10.0.0.10\n  port: 27017\n# Never use bindIpAll: true; limit the port with a firewall.","redis":"# redis.conf:\nbind 127.0.0.1 10.0.0.10\nprotected-mode yes\nport 6379\n# protected-mode blocks external access when no password is set;\n# still limit 6379 with a firewall.","elasticsearch":"# elasticsearch.yml — pin the listening interface:\nnetwork.host: 10.0.0.10\nhttp.port: 9200\ntransport.port: 9300\n# Open only the required ports in the firewall;\n# 9300 (transport) must never be exposed externally."}},{"title":"Restringir los comandos peligrosos","detail":"Un solo comando puede borrar todos los datos. Renombre o elimine los comandos destructivos/administrativos, o bloquee el uso de comodines.","engines":{"mongodb":"// Use role-based control instead of a separate command block:\n// Grant destructive privileges (dropDatabase, dropCollection) only to\n// dbAdmin/clusterAdmin roles; do not grant them to the application user.\ndb.grantRolesToUser(\"app\", [ { role: \"readWrite\", db: \"appdb\" } ])\n// Do NOT grant dbAdmin/root to the application user.","redis":"# For Redis 6+ the preferred way is ACLs (do not give the application user dangerous commands):\n# ACL SETUSER app ... -@dangerous\n# Legacy method — disable/rename commands in redis.conf:\nrename-command FLUSHALL \"\"\nrename-command FLUSHDB \"\"\nrename-command KEYS \"\"\nrename-command DEBUG \"\"\n# Caution: renaming CONFIG can break Sentinel, CONFIG REWRITE and management tools.","elasticsearch":"# elasticsearch.yml — block wildcard/all-index deletion:\naction.destructive_requires_name: true\n# This rejects DELETE /* or DELETE /_all;\n# bulk deletion is not possible without naming the index explicitly."}},{"title":"Habilitar conexiones TLS/cifradas","detail":"Las credenciales y los datos no deben viajar sin cifrar por una red abierta. Habilite TLS para el tráfico de clientes y entre nodos.","engines":{"mongodb":"# mongod.conf — client TLS:\nnet:\n  tls:\n    mode: requireTLS\n    certificateKeyFile: /etc/ssl/mongodb.pem\n    CAFile: /etc/ssl/ca.pem\n# Client: mongosh --tls --tlsCAFile ca.pem","redis":"# redis.conf — enable TLS, close the plain port:\nport 0\ntls-port 6379\ntls-cert-file /etc/redis/redis.crt\ntls-key-file  /etc/redis/redis.key\ntls-ca-cert-file /etc/redis/ca.crt","elasticsearch":"# elasticsearch.yml — HTTP and transport TLS:\nxpack.security.http.ssl.enabled: true\nxpack.security.http.ssl.keystore.path: http.p12\nxpack.security.transport.ssl.enabled: true\nxpack.security.transport.ssl.verification_mode: certificate\nxpack.security.transport.ssl.keystore.path: transport.p12"}},{"title":"Otorgar roles de mínimo privilegio: evitar roles amplios","detail":"Dé a cada usuario solo el acceso que su función requiere. En lugar de roles amplios/administrativos, use roles granulares limitados a bases de datos, patrones de claves o índices específicos.","engines":{"mongodb":"// Instead of broad readWrite, grant only on the required DB;\n// define a custom role if needed:\ndb.createRole({ role: \"ordersRW\",\n  privileges: [ { resource: { db: \"appdb\", collection: \"orders\" },\n    actions: [ \"find\", \"insert\", \"update\" ] } ], roles: [] })\ndb.grantRolesToUser(\"app\", [ { role: \"ordersRW\", db: \"appdb\" } ])","redis":"# Limit by key pattern and command category with ACLs:\nACL SETUSER app on >AppPassword456! ~app:* +@read +@write -@dangerous\n# Only keys with the 'app:' prefix, read/write; dangerous commands forbidden.\nACL LIST\nACL WHOAMI","elasticsearch":"# Define a fine-grained role (read-only on specific indices):\nPOST /_security/role/orders_read\n{ \"indices\": [ { \"names\": [ \"orders-*\" ],\n    \"privileges\": [ \"read\" ] } ] }\n# Assign it to the user:\nPOST /_security/user/app\n{ \"password\": \"...\", \"roles\": [ \"orders_read\" ] }"}},{"title":"Habilitar auditoría/logging: supervisar accesos y cambios","detail":"Los intentos de acceso no autorizado y las operaciones administrativas deben quedar registrados. Habilite el audit log y envíelo a una ubicación central.","engines":{"mongodb":"# Auditing (requires MongoDB Enterprise) — mongod.conf:\nauditLog:\n  destination: file\n  format: JSON\n  path: /var/log/mongodb/audit.json\n# Filter: capture events such as authenticate, createUser, dropDatabase.","redis":"# Redis has no full audit; use ACL LOG to see rejected\n# authentication/command attempts:\nACL LOG\nACL LOG RESET\n# Also: route the loglevel notice and logfile settings to a central collector.","elasticsearch":"# elasticsearch.yml — enable security auditing:\nxpack.security.audit.enabled: true\nxpack.security.audit.logfile.events.include:\n  [ authentication_failed, access_denied, connection_denied ]\n# Records are written to the <cluster>_audit.json file."}}],"related":["list-users","create-user-grant","enable-audit","change-password"]}]},"playbooks/cloud.json":{"schemaVersion":1,"playbooks":[{"id":"cloud-managed-db","title":"Bases de datos gestionadas en la nube (RDS/Aurora/Cloud SQL/Atlas/Azure SQL)","icon":"☁️","symptom":"Objetivo: conocer las diferencias clave entre las bases de datos gestionadas en la nube y las instalaciones autogestionadas; entender cómo funcionan en la nube el backup, los parámetros, el failover, el monitoreo, las actualizaciones y la conectividad.","steps":[{"title":"Cambio de mentalidad: accede a un servicio, no a un servidor","detail":"Con las bases de datos gestionadas NO tiene acceso de shell al SO, al sistema de archivos ni al proceso de la base de datos; no puede entrar por SSH, editar archivos de configuración a mano ni convertirse en superusuario. Solo dispone de la API/consola de gestión del proveedor y de una conexión SQL normal. AWS RDS/Aurora: la gestión es a nivel de recurso (instancia/clúster), el servidor está oculto. GCP Cloud SQL: solo API de instancia, sin SO. MongoDB Atlas: clúster totalmente gestionado, sin acceso a mongod. Azure SQL Database: PaaS, no se ve ni el servidor ni el SO subyacente (Managed Instance da algo más de control). Por eso, la mayoría de los reflejos autogestionados como 'mirar el archivo', 'matar el proceso' o 'ejecutar una herramienta del SO' no aplican."},{"title":"Backup: snapshots automáticos + recuperación a un punto en el tiempo (PITR)","detail":"Usted no programa los backups; el proveedor los toma automáticamente. La recuperación suele ser 'restaurar a un punto concreto en el tiempo' (PITR) o crear una nueva instancia a partir de un snapshot; la instancia existente no se sobrescribe. AWS RDS/Aurora: backups automáticos + ventana de retención; una restauración PITR con precisión de segundos se abre como una nueva instancia/clúster; también se pueden tomar snapshots manuales. GCP Cloud SQL: backups automáticos + PITR mediante binary logs; la restauración va a una nueva instancia. MongoDB Atlas: backup continuo en la nube + PITR (M10+); restauración desde snapshots. Azure SQL: backups automáticos integrados, retención de 1-35 días, PITR a cualquier punto y restauración de BD eliminadas. Regla común: verifique la configuración de backup (ventana, período de retención) y realice pruebas de restauración periódicas."},{"title":"Parámetros: parameter groups / flags, no un archivo de configuración","detail":"En lugar de editar postgresql.conf o my.cnf a mano, se usa la abstracción de parámetros del proveedor; algunos parámetros están bloqueados y otros requieren reinicio (estáticos). AWS RDS/Aurora: se definen mediante un DB parameter group (y un cluster parameter group); los parámetros dinámicos se aplican de inmediato, los estáticos al reiniciar. GCP Cloud SQL: los 'database flags' se definen desde la consola/gcloud. MongoDB Atlas: 'Additional Settings' limitados + tamaño del oplog, etc.; la mayoría de los parámetros internos están gestionados. Azure SQL: la mayoría de los ajustes a nivel de servidor están gestionados; el comportamiento se cambia mediante SCOPED CONFIGURATION y la elección del nivel de servicio. Antes de aplicar, compruebe si el parámetro es dinámico o estático."},{"title":"Alta disponibilidad / failover: Multi-AZ y réplicas gestionados","detail":"Usted no construye el failover; marca una casilla y lo deja en manos del proveedor. El standby y el failover automático están gestionados, y las conexiones se redirigen al nuevo primario a través de un endpoint estable. AWS RDS: Multi-AZ (standby síncrono, failover automático); Aurora: almacenamiento con múltiples copias + failover automático, el cluster endpoint apunta al writer. GCP Cloud SQL: configuración de HA (regional, standby en una zona secundaria) con un clic; las réplicas de lectura se agregan por separado. MongoDB Atlas: replica set integrado, despliegue multirregión/multinube disponible, failover automático. Azure SQL: HA integrada (réplicas locales en Business Critical), una opción con redundancia de zona y geo-replicación activa entre regiones / failover groups. La aplicación siempre debe conectarse al endpoint, nunca a una IP fija."},{"title":"Monitoreo: el servicio de métricas y logs del proveedor","detail":"Como no puede ejecutar herramientas del SO (top, iostat), monitoree a través de la plataforma de métricas del proveedor; allí están las métricas de CPU, número de conexiones, IOPS, lag de replicación y almacenamiento. AWS: métricas de CloudWatch + Enhanced Monitoring + Performance Insights (análisis a nivel de consulta). GCP Cloud SQL: Cloud Monitoring (antes Stackdriver) + Query Insights. MongoDB Atlas: métricas/gráficos integrados de Atlas + Performance Advisor + panel en tiempo real. Azure SQL: Azure Monitor + SQL Analytics + Query Performance Insight. Configure siempre alarmas por umbral (uso de almacenamiento, CPU, límite de conexiones, lag de réplica)."},{"title":"Actualizaciones de versión y mantenimiento: desde la consola, en una ventana de mantenimiento","detail":"En lugar de actualizar los binarios a mano, elige una versión de destino; los parches de versión minor son mayormente automáticos, mientras que las actualizaciones de versión major se planifican y suelen implicar tiempo de inactividad o un failover de HA. AWS RDS/Aurora: selección de la versión del motor + opción de actualización minor automática + actualizaciones major de bajo riesgo con Blue/Green Deployments. GCP Cloud SQL: ventana de mantenimiento + actualización de versión major en autoservicio. MongoDB Atlas: actualización de versión con un clic, de forma rolling con tiempo de inactividad casi nulo. Azure SQL: la plataforma aplica los parches automáticamente (el concepto de versión está en gran parte abstraído); el comportamiento se controla con el nivel de compatibilidad. Pruebe siempre primero en una copia de test/staging y programe la ventana de mantenimiento fuera del horario laboral."},{"title":"Conectividad y seguridad: endpoint + IAM/TLS","detail":"Las conexiones siempre pasan por el endpoint DNS proporcionado por el proveedor y van cifradas (TLS); el acceso de red y la identidad pueden vincularse al sistema de identidades de la nube. AWS RDS/Aurora: endpoint de instancia/clúster + Security Group para la red + autenticación de base de datos IAM opcional + TLS (certificado rds-ca). GCP Cloud SQL: IP pública + Authorized Networks o IP privada/VPC, Cloud SQL Auth Proxy, autenticación de base de datos IAM, TLS. MongoDB Atlas: cadena de conexión SRV (mongodb+srv://), lista de acceso IP + private endpoint/peering, SCRAM o IAM/X.509, TLS obligatorio. Azure SQL: endpoint FQDN del servidor + firewall/Private Link + autenticación con Microsoft Entra ID (antes Azure AD) + TLS obligatorio. Regla: use el endpoint, no una IP fija, en la cadena de conexión, exija TLS y conéctese con una identidad IAM de la nube cuando sea posible."}],"related":["last-backup-status","promote-failover","connect-cli"]}]},"playbooks/automation.json":{"schemaVersion":1,"playbooks":[{"id":"auto-ansible","title":"Automatización de bases de datos con Ansible","icon":"🤖","symptom":"Objetivo: hacer que las tareas de instalación, configuración, usuarios y backup de bases de datos sean repetibles e idempotentes (volver a ejecutar el mismo playbook no rompe el sistema) con Ansible.","steps":[{"title":"Definir el inventario y los grupos","detail":"Agrupe los servidores en el inventario por rol de base de datos; coloque los servidores primarios, réplicas y de backup en grupos separados. Use un inventario estático INI/YAML o un inventario dinámico para la nube (aws_ec2, gcp_compute). Coloque las variables de grupo en group_vars/ y las específicas de cada host en host_vars/.","engines":{"postgres":"[pg_primary]\ndb1.example.local\n\n[pg_replica]\ndb2.example.local\n\n[postgres:children]\npg_primary\npg_replica"}},{"title":"Instalar la colección correcta (community.postgresql / community.mysql / community.mongodb)","detail":"Ansible core ahora distribuye los módulos de bases de datos en colecciones separadas; instale la colección de su motor mediante requirements.yml. Use community.postgresql (postgresql_db, postgresql_user, postgresql_privs) para PostgreSQL, community.mysql (mysql_db, mysql_user) para MySQL/MariaDB y community.mongodb para MongoDB. El cliente Python correspondiente (psycopg2, PyMySQL, pymongo) debe estar instalado en el host de destino.","engines":{"postgres":"# requirements.yml\ncollections:\n  - name: community.postgresql\n\n# install:\nansible-galaxy collection install -r requirements.yml","mysql":"collections:\n  - name: community.mysql","mongodb":"collections:\n  - name: community.mongodb"}},{"title":"Cifrar los secretos con Ansible Vault","detail":"No deje en texto plano las contraseñas de la base de datos, los secretos de replicación ni las credenciales de superusuario; guárdelos en un archivo de variables cifrado con ansible-vault. Descifre en tiempo de ejecución con --ask-vault-pass o --vault-password-file. Así los secretos pueden guardarse de forma segura en Git y el playbook sigue siendo idempotente.","engines":{"postgres":"ansible-vault create group_vars/postgres/vault.yml\n# contents:\nvault_pg_admin_password: \"S3cret!\"\n\n# run:\nansible-playbook site.yml --ask-vault-pass"}},{"title":"Configure la estructura de roles (roles/postgres, roles/mysql)","detail":"Cree un rol por motor y divida la lógica en las subcarpetas tasks/, defaults/, templates/ y handlers/. Esta estructura mejora la reutilización: defaults/main.yml para los valores predeterminados, templates/ para las plantillas de configuración jinja2, handlers/ para la lógica de 'reiniciar solo si algo cambió'. Puede generar el esqueleto del rol con ansible-galaxy init roles/postgres.","engines":{"postgres":"ansible-galaxy init roles/postgres\n# roles/postgres/\n#   defaults/main.yml\n#   tasks/main.yml\n#   templates/postgresql.conf.j2\n#   handlers/main.yml"}},{"title":"Escriba tareas de instalación y configuración idempotentes","detail":"Instale el paquete, inicialice el directorio de datos y genere postgresql.conf/my.cnf a partir de una plantilla; reinicie el servicio mediante un handler cuando cambie la configuración. Los módulos son idempotentes: un paquete ya instalado no se reinstala, y el handler no se dispara si el archivo no cambió. Genere la plantilla de configuración con el módulo template y vincúlela al handler con 'notify'.","engines":{"postgres":"- name: postgresql.conf template\n  ansible.builtin.template:\n    src: postgresql.conf.j2\n    dest: /etc/postgresql/16/main/postgresql.conf\n  notify: restart postgresql\n\n# handlers/main.yml\n- name: restart postgresql\n  ansible.builtin.service:\n    name: postgresql\n    state: restarted","mysql":"- name: my.cnf template\n  ansible.builtin.template:\n    src: my.cnf.j2\n    dest: /etc/mysql/my.cnf\n  notify: restart mysql"}},{"title":"Defina las tareas de base de datos y usuarios","detail":"Cree la base de datos, los usuarios y los privilegios con módulos; tome la contraseña de una variable de Vault. Los módulos community.postgresql son idempotentes: si el usuario/la BD existe, no lo recrean, solo aplican la diferencia. Otorgue los privilegios por separado con postgresql_privs; en MySQL se usa el campo priv de mysql_user.","engines":{"postgres":"- name: application database\n  community.postgresql.postgresql_db:\n    name: appdb\n    state: present\n\n- name: application user\n  community.postgresql.postgresql_user:\n    name: app_user\n    password: \"{{ vault_app_password }}\"\n    db: appdb\n    state: present","mysql":"- name: application user\n  community.mysql.mysql_user:\n    name: app_user\n    password: \"{{ vault_app_password }}\"\n    priv: 'appdb.*:ALL'\n    state: present"}},{"title":"Agregue la tarea de backup y su programación","detail":"Despliegue el script de backup desde una plantilla y prográmelo con ansible.builtin.cron; así el job de backup también se gestiona como código. Convierta en variables el directorio destino del backup, el periodo de retención y la programación. El módulo cron es idempotente e identifica la entrada por 'name', por lo que volver a ejecutarlo no la duplica.","engines":{"postgres":"- name: nightly backup (cron)\n  ansible.builtin.cron:\n    name: \"pg dump nightly\"\n    minute: \"0\"\n    hour: \"2\"\n    job: \"pg_dump -Fc appdb > /backup/appdb_$(date +\\\\%F).dump\""}}],"related":["schedule-job","check-version","logical-dump"]},{"id":"auto-terraform","title":"Aprovisionamiento de bases de datos con Terraform (IaC)","icon":"🏗️","symptom":"Objetivo: definir la infraestructura de base de datos (instancia, grupo de parámetros, red) como código (Infrastructure as Code) con Terraform; configurar el flujo plan/apply, el estado remoto y la gestión del drift.","steps":[{"title":"Elija y configure el provider (aws / google / azurerm)","detail":"Defina el provider de la nube en la que trabaja y fije su versión. Fije el provider hashicorp/aws para AWS, hashicorp/google para GCP y hashicorp/azurerm para Azure en el bloque required_providers. Fijar la versión garantiza un comportamiento coherente en todo el equipo.","engines":{"postgres":"terraform {\n  required_providers {\n    aws = {\n      source  = \"hashicorp/aws\"\n      version = \"~> 5.0\"\n    }\n  }\n}\n\nprovider \"aws\" {\n  region = \"eu-central-1\"\n}"}},{"title":"Configure el estado remoto y el bloqueo","detail":"No mantenga el archivo de estado en local; use un backend remoto para el trabajo en equipo y la seguridad. Las opciones habituales son el backend S3 + una tabla de bloqueo DynamoDB en AWS, el backend gcs en GCP y el backend azurerm (Storage Account) en Azure. El estado remoto evita applies simultáneos mediante el bloqueo y convierte el estado en la única fuente de verdad. Como puede haber contraseñas en texto plano en el estado, mantenga el backend cifrado y con acceso restringido.","engines":{"postgres":"terraform {\n  backend \"s3\" {\n    bucket         = \"company-tfstate\"\n    key            = \"db/terraform.tfstate\"\n    region         = \"eu-central-1\"\n    dynamodb_table = \"tf-lock\"\n    encrypt        = true\n  }\n}"}},{"title":"Defina el recurso de base de datos","detail":"Escriba el recurso principal para su motor y su nube: aws_db_instance en AWS (o aws_rds_cluster para Aurora), google_sql_database_instance en GCP, azurerm_mssql_database en Azure (para SQL Server, bajo un azurerm_mssql_server). Especifique aquí el motor, la versión, la clase, el almacenamiento y la ventana de backup.","engines":{"postgres":"resource \"aws_db_instance\" \"pg\" {\n  identifier        = \"app-db\"\n  engine            = \"postgres\"\n  engine_version    = \"16\"\n  instance_class    = \"db.t3.medium\"\n  allocated_storage = 50\n  db_name           = \"appdb\"\n  username          = \"pgadmin\"\n  password          = var.db_password\n  backup_retention_period = 7\n}","mssql":"resource \"azurerm_mssql_database\" \"db\" {\n  name      = \"appdb\"\n  server_id = azurerm_mssql_server.srv.id\n  sku_name  = \"S1\"\n}"}},{"title":"Escriba el grupo de parámetros como código","detail":"Defina la configuración de la base de datos (max_connections, work_mem, juego de caracteres) con un recurso de parámetros, no manualmente en la consola. Use aws_db_parameter_group en AWS, settings.database_flags dentro de google_sql_database_instance en GCP y el recurso de configuración del servidor en Azure. Así la configuración queda versionada y es reproducible.","engines":{"postgres":"resource \"aws_db_parameter_group\" \"pg\" {\n  name   = \"app-pg16\"\n  family = \"postgres16\"\n\n  parameter {\n    name  = \"max_connections\"\n    value = \"200\"\n  }\n}\n# inside aws_db_instance:\n# parameter_group_name = aws_db_parameter_group.pg.name"}},{"title":"Pase los secretos mediante variables y un gestor de secretos","detail":"No escriba la contraseña directamente en un archivo .tf; defínala como variable sensible (variable ... sensitive = true) y proporcione el valor mediante una variable de entorno TF_VAR_, un gestor de secretos (AWS Secrets Manager, Vault) o un secreto de CI. Recuerde: el valor se escribe en el archivo de estado, por lo que el estado remoto debe estar cifrado. El enfoque más limpio es no pasar nunca la contraseña a Terraform y leerla desde un recurso del gestor de secretos.","engines":{"postgres":"variable \"db_password\" {\n  type      = string\n  sensitive = true\n}\n\n# CI/shell:\nexport TF_VAR_db_password=\"$(vault kv get -field=pw secret/db)\""}},{"title":"Ejecute el flujo plan y apply","detail":"Primero prepare el provider/backend con terraform init, vea qué va a cambiar con terraform plan, apruebe y aplique con terraform apply. No aplique sin revisar la salida del plan; el flujo seguro en CI es publicar el 'plan' como comentario en el PR y aplicar tras la aprobación humana. Detecte los cambios destructivos (p. ej., la recreación de la instancia) por el marcador -/+ en la salida del plan.","engines":{"postgres":"terraform init\nterraform plan -out=plan.tfplan\nterraform apply plan.tfplan"}},{"title":"Detecte y corrija el drift","detail":"Si alguien hace un cambio manual en la consola, la infraestructura real y el estado divergen (drift). Las diferencias que aparecen en terraform plan 'cuando nada debería haber cambiado' revelan el drift; la diferencia se lee con terraform refresh/plan. Refleje el cambio de vuelta en el código o devuelva la infraestructura al estado del código con apply. Prohíba los cambios manuales como norma; el código debe ser la única fuente de verdad.","engines":{"postgres":"# drift check (when no changes are expected):\nterraform plan\n# expect 'No changes'; any diff means drift\n# bring an existing resource into state with import:\nterraform import aws_db_instance.pg app-db"}}],"related":["check-version","apply-patch","schedule-job"]},{"id":"auto-k8s-operator","title":"Bases de datos en Kubernetes (operator)","icon":"☸️","symptom":"Objetivo: ejecutar la base de datos en Kubernetes con un operator; en lugar de un StatefulSet a secas, usar el failover automático, el almacenamiento persistente y las funciones de backup que ofrece el operator.","steps":[{"title":"Por qué un operator: un StatefulSet por sí solo no basta","detail":"Un StatefulSet simple le da pods con identidades estables y PVC, pero no la lógica de failover, configuración de la replicación, backup y actualizaciones. Un operator empaqueta este conocimiento operativo específico de la base de datos como un controller + un recurso personalizado (CRD). Usted escribe un CR llamado 'Cluster' y el operator gestiona por detrás el StatefulSet, el Service, el Secret y los jobs de backup. En producción, en general se prefiere ejecutar las bases de datos con un operator.","engines":{"postgres":"# operators for PostgreSQL:\n#  - CloudNativePG (CNPG)\n#  - Zalando postgres-operator\n#  - Crunchy PGO"}},{"title":"Elija e instale el operator","detail":"Elija un operator para su motor: CloudNativePG o Zalando postgres-operator para PostgreSQL; los operators de Percona (Percona Operator for MySQL / for MongoDB) son habituales para MySQL y MongoDB. Instale el operator con Helm o con un manifiesto bundle; este paso agrega al clúster los CRD y el pod del controller.","engines":{"postgres":"# CloudNativePG installation\nkubectl apply --server-side -f \\\n  https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/releases/cnpg-1.24.0.yaml","mysql":"# Percona Operator for MySQL (Helm)\nhelm install my-op percona/pxc-operator","mongodb":"# Percona Operator for MongoDB (Helm)\nhelm install psmdb-op percona/psmdb-operator"}},{"title":"Planifique el almacenamiento (StorageClass / PVC)","detail":"El estado de la base de datos reside en los PVC; especifique en el CR la StorageClass que se usará. Elija una StorageClass que ofrezca almacenamiento en bloque de baja latencia, idealmente SSD de tipo local; los sistemas de archivos de red (NFS) no se recomiendan para la mayoría de las bases de datos. Defina correctamente desde el principio el tamaño del PVC y su capacidad de expansión (allowVolumeExpansion), porque reducirlo es difícil.","engines":{"postgres":"spec:\n  storage:\n    storageClass: fast-ssd\n    size: 50Gi"}},{"title":"Aplique el CR Cluster (réplicas y failover automático)","detail":"Especifique en el CR el número de instancias/réplicas; el operator configura la replicación síncrona/asíncrona y, si el primario falla, realiza un failover automático promoviendo una réplica. La aplicación debe conectarse al Service '-rw' (lectura-escritura) que crea el operator; este servicio siempre apunta al primario actual. Por eso el destino de conexión no cambia durante el failover.","engines":{"postgres":"apiVersion: postgresql.cnpg.io/v1\nkind: Cluster\nmetadata:\n  name: app-db\nspec:\n  instances: 3\n  storage:\n    storageClass: fast-ssd\n    size: 50Gi\n# the application connects to: app-db-rw"}},{"title":"Escriba los backups en S3 mediante el CR","detail":"Los operators también gestionan los backups de forma declarativa: usted define en el CR el destino principal del backup (almacenamiento de objetos/S3) y el archivado de WAL. En CloudNativePG, los datos de S3 se indican con spec.backup.barmanObjectStore (obsoleto desde 1.26; para nuevas implementaciones use el Barman Cloud Plugin y su recurso ObjectStore); el archivado continuo de WAL + los backups base permiten la recuperación a un punto en el tiempo. Haga referencia a las credenciales desde un Secret de Kubernetes.","engines":{"postgres":"spec:\n  backup:\n    barmanObjectStore:\n      destinationPath: s3://company-backup/app-db\n      s3Credentials:\n        accessKeyId:\n          name: s3-creds\n          key: ACCESS_KEY_ID\n        secretAccessKey:\n          name: s3-creds\n          key: SECRET_ACCESS_KEY"}},{"title":"CR de backup programado y de restauración","detail":"Cree un CR Backup para un backup puntual y un CR ScheduledBackup para los programados; realice backups base periódicos con una expresión cron. Las restauraciones también son declarativas: se inicializa un nuevo Cluster a partir de un backup existente en el almacenamiento de objetos con 'bootstrap.recovery'. Realice simulacros de restauración con regularidad; hacer backups no basta, pruebe la recuperación.","engines":{"postgres":"apiVersion: postgresql.cnpg.io/v1\nkind: ScheduledBackup\nmetadata:\n  name: nightly-backup\nspec:\n  schedule: \"0 0 2 * * *\"\n  cluster:\n    name: app-db"}}],"related":["setup-cdc-replication","logical-dump","restore-dump"]},{"id":"auto-schema-migration","title":"CI/CD de migraciones de esquema (Flyway/Liquibase)","icon":"🧬","symptom":"Objetivo: gestionar los cambios de esquema de la base de datos con archivos de migración versionados; aplicarlos de forma automática pero controlada dentro de CI/CD con Flyway o Liquibase.","steps":[{"title":"Cree archivos de migración versionados (Vxxx__name.sql)","detail":"Escriba cada cambio de esquema en un archivo con nombre y un número de versión creciente; la convención de Flyway es V<version>__<description>.sql (p. ej., V1__initial_schemas.sql, V2__orders_table.sql). Estos archivos se guardan en Git y se aplican en orden de versión. Las migraciones aplicadas se registran en una tabla de seguimiento (flyway_schema_history en Flyway), de modo que cada migración se ejecuta exactamente una vez.","engines":{"postgres":"-- V2__orders_table.sql\nCREATE TABLE orders (\n  id       bigserial PRIMARY KEY,\n  amount    numeric(12,2) NOT NULL,\n  created_at   timestamptz DEFAULT now()\n);"}},{"title":"Preserve la inmutabilidad con checksums","detail":"Flyway/Liquibase almacenan el checksum de cada migración aplicada. Si después edita un archivo ya aplicado, el checksum deja de coincidir y la herramienta falla. Regla: NUNCA edite una migración aplicada; haga la corrección con un nuevo archivo de versión. Esto garantiza que todos los entornos (dev/staging/prod) compartan el mismo historial.","engines":{"postgres":"# repair on checksum mismatch (use with care):\nflyway repair\n# the right way: add the change in a new Vx file"}},{"title":"Establezca una baseline en una base de datos existente","detail":"Use baseline al conectar la herramienta de migración a una base de datos ya poblada y sin historial; toma el estado actual como 'versión inicial' y no intenta recrear los objetos existentes. Se aplican las migraciones posteriores a baselineVersion. En un entorno creado desde cero no se necesita baseline; las migraciones se ejecutan desde V1 en adelante.","engines":{"postgres":"flyway baseline -baselineVersion=1 -baselineDescription=\"existing schema\""}},{"title":"Migre automáticamente en el pipeline de CI","detail":"Ejecute la herramienta de migración en un paso de CI para aplicar los cambios automáticamente en las bases de datos de test y staging; el comando migrate ejecuta solo los archivos nuevos (no aplicados) y es idempotente. Aplicar primero la migración en una base de datos temporal/efímera y validarla detecta el SQL defectuoso antes de que llegue a prod. El esquema debe migrarse ANTES de desplegar el código de la aplicación (con cambios compatibles con versiones anteriores).","engines":{"postgres":"# CI step (Flyway)\nflyway -url=jdbc:postgresql://staging/appdb \\\n  -user=ci -password=$PW migrate","mysql":"flyway -url=jdbc:mysql://staging/appdb \\\n  -user=ci -password=$PW migrate"}},{"title":"Agregue una puerta de aprobación manual para prod","detail":"Test/staging son automáticos, pero haga que el migrate en prod requiera aprobación manual; agregue un paso de 'approval gate' (aprobación manual) en CI/CD. Antes de la aprobación, muestre qué migraciones se ejecutarán con 'flyway info' o 'liquibase status'. Haga siempre un backup reciente antes de aplicar en prod para poder revertir si algo sale mal.","engines":{"postgres":"# what will run before approval?\nflyway info\n# liquibase equivalent:\nliquibase status --verbose"}},{"title":"Defina la reversibilidad y una estrategia de rollback","detail":"Flyway Community no tiene undo automático; adopte el principio de migraciones solo hacia adelante (forward-only): en lugar de deshacer un error, escriba una nueva migración que lo corrija. Los changesets de Liquibase pueden definir un bloque rollback y revertirse con 'liquibase rollback'. La estrategia más sólida: mantener los cambios pequeños y compatibles con versiones anteriores, hacer un backup antes de las operaciones de riesgo (eliminar columnas) y preparar un plan de rollback por escrito.","engines":{"postgres":"-- Liquibase rollback example (inside a changeset)\n--changeset dev:2\nCREATE TABLE orders (...);\n--rollback DROP TABLE orders;"}}],"related":["apply-patch","logical-dump","check-version"]},{"id":"auto-backup","title":"Backups automatizados (pgBackRest/Barman/Xtrabackup/cron)","icon":"💾","symptom":"Objetivo: automatizar los backups de la base de datos; construir una cadena de recuperación fiable con archivado programado completo+incremental+WAL/binlog, retención, verificación y una copia fuera del sitio.","steps":[{"title":"Elija la herramienta de backup adecuada","detail":"Elija una herramienta para su motor: pgBackRest o Barman para PostgreSQL (backup físico + archivo de WAL + PITR); Percona XtraBackup (backup físico en línea) para MySQL/MariaDB, o mysqldump/mydumper (lógico) para conjuntos de datos pequeños. Las herramientas físicas ofrecen backups rápidos y consistentes + recuperación a un punto en el tiempo en bases de datos grandes; los dumps lógicos son portables pero lentos con datos grandes.","engines":{"postgres":"# pgBackRest or Barman","mysql":"# physical: xtrabackup\n# logical: mysqldump / mydumper"}},{"title":"Construya la cadena completo + incremental + archivado continuo","detail":"Configure la recuperación a un punto en el tiempo con backups completos semanales/diarios, backups incrementales/diferenciales entre ellos y archivado continuo de WAL (PostgreSQL) o binlog (MySQL). pgBackRest gestiona los tipos de backup full/diff/incremental y el archivo de WAL; en PostgreSQL se apunta archive_command a pgBackRest. Los backups incrementales + el archivo permiten volver a cualquier punto en el tiempo.","engines":{"postgres":"# postgresql.conf\narchive_mode = on\narchive_command = 'pgbackrest --stanza=appdb archive-push %p'\n\n# full backup\npgbackrest --stanza=appdb --type=full backup\n# incremental\npgbackrest --stanza=appdb --type=incr backup","mysql":"# full\nxtrabackup --backup --target-dir=/backup/full\n# incremental\nxtrabackup --backup --target-dir=/backup/incr1 \\\n  --incremental-basedir=/backup/full"}},{"title":"Programe los backups con cron/timer","detail":"Ejecute los comandos de backup con regularidad mediante cron o un timer de systemd: por ejemplo, un backup completo el domingo por la noche e incrementales las demás noches. Prográmelos fuera del horario laboral y asegúrese de que los jobs full e incr no se solapen. Escriba los logs de salida en algún lugar y genere una alerta en caso de fallo.","engines":{"postgres":"# crontab\n0 1 * * 0 pgbackrest --stanza=appdb --type=full backup\n0 1 * * 1-6 pgbackrest --stanza=appdb --type=incr backup"}},{"title":"Defina una política de retención","detail":"No acumule backups indefinidamente; defina cuántos backups completos y cuántos días de archivos conservar, y deje que la herramienta expire automáticamente los antiguos. En pgBackRest se gestiona con repo1-retention-full y la configuración de retención del archivo; en Barman con retention_policy. La retención debe ajustarse a sus objetivos de recuperación (cuántos días atrás necesita poder volver).","engines":{"postgres":"# pgbackrest.conf\n[global]\nrepo1-retention-full=4\nrepo1-retention-archive=8","mysql":"# purge old backups with cron (example)\nfind /backup -type d -mtime +14 -exec rm -rf {} +"}},{"title":"Copie los backups fuera del sitio / a S3","detail":"Los backups no deben quedarse en la misma máquina/región que la base de datos; cópielos a otra ubicación o a un almacenamiento de objetos (S3, GCS). pgBackRest puede escribir directamente en almacenamiento compatible con S3; de lo contrario, sincronice con un destino fuera del sitio mediante rsync/rclone. Ante un desastre (pérdida del disco local/de la región), solo la copia fuera del sitio lo salvará.","engines":{"postgres":"# pgbackrest.conf S3 repository\n[global]\nrepo1-type=s3\nrepo1-s3-bucket=company-backup\nrepo1-s3-region=eu-central-1\nrepo1-s3-endpoint=s3.eu-central-1.amazonaws.com"}},{"title":"Verifique el backup","detail":"Que exista un backup no significa que pueda restaurarse; verifique la integridad con regularidad. En pgBackRest, el comando 'verify' y las comprobaciones de consistencia del archivo, y en XtraBackup un paso prepare (--prepare) exitoso, demuestran que el backup es válido. Ejecute la verificación automáticamente después de cada backup y genere una alarma en caso de fallo.","engines":{"postgres":"pgbackrest --stanza=appdb verify","mysql":"# make the backup consistent (verification)\nxtrabackup --prepare --target-dir=/backup/full"}},{"title":"Realice simulacros de restauración periódicos","detail":"El paso más crítico: restaure REALMENTE el backup de forma periódica en un servidor aparte y verifique que la base de datos abre y que los datos son consistentes. Este simulacro le permite medir su tiempo de recuperación (RTO) y detectar fallas en el procedimiento fuera de un incidente. Un backup no probado no es un backup.","engines":{"postgres":"# restore + PITR on a separate server\npgbackrest --stanza=appdb \\\n  --type=time --target=\"2026-07-01 03:00:00\" restore","mysql":"# copy the prepared backup back to the data directory\nxtrabackup --copy-back --target-dir=/backup/full"}}],"related":["logical-dump","restore-dump","schedule-job"]},{"id":"auto-gitops-bluegreen","title":"GitOps y despliegue blue-green / sin tiempo de inactividad","icon":"🟦","symptom":"Objetivo: desplegar cambios de esquema y de aplicación sin tiempo de inactividad; acercarse a cero tiempo de inactividad con migraciones expand-contract, entornos blue-green, feature flags y rollback automático.","steps":[{"title":"GitOps: haga de Git la única fuente de verdad","detail":"Defina en Git la infraestructura, las migraciones de esquema y la versión de la aplicación; el despliegue se desencadena con un merge en el repositorio (GitOps). Un reconciliador (Argo CD/Flux o CI/CD) aplica el estado deseado al entorno real. Así cada cambio es auditable, repetible y puede revertirse a un commit anterior cuando sea necesario.","engines":{"postgres":"# repo/\n#   migrations/  (Vxxx__*.sql)\n#   k8s/         (Deployment, Service)\n#   flags/       (feature flag config)"}},{"title":"Cambie el esquema de forma compatible con versiones anteriores (expand-contract)","detail":"Para lograr cero tiempo de inactividad, no cambie el esquema en un único paso que rompa la compatibilidad; divídalo en dos fases. EXPAND: agregue la nueva estructura (nueva columna/tabla) de modo que el código antiguo y el nuevo puedan ejecutarse a la vez (columna nullable, con valor predeterminado, escrituras dobles). DESPUÉS de que la aplicación haya pasado a la nueva estructura, CONTRACT: elimine la columna/tabla antigua que ya no se usa en una migración aparte. Así las versiones antigua y nueva de la aplicación funcionan en paralelo sin problemas durante el despliegue.","engines":{"postgres":"-- EXPAND (V5): backward-compatible addition\nALTER TABLE customer ADD COLUMN email text;\n\n-- for a while the code writes/reads both columns\n\n-- CONTRACT (V9): drop the old column\nALTER TABLE customer DROP COLUMN mail_old;"}},{"title":"Ponga el comportamiento de riesgo detrás de un feature flag","detail":"Ejecute la nueva ruta de código (como leer la nueva columna) de forma condicional detrás de un feature flag; active el flag cuando el esquema esté listo y desactívelo sin tocar el despliegue si algo sale mal. Un flag separa el despliegue del código (deploy) de la activación de la funcionalidad (release). La fase expand se despliega, pero el cambio de comportamiento se habilita de forma controlada y puede revertirse rápidamente.","engines":{"postgres":"-- application layer (pseudocode)\n-- if flag('new_email_field'):\n--     SELECT email FROM customer ...\n-- else:\n--     SELECT mail_old FROM customer ..."}},{"title":"Configure el entorno blue-green","detail":"Mantenga dos entornos idénticos: 'blue' (en producción) y 'green' (nueva versión). Despliegue la nueva versión de la aplicación en green y valídela antes de que reciba tráfico real; cuando esté lista, cambie el router/el tráfico a green. Punto clave: la base de datos suele ser COMPARTIDA, por lo que el esquema debe ser compatible con ambas versiones (expand-contract lo garantiza). Aunque el destino de conexión cambie en el cutover, la compatibilidad del esquema evita el tiempo de inactividad.","engines":{"postgres":"# traffic is switched to green (new version)\n# shared DB: schema must be compatible with both blue and green"}},{"title":"Valide el despliegue con monitoreo (canary/observación)","detail":"Antes de cambiar todo el tráfico, monitoree métricas como la tasa de errores, la latencia, los recuentos de errores/bloqueos de la base de datos y el retraso de replicación; opcionalmente envíe un pequeño porcentaje del tráfico (canary) a green y observe. Si los indicadores de salud son buenos, aumente el tráfico gradualmente; si son malos, no continúe. El monitoreo es la entrada para la decisión de rollback automático.","engines":{"postgres":"-- live health queries\nSELECT count(*) FROM pg_stat_activity WHERE wait_event_type='Lock';\nSELECT * FROM pg_stat_replication;"}},{"title":"Prepare el rollback automático","detail":"Si las métricas superan el umbral, el despliegue debe revertirse automáticamente: en blue-green, devuelva rápidamente el tráfico a blue (la versión antigua). El rollback de la aplicación es instantáneo porque blue sigue en ejecución. En el lado del esquema, el rollback normalmente NO ES NECESARIO: gracias a expand-contract, el nuevo esquema también es compatible con el código antiguo, así que basta con revertir la aplicación. Por eso los cambios de esquema deben mantenerse independientes del despliegue de la aplicación y ser siempre compatibles con versiones anteriores.","engines":{"postgres":"# traffic instantly returns to blue (application rollback)\n# schema is not rolled back: expand-contract is compatible with old code\n# if truly needed: write a new corrective migration"}}],"related":["setup-cdc-replication","apply-patch","check-version"]}]},"install/index.json":{"schemaVersion":1,"categories":[{"id":"install","title":"Instalación"},{"id":"ha","title":"Alta disponibilidad & replicación"},{"id":"backup","title":"Backup & recuperación"},{"id":"ops","title":"Operación diaria & incidencias"},{"id":"pooling","title":"Conexiones & balanceo de carga"},{"id":"patch","title":"Parches & actualizaciones menores"},{"id":"upgrade","title":"Upgrade mayor & migración"},{"id":"monitoring","title":"Monitorización & gestión"},{"id":"security","title":"Seguridad & hardening"}],"guides":[{"id":"pg-install","file":"install/guides/pg-install.json","group":"PostgreSQL","engineId":"postgres","category":"install","title":"Instalación de PostgreSQL en un solo servidor (lista para producción)","summary":"Instalación de PostgreSQL lista para producción en un solo servidor desde el repositorio PGDG: directorio de datos separado, data checksums, parámetros calculados según RAM/CPU, acceso seguro (peer + scram-sha-256), regi","level":"Principiante","duration":"30–45 min","topology":"1 servidor","tags":["postgresql","postgres","instalación","install","pgdg","initdb","tuning","rhel","ubuntu"],"order":1,"steps":23,"scenarios":[]},{"id":"pg-streaming","file":"install/guides/pg-streaming.json","group":"PostgreSQL","engineId":"postgres","category":"ha","title":"Streaming replication de PostgreSQL (primary + standby)","summary":"Añade uno o varios hot standby a un primary ya instalado con pg_basebackup (usuario de replicación, pg_hba, replication slot, modo asíncrono o síncrono ANY/FIRST, supervisión del retraso) y genera los runbooks de operaci","level":"Intermedio","duration":"45–90 min","topology":"1 primary + 1..N standby","tags":["postgresql","streaming replication","replicación","standby","hot standby","pg_basebackup","replication slot","synchronous","replicación síncrona","failover","switchover","pg_promote","pg_rewind","ha","cascading","delayed standby","recovery_min_apply_delay","limpieza de slots","pg_wal lleno","synchronous_standby_names","reconstruir standby"],"order":2,"steps":24,"scenarios":[{"k":"mode","v":"install","l":"Instalación: añadir standby al primary","m":"Añadir / reconstruir standby configurar streaming replication añadir standby hot standby pg_basebackup read replica réplica de lectura physical replication replicación física standby.signal primary_conninfo Se añaden uno o varios hot standby a un primary ya instalado con pg_basebackup (slot, síncrono/asíncrono, supervisión). El primary sigue abierto; la copia inicial genera carga de disco y de red"},{"k":"mode","v":"switchover","l":"Switchover planificado (cambio de rol, sin pérdida de datos)","m":"Cambio de rol switchover planned switchover cambio de rol switchover planificado mantenimiento del primary convertir standby en primary pg_promote pg_ctl promote Mantenimiento planificado: el primary se detiene de forma limpia, se promueve el primer standby y el antiguo primary vuelve como standby. Sin pérdida de datos; la interrupción de escrituras suele durar menos de un minuto."},{"k":"mode","v":"failover","l":"Failover manual (el primary ha caído, promover el standby)","m":"Cambio de rol could not connect to the primary server Connection refused primary down primary caído primary inaccesible failover manual failover promote standby pg_promote split-brain could not receive data from WAL stream servidor primary perdido El primary ha caído o no es accesible: se aísla el antiguo primary (fencing) y se promueve a mano el standby más actualizado. En modo asíncrono pueden p"},{"k":"mode","v":"rejoin","l":"Reincorporar el antiguo primary como standby con pg_rewind","m":"Cambio de rol requested timeline is not a child of this server's history target server must be shut down cleanly could not find previous WAL record pg_rewind diverged timeline reincorporar antiguo primary rejoin old primary Tras un failover, el antiguo primary se resincroniza con el nuevo primary mediante pg_rewind y se reincorpora como standby; es mucho más rápido que una copia completa."},{"k":"mode","v":"cascade","l":"Añadir un nuevo standby alimentado por un standby (cascada)","m":"Añadir / reconstruir standby cascading replication cascade standby replicación en cascada standby desde standby centro de datos remoto DR standby El nuevo standby recibe el WAL del primer standby en lugar del primary: en un centro de datos remoto o en una topología con muchos standby se reduce la carga de red y de envío de WAL del primary. El primary no se ve afectado."},{"k":"mode","v":"delayed","l":"Configurar un standby con retraso (delayed standby)","m":"Añadir / reconstruir standby recovery_min_apply_delay delayed standby standby con retraso borrado por error deshacer DROP TABLE accidental delete pg_wal_replay_pause Standby que aplica el WAL horas después (`recovery_min_apply_delay`): sirve para recuperar datos borrados por error sin restaurar desde backup. No es candidato a failover."},{"k":"mode","v":"rebuild","l":"Reconstruir un standby desde cero","m":"Añadir / reconstruir standby requested WAL segment has already been removed could not receive data from WAL stream database system identifier differs wal_status lost exceeds max_slot_wal_keep_size reconstruir standby standby dañado re-clone standby could not start WAL streaming wal_removed standby retrasado El standby se ha quedado atrás, su WAL se ha eliminado o está dañado: se aparta el director"},{"k":"mode","v":"slotclean","l":"pg_wal crece, el disco se llena (limpieza de slots sin uso)","m":"Mantenimiento y solución de problemas No space left on device could not write to file \"pg_wal pg_wal lleno pg_wal crece acumulación de WAL disco lleno replication slot inactive max_slot_wal_keep_size directorio pg_wal grande WAL disk full Se localiza la causa de la acumulación de WAL (slot inactivo, fallo de archivado, wal_keep_size); se elimina el replication slot que ya no se usa y se recupera e"},{"k":"mode","v":"syncchg","l":"Añadir / quitar un standby síncrono (synchronous_standby_names)","m":"Mantenimiento y solución de problemas canceling wait for synchronous replication synchronous_standby_names COMMIT en espera commit hangs sync standby standby síncrono sync_state potential synchronous_commit Reescribe la lista síncrona según el modo del formulario (ANY/FIRST, k) o vuelve a modo asíncrono; se aplica con reload, sin restart. Un valor incorrecto puede dejar los commits en espera; por "}]},{"id":"pg-patroni","file":"install/guides/pg-patroni.json","group":"PostgreSQL","engineId":"postgres","category":"ha","title":"Clúster con failover automático con Patroni + etcd + HAProxy","summary":"Instala y opera un clúster PostgreSQL con failover automático: en cada nodo Patroni gestiona PostgreSQL, la elección del leader se realiza en un etcd de 3 (o 5) miembros y las aplicaciones se conectan a los puertos de es","level":"Avanzado","duration":"2–3 horas","topology":"3 nodos PostgreSQL + etcd (compartidos o separados) + HAProxy (+ VIP)","tags":["postgresql","patroni","etcd","haproxy","keepalived","vip","failover automático","auto failover","switchover","ha","high availability","cluster","clúster","patronictl","dcs","reinit","pg_rewind","edit-config","pause","resume","etcdctl member","quorum","failsafe","añadir nodo","quitar nodo","patroni upgrade","parada del clúster"],"order":3,"steps":40,"scenarios":[{"k":"mode","v":"install","l":"Instalación: nuevo clúster Patroni","m":"Instalación instalación de patroni patroni install instalación de etcd haproxy failover automático automatic failover high availability alta disponibilidad clúster HA Se instala desde cero un clúster etcd + Patroni + HAProxy (+ VIP). En los servidores no debe existir ningún clúster PostgreSQL: initdb lo ejecuta Patroni (no se aplica pg-install)."},{"k":"mode","v":"ops","l":"Runbook de operación: switchover, prueba de failover, modo de mantenimiento, cambio de parámetros","m":"Comprobación y simulacro prueba de failover failover test simulacro DR drill runbook Prueba en orden las operaciones básicas en un clúster recién instalado: estado, switchover, prueba de failover (se detiene el leader, breve corte de escritura), modo de mantenimiento y cambio de parámetros. Aplíquelo en una ventana de mantenimiento."},{"k":"mode","v":"health","l":"¿Está sano el clúster? (comprobación de estado)","m":"Comprobación y simulacro patronictl list Pending restart Maintenance mode: on start failed patroni health estado del clúster replica lag retardo de réplica timeline diferente Solo lectura: roles, retardo, timeline, API REST, estado de etcd, slots, reinicios pendientes y modo de mantenimiento."},{"k":"mode","v":"switchover","l":"Switchover planificado (pasar el leader a otro nodo)","m":"Cambio de rol candidate name does not match with sync_standby Switchover failed does not exist in cluster or is tagged as nofailover switchover cambiar leader cambio de rol mantenimiento planificado Antes de un mantenimiento, el rol de leader pasa de forma controlada a una réplica; corte de escritura de unos segundos, sin pérdida de datos."},{"k":"mode","v":"rejoin","l":"Hubo failover y el antiguo leader no se reincorpora (pg_rewind / reinit)","m":"Cambio de rol requested timeline is not a child of this server's history could not find previous WAL record pg_rewind: error target server needs to use either data checksums or wal_log_hints start failed diverged antiguo leader después del failover pg_rewind: fatal could not find common ancestor timeline history mismatch El antiguo leader caído debe incorporarse como réplica al arrancar; si pg_rew"},{"k":"mode","v":"reinit","l":"Reconstruir una réplica desde cero (reinit)","m":"Mantenimiento de miembros requested WAL segment has already been removed could not receive data from WAL stream invalid record length invalid page patronictl reinit réplica dañada réplica retrasada rebuild replica replica start failed creating replica La réplica está dañada, muy retrasada o ha perdido WAL: se borra el directorio de datos y se reconstruye desde el leader (o desde un backup). El lea"},{"k":"mode","v":"addmember","l":"Añadir un nuevo nodo al clúster","m":"Mantenimiento de miembros añadir nodo add member nueva réplica scale out ampliar clúster Añada el nuevo nodo al final de la lista del formulario: se instala el software, se actualizan pg_hba y HAProxy en los nodos existentes y se incorpora como réplica."},{"k":"mode","v":"rmmember","l":"Quitar un nodo del clúster","m":"Mantenimiento de miembros quitar nodo remove member decommission retirar servidor eliminar réplica Se retira de forma permanente un nodo réplica: se detiene Patroni, se limpian el registro en el DCS y el slot, y se elimina de HAProxy."},{"k":"mode","v":"editcfg","l":"Cambiar un parámetro de PostgreSQL o de Patroni (edit-config)","m":"Configuración y mantenimiento edit-config Pending restart se perdió un parámetro de postgresql.conf ALTER SYSTEM cambiar parámetro max_connections shared_buffers change parameter work_mem parámetro perdido el parámetro no se aplica El parámetro se escribe en el DCS y se aplica en todos los nodos; para los que requieren reinicio, `restart --pending` (primero las réplicas)."},{"k":"mode","v":"pause","l":"Detener el failover automático para mantenimiento (pause / resume)","m":"Configuración y mantenimiento Maintenance mode: on patronictl pause patronictl resume maintenance mode modo de mantenimiento desactivar failover automático En pause, Patroni no toca PostgreSQL ni hace failover; para mantenimiento de red/etcd o intervención manual. Al terminar, resume."},{"k":"mode","v":"upgrade","l":"Actualizar el software de Patroni (rolling)","m":"Configuración y mantenimiento patroni upgrade actualización de patroni versión de patroni dnf update patroni apt upgrade patroni patroni 3 to 4 El clúster se pone en pause y Patroni se actualiza y reinicia nodo a nodo; PostgreSQL no se detiene y no hay failover."},{"k":"mode","v":"stopstart","l":"Detener / arrancar todo el clúster (orden correcto)","m":"Configuración y mantenimiento detener clúster arrancar clúster shutdown order parada completa parada planificada mantenimiento del centro de datos corte eléctrico Parada completa planificada (mantenimiento de almacenamiento o del centro de datos): primero las réplicas, después el leader y por último etcd; el arranque, en orden inverso."},{"k":"mode","v":"etcdmember","l":"Sustituir o añadir un miembro de etcd (etcdctl member)","m":"etcd (DCS) etcdserver: unhealthy cluster member has already been bootstrapped Peer URLs already exists member count is unequal etcdctl member etcd member add etcd member remove miembro de etcd unhealthy Se retira el miembro de etcd averiado y se añade un nuevo servidor (o se aumenta el número de miembros); se actualiza la lista de etcd de Patroni."},{"k":"mode","v":"dcsloss","l":"Se perdió la mayoría de etcd: el leader pasó a solo lectura","m":"etcd (DCS) Failed to get list of machines DCS is not accessible demoting self etcdserver: request timed out etcdserver: no leader cannot execute INSERT in a read-only transaction quorum failsafe etcd quorum lost sin leader no leader Sin quorum de etcd, Patroni pone el leader en solo lectura y tampoco hay failover. Se recupera etcd o se reconstruye a partir de un único miembro."}]},{"id":"pg-pgbouncer","file":"install/guides/pg-pgbouncer.json","group":"PostgreSQL","engineId":"postgres","category":"pooling","title":"Pool de conexiones PgBouncer","summary":"Instalación de PgBouncer desde el repositorio PGDG: modo de pool transaction o session, autenticación scram-sha-256 mediante auth_query (función SECURITY DEFINER) o userlist.txt, tamaños de pool calculados según max_conn","level":"Intermedio","duration":"30–45 min","topology":"PgBouncer en el servidor de base de datos o en un servidor aparte; con Patroni, delante de HAProxy/VIP","tags":["postgresql","pgbouncer","connection pool","pool de conexiones","pooling","transaction pooling","auth_query","userlist","scram-sha-256","so_reuseport","max_client_conn","default_pool_size","patroni","haproxy","vip","failover"],"order":4,"steps":15,"scenarios":[]},{"id":"pg-pgbackrest","file":"install/guides/pg-pgbackrest.json","group":"PostgreSQL","engineId":"postgres","category":"backup","title":"Copias de seguridad y PITR con pgBackRest","summary":"Copias completas / diferenciales / incrementales con pgBackRest, archivado continuo de WAL y recuperación a un punto en el tiempo (PITR). Para un servidor único, streaming replication (primary + standbys) o un cluster de","level":"Intermedio","duration":"45–90 min","topology":"1 servidor, primary + standbys o un cluster de Patroni; servidor de copias opcional","tags":["postgresql","pgbackrest","copia de seguridad","backup","restore","pitr","wal","archive","s3","minio","cifrado","retention","recuperación","patroni","streaming","standby","cron","backup-standby","systemd timer","delta restore","db-include","target-action","restore point","xid","lsn","clone","clonación","desastre","disaster recovery","verify","expire","type=standby","simulacro"],"order":10,"steps":28,"scenarios":[{"k":"purpose","v":"kur","l":"Instalación de pgBackRest + primera copia + restauración de prueba","m":"Instalación stanza-create archive_command archive-push instalación de pgbackrest pgbackrest install configurar copias de seguridad backup setup plan de copias full diff incr copia programada ERROR: [087] archive_mode must be enabled configurar archivado de WAL primera copia pgBackRest aún no está implantado: software, repositorio, archivado de WAL, primera copia, programación y una restauración de"},{"k":"purpose","v":"geri","l":"Desastre: restauración in situ (al último estado o a un punto en el tiempo)","m":"Restauración (mismo servidor) recovery_target_time recovery_target_xid recovery_target_lsn pg_create_restore_point PITR point in time recovery restaurar a un punto en el tiempo borrado por error DROP TABLE DELETE without WHERE UPDATE without WHERE TRUNCATE restore point restore to time recuperar datos perdidos Devuelva la base de datos, in situ, a un momento cubierto por la copia y el archivo: el "},{"k":"purpose","v":"delta","l":"Archivos dañados o borrados en el directorio de datos (reparar con delta restore)","m":"Restauración (mismo servidor) invalid page in block could not read block page verification failed checksum verification failed could not open file \"base/ could not access status of transaction missing chunk number No such file or directory could not read file bloque dañado data corruption block corruption archivo de datos borrado ERROR:  could not read block PANIC:  could not open file PANIC:  cou"},{"k":"purpose","v":"dbinc","l":"Recuperar una sola base de datos (--db-include)","m":"Restauración (mismo servidor) db-include DROP DATABASE una sola base de datos single database restore base de datos eliminada recuperar una tabla table restore restaurar una sola tabla El cluster sigue en servicio: la base de datos se restaura en una instancia temporal (directorio y puerto aparte), se extrae con pg_dump y se carga en el servidor en producción con un nombre nuevo. Esta tarjeta sirv"},{"k":"purpose","v":"clone","l":"Restaurar la copia en otro servidor (clon / pruebas / informes)","m":"Servidor nuevo: clon, desastre, standby repo1-cipher-pass clone clonación entorno de pruebas refrescar el entorno de pruebas refresh test staging restaurar en un servidor nuevo servidor de informes repositorio cifrado No se toca el servidor en producción. La copia se abre en un servidor nuevo; el archivado de WAL se desactiva para que el clon no escriba en el repositorio de producción. Con un repo"},{"k":"purpose","v":"dr","l":"El servidor se ha perdido por completo: abrir la copia en un servidor nuevo y sustituirlo","m":"Servidor nuevo: clon, desastre, standby PANIC: could not locate a valid checkpoint record desastre disaster recovery DR pérdida del servidor fallo de disco server lost directorio de datos perdido nuevo primary repositorio cifrado could not open file \"global/pg_control\" el servidor no arranca PostgreSQL no arranca Pérdida de disco o de servidor: el servidor nuevo se abre desde la copia y pasa a ser"},{"k":"purpose","v":"standby","l":"Crear un nuevo standby desde la copia (--type=standby)","m":"Servidor nuevo: clon, desastre, standby type=standby crear un standby añadir una réplica standby desde copia standby rebuild en lugar de pg_basebackup add standby Un nuevo standby sin cargar al primary con la copia de datos: los archivos vienen de la copia y el WAL que falta se completa desde el archivo y por streaming. El primary sigue en servicio."},{"k":"purpose","v":"verify","l":"¿Las copias están realmente sanas? (info / check / verify y simulacro de restauración)","m":"Comprobaciones y mantenimiento ERROR: [082] ERROR: [028] ERROR: [056] pgbackrest verify pgbackrest check pgbackrest info verificación de copias backup verification restore test simulacro cuándo fue la última copia verify command la copia es válida restore drill verificación mensual Comprobaciones de solo lectura y un simulacro de restauración en un directorio aparte que no toca el servidor en prod"},{"k":"purpose","v":"retention","l":"Cambiar el periodo de retención y eliminar copias antiguas (expire)","m":"Comprobaciones y mantenimiento No space left on device WARN: option 'repo1-retention-full' is not set repo1-retention-full expire retention retención repositorio lleno disco de copias lleno borrar copias antiguas repo lleno bucket lleno se llenó el disco de copias Cambia los parámetros repo1-retention-full/diff, muestra primero con --dry-run qué se eliminaría y después ejecuta expire. Una copia el"}]},{"id":"pg-pitr-basebackup","file":"install/guides/pg-pitr-basebackup.json","group":"PostgreSQL","engineId":"postgres","category":"backup","title":"Copia de seguridad y PITR con pg_basebackup + archivo de WAL (sin pgBackRest)","summary":"Copia física con las herramientas propias de PostgreSQL, sin software adicional: archivado continuo de WAL con archive_command, base backup nocturno con pg_basebackup y retención con pg_archivecleanup. Recuperación a un ","level":"Intermedio","duration":"45–90 min","topology":"1 servidor; con streaming o Patroni, el directorio de archivo es compartido por todos los nodos (NFS)","tags":["postgresql","pg_basebackup","archive_command","restore_command","pitr","point in time recovery","recovery.signal","recovery_target_time","pg_archivecleanup","archivo de wal","timeline","base backup","copia de seguridad","restauración","pg_verifybackup","cron","simulacro"],"order":12,"steps":14,"scenarios":[{"k":"op","v":"kur","l":"Configurar el archivado de WAL y el base backup nocturno","m":"Instalación archive_mode archive_command archive_timeout pg_stat_archiver wal_level Archivo de WAL WAL archiving continuous archiving archivado continuo pg_basebackup cron base backup copia nocturna nightly backup configurar archivado de wal Instalación inicial: archivado continuo de WAL con archive_command, script de pg_basebackup, programación en /etc/cron.d, retención y limpieza de WAL. archive"},{"k":"op","v":"pitr","l":"Volver a un punto concreto (PITR: tiempo, xid, LSN, restore point)","m":"Restauración recovery ended before configured recovery target was reached requested recovery stop point is before consistent recovery point PITR point in time recovery point in time recovery.signal restore_command recovery_target_time recovery_target_xid recovery_target_lsn recovery_target_name pg_create_restore_point DROP TABLE TRUNCATE borrado por error accidental delete recuperación a un punto "},{"k":"op","v":"drill","l":"Simulacro de restauración (en un directorio separado, sin tocar producción)","m":"Comprobaciones y mantenimiento backup successfully verified pg_verifybackup pg_amcheck restore test restore drill simulacro verificación de copias backup verify RTO la copia se restaura Para demostrar que la copia se restaura de verdad (se recomienda una vez al mes): se verifica el último base backup (pg_verifybackup), se extrae en un directorio separado y se arranca con el archivo en un puerto di"},{"k":"op","v":"cleanup","l":"Limpiar los base backups antiguos y el archivo de WAL","m":"Comprobaciones y mantenimiento No space left on device could not write to file archive command failed pg_archivecleanup disco de archivo lleno archivo lleno disk full borrar copias antiguas retención de copias retention pg_wal crece archive_status .ready El disco de archivo o de copias se está llenando, o ha cambiado la retención: se borran los base backups antiguos y pg_archivecleanup elimina el "}]},{"id":"pg-logical-repl","file":"install/guides/pg-logical-repl.json","group":"PostgreSQL","engineId":"postgres","category":"ha","title":"Logical replication de PostgreSQL (publication / subscription)","summary":"Logical replication a nivel de tabla entre dos clusters PostgreSQL: wal_level=logical, comprobación de replica identity, publication y subscription, sincronización inicial, monitorización, gestión de conflictos y sincron","level":"Avanzado","duration":"45–90 min (+ tiempo de la copia inicial)","topology":"2 servidores: publicador (publisher) + suscriptor (subscriber); el publicador puede ser un solo servidor, streaming replication o un cluster Patroni","tags":["postgresql","logical replication","replicación lógica","publication","subscription","cdc","migración de datos","migration","upgrade","conflict","replica identity","sequence","patroni","failover slot","permanent slots","sync_replication_slots"],"order":15,"steps":25,"scenarios":[{"k":"purpose","v":"sync","l":"Replicación continua (informes, integración, copia parcial)","m":"logical replication publication subscription CREATE SUBSCRIPTION replicación de tablas replicación parcial Las tablas seleccionadas fluyen de forma continua a otro cluster (servidor de informes, alimentación de un data warehouse, integración entre servicios). El suscriptor sigue admitiendo escrituras."},{"k":"purpose","v":"migrate","l":"Migración: nuevo servidor / nueva versión principal + cutover","m":"upgrade sin interrupción near zero downtime cutover migración de servidor logical upgrade Paso a un nuevo servidor o a una nueva versión principal con segundos de interrupción: copia inicial y sincronización continua, después una breve parada de escrituras y el cutover."}]},{"id":"pg-dump-restore","file":"install/guides/pg-dump-restore.json","group":"PostgreSQL","engineId":"postgres","category":"backup","title":"Copia de seguridad lógica y restauración de PostgreSQL (pg_dump / pg_restore)","summary":"Copia de seguridad lógica y restauración con pg_dump, pg_dumpall y pg_restore a nivel de tabla, esquema, base de datos o cluster completo; solo estructura (DDL) o solo datos, traslado a otro servidor/versión, restauració","level":"Intermedio","duration":"20–90 min (según el tamaño de los datos)","topology":"1 servidor, streaming replication (primary + standby) o un cluster Patroni; en un traslado, origen + destino; en copias remotas programadas, además un servidor de copias","tags":["postgresql","postgres","pg_dump","pg_restore","pg_dumpall","copia de seguridad lógica","logical backup","exportar","importar","dump","restore","restaurar dump","migración de base de datos","migration","upgrade","globals","systemd timer","cron","pgpass","copia de seguridad","patroni","streaming replication","réplica","standby","haproxy","hot standby","pg_wrapper","cron.d","flock"],"order":15,"steps":9,"scenarios":[{"k":"mode","v":"tables","l":"Exportar / importar una o varias tablas","m":"Exportar / importar pg_dump -t exportar tabla importar tabla table export copia de una sola tabla no matching tables were found Se vuelcan la estructura y los datos de las tablas seleccionadas (incluidos índices, constraints y triggers) y se cargan en otra base de datos."},{"k":"mode","v":"schema","l":"Exportar / importar un esquema","m":"Exportar / importar pg_dump -n exportar esquema schema export no matching schemas were found Se vuelcan todos los objetos del esquema (tablas, vistas, funciones, secuencias)."},{"k":"mode","v":"db","l":"Copia / restauración de una sola base de datos","m":"Exportar / importar pg_dump pg_restore copia de base de datos database backup database restore errors ignored on restore Una base de datos completa (estructura + datos) se vuelca en un único dump y se restaura en la misma o en otra base de datos. Los roles (globals) no se incluyen."},{"k":"mode","v":"cluster","l":"Cluster completo (roles + tablespaces + cada base de datos)","m":"Exportar / importar pg_dumpall pg_dumpall -g globals copia del cluster completo copia de roles Globals con pg_dumpall -g y un pg_dump separado por cada base de datos; la restauración se ensaya en un cluster de prueba temporal."},{"k":"mode","v":"ddl","l":"Solo la estructura del esquema (DDL)","m":"Estructura y datos por separado pg_dump -s --schema-only DDL export solo estructura Definiciones de tablas, índices, vistas y funciones sin datos; para revisión, control de versiones o para montar un entorno vacío."},{"k":"mode","v":"data","l":"Solo datos","m":"Estructura y datos por separado pg_dump -a --data-only solo datos --disable-triggers circular foreign-key constraints Se transfieren solo las filas (COPY) cuando la estructura ya existe en el destino."},{"k":"mode","v":"migrate","l":"Trasladar a otro servidor / versión","m":"Traslado y recuperación migración de servidor migration dump restore upgrade aborting because of server version mismatch migrar a nueva versión En el servidor de destino, el pg_dump de la nueva versión se conecta al origen por la red; los datos se transfieren mediante un pipe o un archivo."},{"k":"mode","v":"partial","l":"Restaurar una sola tabla / objeto desde un dump","m":"Traslado y recuperación restaurar una tabla pg_restore -L pg_restore --list DROP TABLE tabla eliminada filas borradas borrado por error table restore A partir de un dump custom/directory existente se restaura solo una tabla, con sus índices, constraints y datos, mediante un archivo de lista (-L)."},{"k":"mode","v":"sched","l":"Copia programada (systemd timer / cron)","m":"Copia programada pg_dump cron systemd timer copia automática scheduled backup copia diaria retención de copias Copia diaria de globals + bases de datos, comprobación de legibilidad, retención, log y notificación de errores."}]},{"id":"pg-minor-update","file":"install/guides/pg-minor-update.json","group":"PostgreSQL","engineId":"postgres","category":"patch","title":"Actualización de versión menor de PostgreSQL (patch)","summary":"Actualización menor dentro de la misma versión principal (por ejemplo, 17.4 → 17.x): revisión de las release notes, copia de seguridad y conservación de los RPM/deb actuales para la marcha atrás. En un solo servidor, con","level":"Intermedio","duration":"20–60 min","topology":"1 servidor, primary + standby o un cluster Patroni","tags":["postgresql","minor","patch","actualización","update","upgrade","rolling","switchover","dnf","apt","downgrade","rollback","extension","patroni","patronictl","failover"],"order":20,"steps":17,"scenarios":[{"k":"purpose","v":"upd","l":"Actualización menor","m":"minor update actualización de versión menor patch CVE actualización de seguridad dnf upgrade postgresql apt upgrade postgresql Paso a una versión menor más reciente dentro de la misma versión principal (por ejemplo, 17.4 → 17.6): correcciones de seguridad y de errores. Interrupción breve en un solo servidor; rolling en un cluster."},{"k":"purpose","v":"rb","l":"Marcha atrás: volver a la versión menor anterior (downgrade)","m":"downgrade marcha atrás rollback volver a la versión anterior undefined symbol Ha aparecido un error o una regresión en la nueva versión menor: se vuelve atrás con los RPM/deb guardados de la versión anterior. El directorio de datos no cambia."}]},{"id":"pg-major-upgrade","file":"install/guides/pg-major-upgrade.json","group":"PostgreSQL","engineId":"postgres","category":"upgrade","title":"Actualización de versión mayor de PostgreSQL (pg_upgrade / logical replication)","summary":"Actualización de versión mayor (p. ej., 16 → 17): elección del método (pg_upgrade --link / --copy / --clone o migración casi sin interrupción con logical replication), pg_upgrade --check, compatibilidad de las extensione","level":"Avanzado","duration":"1–3 horas (+ según el tamaño de los datos)","topology":"In situ en el mismo servidor (pg_upgrade; servidor único, primary + standby o clúster Patroni) o hacia un servidor nuevo (logical)","tags":["postgresql","major","upgrade","actualización de versión","pg_upgrade","pg_upgradecluster","link","clone","logical replication","migración","migration","rsync","rollback","patroni","patronictl remove","réplica","streaming replication"],"order":30,"steps":24,"scenarios":[{"k":"purpose","v":"upg","l":"Major upgrade","m":"actualización mayor pg_upgrade pg_upgradecluster actualización de versión versión mayor actualizar postgresql 17 actualizar postgresql 18 Actualización de versión mayor (p. ej., 16 → 17): comprobaciones previas, copia de seguridad, pg_upgrade o logical replication, verificación y limpieza."},{"k":"purpose","v":"rb","l":"Reversión (upgrade fallido / abandonado)","m":"revertir upgrade rollback pg_control.old database files are incompatible with server upgrade fallido El upgrade quedó a medias o la nueva versión da problemas: vuelta a la versión anterior. Con --link, si el nuevo clúster ya se ha iniciado, solo se puede volver desde la copia de seguridad."}]},{"id":"pg-security","file":"install/guides/pg-security.json","group":"PostgreSQL","engineId":"postgres","category":"security","title":"Bastionado de seguridad de PostgreSQL (auditoría de estado, red, TLS, autenticación, privilegios, auditoría, cifrado)","summary":"No es una guía de instalación: encuentra y cierra las brechas de seguridad de un servidor PostgreSQL en funcionamiento. Primero la Auditoría de estado, de solo lectura, enumera las brechas por gravedad e indica a qué tar","level":"Intermedio","duration":"5 min (auditoría) – 3 horas (bastionado completo)","topology":"Un servidor PostgreSQL + servidor de aplicación (verificación del cliente); los entornos con réplicas/Patroni se tratan en las notas","tags":["postgresql","postgres","seguridad","security","hardening","bastionado","cis benchmark","pg_hba","tls","ssl","sslmode","verify-full","scram-sha-256","md5","pgaudit","audit","auditoría","public schema","privilegios","rol","luks","pgcrypto","cifrado","encryption","firewall"],"order":40,"steps":3,"scenarios":[{"k":"scenario","v":"scan","l":"Auditoría de estado: ¿qué brechas tengo? (solo lectura)","m":"Punto de partida auditoría de seguridad security audit hardening check CIS brecha de seguridad vulnerabilidad vulnerability hallazgo de pentest informe de auditoría consulta de auditoría control CIS análisis de seguridad security scan El primer análisis que se ejecuta: pg_hba, dirección de escucha, TLS, contraseñas md5, superusuarios, privilegios de PUBLIC, auditoría, permisos de archivos y firewa"},{"k":"scenario","v":"all","l":"Bastionado completo (todas las áreas en orden)","m":"Punto de partida hardening bastionado seguridad completa full hardening aplicar CIS benchmark STIG Servidor recién instalado o nunca bastionado: todas las áreas se aplican en un orden seguro; PostgreSQL se reinicia una o dos veces (se necesita una ventana de mantenimiento)."},{"k":"scenario","v":"net","l":"Red y conexiones: ¿quién puede conectarse y desde dónde? (listen, firewall, pg_hba)","m":"Red e identidad pg_hba.conf 0.0.0.0/0 trust listen_addresses firewall no pg_hba.conf entry pg_hba.conf rejects connection puerto 5432 abierto FATAL: no pg_hba.conf entry for host 28000 Connection refused listen_addresses = * host all all 0.0.0.0/0 acceso externo restringir acceso PostgreSQL escucha en todas las interfaces, pg_hba tiene 0.0.0.0/0 o trust, el puerto está abierto a todos. Cambiar lis"},{"k":"scenario","v":"tls","l":"Cifrar las conexiones (TLS, sslmode=verify-full)","m":"Red e identidad ssl tls sslmode verify-full hostssl certificate verify failed SSL off no encryption server.crt certificado caducado certificate expired SSL error: certificate verify failed does not match host name root certificate file does not exist server does not support SSL, but SSL was required certificado autofirmado cadena de certificados certificate chain hostnossl ssl = off o los clientes"},{"k":"scenario","v":"auth","l":"Autenticación y contraseñas (scram-sha-256, acceso remoto de postgres)","m":"Red e identidad md5 scram-sha-256 password_encryption password authentication failed authentication method 10 not supported acceso remoto de postgres política de contraseñas password policy 28P01 FATAL: password authentication failed for user SCRAM authentication requires libpq version 10 or above pass-the-hash VALID UNTIL caducidad de contraseña Método md5/password, contraseñas con hash md5, el u"},{"k":"scenario","v":"priv","l":"Mínimo privilegio: roles, PUBLIC y la cuenta de la aplicación","m":"Privilegios y trazabilidad public schema PUBLIC GRANT REVOKE superuser least privilege mínimo privilegio permission denied for schema diseño de roles DEFAULT PRIVILEGES 42501 permission denied for table must be owner of table pg_read_server_files pg_execute_server_program SECURITY DEFINER REASSIGN OWNED informe de privilegios La cuenta de la aplicación es propietaria de las tablas o es superusuari"},{"k":"scenario","v":"audit","l":"Auditoría: ¿quién hizo qué? (pgaudit, log centralizado)","m":"Privilegios y trazabilidad pgaudit audit pista de auditoría audit log AUDIT: SESSION log_connections syslog SIEM RGPD PCI shared_preload_libraries could not access file \"pgaudit\" pgaudit must be loaded via shared_preload_libraries log_statement quién hizo qué who changed El auditor o el equipo de seguridad exige un registro de los cambios de DDL y de roles; instalación de pgaudit y envío del log a"},{"k":"scenario","v":"tde","l":"Cifrar los datos en disco (LUKS / pgcrypto)","m":"Datos y copias de seguridad TDE transparent data encryption cifrado de disco encryption at rest LUKS dm-crypt pgcrypto pg_tde cryptsetup luksFormat pgp_sym_encrypt at-rest encryption Se requiere protección frente al robo de un disco o de una copia de seguridad. PostgreSQL no tiene TDE integrado: LUKS a nivel de disco (con interrupción del servicio para el traslado) o pgcrypto a nivel de columna."},{"k":"scenario","v":"backup","l":"Seguridad de las copias de seguridad (copias cifradas, permisos)","m":"Datos y copias de seguridad cifrado de copias de seguridad backup encryption repo-cipher-type pgbackrest cipher gpg .pgpass permisos de copias de seguridad cipher: none repo-cipher-pass copia de seguridad sin cifrar unencrypted backup Los archivos de copia de seguridad no están cifrados o están en un directorio legible por todos; cifrado del repositorio de pgBackRest o pg_dump + GPG, .pgpass y per"},{"k":"scenario","v":"os","l":"Lado del sistema operativo (cuenta de servicio, permisos, core dump, SELinux)","m":"Servidor y versión SELinux permissive chmod 700 data directory has invalid permissions core dump cuenta postgres sudo avc: denied LimitCORE core dumped passwd -l La cuenta postgres tiene una contraseña utilizable, los permisos del directorio de datos son laxos, SELinux está en Permissive/Disabled. El ajuste de core dump se aplica en el siguiente reinicio."},{"k":"scenario","v":"patch","l":"Parches y versión: ¿estoy en una versión con soporte?","m":"Servidor y versión EOL end of life fin de soporte versión menor CVE parche de seguridad security release out of support server_version calendario de parches minor update pgsql-announce Comprueba la versión, la fecha de fin de soporte y las actualizaciones menores pendientes; remite a la guía de parches para aplicarlas. Solo lectura."}]},{"id":"mysql-install","file":"install/guides/mysql-install.json","group":"MySQL","engineId":"mysql","category":"install","title":"Instalación de MySQL en un servidor (lista para producción)","summary":"Instala desde cero, desde el repositorio oficial y lista para producción, una única instancia de MySQL 8.4 LTS (u 8.0): directorios separados para datos, binlog y logs, un my.cnf calculado según la RAM, la CPU y el tipo ","level":"Principiante","duration":"30–45 min","topology":"1 servidor","tags":["mysql","instalación","install","8.4","lts","innodb","my.cnf","tuning","gtid","binlog","rhel","ubuntu"],"order":1,"steps":24,"scenarios":[]},{"id":"mysql-replication","file":"install/guides/mysql-replication.json","group":"MySQL","engineId":"mysql","category":"ha","title":"Replicación GTID de MySQL (async / semi-sync)","summary":"Configura una replicación basada en GTID con 1 source + N replicas a partir de servidores MySQL ya instalados: usuario de replicación, traslado de los datos existentes con el clone plugin o mysqldump, SOURCE_AUTO_POSITIO","level":"Intermedio","duration":"45–90 min","topology":"1 source + 1..5 replicas","tags":["mysql","replicación","replication","gtid","semi-sync","semisync","replica","source","clone","failover","read_only"],"order":2,"steps":21,"scenarios":[]},{"id":"mysql-innodb-cluster","file":"install/guides/mysql-innodb-cluster.json","group":"MySQL","engineId":"mysql","category":"ha","title":"MySQL InnoDB Cluster (Group Replication + MySQL Router)","summary":"Monta un InnoDB Cluster single-primary a partir de 3 (o 5–9) servidores MySQL ya instalados en el mismo centro de datos: dba.configureInstance, createCluster y addInstance con clone mediante MySQL Shell AdminAPI; bootstr","level":"Avanzado","duration":"90–120 min","topology":"3–9 nodos MySQL + 1 servidor de Router","tags":["mysql","innodb cluster","group replication","mysql shell","mysqlsh","adminapi","mysql router","ha","failover","quorum","clone","single-primary"],"order":3,"steps":39,"scenarios":[]},{"id":"mysql-xtrabackup","file":"install/guides/mysql-xtrabackup.json","group":"MySQL","engineId":"mysql","category":"backup","title":"Copia de seguridad, restauración y PITR con Percona XtraBackup","summary":"Copia de seguridad física con Percona XtraBackup: instalación de la versión compatible de XtraBackup, usuario de backup con auth_socket, script de backup full + incremental comprimido/cifrado, programación con un systemd","level":"Intermedio","duration":"45–90 min","topology":"1 servidor (independiente, source, replica o secondary de InnoDB Cluster); almacenamiento de backup en disco local o NFS","tags":["mysql","xtrabackup","percona","copia de seguridad","backup","restore","incremental","pitr","binlog","recuperación","zstd","cifrado","replica","slave-info","safe-slave-backup","innodb cluster","cron","systemd timer","flock"],"order":4,"steps":26,"scenarios":[{"k":"scenario","v":"setup","l":"Configurar los backups, programarlos y probar una restauración (flujo completo)","m":"Instalación y prueba instalación de xtrabackup script de backup backup script systemd timer cron prueba de restauración backup con xtrabackup backup físico physical backup hot backup incremental backup full backup percona-xtrabackup Instalación de XtraBackup, usuario de backup, script de backup full + incremental, programación y una prueba de restauración (PITR incluido). Elija esta opción si lo c"},{"k":"scenario","v":"r_full","l":"Restauración completa: volver al último backup full","m":"Restauración restore restaurar copy-back datadir perdido InnoDB: corruption MY-011971 MY-012592 Database page corruption mysqld no arranca full restore ERROR 2002 Can't connect to local MySQL server through socket MY-012574 Unable to lock ./ibdata1 datadir borrado fallo de disco disk failure pérdida de datos El directorio de datos se ha perdido/dañado o el servidor no arranca; basta con volver al "},{"k":"scenario","v":"r_inc","l":"Restauración desde una cadena full + incremental (hasta el último incremental)","m":"Restauración incremental restore apply-log-only incremental-dir to_lsn from_lsn This incremental backup seems not to be proper for the target cadena incremental incremental chain xtrabackup_checkpoints applying incremental Quiere volver al momento del último backup incremental (o de un incremental que elija); los incrementales se aplican en orden sobre el full con `--apply-log-only`. MySQL permane"},{"k":"scenario","v":"r_pitr","l":"Recuperar a un punto en el tiempo (PITR): antes de un DELETE / DROP erróneo","m":"Punto en el tiempo PITR point in time borrado por error DROP TABLE DELETE erróneo TRUNCATE mysqlbinlog stop-datetime stop-position exclude-gtids binlog replay UPDATE erróneo DELETE sin WHERE accidental delete accidental drop DROP DATABASE deshacer undo delete recuperación con binlog Datos borrados o dañados por error: se restaura el último backup y se aplican los binlogs hasta justo antes de la tr"},{"k":"scenario","v":"r_table","l":"Restaurar una sola tabla (--export + IMPORT TABLESPACE)","m":"Punto en el tiempo una sola tabla single table restore IMPORT TABLESPACE DISCARD TABLESPACE transportable tablespace --export .cfg ERROR 1808 ERROR 1812 Schema mismatch ERROR 1813 Tablespace exists ERROR 1146 Table doesn't exist tablespace is missing restaurar una tabla table restore archivo ibd Solo necesita la versión de una tabla que hay en el backup; sin restaurar todo el servidor, la tabla se"},{"k":"scenario","v":"r_replica","l":"Crear una nueva replica desde un backup","m":"Migración y clonación nueva replica configurar replica xtrabackup_binlog_info xtrabackup_slave_info gtid_purged slave-info replication from backup 1236 crear replica desde backup seed replica Could not find first log file name in binary log index file purged required binary logs 1236 reconstruir replica Quiere preparar una replica nueva (o reconstruida) a partir de un backup de XtraBackup existent"},{"k":"scenario","v":"r_other","l":"Restaurar el backup en otro servidor (migración, pruebas, clonación)","m":"Migración y clonación otro servidor servidor de pruebas restore test migración migration clon clone to test rsync server_uuid cargar en entorno de pruebas verificación de backup restore drill abrir backup migración de servidor Quiere abrir un backup de producción en un servidor de pruebas/desarrollo, trasladar un servidor o comprobar en un servidor aparte que el backup realmente se restaura. El se"}]},{"id":"mysql-upgrade","file":"install/guides/mysql-upgrade.json","group":"MySQL","engineId":"mysql","category":"upgrade","title":"Upgrade de MySQL 8.0 → 8.4 LTS (in-place)","summary":"Actualiza un MySQL 8.0 instalado a 8.4 LTS en el mismo servidor (in-place): servidor único, source + replicas (primero las replicas, el source al final) o InnoDB Cluster (Router → Shell → metadata → secondaries de uno en","level":"Intermedio","duration":"60–120 min (según el tamaño de los datos)","topology":"1 servidor, source + replicas o InnoDB Cluster (3+ miembros + servidores de MySQL Router)","tags":["mysql","upgrade","8.0","8.4","lts","actualización","checkForServerUpgrade","mysql_native_password","authentication_policy","rollback","vuelta atrás","innodb cluster","group replication","mysql router","upgradeMetadata","setPrimaryInstance","rolling upgrade"],"order":5,"steps":18,"scenarios":[]},{"id":"mysql-repl-ops","file":"install/guides/mysql-repl-ops.json","group":"MySQL","engineId":"mysql","category":"ops","title":"Operación diaria de la replicación MySQL y escenarios de fallo (8.0 / 8.4)","summary":"Runbook de operación diaria y recuperación ante fallos para una topología source + replica ya instalada (async / semi-sync, GTID o posición de binlog): comprobación de estado y retraso, switchover planificado, failover, ","level":"Avanzado","duration":"5 min (comprobación) – 2 horas (reconstrucción)","topology":"1 source + 1..5 replicas (async / semi-sync)","tags":["mysql","replicación","replication","gtid","errant gtid","errant transaction","switchover","failover","replica","source","super_read_only","semi-sync","show replica status","seconds_behind_source","retraso de replicación","lag","1062","1032","1236","duplicate entry","can't find record","gtid_next","sql_replica_skip_counter","source_delay","replica retrasada","delayed replica","clone","xtrabackup","mysqldump","purge binary logs","binlog_expire_logs_seconds","wait_for_executed_gtid_set","change replication source to","runbook","8.0","8.4"],"order":12,"steps":5,"scenarios":[{"k":"scenario","v":"health","l":"Comprobación de estado y retraso (¿la replicación está sana?)","m":"Monitorización y comprobaciones SHOW REPLICA STATUS SHOW SLAVE STATUS Seconds_Behind_Source Seconds_Behind_Master Replica_IO_Running Slave_IO_Running Replica_SQL_Running: No retraso de replicación replication lag replica lag Rpl_semi_sync_source_status 1045 2003 2026 Error connecting to source estado de la replicación Replica_IO_Running: No Replica_IO_Running: Connecting Slave_IO_Running: No Slave"},{"k":"scenario","v":"switchover","l":"Switchover planificado (traspasar el rol de source a una replica)","m":"Cambio de rol switchover switchover planificado cambio de rol role change promote replica cambiar source mantenimiento mantenimiento planificado graceful takeover graceful-master-takeover reemplazar source cambiar primary change primary nuevo source El source y las replicas están sanos; va a traspasar de forma planificada el rol de source a una replica por mantenimiento, cambio de hardware o upgra"},{"k":"scenario","v":"failover","l":"Source perdido: failover a la replica más adelantada","m":"Cambio de rol failover source caído source down master down primary down Error connecting to source Can't connect to MySQL server 2003 2013 Lost connection replica más adelantada most advanced replica Replica_IO_Running: Connecting Slave_IO_Running: Connecting 2005 Unknown MySQL server host error reconnecting to source source inaccesible source apagado primary caído servidor caído server crashed E"},{"k":"scenario","v":"rejoin","l":"Reincorporar el source antiguo como replica tras un failover","m":"Cambio de rol source antiguo old source old master rejoin reincorporar errant split brain 1236 primary antiguo old primary el master antiguo ha vuelto split-brain Replica has more GTIDs than the source has Slave has more GTIDs than the master has reincorporación reintegrate Se ha hecho un failover y el servidor del source antiguo ha vuelto. Primero se cierra a escrituras y se copian sus binlogs, y"},{"k":"scenario","v":"rebuild","l":"Reconstruir una replica desde cero (clone / XtraBackup / mysqldump)","m":"Recuperación 1236 Cannot replicate because the source purged required binary logs purged required binary logs reconstruir rebuild replica reseed clone instance nueva replica divergencia de datos data drift Could not find first log file name in binary log index file the source has purged binary logs containing GTIDs that the replica requires the master has purged binary logs containing GTIDs that t"},{"k":"scenario","v":"errant","l":"Errant GTID: la replica tiene transacciones que el source no tiene","m":"Recuperación errant gtid errant transaction gtid_subtract extra gtid gtid sobrante Having extra GTID events incompatible GTID set 1236 GTID_SUBTRACT diferencia de gtid_executed escrito en la replica write on replica super_read_only desactivado Replica has more GTIDs than the source has Hay transacciones escritas directamente en la replica (con super_read_only desactivado) o heredadas de un source "},{"k":"scenario","v":"sqlerr","l":"Replicación detenida: 1062 Duplicate entry / 1032 Can't find record","m":"Recuperación 1062 1032 1452 1146 1007 1008 Duplicate entry Can't find record HA_ERR_KEY_NOT_FOUND HA_ERR_FOUND_DUPP_KEY Could not execute Write_rows event Could not execute Update_rows event Could not execute Delete_rows event Replica_SQL_Running: No Slave_SQL_Running: No Coordinator stopped because there were error(s) in the worker(s) sql_slave_skip_counter sql_replica_skip_counter gtid_next MY-0"},{"k":"scenario","v":"delayed","l":"Replica retrasada (SOURCE_DELAY): configurarla o recuperar datos borrados","m":"Operación diaria SOURCE_DELAY MASTER_DELAY delayed replica replica retrasada borrado por error DROP TABLE DELETE erróneo SQL_Delay SQL_BEFORE_GTIDS SQL_Remaining_Delay DELETE accidental DELETE sin WHERE accidental delete accidental drop dropped table tabla eliminada recuperación de datos UNTIL SQL_BEFORE_GTIDS Quiere mantener una replica deliberadamente N segundos por detrás (configurar / quitar e"},{"k":"scenario","v":"repoint","l":"Trasladar una replica a otro source (cadena / reconexión)","m":"Operación diaria repoint change source replicación en cadena chained replication intermediate replica intermedia relay replica CHANGE MASTER TO CHANGE REPLICATION SOURCE TO cascading replication replicación en cascada conectar replica a otro source cambiar SOURCE_HOST cambiar MASTER_HOST log_replica_updates Quiere colgar una replica de otra replica (cadena / replica intermedia) o volver a conectar"},{"k":"scenario","v":"binlog","l":"Limpieza y retención de binlogs (el disco se llena)","m":"Operación diaria PURGE BINARY LOGS PURGE MASTER LOGS binlog_expire_logs_seconds expire_logs_days disco lleno de binlogs No space left on device errno 28 limpieza de binlogs binlog retention Disk is full writing OS errno 28 1867 was not purged because it was being read 1373 Target log not found in binlog index binlog crece binlog_expire_logs_auto_purge binlog_transaction_compression Los binlogs est"}]},{"id":"mysql-ic-ops","file":"install/guides/mysql-ic-ops.json","group":"MySQL","engineId":"mysql","category":"ops","title":"Operación diaria y escenarios de fallo de MySQL InnoDB Cluster (MySQL Shell 8.4)","summary":"No es una instalación: un runbook por escenarios para la operación diaria y la recuperación ante fallos de un InnoDB Cluster que ya está en marcha (Group Replication + MySQL Router, desplegado con la AdminAPI de MySQL Sh","level":"Avanzado","duration":"5 min (comprobación) – 1 hora (añadir un miembro)","topology":"3–9 nodos MySQL (single-primary) + MySQL Router","tags":["mysql","innodb cluster","group replication","mysql shell","mysqlsh","adminapi","cluster.status","addinstance","removeinstance","rejoininstance","rebootclusterfromcompleteoutage","forcequorumusingpartitionof","setprimaryinstance","setoption","setinstanceoption","rescan","mysql router","bootstrap","quorum","no_quorum","split brain","expelTimeout","memberWeight","autoRejoinTries","exitStateAction","consistency","clone","MISSING","UNREACHABLE","RECOVERING","ERROR","runbook","8.4"],"order":13,"steps":5,"scenarios":[{"k":"scenario","v":"health","l":"Estado del cluster y comprobación de salud (¿va todo bien?)","m":"Supervisión y comprobaciones cluster.status status() OK_PARTIAL OK_NO_TOLERANCE OK_NO_TOLERANCE_PARTIAL instanceErrors replicationLag flow control RECOVERING applier queue estado del cluster Cluster is ONLINE and can tolerate can tolerate up to ONE failure comprobación de salud health check la cola crece replication_group_members group_replication_member_stats cómo está el cluster Solo lectura: `c"},{"k":"scenario","v":"add","l":"Añadir un miembro nuevo (addInstance: clone / incremental)","m":"Miembros addInstance añadir miembro añadir nodo add instance recoveryMethod clone incremental 3092 Having extra GTID events MYSQLSH 51166 The target instance must be either cloned or fully provisioned Clone Donor Error aumentar capacidad aumentar tolerancia a fallos OK_NO_TOLERANCE Quiere añadir un nuevo nodo MySQL al cluster, aumentar la tolerancia a fallos (3 → 5) o recuperar un nodo que se habí"},{"k":"scenario","v":"remove","l":"Quitar un miembro (removeInstance, con force si hace falta)","m":"Miembros removeInstance quitar miembro quitar nodo remove instance force MISSING decommission Cluster.removeInstance is not reachable and cannot be safely removed eliminar nodo reducir retirada de servidor Quiere quitar un nodo del cluster de forma permanente (devolución de hardware, reducción) o el nodo se ha perdido y hay que borrarlo de los metadatos (`force`). Quitar un secondary no causa inte"},{"k":"scenario","v":"rejoin","l":"Reincorporar un miembro caído (MISSING / ERROR / OFFLINE)","m":"Fallo y recuperación rejoinInstance MISSING ERROR OFFLINE UNREACHABLE expelled expulsado del grupo member was expelled MY-011735 MY-011526 incompatible GTID set (MISSING) This member has more executed transactions than those present in the group has an incompatible GTID set with the seed instance Error connecting to all peers Member join failed group_replication is stopped miembro caído miembro OF"},{"k":"scenario","v":"reboot","l":"Todo el cluster se ha caído: reiniciarlo (rebootClusterFromCompleteOutage)","m":"Fallo y recuperación rebootClusterFromCompleteOutage complete outage caída completa todos los nodos caídos corte eléctrico Group Replication is not active OFFLINE Unable to find a cluster PRIMARY member The cluster is offline Dba.rebootClusterFromCompleteOutage Dba.getCluster: This function is not available through a session to a standalone instance metadata exists, instance belongs to that metada"},{"k":"scenario","v":"quorum","l":"Pérdida de la mayoría (NO_QUORUM): forzar el quorum con los miembros restantes","m":"Fallo y recuperación NO_QUORUM forceQuorumUsingPartitionOf quorum pérdida de la mayoría split-brain split brain UNREACHABLE majority partition 3100 Cluster has no quorum cannot process write transactions split-brain! Instance is not part of the majority group escrituras bloqueadas writes hang forceQuorum La mayoría de los miembros se perdió a la vez (p. ej., 2 de 3) y los restantes dejaron de escr"},{"k":"scenario","v":"switch","l":"Cambio planificado de primary (setPrimaryInstance)","m":"Roles y enrutamiento setPrimaryInstance cambiar primary switchover traspasar primary The appointed primary member has a version that is greater group_replication_set_as_primary Cluster.setPrimaryInstance runningTransactionsTimeout cambio planificado de primary mover primary planned switchover Quiere traspasar de forma planificada el rol de primary a un secondary concreto por mantenimiento (parches"},{"k":"scenario","v":"router","l":"Añadir un Router o volver a hacer su bootstrap","m":"Roles y enrutamiento mysqlrouter MySQL Router bootstrap --force routerErrors re-bootstrap Router listRouters removeRouterMetadata 6446 6447 previously configured in this host Unable to connect to the metadata server Unable to fetch all configuration options The provided server is currently not in a InnoDB cluster group with quorum mysqlrouter.service failed Router no conecta instalación de Router "},{"k":"scenario","v":"options","l":"Cambiar opciones del cluster (consistency, expelTimeout, memberWeight…)","m":"Configuración setOption setInstanceOption consistency BEFORE_ON_PRIMARY_FAILOVER expelTimeout group_replication_member_expel_timeout memberWeight autoRejoinTries exitStateAction OFFLINE_MODE READ_ONLY options() group_replication_consistency group_replication_member_weight group_replication_autorejoin_tries group_replication_exit_state_action miembro expulsado a menudo prioridad de failover Quiere "},{"k":"scenario","v":"rescan","l":"Sincronizar los metadatos con el grupo real (rescan)","m":"Configuración rescan metadatos is not managed not registered in the metadata newlyDiscoveredInstances unavailableInstances instanceErrors obsolete Use cluster.rescan() Instance is not managed by InnoDB cluster Use cluster.rescan() to repair metadatos incoherentes metadatos desincronizados `cluster.status()` menciona un miembro que no está en los metadatos o un registro antiguo que ya no está en el"}]},{"id":"mysql-logical-backup","file":"install/guides/mysql-logical-backup.json","group":"MySQL","engineId":"mysql","category":"backup","title":"Backup lógico y restauración de MySQL (mysqldump, MySQL Shell)","summary":"Backup lógico y restauración con mysqldump o MySQL Shell (util.dumpInstance / dumpSchemas / dumpTables + util.loadDump): una tabla, un esquema o toda la instancia; solo estructura o solo datos; migración a otro servidor;","level":"Intermedio","duration":"20–90 min (según el volumen de datos)","topology":"1 servidor (+ un servidor de destino en la migración y en la restauración de la instancia)","tags":["mysql","mysqldump","mysql shell","mysqlsh","dumpInstance","dumpSchemas","dumpTables","loadDump","backup lógico","logical backup","export","import","restauración","restore","migración","migration","login-path","mysql_config_editor","systemd timer","cron","copia de seguridad","replica","dump-replica","source-data","innodb cluster","cron","systemd timer","flock","login-path"],"order":15,"steps":8,"scenarios":[{"k":"mode","v":"table","l":"Export / import de tablas","m":"Backup y restauración backup de tabla table export copiar tabla copy table mysqldump tabla Hacer un backup de una o varias tablas, copiarlas a otro esquema/servidor o restaurarlas. El origen sigue en servicio; en las tablas InnoDB no se toman locks."},{"k":"mode","v":"schema","l":"Backup / restauración de un esquema (base de datos)","m":"Backup y restauración backup de esquema backup de base de datos database backup dumpSchemas mysqldump database Hacer un backup de una base de datos (esquema) completa o cargarla en otro esquema/servidor. El origen sigue en servicio; en el destino, las tablas con el mismo nombre se eliminan y se vuelven a crear."},{"k":"mode","v":"instance","l":"Instancia completa (usuarios + todos los esquemas)","m":"Backup y restauración todas las bases de datos all databases dumpInstance migrar usuarios full logical backup El backup se hace en este servidor y la restauración en un servidor vacío: usuarios, privilegios y todos los esquemas. Para reconstruir un servidor o cambiar de versión/plataforma."},{"k":"mode","v":"ddl","l":"Solo estructura (DDL)","m":"Estructura y datos por separado solo estructura schema only no-data ddlOnly CREATE TABLE export Definiciones de tablas, vistas, triggers, procedimientos y eventos; sin datos. Para comparar esquemas, montar un entorno vacío o poner la estructura bajo control de versiones."},{"k":"mode","v":"data","l":"Solo datos","m":"Estructura y datos por separado solo datos data only no-create-info dataOnly transferencia de datos Solo se transfieren las filas, con la estructura ya creada en el destino (p. ej. para refrescar datos de prueba). Los triggers del destino pueden dispararse durante la carga; la guía lo comprueba."},{"k":"mode","v":"migrate","l":"Migración a otro servidor","m":"Migración migración migration cambio de servidor migrar a un servidor nuevo 8.0 to 8.4 migration dump and load migrar a HeatWave Se trabaja en el servidor de destino y se conecta al origen por la red con un login-path. En el momento del corte la aplicación deja de escribir; la interrupción dura lo que el dump + la carga. Para cambiar de servidor, de sistema operativo o de versión."},{"k":"mode","v":"extract","l":"Extraer una sola tabla de un dump completo","m":"Recuperación restaurar una tabla extraer tabla del dump extract table from dump deshacer DROP TABLE filas borradas ERROR 1146 Table doesn't exist Tiene un dump completo y solo necesita una tabla (filas borradas, tabla eliminada con DROP). Se extrae a un esquema aparte sin tocar el esquema original, o se vuelve a colocar la tabla eliminada."},{"k":"mode","v":"sched","l":"Backup programado (systemd timer / cron)","m":"Backup programado backup programado scheduled backup cron mysqldump systemd timer backup automático backup nocturno Backup lógico automático todos los días a una hora fija: usuario de backup propio, login-path, script, systemd timer o cron, período de retención y aviso de errores. El origen sigue en servicio."}]},{"id":"mysql-security","file":"install/guides/mysql-security.json","group":"MySQL","engineId":"mysql","category":"security","title":"Hardening de seguridad de MySQL (auditoría de estado, red, TLS, autenticación, privilegios, audit, cifrado)","summary":"No es una guía de instalación: localiza y cierra las brechas de un servidor MySQL 8.4 / 8.0 en funcionamiento. Primero, la Auditoría de estado de solo lectura lista las brechas por orden de gravedad e indica a qué tarjet","level":"Intermedio","duration":"5 min (auditoría) – 3 h (hardening completo)","topology":"Un servidor MySQL + servidor de aplicación (verificación del cliente); réplica/InnoDB Cluster en las notas","tags":["mysql","seguridad","security","hardening","bastionado","cis benchmark","tls","ssl","require_secure_transport","verify_identity","caching_sha2_password","mysql_native_password","validate_password","audit_log","audit","percona","tde","keyring","innodb encryption","root","usuario anónimo","privilegio","rol","login-path"],"order":40,"steps":2,"scenarios":[{"k":"scenario","v":"scan","l":"Auditoría de estado: ¿qué brechas tengo? (solo lectura)","m":"Primeros pasos auditoría de seguridad security audit hardening check CIS brecha vulnerabilidad vulnerability hallazgo de pentest mysql_secure_installation consulta de auditoría análisis de seguridad security scan CRÍTICO El primer análisis que se debe ejecutar: cuentas anónimas, sin contraseña o root remoto, acceso desde cualquier sitio (%), plugin de autenticación débil, privilegios globales, TLS"},{"k":"scenario","v":"all","l":"Hardening completo (todo en secuencia)","m":"Primeros pasos hardening bastionado seguridad completa full hardening aplicar CIS benchmark STIG Servidor recién instalado o nunca bastionado: todas las áreas se aplican en un orden seguro; MySQL se reinicia varias veces (requiere ventana de mantenimiento)."},{"k":"scenario","v":"net","l":"Red y conexión: ¿quién puede conectarse y desde dónde? (bind_address, firewall, restricción de host)","m":"Red e identidad bind_address 0.0.0.0 '%' host '%' firewall 3306 33060 Host is not allowed to connect ERROR 1130 ERROR 2003 Can't connect to MySQL server skip_name_resolve acceso externo restringir acceso MySQL escucha en todas las interfaces, las cuentas tienen host '%' y el puerto está abierto a todos. Si cambia bind_address, se necesita un reinicio."},{"k":"scenario","v":"tls","l":"Cifrar la conexión (TLS, require_secure_transport, VERIFY_IDENTITY)","m":"Red e identidad ssl tls require_secure_transport ssl-mode VERIFY_IDENTITY VERIFY_CA ERROR 3159 Connections using insecure transport are prohibited server-cert.pem caducidad del certificado ERROR 2026 SSL connection error certificate verify failed ERROR 3888 cadena de certificados certificate chain Se usa el certificado autofirmado generado automáticamente, se aceptan conexiones sin cifrar o los cl"},{"k":"scenario","v":"auth","l":"Autenticación y contraseñas (caching_sha2, política de contraseñas, root)","m":"Red e identidad mysql_native_password caching_sha2_password validate_password password_history FAILED_LOGIN_ATTEMPTS root remoto anonymous user usuario anónimo ERROR 1045 Access denied Plugin 'mysql_native_password' is not loaded ERROR 1524 ERROR 1819 ERROR 3955 ERROR 3118 Account is locked ERROR 2061 Authentication requires secure connection auth_socket Cuentas con mysql_native_password, sin polí"},{"k":"scenario","v":"priv","l":"Mínimo privilegio: roles y cuenta de aplicación sin DDL","m":"Privilegios y trazabilidad GRANT REVOKE ALL PRIVILEGES SUPER FILE least privilege mínimo privilegio CREATE ROLE ERROR 1142 command denied ERROR 3530 ERROR 1227 you need (at least one of) the SUPER privilege DEFAULT ROLE informe de privilegios Privilegios CREATE/ALTER/DROP o *.* en la cuenta de aplicación, cuentas con FILE/SUPER; roles de lectura/escritura/DDL e informe de privilegios. Sin interrup"},{"k":"scenario","v":"audit","l":"Audit: ¿quién hizo qué?","m":"Privilegios y trazabilidad audit audit_log audit_log_filter pista de auditoría general_log Enterprise Audit Percona audit SIEM RGPD PCI audit_log_filter_set_filter ERROR 1126 licencia de MySQL Enterprise quién hizo qué El auditor pide un registro de sesiones y consultas. MySQL Community no incluye audit integrado: Enterprise Audit, Audit Log Filter de Percona Server u opciones limitadas para Commu"},{"k":"scenario","v":"tde","l":"Cifrar los datos en disco (cifrado de tablespaces InnoDB, keyring)","m":"Datos y backups TDE encryption at rest ENCRYPTION='Y' keyring component_keyring_file ERROR 3185 Can't find master key from keyring binlog_encryption innodb_redo_log_encrypt ALTER INSTANCE ROTATE INNODB MASTER KEY mysqld.my keyring_component_status master key Cifrado de tablas, redo, undo y binlog frente al robo de un disco o de una copia de backup; un reinicio para instalar el keyring component y "},{"k":"scenario","v":"backup","l":"Seguridad de los backups (backup cifrado, login-path, permisos)","m":"Datos y backups cifrado de backups backup encryption mysqldump gpg xtrabackup --encrypt login-path mysql_config_editor contraseña en .my.cnf xbcrypt --encrypt-key-file backup sin cifrar unencrypted backup Backups sin cifrar o en un directorio legible por todos, contraseña en texto plano en el script; mysqldump + GPG o XtraBackup --encrypt, cuenta de backup y login-path."},{"k":"scenario","v":"os","l":"Lado del sistema operativo (cuenta de servicio, permisos, core dump, SELinux)","m":"Servidor y versión SELinux permissive secure_file_priv local_infile core dump cuenta mysql .mysql_history ERROR 1290 secure-file-priv option ERROR 3948 Loading local data is disabled avc: denied El directorio de datos o los archivos de certificado están abiertos a otros usuarios, secure_file_priv vacío, local_infile activado, SELinux desactivado. Algunos ajustes surten efecto al reiniciar."},{"k":"scenario","v":"patch","l":"Parches y versión: ¿estoy en una versión soportada?","m":"Servidor y versión EOL end of life fin de soporte MySQL 8.0 EOL CPU Critical Patch Update CVE parche de seguridad calendario de parches MySQL 8.0 end of life 8.4 LTS Versión, fin de soporte (8.0 terminó en abril de 2026) y actualizaciones pendientes; para aplicarlas enlaza con la guía de upgrade. Solo lectura."}]},{"id":"mssql-linux-install","file":"install/guides/mssql-linux-install.json","group":"SQL Server","engineId":"mssql","category":"install","title":"Instalación de SQL Server en Linux (RHEL / Ubuntu, lista para producción)","summary":"Instala SQL Server 2022 o 2025 desde el repositorio de Microsoft en un único servidor Linux (familia RHEL o Ubuntu): elección de edición, entrega segura de la contraseña de sa, mssql-tools18 (sqlcmd/bcp), directorios sep","level":"Principiante","duration":"40–60 min","topology":"1 servidor","tags":["sql server","mssql","linux","instalación","install","mssql-conf","sqlcmd","tempdb","maxdop","rhel","ubuntu","2022","2025"],"order":1,"steps":22,"scenarios":[]},{"id":"mssql-windows-install","file":"install/guides/mssql-windows-install.json","group":"SQL Server","engineId":"mssql","category":"install","title":"Instalación de SQL Server 2022 en Windows (desatendida, lista para producción)","summary":"Instala SQL Server 2022 de forma desatendida (unattended) en un único Windows Server con ConfigurationFile.ini: un INI generado según sus elecciones (características, instancia, cuentas de servicio/gMSA, intercalación, d","level":"Intermedio","duration":"45–75 min","topology":"1 Windows Server","tags":["sql server","mssql","windows","instalación","install","desatendida","configurationfile.ini","setup.exe","gmsa","tempdb","maxdop","ifi","lock pages in memory","2022"],"order":2,"steps":18,"scenarios":[]},{"id":"mssql-ag-windows","file":"install/guides/mssql-ag-windows.json","group":"SQL Server","engineId":"mssql","category":"ha","title":"SQL Server Always On Availability Group (Windows, WSFC)","summary":"A partir de servidores Windows con SQL Server 2022 instalado, crea un Always On Availability Group: Windows Server Failover Cluster (WSFC) y testigo de quorum (file share / cloud witness), habilitación de Always On, mirr","level":"Avanzado","duration":"90–150 min","topology":"2–9 nodos Windows (WSFC) + testigo (witness)","tags":["sql server","mssql","always on","availability group","ag","wsfc","failover cluster","listener","witness","automatic seeding","hadr","windows","alta disponibilidad"],"order":3,"steps":25,"scenarios":[]},{"id":"mssql-ag-linux","file":"install/guides/mssql-ag-linux.json","group":"SQL Server","engineId":"mssql","category":"ha","title":"SQL Server Always On Availability Group (Linux, Pacemaker)","summary":"A partir de tres servidores Linux (RHEL 9 o Ubuntu 22.04) con SQL Server 2022 instalado, crea un Always On AG gestionado por Pacemaker: mssql-server-ha, hadr.hadrenabled, mirroring endpoints con autenticación por certifi","level":"Avanzado","duration":"120–180 min","topology":"3 nodos Linux: 3 réplicas síncronas o 2 síncronas + una réplica configuration-only","tags":["sql server","mssql","always on","availability group","ag","linux","pacemaker","corosync","pcs","stonith","fencing","mssql-server-ha","ocf:mssql:ag","cluster_type external","configuration only","listener","ip virtual","rhel","ubuntu","alta disponibilidad"],"order":4,"steps":30,"scenarios":[]},{"id":"mssql-cu-patch","file":"install/guides/mssql-cu-patch.json","group":"SQL Server","engineId":"mssql","category":"patch","title":"Aplicar una Cumulative Update (CU) de SQL Server","summary":"Aplica una Cumulative Update (CU) a una instancia instalada de SQL Server 2022/2025 dentro de la misma versión principal: en Linux con dnf/apt y en Windows mediante instalación desatendida (/quiet) con el archivo de inst","level":"Intermedio","duration":"30–90 min","topology":"1 servidor o AG (2+ réplicas)","tags":["sql server","mssql","cu","cumulative update","parche","actualización","rolling","grupo de disponibilidad","always on","yum","apt","kb"],"order":4,"steps":12,"scenarios":[]},{"id":"mssql-ag-ops","file":"install/guides/mssql-ag-ops.json","group":"SQL Server","engineId":"mssql","category":"ops","title":"Operación de SQL Server Always On AG (Windows y Linux)","summary":"No es una instalación: operación diaria y escenarios de fallo de un Always On Availability Group ya instalado: comprobación de estado, diagnóstico de retraso y crecimiento del log, failover planificado, failover forzado ","level":"Avanzado","duration":"15–90 min (según el escenario)","topology":"AG con 2–9 réplicas (Windows WSFC o Linux Pacemaker)","tags":["sql server","mssql","always on","availability group","ag","failover","forced failover","force_failover_allow_data_loss","suspend","resume","automatic seeding","read-only routing","listener","pacemaker","pcs","wsfc","secondary lag","log_send_queue","redo_queue","required_synchronized_secondaries_to_commit"],"order":5,"steps":3,"scenarios":[{"k":"op","v":"health","l":"Comprobación de estado del AG (consultas del dashboard)","m":"Supervisión y diagnóstico NOT SYNCHRONIZING NOT_HEALTHY DISCONNECTED RESOLVING PARTIALLY_HEALTHY estado del AG availability group health sys.dm_hadr dashboard estado de la réplica Msg 35206 connection timeout AG dashboard Show Dashboard ¿Está todo en orden? Estado de las réplicas / bases de datos, preparación para el failover, cluster y últimos cambios de rol. No modifica nada."},{"k":"op","v":"lag","l":"El secondary se queda atrás / el fichero de log crece (diagnóstico del retraso)","m":"Supervisión y diagnóstico AVAILABILITY_REPLICA log_reuse_wait log_send_queue redo_queue HADR_SYNC_COMMIT secondary lag secondary_lag_seconds Msg 9002 9002 transaction log is full retraso redo blocked el log crece La cola de send/redo crece, los datos del secondary están desfasados, el log del primary está a punto de llenarse (log_reuse_wait = AVAILABILITY_REPLICA) o los commits son lentos (HADR_SY"},{"k":"op","v":"failover","l":"Failover planificado (switchover, sin pérdida de datos)","m":"Cambio de rol planned failover manual failover switchover ALTER AVAILABILITY GROUP FAILOVER pcs resource move Msg 41142 41142 Msg 19407 cambiar rol mover primary Failover Cluster Manager Move failover por mantenimiento Para mantenimiento, patch o pruebas, mueve el primary a un secondary síncrono y SYNCHRONIZED; unos segundos de interrupción, sin pérdida de datos."},{"k":"op","v":"forced","l":"Primary perdido: failover forzado (FORCE_FAILOVER_ALLOW_DATA_LOSS)","m":"Cambio de rol FORCE_FAILOVER_ALLOW_DATA_LOSS forced failover failover forzado quorum lost pérdida de quórum FixQuorum forcequorum disaster recovery DR Msg 41131 Msg 35250 pérdida de sitio primary down Msg 47104 ForceQuorum Cluster is running in ForceQuorum state RESOLVING_NORMAL caída del primary pérdida de datos El primary (o todo el sitio / el quórum del cluster) se ha perdido y no se puede espe"},{"k":"op","v":"suspend","l":"Suspender / reanudar el movimiento de datos (SUSPEND / RESUME)","m":"Mantenimiento SUSPENDED SET HADR SUSPEND SET HADR RESUME is_suspended suspend_reason data movement movimiento de datos detenido Msg 35264 35264 Msg 35265 SUSPEND_FROM_REDO SUSPEND_FROM_USER SUSPEND_FROM_PARTNER suspendida suspender Detener temporalmente el envío de datos al secondary (mantenimiento de red, proceso masivo) o reanudar una base de datos que ha quedado suspendida (SUSPENDED)."},{"k":"op","v":"mode","l":"Cambiar el modo síncrono / asíncrono","m":"Mantenimiento SYNCHRONOUS_COMMIT ASYNCHRONOUS_COMMIT availability mode REQUIRED_SYNCHRONIZED_SECONDARIES_TO_COMMIT required_synchronized_secondaries_to_commit síncrono asíncrono Msg 35211 35211 Msg 35212 35212 FAILOVER_MODE Pasar una réplica de SYNCHRONOUS_COMMIT ↔ ASYNCHRONOUS_COMMIT (sitio remoto, red lenta, simulacro de DR) y ajustar la protección del commit (REQUIRED_SYNCHRONIZED_SECONDARIES_T"},{"k":"op","v":"adddb","l":"Añadir una base de datos al AG (automatic seeding o backup/restore)","m":"Cambio ADD DATABASE automatic seeding SET HADR AVAILABILITY GROUP Msg 35250 Msg 1478 1478 seeding failed añadir base de datos Msg 1475 1475 might contain bulk logged changes manual seeding Incorporar al AG una base de datos nueva o existente: automatic seeding para bases de datos pequeñas/medianas, desde backup para bases de datos grandes o con red lenta."},{"k":"op","v":"addrep","l":"Añadir una réplica (nodo)","m":"Cambio ADD REPLICA Add-ClusterNode pcs cluster node add nueva réplica añadir réplica Msg 41158 41158 Failed to join local availability replica JOIN Añadir un nuevo secondary al AG: el nodo se une al cluster, se prepara el endpoint, ADD REPLICA + JOIN, y las bases de datos llegan por seeding."},{"k":"op","v":"removerep","l":"Quitar una réplica (nodo)","m":"Cambio REMOVE REPLICA Remove-ClusterNode pcs cluster node remove quitar réplica decommission Msg 35223 35223 quitar nodo Quitar del AG y del cluster un secondary que se va a apagar definitivamente o reinstalar."},{"k":"op","v":"routing","l":"Redirección de lecturas (read-only routing)","m":"Conexión read-only routing READ_ONLY_ROUTING_URL READ_ONLY_ROUTING_LIST ApplicationIntent=ReadOnly readable secondary secondary legible Msg 978 978 Msg 976 976 not accessible for queries Msg 10786 ApplicationIntent Redirigir a través del listener las conexiones ApplicationIntent=ReadOnly a los secondary legibles (descargar del primary la carga de informes)."},{"k":"op","v":"listener","l":"Crear o modificar el listener (IP, puerto)","m":"Conexión listener ADD LISTENER MODIFY LISTENER MultiSubnetFailover RegisterAllProvidersIP Msg 19471 19471 Msg 41009 virtualip Msg 19456 19456 HostRecordTTL puerto del listener registro DNS Crear el listener si no existe, cambiar su puerto o (en Windows) añadir una IP para una segunda subred."}]},{"id":"mssql-backup","file":"install/guides/mssql-backup.json","group":"SQL Server","engineId":"mssql","category":"backup","title":"Estrategia de copias de seguridad, automatización y restauración en SQL Server","summary":"Configura las copias de seguridad de una instancia de SQL Server ya instalada (Linux o Windows; instancia única, FCI o réplicas de AG) y recupera a partir de ellas. Copia de seguridad: elección del modelo de recuperación","level":"Intermedio","duration":"30–90 min","topology":"1 servidor, Failover Cluster Instance o réplicas de Always On AG (restauración en otro servidor: origen + destino)","tags":["sql server","mssql","backup","copia de seguridad","respaldo","restore","restaurar base de datos","pitr","stopat","point in time","differential","transaction log","copy_only","compression","checksum","encryption","cifrado de copias","sql server agent","job","ola hallengren","maintenance solution","retention","verifyonly","headeronly","filelistonly","norecovery","standby","with move","task scheduler","cron","availability group","always on","backup preference","automated_backup_preference","fn_hadr_backup_is_preferred_replica","copy_only","sql agent","sysjobhistory","cron","task scheduler","page restore","suspect_pages","824","823","33111","18456","orphaned user","sp_help_revlogin","master restore","single user","msdb restore","model restore","stopatmark","tail-log","no_truncate","recovery_pending","suspect","log chain","lsn"],"order":15,"steps":11,"scenarios":[{"k":"mode","v":"strategy","l":"Configurar una estrategia de copias de seguridad (copias programadas + retención)","m":"Copia de seguridad plan de copias de seguridad backup strategy el archivo de log crece LOG_BACKUP Msg 9002 transaction log is full sql agent job Msg 3013 3013 BACKUP DATABASE is terminating abnormally no se hacen copias de seguridad job de copia de seguridad Servidor nuevo o base de datos sin copias periódicas: se configuran los jobs de full / differential / log, la retención y la supervisión."},{"k":"mode","v":"oneoff","l":"Hacer una copia de seguridad puntual (p. ej. COPY_ONLY antes de un cambio)","m":"Copia de seguridad copy_only copia puntual copia antes de un cambio copia manual Msg 4214 4214 Msg 4208 4208 BACKUP LOG is not allowed while the recovery model is SIMPLE Una única copia antes de un cambio, patch o upgrade, o para copiar a un entorno de pruebas; COPY_ONLY no rompe la cadena."},{"k":"mode","v":"verify","l":"Verificar las copias de seguridad (historial, VERIFYONLY, restauración de prueba)","m":"Ensayo y comprobaciones VERIFYONLY HEADERONLY FILELISTONLY test restore copia de seguridad dañada Msg 3241 Msg 3183 Msg 3203 Para demostrar que las copias se pueden restaurar de verdad: historial de copias, VERIFYONLY, HEADERONLY / FILELISTONLY y restauración de prueba en una base de datos temporal + CHECKDB."},{"k":"mode","v":"chain","l":"¿Está intacta la cadena de copias? (hueco de LSN, archivo que falta)","m":"Ensayo y comprobaciones Msg 4305 4305 too recent to apply LSN log chain cadena rota broken log chain Msg 3136 last_log_backup_lsn cadena de copias de seguridad Antes de una restauración o como auditoría periódica: reconstruye la cadena a partir del historial de msdb y localiza huecos de LSN, archivos que no están en disco y copias intercaladas. No modifica nada."},{"k":"mode","v":"restore","l":"Restaurar una base de datos desde copia de seguridad (cadena, STOPAT / STOPATMARK, MOVE / REPLACE, otro servidor)","m":"Restauración y vuelta a un momento dado STOPAT STOPATMARK STOPBEFOREMARK point in time PITR borrado por error deshacer DELETE WITH MOVE REPLACE tail-log Msg 3154 Msg 3159 Msg 5133 Msg 1834 Msg 3156 The tail of the log for the database Msg 3101 3101 Exclusive access could not be obtained Msg 4335 4335 RESTORE DATABASE Se borraron o actualizaron datos por error, se necesita una copia de la base de d"},{"k":"mode","v":"damaged","l":"La base de datos no se abre: archivo de datos perdido o dañado (tail-log + restauración)","m":"Pérdida / daños Msg 945 945 RECOVERY_PENDING recovery pending SUSPECT Msg 5120 5120 Msg 17204 Msg 17207 Msg 5181 FCB::Open failed Operating system error 2 mdf borrado archivo de datos perdido database cannot be opened Msg 823 NO_TRUNCATE CONTINUE_AFTER_ERROR EMERGENCY La base de datos está en RECOVERY_PENDING o SUSPECT y no se puede leer el .mdf/.ndf, pero la instancia está en ejecución: primero s"},{"k":"mode","v":"page","l":"Algunas páginas dañadas (Msg 823 / 824): page restore","m":"Pérdida / daños Msg 824 824 Msg 823 823 Msg 829 829 suspect_pages torn page incorrect checksum página dañada page corruption page restore CHECKDB found Msg 8928 Msg 8939 Msg 8976 repair_allow_data_loss Las consultas o CHECKDB fallan en páginas concretas (msdb.dbo.suspect_pages): solo se restauran desde la copia las páginas dañadas. En Enterprise/Developer la base de datos sigue en línea; en Standa"},{"k":"mode","v":"master","l":"Restaurar la base de datos master (modo de usuario único)","m":"Bases de datos del sistema master Msg 3108 3108 single user mode single-user modo de usuario único -m Msg 3417 3417 Msg 17113 Msg 18461 master dañada force-setup REBUILDDATABASE master está dañada o se perdieron logins / linked servers / configuración del servidor: la instancia se inicia en modo de usuario único (-m), se restaura master y SQL Server se detiene solo. Requiere downtime; si la instan"},{"k":"mode","v":"sysdb","l":"Restaurar la base de datos msdb o model","m":"Bases de datos del sistema msdb model Msg 3168 3168 han desaparecido los jobs msdb suspect Msg 1807 msdb dañada model dañada Los jobs, el historial de copias, Database Mail (msdb) o la plantilla de las bases de datos nuevas (model) están dañados o se han perdido: se restauran con REPLACE desde una copia hecha en la misma build; Agent se detiene brevemente."},{"k":"mode","v":"orphan","l":"Los usuarios no pueden conectarse tras la restauración (orphaned user, asignación de logins)","m":"Migración y acceso Msg 18456 18456 Login failed for user orphaned user orphan usuario huérfano sp_help_revlogin sp_change_users_login Msg 15023 15023 Msg 916 Msg 4060 asignación de logins SID La base de datos se trasladó o restauró en otro servidor y la aplicación recibe 'Login failed': los logins se transfieren desde el origen con su SID y el hash de la contraseña, y los usuarios restantes se asi"},{"k":"mode","v":"enc","l":"La copia cifrada no se puede restaurar (falta el certificado, Msg 33111)","m":"Migración y acceso Msg 33111 33111 Cannot find server certificate with thumbprint copia cifrada encrypted backup certificado certificate TDE Msg 15581 15581 master key La copia (o la copia de una base de datos con TDE) está cifrada con un certificado que no existe en este servidor: se identifica la huella digital (thumbprint), se carga el certificado con su clave privada y se verifica la copia. No"}]},{"id":"mssql-bcp-export","file":"install/guides/mssql-bcp-export.json","group":"SQL Server","engineId":"mssql","category":"backup","title":"Export/import por tablas en SQL Server (bcp, sqlpackage)","summary":"No es una copia de seguridad, sino traslado lógico de datos: exportar tablas con bcp y cargarlas en otra base de datos (formato nativo o de caracteres, archivo de formato, batch, TABLOCK, columna de identidad, UTF-8), vo","level":"Intermedio","duration":"20–60 min","topology":"1 servidor (los comandos se ejecutan en SQL Server o en un servidor de administración)","tags":["sql server","mssql","bcp","bulk copy","export","import","queryout","format file","native","csv","utf-8","sqlpackage","bacpac","dacpac","extract","publish","azure sql","traslado","migración","copiar tablas"],"order":16,"steps":8,"scenarios":[{"k":"op","v":"table","l":"Trasladar tablas: bcp out → bcp in","m":"Traslado de datos bcp bcp out bcp in bulk copy copiar tabla trasladar tabla Unexpected EOF encountered in BCP data-file Unable to open BCP host data-file Copiar una o varias tablas a otra base de datos de SQL Server; las tablas deben existir previamente en el destino."},{"k":"op","v":"query","l":"Volcar el resultado de una consulta a un archivo: bcp queryout (CSV/texto)","m":"Exportación queryout csv exportar csv resultado de consulta a archivo export to file Escribir el resultado de una consulta en un archivo CSV/de texto que se entregará a otro sistema (Excel, ETL, informes)."},{"k":"op","v":"bacpac","l":"Esquema + datos: sqlpackage Export / Import (.bacpac)","m":"Traslado de datos bacpac sqlpackage SqlPackage Export SqlPackage Import Migración a Azure SQL Database SQL71564 SQL71501 Trasladar esquema y datos en un solo archivo; especialmente para migrar a Azure SQL Database o para bases de datos pequeñas/medianas."},{"k":"op","v":"dacpac","l":"Solo esquema: sqlpackage Extract / Script / Publish (.dacpac)","m":"Esquema dacpac Extract Publish comparación de esquemas schema compare BlockOnPossibleDataLoss The schema update is terminating because data loss might occur Extraer solo el esquema, compararlo con el destino y generar un script de cambios, o crear el esquema vacío en un entorno nuevo."}]},{"id":"mssql-security","file":"install/guides/mssql-security.json","group":"SQL Server","engineId":"mssql","category":"security","title":"Endurecimiento de la seguridad de SQL Server (Linux y Windows: evaluación, red, TLS, autenticación, permisos, auditoría, TDE)","summary":"No es una guía de instalación: localiza y cierra las brechas de una instancia de SQL Server 2019 / 2022 / 2025 en ejecución (Linux o Windows). Primero una Evaluación de seguridad de solo lectura lista las brechas por gra","level":"Intermedio","duration":"5 min (evaluación) – 3 horas (endurecimiento completo)","topology":"Una instancia de SQL Server (Linux o Windows) + servidor de aplicaciones; Always On AG en las notas","tags":["sql server","mssql","seguridad","security","hardening","endurecimiento de seguridad","cis benchmark","stig","force encryption","tls","certificado","sa","check_policy","xp_cmdshell","trustworthy","server audit","audit","tde","transparent data encryption","backup encryption","mssql-conf","windows","linux"],"order":40,"steps":2,"scenarios":[{"k":"scenario","v":"scan","l":"Evaluación de seguridad: ¿qué brechas tengo? (solo lectura)","m":"Inicio evaluación de seguridad security audit hardening check CIS STIG brecha de seguridad vulnerabilidad vulnerability hallazgo de pentest SQL Vulnerability Assessment consulta de evaluación análisis de seguridad security scan CRÍTICO El análisis que se ejecuta primero: sa, directiva de contraseñas, contraseñas vacías, miembros de sysadmin, permisos de public/guest, xp_cmdshell, conexiones sin ci"},{"k":"scenario","v":"all","l":"Endurecimiento completo (todo en orden)","m":"Inicio hardening endurecimiento de seguridad seguridad completa full hardening aplicar CIS benchmark STIG Instancia recién instalada o nunca endurecida: se aplican todas las áreas en un orden seguro; SQL Server se reinicia una o dos veces (hace falta una ventana de mantenimiento)."},{"k":"scenario","v":"net","l":"Red y conectividad: ¿quién puede conectarse y desde dónde? (escucha, firewall, Browser)","m":"Red e identidad 1433 1434 SQL Browser firewall ListenOnAllIPs network.ipaddress Error 53 Error 40 TCP/IP Error 26 Error locating Server/Instance Specified Login timeout expired A network-related or instance-specific error acceso externo restringir el acceso SQL Server escucha en todas las interfaces, el 1433 está abierto a todo el mundo y SQL Browser se ejecuta sin necesidad. Cambiar la dirección "},{"k":"scenario","v":"tls","l":"Cifrar las conexiones (Force Encryption, certificado, validación en el cliente)","m":"Red e identidad Force Encryption TrustServerCertificate Encrypt=Mandatory certificate chain was issued by an authority that is not trusted SSL Provider error 0x80090325 tlscert caducidad del certificado TDS 8.0 strict The target principal name is incorrect certificate verify failed Encrypt=Strict Force Strict Encryption Unable to load user-specified certificate cadena de certificados certificate c"},{"k":"scenario","v":"auth","l":"Autenticación y contraseñas (sa, CHECK_POLICY, autenticación de Windows)","m":"Red e identidad sa CHECK_POLICY CHECK_EXPIRATION mixed mode Login failed for user Error 18456 Error 18486 account is locked out directiva de contraseñas password policy PWDCOMPARE Error 18452 Login is from an untrusted domain Error 18470 The account is disabled Msg 15118 Msg 33062 sa_disabled Windows Authentication sa habilitado, sin directiva de contraseñas en los inicios de sesión SQL, contraseñ"},{"k":"scenario","v":"priv","l":"Privilegios mínimos y superficie de ataque (roles, public, guest, xp_cmdshell)","m":"Permisos y rastro de auditoría db_owner sysadmin public guest xp_cmdshell TRUSTWORTHY CONTROL SERVER least privilege privilegios mínimos Msg 262 Msg 229 permission was denied Msg 15247 Msg 916 db_ddladmin EXECUTE AS informe de permisos El usuario de aplicación es db_owner o sysadmin, permisos adicionales en public/guest, xp_cmdshell/TRUSTWORTHY habilitados; diseño de roles e informe de permisos. S"},{"k":"scenario","v":"audit","l":"Auditoría: ¿quién hizo qué? (Server Audit, Database Audit Specification)","m":"Permisos y rastro de auditoría SQL Server Audit server audit audit specification fn_get_audit_file C2 audit login auditing rastro de auditoría SIEM RGPD PCI sqlaudit Server Audit Specification Database Audit Specification Msg 33072 quién hizo qué El auditor pide un registro de inicios de sesión y de cambios de permisos y de esquema; Server Audit a archivo (o al registro de eventos de Windows), env"},{"k":"scenario","v":"tde","l":"Cifrar los datos en disco (TDE)","m":"Datos y copias de seguridad TDE transparent data encryption database encryption key DEK service master key Msg 33111 Cannot find server certificate with thumbprint encryption at rest Msg 15208 ENCRYPTION_IN_PROGRESS percent_complete Msg 15581 copia del certificado TDE contra la apertura de un disco, un archivo de datos o una copia de seguridad en otro servidor: DMK, certificado, DEK. Se aplica en "},{"k":"scenario","v":"backup","l":"Seguridad de las copias de seguridad (copias cifradas, certificado, permisos)","m":"Datos y copias de seguridad backup encryption cifrado de copias de seguridad WITH ENCRYPTION BACKUP CERTIFICATE permisos de las copias de seguridad Msg 33111 Msg 15151 RESTORE VERIFYONLY key_algorithm Msg 15240 Las copias de seguridad no están cifradas, el directorio de copias está abierto a todos y las contraseñas están en texto plano en los scripts; BACKUP … WITH ENCRYPTION, copia del certificad"},{"k":"scenario","v":"os","l":"Lado del sistema operativo (cuenta de servicio, permisos de archivos, SELinux)","m":"Servidor y versión service account cuenta de servicio LocalSystem gMSA virtual account icacls SELinux usuario mssql NT Service\\MSSQLSERVER mssql-server-selinux unconfined_service_t Configuration Manager Log On El servicio se ejecuta con LocalSystem o con una cuenta de administrador, los directorios de datos/copias están abiertos a todos y SELinux está desactivado; cambiar la cuenta de servicio req"},{"k":"scenario","v":"patch","l":"Parches y versión: ¿estoy en una versión soportada y en la última CU?","m":"Servidor y versión CU cumulative update GDR EOL end of support fuera de soporte CVE actualización de seguridad ProductUpdateLevel ProductUpdateReference Latest updates calendario de parches Versión, nivel de CU y fin del soporte (el de 2016 terminó en julio de 2026); enlaza con la guía de parches CU para aplicarlas. Solo lectura."}]},{"id":"mongo-replset","file":"install/guides/mongo-replset.json","group":"MongoDB","engineId":"mongodb","category":"ha","title":"Instalación de un replica set de MongoDB (3 miembros, keyfile y autorización)","summary":"Crea desde cero un replica set de MongoDB 8.0 (o 7.0) con tres miembros: repositorio oficial, directorio de datos en XFS, caché de WiredTiger dimensionada según la RAM, autenticación entre miembros con keyfile, autorizac","level":"Intermedio","duration":"60–90 min","topology":"3 servidores (1 primary + 2 secondary), número impar de miembros con datos","tags":["mongodb","mongo","replica set","replset","rs.initiate","keyfile","failover","stepdown","write concern","wiredtiger","alta disponibilidad","ha","rhel","ubuntu"],"order":1,"steps":36,"scenarios":[]},{"id":"mongo-sharded","file":"install/guides/mongo-sharded.json","group":"MongoDB","engineId":"mongodb","category":"ha","title":"Instalación de un sharded cluster de MongoDB (config server + shards + mongos)","summary":"Sharded cluster para cuando los datos o la carga de escritura superan un único replica set: config server replica set de tres miembros, dos o tres shards replica set y dos routers mongos; autenticación interna con keyfil","level":"Avanzado","duration":"2–3 horas","topology":"3 config servers + 2×3 miembros de shard + 2 mongos (al menos 11 servidores; menos para pruebas)","tags":["mongodb","sharding","sharded cluster","mongos","config server","shard key","addShard","balancer","hashed","escalado","rhel","ubuntu"],"order":2,"steps":60,"scenarios":[]},{"id":"mongo-upgrade","file":"install/guides/mongo-upgrade.json","group":"MongoDB","engineId":"mongodb","category":"upgrade","title":"Rolling upgrade de MongoDB (7.0 → 8.0): replica set y sharded cluster","summary":"Actualiza a 8.0 sin interrupción (rolling) un replica set o un sharded cluster de MongoDB 7.0 en funcionamiento: comprobaciones previas (versión, FCV, estado de los miembros, compatibilidad de drivers), copia de segurida","level":"Avanzado","duration":"60–120 min (independiente del volumen de datos; unos minutos por miembro)","topology":"Replica set (3+ miembros) o sharded cluster (config server + shards + mongos)","tags":["mongodb","upgrade","actualización","rolling upgrade","8.0","7.0","featureCompatibilityVersion","fcv","stepdown","replica set","sharded","balancer","config server","mongos","fcv","featureCompatibilityVersion","rolling upgrade"],"order":3,"steps":30,"scenarios":[]},{"id":"mongo-backup","file":"install/guides/mongo-backup.json","group":"MongoDB","engineId":"mongodb","category":"backup","title":"Copia de seguridad y restauración de MongoDB (mongodump, mongorestore, snapshot)","summary":"Configura la rutina de copias de seguridad de un MongoDB instalado y describe, escenario por escenario, la restauración desde copia de seguridad. Copia de seguridad: mongodump de una sola collection, una base de datos o ","level":"Intermedio","duration":"45–90 min","topology":"Servidor de copias de seguridad o un miembro + replica set / instancia única / sharded cluster (mongos)","tags":["mongodb","mongo","copia de seguridad","backup","restore","restauración","mongodump","mongorestore","oplog","oplogReplay","nsFrom","nsTo","mongoexport","mongoimport","fsyncLock","lvm snapshot","systemd timer","cron","retención","balancer","activeWindow","config server","cron","systemd timer","flock","oplog","escenarios de restauración","point in time recovery","pitr","oplogLimit","nsInclude","dryRun","restauración sharded","clonado","collection eliminada","borrado por error"],"order":15,"steps":20,"scenarios":[{"k":"scenario","v":"setup","l":"Configurar las copias de seguridad: mongodump, programación y restauración de prueba","m":"Copia de seguridad mongodump configurar copia de seguridad backup setup systemd timer cron retención mongorestore backup user backup.yaml Failed: can't create session oplog overflow mongodump was unable to capture all new oplog entries copias de seguridad Usuario de copia de seguridad, archivo de credenciales, mongodump manual y programado, restauración de prueba con otro nombre; snapshot y mongoe"},{"k":"scenario","v":"r_dry","l":"¿Se puede restaurar realmente la copia de seguridad? (--dryRun y ensayo en una instancia temporal)","m":"Ensayo y comprobaciones dryRun --dryRun restore test ensayo de restauración verificación de copia de seguridad backup verify gzip: invalid compressed data unexpected EOF archive is corrupt archivo dañado gzip -t se restaura la copia de seguridad Sin tocar los datos en producción: integridad del archivo, namespaces y oplog que contiene y, opcionalmente, restauración completa en un mongod temporal c"},{"k":"scenario","v":"r_full","l":"Restaurar todos los datos desde la copia de seguridad (restauración completa, --drop)","m":"Restauración full restore restauración completa restauración completa de la base de datos --drop mongorestore --drop pérdida de datos data loss ransomware rescate todos los datos borrados dropDatabase no oplog file to replay oplogReplay applyOps not authorized … applyOps admin.tempusers Los datos se han perdido o dañado en gran parte y el deployment volverá al momento en que se tomó la copia de se"},{"k":"scenario","v":"r_ns","l":"Recuperar una sola base de datos o collection (--nsInclude, con otro nombre o en su lugar)","m":"Restauración --nsInclude --nsFrom --nsTo una sola collection single collection restore collection borrada collection dropped drop collection documentos eliminados deleted documents deleteMany namespace restore E11000 duplicate key error $merge orders_restored restaurar una sola base de datos Se eliminó o dañó una collection/base de datos y el resto está intacto: solo ese namespace se trae de la co"},{"k":"scenario","v":"r_pitr","l":"Volver a un momento concreto: copia de seguridad + oplog (drop accidental, update/delete erróneo)","m":"Recuperación a un momento dado point in time PITR recuperación a un momento dado --oplogReplay --oplogLimit oplog replay oplog.rs borrado por error accidental drop update erróneo wrong update antes del drop Timestamp oplog sobrescrito rollback to time volver a una hora concreta La operación errónea ocurrió después de la copia de seguridad y también se necesitan las escrituras hasta ese momento: se"},{"k":"scenario","v":"r_other","l":"Restaurar la copia de seguridad en otro cluster / servidor (clonado, entorno de pruebas, migración)","m":"Migración y clonado clone clonado restaurar en otro servidor restore to another cluster migration migración de servidor entorno de pruebas refresh nsExclude admin.system.users Invalid feature compatibility version cannot use --oplogReplay with excludes specified copia al entorno de pruebas No se toca el deployment de origen: la copia de seguridad se restaura en otro replica set o instancia (copia "},{"k":"scenario","v":"r_shard","l":"Restaurar un sharded cluster: límites de mongodump y método correcto","m":"Migración y clonado restauración sharded sharded cluster restore mongos restore shard key shardCollection config server restore balancer inconsistent copia de seguridad inconsistente PBM Percona Backup Ops Manager --oplog mode only supported on full dumps copia de seguridad a través de mongos La topología respaldada es un sharded cluster: restauración lógica a través de mongos (recreando las shard"}]},{"id":"mongo-ops","file":"install/guides/mongo-ops.json","group":"MongoDB","engineId":"mongodb","category":"ops","title":"Operación diaria y escenarios de fallo de MongoDB (replica set y sharded cluster)","summary":"No es una instalación: runbook por escenarios para administrar a diario un replica set y un sharded cluster de MongoDB ya instalados y recuperarlos cuando fallan. Comprobación de estado, alta/baja de miembros, miembros p","level":"Avanzado","duration":"5 min (comprobación) – varias horas (initial sync, drain, reshard)","topology":"Replica set (3+ miembros) o sharded cluster (config server + replica sets de shard + mongos)","tags":["mongodb","mongo","replica set","sharded cluster","rs.status","rs.add","rs.remove","rs.reconfig","force reconfig","stepDown","priority","hidden","delayed","secondaryDelaySecs","arbiter","resync","initial sync","too stale","oplog","replSetResizeOplog","FCV","setFeatureCompatibilityVersion","addShard","removeShard","movePrimary","balancer","activeWindow","jumbo","chunk","reshardCollection","shard key","operación","fallo"],"order":20,"steps":3,"scenarios":[{"k":"scenario","v":"health","l":"Comprobación de estado: miembros, retraso, ventana del oplog (shards y balancer en sharded)","m":"Supervisión y comprobaciones rs.status rs.status() replication lag retraso replLag printSecondaryReplicationInfo sh.status health estado NotYetInitialized RECOVERING STARTUP2 UNKNOWN (not reachable/healthy) DOWN ReplicaSetNoPrimary Server selection timed out lastHeartbeatMessage Solo lectura: estado de los miembros, retraso de replicación, ventana del oplog, configuración; en un sharded cluster, l"},{"k":"scenario","v":"add","l":"Añadir un miembro nuevo al replica set (initial sync)","m":"Gestión de miembros rs.add añadir miembro add member initial sync nuevo secondary newlyAdded STARTUP2 scale out rs.add() Quorum check failed Found two member configurations with same host field votes: 0 Se ha preparado un servidor nuevo (misma versión, keyfile, replSetName); se añade con priority 0 / votes 0 y recibe voto y priority cuando termina el initial sync y pasa a SECONDARY. Sin downtime."},{"k":"scenario","v":"remove","l":"Quitar un miembro del replica set","m":"Gestión de miembros rs.remove quitar miembro remove member decommission retirar servidor REMOVED Our replica set config is invalid or we are not a member of it rs.remove() El servidor se retira o se ha perdido de forma permanente: el miembro se elimina de la configuración y se comprueba que los votos restantes mantienen la mayoría. Si es el primary, primero stepDown."},{"k":"scenario","v":"member_cfg","l":"Cambiar el rol de un miembro: priority, hidden, delayed (diferido)","m":"Gestión de miembros priority hidden delayed secondaryDelaySecs slaveDelay miembro oculto miembro diferido votes rs.reconfig priority must be 0 when hidden=true only one voting member can be added or removed at a time rs.reconfig() Cambiar la prioridad de un miembro para ser primary, convertirlo en miembro oculto (hidden) para informes/backup o en miembro diferido (delayed) como protección frente a"},{"k":"scenario","v":"arbiter","l":"Sustituir el arbiter por un miembro con datos (PSA → PSS)","m":"Gestión de miembros arbiter arbiterOnly PSA rs.addArb majority write concern cache pressure w: majority en espera Rejecting reconfig where the new config has a PSA topology implicit default write concern majority commit point Replica set con primary + secondary + arbiter: cuando cae un miembro con datos, las escrituras majority y la presión sobre la caché se convierten en un problema. Se añade un "},{"k":"scenario","v":"resync","l":"Resincronizar un miembro rezagado o dañado (resync)","m":"Fallo y recuperación too stale too stale to catch up RS102 resync resincronizar initial sync RECOVERING WiredTiger error WT_PANIC fichero de datos dañado data corruption OplogStartMissing maintenance mode InitialSyncFailure fassert checksum mismatch El miembro está bloqueado en RECOVERING e indica 'too stale to catch up', o sus ficheros de datos están dañados: se vacía el directorio de datos del m"},{"k":"scenario","v":"force","l":"Pérdida de la mayoría: reconfigurar a la fuerza con los miembros restantes (force reconfig)","m":"Fallo y recuperación force reconfig force: true no primary sin primary pérdida de la mayoría majority lost NotWritablePrimary not master no replset config split brain DR site pérdida de sitio ReplicaSetNoPrimary Server selection timed out rs.reconfig force La mayoría de los miembros se ha perdido de forma permanente (pérdida de sitio/servidor) y los restantes no pueden elegir primary: rs.reconfig("},{"k":"scenario","v":"stepdown","l":"Cambio planificado de primary (rs.stepDown, hacer primary a un miembro concreto)","m":"Mantenimiento stepDown rs.stepDown replSetStepDown cambiar primary switchover mantenimiento maintenance rs.freeze No electable secondaries caught up InterruptedDueToReplStateChange PrimarySteppedDown cambio de primary Hay que hacer mantenimiento/aplicar un patch en el servidor primary: el primary se retira y un secondary al día pasa a primary. La aplicación puede recibir errores de escritura duran"},{"k":"scenario","v":"oplog","l":"Cambiar el tamaño del oplog (replSetResizeOplog)","m":"Mantenimiento oplog oplog window ventana del oplog replSetResizeOplog oplogSizeMB oplogMinRetentionHours too stale oplog overflow rs.printReplicationInfo getReplicationInfo log length start to end La ventana del oplog es corta: los secondary quedan 'too stale' tras un mantenimiento, el backup no alcanza el oplog o no basta para una recuperación a un punto en el tiempo. Se amplía en cada miembro si"},{"k":"scenario","v":"fcv","l":"Comprobar y completar la Feature Compatibility Version (FCV)","m":"Mantenimiento FCV featureCompatibilityVersion setFeatureCompatibilityVersion Invalid feature compatibility version IncompatibleServerVersion después del upgrade UPGRADE PROBLEM requires confirm: true getParameter featureCompatibilityVersion Tras un upgrade, la FCV se quedó en la versión anterior o hay diferencias de versión entre miembros: se comprueban la versión del binario y la FCV de todos los"},{"k":"scenario","v":"addshard","l":"Añadir un shard al cluster (sh.addShard)","m":"Sharded cluster addShard sh.addShard añadir shard add shard scale out nuevo shard shardAdded is not running with --shardsvr in use by another shard La capacidad no basta: se ha montado un nuevo replica set de shard (shardsvr) y se añade al cluster; el balancer mueve los chunks de forma gradual."},{"k":"scenario","v":"rmshard","l":"Quitar un shard (removeShard: drain y movePrimary)","m":"Sharded cluster removeShard remove shard quitar shard drain draining movePrimary dbsToMove jumboChunks moveCollection collectionsToMove ShardNotFound draining started successfully Un shard se retira: sus chunks se vacían hacia los demás shards (drain), las bases de datos de las que es primary shard se mueven (movePrimary) y después se elimina del cluster."},{"k":"scenario","v":"balancer","l":"Balancer: ventana horaria, detener / iniciar, desactivar por colección","m":"Sharded cluster balancer activeWindow stopBalancer startBalancer sh.stopBalancer disableBalancing ventana del balancer chunk migration moveChunk isBalancerRunning getBalancerState Balancer timed out Las migraciones de chunks generan carga en horas punta, deben detenerse para un mantenimiento/backup o no se quiere equilibrar una colección concreta."},{"k":"scenario","v":"chunks","l":"Distribución desequilibrada o jumbo chunks (diagnóstico y solución)","m":"Sharded cluster jumbo jumbo chunk chunk too big ChunkTooBig desequilibrado imbalance hot shard balancerCollectionStatus clearJumboFlag moveChunk failed getShardDistribution low cardinality splitFind Cannot split on the boundary jumboChunks Un shard está mucho más lleno que los demás, el balancer no mueve datos, hay chunks marcados como 'jumbo' o moveChunk falla: se analizan la distribución, los ju"},{"k":"scenario","v":"reshard","l":"Cambiar la shard key (reshardCollection)","m":"Sharded cluster reshardCollection reshard cambiar shard key change shard key refineCollectionShardKey abortReshardCollection commitReshardCollection forceRedistribution ReshardCollectionInProgress ReshardCollectionAborted unshardCollection La shard key actual provoca un shard caliente, jumbo chunks o consultas scatter-gather: la colección se redistribuye en línea con la nueva shard key (5.0+). Al "},{"k":"mcfg","v":"priority","l":"Cambiar priority (preferencia para ser primary)","m":"Cambia la preferencia del miembro para ser primary (p. ej. preferir el miembro del centro de datos principal, poner a 0 el miembro de DR). Puede provocar una elección; puede haber una breve interrupción de escrituras."},{"k":"mcfg","v":"hidden","l":"Miembro oculto (hidden): invisible para la aplicación, para backup/informes","m":"El miembro es invisible para las aplicaciones (no recibe lecturas); se reserva para informes, analítica o backup. Pasa a priority 0 y no puede ser primary."},{"k":"mcfg","v":"delayed","l":"Miembro diferido (delayed): protección frente a borrados accidentales","m":"El miembro aplica el oplog con el retraso indicado; los datos borrados por error siguen en él durante ese tiempo. Pasa a hidden, priority 0 y votes 0."},{"k":"mcfg","v":"normal","l":"Volver a la normalidad (priority 1, visible, sin retraso, con voto)","m":"Deshace el rol anterior: priority 1, visible, sin retraso, con voto."},{"k":"bal_op","v":"window","l":"Definir una ventana horaria (activeWindow)","m":"Las migraciones solo se hacen en un horario concreto (p. ej. de noche); no generan carga durante el día."},{"k":"bal_op","v":"unwindow","l":"Quitar la ventana horaria (que funcione siempre)","m":"Se elimina la ventana horaria; el balancer migra a cualquier hora cuando lo necesita (p. ej. para equilibrar rápido tras añadir un shard)."},{"k":"bal_op","v":"stop","l":"Detener el balancer (mantenimiento/backup)","m":"Antes de un backup, upgrade o mantenimiento: la migración en curso termina y no empieza ninguna nueva. Al terminar, vuelva a iniciarlo sin falta."},{"k":"bal_op","v":"start","l":"Iniciar el balancer","m":"Activa el balancer detenido; el equilibrado continúa donde se quedó."},{"k":"bal_op","v":"coll_off","l":"Desactivar el equilibrado en una colección","m":"Solo los chunks de una colección dejan de moverse (ubicación deliberada, colección problemática). Las demás se siguen equilibrando."},{"k":"bal_op","v":"coll_on","l":"Activar el equilibrado en una colección","m":"Vuelve a activar el equilibrado desactivado para la colección."}]},{"id":"mongo-security","file":"install/guides/mongo-security.json","group":"MongoDB","engineId":"mongodb","category":"security","title":"Bastionado de seguridad de MongoDB (autorización, TLS, roles, auditoría)","summary":"Runbook por escenarios para bastionar un MongoDB en ejecución (servidor independiente, replica set o sharded cluster) sin tiempo de inactividad: auditoría de seguridad de solo lectura, autorización rolling con keyFile + ","level":"Avanzado","duration":"10 min (auditoría) – 2 horas (bastionado completo)","topology":"Servidor independiente, replica set de 3+ miembros o sharded cluster","tags":["seguridad mongodb","mongodb security","hardening","bastionado","CIS","authorization","keyFile","transitionToAuth","TLS","requireTLS","x.509","SCRAM-SHA-256","custom role","least privilege","bindIp","audit log","auditLog","encryption at rest","LUKS","Percona"],"order":20,"steps":8,"scenarios":[{"k":"op","v":"audit","l":"Auditoría del estado de seguridad (solo lectura)","m":"Auditoría auditoría de seguridad security audit security checklist CIS MongoDB hardening bastionado Access control is not enabled for the database auth deshabilitado bindIp 0.0.0.0 exposed mongodb mongodb expuesto a internet ransom ransomware getCmdLineOpts security checklist MongoDB informe de auditoría No cambia nada. El acceso sin autenticación, las direcciones de escucha, TLS, los mecanismos d"},{"k":"op","v":"all","l":"Bastionado completo (todo en orden)","m":"Auditoría bastionado completo full hardening CIS benchmark proyecto de seguridad hallazgo de pentest hallazgos de auditoría Auditoría → internal auth + autorización (sin tiempo de inactividad con transitionToAuth) → usuarios con privilegios mínimos → SCRAM-SHA-256 → bindIp/firewall → TLS (allowTLS → preferTLS → requireTLS) → auditoría (Enterprise/Percona) → auditoría final. En un replica set se ha"},{"k":"op","v":"auth","l":"Autorización deshabilitada: habilitar la autorización (keyFile, sin tiempo de inactividad)","m":"Identidad y acceso Access control is not enabled for the database Read and write access to data and configuration is unrestricted authorization disabled authorization: enabled auth deshabilitado no authentication transitionToAuth keyFile internal authentication autenticación entre miembros acceso sin autenticación permissions on /etc/mongod.keyfile are too open Authentication failed __system local"},{"k":"op","v":"users","l":"Usuarios con privilegios mínimos y custom roles (aplicación, backup, monitorización)","m":"Identidad y acceso least privilege privilegios mínimos custom role createRole usuario root la aplicación se conecta como root not authorized on Unauthorized command find requires authentication requires authentication readWrite clusterMonitor rol backup createUser grantRolesToUser monitor_user backup_user Las aplicaciones se conectan con root o con una cuenta de administrador compartida. Se crea u"},{"k":"op","v":"scram","l":"Deshabilitar SCRAM-SHA-1, solo SCRAM-SHA-256","m":"Identidad y acceso SCRAM-SHA-1 SCRAM-SHA-256 authenticationMechanisms Received authentication for mechanism SCRAM-SHA-1 which is not enabled Unable to use SCRAM-SHA-256 based authentication for user without any SCRAM-SHA-256 credentials registered MD5 mecanismo Las credenciales de los usuarios antiguos se almacenan con SHA-1, o la auditoría encontró SCRAM-SHA-1 habilitado. Los usuarios se migran a"},{"k":"op","v":"bind","l":"Restringir el acceso de red (bindIp 0.0.0.0, firewall)","m":"Red y cifrado bindIp 0.0.0.0 bindIpAll bind_ip_all 0.0.0.0:27017 puerto 27017 abierto shodan expuesto a internet firewall javascriptEnabled noscripting net.bindIp ufw allow 27017 firewall-cmd 27017 mongod escucha en todas las interfaces (0.0.0.0, `bindIpAll`) o el puerto está abierto a todo el mundo. bindIp se reduce a localhost + la IP interna del servidor, y el firewall solo permite a los miembr"},{"k":"op","v":"tls","l":"Cifrar las conexiones: TLS (allowTLS → preferTLS → requireTLS)","m":"Red y cifrado TLS SSL net.tls.mode requireTLS preferTLS allowTLS conexión sin cifrar cleartext no SSL certificate provided by peer SSL peer certificate validation failed certificate verify failed x.509 x509 clusterAuthMode Hostname/IP does not match certificate's altnames self-signed certificate in certificate chain rotateCertificates allowConnectionsWithoutCertificates El tráfico entre la aplicac"},{"k":"op","v":"auditlog","l":"Habilitar el audit log (Enterprise / Percona)","m":"Rastro de auditoría y protección de datos audit log auditLog rastro de auditoría quién lo borró who dropped Unrecognized option: auditLog unrecognised option '--auditDestination' RGPD PCI DSS registro de logs Debe registrarse quién inició sesión y cuándo, los cambios de usuarios/roles y las operaciones drop. En MongoDB Enterprise y Percona Server for MongoDB se habilita con `auditLog`; Community n"},{"k":"op","v":"ear","l":"Encryption at rest (cifrar los datos en disco)","m":"Rastro de auditoría y protección de datos encryption at rest cifrado de disco enableEncryption encryptionKeyFile KMIP Vault LUKS dm-crypt cifrado de datos Unrecognized option: security.enableEncryption Unable to retrieve key Unable to use encryption at rest cryptsetup luksFormat Si se roba un disco o un backup, los datos no deben poder leerse. En Enterprise/Percona, el cifrado de WiredTiger se hab"}]},{"id":"redis-sentinel","file":"install/guides/redis-sentinel.json","group":"Redis","engineId":"redis","category":"ha","title":"Alta disponibilidad con Redis Sentinel (master + réplicas + 3 Sentinel)","summary":"Monta desde cero una instalación de alta disponibilidad de Redis 7.2 / 8.2 o Valkey en tres servidores: un master, dos réplicas y un Sentinel en cada servidor. Paso a paso: maxmemory según la RAM, persistencia (RDB/AOF),","level":"Intermedio","duration":"45–60 min","topology":"3 servidores: en cada uno Redis (1 master + 2 réplicas) y Sentinel","tags":["redis","valkey","sentinel","réplica","replication","failover","quorum","alta disponibilidad","ha","caché","rhel","ubuntu"],"order":1,"steps":27,"scenarios":[]},{"id":"redis-cluster","file":"install/guides/redis-cluster.json","group":"Redis","engineId":"redis","category":"ha","title":"Instalación de Redis Cluster (3 masters + 3 réplicas)","summary":"Monta desde cero un Redis Cluster (Redis 7.2 / 8.2 o Valkey): los 16384 hash slots se reparten entre tres (o más) masters y cada master tiene una réplica en otro servidor. Paso a paso: disposición con una instancia por s","level":"Intermedio","duration":"45–60 min","topology":"6 instancias: 6 servidores × 1 o 3 servidores × 2","tags":["redis","valkey","cluster","redis cluster","sharding","hash slot","reshard","failover","réplica","escalado","rhel","ubuntu"],"order":2,"steps":33,"scenarios":[]},{"id":"redis-backup","file":"install/guides/redis-backup.json","group":"Redis","engineId":"redis","category":"backup","title":"Backup y restauración de Redis (RDB, AOF, réplica y cluster)","summary":"Configura la rutina de backup de un Redis o Valkey ya instalado (instancia única, master/réplica con Sentinel o Redis Cluster) y explica cómo restaurar desde el backup. Paso a paso: backups RDB (regla save, BGSAVE, LASTS","level":"Intermedio","duration":"30–60 min","topology":"Instancia única, réplica o servidor Redis Cluster (Redis 7.2 / 8.x, Valkey)","tags":["redis","valkey","copia de seguridad","backup","restore","restauración","rdb","dump.rdb","bgsave","lastsave","aof","appendonly","bgrewriteaof","appendonlydir","redis cluster","systemd timer","cron","retention","cron","systemd timer","flock","REDISCLI_AUTH","sentinel"],"order":15,"steps":19,"scenarios":[]},{"id":"redis-ops","file":"install/guides/redis-ops.json","group":"Redis","engineId":"redis","category":"ops","title":"Operación diaria y escenarios de fallo de Redis / Valkey (Cluster y Sentinel)","summary":"No es una instalación: runbook por escenarios para gestionar a diario un Redis Cluster ya instalado o una topología master/replica con Sentinel, y para recuperarlos cuando fallan (Redis 7.2, Redis 8 y Valkey). Cluster: c","level":"Avanzado","duration":"5 min (comprobación) – 1 hora (reshard)","topology":"Redis Cluster (3+ master + replicas) o Sentinel (1 master + replicas + 3 Sentinel)","tags":["redis","valkey","redis cluster","sentinel","redis-cli --cluster","cluster check","add-node","del-node","reshard","rebalance","CLUSTER FAILOVER","FORCE","TAKEOVER","CLUSTER FORGET","cluster fix","SENTINEL FAILOVER","SENTINEL RESET","quorum","CKQUORUM","INFO memory","MEMORY DOCTOR","eviction","maxmemory","bigkeys","memkeys","hotkeys","operación","fallo"],"order":20,"steps":3,"scenarios":[{"k":"scenario","v":"c_health","l":"Comprobación de estado del cluster (--cluster check, CLUSTER INFO / NODES)","m":"Redis Cluster: supervisión cluster_state:fail CLUSTERDOWN CLUSTERDOWN Hash slot not served --cluster check CLUSTER INFO CLUSTER NODES fail? pfail slots not covered Not all 16384 slots are covered [ERR] Nodes don't agree about configuration MOVED ASK CLUSTERDOWN The cluster is down cluster_slots_fail estado del cluster cluster health cluster_state Solo lectura: si todos los slots están cubiertos, s"},{"k":"scenario","v":"c_add","l":"Añadir un nodo al cluster (master o replica)","m":"Redis Cluster: gestión de nodos add-node --cluster add-node añadir nodo add node scale out --cluster-slave --cluster-replica cluster-master-id nuevo master nueva replica Node is not empty. Either the node already knows other nodes New node added correctly aumentar capacidad Hay una nueva instancia preparada (cluster-enabled yes, vacía): se une al cluster como master y recibe slots, o pasa a ser re"},{"k":"scenario","v":"c_remove","l":"Quitar un nodo del cluster (vaciar sus slots + del-node)","m":"Redis Cluster: gestión de nodos del-node --cluster del-node quitar nodo remove node scale in vaciar slots cluster-weight Node … is not empty is not empty! Reshard data away and try again CLUSTER RESET SOFT retirar nodo Se retira un master o una replica: los slots del master se mueven a los demás master y después el nodo se quita del cluster. Sin interrupción; durante la migración se producen redir"},{"k":"scenario","v":"c_reshard","l":"Redistribuir los slots (rebalance / reshard)","m":"Redis Cluster: gestión de nodos rebalance reshard --cluster rebalance --cluster reshard distribución de slots desequilibrado imbalance use-empty-masters MIGRATE importing migrating BUSYKEY IOERR error or timeout reading to target instance has slots in importing state has slots in migrating state Calling MIGRATE No rebalancing needed mover slots Los slots o la memoria están desequilibrados entre lo"},{"k":"scenario","v":"c_failover","l":"Failover manual: convertir una replica en master (CLUSTER FAILOVER, FORCE, TAKEOVER)","m":"Redis Cluster: fallos y mantenimiento CLUSTER FAILOVER failover FORCE TAKEOVER manual failover mantenimiento del master convertir replica en master Manual failover timed out ERR Master is down or failed, please use CLUSTER FAILOVER FORCE ERR You should send CLUSTER FAILOVER to a replica cambiar master Hay que hacer mantenimiento en el servidor del master (planificado, sin pérdida de datos), o el m"},{"k":"scenario","v":"c_replace","l":"Sustituir un master / nodo averiado (FORGET, nueva replica, --cluster fix)","m":"Redis Cluster: fallos y mantenimiento CLUSTER FORGET --cluster fix nodo perdido failed node fail noaddr slots not covered CLUSTERDOWN replace node master averiado handshake ERR Can't forget my master! CLUSTERDOWN Hash slot not served servidor perdido avería de disco El servidor de un nodo se ha perdido de forma permanente: si su replica pasó a master, se hace olvidar el nodo antiguo y se añade una"},{"k":"scenario","v":"s_health","l":"Estado de Sentinel: master, replicas, Sentinel, quórum","m":"Sentinel SENTINEL MASTERS SENTINEL master get-master-addr-by-name CKQUORUM +sdown +odown -odown +switch-master NOQUORUM NOGOODSLAVE estado de sentinel +tilt -failover-abort-not-elected s_down o_down master_link_status:down sentinel health Solo lectura: la dirección del master que ven los Sentinel, las replicas, los demás Sentinel, CKQUORUM y los últimos eventos de failover. Empiece por aquí si no "},{"k":"scenario","v":"s_failover","l":"Failover manual con Sentinel (SENTINEL FAILOVER)","m":"Sentinel SENTINEL FAILOVER sentinel failover switch-master failover planificado INPROG NOGOODSLAVE -failover-abort-no-good-slave NOGOODSLAVE No suitable replica to promote mantenimiento de sentinel Hay que hacer mantenimiento en el servidor del master: Sentinel convierte en master la replica más adecuada y redirige las demás replicas y los clientes al nuevo master. Interrupción de 1–2 s; sin pérdi"},{"k":"scenario","v":"s_replace","l":"Añadir / quitar / sustituir un nodo en una topología Sentinel (SENTINEL RESET)","m":"Sentinel SENTINEL RESET sentinel reset replicaof nueva replica nuevo sentinel añadir sentinel quitar sentinel sigue apareciendo una replica antigua num-other-sentinels known-replica known-sentinel sigue apareciendo un sentinel antiguo Se ha retirado de forma permanente o se ha añadido una replica o un Sentinel: la nueva replica se conecta con REPLICAOF y los registros obsoletos de los Sentinel se "},{"k":"scenario","v":"s_quorum","l":"Cambiar el quórum y los parámetros de Sentinel (SENTINEL SET)","m":"Sentinel SENTINEL SET quorum down-after-milliseconds failover-timeout parallel-syncs CKQUORUM NOQUORUM failover demasiado tarde failover innecesario Ha cambiado el número de Sentinel, o el failover se dispara demasiado tarde/demasiado pronto: se cambian quorum, down-after-milliseconds, failover-timeout y parallel-syncs en todos los Sentinel."},{"k":"scenario","v":"memory","l":"Presión de memoria: diagnóstico de uso, fragmentación y eviction","m":"Memoria y claves OOM command not allowed when used memory > 'maxmemory' OOM maxmemory used_memory evicted_keys eviction maxmemory-policy MEMORY DOCTOR mem_fragmentation_ratio fragmentación activedefrag client-output-buffer-limit memoria llena Out of memory: Killed process oom-killer used_memory_rss mem_clients_normal omem used_memory se acerca a maxmemory, errores 'OOM command not allowed', la evi"},{"k":"scenario","v":"bigkeys","l":"Encontrar claves grandes y calientes (--bigkeys, --memkeys, --hotkeys)","m":"Memoria y claves --bigkeys --memkeys --hotkeys big key clave grande hot key clave caliente MEMORY USAGE UNLINK slowlog latency SCAN HGETALL KEYS * --keystats picos de latencia latency spike La memoria o la CPU de un nodo es más alta que la de los demás, hay picos de latencia, DEL tarda mucho: se localizan las claves más grandes y más accedidas y se eliminan de forma segura (UNLINK)."},{"k":"add_role","v":"master","l":"Master (recibe slots: rebalance después de añadirlo)","m":"Se une como master vacío y después recibe slots mediante un rebalance. Aumenta la capacidad; también conviene añadir una replica para el nuevo master."},{"k":"add_role","v":"replica","l":"Replica (copia de un master)","m":"Pasa a ser copia de un master y toma el relevo si el master falla. Por la red pasa un volumen de datos igual a la memoria del master (sincronización completa)."},{"k":"rs_mode","v":"rebalance","l":"Rebalance: repartir los slots por igual entre los master (incluidos los master vacíos)","m":"Todos los master reciben la misma parte (incluido un master nuevo sin slots). Primero se muestra una simulación. Es la operación más habitual tras añadir un master nuevo."},{"k":"rs_mode","v":"move","l":"Reshard: mover un número determinado de slots de un master a otro","m":"Se mueve un número determinado de slots de un master a otro: para aliviar un master caliente/grande o para vaciar un master de forma gradual."},{"k":"fo_mode","v":"default","l":"Planificado (por defecto): se sincroniza con el master y toma el relevo, sin pérdida de datos","m":"El master debe estar en funcionamiento."},{"k":"fo_mode","v":"force","l":"FORCE: el master no es accesible, tomar el relevo sin esperar a la sincronización","m":"Pueden perderse las últimas escrituras del master; la mayoría de los master sigue teniendo que votar."},{"k":"fo_mode","v":"takeover","l":"TAKEOVER: también falta la mayoría de los master, tomar el relevo sin votación","m":"Último recurso: riesgo de split brain y de colisión de epoch."},{"k":"srep_op","v":"add_replica","l":"Añadir una nueva replica","m":"El Redis del nuevo servidor se conecta al master actual; los Sentinel lo descubren por sí mismos."},{"k":"srep_op","v":"rm_replica","l":"Quitar una replica de forma permanente","m":"Se detiene la replica y se limpia su registro en todos los Sentinel."},{"k":"srep_op","v":"add_sentinel","l":"Añadir un nuevo Sentinel","m":"Se añade un Sentinel en un servidor nuevo (el número total de Sentinel debe seguir siendo impar: 3 → 5)."},{"k":"srep_op","v":"rm_sentinel","l":"Quitar un Sentinel de forma permanente","m":"Se detiene el Sentinel y se limpia su registro en los demás; después se revisa el quórum."}]},{"id":"redis-security","file":"install/guides/redis-security.json","group":"Redis","engineId":"redis","category":"security","title":"Endurecimiento de la seguridad de Redis / Valkey (ACL, TLS, comandos peligrosos)","summary":"No es una guía de instalación ni de operación diaria: es un runbook por escenarios para endurecer sin interrupción un Redis o Valkey en funcionamiento (un solo servidor, réplica + Sentinel o Cluster). Auditoría de seguri","level":"Avanzado","duration":"5 min (auditoría) – 90 min (endurecimiento completo)","topology":"Un solo servidor, master + réplica + Sentinel o Redis Cluster","tags":["seguridad redis","redis security","valkey","hardening","endurecimiento","ACL","ACL SETUSER","aclfile","requirepass","masterauth","protected-mode","bind","rename-command","enable-protected-configs","TLS","tls-replication","contraseña sentinel","CIS"],"order":20,"steps":7,"scenarios":[{"k":"op","v":"audit","l":"Auditoría del estado de seguridad (solo lectura)","m":"Auditoría auditoría de seguridad redis redis security audit CIS Redis hardening endurecimiento redis expuesto exposed redis unauthorized redis redis sin contraseña -NOAUTH crackit redis cryptominer kinsing NOAUTH Authentication required comprobación de seguridad redis redis pentest No cambia nada. El acceso sin contraseña, el usuario default, las direcciones en escucha, protected-mode, los comando"},{"k":"op","v":"all","l":"Endurecimiento completo (todo en orden)","m":"Auditoría endurecimiento completo full hardening proyecto de seguridad redis hallazgo de pentest Auditoría → usuarios ACL (administrador, aplicación, replicación, Sentinel) y desactivación del usuario default → bind/protected-mode/firewall → comandos peligrosos y protected configs → TLS → permisos de archivos → auditoría final. En réplicas/Sentinel, de forma rolling; los pasos de riesgo piden conf"},{"k":"op","v":"acl","l":"Usuarios ACL y desactivación del usuario default (Redis 6+)","m":"Identidad y privilegios ACL ACL SETUSER aclfile default user usuario default NOPERM WRONGPASS invalid username-password pair -NOAUTH Authentication required user default on nopass masteruser least privilege mínimo privilegio NOPERM this user has no permissions to run the NOPERM this user has no permissions to access one of the keys used as arguments ACL LOG contraseña por usuario ACL SAVE Todo el "},{"k":"op","v":"auth","l":"Redis sin contraseña: requirepass / masterauth (medida rápida)","m":"Identidad y privilegios requirepass masterauth auth-pass redis sin contraseña no password MASTER aborted replication with an error: NOAUTH Unable to AUTH to MASTER -NOAUTH ERR AUTH <password> called without any password configured for the default user Client sent AUTH, but no password is set definir contraseña No hay ninguna contraseña y no hay tiempo para pasar a ACL. Se define en caliente una co"},{"k":"op","v":"net","l":"Restringir el acceso de red (bind 0.0.0.0, protected-mode, firewall)","m":"Red y cifrado bind 0.0.0.0 protected-mode no DENIED Redis is running in protected mode 0.0.0.0:6379 puerto 6379 abierto shodan firewall DENIED Redis is running in protected mode because protected mode is enabled and no password is set for the default user *:6379 Could not create server TCP listening socket Redis escucha en todas las interfaces o funciona con `protected-mode no`. bind se reduce a l"},{"k":"op","v":"cmds","l":"Comandos peligrosos y abuso de CONFIG (FLUSHALL, CONFIG, DEBUG, MODULE)","m":"Red y cifrado CONFIG SET dir dbfilename authorized_keys crontab FLUSHALL FLUSHDB DEBUG MODULE LOAD enable-protected-configs enable-debug-command enable-module-command rename-command @dangerous ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config ERR DEBUG command not allowed CONFIG SET dbfilename rename-command CONFIG módulo malicioso exploit SLAVEOF Frente al ab"},{"k":"op","v":"tls","l":"Cifrar las conexiones: TLS (clientes, replicación, Sentinel, cluster bus)","m":"Red y cifrado TLS SSL tls-port tls-replication tls-cluster tls-auth-clients conexión sin cifrar Error accepting a client connection: error:0A00010B:SSL routines::wrong version number SSL_connect certificate verify failed rediss:// Connection reset by peer tls-cert-file conexión cifrada encryption in transit El tráfico va sin cifrar. El puerto TLS se abre en caliente junto al puerto actual, la repl"},{"k":"op","v":"perms","l":"Permisos de archivos y usuario de ejecución","m":"Rastros y archivos permisos de redis.conf world readable chmod dump.rdb appendonly se ejecuta como root running as root permisos de archivos Permission denied 640 User=redis redis root redis.conf (contiene contraseñas), el archivo ACL, los archivos RDB/AOF y de log son legibles por otros usuarios, o Redis se ejecuta como root. Los permisos se restringen a 640/750 y la propiedad pasa al usuario del"}]},{"id":"es-cluster","file":"install/guides/es-cluster.json","group":"Elasticsearch","engineId":"elasticsearch","category":"install","title":"Instalación de un cluster de Elasticsearch (3+ nodos, TLS, Kibana)","summary":"Crea desde cero un cluster de Elasticsearch 8.x / 9.x de 3 o más nodos: repositorio de Elastic, heap según la RAM, bloqueo de memoria, vm.max_map_count, seguridad automática en el primer nodo (TLS + contraseña de elastic","level":"Intermedio","duration":"60–90 min","topology":"3+ nodos de Elasticsearch (+ servidor de Kibana opcional)","tags":["elasticsearch","elastic","kibana","cluster","clúster","instalación","install","enrollment","tls","heap","snapshot","rhel","ubuntu"],"order":1,"steps":54,"scenarios":[]},{"id":"es-rolling-upgrade","file":"install/guides/es-rolling-upgrade.json","group":"Elasticsearch","engineId":"elasticsearch","category":"upgrade","title":"Rolling upgrade de Elasticsearch (minor de 8.x o 8.19 → 9.x)","summary":"No es una instalación: lleva un cluster Elasticsearch en funcionamiento a una versión nueva sin interrupción, actualizando los nodos uno a uno (minor dentro de 8.x o la versión principal 8.19 → 9.x). Paso a paso: comprob","level":"Avanzado","duration":"15–30 min por nodo","topology":"Cluster existente de 3+ nodos (+ Kibana)","tags":["elasticsearch","upgrade","rolling upgrade","minor","major","9.x","8.19","kibana","upgrade assistant","deprecation","snapshot","rollback","rolling upgrade","node roles","master-eligible","deprecation api","versionlock"],"order":2,"steps":34,"scenarios":[{"k":"path","v":"minor","l":"Dentro de la misma versión principal (p. ej. 8.18 → 8.19, 9.1 → 9.2)","m":"Dentro de la misma versión principal (8.18 → 8.19, 9.1 → 9.2): el repositorio no cambia; la comprobación de deprecation se hace igualmente. Se admite saltar versiones minor intermedias."},{"k":"path","v":"major","l":"Versión principal: 8.19.x → 9.x","m":"El cluster de origen debe estar antes en 8.19.x (último patch). Las comprobaciones previas incluyen los pasos de Upgrade Assistant / deprecation API y system feature migration."}]},{"id":"es-snapshot","file":"install/guides/es-snapshot.json","group":"Elasticsearch","engineId":"elasticsearch","category":"backup","title":"Snapshot y restore de Elasticsearch (repository fs / S3, SLM)","summary":"Configura los snapshots (copias de seguridad) de un cluster Elasticsearch 8.x / 9.x ya instalado y explica la recuperación: repository en sistema de archivos compartido (NFS + path.repo) o S3 (keystore), activación con r","level":"Intermedio","duration":"45–90 min","topology":"Cluster Elasticsearch 8.x / 9.x (1–N nodos) + directorio compartido (NFS) o almacenamiento de objetos compatible con S3","tags":["elasticsearch","snapshot","restore","copia de seguridad","backup","restauración","repository","path.repo","nfs","s3","keystore","slm","snapshot lifecycle","retention","rename_pattern","feature_states","readonly repository","migration","slm","snapshot lifecycle","quartz cron","utc","netrc"],"order":15,"steps":33,"scenarios":[]},{"id":"es-ops","file":"install/guides/es-ops.json","group":"Elasticsearch","engineId":"elasticsearch","category":"ops","title":"Operación diaria y escenarios de fallo de Elasticsearch (8.x / 9.x)","summary":"No es una instalación: runbook por escenarios para administrar a diario un cluster Elasticsearch 8.x / 9.x ya instalado y recuperarlo cuando falla. Estado y allocation explain, resolución de shards sin asignar (disco, fi","level":"Avanzado","duration":"5 min (comprobación) – horas (reindex, vaciado de un nodo)","topology":"Cluster Elasticsearch 8.x / 9.x (1–N nodos), TLS y autenticación activados","tags":["elasticsearch","elastic","cluster health","red","yellow","unassigned","allocation explain","reroute","retry_failed","allocate_stale_primary","watermark","flood stage","read_only_allow_delete","exclude","voting_config_exclusions","enrollment token","reindex","alias","ILM","rollover","shrink","split","snapshot restore","operación","resolución de problemas"],"order":20,"steps":6,"scenarios":[{"k":"scenario","v":"health","l":"Estado del cluster: status, nodos, shards sin asignar, disco","m":"Supervisión y diagnóstico red yellow _cluster/health _cat/shards UNASSIGNED allocation explain _cluster/allocation/explain status red status yellow estado del cluster pending tasks master_not_discovered_exception NoNodeAvailableException circuit_breaking_exception heap comprobación de estado del cluster Solo lectura: green/yellow/red, lista de nodos, shards sin asignar (UNASSIGNED) y su causa (all"},{"k":"scenario","v":"unassigned","l":"Resolver un shard sin asignar (UNASSIGNED) (según explain)","m":"Supervisión y diagnóstico UNASSIGNED unassigned shards NODE_LEFT ALLOCATION_FAILED max_retry shard has exceeded the maximum number of retries retry_failed no_valid_shard_copy cannot allocate because a previous copy of the primary shard existed allocate_stale_primary allocate_empty_primary the node is above the high watermark same_shard awareness filter NO_VALID_SHARD_COPY CLUSTER_RECOVERED REPLICA"},{"k":"scenario","v":"watermark","l":"Ver / cambiar la configuración de disk watermark","m":"Disco y bloqueos watermark disk.watermark low disk watermark high disk watermark flood stage disk watermark disco lleno disk full cluster.routing.allocation.disk cluster.routing.allocation.disk.watermark.high disk usage exceeded umbral de disco Los shards se mueven o no se asignan por la ocupación del disco; hay que cambiar los umbrales de watermark (low / high / flood_stage) de forma temporal o p"},{"k":"scenario","v":"readonly","l":"El índice pasó a solo lectura: quitar el bloque read_only_allow_delete","m":"Disco y bloqueos read_only_allow_delete FORBIDDEN/12/index read-only / allow delete (api) TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark cluster_block_exception index.blocks.read_only_allow_delete flood stage solo lectura read-only blocked by: [FORBIDDEN/12/index read-only / allow delete (api)] index read-only escritura rechazada Las escrituras se rechazan con 'FORBIDDEN/12/index r"},{"k":"scenario","v":"add_node","l":"Añadir un nodo al cluster (enrollment token)","m":"Gestión de nodos añadir nodo add node enrollment token elasticsearch-create-enrollment-token elasticsearch-reconfigure-node scale out join cluster master not discovered master not discovered yet, this node has not previously joined a bootstrapped cluster aumentar capacidad nodo nuevo Se necesita más capacidad: se ha instalado Elasticsearch en un servidor nuevo, que se une al cluster con un enrollm"},{"k":"scenario","v":"remove_node","l":"Retirar un nodo de forma segura (exclude, vaciar, apagar)","m":"Gestión de nodos retirar nodo remove node decommission cluster.routing.allocation.exclude._name exclude voting_config_exclusions relocating vaciar nodo voting configuration retirada de nodo sustitución de servidor Un nodo se retira o se le va a cambiar el hardware: con un allocation exclude sus shards se mueven a los demás nodos, se comprueba que ha quedado vacío, si es master-eligible se le aplic"},{"k":"scenario","v":"reindex","l":"Reindex: cambio sin interrupción a un índice nuevo con alias para un cambio de mapping/configuración","m":"Gestión de índices _reindex reindex cambiar mapping mapper_parsing_exception illegal_argument_exception: mapper alias _aliases cambio sin interrupción zero downtime task version_conflict_engine_exception cannot be changed from type mapper [ cambiar analyzer Hay que cambiar el tipo de un campo del mapping, el analyzer o el número de shards: se crea un índice nuevo, los datos se copian con _reindex "},{"k":"scenario","v":"ilm","l":"Configurar / diagnosticar index lifecycle (ILM) y rollover","m":"Gestión de índices ILM index lifecycle rollover _rollover _ilm/explain is_write_index rollover_alias ilm policy index.lifecycle.name ERROR step illegal_argument_exception: index.lifecycle.rollover_alias rollover_alias does not point to index ILM ERROR el índice no se borra el índice de logs crece Los índices de logs/series temporales crecen o no se borran: se configuran una política ILM (rollover "},{"k":"scenario","v":"resize","l":"Corregir el número de shards: _shrink (reducir) / _split (aumentar)","m":"Gestión de índices _shrink _split shrink split número de shards number_of_shards too many shards exceso de shards oversharding index.blocks.write must be read-only to resize index must have all shards allocated on the same node to shrink index number_of_routing_shards El número de shards del índice se eligió mal: los shards muy pequeños (exceso de shards) se combinan con _shrink, los muy grandes s"},{"k":"scenario","v":"snaprestore","l":"Restaurar un único índice desde un snapshot con un nombre nuevo","m":"Gestión de índices snapshot restore _restore restaurar un solo índice rename_pattern rename_replacement índice borrado deleted index index_not_found_exception cannot restore index because an open index with same name already exists snapshot_missing_exception borrado por error recuperar índice Se borró o se dañó un índice: desde el snapshot repository se restaura solo ese índice con un nombre nuevo"},{"k":"un_fix","v":"retry","l":"Límite de reintentos superado: reintentar (_cluster/reroute?retry_failed)","m":"max_retry shard has exceeded the maximum number of retries ALLOCATION_FAILED explain: 'shard has exceeded the maximum number of retries'. Primero se corrige el error de fondo (disco, mapping, memoria)."},{"k":"un_fix","v":"replicas","l":"Más replicas que nodos: reducir las replicas","m":"same_shard a copy of this shard is already allocated to this node explain: same_shard (no se coloca una copia en el mismo nodo); replicas 0 en un cluster de un solo nodo."},{"k":"un_fix","v":"filter","l":"Allocation filtering / exclude lo impide: quitar el filtro","m":"filter node does not match index setting [index.routing.allocation node matches cluster setting [cluster.routing.allocation.exclude explain: filter decider ('node does not match index setting [index.routing.allocation…]' o un exclude a nivel de cluster)."},{"k":"un_fix","v":"disk","l":"Disk watermark lo impide","m":"disk_threshold the node is above the low watermark the node is above the high watermark explain: disk_threshold decider. Le dirige al escenario de watermark."},{"k":"un_fix","v":"stale","l":"Último recurso: asignar el primary desde una copia antigua (stale)","m":"no_valid_shard_copy all the copies of its data in the cluster are stale or corrupt allocate_stale_primary Se ha perdido la copia actualizada del primary y solo queda una copia antigua: se pierden las últimas escrituras que no estén en esa copia."},{"k":"un_fix","v":"empty","l":"Último recurso extremo: asignar un primary vacío (se pierden todos los datos del shard)","m":"no_valid_shard_copy there are no copies of its data in the cluster allocate_empty_primary No existe ninguna copia; si el índice no se puede restaurar desde un snapshot."},{"k":"wm_op","v":"show","l":"Solo mostrar (uso de disco y umbrales actuales)","m":"Solo lectura: disco por nodo y umbrales vigentes."},{"k":"wm_op","v":"set","l":"Cambiar los umbrales","m":"Subir los umbrales de forma temporal (p. ej. `50gb` de espacio libre en lugar de un porcentaje en un disco grande). Cuando se resuelva el problema, vuelva a los valores predeterminados."},{"k":"wm_op","v":"reset","l":"Volver a los valores predeterminados (85% / 90% / 95%)","m":"Elimina los umbrales modificados anteriormente y vuelve a los valores predeterminados (85% / 90% / 95%)."},{"k":"ilm_op","v":"setup","l":"Política nueva + template + índice inicial (write alias)","m":"Se crean la política (rollover + borrado), el index template y el índice inicial (`-000001`, alias de escritura). Para un flujo de logs nuevo."},{"k":"ilm_op","v":"debug","l":"¿Por qué no avanzan los índices existentes? (explain, retry)","m":"Los índices no hacen rollover o no se borran: `_ilm/explain` muestra el paso que falla; una vez corregida la causa se hace retry."},{"k":"ilm_op","v":"rollover","l":"Hacer rollover manual","m":"Se abre un índice de escritura nuevo sin esperar a las condiciones (tras un cambio de mapping o para un índice sobredimensionado)."},{"k":"rs_kind","v":"shrink","l":"_shrink: reducir el número de shards (el destino debe ser divisor del actual)","m":""},{"k":"rs_kind","v":"split","l":"_split: aumentar el número de shards (el destino debe ser múltiplo del actual)","m":""}]},{"id":"es-security","file":"install/guides/es-security.json","group":"Elasticsearch","engineId":"elasticsearch","category":"security","title":"Endurecimiento de seguridad de Elasticsearch (security, TLS, roles, audit)","summary":"No es una instalación ni una guía de operación diaria: runbook por escenarios para endurecer un cluster Elasticsearch en funcionamiento. Auditoría de seguridad de solo lectura, activación de la security desactivada junto","level":"Avanzado","duration":"10 min (auditoría) – 2 horas (endurecimiento completo)","topology":"Cluster Elasticsearch de 1–12 nodos (+ Kibana)","tags":["seguridad elasticsearch","elasticsearch security","hardening","endurecimiento","xpack.security","TLS","elasticsearch-certutil","elasticsearch-users","API key","role mapping","elastic superuser","anonymous","audit logging","IP filtering","kibana_system","service account token","CIS"],"order":20,"steps":4,"scenarios":[{"k":"op","v":"audit","l":"Auditoría del estado de seguridad (solo lectura)","m":"Auditoría auditoría de seguridad elasticsearch elasticsearch security audit CIS Elasticsearch hardening endurecimiento elasticsearch expuesto exposed elasticsearch xpack.security.enabled missing authentication credentials for REST request security_exception comprobación de seguridad elasticsearch elasticsearch pentest RIESGO No cambia nada. Se informa como `OK` / `AVISO` / `RIESGO` si la security "},{"k":"op","v":"all","l":"Endurecimiento completo (todo en orden)","m":"Auditoría endurecimiento completo full hardening proyecto de seguridad elastic hallazgo de pentest Auditoría → security + TLS de transport/HTTP (si está desactivada; reinicio completo del cluster) → usuarios/roles/API key, desactivar anonymous, retirar elastic del uso diario → red y filtrado por IP → audit logging (si la licencia lo permite) → acceso al snapshot repository → conexión de Kibana → a"},{"k":"op","v":"enable","l":"Security desactivada: activar la seguridad y transport TLS","m":"Identidad y cifrado xpack.security.enabled: false security disabled acceso sin autenticación Elasticsearch built-in security features are not enabled Transport SSL must be enabled if security is enabled invalid configuration for xpack.security.transport.ssl seguridad actualización 7.x Security is disabled xpack.security.transport.ssl sin autenticación Cluster con `xpack.security.enabled: false`, o"},{"k":"op","v":"httptls","l":"Pasar el tráfico HTTP (REST) a TLS","m":"Identidad y cifrado http.ssl xpack.security.http.ssl.enabled received plaintext http traffic on an https channel plaintext http https 9200 elasticsearch-certutil http SSLHandshakeException curl: (52) Empty reply from server http:// 9200 contraseña en claro en la red La security está activada pero el puerto 9200 es HTTP sin cifrar: las contraseñas se pueden leer en la red. Se distribuyen certificad"},{"k":"op","v":"users","l":"Usuarios, roles, API key; elastic y acceso anonymous","m":"Identidad y cifrado usuario elastic elastic superuser superuser role mapping API key api_key elasticsearch-users anonymous xpack.security.authc.anonymous action [indices:data/write/bulk[s]] is unauthorized is unauthorized for user unable to authenticate user action [indices:data/read/search] is unauthorized for user caducidad de api key api key sin caducidad break-glass Las aplicaciones se conecta"},{"k":"op","v":"auditlog","l":"Activar el audit logging","m":"Trazas y acceso audit log xpack.security.audit.enabled audit logging _audit.json registro de auditoría quién borró RGPD PCI DSS xpack.security.audit.logfile.events.include registro de accesos Debe quedar registrado quién accedió a qué índice, los intentos de inicio de sesión fallidos, las denegaciones de autorización y los cambios de ajustes de seguridad. `xpack.security.audit.enabled` (estático, "},{"k":"op","v":"net","l":"Acceso de red y filtrado por IP","m":"Trazas y acceso network.host 0.0.0.0 0.0.0.0:9200 puerto 9200 abierto xpack.security.transport.filter xpack.security.http.filter ip filtering shodan firewall 0.0.0.0:9300 network.host: 0.0.0.0 _site_ IP pública 9200/9300 escuchan en todas las interfaces o están abiertos a todo el mundo. `network.host` se limita a la IP interna y, con el firewall y el filtrado por IP de Elasticsearch, solo se permi"},{"k":"op","v":"snapshot","l":"Proteger el acceso al snapshot repository","m":"Trazas y acceso snapshot repo path.repo repository_exception s3.client access_key readonly repository seguridad de snapshot snapshot_operator Object Lock seguridad de copias El directorio de snapshots es legible por todos, el privilegio para gestionar el repository es demasiado amplio o las claves de acceso a la nube están en elasticsearch.yml. Permisos del directorio, rol de operador de snapshots"},{"k":"op","v":"kibana","l":"Proteger la conexión de Kibana","m":"Trazas y acceso kibana_system elasticsearch.username: elastic contraseña en kibana.yml service account token elasticsearch.serviceAccountToken xpack.encryptedSavedObjects.encryptionKey Kibana server is not ready yet unable to retrieve version information from Elasticsearch nodes value of \"elastic\" is forbidden serviceAccountToken kibana.keystore Kibana se conecta con el usuario `elastic` o sin cif"}]},{"id":"mon-prometheus-grafana","file":"install/guides/mon-prometheus-grafana.json","group":"Monitorización","engineId":null,"category":"monitoring","title":"Monitorización de bases de datos con Prometheus + Grafana","summary":"Instala un servidor de monitorización central (Prometheus, Alertmanager, Grafana) y añade a los servidores monitorizados node_exporter y los exporters de base de datos que elija: PostgreSQL, MySQL, Oracle, SQL Server, Mo","level":"Intermedio","duration":"60–120 min","topology":"1 servidor de monitorización + servidores monitorizados","tags":["prometheus","grafana","alertmanager","node_exporter","postgres_exporter","mysqld_exporter","oracledb_exporter","sql_exporter","mongodb_exporter","redis_exporter","elasticsearch_exporter","monitorización","monitoring","alert","dashboard"],"order":1,"steps":44,"scenarios":[]},{"id":"ora19-install","file":"install/guides/ora19-install.json","group":"Oracle","engineId":"oracle","category":"install","title":"Instalación de Oracle Database 19c de instancia única (EE / SE2)","summary":"Instala Oracle Database 19c desde cero en un único servidor con Oracle Linux / RHEL 7.9, 8 o 9: preparación del sistema operativo (RPM preinstall o manual), directorios OFA, instalación silenciosa (silent) del software j","level":"Intermedio","duration":"90–150 min","topology":"1 servidor","tags":["oracle","19c","instalación","install","runInstaller","dbca","netca","silent","release update","applyRU","opatch","cdb","pdb","hugepages","rman","oracle linux","rhel","asm","ol7","multipath","oracle restart"],"order":1,"steps":38,"scenarios":[]},{"id":"ora23-install","file":"install/guides/ora23-install.json","group":"Oracle","engineId":"oracle","category":"install","title":"Instalación de Oracle AI Database 26ai / 23ai (Linux)","summary":"Instala en un solo servidor Oracle AI Database 26ai (la versión que sustituye a 23ai, con número de versión 23.26.x); usted elige una de las tres vías oficiales: instalación OFA desde el gold image ZIP para producción (r","level":"Intermedio","duration":"45–120 min","topology":"1 servidor","tags":["oracle","26ai","23ai","ai database","instalación","install","rpm","free","gold image","setupDBHome","dbca","cdb","pdb","oracle linux","rhel","asm","oracle restart"],"order":2,"steps":30,"scenarios":[]},{"id":"ora-storage-prep","file":"install/guides/ora-storage-prep.json","group":"Oracle","engineId":"oracle","category":"install","title":"Preparación del almacenamiento para Oracle: multipath, nombres de dispositivo persistentes, discos ASM","summary":"Se aplica una sola vez, antes de instalar Oracle Grid Infrastructure, Oracle Restart o la base de datos: los LUN de la SAN se identifican por su WWID, se configuran alias de DM-Multipath cuando corresponde y los discos A","level":"Intermedio","duration":"30–90 min","topology":"1 servidor, o 2–8 nodos RAC + discos SAN/iSCSI/VM","tags":["oracle","asm","multipath","device-mapper-multipath","dm-multipath","multipath.conf","wwid","udev","regla udev","asmlib","oracleasm","afd","asm filter driver","lvm","xfs","fstab","san","lun","almacenamiento","storage","disco","rac","oracle restart"],"order":3,"steps":10,"scenarios":[]},{"id":"ora-grid-asm","file":"install/guides/ora-grid-asm.json","group":"Oracle","engineId":"oracle","category":"install","title":"Oracle Restart: Grid Infrastructure 19c + ASM (un solo servidor)","summary":"Instalación de Oracle Restart (Grid Infrastructure 19c standalone, HA_CONFIG) y ASM en un solo servidor: usuario grid y grupos de ASM, nombres de disco persistentes (udev, ASMLib o ASM Filter Driver; discos de una sola r","level":"Avanzado","duration":"90–150 min","topology":"1 servidor + discos ASM no compartidos","tags":["oracle","19c","grid infrastructure","oracle restart","asm","gridSetup","HA_CONFIG","udev","asmlib","oracleasm","afd","asm filter driver","srvctl","asmcmd","instalación","multipath","ol7"],"order":3,"steps":30,"scenarios":[]},{"id":"ora19-patch-ru","file":"install/guides/ora19-patch-ru.json","group":"Oracle","engineId":"oracle","category":"patch","title":"Aplicar un Release Update (RU) de Oracle Database 19c","summary":"Release Update trimestral para una base de datos 19c de instancia única u Oracle Restart (Grid Infrastructure + ASM): actualización de OPatch, comprobaciones previas de conflictos y espacio, backup del ORACLE_HOME y guar","level":"Intermedio","duration":"60–120 min (interrupción: in-place 30–60 min, out-of-place 10–20 min)","topology":"1 servidor (instancia única u Oracle Restart); para RAC, la guía de rolling patch","tags":["oracle","19c","parche","release update","ru","opatch","opatchauto","datapatch","ojvm","out-of-place","rollback","restore point","oracle restart","rac","rolling","standby-first","data guard","standby","1265700.1"],"order":10,"steps":25,"scenarios":[]},{"id":"ora19-rac","file":"install/guides/ora19-rac.json","group":"Oracle","engineId":"oracle","category":"ha","title":"Instalación de Oracle RAC 19c (Grid Infrastructure + ASM + base de datos RAC)","summary":"Instala desde cero Oracle Grid Infrastructure 19c (Flex ASM, SCAN) y una base de datos Oracle RAC 19c en dos o más nodos: plan de red y DNS, preparación de los discos compartidos (udev o ASMLib; con multipath o con una s","level":"Avanzado","duration":"4–6 horas","topology":"2–8 nodos + disco compartido (SAN/iSCSI) + SCAN en DNS","tags":["oracle","rac","19c","grid infrastructure","clusterware","asm","scan","gridsetup","dbca","srvctl","crsctl","cluster","alta disponibilidad","udev","asmlib","multipath","ol7","scan","ocr","voting disk"],"order":10,"steps":68,"scenarios":[]},{"id":"ora-rac-patch","file":"install/guides/ora-rac-patch.json","group":"Oracle","engineId":"oracle","category":"patch","title":"Release Update rolling en Oracle RAC 19c (opatchauto)","summary":"Aplica una Release Update trimestral de GI a un cluster RAC 19c de 2–8 nodos en funcionamiento, sin parada (rolling): actualización de OPatch, comprobación de conflictos con opatchauto analyze, backup del OCR y punto de ","level":"Avanzado","duration":"45–90 min por nodo","topology":"RAC 19c existente de 2–8 nodos","tags":["oracle","rac","19c","parche","release update","ru","opatchauto","opatch","datapatch","rolling","grid infrastructure","rollback","scan","servicio","standby-first","srvctl"],"order":11,"steps":38,"scenarios":[]},{"id":"ora-rac-ops","file":"install/guides/ora-rac-ops.json","group":"Oracle","engineId":"oracle","category":"ops","title":"Oracle RAC: operación diaria y escenarios de fallo","summary":"Operación diaria y respuesta a incidencias de un cluster Oracle RAC (Grid Infrastructure 19c) instalado y en funcionamiento: comprobación de estado, diagnóstico de node eviction, parada de un nodo por mantenimiento, para","level":"Avanzado","duration":"15 min – 3 horas (según el escenario)","topology":"RAC existente de 2–8 nodos (Grid Infrastructure 19c; diferencias de 23ai/26ai en las notas)","tags":["oracle","rac","grid infrastructure","clusterware","crsctl","srvctl","olsnodes","cluvfy","addnode","añadir nodo","eliminar nodo","delete node","ocr","olr","voting disk","votedisk","ocrconfig","ocrcheck","scan","vip","oifcfg","interconnect","eviction","ocssd","misscount","service","relocate","application continuity","taf","fan","operación diaria","fallo"],"order":12,"steps":10,"scenarios":[{"k":"scenario","v":"health","l":"Comprobación de estado del cluster (diaria / antes del mantenimiento)","m":"Comprobación y diagnóstico crsctl check cluster crsctl stat res -t cluvfy recurso OFFLINE CRS-4535 CRS-4530 CRS-4639 health check comprobación de estado INTERMEDIATE CRS-4537 CRS-2765 CRS-2878 CRS-4700 CRS-4701 crsctl check crs estado del cluster cluster status recurso caído resource offline orachk tfactl comprobación previa al mantenimiento No modifica nada: se comprueban la pila de Clusterware, "},{"k":"scenario","v":"eviction","l":"Un nodo fue expulsado del cluster o se reinició por sí solo (diagnóstico de eviction)","m":"Comprobación y diagnóstico CRS-1609 CRS-1610 CRS-1611 CRS-1612 CRS-1615 CRS-1625 CRS-1632 CRS-1656 CRS-8011 node eviction evicted node reboot split brain ocssd.trc misscount disktimeout nodo expulsado CRS-1601 CRS-1606 CRS-1607 CRS-1608 CRS-1613 CRS-1614 CRS-8013 ORA-29740 ORA-29770 IPC Send timeout network heartbeat disk heartbeat nodo reiniciado el servidor se reinició solo rebootless restart in"},{"k":"scenario","v":"stopnode","l":"Detener un nodo por mantenimiento y volver a iniciarlo","m":"Iniciar / detener crsctl stop crs crsctl start crs mantenimiento de nodo OS patch reboot CRS-4133 CRS-4123 drain_timeout srvctl stop instance CRS-2673 CRS-2677 CRS-2675 crsctl disable crs crsctl enable crs modo de mantenimiento maintenance kernel update actualización del kernel firmware Selecciónelo si en un nodo se va a aplicar un parche del sistema operativo o se va a hacer mantenimiento del ker"},{"k":"scenario","v":"stopall","l":"Parada y arranque planificados de todo el cluster","m":"Iniciar / detener crsctl stop cluster -all crsctl start cluster -all parada completa cluster shutdown interrupción planificada srvctl stop database CRS-2677 CRS-4133 CRS-4123 crsctl stop crs crsctl start crs mantenimiento de SAN mantenimiento del centro de datos datacenter maintenance power off apagar todos los servidores Selecciónelo cuando todos los nodos deban detenerse a la vez, por ejemplo en"},{"k":"scenario","v":"addnode","l":"Añadir un nodo nuevo al cluster","m":"Añadir / eliminar nodos addnode.sh addNode gridSetup.sh -addNode añadir nodo add node cluvfy stage -pre nodeadd dbca -addInstance INS-06006 PRVG- PRVF- INS-13013 INS-40912 CLSRSC-184 PRVG-11050 PRVF-4664 PRVF-7617 cvuqdisk new node añadir servidor nuevo ampliación del cluster Selecciónelo cuando un servidor nuevo se vaya a incorporar al cluster (aumento de capacidad, renovación de hardware). Sin i"},{"k":"scenario","v":"delnode","l":"Eliminar un nodo del cluster","m":"Añadir / eliminar nodos crsctl delete node deinstall -local dbca -deleteInstance eliminar nodo delete node remove node cluvfy stage -post nodedel crsctl unpin css CRS-4658 CRS-4661 runInstaller -updateNodeList srvctl remove instance decommission retirar servidor del cluster desmontar servidor antiguo Selecciónelo cuando un servidor se vaya a retirar del cluster de forma permanente (devolución, fal"},{"k":"scenario","v":"service","l":"Añadir, modificar, trasladar o eliminar un servicio","m":"Servicios y red srvctl add service srvctl modify service srvctl relocate service srvctl remove service ORA-12514 PRCD-1026 preferred available TAF Application Continuity TAC FAN traslado de servicio PRKO-2017 PRCR-1090 ORA-12514: TNS:listener does not currently know of service srvctl start service srvctl status service failback el servicio no ha vuelto drain_timeout añadir servicio create service "},{"k":"scenario","v":"net","l":"Cambio de SCAN, VIP, puerto del listener o interconnect","m":"Servicios y red srvctl modify scan srvctl modify scan_listener srvctl modify nodeapps srvctl modify listener oifcfg setif oifcfg delif oifcfg getif cambio de IP SCAN IP cambio de VIP interconnect private network ORA-12545 listener port PRCS-1034 PRKO-2310 PRCR-1079 oifcfg HAIP 169.254 cambio de puerto cambio de SCAN VIP change remote_listener ASMNET1LSNR_ASM Selecciónelo si van a cambiar el nombre"},{"k":"scenario","v":"ocr_backup","l":"Realizar el backup de OCR y OLR y comprobar los backups","m":"OCR, OLR y voting ocrconfig -manualbackup ocrconfig -showbackup ocrconfig -local -manualbackup backup de OLR backup de OCR OCR backup ocrcheck ocrdump PROT- ocrconfig -copy ocrconfig -showbackuploc OLR backup dónde está el backup de OCR backup manual de OCR Sin interrupción: backup manual de OCR, backup de OLR en cada nodo y copia de los backups fuera de los discos del cluster. Selecciónelo antes "},{"k":"scenario","v":"ocr_restore","l":"OCR dañado o disk group CRS perdido: restaurar desde el backup","m":"OCR, OLR y voting PROT-602 PROC-26 CRS-4535 CRS-4000 CRS-1606 ocrconfig -restore crsctl start crs -excl -nocrs OCR corruption OCR dañado disk group CRS perdido voting disk perdido ORA-15017 ORA-15063 PROT-35 PROT-19 ORA-15018 ORA-15033 ocrdump OCR restore CRSD no se inicia Logical corruption check failed Selecciónelo si `ocrcheck` muestra daños, si CRSD no se inicia (errores PROT-/PROC-) o si el d"},{"k":"scenario","v":"olr_restore","l":"Clusterware no se inicia en un nodo: OLR dañado","m":"OCR, OLR y voting ocrcheck -local ocrconfig -local -restore OLR dañado OLR corrupt ohasd no se inicia CRS-4124 CRS-4639 PROCL- CRS-4123 ohasd.trc olr.loc ocrconfig -local -showbackup Local registry integrity check failed OHASD no se inicia un nodo no arranca Selecciónelo si, con los demás nodos en funcionamiento, `crsctl start crs` no consigue iniciar OHASD en un solo nodo y `ocrcheck -local` devu"},{"k":"scenario","v":"ocrvote_move","l":"Trasladar los archivos OCR y voting a otro disk group","m":"OCR, OLR y voting crsctl replace votedisk crsctl query css votedisk ocrconfig -add ocrconfig -delete traslado de voting disk votedisk traslado de OCR storage migration ORA-15274 ocrconfig -backuploc asmcmd spmove asmcmd pwmove traslado del disk group CRS traslado de archivos voting OCR move cambio de almacenamiento Sin interrupción: ocrconfig -add/-delete y crsctl replace votedisk. Selecciónelo en"}]},{"id":"ora-asm-ops","file":"install/guides/ora-asm-ops.json","group":"Oracle","engineId":"oracle","category":"ops","title":"Operación diaria y escenarios de fallo de Oracle ASM","summary":"Operación diaria de los disk groups de ASM en un Oracle Restart o RAC ya instalado, y actuación ante fallos: comprobación de capacidad (usable_file_mb), seguimiento del rebalance, añadir/sustituir/eliminar discos, disco ","level":"Avanzado","duration":"10 min – varias horas (según la duración del rebalance)","topology":"Oracle Restart (un solo servidor) o RAC de 2–8 nodos, Grid Infrastructure 19c (diferencias de 23ai/26ai en las notas)","tags":["oracle","asm","disk group","diskgroup","add disk","drop disk","replace disk","rebalance","v$asm_operation","usable_file_mb","disk_repair_time","online disk","offline disk","failgroup","compatible.asm","asmcmd","spbackup","md_backup","asm_diskstring","dsset","udev","asmlib","afd","multipath","rac","oracle restart","operación diaria","fallo"],"order":13,"steps":4,"scenarios":[{"k":"op","v":"capacity","l":"Comprobación de capacidad y salud de los disk groups","m":"Comprobación y monitorización usable_file_mb required_mirror_free_mb asmcmd lsdg disk group lleno ocupación de ASM free_mb capacidad ORA-15041 ORA-01653 ORA-17502 ORA-15041: diskgroup space exhausted diskgroup space exhausted disk group full ASM disk group full usable_file_mb negativo negative usable_file_mb ASM sin espacio asmcmd du No cambia nada: si un disk group está lleno, cuánto espacio qued"},{"k":"op","v":"rebal","l":"Supervisar, acelerar o ralentizar un rebalance","m":"Comprobación y monitorización v$asm_operation gv$asm_operation rebalance REBALANCE POWER asm_power_limit asmcmd lsop est_minutes resync compact rebalance lento rebalance slow rebalance atascado rebalance hung ERRS ARB0 cuándo termina el rebalance Progreso y tiempo restante de un rebalance/resync en curso (v$asm_operation, est_minutes) y cambio de POWER. La base de datos sigue abierta; solo cambia "},{"k":"op","v":"add","l":"Añadir un disco nuevo a un disk group","m":"Operaciones con discos ADD DISK ALTER DISKGROUP ADD DISK añadir disco liberar espacio aumentar capacidad ORA-15041 ORA-15031 ORA-15033 ORA-15014 ORA-15410 ORA-15032 ORA-15075 ORA-17502 add disk nuevo LUN new LUN ampliar disk group extend diskgroup Al disk group le queda poco espacio o hay que ampliarlo: prepare el nuevo LUN en todos los nodos (multipath/udev/ASMLib/AFD), añádalo con ADD DISK y sup"},{"k":"op","v":"replace","l":"Sustituir un disco por otro nuevo (REPLACE DISK)","m":"Operaciones con discos REPLACE DISK sustitución de disco replace disk ORA-15145 storage migration renovación de disco ORA-15221 ORA-15033 replace failed disk sustituir disco averiado disk replacement migración de almacenamiento storage migration ASM Sustituye en una sola operación un disco averiado (offline) o que hay que renovar por uno nuevo; solo se copian los datos de ese disco (12.1+, NORMAL/"},{"k":"op","v":"drop","l":"Eliminar un disco de un disk group","m":"Operaciones con discos DROP DISK eliminar disco UNDROP DISKS FORMER ORA-15041 ORA-15067 devolver LUN drop disk DROPPING desconectar disco remove disk from diskgroup devolución de LUN Para devolver un LUN o retirarlo del almacenamiento: con DROP DISK los datos se trasladan a los discos restantes; el disco no se desconecta hasta que termina el rebalance y el disco queda en FORMER. La base de datos s"},{"k":"op","v":"failed","l":"Un disco está offline o averiado (redundancia NORMAL/HIGH)","m":"Operaciones con discos ONLINE DISK OFFLINE DISK disk_repair_time repair_timer mode_status OFFLINE ORA-15042 ORA-15040 ORA-15130 ORA-15063 missing disk disk offline fast mirror resync ORA-15080 ORA-15081 ORA-27061 ORA-27072 ORA-15066 WARNING: Write Failed WARNING: Read Failed remaining active paths: 0 disco pasó a offline SYNCING no se puede leer el disco I/O error ASM En un disk group NORMAL/HIGH "},{"k":"op","v":"create","l":"Crear un nuevo disk group","m":"Gestión de disk groups CREATE DISKGROUP creación de disk group au_size compatible.asm compatible.rdbms failgroup ORA-15018 ORA-15221 ORA-15072 ORA-15031 ORA-15033 create diskgroup new disk group nuevo disk group Para crear un nuevo disk group de datos, de FRA o de aplicación: redundancia, tamaño de AU, atributos compatible.asm/rdbms; en RAC, mount en todos los nodos y recurso de Clusterware. Los d"},{"k":"op","v":"mount","l":"Hacer mount o dismount de un disk group","m":"Gestión de disk groups srvctl start diskgroup srvctl stop diskgroup ALTER DISKGROUP MOUNT DISMOUNT ORA-15027 ORA-15017 el disk group no se monta v$asm_client ORA-15063 ORA-15040 ORA-15130 ORA-15032 ORA-15042 PRCR-1065 CRS-2674 MOUNT FORCE diskgroup dismounted el disk group no se abre insufficient number of disks Para cerrar de forma planificada un disk group (dismount) o para abrir un disk group c"},{"k":"op","v":"spbackup","l":"Backup del spfile de ASM, el archivo de contraseñas y los metadatos","m":"Configuración asmcmd spbackup asmcmd spget asmcmd pwget asmcmd pwcopy md_backup md_restore backup del spfile de ASM orapwASM asmcmd spcopy GPnP profile profile.xml ASM password file backup ASM metadata backup backup de ASM Sin downtime: el spfile de ASM, el archivo de contraseñas, la estructura de los disk groups (md_backup) y, en RAC, el perfil GPnP se copian fuera de ASM, de modo que la configur"},{"k":"op","v":"dstring","l":"Cambiar la cadena de descubrimiento de discos de ASM (asm_diskstring)","m":"Configuración asm_diskstring asmcmd dsset asmcmd dsget discovery string cadena de descubrimiento ORA-15014 kfod ORA-15020 discovered duplicate ASM disk afd_dsset asm_diskstring change no se encuentra el disco Para cambiar el patrón de rutas en el que ASM busca los discos (p. ej. paso a multipath, nuevo directorio udev, ASMLib/AFD): primero se prueba con kfod y después asmcmd dsset. Todos los disco"}]},{"id":"ora-autoupgrade","file":"install/guides/ora-autoupgrade.json","group":"Oracle","engineId":"oracle","category":"upgrade","title":"Upgrade de 19c a Oracle AI Database 26ai (23ai): AutoUpgrade","summary":"Se utiliza para un upgrade de versión principal (19c → 26ai); para un RU trimestral dentro de la misma versión se utiliza la guía \"Aplicar un Release Update (RU) de Oracle Database 19c\". Con AutoUpgrade, la herramienta q","level":"Avanzado","duration":"2–4 horas (interrupción del servicio: 30–90 min según el tamaño de la base de datos y los componentes)","topology":"1 servidor, Oracle Restart o RAC (home 26ai en todos los nodos)","tags":["oracle","actualización","autoupgrade","19c","26ai","23ai","noncdb","pdb","target_cdb","punto de restauración","restoration","zona horaria","compatible","rollback","rac","oracle restart","cluster_database","srvctl"],"order":20,"steps":25,"scenarios":[]},{"id":"ora-dataguard","file":"install/guides/ora-dataguard.json","group":"Oracle","engineId":"oracle","category":"ha","title":"Oracle Data Guard 19c physical standby (con Broker; instancia única, Oracle Restart, RAC)","summary":"Crea una physical standby para una base de datos primary 19c en ejecución con RMAN DUPLICATE … FROM ACTIVE DATABASE y la incorpora al Data Guard Broker. El resultado es una standby que recibe y aplica el redo en tiempo r","level":"Avanzado","duration":"2–4 horas (según el volumen de datos)","topology":"Primary (instancia única / Oracle Restart / RAC de 2–8 nodos) + physical standby (instancia única / Oracle Restart / RAC) + observer opcional","tags":["oracle","data guard","dataguard","standby","physical standby","dgmgrl","broker","rman duplicate","switchover","failover","fast-start failover","observer","19c","dr","rac","oracle restart","srvctl","standby redo log","apply instance"],"order":20,"steps":43,"scenarios":[]},{"id":"ora-datapump","file":"install/guides/ora-datapump.json","group":"Oracle","engineId":"oracle","category":"backup","title":"Oracle Data Pump: export / import, migración, refresco de esquemas y transportable","summary":"Export e import de tablas, esquemas, tablespaces o base de datos completa con expdp/impdp, migración entre dos bases de datos (archivo dump + scp + checksum, o NETWORK_LINK) y extracción de DDL de un dump (SQLFILE). Incl","level":"Intermedio","duration":"30–90 min (según el volumen de datos)","topology":"1 base de datos (instancia única, Oracle Restart o RAC); origen + destino en una migración","tags":["oracle","data pump","datapump","expdp","impdp","export","import","dump","parfile","remap_schema","network_link","sqlfile","table_exists_action","flashback_time","migración de datos","migration","logical backup","copia lógica","rac","scan","cluster","acfs","cron","wallet","programación","refresh","refresco de esquema","prod to test","drop user cascade","transportable tablespace","tts","xtts","full transportable","transportable=always","dbms_tts","transport_set_check","endian","rman convert","cross-platform","11g to 19c","non-cdb to pdb","encryption","encryption_password","compression","attach","kill_job","start_job","continue_client","orphan","dba_datapump_jobs","lob","securefile","data_options"],"order":20,"steps":16,"scenarios":[{"k":"op","v":"export","l":"Export: generar un archivo dump desde la base de datos","m":"Exportación / importación expdp export sacar un dump copia lógica logical backup ORA-39001 ORA-39000 ORA-39070 ORA-31633 ORA-01555 ORA-39095 ORA-39094 ORA-00439 LRM-00123 UDE-00002 ORA-39127 Una tabla, un esquema, un tablespace o toda la base de datos se escribe en un archivo dump (expdp). La base de datos sigue abierta y la aplicación sigue funcionando. Es una copia lógica: no sustituye a un back"},{"k":"op","v":"import","l":"Import: cargar un archivo dump en la base de datos","m":"Exportación / importación impdp import cargar un dump REMAP_SCHEMA REMAP_TABLESPACE TABLE_EXISTS_ACTION ORA-39002 ORA-39087 ORA-39142 ORA-39143 ORA-39083 ORA-12899 ORA-39171 ORA-39082 ORA-31684 ORA-31640 ORA-39059 ORA-01917 ORA-39151 ORA-39111 ORA-01653 ORA-30036 Un archivo dump generado previamente se carga con impdp; los nombres de esquema, tablespace y tabla se pueden remapear. El destino sigue"},{"k":"op","v":"move","l":"Migración: trasladar datos de la base de datos de origen al destino","m":"Migración y refresco migración de datos migration NETWORK_LINK database link scp checksum ORA-31679 ORA-02085 ORA-39183 Export → copia de archivos → import, o directamente con NETWORK_LINK sin archivo dump. Elija esta tarjeta si el esquema aún no existe en el destino (carga inicial); si el mismo esquema ya existe en el destino y debe sustituirse por el de producción, elija la tarjeta \"Refresco de "},{"k":"op","v":"sqlfile","l":"Extraer solo el DDL de un archivo dump (SQLFILE)","m":"Exportación / importación SQLFILE extraer DDL contenido del dump qué hay en el dump metadata DBMS_METADATA No se carga nada; las sentencias CREATE de los objetos se escriben en un archivo .sql. La base de datos no se modifica."},{"k":"op","v":"refresh","l":"Refresco de esquema de producción a pruebas (refresh)","m":"Migración y refresco refresh refresco de esquema refrescar pruebas prod to test de producción a pruebas refresco de UAT DROP USER CASCADE ORA-01940 ORA-02380 ORA-01917 ORA-31684 ORA-00031 TABLE_EXISTS_ACTION=REPLACE Un esquema que ya existe en pruebas/UAT se sustituye por su versión actual de producción: backup del esquema de destino, cierre de sesiones, DROP USER … CASCADE o TABLE_EXISTS_ACTION=R"},{"k":"op","v":"tts","l":"Trasladar un tablespace copiando datafiles (transportable tablespace)","m":"Transportable (copia de datafiles) transportable tablespace TTS XTTS TRANSPORT_TABLESPACES TRANSPORT_DATAFILES DBMS_TTS TRANSPORT_SET_CHECK endian cross-platform RMAN CONVERT ORA-29335 ORA-29341 ORA-29342 ORA-29345 ORA-29349 ORA-19722 ORA-01647 Los tablespaces grandes (cientos de GB o más) se trasladan copiando datafiles en lugar de fila a fila; en la misma o en distinta plataforma/endian (RMAN CO"},{"k":"op","v":"fulltts","l":"Trasladar toda la base de datos: full transportable (11g/12c → 19c, non-CDB → PDB)","m":"Transportable (copia de datafiles) full transportable TRANSPORTABLE=ALWAYS FULL=Y VERSION=12 11g to 19c de 11g a 19c non-CDB to PDB migrar a PDB migration upgrade by migration ORA-39123 ORA-01647 ORA-29349 ORA-39127 Con FULL=Y TRANSPORTABLE=ALWAYS los tablespaces de usuario se trasladan a una nueva PDB como datafiles y todo lo demás (usuarios, PL/SQL, privilegios) como metadatos; VERSION=12 en un "},{"k":"op","v":"jobfix","l":"Job de Data Pump interrumpido / en espera: supervisar, reanudar, terminar","m":"Supervisión y fallos ATTACH START_JOB CONTINUE_CLIENT KILL_JOB STOP_JOB dba_datapump_jobs orphan master table NOT RUNNING IDLING en espera resumable ORA-39171 ORA-31626 ORA-31633 ORA-39002 ORA-31634 ORA-39097 ORA-39126 Un job de expdp/impdp está en espera (resumable wait), detenido o interrumpido; quiere conectarse a él con ATTACH y reanudarlo, terminarlo con KILL_JOB o limpiar una tabla maestra h"}]},{"id":"ora-dataguard-ops","file":"install/guides/ora-dataguard-ops.json","group":"Oracle","engineId":"oracle","category":"ops","title":"Oracle Data Guard: operación diaria y escenarios de fallo (19c / 23ai)","summary":"Runbook de operación diaria y recuperación ante fallos para una configuración Data Guard (physical standby) ya existente; para la instalación se utiliza la guía de instalación de Data Guard. Si no está seguro de qué hace","level":"Avanzado","duration":"5 min (comprobación) – 4 horas (reconstrucción)","topology":"Primary (instancia única / Oracle Restart / RAC) + physical standby (instancia única / Oracle Restart / RAC) + observer opcional","tags":["oracle","data guard","dataguard","standby","physical standby","dgmgrl","broker","switchover","failover","reinstate","archive gap","fal","recover from service","roll forward","snapshot standby","active data guard","redo apply","mrp","apply lag","transport lag","protection mode","fast-start failover","fsfo","observer","standby_file_management","standby redo log","unnamed","rman","runbook","dr","rac","19c","23ai"],"order":21,"steps":5,"scenarios":[{"k":"scenario","v":"health","l":"Comprobación de estado y retardo (¿está todo en orden?)","m":"Supervisión y comprobaciones ORA-16809 ORA-16810 ORA-16853 ORA-16855 ORA-16857 ORA-16766 ORA-16825 apply lag transport lag retardo lag SHOW CONFIGURATION WARNING estado de Data Guard ORA-16854 ORA-16856 ORA-16191 ORA-16198 ORA-03135 ORA-16714 ORA-16792 ORA-16047 standby se queda atrás transport lag alto comprobación de estado de Data Guard health check Solo lectura: configuración, transport/apply "},{"k":"scenario","v":"switchover","l":"Switchover planificado (intercambiar los roles)","m":"Transición de roles switchover cambio de rol role transition simulacro de DR ORA-16475 ORA-16470 ORA-16467 Ready for Switchover ORA-16541 ORA-16516 switchover_status TO STANDBY SESSIONS ACTIVE switchback devolver los roles mantenimiento planificado cambio de sitio Primary y standby están sanos y desea intercambiar los roles de forma planificada (mantenimiento, simulacro de DR, devolver los roles);"},{"k":"scenario","v":"failover","l":"Primary perdido: failover al standby","m":"Fallo y recuperación failover primary caído primary down pérdida del sitio desastre ORA-16472 ORA-01034 ORA-12541 FAILOVER TO ACTIVATE PHYSICAL STANDBY ORA-12514 ORA-12170 ORA-03113 ORA-16625 ORA-16778 primary inaccesible pérdida del centro de datos FAILOVER TO IMMEDIATE emergencia No se puede acceder al primary y no va a volver (pérdida del servidor, del almacenamiento o del sitio); el standby pa"},{"k":"scenario","v":"reinstate","l":"Tras un failover, recuperar el antiguo primary como standby (reinstate)","m":"Fallo y recuperación reinstate ORA-16661 ORA-16795 ORA-38729 ORA-16653 needs to be reinstated antiguo primary flashback ORA-16649 split-brain el antiguo primary no abre FastStartFailoverAutoReinstate REINSTATE DATABASE standby_became_primary_scn Se ha realizado un failover y el antiguo servidor primary ha vuelto; si Flashback Database está habilitado, se convierte en standby del nuevo primary sin "},{"k":"scenario","v":"gap","l":"Gap de archivado: el standby espera archived logs que faltan","m":"Fallo y recuperación archive gap gap de archivado v$archive_gap FAL Fetching gap ORA-00308 ORA-16401 ORA-27037 ORA-16055 gap sequence waiting for log WAIT_FOR_GAP FAL[client] Media Recovery Waiting for thread archived log que falta missing archive log ORA-16089 `v$archive_gap` devuelve filas o el alert log muestra mensajes FAL/gap; el apply está detenido en una secuencia. Las bases de datos siguen"},{"k":"scenario","v":"rollfwd","l":"Standby muy retrasado o sin archived logs: avanzarlo (RECOVER FROM SERVICE)","m":"Fallo y recuperación roll forward avanzar standby RECOVER STANDBY DATABASE FROM SERVICE nologging ORA-01578 ORA-26040 standby retrasado resync resincronizar ORA-10567 BACKUP INCREMENTAL FROM SCN standby con días de retraso NONLOGGED BLOCK incremental roll forward Los archived logs del gap no existen en ningún sitio, operaciones NOLOGGING han dañado el standby o el standby lleva días de retraso; el"},{"k":"scenario","v":"rebuild","l":"Reconstruir el standby desde cero","m":"Fallo y recuperación rebuild reconstruir standby re-create standby ORA-16795 DUPLICATE standby perdido control file perdido ORA-00205 ORA-01157 archivos del standby borrados DROP DATABASE pérdida de disco del standby No es posible el reinstate (sin flashback) ni el avance, o se han perdido los archivos o el control file del standby; el standby se elimina y se vuelve a crear con DUPLICATE. El prima"},{"k":"scenario","v":"snapshot","l":"Convertir en snapshot standby / revertir (copia con escritura para pruebas)","m":"Pruebas y lectura snapshot standby CONVERT TO SNAPSHOT STANDBY entorno de pruebas read write standby ORA-16000 ORA-38784 ORA-16433 CONVERT TO PHYSICAL STANDBY SNAPSHOT STANDBY guaranteed restore point abrir standby para pruebas Desea abrir el standby temporalmente en lectura-escritura para hacer pruebas, y después descartar los cambios y volver a physical standby; el primary no se ve afectado y se"},{"k":"scenario","v":"apply","l":"Detener / iniciar Redo Apply, cambiar la instancia de apply, abrir en read-only","m":"Operación diaria MRP redo apply APPLY-OFF APPLY-ON apply instance read only Active Data Guard ORA-16766 ORA-16136 ORA-01153 real-time query WAIT_FOR_LOG APPLYING_LOG READ ONLY WITH APPLY OPEN READ ONLY PreferredApplyInstance intended state apply detenido MRP no se ejecuta Desea detener o reiniciar MRP por mantenimiento, mover el apply a otra instancia en un standby RAC o abrir el standby para info"},{"k":"scenario","v":"prot","l":"Modo de protección, Fast-Start Failover y observer","m":"Operación diaria protection mode MaxAvailability MaxPerformance MaxProtection SYNC ASYNC FSFO fast-start failover observer ORA-16627 ORA-16651 ORA-16820 ORA-16819 ORA-16072 ORA-16647 NetTimeout LogXptMode FASTSYNC protection_level modo de protección Desea cambiar entre MaxPerformance / MaxAvailability / MaxProtection, habilitar o deshabilitar FSFO o gestionar el observer."},{"k":"scenario","v":"files","l":"Añadir datafiles, redo logs y standby redo logs / archivo UNNAMED","m":"Operación diaria standby_file_management UNNAMED ORA-01111 ORA-01110 ORA-01157 ORA-01274 añadir datafile add datafile añadir redo log standby redo log SRL ORA-16789 db_file_name_convert ADD STANDBY LOGFILE ORA-01136 datafile añadido standby Hay que añadir un datafile o un redo log en el primary, ha aparecido un archivo UNNAMED en el standby (ORA-01111/ORA-01274) o faltan standby redo logs."},{"k":"scenario","v":"backup","l":"Hacer un backup RMAN desde el standby","m":"Operación diaria rman standby backup backup desde el standby archivelog deletion policy RMAN-08137 recovery catalog offload backup RMAN-06820 BACKED UP 1 TIMES CONFIGURE DB_UNIQUE_NAME backup en standby Desea ejecutar los backups RMAN en el standby para descargar al primary de esa carga, y ajustar la política de borrado de archived logs en consecuencia."}]},{"id":"ora-backup-strategy","file":"install/guides/ora-backup-strategy.json","group":"Oracle","engineId":"oracle","category":"backup","title":"Estrategia de copias de seguridad y automatización de Oracle (RMAN + Data Pump)","summary":"Establece un esquema permanente y automatizado de copias de seguridad para una base de datos ya instalada (la recuperación tras un fallo se trata en una guía aparte: \"Restore y recovery con RMAN\"): level 0 semanal + leve","level":"Intermedio","duration":"60–120 min (sin contar el primer level 0)","topology":"1 servidor (instancia única u Oracle Restart) o RAC (2–8 nodos)","tags":["oracle","rman","copia de seguridad","backup","estrategia","level 0","level 1","incremental","archivelog","fra","retention","block change tracking","data pump","expdp","crontab","cron","systemd timer","dbms_scheduler","automatización","wallet","mkstore","rac","oracle restart","snapshot controlfile","asm","/etc/cron.d","ol7","recovery catalog","create catalog","register database","prueba de restauración","restore drill","restore validate","preview"],"order":21,"steps":33,"scenarios":[]},{"id":"ora-rman-restore","file":"install/guides/ora-rman-restore.json","group":"Oracle","engineId":"oracle","category":"backup","title":"Restore y recovery con RMAN","summary":"Le guía paso a paso por la recuperación RMAN correcta en el momento del fallo: 19 escenarios — pérdida de un datafile, SYSTEM/UNDO, tempfile, bloques corruptos, online redo log, controlfile y spfile; restore completo, re","level":"Avanzado","duration":"30 min – varias horas (según el tamaño de la base de datos)","topology":"1 servidor (instancia única u Oracle Restart) o RAC; origen + destino en el escenario de nuevo host","tags":["oracle","rman","restore","recovery","recuperación de base de datos","pitr","point in time","resetlogs","recover table","controlfile","spfile","autobackup","datafile","preview","validate","dbid","nuevo servidor","disaster recovery","rac","oracle restart","cluster_database","srvctl","asm","block corruption","bloque corrupto","ORA-01578","redo log","ORA-00313","clear logfile","tempfile","tspitr","flashback database","restore point","dropped pdb","unplug","from service","data guard","restore archivelog","catalog start with","set newname","undo","system tablespace"],"order":22,"steps":10,"scenarios":[{"k":"scenario","v":"preview","l":"Ensayo: RESTORE … PREVIEW / VALIDATE (no cambia nada)","m":"Ensayo y registros de backup RESTORE PREVIEW RESTORE VALIDATE ensayo de restore prueba de restore funciona mi backup backup test restore drill simulacro de recuperación RMAN VALIDATE es válido el backup No hay nada roto; quiere comprobar, sin escribir datos, que los backups realmente bastan para una recuperación (la base de datos sigue abierta). También es el primer paso del simulacro mensual de r"},{"k":"scenario","v":"catalog","l":"Backups movidos a otro directorio / RMAN no encuentra el backup (CATALOG)","m":"Ensayo y registros de backup CATALOG START WITH RMAN-06023 RMAN-06025 RMAN-06026 ORA-19505 ORA-27037 EXPIRED backup reubicado backup moved no backup found CROSSCHECK cambió el NFS backup no encontrado Las piezas de backup existen pero RMAN no las conoce (se movieron, cambió el NFS, vienen de otro servidor): recibe RMAN-06023/06025. La base de datos sigue abierta."},{"k":"scenario","v":"arch","l":"Restaurar solo archive logs (RESTORE ARCHIVELOG)","m":"Ensayo y registros de backup RESTORE ARCHIVELOG ORA-00308 restaurar archive logs archive log restore archive gap LogMiner SET ARCHIVELOG DESTINATION Necesita en disco los archive logs de un rango de secuencias concreto para LogMiner, un gap del standby o una recuperación manual; la base de datos sigue abierta y no se sobrescribe ningún archivo."},{"k":"scenario","v":"datafile","l":"Pérdida/corrupción de un solo datafile o tablespace (la base de datos sigue abierta)","m":"Pérdida / corrupción ORA-01157 ORA-01110 ORA-01116 ORA-27041 ORA-00376 ORA-01113 datafile perdido datafile borrado missing datafile lost datafile tablespace perdido ORA-27037 ORA-01135 ORA-01115 ORA-01114 datafile corrupto La base de datos está abierta y solo un datafile o tablespace no se puede leer (ORA-01157/01110/01116); todo lo demás funciona. Si el archivo pertenece a SYSTEM/UNDO elija la ta"},{"k":"scenario","v":"block","l":"Hay bloques corruptos (ORA-01578, block media recovery)","m":"Pérdida / corrupción ORA-01578 ORA-26040 ORA-01110 bloque corrupto block corruption corrupt block v$database_block_corruption RECOVER CORRUPTION LIST block media recovery nologging ORA-19566 Marked Corrupt Corrupt block relative dba DBVERIFY Una consulta devuelve ORA-01578/ORA-26040, el backup se detiene con ORA-19566 o VALIDATE encontró bloques corruptos; el resto del archivo está sano. La base d"},{"k":"scenario","v":"sysdf","l":"Datafile de SYSTEM o UNDO perdido (la base de datos no abre)","m":"Pérdida / corrupción ORA-01157 ORA-01110 ORA-01116 ORA-01147 ORA-00376 ORA-01541 SYSTEM datafile UNDO datafile tablespace undo perdido system tablespace lost la base de datos no abre ORA-01113 system01.dbf undotbs El archivo perdido o corrupto pertenece al tablespace SYSTEM o UNDO: no se puede poner offline, la base de datos (o solo esa PDB si es un archivo de PDB) se cierra y se restaura en MOUNT"},{"k":"scenario","v":"temp","l":"Tempfile perdido o corrupto (tablespace TEMP)","m":"Pérdida / corrupción ORA-01157 ORA-01187 ORA-25153 ORA-01116 ORA-01122 ORA-01110 tempfile temp tablespace tablespace temporal TEMP perdido temp01.dbf dba_temp_files Las operaciones de ordenación/hash devuelven ORA-01157/ORA-01187/ORA-25153 y el archivo es un tempfile (el número del mensaje de error es mayor que el valor de `db_files`, p. ej. `data file 201`). No hace falta restore; se añade un tem"},{"k":"scenario","v":"redo","l":"Online redo log perdido o corrupto","m":"Pérdida / corrupción ORA-00313 ORA-00312 ORA-00327 ORA-00366 ORA-00367 ORA-00353 ORA-00354 ORA-00316 ORA-01624 redo log perdido online redo lost redo log CLEAR LOGFILE ORA-00314 ORA-00320 ORA-00321 ORA-00340 ORA-00350 ORA-00470 CLEAR UNARCHIVED En el alert log aparece ORA-00313/00312 (no se puede abrir) u ORA-00327/00366/00367 (corrupto). Si falta un miembro o el grupo está INACTIVE, la base de da"},{"k":"scenario","v":"controlfile","l":"Todas las copias del controlfile perdidas","m":"Pérdida / corrupción ORA-00205 ORA-00202 ORA-00210 ORA-27037 controlfile perdido control file lost RESTORE CONTROLFILE autobackup ORA-00206 ORA-00214 ORA-00221 ORA-00227 controlfile corrupto En la fase MOUNT la instancia no encuentra o no puede leer el controlfile (ORA-00205/00202/00210); los datafiles y el redo están sanos. Si una de las copias está sana no hace falta restore (lo muestra el paso "},{"k":"scenario","v":"spfile","l":"spfile perdido","m":"Pérdida / corrupción ORA-01078 LRM-00109 ORA-01565 spfile perdido spfile lost parameter file RESTORE SPFILE CRS-5017 PRCR-1079 spfile borrado El spfile se borró o se corrompió; la instancia no arranca (ORA-01078/LRM-00109) o está arrancada pero el archivo no existe. No se tocan los datafiles y no hay pérdida de datos; si la instancia sigue arrancada, tampoco hay interrupción del servicio."},{"k":"scenario","v":"full","l":"Restore completo, mismo servidor (datafiles perdidos; controlfile y redo sanos)","m":"Pérdida / corrupción todos los datafiles perdidos all datafiles lost full restore RESTORE DATABASE fallo de disco storage failure disco perdido LUN perdido datafiles borrados Se han perdido todos o la mayoría de los datafiles, y el controlfile y los online redo logs están sanos; la base de datos se cierra y no hay pérdida de datos. Si el disco/directorio antiguo de los archivos ya no se puede usar"},{"k":"scenario","v":"relocate","l":"Disco perdido: restaurar la base de datos en otro directorio / ASM (SET NEWNAME)","m":"Pérdida / corrupción SET NEWNAME SWITCH DATAFILE ALL restore a otro directorio restore a ASM new location disco cambiado punto de montaje perdido relocate ORA-15001 ORA-15040 disk group dismounted El disco o el grupo de discos donde estaban los datafiles no se puede usar; el controlfile y el redo están sanos. Los archivos se restauran en un directorio nuevo o en un grupo de discos ASM, la base de "},{"k":"scenario","v":"pitr","l":"Volver a un momento / SCN (PITR, RESETLOGS)","m":"Vuelta atrás en el tiempo y deshacer PITR point in time recovery volver en el tiempo volver a un SCN DELETE erróneo UPDATE erróneo TRUNCATE error lógico logical error UNTIL TIME datos borrados DROP USER UNTIL SCN RESETLOGS DELETE/UPDATE/TRUNCATE erróneo o versión de aplicación defectuosa: lleva la base de datos (o solo la PDB) a un momento pasado; se pierden todos los cambios posteriores a ese mom"},{"k":"scenario","v":"flashback","l":"Rebobinar con Flashback Database (restore point / SCN / tiempo)","m":"Vuelta atrás en el tiempo y deshacer FLASHBACK DATABASE FLASHBACK PLUGGABLE DATABASE restore point guaranteed restore point rebobinar deshacer upgrade volver atrás tras una prueba FLASHBACK_ON FLASHBACK TO RESTORE POINT Flashback está activado o existe un guaranteed restore point y no falta ningún archivo: se rebobina en minutos sin restore (Enterprise Edition). Requiere RESETLOGS. Para saber si e"},{"k":"scenario","v":"tspitr","l":"Devolver un tablespace a un momento pasado (TSPITR)","m":"Vuelta atrás en el tiempo y deshacer TSPITR tablespace point in time RECOVER TABLESPACE AUXILIARY DESTINATION DBMS_TTS recuperar tablespace ORA-29308 La operación errónea solo afectó a objetos de un tablespace; el resto de la base de datos debe quedarse en el presente (Enterprise Edition). El tablespace queda offline durante la operación y todos sus objetos vuelven al momento objetivo; si solo se "},{"k":"scenario","v":"table","l":"Recuperar una sola tabla (RECOVER TABLE, 12c+ Enterprise Edition)","m":"Vuelta atrás en el tiempo y deshacer RECOVER TABLE tabla borrada DROP TABLE recuperar tabla table recovery ORA-00942 REMAP TABLE ORA-38305 FLASHBACK TABLE recyclebin PURGE Una sola tabla se borró o se dañó por error; el resto de la base de datos debe quedar como está (12c+ Enterprise Edition). Si se hizo DROP sin PURGE, mire primero la recyclebin: `FLASHBACK TABLE … TO BEFORE DROP` tarda segundos "},{"k":"scenario","v":"pdbdrop","l":"Recuperar una PDB eliminada (DROP PLUGGABLE DATABASE)","m":"Vuelta atrás en el tiempo y deshacer DROP PLUGGABLE DATABASE PDB eliminada dropped PDB RMAN-06813 ORA-65011 ORA-65179 KEEP DATAFILES recuperar PDB unplug DBMS_PDB.RECOVER Una PDB se eliminó con `DROP PLUGGABLE DATABASE`. Si los archivos siguen ahí, se vuelve a conectar con el XML; si se borraron, se recupera desde el backup (en 21c+ con un solo comando, en 19c mediante una CDB temporal). La CDB si"},{"k":"scenario","v":"newhost","l":"Restore a otro servidor (nuevo host, desde el autobackup del controlfile)","m":"Otro servidor y standby nuevo servidor otro servidor pérdida del servidor disaster recovery DR new host clonar clone entorno de pruebas servidor destruido server lost restore to another host El servidor se ha perdido por completo o hay que montar un entorno de copia/pruebas: el backup se restaura en otro servidor (solo instancia única, sistema de archivos). Se recupera hasta el último archive log "},{"k":"scenario","v":"fromsvc","l":"Data Guard: obtener el archivo del standby/primary por la red (FROM SERVICE)","m":"Otro servidor y standby RESTORE FROM SERVICE FROM SERVICE RECOVER STANDBY DATABASE restore desde el standby Data Guard standby rezagado archive gap standby controlfile ORA-01110 v$archive_gap apply lag Hay Data Guard: el datafile perdido del primary se obtiene del standby, y el standby rezagado o el standby controlfile perdido del primary, sin usar piezas de backup (12c+)."}]},{"id":"ora-goldengate","file":"install/guides/ora-goldengate.json","group":"Oracle","engineId":"oracle","category":"ha","title":"Oracle GoldenGate Microservices: replicación unidireccional Oracle → Oracle","summary":"Replica de forma continua (con un retraso de segundos) un esquema de una PDB a otra base de datos con GoldenGate 21c o 23ai/26ai Microservices. Al final, en el origen funcionan un Integrated Extract y un Distribution Pat","level":"Avanzado","duration":"3–5 horas","topology":"Servidor de BD de origen (deployment de origen de OGG) + servidor de BD de destino (deployment de destino de OGG); en RAC, GoldenGate en el nodo 1 (o como recurso de cluster con XAG)","tags":["oracle","goldengate","ogg","microservices","replicación","replication","cdc","integrated extract","parallel replicat","distribution path","adminclient","oggca","21c","23ai","26ai","rac","scan","xag","acfs","tranlogoptions"],"order":30,"steps":42,"scenarios":[]},{"id":"ora-goldengate-ops","file":"install/guides/ora-goldengate-ops.json","group":"Oracle","engineId":"oracle","category":"ops","title":"Operación diaria y escenarios de avería de Oracle GoldenGate (Microservices 21c / 23ai)","summary":"Runbook de operación diaria y recuperación ante averías para una replicación GoldenGate Microservices ya instalada (para la instalación, consulte la guía de instalación de GoldenGate). Si no está seguro de qué hacer, emp","level":"Avanzado","duration":"5 min (comprobación) – 2 horas (actualización, añadir una tabla)","topology":"Servidor de origen (deployment de origen, Integrated Extract, Distribution Path) + servidor de destino (deployment de destino, Parallel Replicat)","tags":["oracle","goldengate","ogg","microservices","adminclient","runbook","abend","extract","replicat","lag","stats","info all","heartbeat","report","discard","reperror","handlecollisions","cdr","conflict","add trandata","schematrandata","aftercsn","atcsn","mapexclude","tableexclude","resync","purge trail","purgeoldextracts","trail","alter extract scn","etrollover","upgrade","patch","opatch","oggHome","data guard","switchover","failover","handledlfailover","OGG-00664","OGG-01004","OGG-01296","OGG-02912","21c","23ai","26ai"],"order":31,"steps":8,"scenarios":[{"k":"scenario","v":"health","l":"Comprobación de estado y retraso (¿va todo bien?)","m":"Supervisión y comprobaciones INFO ALL lag retraso STATS heartbeat GG_LAG Service Manager ggserr.log OGG-01027 OGG-01738 Long Running Transaction STOPPED RUNNING Distribution Path DISTPATH Receiver INFO DISTPATH la replicación va retrasada replication lag Solo lectura: Service Manager y los microservicios, INFO ALL, lag, STATS, retraso del heartbeat, estado del capture en la base de datos y ggserr."},{"k":"scenario","v":"ext_abend","l":"Extract ABENDED (detenido con error)","m":"Avería (ABEND) Extract ABENDED ABEND OGG-00664 OGG-00446 OGG-02171 OGG-02912 OGG-02022 OGG-02091 OGG-01044 OGG-01668 ORA-01291 ORA-01017 ORA-28001 ORA-04031 ORA-01031 missing log archived log not found logmining OGG-00868 Extract STARTING Extract detenido capture ABORTED El Extract del origen está en ABENDED o se reinicia una y otra vez: la causa (conexión, archived log que falta, log mining, priv"},{"k":"scenario","v":"rep_abend","l":"Replicat ABENDED (no puede aplicar en el destino)","m":"Avería (ABEND) Replicat ABENDED OGG-01004 OGG-01296 OGG-01154 OGG-00869 OGG-01161 OGG-01169 OGG-01431 OGG-00199 OGG-00423 ORA-01403 ORA-00001 ORA-02291 ORA-00942 ORA-01653 no data found unique constraint discard REPERROR HANDLECOLLISIONS CDR conflict Replicat detenido ORA-01400 ORA-12899 value too large El Replicat del destino no pudo aplicar una transacción: la tabla, la operación y el error se i"},{"k":"scenario","v":"add_table","l":"Añadir una tabla con datos a la replicación (carga inicial + AFTERCSN)","m":"Gestión de tablas añadir tabla add table ADD TRANDATA ADD SCHEMATRANDATA AFTERCSN ATCSN initial load carga inicial flashback_scn MAPEXCLUDE tabla nueva Añade a una replicación en marcha, sin parada, una tabla existente que ya contiene datos: ADD TRANDATA, expdp/impdp con un SCN coherente y un Replicat aparte que aplica solo los cambios de esta tabla posteriores a ese SCN (AFTERCSN). Las demás tabl"},{"k":"scenario","v":"remove_table","l":"Quitar una tabla de la replicación","m":"Gestión de tablas quitar tabla remove table TABLEEXCLUDE MAPEXCLUDE DELETE TRANDATA exclude La tabla dejará de replicarse: TABLEEXCLUDE en el Extract (o eliminar la línea TABLE), MAPEXCLUDE en el Replicat y, si es necesario, DELETE TRANDATA. Los procesos se reinician brevemente."},{"k":"scenario","v":"resync","l":"Resincronizar una sola tabla (resync)","m":"Gestión de tablas resync resincronizar out of sync diferencia de datos desincronizada Veridata OGG-01004 ORA-01403 ORA-00001 row count número de filas distinto Los datos de una tabla en el destino se han desviado del origen (cambio manual, error omitido, carga inicial incompleta): la tabla se vuelve a cargar desde el origen con un SCN coherente y un Replicat temporal aplica los cambios posteriores"},{"k":"scenario","v":"purge","l":"Los archivos de trail llenan el disco: limpieza (Purge Trail)","m":"Mantenimiento trail purge PURGEOLDEXTRACTS Purge Trail disco lleno No space left on device limpieza de trail var/lib/data directorio de trail lleno ENOSPC El directorio de trail crece o el disco está a punto de llenarse: se comprueba qué archivos se están leyendo y se define una tarea Purge Trail (el equivalente de PURGEOLDEXTRACTS en Classic) con USECHECKPOINTS; en caso de emergencia, los archivo"},{"k":"scenario","v":"reposition","l":"Reposicionar Extract en un SCN concreto","m":"Mantenimiento reposition ALTER EXTRACT SCN BEGIN SCN ETROLLOVER iniciar desde SCN pérdida de trail first_scn rebobinar Hay un SCN desde el que quiere que Extract vuelva a empezar (pérdida de trail, punto de inicio erróneo, vuelta atrás en el destino): se arranca desde un nuevo archivo de trail con ALTER EXTRACT … SCN y ETROLLOVER. Los mismos datos pueden llegar al destino por segunda vez."},{"k":"scenario","v":"upgrade","l":"Actualización de versión / parche de GoldenGate (Microservices)","m":"Mantenimiento upgrade patch actualización OPatch out-of-place oggHome Service Manager upgrade bundle patch 21c 23ai 26ai release update GoldenGate patch Service Manager PATCH Paso a una nueva versión o parche de GoldenGate: out-of-place (instalar un nuevo home y trasladar Service Manager y los deployments al nuevo home con REST) o in-place con OPatch. Extract/Replicat se detienen brevemente; la vu"},{"k":"scenario","v":"dg_role","l":"Mantener Extract en marcha tras un switchover/failover de Data Guard en el origen","m":"Cambio de rol Data Guard switchover failover role transition HANDLEDLFAILOVER ORA-01033 ORA-16000 ORA-01109 nueva primary standby FSFO ORA-12514 cambio de rol Extract nueva primary FAILOVERTARGETDESTID Se ha producido un switchover o un failover en la base de datos de origen; Extract debe conectarse a la nueva primary y continuar desde su checkpoint: se comprueban la conexión (credential alias/ser"}]},{"id":"ora-security","file":"install/guides/ora-security.json","group":"Oracle","engineId":"oracle","category":"security","title":"Hardening de seguridad de Oracle Database (evaluación, red, TLS, autenticación, privilegios, Unified Auditing, TDE)","summary":"No es una guía de instalación: localiza y cierra las brechas de seguridad de una base de datos Oracle 19c / 23ai (26ai) en funcionamiento (single instance, Oracle Restart o RAC; CDB/PDB). Primero la Evaluación de segurid","level":"Avanzado","duration":"5 min (evaluación) – 4 horas (hardening completo)","topology":"Single instance / Oracle Restart / RAC (2–8 nodos), CDB + PDB; servidor de aplicaciones (verificación del cliente)","tags":["oracle","seguridad","security","hardening","bastionado de base de datos","cis benchmark","stig","tcps","wallet","orapki","sqlnet.ora","native network encryption","TCP.VALIDNODE_CHECKING","ALLOWED_LOGON_VERSION_SERVER","perfil","ORA12C_STRONG_VERIFY_FUNCTION","unified auditing","AUDSYS","DBMS_AUDIT_MGMT","TDE","keystore","ADMINISTER KEY MANAGEMENT","UTL_FILE","PUBLIC","contraseña por defecto"],"order":40,"steps":2,"scenarios":[{"k":"scenario","v":"scan","l":"Evaluación de seguridad: ¿qué brechas tengo? (solo lectura)","m":"Empiece aquí auditoría de seguridad de base de datos security audit hardening check CIS STIG DBSAT brecha de seguridad vulnerabilidad vulnerability hallazgo de pentest dba_users_with_defpwd consulta de auditoría evaluación de seguridad security scan CRÍTICO El primer análisis que se debe ejecutar: cuentas con contraseña por defecto y cuentas abiertas, verificadores de contraseña antiguos, perfiles"},{"k":"scenario","v":"all","l":"Hardening completo (todas las áreas en orden)","m":"Empiece aquí hardening bastionado de base de datos seguridad completa full hardening aplicar CIS benchmark STIG Base de datos recién instalada o nunca bastionada: se aplican todas las áreas en un orden seguro; el listener y la base de datos se reinician una o dos veces (requiere ventana de mantenimiento)."},{"k":"scenario","v":"net","l":"Red y conexiones: ¿quién puede conectarse y desde dónde? (listener, TCP.VALIDNODE_CHECKING, firewall)","m":"Red y autenticación TCP.VALIDNODE_CHECKING TCP.INVITED_NODES listener 1521 5500 ORA-12537 TNS-12537 ORA-12541 ADMIN_RESTRICTIONS firewall TNS-12546 ORA-12170 invited nodes listener.log acceso externo restringir acceso El listener está abierto a cualquier dirección, no hay invited nodes definidos y 1521/5500 están abiertos a todo el mundo. Se reinicia el listener (las sesiones existentes no se ven "},{"k":"scenario","v":"tls","l":"Cifrar las conexiones (wallet TCPS o native network encryption)","m":"Red y autenticación TCPS SSL TLS wallet orapki SQLNET.ENCRYPTION_SERVER ORA-28860 ORA-29024 ORA-28759 ORA-29106 ORA-12696 ORA-12660 SSL_SERVER_DN_MATCH caducidad del certificado ORA-29003 ORA-29002 TNS-12560 cadena de certificados TLS certificate chain native network encryption El tráfico de red no está cifrado; TLS (TCPS, wallet, certificado, SSL_SERVER_DN_MATCH en el cliente) o native encryption"},{"k":"scenario","v":"auth","l":"Autenticación y contraseñas (verificador 12C, perfiles, cuentas por defecto)","m":"Red y autenticación ALLOWED_LOGON_VERSION_SERVER ORA-28040 ORA-01017 ORA-28000 ORA-28001 ORA-28003 ORA-28007 password_versions 10G PROFILE ORA12C_STRONG_VERIFY_FUNCTION contraseña por defecto default password SCOTT ORA-65066 ORA-00988 password_versions 10G perfil de contraseñas password policy FAILED_LOGIN_ATTEMPTS Verificadores de contraseña 10G/11G, sin perfil de contraseñas, cuentas con contras"},{"k":"scenario","v":"priv","l":"Mínimo privilegio: roles, PUBLIC y la cuenta de aplicación","m":"Privilegios y registro de auditoría PUBLIC UTL_FILE UTL_TCP UTL_HTTP UTL_SMTP DBA role RESOURCE ANY privilege least privilege mínimo privilegio ORA-01031 ORA-00942 schema-only account PLS-00201 ORA-04021 GRANT ANY UNLIMITED TABLESPACE informe de privilegios Privilegios DBA/RESOURCE/ANY en la cuenta de aplicación, UTL_FILE/UTL_TCP/UTL_HTTP concedidos a PUBLIC; propietario del esquema, roles de lect"},{"k":"scenario","v":"audit","l":"Auditoría: ¿quién hizo qué? (Unified Auditing, purga de AUDSYS)","m":"Privilegios y registro de auditoría unified auditing AUDIT POLICY AUDSYS DBMS_AUDIT_MGMT UNIFIED_AUDIT_TRAIL ORA_LOGON_FAILURES ORA_SECURECONFIG UNIFIED_AUDIT_SYSTEMLOG SYSAUX lleno registro de auditoría SIEM ORA-46267 unified audit policy quién hizo qué who changed El auditor pide registros de inicios de sesión, cambios de cuentas y cambios de esquema; políticas de unified audit, purga de AUDSYS "},{"k":"scenario","v":"tde","l":"Cifrar los datos en disco (TDE)","m":"Datos y backups TDE transparent data encryption keystore wallet ADMINISTER KEY MANAGEMENT WALLET_ROOT ORA-28365 ORA-28374 ORA-46665 ENCRYPT_NEW_TABLESPACES encryption at rest ORA-28367 ORA-46658 ORA-28353 Advanced Security master key TDE_CONFIGURATION TDE frente a la lectura de datafiles, backups o copias de disco: WALLET_ROOT, keystore, master key, cifrado de tablespaces (online). Requiere la lic"},{"k":"scenario","v":"backup","l":"Seguridad de los backups (cifrado RMAN, automatización sin contraseñas, permisos)","m":"Datos y backups RMAN encryption CONFIGURE ENCRYPTION ORA-19913 expdp ENCRYPTION secure external password store mkstore SEPS permisos de backup ORA-19914 licencia Advanced Security backup sin cifrar unencrypted backup ENCRYPTION_PWD_PROMPT Backups de RMAN/Data Pump sin cifrar, contraseñas en texto plano en los scripts, directorio de backup legible por todos; cifrado RMAN, ENCRYPTION_PWD_PROMPT, wal"},{"k":"scenario","v":"os","l":"Lado del sistema operativo (cuenta oracle, permisos de ficheros, core dumps, SELinux)","m":"Servidor y versión SELinux orapw cuenta oracle permisos de ficheros core dump permisos de ORACLE_HOME sudo avc: denied passwd -l oracle setuid 6751 limits.d La cuenta oracle tiene contraseña y permite login, los permisos del fichero de contraseñas / network admin / wallet son laxos, SELinux está desactivado. El límite de core dump se aplica en una nueva sesión / tras un reinicio."},{"k":"scenario","v":"patch","l":"Parches y versión: ¿estoy en la última RU?","m":"Servidor y versión RU release update CPU Critical Patch Update opatch lspatches datapatch dba_registry_sqlpatch CVE fin de soporte opatch Doc ID 742060.1 calendario de parches RU instalada, estado de datapatch y calendario de soporte; enlaza con la guía de parches RU para aplicarla. Solo lectura."}]},{"id":"ora-oem","file":"install/guides/ora-oem.json","group":"Oracle","engineId":"oracle","category":"monitoring","title":"Instalación de Oracle Enterprise Manager Cloud Control 13c Release 5 (13.5)","summary":"Instala Oracle Enterprise Manager Cloud Control 13.5: se prepara una base de datos 19c existente como Management Repository (los parámetros de la documentación, el restablecimiento de los parámetros del optimizador), se ","level":"Avanzado","duration":"3–5 horas","topology":"Servidor OMS + servidor de la base de datos del repositorio (+ servidores de destino que se monitorizarán)","tags":["oracle","enterprise manager","oem","em13c","13.5","cloud control","oms","agent","emctl","emcli","monitoring","monitorización","rac","cluster","discover_db","data guard","exportconfig"],"order":40,"steps":32,"scenarios":[]}]}}