Use a separate connection instance for each pid, since it is not safe to use a connection created in a parent process.
See: https://www.sqlite.org/howtocorrupt.html Bug: https://bugs.gentoo.org/736334 Signed-off-by: Zac Medico <zmed...@gentoo.org> --- lib/portage/cache/sqlite.py | 9 +++++---- lib/portage/tests/dbapi/test_auxdb.py | 13 +++++++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/portage/cache/sqlite.py b/lib/portage/cache/sqlite.py index 0395dd516..36a4f049e 100644 --- a/lib/portage/cache/sqlite.py +++ b/lib/portage/cache/sqlite.py @@ -4,6 +4,7 @@ import collections import re +import portage from portage.cache import fs_template from portage.cache import cache_errors from portage import os @@ -25,7 +26,7 @@ class database(fs_template.FsBased): cache_bytes = 1024 * 1024 * 10 _connection_info_entry = collections.namedtuple('_connection_info_entry', - ('connection', 'cursor')) + ('connection', 'cursor', 'pid')) def __init__(self, *args, **config): super(database, self).__init__(*args, **config) @@ -71,13 +72,13 @@ class database(fs_template.FsBased): @property def _db_cursor(self): - if self._db_connection_info is None: + if self._db_connection_info is None or self._db_connection_info.pid != portage.getpid(): self._db_init_connection() return self._db_connection_info.cursor @property def _db_connection(self): - if self._db_connection_info is None: + if self._db_connection_info is None or self._db_connection_info.pid != portage.getpid(): self._db_init_connection() return self._db_connection_info.connection @@ -94,7 +95,7 @@ class database(fs_template.FsBased): connection = self._db_module.connect( database=_unicode_decode(self._dbpath), **connection_kwargs) cursor = connection.cursor() - self._db_connection_info = self._connection_info_entry(connection, cursor) + self._db_connection_info = self._connection_info_entry(connection, cursor, portage.getpid()) self._db_cursor.execute("PRAGMA encoding = %s" % self._db_escape_string("UTF-8")) if not self.readonly and not self._ensure_access(self._dbpath): raise cache_errors.InitializationError(self.__class__, "can't ensure perms on %s" % self._dbpath) diff --git a/lib/portage/tests/dbapi/test_auxdb.py b/lib/portage/tests/dbapi/test_auxdb.py index 5c79357d7..7865c3564 100644 --- a/lib/portage/tests/dbapi/test_auxdb.py +++ b/lib/portage/tests/dbapi/test_auxdb.py @@ -4,7 +4,8 @@ from portage.tests import TestCase from portage.tests.resolver.ResolverPlayground import ResolverPlayground from portage.util.futures import asyncio -from portage.util.futures.compat_coroutine import coroutine +from portage.util.futures.compat_coroutine import coroutine, coroutine_return +from portage.util.futures.executor.fork import ForkExecutor class AuxdbTestCase(TestCase): @@ -61,8 +62,14 @@ class AuxdbTestCase(TestCase): portdb = playground.trees[playground.eroot]["porttree"].dbapi + def test_func(): + return asyncio._wrap_loop().run_until_complete(self._test_mod_async( + ebuilds, ebuild_inherited, eclass_defined_phases, eclass_depend, portdb)) + + self.assertTrue(test_func()) + loop = asyncio._wrap_loop() - loop.run_until_complete(self._test_mod_async(ebuilds, ebuild_inherited, eclass_defined_phases, eclass_depend, portdb)) + self.assertTrue(loop.run_until_complete(loop.run_in_executor(ForkExecutor(), test_func))) @coroutine def _test_mod_async(self, ebuilds, ebuild_inherited, eclass_defined_phases, eclass_depend, portdb): @@ -73,3 +80,5 @@ class AuxdbTestCase(TestCase): self.assertEqual(depend, eclass_depend) self.assertEqual(eapi, metadata['EAPI']) self.assertEqual(frozenset(inherited.split()), ebuild_inherited) + + coroutine_return(True) -- 2.25.3