userver: Caching Component for PostgreSQL
Loading...
Searching...
No Matches
Caching Component for PostgreSQL

A typical components::PostgreCache usage consists of trait definition:

struct PostgresTrivialPolicy {
static constexpr std::string_view kName = "my-pg-cache";
using ValueType = MyStructure;
static constexpr auto kKeyMember = &MyStructure::id;
static constexpr const char* kQuery = "SELECT a, b, updated FROM test.data";
static constexpr const char* kUpdatedField = "updated";
using UpdatedFieldType = storages::postgres::TimePointTz;
};

and registration of the component in components::ComponentList:

See Basics of Caches for introduction into caches.

Configuration

components::PostgreCache static configuration file should have a PostgreSQL component name specified in pgcomponent configuration parameter.

Optionally the operation timeouts for cache loading can be specified.

Avoiding memory leaks

components::CachingComponentBase

Name Description Default value
full-update-op-timeout timeout for a full update 1m
incremental-update-op-timeout timeout for an incremental update 1s
update-correction incremental update window adjustment - (0 for caches with defined GetLastKnownUpdated)
chunk-size number of rows to request from PostgreSQL via portals, 0 to fetch all rows in one request without portals 1000

Cache policy

Cache policy is the template argument of components::PostgreCache component. Please see the following code snippet for documentation.

namespace example {
struct MyStructure {
int id = 0;
std::string bar{};
int get_id() const { return id; }
};
struct PostgresExamplePolicy {
// Name of caching policy component.
//
// Required: **yes**
static constexpr std::string_view kName = "my-pg-cache";
// Object type.
//
// Required: **yes**
using ValueType = MyStructure;
// Key by which the object must be identified in cache.
//
// One of:
// - A pointer-to-member in the object
// - A pointer-to-member-function in the object that returns the key
// - A pointer-to-function that takes the object and returns the key
// - A lambda that takes the object and returns the key
//
// Required: **yes**
static constexpr auto kKeyMember = &MyStructure::id;
// Data retrieve query.
//
// The query should not contain any clauses after the `from` clause. Either
// `kQuery` or `GetQuery` static member function must be defined.
//
// Required: **yes**
static constexpr const char* kQuery = "select id, bar, updated from test.my_data";
// Name of the field containing timestamp of an object.
//
// To turn off incremental updates, set the value to `nullptr`.
//
// Required: **yes**
static constexpr const char* kUpdatedField = "updated";
// Type of the field containing timestamp of an object.
//
// Specifies whether updated field should be treated as a timestamp
// with or without timezone in database queries.
//
// Required: **yes** if incremental updates are used.
using UpdatedFieldType = storages::postgres::TimePointTz;
// Where clause of the query. Either `kWhere` or `GetWhere` can be defined.
//
// Required: no
static constexpr const char* kWhere = "id > 10";
// Cache container type.
//
// It can be of any map type. The default is `unordered_map`, it is not
// necessary to declare the DataType alias if you are OK with
// `unordered_map`.
// The key type must match the type of kKeyMember.
//
// Required: no
using CacheContainer = std::unordered_map<int, MyStructure>;
// Cluster host selection flags to use when retrieving data.
//
// Default value is storages::postgres::ClusterHostType::kSlave, at least one
// cluster role must be present in flags.
//
// Required: no
static constexpr auto kClusterHostType = storages::postgres::ClusterHostType::kSlave;
// Whether Get() is expected to return nullptr.
//
// Default value is false, Get() will throw an exception instead of
// returning nullptr.
//
// Required: no
static constexpr bool kMayReturnNull = false;
};
} // namespace example

The query can be a std::string. But due to non-guaranteed order of static data members initialization, std::string should be returned from a static member function, please see the following code snippet.

struct PostgresExamplePolicy4 {
static constexpr std::string_view kName = "my-pg-cache";
using ValueType = MyStructure;
static constexpr auto kKeyMember = &MyStructure::id;
static std::string GetQuery() { return "select id, bar, updated from test.my_data"; }
static constexpr const char* kUpdatedField = "updated";
// no time zone (should be avoided)
using UpdatedFieldType = storages::postgres::TimePointWithoutTz;
};

Policy may have static function GetLastKnownUpdated. It should be used when new entries from database are taken via revision, identifier, or anything else, but not timestamp of the last update. If this function is supplied, new entries are taken from db with condition 'WHERE kUpdatedField > GetLastKnownUpdated(cache_container)'. Otherwise, condition is 'WHERE kUpdatedField > last_update - correction_'. See the following code snippet for an example of usage

struct MyStructureWithRevision {
int id = 0;
std::string bar{};
int32_t revision = 0;
int get_id() const { return id; }
};
class UserSpecificCache {
public:
void insert_or_assign(int, MyStructureWithRevision&& item) {
latest_revision_ = std::max(latest_revision_, item.revision);
}
static size_t size() { return 0; }
int GetLatestRevision() const { return latest_revision_; }
private:
int latest_revision_ = 0;
};
struct PostgresExamplePolicy3 {
using ValueType = MyStructureWithRevision;
static constexpr std::string_view kName = "my-pg-cache";
static constexpr const char* kQuery = "select id, bar, revision from test.my_data";
using CacheContainer = UserSpecificCache;
static constexpr const char* kUpdatedField = "revision";
using UpdatedFieldType = int32_t;
static constexpr auto kKeyMember = &MyStructureWithRevision::get_id;
// Function to get last known revision/time
//
// Optional
// If one wants to get cache updates not based on updated time, but, for
// example, based on revision > known_revision, this method should be used.
static int32_t GetLastKnownUpdated(const UserSpecificCache& container) { return container.GetLatestRevision(); }
};

In case one provides a custom CacheContainer within Policy, it is notified of Update completion via its public member function OnWritesDone, if any. See the following code snippet for an example of usage:

class UserSpecificCacheWithWriteNotification {
public:
void insert_or_assign(int, MyStructure&&) {}
static size_t size() { return 0; }
void OnWritesDone() {}
};

Forward Declaration

To forward declare a cache you can forward declare a trait and include userver/cache/base_postgres_cache_fwd.hpp header. It is also useful to forward declare the cache value type.

#pragma once
#include <memory> // for std::shared_ptr
USERVER_NAMESPACE_BEGIN
namespace example { // replace with a namespace of your trait
struct PostgresExamplePolicy;
struct MyStructure;
} // namespace example
namespace caches {
using MyCache1Data = std::shared_ptr<const example::MyStructure>;
} // namespace caches