2

First off, I have no idea if "Ownership" is the correct term for this, it's just what I am calling it in Java.

I am currently building a Server that uses SQLite, and I am encountering errors concerning object "ownership":

I have one Module that manages the SQLite Database. Let's call it "pyDB". Simplified:

import threading
import sqlite3

class DB(object):
    def __init__(self):
        self.lockDB = threading.Lock()
        self.conn = sqlite3.connect('./data.sqlite')
        self.c = self.conn.cursor()
        [...]

    def doSomething(self,Param):
        with self.lockDB:
            self.c.execute("SELECT * FROM xyz WHERE ID = ?", Param)

(Note that the lockDB object is there because the Database-Class can be called by multiple concurrent threads, and although SQLite itself is thread-safe, the cursor-Object is not, as far as I know).

Then I have a worker thread that processes stuff.

import pyDB
self.DB = pyDB.DB()

class Thread(threading.Thread):
    [omitting some stuff that is not relevant here]
    def doSomethingElse(self, Param):
        DB.doSomething(Param)

If I am executing this, I am getting the following exception:

self.process(task)
File "[removed]/ProcessingThread.py", line 67, in process
  DB.doSomething(Param)
File "[removed]/pyDB.py", line 101, in doSomething
  self.c.execute(self,"SELECT * FROM xyz WHERE ID = ?", Param)
ProgrammingError: SQLite objects created in a thread can only be used in that same
  thread.The object was created in thread id 1073867776 and this is thread id 1106953360

Now, as far as I can see, this is the same problem I had earlier (Where Object ownership was given not to the initialized class, but to the one that called it. Or so I understand it), and this has led me to finally accept that I generally don't understand how object ownership in Python works. I have seached the Python Documentation for an understandable explanation, but have not found any.

So, my Questions are:

  • Who owns the cursor object in this case? The Processing Thread or the DB thread?
  • Where can I read up on this stuff to finally "get" it?
  • Is the term "Object ownership" even correct, or is there an other term for this in Python? (Edit: For explanations concerning this, read the comments of the main question)

I will be glad to take specific advice for this case, but am generally more interested in the whole concept of "what belongs to who" in Python, because to me it seems pretty different to the way Java handles it, and since I am planning to use Python a lot in the future, I might as well just learn it now, as this is a pretty important part of Python.

Cœur
  • 37,241
  • 25
  • 195
  • 267
malexmave
  • 1,283
  • 2
  • 17
  • 37
  • Assuming this is python2, your class should be new-style, meaning that it inherits from `object`; eg `class MyClass(object)` – Daenyth Mar 05 '12 at 20:56
  • @Daenyth Thanks, good catch. But I assume that this does not change the problem. – malexmave Mar 05 '12 at 20:58
  • 1
    "SQLite itself is thread-safe, the cursor-Object is not": You should be creating a new cursor for each thread at least; It's perfectly practical to create a cursor for each query; they are basically free. If you want to make sure that you have decent performance when you move to a database where this is not so; use a connection pool. – SingleNegationElimination Mar 05 '12 at 21:05
  • @TokenMacGuy I've been thinking about that exact thing while writing up this question, but wanted to ask first. I guess changing this might even fix the Problem. I'm still interested in the general Ownership methodology of Python though. But thanks for confirming my thoughts, I will implement that now. – malexmave Mar 05 '12 at 21:11
  • 1
    (This question has basically nothing to do with "python object ownership", whatever that means, and everything to do with sqlite and threading; future generations will benefit if you adjust the title to reflect the specific problem you're having, rather than the deeper problem you imagine you're having) – SingleNegationElimination Mar 05 '12 at 21:13
  • @TokenMacGuy I imagined that "Ownership" is the wrong word. Still, the problem is that I would like to understand why this layout creates problems with the whole "Created in one thread, used in another" thing. In my mind (which works like java, mostly), calling a function of another class still retains that the objects of the called class are used and created by said called class. And it is this perceived difference to Python that I want to understand. Still, I will try to think of a better title for the question. – malexmave Mar 05 '12 at 21:17
  • @TokenMacGuy Okay, that's where my Java knowledge came back to hurt me. You made me understand the whole System of Python a lot better. Thank you! – malexmave Mar 05 '12 at 21:28
  • This is not peculiar to python. Threads/objects work the same in java; calling a method on a java object still executes the method code in the calling thread. – SingleNegationElimination Mar 05 '12 at 22:05
  • @TokenMacGuy Now I even learned something about java from this. I had never encountered something similar to this problem in java, so I just assumed it was the way I described it. – malexmave Mar 05 '12 at 22:07

2 Answers2

4

ProgrammingError: SQLite objects created in a thread can only be used in that same

The problem is that you're trying to conserve the cursor for some reason. You should not be doing this. Create a new cursor for every transaction; or if you're not totally sure where transactions start or end, a new cursor per query.

import sqlite3

class DB(object):
    def __init__(self):
        self.conn_uri = './data.sqlite'
        [...]

    def doSomething(self,Param):
        conn = sqlite.connect(self.conn_uri)
        c = conn.cursor()
        c.execute("SELECT * FROM xyz WHERE ID = ?", Param)

Edit, Re comments in your question: What's going on here has very little to do with python. When you create a sqlite resource, which is a C library and totally independent of python, sqlite requires that resource be used only in the thread that created it. It verifies this by looking at the thread ID of the currently running thread, and not at all attempting to coordinate the transfer of the resource from one thread to another. As such, you are obligated to create sqlite resources in each thread that needs them.

In your code, you create all of the sqlite resources in the DB object's __init__ method, which is probably called only once, and in the main thread. Thus these resources are only permitted to be used in that thread, threading.Lock not withstanding.

Your questions:

  • Who owns the cursor object in this case? The Processing Thread or the DB thread?

The thread that created it. Since it looks like you're calling DB() at the module level, it's very likely that it's the main thread.

  • Where can I read up on this stuff to finally "get" it?

There's not really much of anything to get. Nothing is happening at all behind the scenes, except what SQLite has to say on the matter, when you are using it.

  • Is the term "Object ownership" even correct, or is there an other term for this in Python?

Python doesn't really have much of anything at all to do with threading, except that it allows you to use threads. It's on you to coordinate multi-threaded applications properly.

EDIT again:

Objects do not live inside particular threads. When you call a method on an object, that method runs in the calling thread. ten threads can call the same method on the same object; all will run concurrently (or whatever passes for that re the GIL), and it's up to the caller or the method body to make sure nothing breaks.

SingleNegationElimination
  • 151,563
  • 33
  • 264
  • 304
2

I'm the author of an alternate SQLite wrapper for Python (APSW) and very familiar with this issue. SQLite itself used to require that objects - the database connection and cursors could only be used in the same thread. Around SQLite 3.5 this was changed and you could use objects concurrently although internally SQLite did its own locking so you didn't actually get concurrent performance. The default Python SQLite wrapper (aka pysqlite) supports even old versions of SQLite 3 so it continues to enforce this restriction even though it is no longer necessary for SQLite itself. However the pysqlite code would need to be modified to allow concurrency as the way it wraps SQLite is not safe - eg handling error messages is not safe because of SQLite API design flaws and requires special handling.

Note that cursors are very cheap. Do not try to reuse them or treat them as precious. The actual underlying SQLite objects (sqlite3_stmt) are kept in a cache and reused as needed.

If you do want maximum concurrency then open multiple connections and use them simultaneously.

The APSW doc has more about multi-threading and re-entrancy. Note that it has extra code to allow the actual concurrent usage that pysqlite does not have, but the other tips and info apply to any usage of SQLite.

Roger Binns
  • 3,203
  • 1
  • 24
  • 33