Introduction

One of the problems of the built-in sqlite3 module is that doesn’t work very well with multithreading. S3M - is a wrapper of sqlite3 that allows you to easily do multithreading:

  • It locks parallel database operations so that only one can run at a time.
  • It can also lock transactions (enabled by default) so that only one transaction can be active at a time.
  • You won’t get an OperationalError saying that the database is locked. All the database operations will just run in a queue.

Keep in mind that this library can only help you with threads, not processes.

What else is different from sqlite3?

  • You can freely share connections between threads (not that you have to), given check_same_thread=False.
  • You can use the with statement with the connection object to acquire the locks.

Example

import random
import threading

# Try replacing 's3m' with 'sqlite3' and see what happens
import s3m

# Open the database file,
# isolation_level=None is needed to prevent sqlite3 from starting transactions on its own
conn = s3m.connect("s3m_example.db", isolation_level=None)

# Create table if it doesn't already exist
conn.execute("CREATE TABLE IF NOT EXISTS numbers(number INTEGER)")

def thread_func():
    conn = s3m.connect("s3m_example.db", isolation_level=None)
    conn.execute("BEGIN IMMEDIATE")
    conn.execute("INSERT INTO numbers VALUES(?)", (random.randint(1, 100),))

    # Imagine there's some intense database work going on
    time.sleep(1)

    conn.commit()

# Make 10 threads
threads = [threading.Thread(target=thread_func) for i in range(10)]

# Start them
for thread in threads:
    thread.start()

for thread in threads:
    thread.join()

# Now let's look at what we got
result = conn.execute("SELECT * FROM numbers").fetchall()
print(result)

As you can see from this example, the usage is pretty much the same as with built-in sqlite3.