pythonsqlitediscord.pyaiosqlite

What's making this code lock my aiosqlite database?


My discord.py bot handles data inputs (invites, bans, warns, etc.) and inserts them into an aiosqlite database. An error is raised:

OperationalError: database is locked.

Multiple commands work in unison to handle warnings and such. When I invoke my warning system, it locks my database:

@commands.command()
async def warn(self, ctx, member: discord.Member, *,
               reason: str = 'No reason provided.'):
    await self.addwarn(ctx, member, reason=reason) # Add a warning


@commands.command()
async def addwarn(self, ctx, member, *, reason):

    self.bot.db = await aiosqlite.connect("database.db") # Connect to db

    async with self.bot.db.cursor() as cursor:
        await cursor.execute("INSERT OR IGNORE INTO Warns "
                             "(User, Reason, Time, Server) VALUES "
                             "(?,?,?,?)",
                             (member.id, reason,
                             int(datetime.now().timestamp()),
                             member.guild.id)) # Register the warning's reason, member id, time, and guild id

    async with self.bot.db.cursor() as cursor:
        await cursor.execute('SELECT Reason, Time FROM Warns WHERE '
                             'User = ? AND Server = ?',
                             (member.id, member.guild.id)) # Fetch past warning records from db

        data = await cursor.fetchall() # Store past records in a variable
        if data:
            warnnum = 0 # Warning count

            for record in data:
                warnnum += 1 # Increment variable for past records in db

            if warnnum >= 3: # If user has 3 warnings, ban them
                await self.ban_member(ctx, member=member,
                                      reason='User has exceeded their '
                                      'warnings limit, and has been '
                                      'banned as a result.')

            if member.joined_at >= datetime.now(timezone.utc) - timedelta(hours=3): # If user has joined within the
                                                                                    # past 3 hours prior to receiving
                                                                                    # a warning, ban them
                await self.ban_member(ctx, member=member,
                                      reason='User has gained an '
                                      'infraction within a short duration '
                                      'of joining.')
    await self.bot.db.commit() # Commit the changes


@commands.command()
async def ban_member(self, ctx, member: discord.Member, *, reason=None):

    await member.ban(reason=reason) # Ban the member

    await self.clearwarn(ctx, member) # Clear a member's entire warning record when they're banned


async def clearwarn(self, ctx, member: discord.Member):
    self.bot.db = await aiosqlite.connect("database.db")

    async with self.bot.db.cursor() as cursor:
        await cursor.execute('DELETE FROM Warns WHERE User = ? '
                             'AND Server = ?',
                             (member.id, ctx.guild.id)) # Clear a member's entire warning record
    await self.bot.db.commit()

What's making this code lock my aiosqlite database?


Solution

  • SQLite is meant to be a lightweight database, and thus can't support a high level of concurrency. OperationalError: database is locked errors indicate that your application is experiencing more concurrency than SQLite can handle in default configuration. This error means that one thread or process has an exclusive lock on the database connection and another thread timed out waiting for the lock to be released.

    Python's SQLite wrapper has a default timeout value that determines how long the second thread is allowed to wait on the lock before it times out and raises the OperationalError: database is locked error.

    Ways to solve this error