SECTOR 03 // REFERENCE

redis

Drive and observe a Redis cache: probes to assert on, actions to drive workload, over go-redis.

Drive and observe. Probes a Redis cache to assert on (ping, get, exists, info) and drives workload against it (set, del, cmd), over go-redis. The cache outage or latency itself is injected by the fault providers (net, toxiproxy, docker); redis is the workload and observation lens, the same way sql pairs with those faults.

providers:
  cache:
    source: redis
    config:
      addr: "localhost:6379"   # or url: "redis://user:pass@localhost:6379/0"
      password: ""             # optional
      db: 0                    # optional, default 0

Configuration takes either an addr (host:port) with optional username, password, and db, or a single url (redis://…). The client connects lazily, so Redis does not need to be up until the first verb runs (after setup).

Verbs #

ping (probe) #

Round-trips a PING. A connection failure is a probe failure, so ping works as a steadyState gate.

No args.

Returns true. output is "PONG". meta is empty.

steadyState:
  - run: cache.ping

get (probe) #

Reads one key. A cache miss is a normal observation, not a failure, so a scenario can assert a key is gone after an outage.

argtypereqdescription
keystringyesthe key to read (primary)

Returns the value as a string on a hit, or null on a miss, with meta.hit (bool) telling the two apart. output is the value, or "(nil)" on a miss.

- run: cache.get
  with: "session:42"
  as: session
- run: assert
  with: { of: "${.outputs.session.value}", equals: null }
  desc: "cache miss during the partition, not an error"

set (action) #

Writes one key, with an optional TTL.

argtypereqdescription
keystringyesthe key to write (primary)
valueanyyesthe value to store
exnumbernoTTL in seconds

Returns true. output is "OK". meta is empty.

- run: cache.set
  with: { key: "session:42", value: "active", ex: 60 }

del (action) #

Deletes one or more keys.

argtypereqdescription
keyslistyesthe keys to delete; a scalar is accepted for one (primary)

Returns the number of keys deleted (int), also in meta.deleted. output is "deleted <n>".

- run: cache.del
  with: ["session:42", "session:43"]

exists (probe) #

Counts how many of the given keys are present.

argtypereqdescription
keyslistyesthe keys to check; a scalar is accepted for one (primary)

Returns the count present (int). output is "<n> present". meta is empty.

- run: cache.exists
  with: "session:42"
  as: present
- run: assert
  with: { of: "${.outputs.present.value}", equals: 0 }
  desc: "the key expired during the outage"

info (probe) #

Scrapes INFO and parses it into a field map.

argtypereqdescription
sectionstringnoone INFO section (replication, clients, …); all sections when omitted (primary)

Returns the parsed field -> value map as the value (read .role, .connected_clients, and so on). output is the raw INFO text. meta is empty.

- run: cache.info
  with: replication
  read: ".role"
  as: role
- run: assert
  with: { of: "${.outputs.role.value}", equals: "master" }

cmd (action) #

The generic escape hatch for any command go-redis does not wrap. Stays a non-fault by default; set effect: outage on the step when the command injects one (CLIENT KILL, DEBUG SLEEP, SHUTDOWN).

argtypereqdescription
argslistyesthe command and its arguments, e.g. [INCR, counter] (primary)

Returns the command result as the value (a []byte reply becomes a string). output is its string form. meta is empty.

- run: cache.cmd
  with: ["INCR", "requests"]
  as: n

- run: cache.cmd                    # a fault injected through cmd
  with: ["CLIENT", "KILL", "TYPE", "normal"]
  effect: outage