> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-mintlify-8c05c8a2.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Gestión del TTL

> Gestión del TTL con ClickStack

export const Image = ({img, alt, size}) => {
  return <Frame>
      <img src={img} alt={alt} />
    </Frame>;
};

<div id="ttl-clickstack">
  ## TTL en ClickStack
</div>

Time-to-Live (TTL) es una funcionalidad crucial de ClickStack para la retención y gestión eficiente de datos, especialmente dado que se generan continuamente grandes volúmenes de datos. TTL permite la expiración y eliminación automática de los datos más antiguos, lo que garantiza un uso óptimo del almacenamiento y mantiene el rendimiento sin intervención manual. Esta capacidad es esencial para mantener la base de datos ligera, reducir los costes de almacenamiento y garantizar que las consultas sigan siendo rápidas y eficientes al centrarse en los datos más relevantes y recientes. Además, ayuda a cumplir las políticas de retención de datos mediante la gestión sistemática del ciclo de vida de los datos, mejorando así la sostenibilidad y la escalabilidad generales de la solución de observabilidad.

**De forma predeterminada, ClickStack conserva los datos durante 3 días. Para modificar esto, consulta ["Modificar TTL"](#modifying-ttl).**

TTL se controla a nivel de tabla en ClickHouse. Por ejemplo, a continuación se muestra el esquema predeterminado para los logs; `${TABLES_TTL}` se sustituye por la retención configurada (3 días, salvo que se cambie) cuando el collector crea la tabla:

```sql theme={null}
CREATE TABLE IF NOT EXISTS ${DATABASE}.otel_logs
(
  `Timestamp` DateTime64(9) CODEC(Delta(8), ZSTD(1)),
  `TraceId` String CODEC(ZSTD(1)),
  `SpanId` String CODEC(ZSTD(1)),
  `TraceFlags` UInt8,
  `SeverityText` LowCardinality(String) CODEC(ZSTD(1)),
  `SeverityNumber` UInt8,
  `ServiceName` LowCardinality(String) CODEC(ZSTD(1)),
  `Body` String CODEC(ZSTD(1)),
  `ResourceSchemaUrl` LowCardinality(String) CODEC(ZSTD(1)),
  `ResourceAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
  `ScopeSchemaUrl` LowCardinality(String) CODEC(ZSTD(1)),
  `ScopeName` String CODEC(ZSTD(1)),
  `ScopeVersion` LowCardinality(String) CODEC(ZSTD(1)),
  `ScopeAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
  `LogAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
  `EventName` String CODEC(ZSTD(1)),
  `__hdx_materialized_k8s.cluster.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.cluster.name'] CODEC(ZSTD(1)),
  `__hdx_materialized_k8s.container.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.container.name'] CODEC(ZSTD(1)),
  `__hdx_materialized_k8s.deployment.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.deployment.name'] CODEC(ZSTD(1)),
  `__hdx_materialized_k8s.namespace.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.namespace.name'] CODEC(ZSTD(1)),
  `__hdx_materialized_k8s.node.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.node.name'] CODEC(ZSTD(1)),
  `__hdx_materialized_k8s.pod.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.pod.name'] CODEC(ZSTD(1)),
  `__hdx_materialized_k8s.pod.uid` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.pod.uid'] CODEC(ZSTD(1)),
  `__hdx_materialized_deployment.environment.name` LowCardinality(String) MATERIALIZED ResourceAttributes['deployment.environment.name'] CODEC(ZSTD(1)),
  `ResourceAttributeItems` Array(String) ALIAS arrayMap((arr) -> concat(arr.1, '=', arr.2), ResourceAttributes::Array(Tuple(String, String))),
  `ScopeAttributeItems` Array(String) ALIAS arrayMap((arr) -> concat(arr.1, '=', arr.2), ScopeAttributes::Array(Tuple(String, String))),
  `LogAttributeItems` Array(String) ALIAS arrayMap((arr) -> concat(arr.1, '=', arr.2), LogAttributes::Array(Tuple(String, String))),
  INDEX idx_trace_id TraceId TYPE text(tokenizer = 'array'),
  INDEX idx_res_attr_key mapKeys(ResourceAttributes) TYPE text(tokenizer = 'array'),
  INDEX idx_res_attr_items ResourceAttributeItems TYPE text(tokenizer = 'array'),
  INDEX idx_scope_attr_key mapKeys(ScopeAttributes) TYPE text(tokenizer = 'array'),
  INDEX idx_scope_attr_items ScopeAttributeItems TYPE text(tokenizer = 'array'),
  INDEX idx_log_attr_key mapKeys(LogAttributes) TYPE text(tokenizer = 'array'),
  INDEX idx_log_attr_items LogAttributeItems TYPE text(tokenizer = 'array'),
  INDEX idx_lower_body lower(Body) TYPE text(tokenizer = 'splitByNonAlpha')
)
ENGINE = MergeTree
PARTITION BY toDate(Timestamp)
ORDER BY (toStartOfFiveMinutes(Timestamp), ServiceName, Timestamp)
TTL toDateTime(Timestamp) + ${TABLES_TTL}
SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1, enable_block_number_column = 1, enable_block_offset_column = 1;
```

El particionado en ClickHouse permite separar lógicamente los datos en disco según una columna o una expresión SQL. Al separar los datos de forma lógica, cada partición puede gestionarse de manera independiente; por ejemplo, eliminarse cuando expira según una política de TTL.

Como se muestra en el ejemplo anterior, el particionado se especifica en una tabla cuando se define inicialmente mediante la cláusula `PARTITION BY`. Esta cláusula puede contener una expresión SQL sobre cualquier columna o columnas, cuyos resultados definirán a qué partición se envía una fila. Esto hace que los datos queden asociados lógicamente (mediante un prefijo común en el nombre de la carpeta) con cada partición en el disco, que luego puede consultarse de forma aislada. En el ejemplo anterior, el esquema predeterminado de `otel_logs` particiona por día usando la expresión `toDate(Timestamp)`. A medida que las filas se insertan en ClickHouse, esta expresión se evaluará para cada fila y se enviará a la partición resultante si existe (si la fila es la primera de un día, se creará la partición). Para obtener más detalles sobre el particionado y sus otras aplicaciones, consulta ["Table Partitions"](/es/concepts/core-concepts/partitions).

<Image img="https://mintcdn.com/private-7c7dfe99-mintlify-8c05c8a2/6-CMW43ytOARd9iS/images/use-cases/observability/observability-14.png?fit=max&auto=format&n=6-CMW43ytOARd9iS&q=85&s=da6cc23c3d51eb2bc3c0f9d7bb951bf2" alt="Particiones" size="lg" width="1600" height="1077" data-path="images/use-cases/observability/observability-14.png" />

El esquema de la tabla también incluye `TTL toDateTime(Timestamp) + ${TABLES_TTL}` y la configuración `ttl_only_drop_parts = 1`. La primera cláusula garantiza que los datos se eliminen una vez superado el TTL configurado (3 días de forma predeterminada). La configuración `ttl_only_drop_parts = 1` hace que solo caduquen las partes de datos en las que todos los datos han expirado (en lugar de intentar eliminar filas de forma parcial). Como el particionado garantiza que los datos de días distintos nunca se fusionen, los datos pueden eliminarse de forma eficiente.

<Warning>
  **`ttl_only_drop_parts`**

  Recomendamos usar siempre la configuración [`ttl_only_drop_parts=1`](/es/reference/settings/merge-tree-settings#ttl_only_drop_parts). Cuando esta configuración está habilitada, ClickHouse elimina una parte completa cuando todas las filas que contiene han expirado. Eliminar partes completas en lugar de limpiar parcialmente filas afectadas por TTL (algo que se logra mediante mutaciones con un alto consumo de recursos cuando `ttl_only_drop_parts=0`) permite usar tiempos de `merge_with_ttl_timeout` más cortos y reducir el impacto en el rendimiento del sistema. Si los datos están particionados por la misma unidad con la que se aplica la expiración de TTL, por ejemplo, por día, las partes contendrán de forma natural únicamente datos del intervalo definido. Esto garantizará que `ttl_only_drop_parts=1` pueda aplicarse de forma eficiente.
</Warning>

De forma predeterminada, los datos con TTL expirado se eliminan cuando ClickHouse [fusiona partes de datos](/es/reference/engines/table-engines/mergetree-family/mergetree#mergetree-data-storage). Cuando ClickHouse detecta que los datos han expirado, realiza una fusión fuera de programación.

<Info>
  **Programación de TTL**

  Los TTL no se aplican de inmediato, sino según una programación, como se indicó antes. La configuración de tabla de MergeTree `merge_with_ttl_timeout` establece el retraso mínimo, en segundos, antes de repetir una fusión con TTL de eliminación. El valor predeterminado es 14400 segundos (4 horas). Pero ese es solo el retraso mínimo; puede pasar más tiempo hasta que se active una fusión de TTL. Si el valor es demasiado bajo, se realizarán muchas fusiones fuera de programación que pueden consumir muchos recursos. La expiración de TTL puede forzarse con el comando `ALTER TABLE my_table MATERIALIZE TTL`.
</Info>

<div id="modifying-ttl">
  ## Modificar TTL
</div>

Para modificar TTL, puede optar por una de estas opciones:

1. **Modificar los esquemas de la tabla (recomendado)**. Para ello, es necesario conectarse a la instancia de ClickHouse, por ejemplo, mediante [clickhouse-client](/es/concepts/features/interfaces/cli) o [Cloud SQL Console](/es/products/cloud/features/sql-console-features/sql-console). Por ejemplo, podemos modificar el TTL de la tabla `otel_logs` mediante el siguiente DDL:

```sql theme={null}
ALTER TABLE default.otel_logs
MODIFY TTL TimestampTime + toIntervalDay(7);
```

2. **Modifique el OTel collector**. El ClickStack OpenTelemetry collector crea tablas en ClickHouse si no existen. Esto se logra mediante el ClickHouse exporter, que a su vez expone un parámetro `ttl` para controlar la expresión TTL predeterminada, por ejemplo.

```yaml theme={null}
exporters:
 clickhouse:
   endpoint: tcp://localhost:9000?dial_timeout=10s&compress=lz4&async_insert=1
   ttl: 72h
```

<div id="column-level-ttl">
  ### TTL a nivel de columna
</div>

Los ejemplos anteriores hacen que los datos caduquen a nivel de tabla. También puede hacer que los datos caduquen a nivel de columna. A medida que los datos envejecen, esto puede usarse para eliminar columnas cuyo valor en las investigaciones no justifica el consumo de recursos que supone conservarlas. Por ejemplo, recomendamos conservar la columna `Body` por si se añaden nuevos metadatos dinámicos que no se hayan extraído en el momento de la inserción, p. ej., una nueva etiqueta de Kubernetes. Tras un período de, p. ej., 1 mes, puede resultar evidente que estos metadatos adicionales no son útiles, lo que reduce el valor de conservar la columna `Body`.

A continuación, mostramos cómo puede eliminarse la columna `Body` después de 30 días.

```sql theme={null}
CREATE TABLE otel_logs_v2
(
        `Body` String TTL Timestamp + INTERVAL 30 DAY,
        `Timestamp` DateTime,
 ...
)
ENGINE = MergeTree
ORDER BY (ServiceName, Timestamp)
```

<Note>
  Para especificar un TTL a nivel de columna, los usuarios deben definir su propio esquema. Esto no se puede especificar en el OTel collector.
</Note>
