//------------------------------------------------------------------------------
// File: Options.hh
// Author: Georgios Bitzes - CERN
//------------------------------------------------------------------------------
/************************************************************************
* qclient - A simple redis C++ client with support for redirects *
* Copyright (C) 2018 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#ifndef QCLIENT_OPTIONS_HH
#define QCLIENT_OPTIONS_HH
#include
#include
#include "TlsFilter.hh"
#include "Handshake.hh"
namespace qclient {
class Handshake;
class Logger;
class MessageListener;
//------------------------------------------------------------------------------
//! This struct specifies how to rate-limit writing into QClient.
//!
//! Since QClient offers an asynchornous API, what happens if we're able to
//! produce requests faster than they can be serviced? The request backlog size
//! will start increasing to infinity, and we'll run out of memory.
//!
//! Specifying a backpressure strategy will prevent that.
//------------------------------------------------------------------------------
class BackpressureStrategy {
public:
//----------------------------------------------------------------------------
//! Use this if unsure, should provide a reasonable default value.
//----------------------------------------------------------------------------
static BackpressureStrategy Default() {
return RateLimitPendingRequests();
}
//----------------------------------------------------------------------------
//! Limit pending requests to the specified amount. Once this limit is
//! reached, attempts to issue more requests will block.
//----------------------------------------------------------------------------
static BackpressureStrategy RateLimitPendingRequests(size_t sz = 262144u) {
BackpressureStrategy ret;
ret.enabled = true;
ret.pendingRequestLimit = sz;
return ret;
}
//----------------------------------------------------------------------------
//! Use this only if you have a good reason to, Default() should work fine
//! for the vast majority of use cases.
//----------------------------------------------------------------------------
static BackpressureStrategy InfinitePendingRequests() {
BackpressureStrategy ret;
ret.enabled = false;
return ret;
}
bool active() const {
return enabled;
}
size_t getRequestLimit() const {
return pendingRequestLimit;
}
private:
//----------------------------------------------------------------------------
//! Private constructor - use static methods above to create an object.
//----------------------------------------------------------------------------
BackpressureStrategy() {}
bool enabled = false;
size_t pendingRequestLimit = 0u;
};
//------------------------------------------------------------------------------
//! Class RetryStrategy
//------------------------------------------------------------------------------
class RetryStrategy {
private:
//----------------------------------------------------------------------------
//! Private constructor, use static methods below to construct an object.
//----------------------------------------------------------------------------
RetryStrategy() {}
public:
enum class Mode {
kNoRetries = 0,
kRetryWithTimeout,
kInfiniteRetries,
kNRetries
};
//----------------------------------------------------------------------------
//! No retries.
//----------------------------------------------------------------------------
static RetryStrategy NoRetries() {
RetryStrategy val;
val.mode = Mode::kNoRetries;
return val;
}
//----------------------------------------------------------------------------
//! Retry, up until the specified timeout.
//! NOTE: Timeout is per-connection, not per request.
//----------------------------------------------------------------------------
static RetryStrategy WithTimeout(std::chrono::seconds tm) {
RetryStrategy val;
val.mode = Mode::kRetryWithTimeout;
val.timeout = tm;
return val;
}
//----------------------------------------------------------------------------
//! Infinite number of retries - hang forever if backend is not available.
//----------------------------------------------------------------------------
static RetryStrategy InfiniteRetries() {
RetryStrategy val;
val.mode = Mode::kInfiniteRetries;
return val;
}
//----------------------------------------------------------------------------
//! Limited number of retries
//----------------------------------------------------------------------------
static RetryStrategy NRetries(int64_t retries) {
RetryStrategy val;
if (retries) {
val.mode = Mode::kNRetries;
val.retries = retries;
} else {
val.mode = Mode::kNoRetries;
}
return val;
}
Mode getMode() const {
return mode;
}
std::chrono::seconds getTimeout() const {
return timeout;
}
int64_t getRetries() const {
return retries;
}
bool active() const {
return mode != Mode::kNoRetries;
}
private:
Mode mode { Mode::kNoRetries };
int64_t retries {0};
//----------------------------------------------------------------------------
//! Timeout is per-connection, not per request. Only applies if mode
//! is kRetryWithTimeout.
//----------------------------------------------------------------------------
std::chrono::seconds timeout {0};
};
//------------------------------------------------------------------------------
//! QClient Options class.
//------------------------------------------------------------------------------
class Options {
public:
//----------------------------------------------------------------------------
//! If enabled, QClient will try to transparently handle -MOVED redirects.
//----------------------------------------------------------------------------
bool transparentRedirects = false;
//----------------------------------------------------------------------------
//! Specifies how to handle failing rqeuests. Default is NoRetries, meaning
//! if the connection drops, some requests receive redisReplyPtr == nullptr,
//! and it is up to the user to resubmit them.
//!
//! An alternative would be RetryStrategy::WithTimeout(60), which will
//! resubmit the request for up to 60 seconds, before returning
//! redisReplyPtr == nullptr for any particular request.
//----------------------------------------------------------------------------
RetryStrategy retryStrategy = RetryStrategy::NoRetries();
//----------------------------------------------------------------------------
//! Specifies whether to rate-limit writing into QClient. If there are
//! too many un-acknowledged pending requests, attempting to issue more will
//! block.
//!
//! Default is a maximum of 262144 pending requests in-flight.
//----------------------------------------------------------------------------
BackpressureStrategy backpressureStrategy = BackpressureStrategy::Default();
//----------------------------------------------------------------------------
//! Specifies whether to use TLS - default is off.
//----------------------------------------------------------------------------
TlsConfig tlsconfig = {};
//----------------------------------------------------------------------------
//! Specifies the handshake to use. A handshake is a sequence of redis
//! commands sent before any other on a particular connection. If the
//! connection drops and reconnects, the handshake will run again.
//!
//! Ideal for things like AUTH.
//----------------------------------------------------------------------------
std::unique_ptr handshake = {};
//----------------------------------------------------------------------------
//! If enabled, QClient will make sure to prime a connection immediately
//! after connecting.
//----------------------------------------------------------------------------
bool ensureConnectionIsPrimed = true;
//----------------------------------------------------------------------------
//! TCP connection timeout: The amount of time to wait between issuing
//! ::connect() towards an endpoint, and deciding that endpoint is broken if
//! no connection has been established within the timeout.
//!
//! This is required because QClient may have to try several endpoints in a
//! row: Multiple IPs from a host times multiple hosts.
//!
//! Given the default TCP timeout is often in the minutes, we would hang for
//! very long in case some of the endpoints to try were dropping packets.
//----------------------------------------------------------------------------
std::chrono::seconds tcpTimeout = std::chrono::seconds(2);
//----------------------------------------------------------------------------
//! Specifies the logger object to use. If left empty, a simple logger
//! writing to stderr will be used, with LogLevel::kInfo.
//----------------------------------------------------------------------------
std::shared_ptr logger;
//----------------------------------------------------------------------------
//! Specifies the message listener to use. Receives all push responses, and
//! sets connection to exclusive pub-sub mode if exclusivePubsub is set
//! to true.
//----------------------------------------------------------------------------
std::shared_ptr messageListener;
//----------------------------------------------------------------------------
//! If a message listener is set, should we set the connection to exclusive
//! pubsub mode, and deliver everything to the listener?
//!
//! If true: Everything passes through the message listener - do not use
//! the QClient object in normal request / response mode.
//!
//! If false: Only push types go through the listener, and the connection
//! can be used normally. (Only makes sense to enable with QDB right now)
//----------------------------------------------------------------------------
bool exclusivePubsub = true;
//----------------------------------------------------------------------------
//! Performance object callback
//----------------------------------------------------------------------------
std::shared_ptr mPerfCb;
//----------------------------------------------------------------------------
//! Fluent interface: Chain a handshake. Explicit transfer of ownership to
//! this object.
//!
//! If given handshake is nullptr, nothing is done.
//! If there's no existing handshake, the given handshake is set to be the
//! top-level one.
//----------------------------------------------------------------------------
qclient::Options& chainHandshake(std::unique_ptr handshake);
//----------------------------------------------------------------------------
//! Fluent interface: Chain HMAC handshake. If password is empty, any existing
//! handshake is left untouched.
//----------------------------------------------------------------------------
qclient::Options& chainHmacHandshake(const std::string &password);
//----------------------------------------------------------------------------
//! Fluent interface: Enable transparent redirects
//----------------------------------------------------------------------------
qclient::Options& withTransparentRedirects();
//----------------------------------------------------------------------------
//! Fluent interface: Disable transparent redirects
//----------------------------------------------------------------------------
qclient::Options& withoutTransparentRedirects();
//----------------------------------------------------------------------------
//! Fluent interface: Setting backpressure strategy
//----------------------------------------------------------------------------
qclient::Options& withBackpressureStrategy(const BackpressureStrategy& str);
//----------------------------------------------------------------------------
//! Fluent interface: Setting retry strategy
//----------------------------------------------------------------------------
qclient::Options& withRetryStrategy(const RetryStrategy& str);
};
//------------------------------------------------------------------------------
//! Options class for a Subscriber.
//------------------------------------------------------------------------------
class SubscriptionOptions {
public:
//----------------------------------------------------------------------------
//! Specifies whether to use TLS - default is off.
//----------------------------------------------------------------------------
TlsConfig tlsconfig = {};
//----------------------------------------------------------------------------
//! Specifies the handshake to use. A handshake is a sequence of redis
//! commands sent before any other on a particular connection. If the
//! connection drops and reconnects, the handshake will run again.
//!
//! Ideal for things like AUTH.
//----------------------------------------------------------------------------
std::unique_ptr handshake = {};
//----------------------------------------------------------------------------
//! Specifies the logger object to use. If left empty, a simple logger
//! writing to stderr will be used, with LogLevel::kInfo.
//----------------------------------------------------------------------------
std::shared_ptr logger;
//----------------------------------------------------------------------------
//! Use push types? (QDB only for now, but official redis should work as well
//! once RESP3 is released)
//----------------------------------------------------------------------------
bool usePushTypes = false;
//----------------------------------------------------------------------------
//! Specifies how to handle failing rqeuests. Default is NoRetries, meaning
//! if the connection drops, some requests receive redisReplyPtr == nullptr,
//! and it is up to the user to resubmit them.
//----------------------------------------------------------------------------
RetryStrategy retryStrategy = RetryStrategy::NoRetries();
};
}
#endif