Cache.h 6.21 KB
Newer Older
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
1 2 3
#ifndef CACHE_H
#define CACHE_H

4
#include <cassert>
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#include <memory>
#include <mutex>
#include <unordered_map>

#include "SqliteTools.h"

template <typename TYPE>
class PrimaryKeyCacheKeyPolicy
{
    public:
        typedef unsigned int KeyType;
        static unsigned int key( const std::shared_ptr<TYPE>& self )
        {
            return self->id();
        }
20 21 22 23
        static unsigned int key( const TYPE* self )
        {
            return self->id();
        }
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
24 25 26 27 28 29 30 31 32
        static unsigned int key( sqlite3_stmt* stmt )
        {
            return Traits<unsigned int>::Load( stmt, 0 );
        }
};

/**
 * This utility class eases up the implementation of caching.
 *
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
33 34 35 36 37 38 39 40 41 42 43
 * It is driven by 2 policy classes:
 * - TABLEPOLICY describes the basics required to fetch a record. It has 2 static fields:
 *      - Name: the table name
 *      - CacheColumn: The column used to cache records.
 * - CACHEPOLICY describes which column to use for caching by providing two "key" methods.
 *   One that takes a sqlite3_stmt and one that takes an instance of the type being cached
 *
 * The default CACHEPOLICY implementation bases itself on an unsigned int column, assumed
 * to be the primary key, at index 0 of a fetch statement.
 *
 * Other template parameter:
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
44 45 46 47 48
 * - IMPL: The actual implementation. Typically the type that inherits this class
 * - INTF: An interface that express the type. This is used when fetching containers, as they
 *         are not polymorphic.
 *
 * How to use it:
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
49
 * - Inherit this class and specify the template parameter & policies accordingly
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
50 51
 * - Make this class a friend class of the class you inherit from
 */
52
template <typename IMPL, typename INTF, typename TABLEPOLICY, typename CACHEPOLICY = PrimaryKeyCacheKeyPolicy<IMPL> >
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
53 54 55
class Cache
{
    public:
56
        static std::shared_ptr<IMPL> fetch( DBConnection dbConnectionWeak, const typename CACHEPOLICY::KeyType& key )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
57
        {
58
            Lock lock( Mutex );
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
59 60 61
            auto it = Store.find( key );
            if ( it != Store.end() )
                return it->second;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
62
            static const std::string req = "SELECT * FROM " + TABLEPOLICY::Name +
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
63
                            " WHERE " + TABLEPOLICY::CacheColumn + " = ?";
64
            auto res = SqliteTools::fetchOne<IMPL>( dbConnectionWeak, req.c_str(), key );
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
65 66 67 68 69 70 71 72 73 74
            Store[key] = res;
            return res;
        }

        /*
         * Will fetch all elements from the database & cache them.
         *
         * @param res   A reference to the result vector. All existing elements will
         *              be discarded.
         */
75
        static bool fetchAll( DBConnection dbConnectionWeak, std::vector<std::shared_ptr<INTF> >& res )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
76
        {
77
            static const std::string req = "SELECT * FROM " + TABLEPOLICY::Name;
78
            return SqliteTools::fetchAll<IMPL, INTF>( dbConnectionWeak, req.c_str(), res );
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
79 80
        }

81
        static std::shared_ptr<IMPL> load( std::shared_ptr<sqlite3> dbConnection, sqlite3_stmt* stmt )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
82 83 84
        {
            auto cacheKey = CACHEPOLICY::key( stmt );

85
            Lock lock( Mutex );
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
86 87 88 89 90 91 92 93
            auto it = Store.find( cacheKey );
            if ( it != Store.end() )
                return it->second;
            auto inst = std::make_shared<IMPL>( dbConnection, stmt );
            Store[cacheKey] = inst;
            return inst;
        }

94
        static bool destroy( DBConnection dbConnectionWeak, const typename CACHEPOLICY::KeyType& key )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
95
        {
96
            Lock lock( Mutex );
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
97 98 99 100 101
            auto it = Store.find( key );
            if ( it != Store.end() )
                Store.erase( it );
            static const std::string req = "DELETE FROM " + TABLEPOLICY::Name + " WHERE " +
                    TABLEPOLICY::CacheColumn + " = ?";
102
            return SqliteTools::executeDelete( dbConnectionWeak, req.c_str(), key );
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
103 104
        }

105
        static bool destroy( DBConnection dbConnectionWeak, const std::shared_ptr<IMPL>& self )
106 107
        {
            const auto& key = CACHEPOLICY::key( self );
108
            return destroy( dbConnectionWeak, key );
109 110
        }

111
        static bool destroy( DBConnection dbConnectionWeak, const IMPL* self )
112 113
        {
            const auto& key = CACHEPOLICY::key( self );
114
            return destroy( dbConnectionWeak, key );
115 116
        }

117 118
        static void clear()
        {
119
            Lock lock( Mutex );
120 121 122
            Store.clear();
        }

123 124 125 126 127 128
        /**
         * @brief discard Discard a record from the cache
         * @param key The key used for cache
         * @return
         */
        static bool discard( const std::shared_ptr<IMPL>& record )
129
        {
130
            auto key = CACHEPOLICY::key( record );
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
131
            Lock lock( Mutex );
132
            auto it = Store.find( key );
133
            if ( it == Store.end() )
134 135 136 137 138
                return false;
            Store.erase( it );
            return true;
        }

139 140 141 142 143
    protected:
        /*
         * Create a new instance of the cache class.
         */
        template <typename... Args>
144
        static bool insert( DBConnection dbConnectionWeak, std::shared_ptr<IMPL> self, const std::string& req, const Args&... args )
145
        {
146
            unsigned int pKey = SqliteTools::insert( dbConnectionWeak, req, args... );
147
            if ( pKey == 0 )
148 149
                return false;
            (self.get())->*TABLEPOLICY::PrimaryKey = pKey;
150 151
            auto cacheKey = CACHEPOLICY::key( self );

152
            Lock lock( Mutex );
153 154 155 156
            // We expect the cache column to be PRIMARY KEY / UNIQUE, so an insertion with
            // a duplicated key should have been rejected by sqlite. This indicates an invalid state
            assert( Store.find( cacheKey ) == Store.end() );
            Store[cacheKey] = self;
157
            return true;
158 159
        }

Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
160
    private:
161
        static std::unordered_map<typename CACHEPOLICY::KeyType, std::shared_ptr<IMPL> > Store;
162 163
        static std::recursive_mutex Mutex;
        typedef std::lock_guard<std::recursive_mutex> Lock;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
164 165 166
};

template <typename IMPL, typename INTF, typename TABLEPOLICY, typename CACHEPOLICY>
167
std::unordered_map<typename CACHEPOLICY::KeyType, std::shared_ptr<IMPL> >
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
168 169 170
Cache<IMPL, INTF, TABLEPOLICY, CACHEPOLICY>::Store;

template <typename IMPL, typename INTF, typename TABLEPOLICY, typename CACHEPOLICY>
171
std::recursive_mutex Cache<IMPL, INTF, TABLEPOLICY, CACHEPOLICY>::Mutex;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
172 173

#endif // CACHE_H