El seguimiento del uso de la API puede ser todo un reto técnico debido a la alta velocidad y el volumen de solicitudes. Sin embargo, tener un análisis preciso de su API es crucial, especialmente si depende de ella para facturar a sus clientes. Es posible ser rápido y preciso con una base de datos de series temporales llamada TimescaleDB. De hecho, esta es la solución que implementamos detrás de NLP Cloud.

¿Qué es el análisis de la API y por qué es difícil?

El análisis de la API consiste en recuperar diversas métricas relacionadas con el uso de su API.

Por ejemplo, detrás de la API NLP Cloud queremos saber lo siguiente:

  • Cuántas solicitudes se hicieron durante el último minuto, la última hora, el último día, el último mes y el último año
  • Cuántas solicitudes se hicieron por punto final de la API y por usuario
  • Cuántas palabras fueron generadas por nuestros modelos NLP de generación de texto (como GPT-J)
  • Cuántos caracteres fueron utilizados por nuestro complemento de NLP multilingüe

Todas estas métricas son importantes para entender mejor cómo utilizan nuestra API nuestros clientes. Sin estos datos, no podemos saber qué modelos de PNL son los más utilizados, quiénes son nuestros clientes más importantes, etc.

Pero lo más importante es que algunas de estas métricas se utilizan para la facturación. Por ejemplo, a los clientes suscritos a un plan de “pago por uso” se les cobra en función del número de palabras que han generado con nuestra API.

Un volumen muy elevado de datos pasa por nuestra pasarela API, lo que supone un reto en términos de rendimiento. Es muy fácil que la API se ralentice o que se pierdan algunos datos.

Por lo tanto, es crucial que dicho sistema de análisis de la API sea rápido y fiable.

TimescaleDB al rescate

TimescaleDB es una base de datos PostgreSQL optimizada para series temporales.

TimescaleDB

Básicamente, Timescale está optimizado para un alto volumen de escrituras atómicas. Es perfecto para un caso de uso en el que se escriben toneladas de datos de forma muy regular, pero casi nunca se alteran estos datos, y sólo se leen los datos ocasionalmente.

Timescale viene con interesantes herramientas que facilitan las series temporales. Por ejemplo, tienen los llamados “agregados continuos”. Estos agregados son una forma de “muestrear” automáticamente sus datos de forma regular. El muestreo descendente significa que se eliminan los datos antiguos después de un tiempo y sólo se conservan algunos agregados de estos datos (basados en sumas, recuentos, promedios, etc.). Es crucial por dos razones:

  • Las series temporales pueden crecer muy rápidamente, por lo que es una muy buena forma de ahorrar espacio en disco
  • La lectura de una tabla repleta de datos puede ser dolorosamente lenta. Es mucho más fácil leer los datos de una tabla agregada que contiene menos datos.

A diferencia de otras soluciones como InfluxDB, TimescaleDB es una solución puramente SQL, por lo que la curva de aprendizaje es bastante baja, y hará la integración mucho más fácil. Por ejemplo, en NLP Cloud estamos interactuando con TimescaleDB tanto en aplicaciones Python como Go y podemos utilizar nuestras librerías PostgreSQL habituales.

Instalación

Puedes instalar TimescaleDB como un paquete de sistema, pero es más sencillo instalarlo como un contenedor Docker.

En primer lugar, extraiga la imagen Docker:

docker pull timescale/timescaledb:latest-pg14

A continuación, inicie su contenedor y pase una contraseña para su DB:

docker run -d --name timescaledb -p 5432:5432 -e POSTGRES_PASSWORD=password timescale/timescaledb:latest-pg14

Estructura de datos en TimescaleDB

En este ejemplo, queremos almacenar las peticiones de la API. Queremos que cada solicitud contenga lo siguiente:

  • La hora de la solicitud
  • El ID del usuario que hizo la solicitud
  • El punto final de la API utilizado durante la solicitud

La primera vez que lances TimescaleDB, tendrás que crear varias cosas.

En primer lugar, iniciar la extensión de TimescaleDB.

CREATE EXTENSION IF NOT EXISTS timescaledb;

Crear la tabla que almacenará las peticiones de la API, como haríamos en cualquier BD de PostgreSQL:

CREATE TABLE IF NOT EXISTS api_calls (
  time TIMESTAMPTZ NOT NULL,
  user_id TEXT NOT NULL,
  endpoint TEXT NOT NULL
);

A continuación, creamos una llamada “hipertabla” a partir de ella:

SELECT create_hypertable('api_calls', 'time', if_not_exists => TRUE);

Las hipertablas son el corazón de TimescaleDB. Añaden automáticamente muchas cosas inteligentes con el fin de gestionar sus datos de manera eficiente.

Ahora creamos una vista específica de su tabla api_calls llamada api_calls_per_hour. Es una vista que almacenará los datos agregados procedentes de api_calls. Cada hora, el número de peticiones de la API en api_calls será contado y puesto en api_calls_per_hour. La vista será mucho más rápida de consultar ya que contiene muchos menos datos que la tabla inicial api_calls.

CREATE MATERIALIZED VIEW IF NOT EXISTS api_calls_per_hour
WITH (timescaledb.continuous) AS
SELECT time_bucket('1 hour', time) as bucket, user_id, endpoint,
COUNT(time)
FROM api_calls
GROUP BY bucket, user_id, endpoint;

Por último, creamos una política de agregación continua y una política de retención. Ambas serán gestionadas por trabajadores en segundo plano. La mayoría de las veces todo funciona bien, pero si empiezas a tener muchas políticas puedes quedarte sin trabajadores en segundo plano y verás algunos mensajes de error en tus registros. En ese caso, el truco es aumentar el número de trabajadores en segundo plano en /var/lib/postgresql/data/postgresql.conf.

La política de agregación continua se encargará de muestrear regularmente los datos de api_calls y ponerlos en api_calls_per_hour. La política de retención se encargará de borrar los datos antiguos de api_calls para que nunca te quedes sin espacio en el disco:

SELECT add_continuous_aggregate_policy('api_calls_per_hour',
  start_offset => INTERVAL '1 day',
  end_offset => INTERVAL '1 hour',
  schedule_interval => INTERVAL '1 minute',
  if_not_exists => TRUE);

SELECT add_retention_policy('api_calls', INTERVAL '90 days', if_not_exists => TRUE);

Como puedes ver, no era demasiado complejo.

Inserción de datos

En su aplicación, ahora puede conectarse a su base de datos de Timescale e insertar solicitudes.

Por ejemplo, así es como lo harías en Python:

import psycopg2

conn = psycopg2.connect(
  "host=timescaledb dbname={} user={} password={}".format("name", "user", "password"))
cur = conn.cursor()
cur.execute("INSERT INTO api_calls (time, user_id, endpoint) VALUES (%s, %s, %s)",
  (datetime.now(), "1584586", "/v1/gpu/bart-large-cnn/summarization"))
conn.commit()
cur.close()
conn.close()

Y ahora en Go:

import (
  "github.com/jackc/pgx/v4"
  "github.com/jackc/pgx/v4/pgxpool"
)

func main(){
timescaledbURL := fmt.Sprintf("postgres://%s:%s@timescaledb:5432/%s", "user", "password", "name")
timescaledbDatabase, err := pgxpool.Connect(context.Background(), timescaledbURL)
if err != nil {
  log.Fatalf("Cannot connect to TimescaleDB database: %v. Stopping here.", err)
}

query := `INSERT into api_calls (time, user_id, endpoint) VALUES ($1, $2, $3)`
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  defer cancel()

  _, err := timescaledbDatabase.Exec(ctx, query, time.Now(), "1584586", "/v1/gpu/bart-large-cnn/summarization")
  if err != nil {
    log.Printf("Cannot insert metric in TimescaleDB: %v", err)
  }
}

Punto importante: lo más probable es que no quieras ralentizar las peticiones de la API del usuario debido a un procesamiento potencialmente lento en el lado de TimescaleDB. La solución es insertar sus datos de forma asíncrona, de modo que la respuesta de la API del usuario regrese incluso si los datos no están insertados en su DB todavía. Pero esto está más allá del alcance de este artículo.

Para mejorar el rendimiento, también puedes insertar varias solicitudes de la API a la vez. La idea es que primero se necesita almacenar en caché algunas solicitudes en la memoria, y luego guardar muchas de ellas en la DB a la vez después de algún tiempo.

Visualización de datos

Existen muchas herramientas de visualización de datos. A mí me gusta Grafana porque es fácil conectarla a TimescaleDB, y las capacidades de los gráficos son innumerables.

Aquí hay un buen tutorial sobre cómo configurar TimescaleDB con Grafana: verlo aquí.

Grafana

Conclusión

TimescaleDB es una poderosa herramienta para las series de tiempo, y es una gran solución si quieres analizar adecuadamente el uso de tu API.

Como puedes ver, configurar y utilizar TimescaleDB es bastante fácil. Sin embargo, tenga cuidado: TimescaleDB puede utilizar rápidamente mucha memoria RAM, así que asegúrate de tenerlo en cuenta antes de aprovisionar tu instancia de servidor.

Si tienes alguna pregunta, no dudes en preguntar.

Also available in English | Existe aussi en français

Rate limiting de la API con Traefik, Docker, Go y Caching

Limitar el uso de la API basándose en una regla avanzada de limitación de velocidad no es tan fácil. Para lograr esto detrás de la API de NLP Cloud, estamos utilizando una combinación de Docker, Traefik (como proxy inverso) y el almacenamiento en caché local dentro de un script Go. Cuando se hace correctamente, se puede mejorar considerablemente el rendimiento de la limitación de la tasa y estrangular adecuadamente las solicitudes de la API sin sacrificar la velocidad de las solicitudes. Seguir leyendo