[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [nyx/master] Schema versioning and cache thread safety



commit 3b9786e3d22b938e7c25fc5a40179208707fd68b
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date:   Wed Aug 30 10:32:44 2017 -0700

    Schema versioning and cache thread safety
    
    Versioning nyx's cache schema and ensure use of the cache is done in a thread
    safe fashion.
---
 nyx/__init__.py | 40 ++++++++++++++++++++++++++++++++++++----
 test/cache.py   | 31 ++++++++++++++++---------------
 2 files changed, 52 insertions(+), 19 deletions(-)

diff --git a/nyx/__init__.py b/nyx/__init__.py
index c7db0cb..ee146a6 100644
--- a/nyx/__init__.py
+++ b/nyx/__init__.py
@@ -31,6 +31,7 @@ Tor curses monitoring application.
     +- halt - stops daemon panels
 """
 
+import contextlib
 import distutils.spawn
 import os
 import sqlite3
@@ -86,6 +87,14 @@ NYX_INTERFACE = None
 TOR_CONTROLLER = None
 BASE_DIR = os.path.sep.join(__file__.split(os.path.sep)[:-1])
 CACHE = None
+CACHE_LOCK = threading.RLock()
+
+SCHEMA_VERSION = 1  # version of our scheme, bump this if you change the following
+SCHEMA = (
+  'CREATE TABLE schema(version NUMBER)',
+  'INSERT INTO schema(version) VALUES (%i)' % SCHEMA_VERSION,
+)
+
 
 # technically can change but we use this query a *lot* so needs to be cached
 
@@ -252,6 +261,7 @@ def data_directory(filename, config):
   return os.path.join(data_dir, filename)
 
 
+@contextlib.contextmanager
 def cache():
   """
   Provides the sqlite cache for application data.
@@ -261,11 +271,33 @@ def cache():
 
   global CACHE
 
-  if CACHE is None:
-    cache_path = data_directory('cache.sqlite')
-    CACHE = sqlite3.connect(cache_path if cache_path else ':memory:')
+  with CACHE_LOCK:
+    if CACHE is None:
+      cache_path = data_directory('cache.sqlite')
+
+      if cache_path:
+        try:
+          CACHE = sqlite3.connect(cache_path)
+          schema = CACHE.execute('SELECT version FROM schema').fetchone()[0]
+        except:
+          schema = 'no schema'
+
+        if schema != SCHEMA_VERSION:
+          stem.util.log.info('Cache schema of %s is out of date (has %s but current version is %s). Clearing the cache.' % (cache_path, schema, SCHEMA_VERSION))
+
+          CACHE.close()
+          os.remove(cache_path)
+          CACHE = sqlite3.connect(cache_path)
+
+          for cmd in SCHEMA:
+            CACHE.execute(cmd)
+      else:
+        CACHE = sqlite3.connect(':memory:')
+
+        for cmd in SCHEMA:
+          CACHE.execute(cmd)
 
-  return CACHE
+    yield CACHE
 
 
 @uses_settings
diff --git a/test/cache.py b/test/cache.py
index dc1fa40..1c30f40 100644
--- a/test/cache.py
+++ b/test/cache.py
@@ -16,25 +16,26 @@ class TestCache(unittest.TestCase):
 
   @patch('nyx.data_directory', Mock(return_value = None))
   def test_memory_cache(self):
-    cache = nyx.cache()
-    self.assertEqual((0, 'main', ''), cache.execute("PRAGMA database_list").fetchone())
+    with nyx.cache() as cache:
+      self.assertEqual((0, 'main', ''), cache.execute("PRAGMA database_list").fetchone())
 
-    cache.execute('CREATE TABLE aliases(alias TEXT, command TEXT)')
-    cache.execute('INSERT INTO aliases(alias, command) VALUES (?,?)', ('l', 'ls -xF --color=auto'))
-    cache.execute('INSERT INTO aliases(alias, command) VALUES (?,?)', ('ll', 'ls -hlA --color=auto'))
-    self.assertEqual('ls -hlA --color=auto', cache.execute('SELECT command FROM aliases WHERE alias=?', ('ll',)).fetchone()[0])
+      cache.execute('CREATE TABLE aliases(alias TEXT, command TEXT)')
+      cache.execute('INSERT INTO aliases(alias, command) VALUES (?,?)', ('l', 'ls -xF --color=auto'))
+      cache.execute('INSERT INTO aliases(alias, command) VALUES (?,?)', ('ll', 'ls -hlA --color=auto'))
+      self.assertEqual('ls -hlA --color=auto', cache.execute('SELECT command FROM aliases WHERE alias=?', ('ll',)).fetchone()[0])
 
   def test_file_cache(self):
     with tempfile.NamedTemporaryFile(suffix = '.sqlite') as tmp:
       with patch('nyx.data_directory', Mock(return_value = tmp.name)):
-        cache = nyx.cache()
-        self.assertEqual((0, 'main', tmp.name), cache.execute("PRAGMA database_list").fetchone())
-
-        cache.execute('CREATE TABLE aliases(alias TEXT, command TEXT)')
-        cache.execute('INSERT INTO aliases(alias, command) VALUES (?,?)', ('l', 'ls -xF --color=auto'))
-        cache.execute('INSERT INTO aliases(alias, command) VALUES (?,?)', ('ll', 'ls -hlA --color=auto'))
-        cache.commit()
-        cache.close()
+        with nyx.cache() as cache:
+          self.assertEqual((0, 'main', tmp.name), cache.execute("PRAGMA database_list").fetchone())
+
+          cache.execute('CREATE TABLE aliases(alias TEXT, command TEXT)')
+          cache.execute('INSERT INTO aliases(alias, command) VALUES (?,?)', ('l', 'ls -xF --color=auto'))
+          cache.execute('INSERT INTO aliases(alias, command) VALUES (?,?)', ('ll', 'ls -hlA --color=auto'))
+          cache.commit()
+
         nyx.CACHE = None
 
-        self.assertEqual('ls -hlA --color=auto', nyx.cache().execute('SELECT command FROM aliases WHERE alias=?', ('ll',)).fetchone()[0])
+        with nyx.cache() as cache:
+          self.assertEqual('ls -hlA --color=auto', cache.execute('SELECT command FROM aliases WHERE alias=?', ('ll',)).fetchone()[0])



_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits