De Airtable a AWS S3

Introducción

En este post explicamos cómo exportamos nuestros datos desde Airtable, cómo los copiamos en AWS S3 y cómo hacer consultas sobre nuestros datos en S3. Para esto último, comparamos dos formas de hacerlo y miramos las implicaciones que tienen en términos de uso de memoria y coste. Vemos que ambos métodos, si bien son sencillos, son ineficientes en términos de memoria y no serían la forma adecuada de hacerlo a gran escala, por el simple hecho que implican copiar los datos en lugar de trabajar sobre los datos en origen.

Pero, antes de entrar en el detalle de estos pasos ¿Por qué usamos Airtable? ¿Por qué hacer un back up de nuestros datos? y ¿Por qué AWS S3?

Airtable

Hace unos años empezamos a utilizar Airtable para registrar nuestras oportunidades de inversión. Es nuestro CRM. Antes utilizábamos Excel. La ventaja de utilizar Airtable está sobre todo en la usabilidad. Es muy fácil implementar formularios o interfaces web para introducir datos o visualizarlos de forma mucho más cómoda que en una hoja de cálculo. También es muy fácil tener vistas personalizadas e implementar automatizaciones.

¿Por qué hacer un back up?

Nuestra experiencia con Airtable ha sido buena, pero quizás en el futuro queremos cambiar de herramienta y para eso tendremos que exportar nuestros datos; también, a veces queremos probar alguna herramienta de analytics y una buena forma de hacerlo es con nuestros datos. Por último, ahora es relativamente fácil exportar datos de Airtable, pero esto podría cambiar en el futuro, por lo que pensamos que es una buena idea hacer back ups periódicos.

¿Por qué AWS S3?

S3 es uno de los varios servicios de almacenamiento que ofrece AWS. Hemos escogido AWS S3 por 3 motivos: i) es un tipo de almacenaje muy escalable y económico; ii) cada vez hay más herramientas que tienen integraciones con AWS S3 (como Langchain, Retool o Hex Analytics); iii) no tenemos que especificar previamente un schema para almacenar nuestros datos en S3.

El último punto es importante para nuestro caso concreto. AWS S3 permite almacenar datos estructurados y no estructurados. Cuando nosotros sacamos nuestros datos de Airtable, éstos ya tienen una estructura que conocemos y esto es algo que aprovecharemos. En concreto, lo que exportamos de Airtable es una lista de elementos, donde cada elemento es una oportunidad de inversión que está guardada en un formato json. Cada elemento tiene una colección de atributos que conocemos a priori y que nos facilitarán más adelante hacer consultas sin haber especificado un schema para AWS S3. Aquí mostramos un fragmento de un JSON con una oportunidad de inversión (que acabó siendo una participada de Athos):

{
    "id": "rec01ZI5MW438Wiu3",
    "fields": {
        "Opportunity": "Vosbor",
        "Register date": "2020-09-15",
        "Based in": "Netherlands",
        "Source": "JACEK ŁUBIŃSKI (MOC)",
        "Repeat?": "No",
        ...
        "B2b/b2c": "b2b",
        "Current status": "Portfolio",
				...
        "Tags": ["Commodities"]
    }
}

Paso 1: Exportar Datos desde Airtable

Primero, necesitamos obtener nuestros datos de Airtable. Accedemos a la API de Airtable con nuestro Personal Access Token para recuperar todos nuestros registros y guardarlos en un archivo JSON.

En el código siguiente de Python mostramos cómo lo hacemos. Los principales elementos de este código son: la variable url, que almacena el endpoint donde está nuestra tabla en Airtable; y data, que es la variable que almacena los datos extraídos de Airtable.

Script en Python para Obtener Datos de Airtable:

# Función para obtener datos de Airtable
def fetch_all_records():
    url = f'<https://api.airtable.com/v0/{base_id}/{table_name}>'
    headers = {'Authorization': f'Bearer {personal_access_token}'}
    records = []
    offset = None

    while True:
        params = {}
        if offset:
            params['offset'] = offset

        # Llamada a la API
        response = requests.get(url, headers=headers, params=params)
        data = response.json()
        records.extend(data['records'])

        if 'offset' in data:
            offset = data['offset']
        else:
            break

    return records

# Llamar función que obtiene los datos de Airtable
data = fetch_all_records()

# Guardar datos en un archivo JSON
with open('airtable_data.json', 'w') as f:
    json.dump(data, f, indent=4)

print("Datos obtenidos y guardados exitosamente en airtable_data.json")

Paso 2: Subir Nuestros Datos en Formato JSON a AWS S3

Ahora que tenemos nuestros datos en un fichero con formato JSON, es hora de subirlos a AWS S3. Previamente tenemos que haber creado un bucket de S3 desde el panel de control de AWS; definir un usuario IAM con el permiso “AmazonS3FullAccess” (IAM es el servicio de AWS que permite definir los recursos a los que puede acceder cada usuario); y guardar el access key y el secret key proporcionados.

Script en Python para Subir nuestros datos JSON a S3:

# Credenciales de AWS
access_key = 'YOUR_AWS_ACCESS_KEY'
secret_key = 'YOUR_AWS_SECRET_KEY'
bucket_name = 'YOUR_BUCKET_NAME'
s3_file_name = 'airtable_data.json'

# Inicializar cliente S3
s3 = boto3.client('s3',
                  aws_access_key_id=access_key,
                  aws_secret_access_key=secret_key,
                  region_name='us-east-1')

# Subir nuestro archivo json a S3
s3.upload_file('airtable_data.json', bucket_name, s3_file_name)

print("Archivo subido exitosamente.")

Este script configura nuestras credenciales de AWS y los detalles del bucket S3, inicializa un cliente S3 usando la librería boto3, y sube el archivo JSON al bucket de S3 especificado.

Paso 3: Analizar Nuestros Datos en Python

AWS S3 no tiene funcionalidades propias para hacer consultas de datos (querying); es necesario usar un servicio adicional como AWS Athena para hacer consultas sobre los datos en S3.

Una alternativa común es implementar un script de Python que lea todos nuestros datos de AWS S3 y los procese como queremos. Esta opción es la que mostramos a continuación. Como analizamos en más detalle más adelante, ésta es una opción ineficiente en términos de memoria, ya que estamos copiando los datos en lugar de analizarlos en origen (en AWS S3).

Veremos dos opciones para analizar nuestros datos en Python.

  • En el primer caso (opción 1), leemos los datos de AWS S3 directamente desde el script de Python y los analizamos con las funciones de la librería Pandas; esto implica que los datos son copiados y utilizan la memoria temporal de nuestro ordenador.
  • En el segundo caso (opción 2), primero descargamos todos nuestros datos a nuestro ordenador y luego los leemos desde el script de Python y los procesamos con Pandas. Esto implica que los datos serán copiados y ocuparon espacio en el disco duro de nuestro ordenador, y luego serán copiados otra vez (cuando son leídos desde Python) y utilizarán memoria temporal de nuestro ordenador.

La segunda opción, como explicamos mejor más abajo, puede tener sentido en contextos donde vamos a realizar un número muy alto de consultas sobre nuestros datos y no nos preocupa que los datos que hemos descargado dejen de ser vigentes (el problema de data staleness). También puede tener sentido descargar los datos si no vamos a tener acceso continuo a la red.

Pero primero vamos a mostrar los dos scripts que implementan las dos opciones comentadas.

Opción 1 – consultar nuestros datos en AWS S3 desde un script de Python

# Inicializar cliente S3
s3 = boto3.client('s3',
                  aws_access_key_id=access_key,
                  aws_secret_access_key=secret_key,
                  region_name='us-east-1')

# Leer el archivo JSON directamente desde S3
obj = s3.get_object(Bucket=bucket_name, Key=s3_file_name)
data = json.loads(obj['Body'].read())

# Procesar datos JSON
records = []
for record in data:
    if 'fields' in record:
        fields = record['fields']
        fields['id'] = record.get('id', None)
        fields['createdTime'] = record.get('createdTime', None)
        records.append(fields)

df = pd.DataFrame(records)

# Consultar los datos
portfolio_companies = df[df['Current status'] == 'Portfolio']

# Seleccionar y mostrar solo los campos 'Company', 'Date', y 'One line description'
filtered_companies = portfolio_companies[['Company', 'Date', 'One line description']]

print(filtered_companies)

En este script, inicializamos un cliente S3 y leemos el archivo JSON que está en S3. Los datos se procesan y convierten en un DataFrame de la librería Pandas. Desde allí, podemos consultar los datos usando Pandas, lo cual ofrece una manera potente y flexible de manejar los datos.

Output:

Al ejecutar este script, verás el DataFrame filtrado, mostrando sólo los registros de compañías que forman parte de nuestro portfolio. A continuación mostramos un fragmento del output:

    Company        Date                       One line description
0   Vosbor    15/9/2020   Blockchain-based commodity trading platform
1 Kaleidos   27/11/2020            open source prototyping platform

Opción 2 – primero descargar nuestros datos antes de procesarlos con Python

Añadimos las siguientes líneas de código en distintos puntos del script anterior (opción 1):

# Descargar el archivo JSON desde S3
s3.download_file(bucket_name, s3_file_name, 'local_airtable_data.json')

# Cargar el archivo JSON
with open('local_airtable_data.json', 'r') as f:
    data = json.load(f)

# Consultar los datos
portfolio_companies = df[df['Current status'] == 'Portfolio']
print(filtered_companies)

Este script descarga el archivo JSON desde S3 a tu máquina local, lo carga en un DataFrame de Pandas, y lo consulta.

El output será el mismo que obtenemos con la opción anterior.


Analizamos el coste y el uso de memoria de las dos opciones anteriores

Vamos a analizar el coste y el uso de memoria de las dos opciones anteriores.

  • Primero calculamos el tamaño de nuestros datos (el fichero JSON que exportamos de Airtable y subimos a AWS S3).
  • Después cuantificamos el uso de memoria RAM cuando leemos nuestros datos desde un script de Python y los procesamos con Pandas.
  • Por último cuantificamos los costes de AWS que implican las distintas opciones.

Tamaño del Archivo JSON

Primero, determinamos el tamaño del archivo JSON.

file_path = '/mnt/data/airtable_all_data.json'
file_size = os.path.getsize(file_path)  # Tamaño del archivo en bytes
file_size_mb = file_size / (1024 * 1024)  # Convertimos a MB

print(f"Tamaño del archivo JSON: {file_size_mb:.2f} MB")

El archivo JSON con los datos que hemos sacado de Airtable tiene un tamaño de 4.70 MB.

Uso de Memoria RAM

A continuación, cargamos el archivo JSON en un DataFrame de Pandas y medimos el uso de memoria.

Para calcular el overhead de memoria: primero medimos el uso de memoria al cargar el archivo JSON; luego convertimos el JSON en un DataFrame de Pandas y medimos el uso de memoria nuevamente. La diferencia entre estos dos valores es el overhead, que representa la memoria adicional que utiliza Pandas para gestionar y manipular los datos.

Código para medir el uso de memoria y el overhead

file_path = '/mnt/data/airtable_all_data.json'

# Medir el uso de memoria del archivo JSON
tracemalloc.start()
with open(file_path, 'r') as f:
    data = json.load(f)
current, peak = tracemalloc.get_traced_memory()
json_memory_usage = peak / (1024 * 1024)  # Convertimos a MB
tracemalloc.stop()
print(f"Uso de memoria al cargar el JSON: {json_memory_usage:.2f} MB")

# Medir el uso de memoria del DataFrame de pandas
tracemalloc.start()
records = [record['fields'] for record in data if 'fields' in record]
df = pd.DataFrame(records)
current, peak = tracemalloc.get_traced_memory()
df_memory_usage = peak / (1024 * 1024)  # Convertimos a MB
tracemalloc.stop()
print(f"Uso de memoria del DataFrame: {df_memory_usage:.2f} MB")

# Calcular el overhead
overhead_memory_usage = df_memory_usage - json_memory_usage
print(f"Overhead de memoria al convertir a DataFrame: {overhead_memory_usage:.2f} MB")

En este ejemplo, al cargar el archivo JSON, el uso de memoria es aproximadamente 5 MB y el overhead adicional al convertirlo a un DataFrame de pandas es de 3 MB, más de un 50% de los datos originales que queremos procesar.

Cálculo de Costes de AWS

Opción 1 – consultar nuestros datos en AWS S3 desde un script de Python

El coste de almacenamiento en S3 es aproximadamente $0.023 por GB al mes. Para calcular el coste de almacenamiento de nuestro archivo de 4.70 MB, usamos la fórmula: (4.70 MB / 1024 MB) * $0.023, lo que da un coste mensual de aproximadamente $0.0001055.

El coste de una solicitud GET estándar en S3 es de $0.0004 por 1,000 solicitudes. Supongamos que realizamos 10 consultas al mes. El coste mensual de consultas sería: 10 * $0.0004 / 1000, resultando en aproximadamente $0.000004.

Opción 2 – primero descargar nuestros datos antes de procesarlos con Python

El coste de almacenamiento en S3 es el mismo que el del método 1, $0.0001055 por mes.

El coste de transferencia de datos fuera de S3 es de $0.09 por GB, siendo el primer GB gratis cada mes. Para calcular el coste de transferencia de nuestro archivo de 4.70 MB, usamos la fórmula: (4.70 MB / 1024 MB) * $0.09, lo que da un coste de transferencia de datos de aproximadamente $0.0004125.

Podemos ver con el cálculo anterior que el coste de descargar los datos es aproximadamente el mismo que hacer 1000 lecturas de nuestros datos desde un script de Python.

Conclusión

En este artículo, hemos cubierto todo el proceso de migrar datos desde Airtable a AWS S3 y luego consultarlos desde un script de Python.

En este proceso hemos visto lo siguiente:

  • Es muy fácil exportar nuestros datos de Airtable y guardarlos en AWS S3.
  • Nuestros datos ocupan un espacio muy reducido en un fichero JSON.
  • El coste de guardar nuestros datos en AWS S3 es muy reducido.
  • El coste de leer y procesar nuestros datos desde un script de Python es muy reducido. No obstante, vemos que los métodos que hemos implementado son ineficientes en términos de memoria y no son escalables.
  • Descargar nuestros datos a nuestro ordenador para analizarlos sólo tiene sentido si vamos a realizar un número muy alto de consultas y no nos preocupa que los datos que estamos procesando puedan quedar desactualizados.

El siguiente paso podría ser integrar AWS Athena en nuestro flujo de trabajo. Utilizar AWS Athena ofrece varias ventajas significativas. En primer lugar, podemos ejecutar consultas SQL estándar sobre los datos almacenados en S3 sin necesidad de moverlos, lo que simplifica enormemente el proceso de análisis. Además, con Athena solo pagamos por las consultas que ejecutamos y por la cantidad de datos escaneados, lo cual puede ser más económico en comparación con mantener una infraestructura de base de datos dedicada. Otra ventaja importante es la integración perfecta de Athena con otros servicios de AWS, como AWS Glue, que nos puede ayudar a catalogar y preparar los datos para su análisis.

Para implementar AWS Athena, los próximos pasos serían crear una base de datos en Athena, lo que implicaría definir el esquema de datos para que Athena pueda interpretarlos correctamente y comenzar a realizar consultas SQL directamente sobre los datos en S3. Estos nos permitiría realizar consultas avanzadas sobre nuestros datos sin duplicarlos.

Keep reading