Edit: I get periodic upvotes on this. If you'd like to kill access without rebooting the terminal, then from commandline you can do:
from django
import db
db.connections.close_all()
As others have told, there is another process that is using the SQLite file and has not closed the connection. In case you are using Linux, you can see which processes are using the file (for example db.sqlite3
) using the fuser
command as follows:
$ sudo fuser - v db.sqlite3 USER PID ACCESS COMMAND / path / to / db.sqlite3: user 955 F....apache2
If you want to stop the processes to release the lock, use fuser -k
which sends the KILL
signal to all processes accessing the file:
sudo fuser - k db.sqlite3
Here's my code that runs FooModel.objects.get_or_create
simultaneously from two different threads, in case it is helpful:
from concurrent.futures
import ThreadPoolExecutor
import configurations
configurations.setup()
from django.db
import transaction
from submissions.models
import ExerciseCollectionSubmission
def makeSubmission(user_id):
try:
with transaction.atomic():
e, _ = ExerciseCollectionSubmission.objects.get_or_create(
student_id = user_id, exercise_collection_id = 172)
except Exception as e:
return f 'failed: {e}'
e.delete()
return 'success'
futures = []
with ThreadPoolExecutor(max_workers = 2) as executor:
futures.append(executor.submit(makeSubmission, 296))
futures.append(executor.submit(makeSubmission, 297))
for future in futures:
print(future.result())
Btw, if you want to just test PostgreSQL:
docker run--rm--name django - postgres\ - e POSTGRES_PASSWORD = mypassword\ - e PGPORT = 5432\ - e POSTGRES_DB = myproject\ - p 5432: 5432\ postgres: 9.6 .17 - alpine
Change the settings.py
to add this DATABASES
:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'myproject',
'USER': 'postgres',
'PASSWORD': 'mypassword',
'HOST': 'localhost',
'PORT': '5432',
}
}
...and add database adapter:
pip install psycopg2 - binary
In my case, I added a new record manually saved and again through shell tried to add new record this time it works perfectly check it out.
In [7]: from main.models import Flight
In [8]: f = Flight(origin="Florida", destination="Alaska", duration=10)
In [9]: f.save()
In [10]: Flight.objects.all()
Out[10]: <QuerySet [<Flight: Flight object (1)>, <Flight: Flight object (2)>, <Flight: Flight object (3)>, <Flight: Flight object (4)>]>
More specifically, using DRF, I was overriding create method in a view, and I did
def create(self, request, * args, ** kwargs):
....
....
return self.create(request, * args, ** kwargs)
Treat the "database is locked" as if it were a "serialization error", which it kind of is. That is, the app must retry the transaction until it commits. That works, but is somewhat unsatisfactory. ,The thread that didn't get the write lock immediately gets a "database is locked" error. Its transaction is aborted. , I'd like to propose a change like this, which I think would fix a class of SQLite "database is locked" problems. But it's possible you want to add a config option, or an argument to transaction.atomic(), or something of the kind. , Explanation: Consider a type of transaction consisting of, for example, an update_or_create() call, which you run in a "with transaction.atomic()" context. Suppose two threads or processes run such a transaction at exactly the same time (but with different keys, doesn't matter, SQLite locks the whole database anyway).
I'd like to propose a change like this, which I think would fix a class of SQLite "database is locked" problems. But it's possible you want to add a config option, or an argument to transaction.atomic(), or something of the kind.
diff--git a / django / db / backends / sqlite3 / base.py b / django / db / backends / sqlite3 / base.py index 3989028930. .391 a50789e 100644 -- - a / django / db / backends / sqlite3 / base.py ++ + b / django / db / backends / sqlite3 / base.py @ @ - 272, 7 + 272, 7 @ @ class DatabaseWrapper(BaseDatabaseWrapper): Staying in autocommit mode works around a bug of sqlite3 that breaks savepoints when autocommit is disabled. "" " - self.cursor().execute("BEGIN") + self.cursor().execute("BEGIN IMMEDIATE") def is_in_memory_db(self): return self.creation.is_in_memory_db(self.settings_dict['NAME'])
If you add the following to your project's __init__.py
:
from django.db import connection, DEFAULT_DB_ALIAS from django.db import transaction def _monkey_patch_atomic(): def atomic(using = None, savepoint = True, immediate = False): # Bare decorator: @atomic--although the first argument is called # `using`, it 's actually the function being decorated. if callable(using): a = transaction.Atomic(DEFAULT_DB_ALIAS, savepoint)(using) # Decorator: @atomic(...) or context manager: with atomic(...): ... else: a = transaction.Atomic(using, savepoint) a.immediate = immediate return a def __enter__(self): connection = transaction.get_connection(self.using) if not connection.in_atomic_block: # Reset state when entering an outermost atomic block. connection.commit_on_exit = True connection.needs_rollback = False if not connection.get_autocommit(): # Pretend we 're already in an atomic block to bypass the code # that disables autocommit to enter a transaction, and make a # note to deal with this case in __exit__. connection.in_atomic_block = True connection.commit_on_exit = False if connection.in_atomic_block: # We 're already in a transaction; create a savepoint, unless we # were told not to or we 're already waiting for a rollback. The # second condition avoids creating useless savepoints and prevents # overwriting needs_rollback until the rollback is performed. if self.savepoint and not connection.needs_rollback: sid = connection.savepoint() connection.savepoint_ids.append(sid) else: connection.savepoint_ids.append(None) else: if self.immediate: connection.set_autocommit(False) connection.cursor().execute('BEGIN IMMEDIATE') else: connection.set_autocommit(False, force_begin_transaction_with_broken_autocommit = True) connection.in_atomic_block = True transaction.atomic = atomic transaction.Atomic.immediate = False transaction.Atomic.__enter__ = __enter__ _monkey_patch_atomic()
You can then do:
with atomic(immediate = True):
ModelName.objects.update_or_create(foo = foo)
The practical reason for this is usually that the python or django shells have opened a request to the DB and it has not been closed properly; killing your terminal access usually frees it up. I am getting this error on running command line tests today.,I have restarted the server as well, but still, the error persists.,I have been doing some repetitive processes in my application (testing it), and suddenly I’m receiving a weird error:,If you want to kill access without rebooting the terminal, then from commandline you can do:
OperationalError: database is locked
from django
import db
db.connections.close_all()