pub struct Redis { /* private fields */ }Expand description
A key in Redis, as a configuration source.
Not Clone: it holds a connection, and two clones sharing a key while each
opening their own would double the connections for no gain. Wrap it in an
Arc if two places need one.
Implementations§
Source§impl Redis
impl Redis
Sourcepub fn new(url: &str, keys: impl Into<Keys>) -> Result<Self, Error>
pub fn new(url: &str, keys: impl Into<Keys>) -> Result<Self, Error>
The key key, on the Redis at url.
The format is taken from the key’s extension — myapp/db.json is JSON.
A key without one needs with_format.
§Errors
If the URL cannot be parsed. Not if the server is unreachable: the connection is opened on the first read, so that construction stays free of I/O like every other source in this family.
Sourcepub fn with_tls(
url: &str,
keys: impl Into<Keys>,
tls: &TlsConfig,
) -> Result<Self, Error>
Available on crate feature tls only.
pub fn with_tls( url: &str, keys: impl Into<Keys>, tls: &TlsConfig, ) -> Result<Self, Error>
tls only.The key key on the Redis at url, with a private certificate
authority or a client certificate.
The same three settings, spelled the same way, in all seven store
crates — and spelled as data, so nothing here names a redis type:
let redis = Redis::with_tls(
"rediss://cache.internal:6379",
"myapp/db.json",
&TlsConfig::new().with_ca_certificate_file("/etc/ssl/private-ca.pem"),
)?;Redis expresses all of it: a CA from a file or from bytes, and a client
certificate from either. The credentials still travel in the URL, as
they do for new.
The URL must be rediss://. A redis:// URL with TLS material is a
deployment that believes it is encrypted and is not, so it is refused
here rather than by the client three layers down.
There is no way to turn verification off; TlsConfig’s own
documentation argues that one. Redis’ client has its own spelling —
the #insecure URL fragment, behind a further feature — and it stays
where it is, under its own frightening name.
§Errors
If the URL cannot be parsed, if it is not rediss://, if a PEM file
cannot be read, or if the material is not PEM. Not if the server is
unreachable: the connection is opened on the first read.
Sourcepub fn from_client(client: Client, keys: impl Into<Keys>) -> Self
pub fn from_client(client: Client, keys: impl Into<Keys>) -> Self
Uses a client the program already has.
For a caller that already talks to Redis, or one that built its client
with options a URL cannot express — including a TLS configuration
with_tls has no spelling for.
Sourcepub fn with_format(self, format: Format) -> Self
pub fn with_format(self, format: Format) -> Self
States the format, for a key whose name does not.
Required for Keys::Prefix — a prefix has no extension — and it also
settles a list whose keys name two different formats.
Sourcepub fn with_timeout(self, timeout: Duration) -> Self
pub fn with_timeout(self, timeout: Duration) -> Self
How long a single fetch may take before it is given up on. Ten seconds by default.
The deadline for one fetch attempt, excluding retries the underlying client performs — the same sentence every store in this family answers to. Redis splits it into three, and this sets all of them from the one value: opening the connection, writing the command, and waiting for the reply.
All three, because any one of them alone is the mistake: a deadline that only covers connecting sails straight past a server that accepted the socket and then stopped answering, which is what a wedged Redis actually looks like.
It bounds each read watch performs, not the watch
itself — a subscription waiting for the next notification is supposed
to wait.
Sourcepub fn reporting_to(self, sink: RemoteSink) -> Self
pub fn reporting_to(self, sink: RemoteSink) -> Self
Reports this source’s watch failures to sink.
A watch loop is the half of a store dynamic-config cannot see. A
delivery keeps RemoteStatus current because
RemoteSink::apply records one — but a loop whose subscription
died, or whose re-read keeps failing, delivers nothing and would
otherwise say nothing: dynamic_config_remote_up would report the
last delivery rather than the last attempt, and a Redis that
stopped answering an hour ago would look healthy until something
called refresh_remote().
// Taken once, where the loop is wired: a sink captures the generation
// of the source installed at that moment, which is what stops a loop
// winding down from charging its failures to its replacement.
let sink = DbConfig::remote_sink();
let watcher = Redis::new(url, "myapp/db.json")?.reporting_to(sink);A failure moves the failure streak and nothing else. The fetch
count and the clock are left alone, so
dynamic_config_remote_last_fetch_seconds keeps ageing while
dynamic_config_remote_up goes to zero — the pair an alert wants.
Only the failure’s kind and key path are recorded; a Redis URL never
reaches a RemoteStatus.
It changes nothing about what watch returns, and
nothing about fetch, which already records
itself through refresh_remote().
Sourcepub fn watch<F>(&self, watching: &Watching, on_change: F) -> Result<(), Error>
pub fn watch<F>(&self, watching: &Watching, on_change: F) -> Result<(), Error>
Calls on_change whenever what this source reads changes.
Uses keyspace notifications: Redis publishes to
__keyspace@{db}__:{key} when a key is written, and this subscribes to
exactly that channel — one per key of the set. Genuinely change-driven —
no polling, no timer.
One key or a named list. A named list is the multi-key case Redis
can answer honestly, and the whole reason is MGET: it is one
command, and Redis executes commands one at a time, so the values it
answers with are the set as of one point in the command stream. The
document delivered here is therefore a state the server really held —
never one key’s new value beside another’s old one, which is the tear
that made every other network store refuse. A prefix is refused;
the reason is on Keys::Prefix.
What the read is not is simultaneous with the notification. It
follows the event, so the document may be newer than the write that
woke the loop, and two writes landing together can deliver the later
state rather than each state in turn. Spurious, never torn — the
same bargain dynamic-config-git’s watch makes, and the one that
matters: a delivery is always a state the store was in, and never an
older one than the delivery before it.
A named list therefore also coalesces: writing three keys together publishes three notifications and this delivers once, because the document the second and third would carry is the one already delivered. Every reload hook running three times for one deployment is a cost with nothing to buy it.
Keyspace notifications are off by default in Redis. A server that has not enabled them publishes nothing, and this loop would wait forever, so it checks at start-up and reports rather than hanging:
CONFIG SET notify-keyspace-events KEAThe current value is not delivered at startup, for the same reason a file watcher does not report an edit when it starts. Fetch first if the starting value matters.
A key that holds nothing is not a change this reports. For one key that
is a deletion; for a named list it is one member of the set going away,
which fails the read the same way fetch does —
and a failed read here is treated as transient, because the next write
notifies again. No configuration is not a configuration, so the running
snapshot stays either way.
§What a failing loop reports
Nothing, unless reporting_to was given a sink.
With one, the two failures inside the loop are reported to the
RemoteStatus as they happen: a re-read that came back with nothing,
and a subscription that died. The refusals at the door — a prefix,
no format, no keys, notifications off, a server that will not accept
the subscription — are not, because they are returned to the caller by
this very call, before there is a loop to be silent in; and half of
them are deployment mistakes rather than a store that stopped
answering, which is not what dynamic_config_remote_up means.
§Errors
If the subscription cannot be established, if keyspace notifications
are off, if the source reads a prefix, if the subscription itself
breaks — a dead connection ends the watch with an error rather than
spinning; restart it to resubscribe — or if on_change returns an
error, so a caller that wants to survive a bad document should log it
and return Ok.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for Redis
impl RefUnwindSafe for Redis
impl Send for Redis
impl Sync for Redis
impl Unpin for Redis
impl UnsafeUnpin for Redis
impl UnwindSafe for Redis
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<T> ErasedDestructor for Twhere
T: 'static,
impl<T> MaybeSendSync for T
Source§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
Source§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();Source§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
Source§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
Source§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
Source§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
Source§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
Source§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
Source§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
Source§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
Source§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();Source§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
Source§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
Source§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
Source§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
Source§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
Source§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
Source§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
Source§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
Source§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
Source§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
Source§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling Attribute value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();Source§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
Source§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi Quirk value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();Source§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
Source§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the Condition value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);