//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany /// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Jan Steemann //////////////////////////////////////////////////////////////////////////////// #ifndef ARANGOD_AQL_QUERY_CACHE_H #define ARANGOD_AQL_QUERY_CACHE_H 1 #include "Aql/QueryString.h" #include "Basics/Common.h" #include "Basics/Mutex.h" #include "Basics/ReadWriteLock.h" #include #include struct TRI_vocbase_t; namespace arangodb { class LogicalDataSource; // forward declaration } namespace arangodb { namespace velocypack { class Builder; class Slice; } // namespace velocypack namespace aql { /// @brief cache mode enum QueryCacheMode { CACHE_ALWAYS_OFF, CACHE_ALWAYS_ON, CACHE_ON_DEMAND }; struct QueryCacheProperties { QueryCacheMode mode; uint64_t maxResultsCount; uint64_t maxResultsSize; uint64_t maxEntrySize; bool includeSystem; bool showBindVars; }; struct QueryCacheResultEntry { QueryCacheResultEntry() = delete; QueryCacheResultEntry(uint64_t hash, QueryString const& queryString, std::shared_ptr const& queryResult, std::shared_ptr const& bindVars, std::unordered_map&& dataSources ); ~QueryCacheResultEntry() = default; uint64_t const _hash; std::string const _queryString; std::shared_ptr const _queryResult; std::shared_ptr const _bindVars; // stores datasource guid -> datasource name std::unordered_map const _dataSources; std::shared_ptr _stats; size_t _size; size_t _rows; std::atomic _hits; double _stamp; QueryCacheResultEntry* _prev; QueryCacheResultEntry* _next; void increaseHits() { _hits.fetch_add(1, std::memory_order_relaxed); } double executionTime() const; void toVelocyPack(arangodb::velocypack::Builder& builder) const; }; struct QueryCacheDatabaseEntry { QueryCacheDatabaseEntry(QueryCacheDatabaseEntry const&) = delete; QueryCacheDatabaseEntry& operator=(QueryCacheDatabaseEntry const&) = delete; /// @brief create a database-specific cache QueryCacheDatabaseEntry(); /// @brief destroy a database-specific cache ~QueryCacheDatabaseEntry(); /// @brief lookup a query result in the database-specific cache std::shared_ptr lookup( uint64_t hash, QueryString const& queryString, std::shared_ptr const& bindVars) const; /// @brief store a query result in the database-specific cache void store(std::shared_ptr&& entry, size_t allowedMaxResultsCount, size_t allowedMaxResultsSize); /// @brief invalidate all entries for the given data sources in the /// database-specific cache void invalidate(std::vector const& dataSourceGuids); /// @brief invalidate all entries for a data source in the /// database-specific cache void invalidate(std::string const& dataSourceGuid); void queriesToVelocyPack(arangodb::velocypack::Builder& builder) const; /// @brief enforce maximum number of results /// must be called under the shard's lock void enforceMaxResults(size_t numResults, size_t sizeResults); /// @brief enforce maximum size of individual entries /// must be called under the shard's lock void enforceMaxEntrySize(size_t value); /// @brief exclude all data from system collections /// must be called under the shard's lock void excludeSystem(); /// @brief unlink the result entry from all datasource maps void removeDatasources(QueryCacheResultEntry const* e); /// @brief unlink the result entry from the list void unlink(QueryCacheResultEntry*); /// @brief link the result entry to the end of the list void link(QueryCacheResultEntry*); /// @brief hash table that maps query hashes to query results std::unordered_map> _entriesByHash; /// @brief hash table that contains all data souce-specific query results /// maps from data sources GUIDs to a set of query results as defined in /// _entriesByHash std::unordered_map>> _entriesByDataSourceGuid; /// @brief beginning of linked list of result entries QueryCacheResultEntry* _head; /// @brief end of linked list of result entries QueryCacheResultEntry* _tail; /// @brief number of results in this cache size_t _numResults; /// @brief total size of results in this cache size_t _sizeResults; }; class QueryCache { public: QueryCache(QueryCache const&) = delete; QueryCache& operator=(QueryCache const&) = delete; /// @brief create cache QueryCache(); /// @brief destroy the cache ~QueryCache(); public: /// @brief return the query cache properties QueryCacheProperties properties() const; /// @brief sets the cache properties void properties(QueryCacheProperties const& properties); /// @brief sets the cache properties void properties(arangodb::velocypack::Slice const& properties); /// @brief return the query cache properties void toVelocyPack(arangodb::velocypack::Builder& builder) const; /// @brief test whether the cache might be active /// this is a quick test that may save the caller from further bothering /// about the query cache if case it returns `false` bool mayBeActive() const; /// @brief return whether or not the query cache is enabled QueryCacheMode mode() const; /// @brief return a string version of the mode static std::string modeString(QueryCacheMode); /// @brief return the internal type for a mode string static QueryCacheMode modeString(std::string const&); /// @brief lookup a query result in the cache std::shared_ptr lookup( TRI_vocbase_t* vocbase, uint64_t hash, QueryString const& queryString, std::shared_ptr const& bindVars) const; /// @brief store a query cache entry in the cache void store(TRI_vocbase_t* vocbase, std::shared_ptr entry); /// @brief invalidate all queries for the given data sources void invalidate(TRI_vocbase_t* vocbase, std::vector const& dataSourceGuids); /// @brief invalidate all queries for a particular data source void invalidate(TRI_vocbase_t* vocbase, std::string const& dataSourceGuid); /// @brief invalidate all queries for a particular database void invalidate(TRI_vocbase_t* vocbase); /// @brief invalidate all queries void invalidate(); /// @brief get the pointer to the global query cache static QueryCache* instance(); /// @brief create a velocypack representation of the queries in the cache void queriesToVelocyPack(TRI_vocbase_t* vocbase, arangodb::velocypack::Builder& builder) const; private: /// @brief invalidate all entries in the cache part /// note that the caller of this method must hold the write lock void invalidate(unsigned int part); /// @brief enforce maximum number of results in each database-specific cache /// must be called under the cache's properties lock void enforceMaxResults(size_t numResults, size_t sizeResults); /// @brief enforce maximum size of individual entries in each /// database-specific cache must be called under the cache's properties lock void enforceMaxEntrySize(size_t value); /// @brief exclude all data from system collections void excludeSystem(); /// @brief sets the maximum number of elements in the cache void setMaxResults(size_t numResults, size_t sizeResults); /// @brief sets the maximum size for each entry in the cache void setMaxEntrySize(size_t value); /// @brief sets the "include-system" flag void setIncludeSystem(bool value); /// @brief enable or disable the query cache void setMode(QueryCacheMode); /// @brief determine which part of the cache to use for the cache entries unsigned int getPart(TRI_vocbase_t const*) const; private: /// @brief number of R/W locks for the query cache static constexpr uint64_t numberOfParts = 16; /// @brief protect mode changes with a mutex mutable arangodb::Mutex _propertiesLock; /// @brief read-write lock for the cache mutable arangodb::basics::ReadWriteLock _entriesLock[numberOfParts]; /// @brief cached query entries, organized per database std::unordered_map> _entries[numberOfParts]; }; } // namespace aql } // namespace arangodb #endif