[PATCH 5/7] Re: Don't keep sqlite connections open
Nicolas Sebrecht
nicolas.s-dev at laposte.net
Thu May 5 23:34:39 BST 2011
On Thu, May 05, 2011 at 03:59:27PM +0200, Sebastian Spaeth wrote:
<...>
> if version < LocalStatusSQLiteFolder.cur_version:
> self.upgrade_db(version)
> - self.connection.close()
> +
> + def get_cursor(self):
> + """Return a db (connection, cursor) that we can use
> +
> + You are responsible for connection.commit() and
> + connection.close() yourself. Connection close() happens
> + automatically when the connection variable is destroyed
> + though. According to sqlite docs, you need to commit() before
> + the connection is closed or your changes will be lost!"""
> + #get db connection which autocommits
> + connection = sqlite.connect(self.filename, isolation_level=None)
> + cursor = connection.cursor()
> + return connection, cursor
>
> def upgrade_db(self, from_ver):
> """Upgrade the sqlite format from version 'from_ver' to current"""
This is a good improvement but...
> + conn, cursor = self.get_cursor()
> + with conn:
<...>
> + cursor.execute('INSERT INTO status (id,flags) VALUES (?,?)',
> (uid,flags))
> + conn.commit()
<...>
> + conn, cursor = self.get_cursor()
> + with conn:
> + cursor.execute('CREATE TABLE metadata (key VARCHAR(50) PRIMARY KEY, value VARCHAR(128))')
> + cursor.execute("INSERT INTO metadata VALUES('db_version', '1')")
> + cursor.execute('CREATE TABLE status (id INTEGER PRIMARY KEY, flags VARCHAR(50))')
> + conn.commit()
<...>
> + conn, cursor = self.get_cursor()
> + with conn:
> + cursor.execute('DELETE FROM status')
> + conn.commit()
<...>
> + conn, cursor = self.get_cursor()
> + with conn:
> + cursor.execute('SELECT id,flags from status')
> + for row in cursor:
> + flags = [x for x in row[1]]
> + self.messagelist[row[0]] = {'uid': row[0], 'flags': flags}
<...>
> + conn, cursor = self.get_cursor()
> + with conn:
> + while True:
> + try:
> + cursor.execute('INSERT INTO status (id,flags) VALUES (?,?)',
> + (uid,flags))
> + conn.commit()
> + except sqlite.OperationalError as e:
> + if e.args[0] != 'database is locked':
> + raise
> + else: #success
> + break
<...>
> + conn, cursor = self.get_cursor()
> + with conn:
> + while True:
> + try:
> + cursor.execute('UPDATE status SET flags=? WHERE id=?',(flags,uid))
> + conn.commit()
> + except sqlite.OperationalError as e:
> + if e.args[0] != 'database is locked':
> + raise
> + else: #success
> + break
<...>
> + conn, cursor = self.get_cursor()
> + with conn:
> + for uid in uidlist:
> + del(self.messagelist[uid])
> + while True:
> + try:
> + cursor.execute('DELETE FROM status WHERE id=?',
> + uid)
> + conn.commit()
> + except sqlite.OperationalError as e:
> + if e.args[0] != 'database is locked':
> + raise
> + else: #success
> + break
...we tend to have a lot of constructs like this
conn, cursor = self.get_cursor()
with conn:
while True:
try:
<execute some SQL request and commit>
except blablah:
<raise on database lock>
else:
<cool, it worked>
it would be nice to factorize out. Not that this is wrong by design but
it will cause harder work to maintain with possible errors and bugs.
I think we need a wrapper (or more) proceeding the SQL requests with
generic data methods. Let's me try to categorize the different request
types:
- read only: mostly SELECTs with no need to commit()
- write (we need to commit()):
* one statement: easy
* multiple statements: easy using a list
* statements including additional code to execute: pass both the
request and a function to the wrapper
It would really help to delegate the entire read/write process to the
lower stage. Why? Think a bit more about the potential of the SQL
backend. With nice data drivers it would actually be SO easy to add
features like
- statistics
- advanced tracking system for logging IMAP/cache sessions
- manage miscallenous exceptions
- multiple data formats as backend (usefull for testing purpose ,-))
- thread safe concurrent access by managing queues
I think the SQL topic is a very good opportunity for improving our data
driver design. In short, I would expect something like data.write(),
data.read(), (...).
--
Nicolas Sebrecht
More information about the OfflineIMAP-project
mailing list