Blob Blame History Raw
From 215b188d891d5236fe94131d176d7ddc3ae02d5d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
Date: Fri, 5 Dec 2014 17:12:28 +0100
Subject: [PATCH 20/30] Avoid ridiculous amount of SQL queries by caching
 PartTypes

PartTypes are identified by their FQ name, which is in form NAMESPACE:NAME,
where namespace and name are stored in individual columns. For this reason
the standard ::retrieveByName() and name cache generated from entities.xslt
does not work. This patch adds special handling for PartType table, so that
a special PartType::retrieveByFQName() method as well as PartType name cache
handling are generated during the XSL Transformation, allowing us to cache
all the PartTypes.

This reduces the amount of SQL queries by at least two for each single AKAPPEND,
MERGE, STORE and FETCH command, providing a nice performance boost during
sync.
---
 server/src/handler/append.cpp          |  4 ++--
 server/src/storage/datastore.cpp       |  4 +++-
 server/src/storage/entities-header.xsl |  7 ++++++-
 server/src/storage/entities-source.xsl | 31 ++++++++++++++++++++++++++++++-
 server/src/storage/entities.xsl        |  7 ++++++-
 server/src/storage/parttypehelper.cpp  | 29 +----------------------------
 server/src/storage/parttypehelper.h    | 13 -------------
 7 files changed, 48 insertions(+), 47 deletions(-)

diff --git a/server/src/handler/append.cpp b/server/src/handler/append.cpp
index c503216..b594e27 100644
--- a/server/src/handler/append.cpp
+++ b/server/src/handler/append.cpp
@@ -134,7 +134,7 @@ bool Append::commit()
 
     // wrap data into a part
     Part part;
-    part.setPartType( PartTypeHelper::fromName( "PLD", "RFC822" ) );
+    part.setPartType( PartType::retrieveByFQName( QLatin1String("PLD"), QLatin1String("RFC822") ) );
     part.setData( m_data );
     part.setPimItemId( item.id() );
     part.setDatasize( dataSize );
@@ -148,7 +148,7 @@ bool Append::commit()
     //akDebug() << "Append handler: doPreprocessing is" << doPreprocessing;
     if ( doPreprocessing ) {
       Part hiddenAttribute;
-      hiddenAttribute.setPartType( PartTypeHelper::fromName( "ATR", "HIDDEN" ) );
+      hiddenAttribute.setPartType( PartType::retrieveByFQName( QLatin1String("ATR"), QLatin1String("HIDDEN") ) );
       hiddenAttribute.setData( QByteArray() );
       hiddenAttribute.setPimItemId( item.id() );
       hiddenAttribute.setDatasize( 0 );
diff --git a/server/src/storage/datastore.cpp b/server/src/storage/datastore.cpp
index ae78bab..304f0e8 100644
--- a/server/src/storage/datastore.cpp
+++ b/server/src/storage/datastore.cpp
@@ -183,6 +183,7 @@ bool DataStore::init()
   Flag::enableCache( true );
   Resource::enableCache( true );
   Collection::enableCache( true );
+  PartType::enableCache( true );
 
   return true;
 }
@@ -1025,7 +1026,8 @@ bool DataStore::unhideAllPimItems()
   akDebug() << "DataStore::unhideAllPimItems()";
 
   try {
-    return PartHelper::remove( Part::partTypeIdFullColumnName(), PartTypeHelper::fromName( "ATR", "HIDDEN" ).id() );
+    return PartHelper::remove( Part::partTypeIdFullColumnName(),
+                               PartType::retrieveByFQName( QLatin1String("ATR"), QLatin1String("HIDDEN") ).id() );
   } catch ( ... ) {} // we can live with this failing
 
   return false;
diff --git a/server/src/storage/entities-header.xsl b/server/src/storage/entities-header.xsl
index 4966966..d515fd3 100644
--- a/server/src/storage/entities-header.xsl
+++ b/server/src/storage/entities-header.xsl
@@ -133,11 +133,16 @@ class <xsl:value-of select="$className"/> : private Entity
     <xsl:text>static </xsl:text><xsl:value-of select="$className"/> retrieveById( qint64 id );
     </xsl:if>
 
-    <xsl:if test="column[@name = 'name']">
+    <xsl:if test="column[@name = 'name'] and $className != 'PartType'">
     /** Returns the record with name @p name. */
     <xsl:text>static </xsl:text><xsl:value-of select="$className"/> retrieveByName( const <xsl:value-of select="column[@name = 'name']/@type"/> &amp;name );
     </xsl:if>
 
+    <xsl:if test="column[@name = 'name'] and $className = 'PartType'">
+    <!-- Special case for PartTypes, which are identified by "NS:NAME" -->
+    <xsl:text>static PartType retrieveByFQName( const QString &amp;ns, const QString &amp;name );</xsl:text>
+    </xsl:if>
+
     /** Retrieve all records from this table. */
     static <xsl:value-of select="$className"/>::List retrieveAll();
     /** Retrieve all records with value @p value in column @p key. */
diff --git a/server/src/storage/entities-source.xsl b/server/src/storage/entities-source.xsl
index e398da5..46ef3a6 100644
--- a/server/src/storage/entities-source.xsl
+++ b/server/src/storage/entities-source.xsl
@@ -130,7 +130,15 @@ void <xsl:value-of select="$className"/>::Private::addToCache( const <xsl:value-
   idCache.insert( entry.id(), entry );
   </xsl:if>
   <xsl:if test="column[@name = 'name']">
+    <xsl:choose>
+     <xsl:when test="$className = 'PartType'">
+      <!-- special case for PartType, which is identified as "NS:NAME" -->
+  nameCache.insert( entry.ns() + QLatin1Char(':') + entry.name(), entry );
+      </xsl:when>
+      <xsl:otherwise>
   nameCache.insert( entry.name(), entry );
+      </xsl:otherwise>
+    </xsl:choose>
   </xsl:if>
 }
 
@@ -323,7 +331,7 @@ QVector&lt; <xsl:value-of select="$className"/> &gt; <xsl:value-of select="$clas
 }
 
 </xsl:if>
-<xsl:if test="column[@name = 'name']">
+<xsl:if test="column[@name = 'name'] and $className != 'PartType'">
 <xsl:value-of select="$className"/><xsl:text> </xsl:text><xsl:value-of select="$className"/>::retrieveByName( const <xsl:value-of select="column[@name = 'name']/@type"/> &amp;name )
 {
   <xsl:call-template name="data-retrieval">
@@ -333,6 +341,19 @@ QVector&lt; <xsl:value-of select="$className"/> &gt; <xsl:value-of select="$clas
 }
 </xsl:if>
 
+<xsl:if test="column[@name = 'name'] and $className = 'PartType'">
+<xsl:text>PartType PartType::retrieveByFQName( const QString &amp; ns, const QString &amp; name )</xsl:text>
+{
+  const QString fqname = ns + QLatin1Char(':') + name;
+  <xsl:call-template name="data-retrieval">
+  <xsl:with-param name="key">ns</xsl:with-param>
+  <xsl:with-param name="key2">name</xsl:with-param>
+  <xsl:with-param name="lookupKey">fqname</xsl:with-param>
+  <xsl:with-param name="cache">nameCache</xsl:with-param>
+  </xsl:call-template>
+}
+</xsl:if>
+
 QVector&lt;<xsl:value-of select="$className"/>&gt; <xsl:value-of select="$className"/>::retrieveAll()
 {
   QSqlDatabase db = DataStore::self()->database();
@@ -588,7 +609,15 @@ void <xsl:value-of select="$className"/>::invalidateCache() const
     Private::idCache.remove( id() );
     </xsl:if>
     <xsl:if test="column[@name = 'name']">
+      <xsl:choose>
+        <xsl:when test="$className = 'PartType'">
+        <!-- Special handling for PartType, which is identified as "NS:NAME" -->
+    Private::nameCache.remove( ns() + QLatin1Char(':') + name() );
+        </xsl:when>
+        <xsl:otherwise>
     Private::nameCache.remove( name() );
+        </xsl:otherwise>
+      </xsl:choose>
     </xsl:if>
   }
 }
diff --git a/server/src/storage/entities.xsl b/server/src/storage/entities.xsl
index c8fb1fd..2cf96c4 100644
--- a/server/src/storage/entities.xsl
+++ b/server/src/storage/entities.xsl
@@ -169,12 +169,14 @@ set<xsl:value-of select="$methodName"/>( <xsl:call-template name="argument"/> )
 <!-- data retrieval for a given key field -->
 <xsl:template name="data-retrieval">
 <xsl:param name="key"/>
+<xsl:param name="key2"/>
+<xsl:param name="lookupKey" select="$key"/>
 <xsl:param name="cache"/>
 <xsl:variable name="className"><xsl:value-of select="@name"/></xsl:variable>
   <xsl:if test="$cache != ''">
   if ( Private::cacheEnabled ) {
     QMutexLocker lock(&amp;Private::cacheMutex);
-    QHash&lt;<xsl:value-of select="column[@name = $key]/@type"/>, <xsl:value-of select="$className"/>&gt;::const_iterator it = Private::<xsl:value-of select="$cache"/>.constFind(<xsl:value-of select="$key"/>);
+    QHash&lt;<xsl:value-of select="column[@name = $key]/@type"/>, <xsl:value-of select="$className"/>&gt;::const_iterator it = Private::<xsl:value-of select="$cache"/>.constFind(<xsl:value-of select="$lookupKey"/>);
     if ( it != Private::<xsl:value-of select="$cache"/>.constEnd() ) {
       return it.value();
     }
@@ -188,6 +190,9 @@ set<xsl:value-of select="$methodName"/>( <xsl:call-template name="argument"/> )
   static const QStringList columns = removeEntry(columnNames(), <xsl:value-of select="$key"/>Column());
   qb.addColumns( columns );
   qb.addValueCondition( <xsl:value-of select="$key"/>Column(), Query::Equals, <xsl:value-of select="$key"/> );
+  <xsl:if test="$key2 != ''">
+  qb.addValueCondition( <xsl:value-of select="$key2"/>Column(), Query::Equals, <xsl:value-of select="$key2"/> );
+  </xsl:if>
   if ( !qb.exec() ) {
     akDebug() &lt;&lt; "Error during selection of record with <xsl:value-of select="$key"/>"
       &lt;&lt; <xsl:value-of select="$key"/> &lt;&lt; "from table" &lt;&lt; tableName()
diff --git a/server/src/storage/parttypehelper.cpp b/server/src/storage/parttypehelper.cpp
index b73dcd5..7654108 100644
--- a/server/src/storage/parttypehelper.cpp
+++ b/server/src/storage/parttypehelper.cpp
@@ -37,7 +37,7 @@ QPair< QString, QString > PartTypeHelper::parseFqName(const QString& fqName)
 PartType PartTypeHelper::fromFqName(const QString& fqName)
 {
   const QPair<QString, QString> p = parseFqName( fqName );
-  return fromName( p.first, p.second );
+  return PartType::retrieveByFQName(p.first, p.second);
 }
 
 PartType PartTypeHelper::fromFqName(const QByteArray& fqName)
@@ -45,33 +45,6 @@ PartType PartTypeHelper::fromFqName(const QByteArray& fqName)
   return fromFqName( QLatin1String(fqName) );
 }
 
-PartType PartTypeHelper::fromName(const QString& ns, const QString& typeName)
-{
-  SelectQueryBuilder<PartType> qb;
-  qb.addValueCondition( PartType::nsColumn(), Query::Equals, ns );
-  qb.addValueCondition( PartType::nameColumn(), Query::Equals, typeName );
-  if ( !qb.exec() )
-    throw PartTypeException( "Unable to query part type table." );
-  const PartType::List result = qb.result();
-  if ( result.size() == 1 )
-    return result.first();
-  if ( result.size() > 1 )
-    throw PartTypeException( "Part type uniqueness constraint violation." );
-
-  // doesn't exist yet, so let's create a new one
-  PartType type;
-  type.setName( typeName );
-  type.setNs( ns );
-  if ( !type.insert() )
-    throw PartTypeException( "Creating a new part type failed." );
-  return type;
-}
-
-PartType PartTypeHelper::fromName(const char* ns, const char* typeName)
-{
-  return fromName( QLatin1String(ns), QLatin1String(typeName) );
-}
-
 Query::Condition PartTypeHelper::conditionFromFqName(const QString& fqName)
 {
   const QPair<QString, QString> p = parseFqName( fqName );
diff --git a/server/src/storage/parttypehelper.h b/server/src/storage/parttypehelper.h
index 38cb858..4c4f42f 100644
--- a/server/src/storage/parttypehelper.h
+++ b/server/src/storage/parttypehelper.h
@@ -48,19 +48,6 @@ namespace PartTypeHelper
   PartType fromFqName( const QByteArray &fqName );
 
   /**
-   * Retrieve (or create) PartType for the given namespace and type name.
-   * @param ns Namespace
-   * @param typeName Part type name.
-   * @throws PartTypeException
-   */
-  PartType fromName( const QString &ns, const QString &typeName );
-
-  /**
-   * Convenience overload of the above.
-   */
-  PartType fromName( const char *ns, const char *typeName );
-
-  /**
    * Returns a query condition that matches the given part.
    * @param fqName fully-qualified part type name
    * @throws PartTypeException
-- 
2.1.0