diff --git a/README.md b/README.md
index 0f3772c..667e671 100644
--- a/README.md
+++ b/README.md
@@ -1,138 +1,172 @@
-# socket.io-redis
+# Socket.IO Redis adapter
-[![Build Status](https://github.com/socketio/socket.io-redis/workflows/CI/badge.svg?branch=main)](https://github.com/socketio/socket.io-redis/actions)
-[![npm version](https://badge.fury.io/js/%40socket.io%2Fredis-adapter.svg)](https://badge.fury.io/js/%40socket.io%2Fredis-adapter)
+The `@socket.io/redis-adapter` package allows broadcasting packets between multiple Socket.IO servers.
-## Table of contents
+
-- [How to use](#how-to-use)
- - [CommonJS](#commonjs)
- - [ES6 module](#es6-modules)
- - [TypeScript](#typescript)
- - [Sharded Redis Pub/Sub](#sharded-redis-pubsub)
+**Table of contents**
+
+- [Supported features](#supported-features)
+- [Installation](#installation)
- [Compatibility table](#compatibility-table)
-- [How does it work under the hood?](#how-does-it-work-under-the-hood)
-- [API](#api)
- - [adapter(pubClient, subClient[, opts])](#adapterpubclient-subclient-opts)
- - [RedisAdapter](#redisadapter)
- - [RedisAdapter#allRooms()](#redisadapterallrooms)
-- [With ioredis client](#with-ioredis-client)
- - [Cluster example](#cluster-example)
- - [Sentinel Example](#sentinel-example)
-- [Protocol](#protocol)
-- [Migrating from `socket.io-redis`](#migrating-from-socketio-redis)
+- [Usage](#usage)
+ - [With the `redis` package](#with-the-redis-package)
+ - [With the `redis` package and a Redis cluster](#with-the-redis-package-and-a-redis-cluster)
+ - [With the `ioredis` package](#with-the-ioredis-package)
+ - [With the `ioredis` package and a Redis cluster](#with-the-ioredis-package-and-a-redis-cluster)
+ - [With Redis sharded Pub/Sub](#with-redis-sharded-pubsub)
+- [Options](#options)
+ - [Default adapter](#default-adapter)
+ - [Sharded adapter](#sharded-adapter)
- [License](#license)
-## How to use
+## Supported features
+
+| Feature | `socket.io` version | Support |
+|---------------------------------|---------------------|------------------------------------------------|
+| Socket management | `4.0.0` | :white_check_mark: YES (since version `6.1.0`) |
+| Inter-server communication | `4.1.0` | :white_check_mark: YES (since version `7.0.0`) |
+| Broadcast with acknowledgements | `4.5.0` | :white_check_mark: YES (since version `7.2.0`) |
+| Connection state recovery | `4.6.0` | :x: NO |
-Installation:
+## Installation
```
-$ npm install @socket.io/redis-adapter redis
+npm install @socket.io/redis-adapter
```
-### CommonJS
-
-```js
-const { Server } = require('socket.io');
-const { createClient } = require('redis');
-const { createAdapter } = require('@socket.io/redis-adapter');
+## Compatibility table
-const io = new Server();
-const pubClient = createClient({ host: 'localhost', port: 6379 });
-const subClient = pubClient.duplicate();
+| Redis Adapter version | Socket.IO server version |
+|-----------------------|--------------------------|
+| 4.x | 1.x |
+| 5.x | 2.x |
+| 6.0.x | 3.x |
+| 6.1.x | 4.x |
+| 7.x and above | 4.3.1 and above |
-Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
- io.adapter(createAdapter(pubClient, subClient));
- io.listen(3000);
-});
-```
+## Usage
-With `redis@3`, calling `connect()` on the Redis clients is not needed:
+### With the `redis` package
```js
-const { Server } = require('socket.io');
-const { createClient } = require('redis');
-const { createAdapter } = require('@socket.io/redis-adapter');
+import { createClient } from "redis";
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/redis-adapter";
-const io = new Server();
-const pubClient = createClient({ host: 'localhost', port: 6379 });
+const pubClient = createClient({ url: "redis://localhost:6379" });
const subClient = pubClient.duplicate();
-io.adapter(createAdapter(pubClient, subClient));
+await Promise.all([
+ pubClient.connect(),
+ subClient.connect()
+]);
+
+const io = new Server({
+ adapter: createAdapter(pubClient, subClient)
+});
io.listen(3000);
```
-### ES6 modules
+### With the `redis` package and a Redis cluster
```js
-import { Server } from 'socket.io';
-import { createClient } from 'redis';
-import { createAdapter } from '@socket.io/redis-adapter';
-
-const io = new Server();
-const pubClient = createClient({ host: 'localhost', port: 6379 });
+import { createCluster } from "redis";
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/redis-adapter";
+
+const pubClient = createCluster({
+ rootNodes: [
+ {
+ url: "redis://localhost:7000",
+ },
+ {
+ url: "redis://localhost:7001",
+ },
+ {
+ url: "redis://localhost:7002",
+ },
+ ],
+});
const subClient = pubClient.duplicate();
-Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
- io.adapter(createAdapter(pubClient, subClient));
- io.listen(3000);
+await Promise.all([
+ pubClient.connect(),
+ subClient.connect()
+]);
+
+const io = new Server({
+ adapter: createAdapter(pubClient, subClient)
});
+
+io.listen(3000);
```
-### TypeScript
+### With the `ioredis` package
-```ts
-import { Server } from 'socket.io';
-import { createClient } from 'redis';
-import { createAdapter } from '@socket.io/redis-adapter';
+```js
+import { Redis } from "ioredis";
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/redis-adapter";
-const io = new Server();
-const pubClient = createClient({ host: 'localhost', port: 6379 });
+const pubClient = new Redis();
const subClient = pubClient.duplicate();
-Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
- io.adapter(createAdapter(pubClient, subClient));
- io.listen(3000);
+const io = new Server({
+ adapter: createAdapter(pubClient, subClient)
});
-```
-By running Socket.IO with the `@socket.io/redis-adapter` adapter you can run
-multiple Socket.IO instances in different processes or servers that can
-all broadcast and emit events to and from each other.
+io.listen(3000);
+```
-So any of the following commands:
+### With the `ioredis` package and a Redis cluster
```js
-io.emit('hello', 'to all clients');
-io.to('room42').emit('hello', "to all clients in 'room42' room");
+import { Cluster } from "ioredis";
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/redis-adapter";
-io.on('connection', (socket) => {
- socket.broadcast.emit('hello', 'to all clients except sender');
- socket.to('room42').emit('hello', "to all clients in 'room42' room except sender");
-});
-```
+const pubClient = new Cluster([
+ {
+ host: "localhost",
+ port: 7000,
+ },
+ {
+ host: "localhost",
+ port: 7001,
+ },
+ {
+ host: "localhost",
+ port: 7002,
+ },
+]);
+const subClient = pubClient.duplicate();
-will properly be broadcast to the clients through the Redis [Pub/Sub mechanism](https://redis.io/topics/pubsub).
+const io = new Server({
+ adapter: createAdapter(pubClient, subClient)
+});
-If you need to emit events to socket.io instances from a non-socket.io
-process, you should use [socket.io-emitter](https://github.com/socketio/socket.io-emitter).
+io.listen(3000);
+```
-### Sharded Redis Pub/Sub
+### With Redis sharded Pub/Sub
Sharded Pub/Sub was introduced in Redis 7.0 in order to help scaling the usage of Pub/Sub in cluster mode.
-Reference: https://redis.io/docs/manual/pubsub/#sharded-pubsub
+Reference: https://redis.io/docs/interact/pubsub/#sharded-pubsub
A dedicated adapter can be created with the `createShardedAdapter()` method:
```js
-import { Server } from 'socket.io';
-import { createClient } from 'redis';
-import { createShardedAdapter } from '@socket.io/redis-adapter';
+import { Server } from "socket.io";
+import { createClient } from "redis";
+import { createShardedAdapter } from "@socket.io/redis-adapter";
-const pubClient = createClient({ host: 'localhost', port: 6379 });
+const pubClient = createClient({ host: "localhost", port: 6379 });
const subClient = pubClient.duplicate();
await Promise.all([
@@ -152,201 +186,26 @@ Minimum requirements:
- Redis 7.0
- [`redis@4.6.0`](https://github.com/redis/node-redis/commit/3b1bad229674b421b2bc6424155b20d4d3e45bd1)
+Note: it is not currently possible to use the sharded adapter with the `ioredis` package and a Redis cluster ([reference](https://github.com/luin/ioredis/issues/1759)).
-## Compatibility table
-
-| Redis Adapter version | Socket.IO server version |
-|-----------------------| ------------------------ |
-| 4.x | 1.x |
-| 5.x | 2.x |
-| 6.0.x | 3.x |
-| 6.1.x | 4.x |
-| 7.x and above | 4.3.1 and above |
-
-## How does it work under the hood?
-
-This adapter extends the [in-memory adapter](https://github.com/socketio/socket.io-adapter/) that is included by default with the Socket.IO server.
-
-The in-memory adapter stores the relationships between Sockets and Rooms in two Maps.
-
-When you run `socket.join("room21")`, here's what happens:
-
-```
-console.log(adapter.rooms); // Map { "room21" => Set { "mdpk4kxF5CmhwfCdAHD8" } }
-console.log(adapter.sids); // Map { "mdpk4kxF5CmhwfCdAHD8" => Set { "mdpk4kxF5CmhwfCdAHD8", "room21" } }
-// "mdpk4kxF5CmhwfCdAHD8" being the ID of the given socket
-```
-
-Those two Maps are then used when broadcasting:
-
-- a broadcast to all sockets (`io.emit()`) loops through the `sids` Map, and send the packet to all sockets
-- a broadcast to a given room (`io.to("room21").emit()`) loops through the Set in the `rooms` Map, and sends the packet to all matching sockets
-
-The Redis adapter extends the broadcast function of the in-memory adapter: the packet is also [published](https://redis.io/topics/pubsub) to a Redis channel (see [below](#protocol) for the format of the channel name).
-
-Each Socket.IO server receives this packet and broadcasts it to its own list of connected sockets.
-
-To check what's happening on your Redis instance:
-
-```
-$ redis-cli
-127.0.0.1:6379> PSUBSCRIBE *
-Reading messages... (press Ctrl-C to quit)
-1) "psubscribe"
-2) "*"
-3) (integer) 1
-
-1) "pmessage"
-2) "*"
-3) "socket.io#/#" (a broadcast to all sockets or to a list of rooms)
-4)
-
-1) "pmessage"
-2) "*"
-3) "socket.io#/#room21#" (a broadcast to a single room)
-4)
-```
-
-Note: **no data** is stored in Redis itself
-
-There are 3 Redis subscriptions per namespace:
-
-- main channel: `##*` (glob)
-- request channel: `-request##`
-- response channel: `-response##`
-
-The request and response channels are used in the additional methods exposed by the Redis adapter, like [RedisAdapter#allRooms()](#redisadapterallrooms).
-
-
-## API
-
-### adapter(pubClient, subClient[, opts])
-
-The following options are allowed:
-
-- `key`: the name of the key to pub/sub events on as prefix (`socket.io`)
-- `requestsTimeout`: optional, after this timeout the adapter will stop waiting from responses to request (`5000ms`)
-- `parser`: optional, parser to use for encoding and decoding messages passed through Redis ([`notepack.io`](https://www.npmjs.com/package/notepack.io))
-
-### RedisAdapter
-
-The redis adapter instances expose the following properties
-that a regular `Adapter` does not
-
-- `uid`
-- `prefix`
-- `pubClient`
-- `subClient`
-- `requestsTimeout`
-- `parser`
-
-### RedisAdapter#allRooms()
-
-Returns the list of all rooms.
-
-```js
-const rooms = await io.of('/').adapter.allRooms();
-console.log(rooms); // a Set containing all rooms (across every node)
-```
-
-## With ioredis client
-
-### Cluster example
-
-```js
-const io = require('socket.io')(3000);
-const redisAdapter = require('@socket.io/redis-adapter');
-const Redis = require('ioredis');
-
-const startupNodes = [
- {
- port: 6380,
- host: '127.0.0.1'
- },
- {
- port: 6381,
- host: '127.0.0.1'
- }
-];
-
-const pubClient = new Redis.Cluster(startupNodes);
-const subClient = pubClient.duplicate();
-
-io.adapter(redisAdapter(pubClient, subClient));
-```
-
-### Sentinel Example
-
-```js
-const io = require('socket.io')(3000);
-const redisAdapter = require('@socket.io/redis-adapter');
-const Redis = require('ioredis');
-
-const options = {
- sentinels: [
- { host: 'somehost1', port: 26379 },
- { host: 'somehost2', port: 26379 }
- ],
- name: 'master01'
-};
-
-const pubClient = new Redis(options);
-const subClient = pubClient.duplicate();
-
-io.adapter(redisAdapter(pubClient, subClient));
-```
-
-## Protocol
-
-The `@socket.io/redis-adapter` adapter broadcasts and receives messages on particularly named Redis channels. For global broadcasts the channel name is:
-```
-prefix + '#' + namespace + '#'
-```
-
-In broadcasting to a single room the channel name is:
-```
-prefix + '#' + namespace + '#' + room + '#'
-```
-
-
-- `prefix`: The base channel name. Default value is `socket.io`. Changed by setting `opts.key` in `adapter(opts)` constructor
-- `namespace`: See https://github.com/socketio/socket.io#namespace.
-- `room` : Used if targeting a specific room.
-
-A number of other libraries adopt this protocol including:
-
-- [socket.io-redis-emitter](https://github.com/socketio/socket.io-redis-emitter)
-- [socket.io-python-emitter](https://github.com/GameXG/socket.io-python-emitter)
-- [socket.io-emitter-go](https://github.com/stackcats/socket.io-emitter-go)
-
-## Migrating from `socket.io-redis`
+## Options
-The package was renamed from `socket.io-redis` to `@socket.io/redis-adapter` in [v7](https://github.com/socketio/socket.io-redis-adapter/releases/tag/7.0.0), in order to match the name of the Redis emitter (`@socket.io/redis-emitter`).
+### Default adapter
-To migrate to the new package, you'll need to make sure to provide your own Redis clients, as the package will no longer create Redis clients on behalf of the user.
+| Name | Description | Default value |
+|------------------------------------|-------------------------------------------------------------------------------|---------------|
+| `key` | The prefix for the Redis Pub/Sub channels. | `socket.io` |
+| `requestsTimeout` | After this timeout the adapter will stop waiting from responses to request. | `5_000` |
+| `publishOnSpecificResponseChannel` | Whether to publish a response to the channel specific to the requesting node. | `false` |
+| `parser` | The parser to use for encoding and decoding messages sent to Redis. | `-` |
-Before:
-
-```js
-const redisAdapter = require("socket.io-redis");
-
-io.adapter(redisAdapter({ host: "localhost", port: 6379 }));
-```
-
-After:
-
-```js
-const { createClient } = require("redis");
-const { createAdapter } = require("@socket.io/redis-adapter");
-
-const pubClient = createClient({ host: "localhost", port: 6379 });
-const subClient = pubClient.duplicate();
-
-io.adapter(createAdapter(pubClient, subClient));
-```
+### Sharded adapter
-Please note that the communication protocol between the Socket.IO servers has not been updated, so you can have some servers with `socket.io-redis` and some others with `@socket.io/redis-adapter` at the same time.
+| Name | Description | Default value |
+|--------------------|-----------------------------------------------------------------------------------------|---------------|
+| `channelPrefix` | The prefix for the Redis Pub/Sub channels. | `socket.io` |
+| `subscriptionMode` | The subscription mode impacts the number of Redis Pub/Sub channels used by the adapter. | `dynamic` |
## License
-MIT
+[MIT](LICENSE)
diff --git a/assets/adapter.excalidraw b/assets/adapter.excalidraw
new file mode 100644
index 0000000..42c349c
--- /dev/null
+++ b/assets/adapter.excalidraw
@@ -0,0 +1,1544 @@
+{
+ "type": "excalidraw",
+ "version": 2,
+ "source": "https://excalidraw.com",
+ "elements": [
+ {
+ "type": "text",
+ "version": 184,
+ "versionNonce": 1779359888,
+ "isDeleted": false,
+ "id": "5hUB5ALUlsn26W0PzU4fM",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 531,
+ "y": -120.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 63.95000076293945,
+ "height": 25,
+ "seed": 28708370,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710340018665,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "socket",
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": null,
+ "originalText": "socket",
+ "lineHeight": 1.25
+ },
+ {
+ "type": "rectangle",
+ "version": 199,
+ "versionNonce": 1125439632,
+ "isDeleted": false,
+ "id": "lmQ4o4New7xuXQLwavuSn",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 461,
+ "y": -204,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 277,
+ "height": 311,
+ "seed": 1594950354,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [
+ {
+ "type": "arrow",
+ "id": "_wBO22vaQplcoKyBXbWRC"
+ },
+ {
+ "type": "arrow",
+ "id": "BZVwnsrGk9G-X87ZHkh-6"
+ }
+ ],
+ "updated": 1710339987249,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 112,
+ "versionNonce": 725168240,
+ "isDeleted": false,
+ "id": "ZQsZmj4NaTubBHMkVG2dl",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 480,
+ "y": -193,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 84.81666564941406,
+ "height": 26,
+ "seed": 126533902,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710340018665,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Server A",
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Server A",
+ "lineHeight": 1.3
+ },
+ {
+ "type": "arrow",
+ "version": 166,
+ "versionNonce": 1893470352,
+ "isDeleted": false,
+ "id": "ABQydsvmkN5ptLyYQaUA3",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 474.8983868047594,
+ "y": -102.13129275811838,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 251.33111393617446,
+ "height": 0.7613046474941143,
+ "seed": 1466702734,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -251.33111393617446,
+ -0.7613046474941143
+ ]
+ ]
+ },
+ {
+ "type": "rectangle",
+ "version": 241,
+ "versionNonce": 975928976,
+ "isDeleted": false,
+ "id": "x54ljUV2PW8AfubZ6fiVJ",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 73,
+ "y": -132,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 129,
+ "height": 56,
+ "seed": 486293390,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "arrow",
+ "version": 182,
+ "versionNonce": 1130179728,
+ "isDeleted": false,
+ "id": "zdzgdf3hgOYX0SgjEtyIZ",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 474.24341762810946,
+ "y": -36.807185424128534,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 247.23231148719788,
+ "height": 2.2114410393964476,
+ "seed": 1674715794,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -247.23231148719788,
+ 2.2114410393964476
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 216,
+ "versionNonce": 612650640,
+ "isDeleted": false,
+ "id": "dXknKeuYe3X3K-0Hw9P95",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 121,
+ "y": -115,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 40.983333587646484,
+ "height": 20,
+ "seed": 1858283854,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710340018665,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 1,
+ "text": "client",
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "client",
+ "lineHeight": 1.25
+ },
+ {
+ "type": "rectangle",
+ "version": 278,
+ "versionNonce": 1162299536,
+ "isDeleted": false,
+ "id": "Ce1Lw4MMOtiunstd3FPJv",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 74.5,
+ "y": -57,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 129,
+ "height": 56,
+ "seed": 568384654,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 282,
+ "versionNonce": 261726832,
+ "isDeleted": false,
+ "id": "rcCUGk-XM0jKzcGaeO0iS",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 121.5,
+ "y": -40,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 40.983333587646484,
+ "height": 20,
+ "seed": 244546386,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710340018665,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 1,
+ "text": "client",
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "client",
+ "lineHeight": 1.25
+ },
+ {
+ "type": "rectangle",
+ "version": 284,
+ "versionNonce": 1884972176,
+ "isDeleted": false,
+ "id": "4iido5zQ7QhoIfnOzWp3h",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 74.5,
+ "y": 18,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 129,
+ "height": 56,
+ "seed": 1055485070,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 276,
+ "versionNonce": 169606288,
+ "isDeleted": false,
+ "id": "D1E2DkimaDb8hGxIfXKmq",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 121.5,
+ "y": 35,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 40.983333587646484,
+ "height": 20,
+ "seed": 270265170,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710340018665,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 1,
+ "text": "client",
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "client",
+ "lineHeight": 1.25
+ },
+ {
+ "type": "rectangle",
+ "version": 237,
+ "versionNonce": 1314291856,
+ "isDeleted": false,
+ "id": "RRrk3Vsl-pM8Z1r8Fj3Vu",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 497.5,
+ "y": -132,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 129,
+ "height": 56,
+ "seed": 1013161166,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 244,
+ "versionNonce": 857461872,
+ "isDeleted": false,
+ "id": "8pCtm42TpakWdZ7WNS4VN",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 530,
+ "y": -50.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 63.95000076293945,
+ "height": 25,
+ "seed": 684338382,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710340018665,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "socket",
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": null,
+ "originalText": "socket",
+ "lineHeight": 1.25
+ },
+ {
+ "type": "rectangle",
+ "version": 295,
+ "versionNonce": 2029755536,
+ "isDeleted": false,
+ "id": "thsI1AfZ_VshmC8wdQoT_",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 496.5,
+ "y": -62,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 129,
+ "height": 56,
+ "seed": 1104563986,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 243,
+ "versionNonce": 2089675408,
+ "isDeleted": false,
+ "id": "dfFxeVTIg6OH8ny7WuBsb",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 527,
+ "y": 26.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 63.95000076293945,
+ "height": 25,
+ "seed": 1000469902,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710340018665,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "socket",
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": null,
+ "originalText": "socket",
+ "lineHeight": 1.25
+ },
+ {
+ "type": "rectangle",
+ "version": 296,
+ "versionNonce": 436844688,
+ "isDeleted": false,
+ "id": "Ejm4QTgpRy-0064kg5DDC",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 493.5,
+ "y": 15,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 129,
+ "height": 56,
+ "seed": 1070363218,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "arrow",
+ "version": 217,
+ "versionNonce": 1063528080,
+ "isDeleted": false,
+ "id": "yn0_EJ_FjGmr2PHYTCPsC",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 474.61615574359894,
+ "y": 42.89427948030175,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 247.23231148719788,
+ "height": 2.2114410393964476,
+ "seed": 1559186084,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -247.23231148719788,
+ 2.2114410393964476
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 193,
+ "versionNonce": 1267189360,
+ "isDeleted": false,
+ "id": "2KQuRzgUL-iSoMHZQ9zbS",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 529.5,
+ "y": 282,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 63.95000076293945,
+ "height": 25,
+ "seed": 1479277478,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710340018665,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "socket",
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": null,
+ "originalText": "socket",
+ "lineHeight": 1.25
+ },
+ {
+ "type": "rectangle",
+ "version": 223,
+ "versionNonce": 324518544,
+ "isDeleted": false,
+ "id": "dJhDWOnAJOszWt_UNEXdt",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 459.5,
+ "y": 198.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 277,
+ "height": 280,
+ "seed": 224360890,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [
+ {
+ "type": "arrow",
+ "id": "qmYaJfZ9NO1RK7YHGQGo6"
+ },
+ {
+ "type": "arrow",
+ "id": "x_nMpLlFEV43XGOAM6Gxj"
+ }
+ ],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 130,
+ "versionNonce": 1720850576,
+ "isDeleted": false,
+ "id": "lyh4RgaTTCZNLUjl519k9",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 478.5,
+ "y": 209.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 86.23332977294922,
+ "height": 26,
+ "seed": 364484326,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710340018665,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Server B",
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Server B",
+ "lineHeight": 1.3
+ },
+ {
+ "type": "arrow",
+ "version": 204,
+ "versionNonce": 1532623504,
+ "isDeleted": false,
+ "id": "x7ujWlTTvv0aN7XIFTWjr",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 479.3983868047594,
+ "y": 301.3687072418816,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 251.33111393617446,
+ "height": 0.7613046474941143,
+ "seed": 1836855930,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -251.33111393617446,
+ -0.7613046474941143
+ ]
+ ]
+ },
+ {
+ "type": "rectangle",
+ "version": 211,
+ "versionNonce": 828123280,
+ "isDeleted": false,
+ "id": "cqdTPTcZefvtqeNEAMTBe",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 77.5,
+ "y": 271.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 129,
+ "height": 56,
+ "seed": 1567738406,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "arrow",
+ "version": 220,
+ "versionNonce": 585668240,
+ "isDeleted": false,
+ "id": "59kripFevaDD2Mo2bkYk-",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 478.74341762810946,
+ "y": 366.69281457587147,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 247.23231148719788,
+ "height": 2.2114410393964476,
+ "seed": 1124324154,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -247.23231148719788,
+ 2.2114410393964476
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 189,
+ "versionNonce": 363165808,
+ "isDeleted": false,
+ "id": "U0x2FIFxg4BZgOIK6sVnW",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 125.5,
+ "y": 288.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 40.983333587646484,
+ "height": 20,
+ "seed": 1044485478,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710340018665,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 1,
+ "text": "client",
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "client",
+ "lineHeight": 1.25
+ },
+ {
+ "type": "rectangle",
+ "version": 231,
+ "versionNonce": 1417687696,
+ "isDeleted": false,
+ "id": "NU9potS0F6f8sxY5IT0Lt",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 79,
+ "y": 346.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 129,
+ "height": 56,
+ "seed": 1884904442,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 210,
+ "versionNonce": 467699344,
+ "isDeleted": false,
+ "id": "IpJJ20xja0yqXQC_netfw",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 126,
+ "y": 363.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 40.983333587646484,
+ "height": 20,
+ "seed": 1635121318,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710340018665,
+ "link": null,
+ "locked": false,
+ "fontSize": 16,
+ "fontFamily": 1,
+ "text": "client",
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "client",
+ "lineHeight": 1.25
+ },
+ {
+ "type": "rectangle",
+ "version": 250,
+ "versionNonce": 1105251984,
+ "isDeleted": false,
+ "id": "scSxnujNYgELyMUDbnTNS",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 496,
+ "y": 270.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 129,
+ "height": 56,
+ "seed": 303703418,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 234,
+ "versionNonce": 1966926448,
+ "isDeleted": false,
+ "id": "Lyv2NwV0SfYm5kvp9sJEn",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 528.5,
+ "y": 352,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 63.95000076293945,
+ "height": 25,
+ "seed": 1344309030,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710340018666,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "socket",
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": null,
+ "originalText": "socket",
+ "lineHeight": 1.25
+ },
+ {
+ "type": "rectangle",
+ "version": 291,
+ "versionNonce": 1288333968,
+ "isDeleted": false,
+ "id": "e3D2rl_rbVQwQUKshOG8E",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 495,
+ "y": 340.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 129,
+ "height": 56,
+ "seed": 627795514,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "diamond",
+ "version": 272,
+ "versionNonce": 2067699856,
+ "isDeleted": false,
+ "id": "k0pJTVL4F3HHsfRPlE-gO",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 786,
+ "y": -58,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 46,
+ "height": 46,
+ "seed": 1260350118,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [
+ {
+ "type": "arrow",
+ "id": "Sp9AvxDh8gwRvSC53VFKe"
+ }
+ ],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 285,
+ "versionNonce": 1550342288,
+ "isDeleted": false,
+ "id": "DiLMkDsU2SrPef3STL9fw",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dashed",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 765.5583343505859,
+ "y": -97.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 139.88333129882812,
+ "height": 26,
+ "seed": 1810644198,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710339990684,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Redis adapter",
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Redis adapter",
+ "lineHeight": 1.3
+ },
+ {
+ "type": "arrow",
+ "version": 57,
+ "versionNonce": 1020103280,
+ "isDeleted": false,
+ "id": "Sp9AvxDh8gwRvSC53VFKe",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 766,
+ "y": -35,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 109,
+ "height": 1,
+ "seed": 714162918,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1710339987276,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "k0pJTVL4F3HHsfRPlE-gO",
+ "focus": -0.01715197447147986,
+ "gap": 14.142135623730947
+ },
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -109,
+ -1
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 73,
+ "versionNonce": 1637006448,
+ "isDeleted": false,
+ "id": "_wBO22vaQplcoKyBXbWRC",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 763,
+ "y": -41,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 105,
+ "height": 57,
+ "seed": 1243541542,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1710339987276,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "lmQ4o4New7xuXQLwavuSn",
+ "focus": 0.35224176368590543,
+ "gap": 25
+ },
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -105,
+ -57
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 88,
+ "versionNonce": 1524739696,
+ "isDeleted": false,
+ "id": "BZVwnsrGk9G-X87ZHkh-6",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 765,
+ "y": -28,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 95,
+ "height": 62,
+ "seed": 1890534970,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1710339987276,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "lmQ4o4New7xuXQLwavuSn",
+ "focus": -0.522635330379503,
+ "gap": 27
+ },
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -95,
+ 62
+ ]
+ ]
+ },
+ {
+ "type": "diamond",
+ "version": 378,
+ "versionNonce": 1094118544,
+ "isDeleted": false,
+ "id": "vJwd2LS9grrvUFlbCugEG",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 786.25,
+ "y": 343,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 46,
+ "height": 46,
+ "seed": 1072510330,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [
+ {
+ "type": "arrow",
+ "id": "x_nMpLlFEV43XGOAM6Gxj"
+ }
+ ],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "arrow",
+ "version": 277,
+ "versionNonce": 1904369776,
+ "isDeleted": false,
+ "id": "x_nMpLlFEV43XGOAM6Gxj",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 760.25,
+ "y": 365,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 109,
+ "height": 1,
+ "seed": 1180464698,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1710339987277,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "dJhDWOnAJOszWt_UNEXdt",
+ "focus": -0.17704646556482773,
+ "gap": 23.75
+ },
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -109,
+ -1
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 268,
+ "versionNonce": 356307568,
+ "isDeleted": false,
+ "id": "qmYaJfZ9NO1RK7YHGQGo6",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 756.9214748277186,
+ "y": 355.7229508196721,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 104.67147482771861,
+ "height": 53.72295081967212,
+ "seed": 880321126,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1710339987277,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "dJhDWOnAJOszWt_UNEXdt",
+ "focus": 0.304824173970933,
+ "gap": 20.421474827718612
+ },
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -104.67147482771861,
+ -53.72295081967212
+ ]
+ ]
+ },
+ {
+ "type": "ellipse",
+ "version": 135,
+ "versionNonce": 2074179216,
+ "isDeleted": false,
+ "id": "EQmjbilyrf3OcSwGbMZrg",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 841,
+ "y": 94,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 150.00000000000003,
+ "height": 93.00000000000001,
+ "seed": 1885795942,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [
+ {
+ "type": "arrow",
+ "id": "xDobZ6graJnZZP8g59wJ4"
+ },
+ {
+ "type": "arrow",
+ "id": "eU1gfEXnHSjxc-pEgv43A"
+ }
+ ],
+ "updated": 1710339987250,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 64,
+ "versionNonce": 857393296,
+ "isDeleted": false,
+ "id": "wV6Y3XyIP5TbX50EF6xs6",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 892.0666675567627,
+ "y": 129.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 51.13333511352539,
+ "height": 26,
+ "seed": 1433614630,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [],
+ "updated": 1710339993804,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Redis",
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": null,
+ "originalText": "Redis",
+ "lineHeight": 1.3
+ },
+ {
+ "type": "arrow",
+ "version": 132,
+ "versionNonce": 2094597232,
+ "isDeleted": false,
+ "id": "eU1gfEXnHSjxc-pEgv43A",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 831,
+ "y": -9,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 47.87517320626739,
+ "height": 85.84605939285643,
+ "seed": 1145880934,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1710339987278,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": {
+ "elementId": "EQmjbilyrf3OcSwGbMZrg",
+ "focus": -0.02048842961361912,
+ "gap": 22.17434859207502
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 47.87517320626739,
+ 85.84605939285643
+ ]
+ ]
+ },
+ {
+ "type": "arrow",
+ "version": 197,
+ "versionNonce": 1554695312,
+ "isDeleted": false,
+ "id": "xDobZ6graJnZZP8g59wJ4",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 885.1947399534047,
+ "y": 201.03676246231026,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 29.812198779418623,
+ "height": 92.55583013028235,
+ "seed": 1443544058,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": {
+ "type": 2
+ },
+ "boundElements": [],
+ "updated": 1710339997274,
+ "link": null,
+ "locked": false,
+ "startBinding": {
+ "elementId": "EQmjbilyrf3OcSwGbMZrg",
+ "focus": 0.14442451001935527,
+ "gap": 17.685613369250504
+ },
+ "endBinding": {
+ "elementId": "g_nwmfFr4gmfrn6naI6-1",
+ "focus": 0.08950820540708966,
+ "gap": 14.15740740740739
+ },
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -29.812198779418623,
+ 92.55583013028235
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 339,
+ "versionNonce": 1211082896,
+ "isDeleted": false,
+ "id": "g_nwmfFr4gmfrn6naI6-1",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dashed",
+ "roughness": 0,
+ "opacity": 100,
+ "angle": 0,
+ "x": 770.0583343505859,
+ "y": 307.75,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 139.88333129882812,
+ "height": 26,
+ "seed": 1287799958,
+ "groupIds": [],
+ "frameId": null,
+ "roundness": null,
+ "boundElements": [
+ {
+ "type": "arrow",
+ "id": "xDobZ6graJnZZP8g59wJ4"
+ }
+ ],
+ "updated": 1710339997271,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Redis adapter",
+ "textAlign": "center",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Redis adapter",
+ "lineHeight": 1.3
+ }
+ ],
+ "appState": {
+ "gridSize": null,
+ "viewBackgroundColor": "#ffffff"
+ },
+ "files": {}
+}
\ No newline at end of file
diff --git a/assets/adapter.png b/assets/adapter.png
new file mode 100644
index 0000000..6bc0ce5
Binary files /dev/null and b/assets/adapter.png differ
diff --git a/assets/adapter_dark.png b/assets/adapter_dark.png
new file mode 100644
index 0000000..e8ea796
Binary files /dev/null and b/assets/adapter_dark.png differ