Como eu deixei meu banco de dados open-source 4 VEZES mais rápido
Tags: database, python, opensource, performance
Published: 2023-04-05 01:10:39

Sim, eu desenvolvi um banco de dados noSQL em Python chamado [CookieDB](https://github.com/jaedsonpys/cookiedb) que armazena seus dados em arquivos criptografados de forma rápida e simples, sem complicações. Mas no início, não era tão rápido assim...

## Conhecendo o CookieDB

Eu decidi criar o este banco de dados como forma de aprendizado, além de estar empolgado com a ideia de usar meu próprio banco de dados em outros projetos.

A ideia era armazenar todos os dados em **um arquivo JSON**, utilizando uma classe em Python com métodos para criar, obter, atualizar e deletar dados. Algo realmente simples. Porém, como forma de segurança, eu adicionei a possibilidade de criptografar o banco de dados usando a biblioteca [cryptography](https://pypi.org/project/cryptography). Mas o problema de performance começa ao usar o **JSON** e a biblioteca **cryptography**.

## Escrita e leitura de um grande volume de dados 😰

Imagine que uma aplicação receba 1 milhão de usuários em um dia, obviamente esses usuários não se registraram de uma vez só, mas cada escrita de um novo usuário no banco de dados demora alguns milissegundos que, somados no final do dia, resultarão em alguns segundos ou minutos, dependendo do desempenho do banco de dados. Se o número de usuários por dia aumentar, o tempo total também aumenta, podendo causar transtornos na aplicação.

Então, escrevi um código que adiciona, obtém e deleta 1 milhão de usuários para testar a performance entre as versões `7.0` e `5.0` do **CookieDB**. A versão `7.0` possui as otimização feitas para alcançar uma velocidade **4 vezes** maior que a versão `5.0`, que não possui tais otimizações. Esse é o código:

```python
import random
import secrets
import time

import cookiedb

cookiedb_version = cookiedb.__version__

db = cookiedb.CookieDB(key='ultra-secret-key!')
db.create_database(f'test-{cookiedb_version}', if_not_exists=True)
db.open(f'test-{cookiedb_version}')


def generate_data(users_number: int) -> list:
    names = ['Jaedson', 'Pedro', 'Maria', 'Lucas',
             'Larissa', 'Mateus', 'Edvaldo', 'Luíza']
    password = secrets.token_hex(32)

    users = []

    for __ in range(users_number):
        name = random.choice(names)
        email = name.lower() + '@mail.com'

        users.append({
            'name': name,
            'email': email,
            'password': password
        })

    return users


def test_add_users(users: list) -> None:
    db.add('users/', users)


def test_get_users() -> None:
    db.get('users/')


def test_delete_users() -> None:
    db.delete('users/')


def main() -> None:
    users_test_number = 1000000
    total_time = 0

    print(f'Test CookieDB version {cookiedb_version}')
    print(f'Test with {users_test_number} users data')

    users = generate_data(users_test_number)
    print('Data ready. Testing...\n')

    start = time.time()
    test_add_users(users)
    test_time = time.time() - start
    total_time += test_time

    print(f'Add users in {test_time:.4f}')

    start = time.time()
    test_get_users()
    test_time = time.time() - start
    total_time += test_time

    print(f'Get users in {test_time:.4f}')

    start = time.time()
    test_delete_users()
    test_time = time.time() - start
    total_time += test_time

    print(f'Delete users in {test_time:.4f}')

    print(f'\nTest finished in {total_time:.4f}')


main()
```

Agora, veja a comparação de performance entre as duas versões do banco de dados CookieDB:

### Versão 5.0

```
Test CookieDB version 5.0.0
Test with 1000000 users data
Data ready. Testing...

Add users in 13.4102
Get users in 9.3546
Delete users in 18.8088

Test finished in 41.5736
```

### Versão 7.0

```
Test CookieDB version 7.0.0
Test with 1000000 users data
Data ready. Testing...

Add users in 3.0604
Get users in 2.5955
Delete users in 5.1419

Test finished in 10.7977
```

Observem a diferença: de 40 para apenas 10 segundos para executar três ações com 1 milhão de usuários. Pode parecer pouco, mas se executarmos esse teste com 10 milhões de usuários, a diferença seria de 7 minutos para 1 minuto!

## Os problemas que encontrei...

Os problemas que encontrei estão relacionados a biblioteca `cryptography` e o módulo `json`, que já vem embutido no Python 3.

### Biblioteca `cryptography`

O resultado da criptografia utilizando a biblioteca `cryptography` é codificado em `base64` para facilitar o transporte do conteúdo criptografado. O que é desnecessário em meu projeto, já que o banco de dados é local.

Obviamente, a codificação em `base64` leva tempo, inclusive se o conteúdo a ser codificado for grande.

Para resolver este problema, utilizei a biblioteca [pycryptodome](https://pypi.org/project/pycryptodome/), que permite manipular a criptografia de uma forma "pura", onde o resultado da criptografia é retornado em bytes, sem serem codificados em `base64`. Com isso, criei uma classe em Python para facilitar a criptografia dos dados. Você pode ver o código no [arquivo `_encrypt.py`](https://github.com/jaedsonpys/cookiedb/blob/master/cookiedb/_encrypt.py) do projeto CookieDB.

### Módulo `json`

O problema aqui não é o módulo em si, e sim **sua velocidade** em codificar e decodificar dados JSON. Isso pode ser resolvido utilizando o módulo `pickle` do Python, onde você pode serializar dicionários, listas, strings, classes e diversos tipos da linguagem Python. Além de dar uma grande melhoria na velocidade, o tamanho do arquivo também diminui muito, já que não é preciso ser lido por humanos, igual o JSON.

## Conclusão

Procure otimizar seu projeto ao máximo, há sempre uma forma de deixá-lo mais rápido, seja criando suas próprias soluções ou utilizando soluções já desenvolvidas e testadas.

O projeto CookieDB passa por diversas mudanças constantemente, seja para uma nova funcionalidade, correção de bugs ou melhoria de performance. Meu objetivo é fazer com que ele seja usável em grandes aplicações, e pra isso, é necessário um bom trabalho no desenvolvimento.

### Links úteis

- [Código-fonte CookieDB](https://github.com/jaedsonpys/cookiedb)
- [Documentação CookieDB](https://jaedsonpys.github.io/cookiedb)
- [Meu perfil no GitHub](https://github.com/jaedsonpys)