A high-performance, Redis 7.2.5-compatible in-memory data store written in modern C++20.
Docker image: alsatianco/peadb-server on Docker Hub.
PeaDB aims to be behaviorally indistinguishable from Redis 7.2.5 — same protocol, same commands, same edge-case semantics — built on cache-friendly data structures and a lean event loop. The command path is single-threaded today (like Redis); a sharded, multi-core architecture is the roadmap target (see docs/architecture.md).
- Full RESP2 / partial RESP3 protocol — works with existing Redis clients and tools, zero changes required.
- Broad command coverage — strings, hashes, lists, sets, sorted sets, streams, HyperLogLog, geo, bitmaps, pub/sub (incl. introspection + sharded aliases), transactions, Lua scripting, and more. (Authoritative, always-current command list, count, and per-command status:
compat/commands.generated.md.) - Persistence — RDB snapshots and AOF logging with a configurable
appendfsyncpolicy (always/everysec/no), plusSAVE/BGSAVE/BGREWRITEAOF. RDB files use Redis's format and checksum, so real Redis and PeaDB load each other's files (including streams and pre-7.0 zipmap/ziplist encodings), and a corrupt dump is refused. For remaining persistence and migration limits, readcompat/delta.mditems 3 and 53 before relying on them. - Memory & eviction —
maxmemorywith all eightmaxmemory-policymodes (LRU/LFU/random/TTL, approximate sampling), a compact tagged-union value layout, per-classclient-output-buffer-limit(slow-consumer/lagging-replica protection), and backgroundUNLINK/FLUSH ASYNC(lazyfree).used_memorycounts every byte PeaDB allocates, as Redis's does, somaxmemorybounds the process; withnoeviction, only commands that can grow memory get-OOM(delta items 38–39). The eviction choice is sampled without Redis's eviction pool (delta item 12). - Replication — native PSYNC2-based replication with multi-replica support, chained replicas,
WAIT(counts only what replicas have applied), manual replica promotion (the promoted server continues the stream, so Redis replicas resume from it with a partial resync), automatic reconnect (exponential backoff) andmasterauth, configurablerepl-backlog-size, and master-driven replica expiry (the master propagatesDEL). Automatic failover/Sentinel is not yet implemented. Full sync forks a disk-backed snapshot and streams it separately from the replica command buffer (delta 46). Reconnects resume cached history with PSYNC; replication timeouts, periodic master PINGs and replica TCP keepalive are active (delta 47). Claims propagate deterministic effects (delta 48). RDB restart history and fallback boundaries are in compatibility delta 47. - Cluster (experimental, not production-safe) — 16 384 hash slots with real per-node ownership, Redis 7.2 reply shapes (
CLUSTER SLOTS/SHARDS/NODES/INFO) andMOVED/ASKredirection naming each slot's true owner, over an operator-configured topology: no gossip, no automatic failover, nonodes.conf. Today cluster mode can write keys on the wrong node (routing checks only the first key; there is noCROSSSLOT), and a restarted node serves slots that other nodes own;redis-cli --clusterworkflows do not work. Seecompat/delta.mditems 6 and 64–67 and the design record. - Lua scripting —
EVAL/EVALSHA,FUNCTIONsubsystem,FCALL/FCALL_RO, with cjson, cmsgpack, bit, and struct libraries. These libraries are PeaDB's own code and some output differs from Redis (for examplecjson.encodewrites integers as1.0, delta item 63), and a script whose text containswhile true dois never run (delta item 54). - Redis Modules API — minimal
RedisModule_*C API; load.somodules at runtime viaMODULE LOAD. - Observability — populated
SLOWLOGandLATENCY(real per-command timing), keyspace notifications (notify-keyspace-events, incl.expired; many write commands still send no event or the wrong one, delta item 72),MONITOR,CLIENT LIST/KILL, runtimeCONFIG SET loglevel, andINFOin Redis's format (RSS, fragmentation ratio, fork timing, ops/sec) forredis_exporterand dashboards. About 16 standard INFO fields are still missing (delta item 22), and each INFO call walks the whole keyspace (delta item 55). - Docker-ready — published image at
alsatianco/peadb-server, plus multi-stage Dockerfile and docker-compose for local builds.
- C++20 compiler (GCC 11+ or Clang 14+)
- CMake ≥ 3.16
Lua 5.1.5 is bundled (third_party/lua-5.1.5), so no Lua dev package is needed. It is stock Lua 5.1.5, not the patched copy Redis 7.2.5 embeds, so some script results differ from Redis (for example table order for long string keys): see compat/delta.md items 62–63.
On Ubuntu/Debian:
sudo apt-get install build-essential cmake g++Or use the provided helper:
scripts/dev/install_deps_ubuntu.shcmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)./build/peadb-server --port 6379 --bind 127.0.0.1Then connect with any Redis client:
redis-cli -p 6379
127.0.0.1:6379> SET hello world
OK
127.0.0.1:6379> GET hello
"world"Or use the one-liner:
scripts/dev/quick_start.shquick_start.sh also does a best-effort raise of the shell nofile soft limit to 65536 (override with PEADB_NOFILE_SOFT) to keep high-connection tests stable out of the box.
Run the published image directly:
docker run -d --name peadb -p 6379:6379 -v peadb-data:/data alsatianco/peadb-server:latest \
--port 6379 --bind 0.0.0.0 --dir /data --appendonly yes --requirepass "<your-password>"Arguments after the image name replace the image's default CMD (--port 6379 --bind 0.0.0.0 --dir /data) completely, so the line above repeats those defaults. Without --bind 0.0.0.0, PeaDB listens on 127.0.0.1 inside the container and the published port cannot reach it.
The image turns on neither save points nor AOF, so without --appendonly yes (or --save) nothing is saved to the /data volume unless you run SAVE or BGSAVE yourself. docker stop sends SIGTERM, which shuts PeaDB down like SHUTDOWN (with save points it saves first, as Redis does, and a master first waits up to shutdown-timeout seconds, default 10, for replicas that are behind); Docker's default grace time is also 10 seconds, so give it more (docker stop -t) when the dataset is large or replicas may lag. Read the persistence and replication boundaries in compat/delta.md (items 2/3/8/53) first.
protected-mode is on by default: a client reaching the container through the published port is not seen as a loopback connection, so without a password PeaDB refuses it with -DENIED. Pass --requirepass as above, or, only if your network already isolates the container (for example a private Docker network with no published port), use --protected-mode no instead.
See the Docker Hub tags page for the current list of published tags — see docs/RELEASING.md for what each tag means and when latest moves.
Or build and run locally with Docker Compose:
docker compose up -dThe container exposes port 6379 (configurable via PEADB_PORT) and mounts a named volume at /data. Like the image, the compose file turns on neither save points nor AOF, so add --appendonly yes to its command to keep data in that volume. For high-connection load tests, compose sets ulimits.nofile by default (PEADB_NOFILE_SOFT / PEADB_NOFILE_HARD, both default to 65536).
PeaDB reads its configuration the way redis-server 7.2.5 does: an optional config file as the first argument (peadb-server /etc/redis/redis.conf), then command-line options, where --name value means the same as a name value line and the last value wins (peadb-server --port 6380 --save ""). A lone - as the first or last argument reads the config from stdin, and PeaDB also accepts --config <path> as another way to name the file. Lines are split like Redis splits them (quotes and escapes work, and # starts a comment only at the start of a line, so requirepass pass#word keeps the #). Setting names and their old aliases (slaveof, hash-max-ziplist-entries, …) are Redis's own, include works (also with wildcards), and every value is checked the way Redis checks it.
PeaDB refuses to start, with Redis's error text and the line number, in the cases where Redis's config loader refuses: a config file that cannot be opened, a name Redis does not know, the wrong number of arguments (a # note after a value counts as extra arguments), unbalanced quotes, a value Redis rejects, a logfile that cannot be opened, rename-command with no target, and rename-command for a command that does not exist (or no longer exists after an earlier rename) or onto a name that is taken. It also refuses the Redis settings it cannot honour safely: user and aclfile (PeaDB has no ACL users), loadmodule, masteruser, tls-port other than 0, tls-replication yes, tls-cluster yes, enable-debug-command no or local, bind with more than one address, and --sentinel. These settings are judged on their final value, so a later line or option can undo an earlier one (--bind 0.0.0.0 after the stock bind 127.0.0.1 -::1 line starts). Every other Redis setting that PeaDB does not apply yet is accepted, and the log gets one warning for each, naming its last line (compat/delta.md item 24 lists them and the few checks Redis makes that PeaDB does not), unless the value is what PeaDB always does (for example daemonize no or databases 16).
The settings PeaDB applies (each works as a config-file line and as a --name option):
| Setting | Default | Description |
|---|---|---|
port |
6379 |
Listening port; 0 opens no TCP listener (then unixsocket is needed), as in Redis |
bind |
127.0.0.1 |
Bind address; PeaDB listens on one address only (delta item 69) |
loglevel |
notice |
debug, verbose, notice, warning or nothing |
dir |
. |
Data directory for snapshots and AOF; it must exist. PeaDB makes it the working directory, as Redis does, so later relative paths are inside it |
dbfilename |
dump.rdb |
RDB snapshot filename (a file name, not a path) |
maxclients |
10000 |
Maximum concurrent client connections; PeaDB raises the open-files limit to fit them, or lowers this value |
appendonly |
no |
Enable AOF persistence |
appendfilename |
appendonly.aof |
AOF filename (a file name, not a path) |
appendfsync |
everysec |
AOF fsync policy (always / everysec / no) |
requirepass |
— | Require clients to AUTH with this password (empty = open, like Redis) |
replicaof (slaveof) |
— | Start as a replica of host port; no one starts as a master. See replica persistence and restart item 45 |
masterauth |
— | Password a replica sends during each master handshake; empty skips AUTH |
masteruser |
— | Master ACL username; empty uses password-only AUTH. Config/CLI/CONFIG SET, effective on reconnect; see replication authentication item 50 |
protected-mode |
yes |
Refuse non-loopback clients when no password is set |
enable-module-command |
no |
Gate MODULE LOAD (yes / no / local) — MODULE LOAD runs arbitrary native code |
enable-protected-configs |
no |
Allow CONFIG SET dir and CONFIG SET dbfilename (yes, or local for loopback and unix-socket clients) |
rename-command |
— | rename-command FROM TO renames a command; rename-command FROM "" removes it, as in Redis |
include |
— | Read another config file at this point; a path with *, ? or [ is a wildcard, and one that matches nothing is skipped |
maxmemory |
0 (off) |
Memory limit (e.g. 100mb, 2gb); above it, keys are evicted per maxmemory-policy, else commands that can grow memory (Redis's denyoom ones: SET, RPUSH, …) get -OOM, while DEL, EXPIRE, reads and the like still run |
maxmemory-eviction-tenacity |
10 |
How long one eviction pass may run before the rest goes on in the background (10: 500 µs, 100: no limit) |
maxmemory-policy |
noeviction |
noeviction | allkeys-lru | allkeys-lfu | allkeys-random | volatile-lru | volatile-lfu | volatile-random | volatile-ttl (approximate sampling) |
maxmemory-samples |
5 |
Keys sampled per eviction when choosing a victim (higher = more accurate, slower) |
lfu-log-factor |
10 |
How slowly the LFU access counter grows (OBJECT FREQ); counters never decay (lfu-decay-time is always 0) |
timeout |
0 (off) |
Close clients idle longer than N seconds (replicas, blocked, MULTI, and pub/sub clients are exempt) |
tcp-keepalive |
300 |
Seconds of silence before the first TCP keepalive probe on a new client connection; 0 turns keepalive off |
proto-max-bulk-len |
512mb |
Largest bulk argument a client may send, and largest string SETRANGE/APPEND/SETBIT may build |
client-query-buffer-limit |
1gb |
A client whose unread input grows past this is disconnected |
tcp-backlog |
511 |
listen(2) backlog (Redis default) |
unixsocket |
— | Also listen on an AF_UNIX socket path |
unixsocketperm |
0 |
Octal file mode for the unix socket (e.g. 700); 0 leaves the mode it was created with |
save |
none | Save points, e.g. save 900 1 (each line adds one; save "" clears them; save 60 0 saves every 60 s); triggers BGSAVE + saves on SHUTDOWN, SIGTERM and SIGINT (change that with shutdown-on-sigterm/shutdown-on-sigint). With no save line Redis uses 3600 1 300 100 60 10000, PeaDB uses none (delta item 2) |
shutdown-timeout |
10 |
Seconds a master's SHUTDOWN, SIGTERM or SIGINT waits for replicas that have not acknowledged every write; writes wait meanwhile, reads are served (0: no wait; SHUTDOWN NOW does not wait either) |
client-output-buffer-limit |
Redis defaults | Per-class (normal/replica/pubsub) output-buffer limits; overflowing clients are disconnected |
notify-keyspace-events |
"" (off) |
Keyspace notification classes (Redis flag syntax) |
repl-backlog-size |
1mb |
Replication backlog size for partial resync |
repl-timeout |
60 |
Seconds of inactivity before a replica reconnects during connect/handshake/transfer/streaming, or a master disconnects a replica whose ACKs stop |
repl-ping-replica-period (repl-ping-slave-period) |
10 |
Seconds between top-level master PINGs in the replication stream |
min-replicas-to-write |
0 |
Require healthy replicas for writes; see replication admission (item 8) |
min-replicas-max-lag |
10 |
Maximum ACK age in seconds for replica write admission |
replica-serve-stale-data |
yes |
Whether a replica answers reads while its master link is down (with no, only the commands Redis allows on a stale replica run) |
busy-reply-threshold (lua-time-limit) |
5000 |
Milliseconds a script may run before other clients get -BUSY |
lazyfree-lazy-server-del |
no |
Propagate server-side deletes as UNLINK |
stop-writes-on-bgsave-error |
yes |
Refuse writes and PING with -MISCONF after a failed background save while save points are set, until a save succeeds (a failed fork() counts, as in Redis) |
aof-load-truncated |
yes |
Accept an AOF whose last command is cut short, and cut that command off the file, as Redis does (delta item 30) |
slowlog-log-slower-than, slowlog-max-len |
10000, 128 |
SLOWLOG threshold (µs) and length |
latency-monitor-threshold |
0 (off) |
LATENCY monitor threshold (ms) |
hash-max-listpack-entries, hash-max-listpack-value, list-max-listpack-size, set-max-intset-entries, set-max-listpack-entries, set-max-listpack-value, zset-max-listpack-entries, zset-max-listpack-value |
Redis defaults | Encoding thresholds reported by OBJECT ENCODING |
cluster-enabled |
no |
Enable cluster mode; startup-only, like Redis's IMMUTABLE_CONFIG — CONFIG SET cluster-enabled is refused |
cluster-announce-ip |
— | Address this node announces to peers/clients; empty falls back to bind — a documented simplification of Redis's live-connection auto-detection (see docs/decisions/p5-cluster-addressing-and-version-provenance.md). With cluster-enabled yes, the server refuses to start if this falls back to a wildcard (0.0.0.0, ::, *, or empty) |
cluster-announce-port |
0 |
Port this node announces; 0 falls back to port |
cluster-announce-bus-port |
0 |
Cluster bus port this node announces; 0 falls back to the announced port + 10000 |
CONFIG GET and CONFIG SET work like Redis 7.2.5's: CONFIG GET knows every Redis setting and alias, and CONFIG SET takes several settings at once, all or nothing, with Redis's checks and error text (an unknown name, an immutable setting, a protected one such as dir, a bad value). The settings in the table above take effect at once, appendonly yes included, except port and bind, which PeaDB cannot change while it runs yet (delta item 25). For a Redis setting PeaDB does not apply, CONFIG SET refuses every value except the one that describes what PeaDB always does (so rdbcompression yes is refused); a setting with no such value keeps what it is given, has no effect, and gets a warning in the log (for example hz). Delta item 24 lists these settings. Use CONFIG REWRITE to persist runtime settings and the current replication role to the startup config file; its file-publication and include/default-user boundaries are documented in delta item 25.
Command list (see commands.generated.md for the authoritative, always-current list)
Connection & Server — PING, ECHO, QUIT, HELLO, AUTH, ACL, INFO, COMMAND, CONFIG, DEBUG, SELECT, SAVE, BGSAVE, LASTSAVE, BGREWRITEAOF, WAIT, TIME, DBSIZE, FLUSHALL, FLUSHDB, SHUTDOWN
Strings — GET, SET, SETEX, PSETEX, SETNX, MGET, MSET, MSETNX, GETDEL, GETSET, GETEX, APPEND, STRLEN, INCR, INCRBY, DECR, DECRBY, INCRBYFLOAT, SETBIT, GETBIT, SETRANGE, GETRANGE, SUBSTR, LCS
Keys — DEL, UNLINK, EXISTS, TYPE, TTL, PTTL, EXPIRETIME, PEXPIRETIME, EXPIRE, PEXPIRE, EXPIREAT, PEXPIREAT, PERSIST, KEYS, RANDOMKEY, SCAN, RENAME, RENAMENX, COPY, MOVE, DUMP, RESTORE, RESTORE-ASKING, MIGRATE, SORT, OBJECT, SWAPDB
Hashes — HSET, HGET, HMGET, HMSET, HSETNX, HDEL, HLEN, HEXISTS, HGETALL, HVALS, HKEYS, HINCRBY, HINCRBYFLOAT, HSCAN
Lists — LPUSH, RPUSH, LPOP, RPOP, LLEN, LRANGE, BLPOP, BRPOP, LMOVE, BLMOVE, BRPOPLPUSH
Sets — SADD, SREM, SISMEMBER, SMEMBERS, SCARD, SPOP, SMOVE, SINTERCARD, SSCAN
HyperLogLog — PFADD, PFCOUNT, PFMERGE (self-consistent dense form; see compat/delta.md)
Sorted Sets — ZADD (NX/XX/GT/LT/INCR), ZRANGE, ZSCAN, ZPOPMIN, ZPOPMAX, ZMPOP, BZPOPMIN, BZPOPMAX
Streams — XADD, XDEL, XLEN, XRANGE, XREVRANGE, XGROUP, XREADGROUP, XREAD (blocking), XACK, XPENDING, XINFO, XSETID, XTRIM, XCLAIM, XAUTOCLAIM — with a full PEL (delivery time/count), deterministic claim effects through replication and AOF replay/rewrite (delta 48), and RDB persistence + replication of streams
Pub/Sub — SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB (CHANNELS/NUMSUB/NUMPAT), SSUBSCRIBE/SUNSUBSCRIBE/SPUBLISH (sharded aliases)
Transactions — MULTI, EXEC, DISCARD, WATCH, UNWATCH
Scripting — EVAL, EVALSHA, EVAL_RO, EVALSHA_RO, SCRIPT (LOAD/EXISTS/FLUSH/KILL), FUNCTION (LOAD/LIST/DELETE/FLUSH/STATS/KILL), FCALL, FCALL_RO
Replication — SYNC, PSYNC, REPLCONF, REPLICAOF, SLAVEOF, WAIT
Cluster — CLUSTER (INFO/MYID/NODES/SLOTS/SHARDS/KEYSLOT/MEET/ADDSLOTS/DELSLOTS/ADDSLOTSRANGE/DELSLOTSRANGE/SETSLOT/COUNTKEYSINSLOT/GETKEYSINSLOT/HELP, plus MEETBACK, a PeaDB-only extension not in Redis), ASKING
Modules — MODULE (LOAD/LIST/UNLOAD)
PeaDB has a comprehensive suite spanning unit, integration, fuzz, and differential testing.
Parity against real Redis is measured by the upstream Redis 7.2.5 TCL suite running in native mode (the official harness spawns peadb-server as a redis-server stand-in). Adopted suites, per-suite baselines, and the monotonic no-regression ratchet live in compat/redis_suite_ledger.json; every skipped upstream test is a tracked debt in scripts/redis/skipfiles/ tied to a compat/delta.md entry.
# Run all tests, including native Redis reports and the suite ratchet
scripts/run_all_tests.sh
# Standalone upstream Redis TCL sweep and diagnostic pass-rate summary
SUITE_MODE=native OUT_DIR=artifacts/redis-native scripts/redis/run_redis_tests.sh
python3 scripts/redis/parse_tcl_results.py artifacts/redis-native
# Unit tests only (via CTest)
cd build && ctest --output-on-failure
# Integration tests only
scripts/ci/run_integration_tests.sh
# Fuzz / stability checks (ASAN/UBSAN)
scripts/qa/run_fuzz_sanity.sh
scripts/qa/run_stability_checks.sh
# Differential tests (compare output against a real Redis instance)
python3 tests/diff/run_diff_tests.py
# Upstream Redis TCL test suite
scripts/redis/run_redis_tests.shIntegration tests are organized by milestone (m0–m9), polish stages (p0–p3), and hardening batches (b*), covering 100+ Python test files across all data types, persistence modes, replication, scripting, modules, and edge cases.
cmake -S . -B build-asan \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer"
cmake --build build-asan -j$(nproc)This build replaces the repo-root peadb-server with the sanitised binary, on purpose. Every integration test resolves the server as ROOT / "peadb-server", so this is how scripts/ci/run_integration_tests.sh ends up running against the sanitiser instead of a plain Release build. The nightly CI "Integration smoke (ASAN)" step relies on the same swap; it runs the tests from build-asan/. If you would rather keep the sanitised binary out of the way in build-asan/ instead, add -DPEADB_SERVER_OUTPUT_DIR=build-asan to the first command above — this is what scripts/qa/run_stability_checks.sh does. Either way, rebuild Release afterwards (cmake --build build) to put a plain peadb-server back at the repo root.
Indicative single-connection throughput (representative, not peak):
| Operation | ops/sec |
|---|---|
| SET | 63,694 |
| GET | 59,524 |
| LPUSH | 62,893 |
| LPOP | 61,728 |
| INCR | 63,694 |
| SADD | 63,694 |
Reproduce with scripts/dev/redis_vs_peadb_benchmark.sh (compares PeaDB against a Redis baseline). The command path is single-threaded, so throughput is competitive at low concurrency while tail latency rises under very high concurrency — the sharded, multi-core architecture on the roadmap targets this.
Standard Redis clients connect to PeaDB unmodified. Native RDB snapshots, DUMP/RESTORE and MIGRATE now exchange every supported type with Redis 7.2.5, including stream history, consumers and pending messages (compat/delta.md items 5 and 51). Named ACL master authentication and deterministic stream claim propagation are implemented (items 50 and 48). Release publication requires the authenticated migration and rollback acceptance gate (item 49); the RDB bridge scripts check replies and preserve databases, stream metadata and functions (item 52). Older zipmap/ziplist encodings load natively; files selecting a database outside 0–15 are refused (item 53). Read the migration guide before relying on these paths.
Full detail — drop-in posture, tested clients, redis.conf support, migration & rollback steps, the feature-status table, persistence internals, and security/monitoring notes — lives in docs/compatibility.md. The authoritative, always-current gap registry is compat/delta.md.
To report a security vulnerability privately, or to read the known security gaps (no TLS, no multi-user ACL, no Sentinel, cluster mode not production-safe) before you deploy, see SECURITY.md.
| Document | What it covers |
|---|---|
| docs/compatibility.md | Drop-in posture, migration & rollback, feature status, persistence & durability, ops notes |
| docs/architecture.md | How PeaDB works internally + design principles and roadmap (for contributors) |
| compat/delta.md | Canonical registry of compatibility gaps vs Redis 7.2.5 |
| compat/commands.generated.md | Auto-generated per-command coverage scoreboard |
| scripts/README.md | Guide to the helper, CI, QA, and dev scripts |
| CLAUDE.md | Build, test, and architecture notes for contributors and AI assistants |
| CHANGELOG.md | Release history and the [Unreleased] parity-wave log |
| SECURITY.md | Supported versions, private vulnerability reporting, known security gaps |
| docs/RELEASING.md | Release tagging convention and the multi-arch publish sequence |
include/ C++ headers (command, config, datastore, protocol, server, …)
src/ Implementation files + main entry point
tests/
unit/ C++ unit tests (CTest)
integration/ Python integration tests (100+ files)
fuzz/ RESP parser and command dispatcher fuzz tests
diff/ Differential tests (PeaDB vs Redis)
scripts/
ci/ CI pipelines and checks
qa/ Fuzz, stability, perf, and sentinel validation
dev/ Developer helpers (quick_start, install_deps)
redis/ Upstream Redis test runner + RDB bridge scripts
compat/ Compatibility delta tracker + generated scoreboard
docs/ Architecture, compatibility, and design-decision records
artifacts/ Benchmark results and performance baselines
MIT © 2026 alsatianco