You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Parse Server has no built-in query cache. A simple caching functionality could significantly reduce database costs.
Feature / Enhancement Description
Add a very simple but versatile query cache.
Scope
Only available in Cloud Code.
Auto-fallback on database if no cached result.
Maintain all Cloud Code triggers to be called agnostically, regardless of data source (DB or cache).
Allow to configure cache params (TTL, etc.) per query request for higher versatility.
Provide methods for the developer to flush the cache with custom granularity.
Optionally restart TTL on cache match; useful for data that does not change over time.
Optionally allow to set a custom hash value; this allows to optimize the hash algorithm according to the data set.
Cache key composed of hash value calculated from:
query conditions, e.g. via the hashed JSON.stringify(query.toJSON()) string.
sessionToken or masterKey authentication to preserve ACL, so query result can only be retrieved by session or master key that cached it.
Out of scope:
Automatically flush cache, for example on Parse.Object.save, Parse.Object.destroy and related batch functions --> this would require a complex granular determination of what has changed after saving an object, because changing a field that is not a condition of a cached query shouldn't delete the cached entry; flushing the entire class cache because an object has been saved is likely inefficient in most use cases.
Cache statistics --> There are existing Redis tools for that.
Suggested Parse.Query.cache() method:
/** * @param {Object} options The query cache options. * @param {number|undefined} [options.ttl] Time-to-live in milliseconds for the cached query. Optional; * default is the Parse Server options cache adapter TTL value. * @param {string|undefined} [options.id] The query ID for fine-grained cache invalidation. Optional; * default is undefined. * @param {string|undefined} [options.hash] The hash value for advanced cache customization. Optional; * default is the hash value created from the Parse Query conditions. This allows to override the default * hash value and create a hash that omits query conditions to prioritize reduced database load over data * freshness. * @param {string[]|undefined} [options.indexes] A list of indexes for fine-grained cache invalidation. * Each index creates an entry in a set to logically group cache entries. This allows to invalidate all * cache entries within a logical groups. * * @returns {Parse.Query} The query instance with caching enabled. */
Parse Server cache options:
{queryCache: {// This globally enables or disabled the query cache; when set to `false`, all `Parse.Query.cache()`// conditions have no effect and the cache is not used; this allows to quickly disable caching in// case of unexpected issues with the cache itselfenabled: true,// The default TTL of cached entries; can be overridden by `Parse.Query.cache({ ttl })`ttl: 1_000,// The timeout is milliseconds after which the cache request is cancelled and a request is made to// the database; can be overridden by `Parse.Query.cache({ timeout })`timeout: 10_000,// Reset the TTL of a cached entry on cache hit. Default is `false`; can be overridden by// `Parse.Query.cache({ resetTtlOnCacheHit })`resetTtlOnCacheHit: false,}}
Example Use Case
Simple caching based on class
// Queryconstquery=newParse.Query('Purchase');query.equalTo('user','<USER_OBJECT_ID>');query.equalTo('paid','<PAID_BOOL_VALUE>');query.cache();constusers=awaitquery.find();// Cache keys// parse:query:Purchase:<QUERY_HASH>// Flush entire query cacheawaitParse.Cloud.QueryCache.flush();// Flush query cache only for specific classawaitParse.Cloud.QueryCache.flush({class: 'Purchase'});
Caching with query ID for targeted flushing
// QueryconstpaidQuery=newParse.Query('Purchase');paidQuery.equalTo('user',user);paidQuery.equalTo('paid',true);paidQuery.cache({id: 'paid'});constusers=awaitpaidQuery.find();constinvoicedQuery=newParse.Query('Purchase');invoicedQuery.equalTo('user',user);invoicedQuery.equalTo('invoiced',true);invoicedQuery.cache({id: 'invoiced'});constusers=awaitinvoicedQuery.find();// Cache keys// parse:query:Purchase:paid:<QUERY_HASH>// parse:query:Purchase:invoiced:<QUERY_HASH>// Flush query cache only for specific queryawaitParse.Cloud.QueryCache.flush({class: 'Purchase',id: 'paid'});awaitParse.Cloud.QueryCache.flush({class: 'Purchase',id: 'invoiced'});
Advanced caching with custom hash, indexes for targeted flushing and custom cache TTL
In the example below, the hash value is calculated after removing the paid condition from the query. The purpose of removing the key is to prioritize cost reduction over data freshness. In the example below, Parse Server first tries to find a cached query ignoring the paid condition. If one was found, it returns it, even if it may be outdated. If none was found, it makes a query request to the DB including the paid condition and then caches the result omitting the paid field in the hash value. In order to distinguish a query with an omitted field from a query in which the field is not part of the original query, the id must be set.
// QueryconstqueryJson={className: 'Purchase',where: {user: {'__type': 'Pointer',className: '_User',objectId: '<USER_OBJECT_ID>'},paid: '<PAID_BOOL_VALUE>'},limit: 10};// Create JSON hash without `paid` keyconsthashJson=JSON.parse(JSON.stringify(queryJson));deletehashJson.where.paid;consthash=getHashValue(hashJson);constindexes=['paid:<PAID_BOOL_VALUE>','userId:<USER_OBJECT_ID>'];constquery=Parse.Query.withJSON(queryJson);query.cache({ hash, indexes,id: 'state'ttl: 1000});constusers=awaitquery.find();// Cache keys// parse:query:Purchase:state:<QUERY_HASH>// Created sets of which the cache key above is a member// parse:query:Purchase:state:paid:<PAID_BOOL_VALUE>// parse:query:Purchase:state:userId:<USER_OBJECT_ID>// Flush query cache for all purchase statesawaitParse.Cloud.QueryCache.flush({class: 'Purchase',id: 'state'});// Flush query cache for all purchase states of a specific userawaitParse.Cloud.QueryCache.flush({class: 'Purchase',id: 'state',index: '<USER_OBJECT_ID>'});// Flush query cache for all paid purchases of a specific stateawaitParse.Cloud.QueryCache.flush({class: 'Purchase',id: 'state',index: '<PAID_BOOL_VALUE>'});
New Feature / Enhancement Checklist
Current Limitation
Parse Server has no built-in query cache. A simple caching functionality could significantly reduce database costs.
Feature / Enhancement Description
Add a very simple but versatile query cache.
Scope
JSON.stringify(query.toJSON())
string.sessionToken
ormasterKey
authentication to preserve ACL, so query result can only be retrieved by session or master key that cached it.Out of scope:
Parse.Object.save
,Parse.Object.destroy
and related batch functions --> this would require a complex granular determination of what has changed after saving an object, because changing a field that is not a condition of a cached query shouldn't delete the cached entry; flushing the entire class cache because an object has been saved is likely inefficient in most use cases.Suggested
Parse.Query.cache()
method:Parse Server cache options:
Example Use Case
Simple caching based on class
Caching with query ID for targeted flushing
Advanced caching with custom hash, indexes for targeted flushing and custom cache TTL
In the example below, the hash value is calculated after removing the
paid
condition from the query. The purpose of removing the key is to prioritize cost reduction over data freshness. In the example below, Parse Server first tries to find a cached query ignoring thepaid
condition. If one was found, it returns it, even if it may be outdated. If none was found, it makes a query request to the DB including thepaid
condition and then caches the result omitting thepaid
field in the hash value. In order to distinguish a query with an omitted field from a query in which the field is not part of the original query, theid
must be set.References
The text was updated successfully, but these errors were encountered: