AioSQLite: Asynchronous SQLite in Python

SQLite is one of the most widely used database engines in the world. Its lightweight, file-based architecture makes it perfect for embedded databases in desktop and mobile applications. However, Python’s standard SQLite library blocks the event loop while executing queries, hurting performance in asynchronous programs.

Enter AioSQLite – an elegant asynchronous library interacting with SQLite from Python’s asyncio event loop without blocking. Let’s learn how AioSQLite makes SQLite fully non-blocking and explore some real-world use cases through examples.

What is AioSQLite?

AioSQLite is an asynchronousnon-blocking SQLite driver for Python. It allows you to access SQLite databases from asyncio coroutines without blocking the main event loop thread.

Here are some key things to know about AioSQLite:

  • It provides an async version of the SQLite Python API, replicating nearly all the features of sqlite3
  • Uses a background thread per connection to talk to SQLite without blocking asyncio event loop
  • Lets you access SQLite using async/await instead of callbacks
  • Supports async context managers for connections and cursors
  • Works with Python 3.7+

In summary, AioSQLite makes SQLite play nicely with asyncio!

Installing AioSQLite

Installing AioSQLite is as simple as:

pip install aiosqlite

That’s it! Now, let’s look at how to use it.

Basic AioSQLite Usage

The API for AioSQLite mirrors sqlite3, but with async methods instead of synchronous ones.

For example, here is how to connect to an SQLite database file, insert data, and query it:

import aiosqlite

async def main():
    # Connect to a database
    async with aiosqlite.connect('example.db') as db:

        # Insert some data
        await db.execute('INSERT INTO some_table VALUES (42, "demo")')
        await db.commit()

        # Query for the data
        async with db.execute('SELECT * FROM some_table') as cursor:
            print(await cursor.fetchall())

asyncio.run(main())

There are a few key things to notice here:

  • aiosqlite.connect() opens a connection using an async context manager
  • Executing SQL happens via await db.execute(), using await
  • Getting query results uses an async cursor that returns rows via await
  • Transactions happen explicitly via await db.commit()

This allows the database code to run non-blockingly within an asyncio event loop.

Async Context Managers

A key feature of AioSQLite is the use of async context managers to manage connections and cursors automatically:

async with aiosqlite.connect('example.db') as db:
    # db opened automatically
    
    async with db.execute('SELECT * FROM some_table') as cursor:
         # cursor opened
         print(await cursor.fetchall())
         # cursor closed automatically
         
# db closed automatically 

This takes care of opening and closing connections for you.

Advanced Features

AioSQLite replicates most other advanced features of sqlite3 too:

Custom row factories:

db.row_factory = aiosqlite.Row # Access columns by name 
async for row in cursor:
   print(row['column_name'])

Accessing metadata about queries:

cursor = await db.execute('SELECT * FROM some_table')
print(cursor.rowcount)
print(cursor.lastrowid) 

SQLite types:

import aiosqlite

def converter(value):
    # Convert from SQLite types to Python types
    ...

converter_func = aiosqlite.register_converter("CUSTOM", converter)

db.set_type_translation(aiosqlite.PARSE_DECLTYPES)
db.register_converter("CUSTOM", converter_func)

The API supports nearly everything you can do in standard SQLite.

Sample Data Analytics Database

Here is an example of using AioSQLite with a real-world data analytics database containing user analytics events:

import json
import aiosqlite

TABLE_DEF = """
CREATE TABLE events (
  id INTEGER PRIMARY KEY,
  user_id TEXT,
  name TEXT, 
  properties BLOB,
  timestamp DATETIME 
)
"""

async def insert_event(db, event_data):
    """Insert a new event into the events table"""
    sql = """INSERT INTO events 
              (user_id, name, properties, timestamp)  
              VALUES (?, ?, ?, ?)"""

    await db.execute(sql, (
       event_data['userId'],
       event_data['name'],
       json.dumps(event_data['properties']),
       event_data['timestamp']
    ))
    await db.commit()
    
    
async def main():  
    async with aiosqlite.connect('analytics.db') as db:
        await db.execute(TABLE_DEF)
        
        event_data = {
           'userId': '1234',
           'name': 'search',
           'properties': {'query': 'python'},    
           'timestamp': '2023-01-01T12:00:00', 
        }
        
        await insert_event(db, event_data)

This allows storing analytic event data from users in SQLite by leveraging AioSQLite’s non-blocking API!

Key Benefits of AioSQLite

Here are some of the major benefits you get from using AioSQLite:

  • Asynchronous, non-blocking access to SQLite from asyncio programs
  • Avoid callback hell by using async/await instead
  • Context managers handle opening/closing connections/cursors
  • Compatible with Python 3.7+
  • Replicates nearly all functionality of sqlite3 module
  • Enables real-time analytics, streaming, and more use cases by removing SQLite blocking behavior

If you need to use SQLite from an asyncio app, AioSQLite is currently the best way to go about it while avoiding bottlenecks.

Comparison of AioSQLite and sqlite3

Featuresqlite3AioSQLite
Async methodsNoYes
Async context managersNoYes
Non-blockingNoYes
Callback-basedYesCoroutines via async/await
Works with asyncioNoYes
FunctionalityFull SQLite feature setNearly full SQLite feature set

As you can see, AioSQLite retains nearly all the underlying SQLite functionality while adding async capabilities.

Conclusion

AioSQLite brings the simplicity and ubiquity of SQLite to the world of asyncio and Python async programming. With its async API using async/await, context managers, non-blocking behavior, and Python 3.7+ support, AioSQLite is the go-to choice for accessing SQLite from an asyncio application.

Whether you need real-time analytics, streaming processing, or just a simple offline data store, AioSQLite takes the blocking factor out of SQLite. This lets you build highly concurrent database-backed programs without the headaches of callback hell or stalling the event loop.

If you found this useful, be sure to check out the AioSQLite documentation to learn more!