Add racefree way to add a new unique data point
authorSimo Sorce <simo@redhat.com>
Fri, 4 Apr 2014 14:34:21 +0000 (10:34 -0400)
committerSimo Sorce <simo@redhat.com>
Fri, 4 Apr 2014 16:38:41 +0000 (12:38 -0400)
Our schema gathers together data related to a service by using an ID
column. This column cannot be unique or a primary key as the ID is
repeated for each key/value pair in the datum group.

Use a unique identifier to make sure we can let dqlite generate a new
ID internally and then find out wat it is as race-free as possible.

We keep this method in the data module so it can be changed later
without affecting application logic.

Signed-off-by: Simo Sorce <simo@redhat.com>
ipsilon/util/data.py
ipsilon/util/plugin.py

index ec32b43..52dfa78 100755 (executable)
@@ -20,6 +20,8 @@
 import os
 import sqlite3
 import cherrypy
 import os
 import sqlite3
 import cherrypy
+from random import randint
+import sys
 
 
 class Store(object):
 
 
 class Store(object):
@@ -366,6 +368,34 @@ class Store(object):
             if con:
                 con.close()
 
             if con:
                 con.close()
 
+    def new_datum(self, plugin, datum):
+        ID = "(SELECT IFNULL(MAX(id), 0) + 1 FROM %s_data)" % plugin
+        INSERT_NEW = "INSERT INTO %s_data VALUES(%s,?,?)" % (plugin, ID)
+        INSERT = "INSERT INTO %s_data VALUES(?,?,?)" % plugin
+        SELECT = "SELECT id FROM %s_data WHERE name=? AND value=?" % plugin
+        DELETE = "DELETE FROM %s_data WHERE name=? AND value=?" % plugin
+        con = None
+        try:
+            con = sqlite3.connect(self._admin_dbname)
+            cur = con.cursor()
+            tmpid = ('new', str(randint(0, sys.maxint)))
+            cur.execute(INSERT_NEW, tmpid)
+            cur.execute(SELECT, tmpid)
+            rows = cur.fetchall()
+            idval = rows[0][0]
+            for name in datum:
+                cur.execute(INSERT, (idval, name, datum[name]))
+            cur.execute(DELETE, tmpid)
+            con.commit()
+        except sqlite3.Error, e:
+            if con:
+                con.rollback()
+            cherrypy.log.error("Failed to store %s data: [%s]" % (plugin, e))
+            raise
+        finally:
+            if con:
+                con.close()
+
     def wipe_data(self, plugin):
         # Try to backup old data first, just in case
         try:
     def wipe_data(self, plugin):
         # Try to backup old data first, just in case
         try:
index 6c329d6..cdf997e 100755 (executable)
@@ -157,6 +157,9 @@ class PluginObject(object):
     def save_data(self, data):
         self._data.save_data(self.name, data)
 
     def save_data(self, data):
         self._data.save_data(self.name, data)
 
+    def new_datum(self, datum):
+        self._data.new_datum(self.name, datum)
+
     def wipe_config_values(self, facility):
         self._data.wipe_plugin_config(facility, self.name)
 
     def wipe_config_values(self, facility):
         self._data.wipe_plugin_config(facility, self.name)