mirror of
https://git.yoctoproject.org/poky
synced 2026-02-07 01:06:37 +01:00
It appears the timeout sometimes has no effect and we see database access failures. Combat this by wrapping the execute function in all cases and retrying manually ourselves. Thanks to Kevin Tian for help debugging this. Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>
134 lines
4.9 KiB
Python
134 lines
4.9 KiB
Python
# BitBake Persistent Data Store
|
|
#
|
|
# Copyright (C) 2007 Richard Purdie
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License version 2 as
|
|
# published by the Free Software Foundation.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along
|
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
import bb, os
|
|
import bb.utils
|
|
|
|
try:
|
|
import sqlite3
|
|
except ImportError:
|
|
try:
|
|
from pysqlite2 import dbapi2 as sqlite3
|
|
except ImportError:
|
|
bb.msg.fatal(bb.msg.domain.PersistData, "Importing sqlite3 and pysqlite2 failed, please install one of them. Python 2.5 or a 'python-pysqlite2' like package is likely to be what you need.")
|
|
|
|
sqlversion = sqlite3.sqlite_version_info
|
|
if sqlversion[0] < 3 or (sqlversion[0] == 3 and sqlversion[1] < 3):
|
|
bb.msg.fatal(bb.msg.domain.PersistData, "sqlite3 version 3.3.0 or later is required.")
|
|
|
|
class PersistData:
|
|
"""
|
|
BitBake Persistent Data Store
|
|
|
|
Used to store data in a central location such that other threads/tasks can
|
|
access them at some future date.
|
|
|
|
The "domain" is used as a key to isolate each data pool and in this
|
|
implementation corresponds to an SQL table. The SQL table consists of a
|
|
simple key and value pair.
|
|
|
|
Why sqlite? It handles all the locking issues for us.
|
|
"""
|
|
def __init__(self, d, persistent_database_connection):
|
|
if "connection" in persistent_database_connection:
|
|
self.cursor = persistent_database_connection["connection"].cursor()
|
|
return
|
|
self.cachedir = bb.data.getVar("PERSISTENT_DIR", d, True) or bb.data.getVar("CACHE", d, True)
|
|
if self.cachedir in [None, '']:
|
|
bb.msg.fatal(bb.msg.domain.PersistData, "Please set the 'PERSISTENT_DIR' or 'CACHE' variable.")
|
|
try:
|
|
os.stat(self.cachedir)
|
|
except OSError:
|
|
bb.utils.mkdirhier(self.cachedir)
|
|
|
|
self.cachefile = os.path.join(self.cachedir, "bb_persist_data.sqlite3")
|
|
bb.msg.debug(1, bb.msg.domain.PersistData, "Using '%s' as the persistent data cache" % self.cachefile)
|
|
|
|
connection = sqlite3.connect(self.cachefile, timeout=5, isolation_level=None)
|
|
persistent_database_connection["connection"] = connection
|
|
self.cursor = persistent_database_connection["connection"].cursor()
|
|
|
|
def addDomain(self, domain):
|
|
"""
|
|
Should be called before any domain is used
|
|
Creates it if it doesn't exist.
|
|
"""
|
|
self._execute("CREATE TABLE IF NOT EXISTS %s(key TEXT, value TEXT);" % domain)
|
|
|
|
def delDomain(self, domain):
|
|
"""
|
|
Removes a domain and all the data it contains
|
|
"""
|
|
self._execute("DROP TABLE IF EXISTS %s;" % domain)
|
|
|
|
def getKeyValues(self, domain):
|
|
"""
|
|
Return a list of key + value pairs for a domain
|
|
"""
|
|
ret = {}
|
|
data = self._execute("SELECT key, value from %s;" % domain)
|
|
for row in data:
|
|
ret[str(row[0])] = str(row[1])
|
|
|
|
return ret
|
|
|
|
def getValue(self, domain, key):
|
|
"""
|
|
Return the value of a key for a domain
|
|
"""
|
|
data = self._execute("SELECT * from %s where key=?;" % domain, [key])
|
|
for row in data:
|
|
return row[1]
|
|
|
|
def setValue(self, domain, key, value):
|
|
"""
|
|
Sets the value of a key for a domain
|
|
"""
|
|
data = self._execute("SELECT * from %s where key=?;" % domain, [key])
|
|
rows = 0
|
|
for row in data:
|
|
rows = rows + 1
|
|
if rows:
|
|
self._execute("UPDATE %s SET value=? WHERE key=?;" % domain, [value, key])
|
|
else:
|
|
self._execute("INSERT into %s(key, value) values (?, ?);" % domain, [key, value])
|
|
|
|
def delValue(self, domain, key):
|
|
"""
|
|
Deletes a key/value pair
|
|
"""
|
|
self._execute("DELETE from %s where key=?;" % domain, [key])
|
|
|
|
#
|
|
# We wrap the sqlite execute calls as on contended machines or single threaded
|
|
# systems we can have multiple processes trying to access the DB at once and it seems
|
|
# sqlite sometimes doesn't wait for the timeout. We therefore loop but put in an
|
|
# emergency brake too
|
|
#
|
|
def _execute(self, *query):
|
|
count = 0
|
|
while True:
|
|
try:
|
|
ret = self.cursor.execute(*query)
|
|
#print "Had to retry %s times" % count
|
|
return ret
|
|
except sqlite3.OperationalError as e:
|
|
if 'database is locked' in str(e) and count < 500:
|
|
count = count + 1
|
|
continue
|
|
raise
|