Adding upstream version 1.9.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
2bf0435a35
commit
031879240c
356 changed files with 26924 additions and 0 deletions
9
.bumpversion.cfg
Normal file
9
.bumpversion.cfg
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[bumpversion]
|
||||||
|
current_version = 1.9.1
|
||||||
|
commit = True
|
||||||
|
tag = True
|
||||||
|
|
||||||
|
[bumpversion:file:iredis/__init__.py]
|
||||||
|
|
||||||
|
[bumpversion:file:pyproject.toml]
|
||||||
|
|
5
.flake8
Normal file
5
.flake8
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[flake8]
|
||||||
|
ignore = D203,W503,W605,C901,E203
|
||||||
|
exclude = .git,__pycache__,build,dist,venv
|
||||||
|
max-complexity = 14
|
||||||
|
max-line-length = 120
|
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
patreon: laixintao
|
||||||
|
custom: ["https://www.kawabangga.com/sponsor", "http://paypal.me/laixintao"]
|
152
.github/workflows/release.yaml
vendored
Normal file
152
.github/workflows/release.yaml
vendored
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-pypi:
|
||||||
|
name: release-pypi
|
||||||
|
runs-on: ubuntu-16.04
|
||||||
|
|
||||||
|
# FIXME
|
||||||
|
# help test shouldn't depends on this to run
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:5
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
|
options: --entrypoint redis-server
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: 3.7
|
||||||
|
architecture: 'x64'
|
||||||
|
- name: Cache venv
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: venv
|
||||||
|
# Look to see if there is a cache hit for the corresponding requirements file
|
||||||
|
key: ubuntu-16.04-poetryenv-${{ hashFiles('poetry.lock') }}
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
python3 -m venv venv
|
||||||
|
. venv/bin/activate
|
||||||
|
pip install -U pip
|
||||||
|
pip install poetry
|
||||||
|
poetry install
|
||||||
|
python -c "import sys; print(sys.version)"
|
||||||
|
pip list
|
||||||
|
- name: Poetry Build
|
||||||
|
run: |
|
||||||
|
. venv/bin/activate
|
||||||
|
poetry build
|
||||||
|
- name: Test Build
|
||||||
|
run: |
|
||||||
|
python3 -m venv fresh_env
|
||||||
|
. fresh_env/bin/activate
|
||||||
|
pip install dist/*.whl
|
||||||
|
|
||||||
|
iredis -h
|
||||||
|
iredis help GET
|
||||||
|
|
||||||
|
- name: Upload to Pypi
|
||||||
|
env:
|
||||||
|
PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
||||||
|
run: |
|
||||||
|
. venv/bin/activate
|
||||||
|
poetry publish --username __token__ --password ${PASSWORD}
|
||||||
|
|
||||||
|
release-binary:
|
||||||
|
name: Release Executable Binary.
|
||||||
|
runs-on: ubuntu-16.04
|
||||||
|
|
||||||
|
# FIXME
|
||||||
|
# help test shouldn't depends on this to run
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
|
options: --entrypoint redis-server
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: 3.7
|
||||||
|
architecture: 'x64'
|
||||||
|
- name: Cache venv
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: venv
|
||||||
|
# Look to see if there is a cache hit for the corresponding requirements file
|
||||||
|
key: ubuntu-16.04-poetryenv-${{ hashFiles('poetry.lock') }}
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
python3 -m venv venv
|
||||||
|
. venv/bin/activate
|
||||||
|
pip install -U pip
|
||||||
|
pip install poetry
|
||||||
|
poetry install
|
||||||
|
python -c "import sys; print(sys.version)"
|
||||||
|
pip list
|
||||||
|
- name: Poetry Build
|
||||||
|
run: |
|
||||||
|
. venv/bin/activate
|
||||||
|
poetry build
|
||||||
|
- name: Test Build
|
||||||
|
run: |
|
||||||
|
python3 -m venv fresh_env
|
||||||
|
. fresh_env/bin/activate
|
||||||
|
pip install dist/*.whl
|
||||||
|
|
||||||
|
iredis -h
|
||||||
|
iredis help GET
|
||||||
|
|
||||||
|
- name: Cache cargo registry
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.cargo/registry
|
||||||
|
key: ${{ runner.os }}-cargo-registry
|
||||||
|
|
||||||
|
- name: Executable Build
|
||||||
|
run: |
|
||||||
|
# pyoxidizer doesn't know the wheel path, and it doesn't support passing env vars
|
||||||
|
export WHEEL_PATH=`ls ./dist/iredis*.whl`
|
||||||
|
envsubst '$WHEEL_PATH' < pyoxidizer.template.bzl > pyoxidizer.bzl
|
||||||
|
cargo install pyoxidizer --vers 0.6.0
|
||||||
|
pyoxidizer build --release install
|
||||||
|
cd ./build/x86*/release/install
|
||||||
|
tar -zcf ../../../iredis.tar.gz lib/ iredis
|
||||||
|
cd -
|
||||||
|
|
||||||
|
- name: Test Executable
|
||||||
|
run: |
|
||||||
|
./build/x86*/release/install/iredis -h
|
||||||
|
./build/x86*/release/install/iredis help GET
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
id: create_release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.ref }}
|
||||||
|
release_name: Release ${{ github.ref }}
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
|
- name: Upload Release Asset
|
||||||
|
id: upload-release-asset
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./build/iredis.tar.gz
|
||||||
|
asset_name: iredis.tar.gz
|
||||||
|
asset_content_type: application/gzip
|
81
.github/workflows/test.yaml
vendored
Normal file
81
.github/workflows/test.yaml
vendored
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
name: Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Pytest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-16.04]
|
||||||
|
python: ['3.6', '3.7', '3.8']
|
||||||
|
redis: [5, 6]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:${{ matrix.redis }}
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
|
options: --entrypoint redis-server
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python }}
|
||||||
|
architecture: 'x64'
|
||||||
|
- name: Cache venv
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: venv
|
||||||
|
# Look to see if there is a cache hit for the corresponding requirements file
|
||||||
|
key: ${{ matrix.os }}-poetryenv-${{ hashFiles('poetry.lock') }}
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
python3 -m venv venv
|
||||||
|
. venv/bin/activate
|
||||||
|
pip install -U pip
|
||||||
|
pip install poetry
|
||||||
|
poetry install
|
||||||
|
python -c "import sys; print(sys.version)"
|
||||||
|
pip list
|
||||||
|
- name: Pytest
|
||||||
|
env:
|
||||||
|
REDIS_VERSION: ${{ matrix.redis }}
|
||||||
|
run: |
|
||||||
|
. venv/bin/activate
|
||||||
|
pytest
|
||||||
|
lint:
|
||||||
|
name: flake8 & black
|
||||||
|
runs-on: ubuntu-16.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: 3.7
|
||||||
|
architecture: 'x64'
|
||||||
|
- name: Cache venv
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: venv
|
||||||
|
# Look to see if there is a cache hit for the corresponding requirements file
|
||||||
|
key: lintenv-v2
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
python3 -m venv venv
|
||||||
|
. venv/bin/activate
|
||||||
|
pip install -U pip flake8 black
|
||||||
|
- name: Flake8 test
|
||||||
|
run: |
|
||||||
|
. venv/bin/activate
|
||||||
|
flake8 .
|
||||||
|
- name: Black test
|
||||||
|
run: |
|
||||||
|
. venv/bin/activate
|
||||||
|
black --check .
|
108
.gitignore
vendored
Normal file
108
.gitignore
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# celery beat schedule file
|
||||||
|
celerybeat-schedule
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
*.aof
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode
|
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[submodule "iredis/data/redis-doc"]
|
||||||
|
path = iredis/data/redis-doc
|
||||||
|
url = https://github.com/antirez/redis-doc.git
|
||||||
|
[submodule "redis-doc"]
|
||||||
|
path = redis-doc
|
||||||
|
url = https://github.com/antirez/redis-doc.git
|
218
CHANGELOG.md
Normal file
218
CHANGELOG.md
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
### 1.9.1
|
||||||
|
|
||||||
|
- Feature: support auto-reissue command to another Redis server, when got a
|
||||||
|
"MOVED" error in redis cluster.
|
||||||
|
|
||||||
|
## 1.9
|
||||||
|
|
||||||
|
- Feature: Support `LPOS` command.
|
||||||
|
- Doc: Update docs in `HELP` command update to date.
|
||||||
|
|
||||||
|
## 1.8
|
||||||
|
|
||||||
|
- Feature: Fully support Redis6!
|
||||||
|
- Support `STRALGO` command.
|
||||||
|
- `MIGRATE` command now support `AUTH2`.
|
||||||
|
- DISABLE `hello` command, IRedis not support RESP3.
|
||||||
|
|
||||||
|
### 1.7.4
|
||||||
|
|
||||||
|
- Bugfix: Lock wcwidth's version on `1.9.0`. Fix binary build.
|
||||||
|
|
||||||
|
### 1.7.3
|
||||||
|
|
||||||
|
- Bugfix: IRedis can be suspended by <kbd>Ctrl</kbd> + <kbd>Z</kbd>. (Thanks
|
||||||
|
[wooden-robot])
|
||||||
|
- Bugfix: Press <kbd>Enter</kbd> when completion is open will not execute
|
||||||
|
commands. (Thanks [wooden-robot])
|
||||||
|
- Feature: `AUTH` command is now compatible with both Redis 5 and Redis 6.
|
||||||
|
- Redis6 support: `CLIENT KILL` support kill by `USER`, `XINFO` command support
|
||||||
|
`FULL` option.
|
||||||
|
|
||||||
|
### 1.7.2
|
||||||
|
|
||||||
|
- Feature: Support `ACL` ( [#340](https://github.com/laixintao/iredis/pull/343)
|
||||||
|
).
|
||||||
|
- Bugfix: Include tests in source distribution.
|
||||||
|
|
||||||
|
### 1.7.1
|
||||||
|
|
||||||
|
- Bugfix: `command in` considered as an invalid input case, due to matched with
|
||||||
|
`command`'s syntax, and `in` as an extra args. Fixed by falling back to
|
||||||
|
default grammar if there are ambiguous commands that can match.
|
||||||
|
|
||||||
|
## 1.7
|
||||||
|
|
||||||
|
- Update: Builtin doc was updated with latest
|
||||||
|
redis-doc(dd4159397f115d53423c21337eedb04d3258d291).
|
||||||
|
- Feature: New command support: `CLIENT GETREDIR`, `CLIENT TRACKING` and
|
||||||
|
- Test: IRedis now was tested in both Redis 5 and Redis 6.
|
||||||
|
- Bugfix: Fix exception when transaction fails. (Thanks [brianmaissy])
|
||||||
|
- Bugfix: Merging multiple spaces bug, e.g. `set foo "hello world"` will result
|
||||||
|
in sending `set foo "hello world"` to redis-server. `CLIENT CACHING`.
|
||||||
|
- Bugfix: `--url` options is ignored, but don't worry, it is fixed now by
|
||||||
|
[otms61].
|
||||||
|
|
||||||
|
### 1.6.2
|
||||||
|
|
||||||
|
- Bugfix: `INFO` command accepts `section` now.
|
||||||
|
- Bugfix: refused to start when can not create connection.
|
||||||
|
|
||||||
|
### 1.6.1
|
||||||
|
|
||||||
|
- Bugfix: Dangerous command will still run even user canceled.
|
||||||
|
|
||||||
|
## 1.6
|
||||||
|
|
||||||
|
- Feature: support pager. You can disable it using `--no-pager` or in your
|
||||||
|
`iredisrc`, or change the pager behavior by setting `pager` in `iredisrc`.
|
||||||
|
|
||||||
|
## 1.5
|
||||||
|
|
||||||
|
- Bugfix: PEEK command do not use MEMORY USAGE before redis version 4.0.
|
||||||
|
- Feature: Support disable shell pipeline feature in iredisrc. (Thanks
|
||||||
|
[wooden-robot])
|
||||||
|
|
||||||
|
### 1.4.3
|
||||||
|
|
||||||
|
- Support `LOLWUT` command of Redis 6 version.
|
||||||
|
|
||||||
|
### 1.4.2
|
||||||
|
|
||||||
|
- Password for `AUTH` command will be hidden as `*`.
|
||||||
|
|
||||||
|
### 1.4.1
|
||||||
|
|
||||||
|
- This is a test release, nothing new.
|
||||||
|
|
||||||
|
## 1.4.0
|
||||||
|
|
||||||
|
- Bugfix: Fix PyOxidizer binary build, by locking the importlib_resources
|
||||||
|
version.
|
||||||
|
|
||||||
|
### 1.3.1
|
||||||
|
|
||||||
|
- Bugfix: Fix PyOxidizer binary build.
|
||||||
|
- Feature: Completer for HELP command.
|
||||||
|
- Bugfix: Lowercase for `--newbie` mode.
|
||||||
|
- Bugfix: Bottom hint for IRedis builtin commands.
|
||||||
|
|
||||||
|
## 1.3.0
|
||||||
|
|
||||||
|
- Catch up with redis-doc: d19fb20..6927ef0:
|
||||||
|
- `SET` command support `KEEPTTL` option.
|
||||||
|
- `LPUSHX` accepts multiple elements.
|
||||||
|
- Add commands support for:
|
||||||
|
- CLUSTER BUMPEPOCH
|
||||||
|
- CLUSTER FLUSHSLOTS
|
||||||
|
- CLUSTER MYID
|
||||||
|
- MODULE LIST
|
||||||
|
- MODULE LOAD
|
||||||
|
- MODULE UNLOAD
|
||||||
|
- PSYNC
|
||||||
|
- LATENCY DOCTOR
|
||||||
|
- LATENCY GRAPH
|
||||||
|
- LATENCY HISTORY
|
||||||
|
- LATENCY LATEST
|
||||||
|
- LATENCY RESET
|
||||||
|
- LATENCY HELP
|
||||||
|
|
||||||
|
## 1.2.0
|
||||||
|
|
||||||
|
- Feature: Peek command now displays more friendly, before each "info" will take
|
||||||
|
one line, now type/encoding/ttl/memory usage will display in one line, makes
|
||||||
|
the result looks more clear.
|
||||||
|
- Support DSN. (Thanks [lyqscmy]).
|
||||||
|
- Support URL.
|
||||||
|
- Support socket connection.
|
||||||
|
|
||||||
|
### 1.1.2
|
||||||
|
|
||||||
|
- Feature: support history location config.
|
||||||
|
|
||||||
|
### 1.1.1
|
||||||
|
|
||||||
|
- This release is for testing the binary build, nothing else changed.
|
||||||
|
|
||||||
|
## 1.1
|
||||||
|
|
||||||
|
- Feature: Package into a single binary with PyOxidizer (thanks [Mac Chaffee])
|
||||||
|
|
||||||
|
### 1.0.5
|
||||||
|
|
||||||
|
- Feature: <kbd>Ctrl - X</kbd> then <kbd>Ctrl -E</kbd> to open an editor to edit
|
||||||
|
command.
|
||||||
|
- Feature: Support `completion_casing` config.
|
||||||
|
|
||||||
|
### 1.0.4
|
||||||
|
|
||||||
|
- Bugfix: command completions when a command is substring of another command.
|
||||||
|
[issue#198](https://github.com/laixintao/iredis/issues/198)
|
||||||
|
|
||||||
|
### 1.0.3
|
||||||
|
|
||||||
|
- Feature: Support `bitfield` command, and a new completer for int type.
|
||||||
|
|
||||||
|
### 1.0.2
|
||||||
|
|
||||||
|
- Internal: Migrate CI from travis and circleci to github action.
|
||||||
|
|
||||||
|
### 1.0.1
|
||||||
|
|
||||||
|
- Bugfix: Fix info command decode error on
|
||||||
|
decode=utf-8 #[266](https://github.com/laixintao/iredis/pull/266)
|
||||||
|
|
||||||
|
# 1.0
|
||||||
|
|
||||||
|
- Feature: Support `EXIT` to exit iredis REPL.
|
||||||
|
- Feature: Support `CLEAR` to clear screen.
|
||||||
|
- Feature: Support config log location in iredisrc file, default to None.
|
||||||
|
|
||||||
|
### 0.9.1
|
||||||
|
|
||||||
|
- Feature: Support `PEEK` Command.
|
||||||
|
|
||||||
|
## 0.9
|
||||||
|
|
||||||
|
- Refactor: split completer update and response render; Move cli tests to travis
|
||||||
|
ci. (Thanks: [ruohan.chen])
|
||||||
|
- Support stream commands. _ Timestamp completer support. _ Stream command
|
||||||
|
renders and lexers.
|
||||||
|
- Bugfix: When response is None,
|
||||||
|
`iredis.completers.udpate_completer_for_responase` will raise Exception.
|
||||||
|
|
||||||
|
### 0.8.12
|
||||||
|
|
||||||
|
- Bugfix: Multi spaces between commands can be recongnised as correct commands
|
||||||
|
now.
|
||||||
|
- Feature: Warning on dangerous command.
|
||||||
|
|
||||||
|
### 0.8.11
|
||||||
|
|
||||||
|
- Bugfix: Fix HELP command can not render markdown with a `<h3>` header.
|
||||||
|
- Bugfix: Pipeline using a builtin Python API.
|
||||||
|
|
||||||
|
### 0.8.10
|
||||||
|
|
||||||
|
- Bugfix: previous version of iredis didn't package redis-doc correctly.
|
||||||
|
- Feature: prompt for dangerous commands.
|
||||||
|
|
||||||
|
### 0.8.9
|
||||||
|
|
||||||
|
- Support config files.
|
||||||
|
|
||||||
|
### 0.8.8
|
||||||
|
|
||||||
|
- Bugfix: pipeline in iredis can run shell command include pipes. thanks to
|
||||||
|
[Wooden-Robot].
|
||||||
|
|
||||||
|
### 0.8.7
|
||||||
|
|
||||||
|
- Support connect shell utilities with pipeline
|
||||||
|
|
||||||
|
[wooden-robot]: https://github.com/Wooden-Robot
|
||||||
|
[ruohan.chen]: https://github.com/crhan
|
||||||
|
[mac chaffee]: https://github.com/mac-chaffee
|
||||||
|
[lyqscmy]: https://github.com/lyqscmy
|
||||||
|
[brianmaissy]: https://github.com/brianmaissy
|
||||||
|
[otms61]: https://github.com/otms61
|
18
Dockerfile
Normal file
18
Dockerfile
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
FROM python:3
|
||||||
|
|
||||||
|
WORKDIR /iredis
|
||||||
|
COPY README.md poetry.lock pyproject.toml ./
|
||||||
|
COPY iredis ./iredis
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --allow-unauthenticated \
|
||||||
|
redis-server && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
|
||||||
|
RUN python3 -m venv iredis_env && \
|
||||||
|
. iredis_env/bin/activate && \
|
||||||
|
pip install poetry && \
|
||||||
|
poetry install --no-dev && \
|
||||||
|
rm -rf ~/.cache
|
||||||
|
|
||||||
|
CMD ["sh","-c","redis-server --daemonize yes && . iredis_env/bin/activate && iredis"]
|
10
LICENSE
Normal file
10
LICENSE
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
Copyright (c) 2019, laixintao
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
IRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* IRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
* IRedistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of IRedis nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
228
README.md
Normal file
228
README.md
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
<p align="center">
|
||||||
|
<img width="100" height="100" src="https://raw.githubusercontent.com/laixintao/iredis/master/docs/assets/logo.png" />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 align="center">Interactive Redis: A Cli for Redis with AutoCompletion and Syntax Highlighting.</h4>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/laixintao/iredis/actions"><img src="https://github.com/laixintao/iredis/workflows/Test/badge.svg" alt="Github Action"></a>
|
||||||
|
<a href="https://badge.fury.io/py/iredis"><img src="https://badge.fury.io/py/iredis.svg" alt="PyPI version"></a>
|
||||||
|
<img src="https://badgen.net/badge/python/3.6%20|%203.7%20|%203.8/" alt="Python version">
|
||||||
|
<a href="https://pepy.tech/project/iredis"><img src="https://pepy.tech/badge/iredis" alt="Download stats"></a>
|
||||||
|
<a href="https://t.me/iredis_users"><img src="https://badgen.net/badge/icon/join?icon=telegram&label=usergroup" alt="Chat on telegram"></a>
|
||||||
|
<a href="https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/laixintao/iredis&cloudshell_print=docs/cloudshell/run-in-docker.txt"><img src="https://badgen.net/badge/run/GoogleCloudShell/blue?icon=terminal" alt="Open in Cloud Shell"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="./docs/assets/demo.svg" alt="demo">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
IRedis is a terminal client for redis with auto-completion and syntax
|
||||||
|
highlighting. IRedis lets you type Redis commands smoothly, and displays results
|
||||||
|
in a user-friendly format.
|
||||||
|
|
||||||
|
IRedis is an alternative for redis-cli. In most cases, IRedis behaves exactly
|
||||||
|
the same as redis-cli. Besides, it is safer to use IRedis on production servers
|
||||||
|
than redis-cli: IRedis will prevent accidentally running dangerous commands,
|
||||||
|
like `KEYS *` (see
|
||||||
|
[Redis docs / Latency generated by slow commands](https://redis.io/topics/latency#latency-generated-by-slow-commands)).
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Advanced code completion. If you run command `KEYS` then run `DEL`, IRedis
|
||||||
|
will auto-complete your command based on `KEYS` result.
|
||||||
|
- Command validation. IRedis will validate command while you are typing, and
|
||||||
|
highlight errors. E.g. try `CLUSTER MEET IP PORT`, IRedis will validate IP and
|
||||||
|
PORT for you.
|
||||||
|
- Command highlighting, fully based on redis grammar. Any valid command in
|
||||||
|
IRedis shell is a valid redis command.
|
||||||
|
- Human-friendly result display.
|
||||||
|
- _pipeline_ feature, you can use your favorite shell tools to parse redis'
|
||||||
|
response, like `get json | jq .`.
|
||||||
|
- Support pager for long output.
|
||||||
|
- Support connection via URL, `iredis --url redis://example.com:6379/1`.
|
||||||
|
- Store server configuration: `iredis -d prod-redis` (see [dsn](#using-dsn) for
|
||||||
|
more).
|
||||||
|
- `peek` command to check the key's type then automatically call
|
||||||
|
`get`/`lrange`/`sscan`, etc, depending on types. You don't need to call the
|
||||||
|
`type` command then type another command to get the value. `peek` will also
|
||||||
|
display the key's length and memory usage.
|
||||||
|
- <kbd>Ctrl</kbd> + <kbd>C</kbd> to cancel the current typed command, this won't
|
||||||
|
exit IRedis, exactly like bash behaviour. Use <kbd>Ctrl</kbd> + <kbd>D</kbd>
|
||||||
|
to send a EOF to exit IRedis.
|
||||||
|
- <kbd>Ctrl</kbd> + <kbd>R</kbd> to open **reverse-i-search** to search through
|
||||||
|
your command history.
|
||||||
|
- Auto suggestions. (Like [fish shell](http://fishshell.com/).)
|
||||||
|
- Support `--encode=utf-8`, to decode Redis' bytes responses.
|
||||||
|
- Command hint on bottom, include command syntax, supported redis version, and
|
||||||
|
time complexity.
|
||||||
|
- Official docs with built-in `HELP` command, try `HELP SET`!
|
||||||
|
- Written in pure Python, but IRedis was packaged into a single binary with
|
||||||
|
[PyOxidizer](https://github.com/indygreg/PyOxidizer), you can use cURL to
|
||||||
|
download and run, it just works, even you don't have a Python interpreter.
|
||||||
|
- Hide password for `AUTH` command.
|
||||||
|
- Says "Goodbye!" to you when you exit!
|
||||||
|
- For full features, please see: [iredis.io](https://www.iredis.io)
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Install via pip:
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install iredis
|
||||||
|
```
|
||||||
|
|
||||||
|
[pipx](https://github.com/pipxproject/pipx) is recommended:
|
||||||
|
|
||||||
|
```
|
||||||
|
pipx install iredis
|
||||||
|
```
|
||||||
|
|
||||||
|
Or you can download the executable binary with cURL(or wget), untar, then run.
|
||||||
|
It is especially useful when you don't have a python interpreter(E.g. the
|
||||||
|
[official Redis docker image](https://hub.docker.com/_/redis/) which doesn't
|
||||||
|
have Python installed.):
|
||||||
|
|
||||||
|
```
|
||||||
|
wget https://github.com/laixintao/iredis/releases/latest/download/iredis.tar.gz \
|
||||||
|
&& tar -xzf iredis.tar.gz \
|
||||||
|
&& ./iredis
|
||||||
|
```
|
||||||
|
|
||||||
|
(Check the [release page](https://github.com/laixintao/iredis/releases) if you
|
||||||
|
want to download an old version of IRedis.)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Once you install IRedis, you will know how to use it. Just remember, IRedis
|
||||||
|
supports similar options like redis-cli, like `-h` for redis-server's host and
|
||||||
|
`-p` for port.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ iredis --help
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using DSN
|
||||||
|
|
||||||
|
IRedis support storing server configuration in config file. Here is a DSN
|
||||||
|
config:
|
||||||
|
|
||||||
|
```
|
||||||
|
[alias_dsn]
|
||||||
|
dev=redis://localhost:6379/4
|
||||||
|
staging=redis://username:password@staging-redis.example.com:6379/1
|
||||||
|
```
|
||||||
|
|
||||||
|
Put this in your `iredisrc` then connect via `iredis -d staging` or
|
||||||
|
`iredis -d dev`.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
IRedis supports config files. Command-line options will always take precedence
|
||||||
|
over config. Configuration resolution from highest to lowest precedence is:
|
||||||
|
|
||||||
|
- _Options from command line_
|
||||||
|
- `$PWD/.iredisrc`
|
||||||
|
- `~/.iredisrc` (this path can be changed with `iredis --iredisrc $YOUR_PATH`)
|
||||||
|
- `/etc/iredisrc`
|
||||||
|
- default config in IRedis package.
|
||||||
|
|
||||||
|
You can copy the _self-explained_ default config here:
|
||||||
|
|
||||||
|
https://raw.githubusercontent.com/laixintao/iredis/master/iredis/data/iredisrc
|
||||||
|
|
||||||
|
And then make your own changes.
|
||||||
|
|
||||||
|
(If you are using an old versions of IRedis, please use the config file below,
|
||||||
|
and change the version in URL):
|
||||||
|
|
||||||
|
https://raw.githubusercontent.com/laixintao/iredis/v1.0.4/iredis/data/iredisrc
|
||||||
|
|
||||||
|
### Keys
|
||||||
|
|
||||||
|
IRedis support unix/readline-style REPL keyboard shortcuts, which means keys
|
||||||
|
like <kbd>Ctrl</kbd> + <kbd>F</kbd> to forward work.
|
||||||
|
|
||||||
|
Also:
|
||||||
|
|
||||||
|
- <kbd>Ctrl</kbd> + <kbd>F</kbd> (i.e. EOF) to exit; you can also use the `exit`
|
||||||
|
command.
|
||||||
|
- <kbd>Ctrl</kbd> + <kbd>L</kbd> to clear screen; you can also use the `clear`
|
||||||
|
command.
|
||||||
|
- <kbd>Ctrl</kbd> + <kbd>X</kbd> <kbd>Ctrl</kbd> + <kbd>E</kbd> to open an
|
||||||
|
editor to edit command, or <kbd>V</kbd> in vi-mode.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Release Strategy
|
||||||
|
|
||||||
|
IRedis is built and released by CircleCI. Whenever a tag is pushed to the
|
||||||
|
`master` branch, a new release is built and uploaded to pypi.org, it's very
|
||||||
|
convenient.
|
||||||
|
|
||||||
|
Thus, we release as often as possible, so that users can always enjoy the new
|
||||||
|
features and bugfixes quickly. Any bugfix or new feature will get at least a
|
||||||
|
patch release, whereas big features will get a minor release.
|
||||||
|
|
||||||
|
### Setup Environment
|
||||||
|
|
||||||
|
IRedis favors [poetry](https://github.com/sdispater/poetry) as package
|
||||||
|
management tool. To setup a develop environment on your computer:
|
||||||
|
|
||||||
|
First, install poetry (you can do it in a python's virtualenv):
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install poetry
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run (which is similar to `pip install -e .`):
|
||||||
|
|
||||||
|
```
|
||||||
|
poetry install
|
||||||
|
```
|
||||||
|
|
||||||
|
**Be careful running testcases locally, it may flush you db!!!**
|
||||||
|
|
||||||
|
### Development Logs
|
||||||
|
|
||||||
|
This is a command-line tool, so we don't write logs to stdout.
|
||||||
|
|
||||||
|
You can `tail -f ~/.iredis.log` to see logs, the log is pretty clear, you can
|
||||||
|
see what actually happens from log files.
|
||||||
|
|
||||||
|
### Catch Up with Latest Redis-doc
|
||||||
|
|
||||||
|
IRedis use a git submodule to track current-up-to-date redis-doc version. To
|
||||||
|
catch up with latest:
|
||||||
|
|
||||||
|
1. Git pull in redis-doc
|
||||||
|
2. Copy doc files to `/data`: `cp -r redis-doc/commands* iredis/data`
|
||||||
|
3. Prettier
|
||||||
|
markdown`prettier --prose-wrap always iredis/data/commands/*.md --write`
|
||||||
|
4. Check the diff, update IRedis' code if needed.
|
||||||
|
|
||||||
|
## Related Projects
|
||||||
|
|
||||||
|
- [redis-tui](https://github.com/mylxsw/redis-tui)
|
||||||
|
|
||||||
|
If you like iredis, you may also like other cli tools by
|
||||||
|
[dbcli](https://www.dbcli.com/):
|
||||||
|
|
||||||
|
- [pgcli](https://www.pgcli.com) - Postgres Client with Auto-completion and
|
||||||
|
Syntax Highlighting
|
||||||
|
- [mycli](https://www.mycli.net) - MySQL/MariaDB/Percona Client with
|
||||||
|
Auto-completion and Syntax Highlighting
|
||||||
|
- [litecli](https://litecli.com) - SQLite Client with Auto-completion and Syntax
|
||||||
|
Highlighting
|
||||||
|
- [mssql-cli](https://github.com/dbcli/mssql-cli) - Microsoft SQL Server Client
|
||||||
|
with Auto-completion and Syntax Highlighting
|
||||||
|
- [athenacli](https://github.com/dbcli/athenacli) - AWS Athena Client with
|
||||||
|
Auto-completion and Syntax Highlighting
|
||||||
|
- [vcli](https://github.com/dbcli/vcli) - VerticaDB client
|
||||||
|
- [iredis](https://github.com/laixintao/iredis/) - Client for Redis with
|
||||||
|
AutoCompletion and Syntax Highlighting
|
||||||
|
|
||||||
|
IRedis is build on the top of
|
||||||
|
[prompt_toolkit](https://github.com/jonathanslenders/python-prompt-toolkit), a
|
||||||
|
Python library (by [Jonathan Slenders](https://twitter.com/jonathan_s)) for
|
||||||
|
building rich commandline applications.
|
294
docs/assets/color.css
Normal file
294
docs/assets/color.css
Normal file
|
@ -0,0 +1,294 @@
|
||||||
|
" Vim color file
|
||||||
|
"
|
||||||
|
" Author: Tomas Restrepo <tomas@winterdom.com>
|
||||||
|
" https://github.com/tomasr/molokai
|
||||||
|
"
|
||||||
|
" Note: Based on the Monokai theme for TextMate
|
||||||
|
" by Wimer Hazenberg and its darker variant
|
||||||
|
" by Hamish Stuart Macpherson
|
||||||
|
"
|
||||||
|
|
||||||
|
hi clear
|
||||||
|
|
||||||
|
if version > 580
|
||||||
|
" no guarantees for version 5.8 and below, but this makes it stop
|
||||||
|
" complaining
|
||||||
|
hi clear
|
||||||
|
if exists("syntax_on")
|
||||||
|
syntax reset
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
let g:colors_name="molokai"
|
||||||
|
|
||||||
|
if exists("g:molokai_original")
|
||||||
|
let s:molokai_original = g:molokai_original
|
||||||
|
else
|
||||||
|
let s:molokai_original = 0
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
hi Boolean guifg=#AE81FF
|
||||||
|
hi Character guifg=#E6DB74
|
||||||
|
hi Number guifg=#AE81FF
|
||||||
|
hi String guifg=#E6DB74
|
||||||
|
hi Conditional guifg=#F92672 gui=bold
|
||||||
|
hi Constant guifg=#AE81FF gui=bold
|
||||||
|
hi Cursor guifg=#000000 guibg=#F8F8F0
|
||||||
|
hi iCursor guifg=#000000 guibg=#F8F8F0
|
||||||
|
hi Debug guifg=#BCA3A3 gui=bold
|
||||||
|
hi Define guifg=#66D9EF
|
||||||
|
hi Delimiter guifg=#8F8F8F
|
||||||
|
hi DiffAdd guibg=#13354A
|
||||||
|
hi DiffChange guifg=#89807D guibg=#4C4745
|
||||||
|
hi DiffDelete guifg=#960050 guibg=#1E0010
|
||||||
|
hi DiffText guibg=#4C4745 gui=italic,bold
|
||||||
|
|
||||||
|
hi Directory guifg=#A6E22E gui=bold
|
||||||
|
hi Error guifg=#E6DB74
|
||||||
|
guibg=#1E0010
|
||||||
|
hi ErrorMsg guifg=#F92672
|
||||||
|
guibg=#232526 gui=bold
|
||||||
|
hi Exception guifg=#A6E22E gui=bold
|
||||||
|
hi Float guifg=#AE81FF
|
||||||
|
hi FoldColumn guifg=#465457 guibg=#000000
|
||||||
|
hi Folded guifg=#465457 guibg=#000000
|
||||||
|
hi Function guifg=#A6E22E
|
||||||
|
hi Identifier guifg=#FD971F
|
||||||
|
hi Ignore guifg=#808080 guibg=bg
|
||||||
|
hi IncSearch guifg=#C4BE89 guibg=#000000
|
||||||
|
|
||||||
|
hi Keyword guifg=#F92672 gui=bold
|
||||||
|
hi Label guifg=#E6DB74 gui=none
|
||||||
|
hi Macro guifg=#C4BE89 gui=italic
|
||||||
|
hi SpecialKey guifg=#66D9EF gui=italic
|
||||||
|
|
||||||
|
hi MatchParen guifg=#000000 guibg=#FD971F gui=bold
|
||||||
|
hi ModeMsg guifg=#E6DB74
|
||||||
|
hi MoreMsg guifg=#E6DB74
|
||||||
|
hi Operator guifg=#F92672
|
||||||
|
|
||||||
|
" complete menu
|
||||||
|
hi Pmenu guifg=#66D9EF guibg=#000000
|
||||||
|
hi PmenuSel guibg=#808080
|
||||||
|
hi PmenuSbar guibg=#080808
|
||||||
|
hi PmenuThumb guifg=#66D9EF
|
||||||
|
|
||||||
|
hi PreCondit guifg=#A6E22E gui=bold
|
||||||
|
hi PreProc guifg=#A6E22E
|
||||||
|
hi Question guifg=#66D9EF
|
||||||
|
hi Repeat guifg=#F92672 gui=bold
|
||||||
|
hi Search guifg=#000000 guibg=#FFE792
|
||||||
|
" marks
|
||||||
|
hi SignColumn guifg=#A6E22E guibg=#232526
|
||||||
|
hi SpecialChar guifg=#F92672 gui=bold
|
||||||
|
hi SpecialComment guifg=#7E8E91 gui=bold
|
||||||
|
hi Special guifg=#66D9EF guibg=bg gui=italic
|
||||||
|
if has("spell")
|
||||||
|
hi SpellBad guisp=#FF0000 gui=undercurl
|
||||||
|
hi SpellCap guisp=#7070F0 gui=undercurl
|
||||||
|
hi SpellLocal guisp=#70F0F0 gui=undercurl
|
||||||
|
hi SpellRare guisp=#FFFFFF gui=undercurl
|
||||||
|
endif
|
||||||
|
hi Statement guifg=#F92672 gui=bold
|
||||||
|
hi StatusLine guifg=#455354 guibg=fg
|
||||||
|
hi StatusLineNC guifg=#808080 guibg=#080808
|
||||||
|
hi StorageClass guifg=#FD971F gui=italic
|
||||||
|
hi Structure guifg=#66D9EF
|
||||||
|
hi Tag guifg=#F92672 gui=italic
|
||||||
|
hi Title guifg=#ef5939
|
||||||
|
hi Todo guifg=#FFFFFF guibg=bg gui=bold
|
||||||
|
|
||||||
|
hi Typedef guifg=#66D9EF
|
||||||
|
hi Type guifg=#66D9EF gui=none
|
||||||
|
hi Underlined guifg=#808080 gui=underline
|
||||||
|
|
||||||
|
hi VertSplit guifg=#808080 guibg=#080808 gui=bold
|
||||||
|
hi VisualNOS guibg=#403D3D
|
||||||
|
hi Visual guibg=#403D3D
|
||||||
|
hi WarningMsg guifg=#FFFFFF guibg=#333333 gui=bold
|
||||||
|
hi WildMenu guifg=#66D9EF guibg=#000000
|
||||||
|
|
||||||
|
hi TabLineFill guifg=#1B1D1E guibg=#1B1D1E
|
||||||
|
hi TabLine guibg=#1B1D1E guifg=#808080 gui=none
|
||||||
|
|
||||||
|
if s:molokai_original == 1
|
||||||
|
hi Normal guifg=#F8F8F2 guibg=#272822
|
||||||
|
hi Comment guifg=#75715E
|
||||||
|
hi CursorLine guibg=#3E3D32
|
||||||
|
hi CursorLineNr guifg=#FD971F gui=none
|
||||||
|
hi CursorColumn guibg=#3E3D32
|
||||||
|
hi ColorColumn guibg=#3B3A32
|
||||||
|
hi LineNr guifg=#BCBCBC guibg=#3B3A32
|
||||||
|
hi NonText guifg=#75715E
|
||||||
|
hi SpecialKey guifg=#75715E
|
||||||
|
else
|
||||||
|
hi Normal guifg=#F8F8F2 guibg=#1B1D1E
|
||||||
|
hi Comment guifg=#7E8E91
|
||||||
|
hi CursorLine guibg=#293739
|
||||||
|
hi CursorLineNr guifg=#FD971F gui=none
|
||||||
|
hi CursorColumn guibg=#293739
|
||||||
|
hi ColorColumn guibg=#232526
|
||||||
|
hi LineNr guifg=#465457 guibg=#232526
|
||||||
|
hi NonText guifg=#465457
|
||||||
|
hi SpecialKey guifg=#465457
|
||||||
|
end
|
||||||
|
|
||||||
|
"
|
||||||
|
" Support for 256-color terminal
|
||||||
|
"
|
||||||
|
if &t_Co > 255
|
||||||
|
if s:molokai_original == 1
|
||||||
|
hi Normal ctermbg=234
|
||||||
|
hi CursorLine ctermbg=235 cterm=none
|
||||||
|
hi CursorLineNr ctermfg=208 cterm=none
|
||||||
|
else
|
||||||
|
hi Normal ctermfg=252 ctermbg=233
|
||||||
|
hi CursorLine ctermbg=234 cterm=none
|
||||||
|
hi CursorLineNr ctermfg=208 cterm=none
|
||||||
|
endif
|
||||||
|
hi Boolean ctermfg=135
|
||||||
|
hi Character ctermfg=144
|
||||||
|
hi Number ctermfg=135
|
||||||
|
hi String ctermfg=144
|
||||||
|
hi Conditional ctermfg=161 cterm=bold
|
||||||
|
hi Constant ctermfg=135 cterm=bold
|
||||||
|
hi Cursor ctermfg=16 ctermbg=253
|
||||||
|
hi Debug ctermfg=225 cterm=bold
|
||||||
|
hi Define ctermfg=81
|
||||||
|
hi Delimiter ctermfg=241
|
||||||
|
|
||||||
|
hi DiffAdd ctermbg=24
|
||||||
|
hi DiffChange ctermfg=181 ctermbg=239
|
||||||
|
hi DiffDelete ctermfg=162 ctermbg=53
|
||||||
|
hi DiffText ctermbg=102 cterm=bold
|
||||||
|
|
||||||
|
hi Directory ctermfg=118 cterm=bold
|
||||||
|
hi Error ctermfg=219 ctermbg=89
|
||||||
|
hi ErrorMsg ctermfg=199 ctermbg=16 cterm=bold
|
||||||
|
hi Exception ctermfg=118 cterm=bold
|
||||||
|
hi Float ctermfg=135
|
||||||
|
hi FoldColumn ctermfg=67 ctermbg=16
|
||||||
|
hi Folded ctermfg=67 ctermbg=16
|
||||||
|
hi Function ctermfg=118
|
||||||
|
hi Identifier ctermfg=208 cterm=none
|
||||||
|
hi Ignore ctermfg=244 ctermbg=232
|
||||||
|
hi IncSearch ctermfg=193 ctermbg=16
|
||||||
|
|
||||||
|
hi keyword ctermfg=161 cterm=bold
|
||||||
|
hi Label ctermfg=229 cterm=none
|
||||||
|
hi Macro ctermfg=193
|
||||||
|
hi SpecialKey ctermfg=81
|
||||||
|
|
||||||
|
hi MatchParen ctermfg=233 ctermbg=208 cterm=bold
|
||||||
|
hi ModeMsg ctermfg=229
|
||||||
|
hi MoreMsg ctermfg=229
|
||||||
|
hi Operator ctermfg=161
|
||||||
|
|
||||||
|
" complete menu
|
||||||
|
hi Pmenu ctermfg=81 ctermbg=16
|
||||||
|
hi PmenuSel ctermfg=255 ctermbg=242
|
||||||
|
hi PmenuSbar ctermbg=232
|
||||||
|
hi PmenuThumb ctermfg=81
|
||||||
|
|
||||||
|
hi PreCondit ctermfg=118 cterm=bold
|
||||||
|
hi PreProc ctermfg=118
|
||||||
|
hi Question ctermfg=81
|
||||||
|
hi Repeat ctermfg=161 cterm=bold
|
||||||
|
hi Search ctermfg=0 ctermbg=222 cterm=NONE
|
||||||
|
|
||||||
|
" marks column
|
||||||
|
hi SignColumn ctermfg=118 ctermbg=235
|
||||||
|
hi SpecialChar ctermfg=161 cterm=bold
|
||||||
|
hi SpecialComment ctermfg=245 cterm=bold
|
||||||
|
hi Special ctermfg=81
|
||||||
|
if has("spell")
|
||||||
|
hi SpellBad ctermbg=52
|
||||||
|
hi SpellCap ctermbg=17
|
||||||
|
hi SpellLocal ctermbg=17
|
||||||
|
hi SpellRare ctermfg=none ctermbg=none cterm=reverse
|
||||||
|
endif
|
||||||
|
hi Statement ctermfg=161 cterm=bold
|
||||||
|
hi StatusLine ctermfg=238 ctermbg=253
|
||||||
|
hi StatusLineNC ctermfg=244 ctermbg=232
|
||||||
|
hi StorageClass ctermfg=208
|
||||||
|
hi Structure ctermfg=81
|
||||||
|
hi Tag ctermfg=161
|
||||||
|
hi Title ctermfg=166
|
||||||
|
hi Todo ctermfg=231 ctermbg=232 cterm=bold
|
||||||
|
|
||||||
|
hi Typedef ctermfg=81
|
||||||
|
hi Type ctermfg=81 cterm=none
|
||||||
|
hi Underlined ctermfg=244 cterm=underline
|
||||||
|
|
||||||
|
hi VertSplit ctermfg=244 ctermbg=232 cterm=bold
|
||||||
|
hi VisualNOS ctermbg=238
|
||||||
|
hi Visual ctermbg=235
|
||||||
|
hi WarningMsg ctermfg=231 ctermbg=238 cterm=bold
|
||||||
|
hi WildMenu ctermfg=81 ctermbg=16
|
||||||
|
|
||||||
|
hi Comment ctermfg=59
|
||||||
|
hi CursorColumn ctermbg=236
|
||||||
|
hi ColorColumn ctermbg=236
|
||||||
|
hi LineNr ctermfg=250 ctermbg=236
|
||||||
|
hi NonText ctermfg=59
|
||||||
|
|
||||||
|
hi SpecialKey ctermfg=59
|
||||||
|
|
||||||
|
if exists("g:rehash256") && g:rehash256 == 1
|
||||||
|
hi Normal ctermfg=252 ctermbg=234
|
||||||
|
hi CursorLine ctermbg=236 cterm=none
|
||||||
|
hi CursorLineNr ctermfg=208 cterm=none
|
||||||
|
|
||||||
|
hi Boolean ctermfg=141
|
||||||
|
hi Character ctermfg=222
|
||||||
|
hi Number ctermfg=141
|
||||||
|
hi String ctermfg=222
|
||||||
|
hi Conditional ctermfg=197 cterm=bold
|
||||||
|
hi Constant ctermfg=141 cterm=bold
|
||||||
|
|
||||||
|
hi DiffDelete ctermfg=125 ctermbg=233
|
||||||
|
|
||||||
|
hi Directory ctermfg=154 cterm=bold
|
||||||
|
hi Error ctermfg=222 ctermbg=233
|
||||||
|
hi Exception ctermfg=154 cterm=bold
|
||||||
|
hi Float ctermfg=141
|
||||||
|
hi Function ctermfg=154
|
||||||
|
hi Identifier ctermfg=208
|
||||||
|
|
||||||
|
hi Keyword ctermfg=197 cterm=bold
|
||||||
|
hi Operator ctermfg=197
|
||||||
|
hi PreCondit ctermfg=154 cterm=bold
|
||||||
|
hi PreProc ctermfg=154
|
||||||
|
hi Repeat ctermfg=197 cterm=bold
|
||||||
|
|
||||||
|
hi Statement ctermfg=197 cterm=bold
|
||||||
|
hi Tag ctermfg=197
|
||||||
|
hi Title ctermfg=203
|
||||||
|
hi Visual ctermbg=238
|
||||||
|
|
||||||
|
hi Comment ctermfg=244
|
||||||
|
hi LineNr ctermfg=239 ctermbg=235
|
||||||
|
hi NonText ctermfg=239
|
||||||
|
hi SpecialKey ctermfg=239
|
||||||
|
endif
|
||||||
|
end
|
||||||
|
|
||||||
|
" Must be at the end, because of ctermbg=234 bug.
|
||||||
|
" https://groups.google.com/forum/#!msg/vim_dev/afPqwAFNdrU/nqh6tOM87QUJ
|
||||||
|
set background=dark
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
|
||||||
|
bottom-toolbar = 'bg:#222222 #aaaaaa'
|
||||||
|
bottom-toolbar.off = 'bg:#222222 #888888'
|
||||||
|
bottom-toolbar.on = 'bg:#222222 #ffffff'
|
||||||
|
|
||||||
|
bottom-toolbar.transaction.valid = 'bg:#222222 #00ff5f bold'
|
||||||
|
bottom-toolbar.transaction.failed = 'bg:#222222 #ff005f bold'
|
||||||
|
|
||||||
|
# style classes for colored table output
|
||||||
|
output.header = "#00ff5f bold"
|
||||||
|
output.odd-row = ""
|
||||||
|
output.even-row = ""
|
386
docs/assets/demo.cast
Normal file
386
docs/assets/demo.cast
Normal file
|
@ -0,0 +1,386 @@
|
||||||
|
{"version": 2, "width": 66, "height": 20}
|
||||||
|
[0.0, "o", "\u001b]1337;RemoteHost=laixintao@Chico.local\u0007\u001b]1337;CurrentDir=/Users/laixintao\u0007\u001b]1337;ShellIntegrationVersion=6;shell=zsh\u0007"]
|
||||||
|
[0.034194, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||||
|
[0.054034, "o", "\u001b]133;D;0\u0007\u001b]1337;RemoteHost=laixintao@Chico.local\u0007\u001b]1337;CurrentDir=/Users/laixintao\u0007"]
|
||||||
|
[0.05708, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b]133;A\u0007$ \u001b]133;B\u0007\u001b[K"]
|
||||||
|
[0.057424, "o", "\u001b[?1h\u001b="]
|
||||||
|
[0.057603, "o", "\u001b[?2004h"]
|
||||||
|
[1.404776, "o", "i"]
|
||||||
|
[1.709882, "o", "\bir"]
|
||||||
|
[1.805422, "o", "e"]
|
||||||
|
[1.957047, "o", "d"]
|
||||||
|
[2.073349, "o", "i"]
|
||||||
|
[2.170964, "o", "s"]
|
||||||
|
[2.308611, "o", "\u001b[?1l\u001b>"]
|
||||||
|
[2.308783, "o", "\u001b[?2004l\r\r\n"]
|
||||||
|
[2.311011, "o", "\u001b]133;C;\u0007"]
|
||||||
|
[2.589501, "o", "\u001b[0m\u001b[?7h\u001b[0miredis 0.8.0\r\r\nredis-server 5.0.6 \r\r\nHome: https://iredis.io\r\r\nIssues: https://iredis.io/issues\u001b[0m\u001b[0m"]
|
||||||
|
[2.591596, "o", "\u001b[0m\u001b[?7h\u001b[0m\r\r\n\u001b[0m"]
|
||||||
|
[2.592274, "o", "\u001b[?1l"]
|
||||||
|
[2.592372, "o", "\u001b[6n"]
|
||||||
|
[2.599827, "o", "\u001b[?2004h\u001b[?25l\u001b[0m\u001b[?7l\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0m \r\u001b[65C \r\u001b[7A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[2.617441, "o", "\u001b[?25l\u001b[?7l\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0m \r\u001b[65C \u001b[0m\r\r\n\u001b[0;38;5;248;48;5;235mCtrl-D to exit; \r\u001b[65C \r\u001b[14A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[3.516857, "o", "\u001b[?25l\u001b[?7l\u001b[0mk \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m KEYS \u001b[0;38;5;16;48;5;238m \u001b[A\u001b[8D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[3.522751, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241meys *\u001b[0m \u001b[6D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[3.626927, "o", "\u001b[?25l\u001b[?7l\u001b[0me \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m KEYS\u001b[C \u001b[0;38;5;16;48;5;238m \u001b[A\u001b[8D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[3.632562, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mys *\u001b[0m \u001b[5D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[3.750562, "o", "\u001b[?25l\u001b[?7l\u001b[0my \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m KEYS\u001b[C \u001b[0;38;5;16;48;5;238m \u001b[A\u001b[8D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[3.755718, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241ms *\u001b[0m \u001b[4D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[3.859462, "o", "\u001b[?25l\u001b[?7l\u001b[3D\u001b[0;38;5;28;1mkeys\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m KEYS\u001b[C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;167;48;5;235;1m(generic) \u001b[0;38;5;28;48;5;235;1mKEYS\u001b[0;38;5;71;48;5;235;1m pattern\u001b[0;38;5;136;48;5;235m since: 1.0.0\u001b[0;38;5;241;48;5;235m complexity:O(N) with N bein\r\u001b[65Cg\u001b[14A\r\u001b[20C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[3.864549, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m *\u001b[0m \u001b[3D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[4.112384, "o", "\u001b[?25l\u001b[?7l\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[A\u001b[20C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[4.121805, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m*\u001b[0m \u001b[2D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[4.49726, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71;1m*\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[4.503062, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[4.796083, "o", "\u001b[?25l\u001b[?7l\u001b[22D\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0;38;5;28;1mkeys\u001b[0m \u001b[0;38;5;71;1m*\u001b[0m \r\u001b[65C \r\u001b[0m\r\r\n\u001b[J\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h\u001b[?2004l"]
|
||||||
|
[4.80137, "o", "\u001b[0m\u001b[?7h\u001b[0m 1)\u001b[0m \u001b[0;38;5;71m\"myzset\"\u001b[0m\r\r\n\u001b[0m 2)\u001b[0m \u001b[0;38;5;71m\"mylist1\"\u001b[0m\r\r\n\u001b[0m 3)\u001b[0m \u001b[0;38;5;71m\"abc\"\u001b[0m\r\r\n\u001b[0m 4)\u001b[0m \u001b[0;38;5;71m\"a\"\u001b[0m\r\r\n\u001b[0m 5)\u001b[0m \u001b[0;38;5;71m\"mstream\"\u001b[0m\r\r\n\u001b[0m 6)\u001b[0m \u001b[0;38;5;71m\"testKeyDB2\"\u001b[0m\r\r\n\u001b[0m 7)\u001b[0m \u001b[0;38;5;71m\"list:restaurant\"\u001b[0m\r\r\n\u001b[0m 8)\u001b[0m \u001b[0;38;5;71m\"Sicily\"\u001b[0m\r\r\n\u001b[0m 9)\u001b[0m \u001b[0;38;5;71m\"cars\"\u001b[0m\r\r\n\u001b[0m10)\u001b[0m \u001b[0;38;5;71m\"hash1\"\u001b[0m\r\r\n\u001b[0m11)\u001b[0m \u001b[0;38;5;71m\"list:buildings\"\u001b[0m\r\r\n\u001b[0m12)\u001b[0m \u001b[0;38;5;71m\"hash3\"\u001b[0m\r\r\n\u001b[0m13)\u001b[0m \u001b[0;38;5;71m\"fooset\"\u001b[0m\r\r\n\u001b[0m14)\u001b[0m \u001b[0;38;5;71m\"foo\"\u001b[0m\r\r\n\u001b[0m15)\u001b[0m \u001b[0;38;5;71m\"myset\"\u001b[0m\r\r\n\u001b[0m16)\u001b[0m \u001b[0;38;5;71m\"hash2\"\u001b[0m\r\r\n\u001b[0m17)\u001b[0m \u001b[0;38;5;71m\"list:animals\"\u001b[0m\r\r\n\u001b[0m18)\u001b[0m \u001b[0;38;5;71m\"af\"\u001b[0m\r\r\n\u001b[0m19)\u001b[0m \u001b[0;38;5;71m\"somestream\"\u001b[0m\r\r\n\u001b[0m20)\u001b[0m \u001b[0;38;5;71m\"kkk\"\u001b[0m\u001b[0m"]
|
||||||
|
[4.803285, "o", "\u001b[0m\u001b[?7h\u001b[0m\r\r\n\u001b[0m"]
|
||||||
|
[4.804015, "o", "\u001b[?1l"]
|
||||||
|
[4.804128, "o", "\u001b[6n"]
|
||||||
|
[4.807349, "o", "\u001b[?2004h\u001b[?25l\u001b[0m\u001b[?7l\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0m \r\u001b[65C \r\u001b[7A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[4.812015, "o", "\u001b[?25l\u001b[?7l\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;248;48;5;235mCtrl-D to exit; \r\u001b[65C \r\u001b[8A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[5.558691, "o", "\u001b[?25l\u001b[?7l\u001b[0mt \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TTL \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TIME \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TYPE \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TOUCH \u001b[0;38;5;16;48;5;238m \u001b[4A\u001b[8D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[5.562825, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mype myset\u001b[0m \u001b[10D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[5.802861, "o", "\u001b[?25l\u001b[?7l\u001b[0my \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m TYPE \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[4A\u001b[17C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[5.807952, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mpe myset\u001b[0m \u001b[9D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[6.011176, "o", "\u001b[?25l\u001b[?7l\u001b[0mp \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m TYPE\u001b[C \u001b[0;38;5;16;48;5;238m \u001b[A\u001b[8D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[6.01494, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241me myset\u001b[0m \u001b[8D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[6.1425, "o", "\u001b[?25l\u001b[?7l\u001b[3D\u001b[0;38;5;28;1mtype\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m TYPE\u001b[C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;167;48;5;235;1m(generic) \u001b[0;38;5;28;48;5;235;1mTYPE\u001b[0;38;5;71;48;5;235m key\u001b[0;38;5;136;48;5;235m since: 1.0.0\u001b[0;38;5;241;48;5;235m complexity:O(1)\u001b[8A\u001b[29D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[6.14741, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m myset\u001b[0m \u001b[7D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[6.244466, "o", "\u001b[?25l\u001b[?7l\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m kkk \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m somestream \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m af \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m list:animals \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m hash2 \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m myset \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m foo \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[6.253095, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mmyset\u001b[0m \u001b[6D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[6.712731, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mm\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mm\u001b[0;38;5;238;48;5;30myset\u001b[8C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mm\u001b[0;38;5;238;48;5;30mstream\u001b[0;38;5;231;48;5;30m \u001b[4C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mm\u001b[0;38;5;238;48;5;30mylist1\u001b[6C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mm\u001b[0;38;5;238;48;5;30myzset\u001b[0;38;5;231;48;5;30m \u001b[2C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mso\u001b[0;38;5;231;48;5;30;1;4mm\u001b[0;38;5;238;48;5;30mestream\u001b[3C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mlist:ani\u001b[0;38;5;231;48;5;30;1;4mm\u001b[0;38;5;238;48;5;30mals\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[7A\u001b[17D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[6.718052, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241myset\u001b[0m \u001b[5D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[9.031011, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71my\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[22C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mmy\u001b[0;38;5;238;48;5;30mset\u001b[3C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[22C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mmy\u001b[0;38;5;238;48;5;30mlist1\u001b[C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[22C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mmy\u001b[0;38;5;238;48;5;30mzset\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[6A\u001b[22C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[9.048076, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mset\u001b[0m \u001b[4D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[9.337635, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mset\u001b[0m\r\r\n\u001b[23C\u001b[0;38;5;16;48;5;231m \u001b[0;38;5;16;48;5;231;4mmy\u001b[0;48;5;231mset\u001b[0;38;5;16;48;5;231m \u001b[A\u001b[6D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[9.341531, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[9.587591, "o", "\u001b[?25l\u001b[?7l\u001b[26D\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0;38;5;28;1mtype\u001b[0m \u001b[0;38;5;71mmyset\u001b[0m \r\u001b[65C \r\u001b[0m\r\r\n\u001b[J\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h\u001b[?2004l"]
|
||||||
|
[9.592731, "o", "\u001b[0m\u001b[?7h\u001b[0m\"set\"\u001b[0m\u001b[0m"]
|
||||||
|
[9.595501, "o", "\u001b[0m\u001b[?7h\u001b[0m\r\r\n\u001b[0m"]
|
||||||
|
[9.596128, "o", "\u001b[?1l"]
|
||||||
|
[9.59625, "o", "\u001b[6n"]
|
||||||
|
[9.598996, "o", "\u001b[?2004h\u001b[?25l\u001b[0m\u001b[?7l\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0m \r\u001b[65C \r\u001b[7A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[9.602329, "o", "\u001b[?25l\u001b[?7l\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;248;48;5;235mCtrl-D to exit; \r\u001b[65C \r\u001b[8A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[10.630148, "o", "\u001b[?25l\u001b[?7l\u001b[0ms \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m SET \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m SREM \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m SPOP \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m SADD \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m SYNC \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m SAVE \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m SORT \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[16D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[10.635184, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241met foo bar\u001b[0m \u001b[11D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[11.02782, "o", "\u001b[?25l\u001b[?7l\u001b[0mm \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m SMOVE\u001b[4C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m SMEMBERS \u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[7A\u001b[15D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[11.031124, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241members fooset\u001b[0m \u001b[14D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[11.103554, "o", "\u001b[?25l\u001b[?7l\u001b[0me \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m SMEMBERS \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[2A\u001b[18C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[11.107347, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mmbers fooset\u001b[0m \u001b[13D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[11.198052, "o", "\u001b[?25l\u001b[?7l\u001b[0mm \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m SMEMBERS \u001b[0;38;5;16;48;5;238m \u001b[A\u001b[11D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[11.201012, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mbers fooset\u001b[0m \u001b[12D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[11.568186, "o", "\u001b[?25l\u001b[?7l\u001b[4D\u001b[0;38;5;28;1mSMEMBERS\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[20C\u001b[0;38;5;16;48;5;231m SMEMBERS \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;167;48;5;235;1m(set) \u001b[0;38;5;28;48;5;235;1mSMEMBERS\u001b[0;38;5;71;48;5;235m key\u001b[0;38;5;136;48;5;235m since: 1.0.0\u001b[0;38;5;241;48;5;235m complexity:O(N) where N is the \r\u001b[65Cs\u001b[8A\r\u001b[24C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[11.572482, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[11.824354, "o", "\u001b[?25l\u001b[?7l\u001b[C\u001b[0m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m myset \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[25C\u001b[0;38;5;231;48;5;30m kkk \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[25C\u001b[0;38;5;231;48;5;30m somestream \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[25C\u001b[0;38;5;231;48;5;30m af \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[25C\u001b[0;38;5;231;48;5;30m list:animals \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[25C\u001b[0;38;5;231;48;5;30m hash2 \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[25C\u001b[0;38;5;231;48;5;30m foo \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[11.828823, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mmyset\u001b[0m \u001b[6D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[12.363643, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mm\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[25C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mm\u001b[0;38;5;238;48;5;30myset\u001b[8C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[25C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mm\u001b[0;38;5;238;48;5;30mstream\u001b[6C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[25C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mm\u001b[0;38;5;238;48;5;30mylist1\u001b[0;38;5;231;48;5;30m \u001b[4C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[25C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mm\u001b[0;38;5;238;48;5;30myzset\u001b[7C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[25C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mso\u001b[0;38;5;231;48;5;30;1;4mm\u001b[0;38;5;238;48;5;30mestream\u001b[0;38;5;231;48;5;30m \u001b[2C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[25C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mlist:ani\u001b[0;38;5;231;48;5;30;1;4mm\u001b[0;38;5;238;48;5;30mals\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[25C\u001b[0m \u001b[7A\u001b[17D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[12.368073, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241myset\u001b[0m \u001b[5D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[12.639026, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71my\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[26C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mmy\u001b[0;38;5;238;48;5;30mset\u001b[3C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[26C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mmy\u001b[0;38;5;238;48;5;30mlist1\u001b[C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[26C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mmy\u001b[0;38;5;238;48;5;30mzset\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[6A\u001b[26C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[12.642963, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mset\u001b[0m \u001b[4D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[12.84344, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mset\u001b[0m\r\r\n\u001b[27C\u001b[0;38;5;16;48;5;231m \u001b[0;38;5;16;48;5;231;4mmy\u001b[0;48;5;231mset\u001b[0;38;5;16;48;5;231m \u001b[A\u001b[6D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[12.847415, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[13.250149, "o", "\u001b[?25l\u001b[?7l\u001b[30D\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0;38;5;28;1mSMEMBERS\u001b[0m \u001b[0;38;5;71mmyset\u001b[0m \r\u001b[65C \r\u001b[0m\r\r\n\u001b[J\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h\u001b[?2004l"]
|
||||||
|
[13.255777, "o", "\u001b[0m\u001b[?7h\u001b[0m1)\u001b[0m \u001b[0;38;5;208m\"foo\"\u001b[0m\r\r\n\u001b[0m2)\u001b[0m \u001b[0;38;5;208m\"world\"\u001b[0m\r\r\n\u001b[0m3)\u001b[0m \u001b[0;38;5;208m\"bar\"\u001b[0m\r\r\n\u001b[0m4)\u001b[0m \u001b[0;38;5;208m\"hello\"\u001b[0m\u001b[0m"]
|
||||||
|
[13.257576, "o", "\u001b[0m\u001b[?7h\u001b[0m\r\r\n\u001b[0m"]
|
||||||
|
[13.25837, "o", "\u001b[?1l\u001b[6n"]
|
||||||
|
[13.260811, "o", "\u001b[?2004h\u001b[?25l\u001b[0m\u001b[?7l\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0m \r\u001b[65C \r\u001b[7A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[13.264088, "o", "\u001b[?25l\u001b[?7l\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;248;48;5;235mCtrl-D to exit; \r\u001b[65C \r\u001b[8A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[15.018012, "o", "\u001b[?25l\u001b[?7l\u001b[0mt \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TTL \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TIME \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TYPE \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TOUCH \u001b[0;38;5;16;48;5;238m \u001b[4A\u001b[8D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[15.021342, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mype myset\u001b[0m \u001b[10D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[15.136615, "o", "\u001b[?25l\u001b[?7l\u001b[0my \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m TYPE \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[4A\u001b[17C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[15.139872, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mpe myset\u001b[0m \u001b[9D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[15.301704, "o", "\u001b[?25l\u001b[?7l\u001b[0mp \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m TYPE\u001b[C \u001b[0;38;5;16;48;5;238m \u001b[A\u001b[8D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[15.305109, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241me myset\u001b[0m \u001b[8D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[15.381888, "o", "\u001b[?25l\u001b[?7l\u001b[3D\u001b[0;38;5;28;1mtype\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m TYPE\u001b[C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;167;48;5;235;1m(generic) \u001b[0;38;5;28;48;5;235;1mTYPE\u001b[0;38;5;71;48;5;235m key\u001b[0;38;5;136;48;5;235m since: 1.0.0\u001b[0;38;5;241;48;5;235m complexity:O(1)\u001b[8A\u001b[29D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[15.387144, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m myset\u001b[0m \u001b[7D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[15.493325, "o", "\u001b[?25l\u001b[?7l\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m myset \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m kkk \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m somestream \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m af \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m list:animals \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m hash2 \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m foo \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[15.498349, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mmyset\u001b[0m \u001b[6D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[15.792526, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mc\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mc\u001b[0;38;5;238;48;5;30mars\u001b[3C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mSi\u001b[0;38;5;231;48;5;30;1;4mc\u001b[0;38;5;238;48;5;30mily\u001b[C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mab\u001b[0;38;5;231;48;5;30;1;4mc\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[7A\u001b[17D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[15.797662, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mars\u001b[0m \u001b[4D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[15.880584, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71ma\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[22C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mca\u001b[0;38;5;238;48;5;30mrs\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[3A\u001b[22C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[15.885051, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mrs\u001b[0m \u001b[3D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[16.48149, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mrs\u001b[0m\r\r\n\u001b[23C\u001b[0;38;5;16;48;5;231m \u001b[0;38;5;16;48;5;231;4mca\u001b[0;48;5;231mrs\u001b[0;38;5;16;48;5;231m \u001b[A\u001b[5D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[16.486215, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[16.848883, "o", "\u001b[?25l\u001b[?7l\u001b[25D\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0;38;5;28;1mtype\u001b[0m \u001b[0;38;5;71mcars\u001b[0m \r\u001b[65C \r\u001b[0m\r\r\n\u001b[J\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h\u001b[?2004l"]
|
||||||
|
[16.852972, "o", "\u001b[0m\u001b[?7h\u001b[0m\"zset\"\u001b[0m\u001b[0m"]
|
||||||
|
[16.854529, "o", "\u001b[0m\u001b[?7h\u001b[0m\r\r\n\u001b[0m"]
|
||||||
|
[16.855119, "o", "\u001b[?1l\u001b[6n"]
|
||||||
|
[16.858567, "o", "\u001b[?2004h\u001b[?25l\u001b[0m\u001b[?7l\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0m \r\u001b[65C \r\u001b[7A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[16.862469, "o", "\u001b[?25l\u001b[?7l\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;248;48;5;235mCtrl-D to exit; \r\u001b[65C \r\u001b[8A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[17.715952, "o", "\u001b[?25l\u001b[?7l\u001b[0mz \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m ZREM \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m ZADD \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m ZSCAN \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m ZRANK \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m ZCARD \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m ZSCORE \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m ZRANGE \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[19D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[17.72013, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mscan kkk 0\u001b[0m \u001b[11D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[17.977719, "o", "\u001b[?25l\u001b[?7l\u001b[0ms \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m ZSCAN\u001b[2C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m ZSCORE \u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[7A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[17.981853, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mcan kkk 0\u001b[0m \u001b[10D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[18.172029, "o", "\u001b[?25l\u001b[?7l\u001b[0mc \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m ZSCAN\u001b[C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m ZSCORE \u001b[0;38;5;16;48;5;238m \u001b[2A\u001b[9D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[18.176161, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241man kkk 0\u001b[0m \u001b[9D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[18.555927, "o", "\u001b[?25l\u001b[?7l\u001b[3D\u001b[0;38;5;28;1mZSCAN\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[19C\u001b[0;38;5;16;48;5;231m ZSCAN \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;167;48;5;235;1m(sorted_set) \u001b[0;38;5;28;48;5;235;1mZSCAN\u001b[0;38;5;71;48;5;235m key\u001b[0;38;5;141;48;5;235m cursor\u001b[0;38;5;28;48;5;235;1m [MATCH\u001b[0;38;5;71;48;5;235;1m pattern\u001b[0;38;5;28;48;5;235;1m] [COUNT\u001b[0;38;5;141;48;5;235m count\u001b[0;38;5;28;48;5;235;1m]\u001b[0;38;5;136;48;5;235m sin\r\u001b[65Cc\u001b[8A\r\u001b[21C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[18.5603, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[18.890406, "o", "\u001b[?25l\u001b[?7l\u001b[C\u001b[0m \u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m cars \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m myset \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[22C\u001b[0;38;5;231;48;5;30m kkk \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[22C\u001b[0;38;5;231;48;5;30m somestream \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[22C\u001b[0;38;5;231;48;5;30m af \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[22C\u001b[0;38;5;231;48;5;30m list:animals \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[22C\u001b[0;38;5;231;48;5;30m hash2 \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[18.895303, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mcars 0\u001b[0m \u001b[7D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[19.875157, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mcars\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[22C\u001b[0;38;5;16;48;5;231m cars \u001b[A\u001b[13D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[19.880817, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[20.080926, "o", "\u001b[?25l\u001b[?7l\u001b[26D\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0;38;5;28;1mZSCAN\u001b[0m \u001b[0;38;5;71mcars\u001b[0m \r\u001b[65C \r\u001b[0m\r\r\n\u001b[J\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h\u001b[?2004l"]
|
||||||
|
[20.089992, "o", "\u001b[0m\u001b[?7h\u001b[0;38;5;102m(error) \u001b[0;38;5;197;1mwrong number of arguments for 'zscan' command\u001b[0m\u001b[0m"]
|
||||||
|
[20.091761, "o", "\u001b[0m\u001b[?7h\u001b[0m\r\r\n\u001b[0m"]
|
||||||
|
[20.092544, "o", "\u001b[?1l\u001b[6n"]
|
||||||
|
[20.101137, "o", "\u001b[?2004h\u001b[?25l\u001b[0m\u001b[?7l\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0m \r\u001b[65C \r\u001b[7A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[20.109017, "o", "\u001b[?25l\u001b[?7l\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;248;48;5;235mCtrl-D to exit; \r\u001b[65C \r\u001b[8A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[21.330573, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;28;1mZSCAN\u001b[0m \u001b[0;38;5;71mcars\u001b[0m \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;167;48;5;235;1m(sorted_set) \u001b[0;38;5;28;48;5;235;1mZSCAN\u001b[0;38;5;71;48;5;235m key\u001b[0;38;5;141;48;5;235m cursor\u001b[0;38;5;28;48;5;235;1m [MATCH\u001b[0;38;5;71;48;5;235;1m pattern\u001b[0;38;5;28;48;5;235;1m] [COUNT\u001b[0;38;5;141;48;5;235m count\u001b[0;38;5;28;48;5;235;1m]\u001b[0;38;5;136;48;5;235m sin\r\u001b[65Cc\u001b[8A\r\u001b[26C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[21.340066, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[21.796556, "o", "\u001b[?25l\u001b[?7l\u001b[C\u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[21.800207, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m0\u001b[0m \u001b[2D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[22.451791, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;141m0\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[22.460219, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[23.089162, "o", "\u001b[?25l\u001b[?7l\u001b[28D\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0;38;5;28;1mZSCAN\u001b[0m \u001b[0;38;5;71mcars\u001b[0m \u001b[0;38;5;141m0\u001b[0m \r\u001b[65C \r\u001b[0m\r\r\n\u001b[J\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h\u001b[?2004l"]
|
||||||
|
[23.100459, "o", "\u001b[0m\u001b[?7h\u001b[0;38;5;102m(cursor) \u001b[0;38;5;141m0\u001b[0m\r\r\n\u001b[0m1)\u001b[0m \u001b[0;38;5;141m1367522908124000 \u001b[0;38;5;208m\"robins-car\"\u001b[0m\u001b[0m"]
|
||||||
|
[23.102113, "o", "\u001b[0m\u001b[?7h\u001b[0m\r\r\n\u001b[0m"]
|
||||||
|
[23.102697, "o", "\u001b[?1l"]
|
||||||
|
[23.102802, "o", "\u001b[6n"]
|
||||||
|
[23.105131, "o", "\u001b[?2004h\u001b[?25l\u001b[0m\u001b[?7l\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0m \r\u001b[65C \r\u001b[7A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[23.108162, "o", "\u001b[?25l\u001b[?7l\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;248;48;5;235mCtrl-D to exit; \r\u001b[65C \r\u001b[8A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[25.209658, "o", "\u001b[?25l\u001b[?7l\u001b[0mt \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TTL \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TIME \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TYPE \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TOUCH \u001b[0;38;5;16;48;5;238m \u001b[4A\u001b[8D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[25.218301, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mype cars\u001b[0m \u001b[9D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[25.35018, "o", "\u001b[?25l\u001b[?7l\u001b[0my \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m TYPE \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[4A\u001b[17C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[25.354149, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mpe cars\u001b[0m \u001b[8D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[25.529032, "o", "\u001b[?25l\u001b[?7l\u001b[0mp \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m TYPE\u001b[C \u001b[0;38;5;16;48;5;238m \u001b[A\u001b[8D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[25.533149, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241me cars\u001b[0m \u001b[7D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[25.630584, "o", "\u001b[?25l\u001b[?7l\u001b[3D\u001b[0;38;5;28;1mtype\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m TYPE\u001b[C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;167;48;5;235;1m(generic) \u001b[0;38;5;28;48;5;235;1mTYPE\u001b[0;38;5;71;48;5;235m key\u001b[0;38;5;136;48;5;235m since: 1.0.0\u001b[0;38;5;241;48;5;235m complexity:O(1)\u001b[8A\u001b[29D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[25.634871, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m cars\u001b[0m \u001b[6D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[25.742093, "o", "\u001b[?25l\u001b[?7l\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m cars \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m myset \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m kkk \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m somestream \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m af \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m list:animals \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m hash2 \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[25.750379, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mcars\u001b[0m \u001b[5D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[25.984703, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71ml\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ml\u001b[0;38;5;238;48;5;30mist:animals\u001b[3C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ml\u001b[0;38;5;238;48;5;30mist:buildings\u001b[C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ml\u001b[0;38;5;238;48;5;30mist:restaurant\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mmy\u001b[0;38;5;231;48;5;30;1;4ml\u001b[0;38;5;238;48;5;30mist1\u001b[0;38;5;231;48;5;30m \u001b[6C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mSici\u001b[0;38;5;231;48;5;30;1;4ml\u001b[0;38;5;238;48;5;30my\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[7A\u001b[17D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[25.988778, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mist:animals\u001b[0m \u001b[12D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[26.206791, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mi\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[22C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mli\u001b[0;38;5;238;48;5;30mst:animals\u001b[3C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[22C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mli\u001b[0;38;5;238;48;5;30mst:buildings\u001b[C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[22C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mli\u001b[0;38;5;238;48;5;30mst:restaurant\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[22C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mmy\u001b[0;38;5;231;48;5;30;1;4mli\u001b[0;38;5;238;48;5;30mst1\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[5A\u001b[22C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[26.213146, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mst:animals\u001b[0m \u001b[11D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[26.905529, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mst:animals\u001b[0m\r\r\n\u001b[23C\u001b[0;38;5;16;48;5;231m \u001b[0;38;5;16;48;5;231;4mli\u001b[0;48;5;231mst:animals\u001b[0;38;5;16;48;5;231m \u001b[A\u001b[7D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[26.915375, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[27.735947, "o", "\u001b[?25l\u001b[?7l\u001b[33D\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0;38;5;28;1mtype\u001b[0m \u001b[0;38;5;71mlist:animals\u001b[0m \r\u001b[65C \r\u001b[0m\r\r\n\u001b[J\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h\u001b[?2004l"]
|
||||||
|
[27.740639, "o", "\u001b[0m\u001b[?7h\u001b[0m\"list\"\u001b[0m\u001b[0m"]
|
||||||
|
[27.743095, "o", "\u001b[0m\u001b[?7h\u001b[0m\r\r\n\u001b[0m"]
|
||||||
|
[27.743675, "o", "\u001b[?1l"]
|
||||||
|
[27.74377, "o", "\u001b[6n"]
|
||||||
|
[27.746106, "o", "\u001b[?2004h\u001b[?25l\u001b[0m\u001b[?7l\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0m \r\u001b[65C \r\u001b[7A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[27.750128, "o", "\u001b[?25l\u001b[?7l\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;248;48;5;235mCtrl-D to exit; \r\u001b[65C \r\u001b[8A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[28.104759, "o", "\u001b[?25l\u001b[?7l\u001b[0ml \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m LSET \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m LREM \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m LPOP \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m LLEN \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m LTRIM \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m LPUSH \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m LRANGE \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[11D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[28.108111, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mrange list:animals 0 6\u001b[0m \u001b[23D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[28.254897, "o", "\u001b[?25l\u001b[?7l\u001b[0ml \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m LLEN \u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[7A\u001b[10D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[28.259097, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241men testKeyDB2\u001b[0m \u001b[14D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[28.758049, "o", "\u001b[?25l\u001b[?7l\u001b[2D\u001b[0;38;5;28;1mLLEN\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0;38;5;16;48;5;231m LLEN \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;167;48;5;235;1m(list) \u001b[0;38;5;28;48;5;235;1mLLEN\u001b[0;38;5;71;48;5;235m key\u001b[0;38;5;136;48;5;235m since: 1.0.0\u001b[0;38;5;241;48;5;235m complexity:O(1)\u001b[8A\u001b[26D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[28.762078, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[28.981063, "o", "\u001b[?25l\u001b[?7l\u001b[C\u001b[0m \u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m list:animals \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m cars \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m myset \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m kkk \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m somestream \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m af \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m hash2 \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[28.987768, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mlist:animals\u001b[0m \u001b[13D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[29.889228, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mi\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30ml\u001b[0;38;5;231;48;5;30;1;4mi\u001b[0;38;5;238;48;5;30mst:animals\u001b[3C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30ml\u001b[0;38;5;231;48;5;30;1;4mi\u001b[0;38;5;238;48;5;30mst:buildings\u001b[C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mS\u001b[0;38;5;231;48;5;30;1;4mi\u001b[0;38;5;238;48;5;30mcily\u001b[9C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30ml\u001b[0;38;5;231;48;5;30;1;4mi\u001b[0;38;5;238;48;5;30mst:restaurant\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mmyl\u001b[0;38;5;231;48;5;30;1;4mi\u001b[0;38;5;238;48;5;30mst1\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[7A\u001b[17D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[29.895168, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[30.516043, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[5A\u001b[20C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[30.520459, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[30.845385, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71ml\u001b[0m \u001b[0m\r\r\n\u001b[22C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ml\u001b[0;38;5;238;48;5;30mist:animals\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[22C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ml\u001b[0;38;5;238;48;5;30mist:buildings\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[22C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ml\u001b[0;38;5;238;48;5;30mist:restaurant\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[22C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mmy\u001b[0;38;5;231;48;5;30;1;4ml\u001b[0;38;5;238;48;5;30mist1\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[22C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mSici\u001b[0;38;5;231;48;5;30;1;4ml\u001b[0;38;5;238;48;5;30my\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[5A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[30.84925, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mist:animals\u001b[0m \u001b[12D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[31.051905, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mi\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[22C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mli\u001b[0;38;5;238;48;5;30mst:animals\u001b[3C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[22C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mli\u001b[0;38;5;238;48;5;30mst:buildings\u001b[C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[22C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4mli\u001b[0;38;5;238;48;5;30mst:restaurant\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[22C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mmy\u001b[0;38;5;231;48;5;30;1;4mli\u001b[0;38;5;238;48;5;30mst1\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[5A\u001b[22C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[31.055859, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mst:animals\u001b[0m \u001b[11D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[31.292784, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mst:animals\u001b[0m\r\r\n\u001b[23C\u001b[0;38;5;16;48;5;231m \u001b[0;38;5;16;48;5;231;4mli\u001b[0;48;5;231mst:animals\u001b[0;38;5;16;48;5;231m \u001b[A\u001b[7D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[31.296722, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[32.658483, "o", "\u001b[?25l\u001b[?7l\u001b[33D\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0;38;5;28;1mLLEN\u001b[0m \u001b[0;38;5;71mlist:animals\u001b[0m \r\u001b[65C \r\u001b[0m\r\r\n\u001b[J\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h\u001b[?2004l"]
|
||||||
|
[32.662927, "o", "\u001b[0m\u001b[?7h\u001b[0;38;5;102m(integer) \u001b[0m51\u001b[0m\u001b[0m"]
|
||||||
|
[32.664937, "o", "\u001b[0m\u001b[?7h\u001b[0m\r\r\n\u001b[0m"]
|
||||||
|
[32.665552, "o", "\u001b[?1l"]
|
||||||
|
[32.665646, "o", "\u001b[6n"]
|
||||||
|
[32.66873, "o", "\u001b[?2004h\u001b[?25l\u001b[0m\u001b[?7l\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0m \r\u001b[65C \r\u001b[7A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[32.671953, "o", "\u001b[?25l\u001b[?7l\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;248;48;5;235mCtrl-D to exit; \r\u001b[65C \r\u001b[8A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[35.303978, "o", "\u001b[?25l\u001b[?7l\u001b[0ml \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m LSET \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m LREM \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m LPOP \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m LLEN \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m LTRIM \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m LPUSH \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m LRANGE \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[11D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[35.320666, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mrange list:animals 0 6\u001b[0m \u001b[23D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[35.652587, "o", "\u001b[?25l\u001b[?7l\u001b[0mr \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m LREM\u001b[3C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m LRANGE \u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[7A\u001b[10D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[35.656638, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mange list:animals 0 6\u001b[0m \u001b[22D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[35.741188, "o", "\u001b[?25l\u001b[?7l\u001b[0ma \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m LRANGE \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[2A\u001b[18C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[35.744004, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mnge list:animals 0 6\u001b[0m \u001b[21D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[35.819735, "o", "\u001b[?25l\u001b[?7l\u001b[0mn \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m LRANGE \u001b[0;38;5;16;48;5;238m \u001b[A\u001b[9D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[35.823559, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mge list:animals 0 6\u001b[0m \u001b[20D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[35.956968, "o", "\u001b[?25l\u001b[?7l\u001b[0mg \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m LRANGE \u001b[0;38;5;16;48;5;238m \u001b[A\u001b[9D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[35.960667, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241me list:animals 0 6\u001b[0m \u001b[19D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[36.069943, "o", "\u001b[?25l\u001b[?7l\u001b[5D\u001b[0;38;5;28;1mlrange\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m LRANGE \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;167;48;5;235;1m(list) \u001b[0;38;5;28;48;5;235;1mLRANGE\u001b[0;38;5;71;48;5;235m key\u001b[0;38;5;141;48;5;235m start stop\u001b[0;38;5;136;48;5;235m since: 1.0.0\u001b[0;38;5;241;48;5;235m complexity:O(S+N) whe\r\u001b[65Cr\u001b[8A\r\u001b[22C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[36.076669, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m list:animals 0 6\u001b[0m \u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[36.209277, "o", "\u001b[?25l\u001b[?7l\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[22C\u001b[0m \u001b[0;38;5;231;48;5;30m list:animals \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[23C\u001b[0;38;5;231;48;5;30m cars \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[23C\u001b[0;38;5;231;48;5;30m myset \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[23C\u001b[0;38;5;231;48;5;30m kkk \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[23C\u001b[0;38;5;231;48;5;30m somestream \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[23C\u001b[0;38;5;231;48;5;30m af \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[23C\u001b[0;38;5;231;48;5;30m hash2 \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[36.216403, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mlist:animals 0 6\u001b[0m \u001b[17D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[37.3095, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71ml\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[23C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ml\u001b[0;38;5;238;48;5;30mist:animals\u001b[3C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[23C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ml\u001b[0;38;5;238;48;5;30mist:buildings\u001b[C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[23C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ml\u001b[0;38;5;238;48;5;30mist:restaurant\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[23C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mmy\u001b[0;38;5;231;48;5;30;1;4ml\u001b[0;38;5;238;48;5;30mist1\u001b[8C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[23C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mSici\u001b[0;38;5;231;48;5;30;1;4ml\u001b[0;38;5;238;48;5;30my\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[23C\u001b[0m \u001b[7A\u001b[17D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[37.319061, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mist:animals 0 6\u001b[0m \u001b[16D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[37.965239, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mist:animals\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[24C\u001b[0;38;5;16;48;5;231m \u001b[0;38;5;16;48;5;231;4ml\u001b[0;48;5;231mist:animals\u001b[0;38;5;16;48;5;231m \u001b[A\u001b[6D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[37.96939, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[38.184492, "o", "\u001b[?25l\u001b[?7l\u001b[C\u001b[0m \u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[5A\u001b[35C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[38.189811, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m0 6\u001b[0m \u001b[4D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[38.809319, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;141m0\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[38.813992, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m 6\u001b[0m \u001b[3D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[38.969048, "o", "\u001b[?25l\u001b[?7l\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[38.973767, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m6\u001b[0m \u001b[2D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[41.005131, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;141m6\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[41.015952, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[41.427252, "o", "\u001b[?25l\u001b[?7l\u001b[39D\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0;38;5;28;1mlrange\u001b[0m \u001b[0;38;5;71mlist:animals\u001b[0m \u001b[0;38;5;141m0\u001b[0m \u001b[0;38;5;141m6\u001b[0m \r\u001b[65C \r\u001b[0m\r\r\n\u001b[J\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h\u001b[?2004l"]
|
||||||
|
[41.437426, "o", "\u001b[0m\u001b[?7h\u001b[0m1)\u001b[0m \u001b[0;38;5;208m\"wolf\"\u001b[0m\r\r\n\u001b[0m2)\u001b[0m \u001b[0;38;5;208m\"turtle\"\u001b[0m\r\r\n\u001b[0m3)\u001b[0m \u001b[0;38;5;208m\"tiger\"\u001b[0m\r\r\n\u001b[0m4)\u001b[0m \u001b[0;38;5;208m\"squirrel\"\u001b[0m\r\r\n\u001b[0m5)\u001b[0m \u001b[0;38;5;208m\"spider\"\u001b[0m\r\r\n\u001b[0m6)\u001b[0m \u001b[0;38;5;208m\"snake\"\u001b[0m\r\r\n\u001b[0m7)\u001b[0m \u001b[0;38;5;208m\"snail\"\u001b[0m\u001b[0m"]
|
||||||
|
[41.439085, "o", "\u001b[0m\u001b[?7h\u001b[0m\r\r\n\u001b[0m"]
|
||||||
|
[41.439711, "o", "\u001b[?1l"]
|
||||||
|
[41.439807, "o", "\u001b[6n"]
|
||||||
|
[41.444224, "o", "\u001b[?2004h\u001b[?25l\u001b[0m\u001b[?7l\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0m \r\u001b[65C \r\u001b[7A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[41.447447, "o", "\u001b[?25l\u001b[?7l\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;248;48;5;235mCtrl-D to exit; \r\u001b[65C \r\u001b[8A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[45.439479, "o", "\u001b[?25l\u001b[?7l\u001b[0mt \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TTL \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TIME \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TYPE \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m TOUCH \u001b[0;38;5;16;48;5;238m \u001b[4A\u001b[8D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[45.444328, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mype list:animals\u001b[0m \u001b[17D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[45.592128, "o", "\u001b[?25l\u001b[?7l\u001b[0my \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m TYPE \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[4A\u001b[17C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[45.59538, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mpe list:animals\u001b[0m \u001b[16D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[45.812073, "o", "\u001b[?25l\u001b[?7l\u001b[0mp \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m TYPE\u001b[C \u001b[0;38;5;16;48;5;238m \u001b[A\u001b[8D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[45.816301, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241me list:animals\u001b[0m \u001b[15D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[45.953551, "o", "\u001b[?25l\u001b[?7l\u001b[3D\u001b[0;38;5;28;1mtype\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m TYPE\u001b[C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;167;48;5;235;1m(generic) \u001b[0;38;5;28;48;5;235;1mTYPE\u001b[0;38;5;71;48;5;235m key\u001b[0;38;5;136;48;5;235m since: 1.0.0\u001b[0;38;5;241;48;5;235m complexity:O(1)\u001b[8A\u001b[29D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[45.958932, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m list:animals\u001b[0m \u001b[14D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[46.676064, "o", "\u001b[?25l\u001b[?7l\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m list:animals \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m cars \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m myset \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m kkk \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m somestream \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m af \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m hash2 \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[46.68263, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mlist:animals\u001b[0m \u001b[13D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[46.908434, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mlist:animals\u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;16;48;5;231m list:animals \u001b[A\u001b[5D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[46.912994, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[47.594674, "o", "\u001b[?25l\u001b[?7l\u001b[12D\u001b[0;38;5;71mcars\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m list:animals \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;16;48;5;231m cars \u001b[2A\u001b[13D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[47.599769, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[48.036896, "o", "\u001b[?25l\u001b[?7l\u001b[4D\u001b[0;38;5;71mmyset\u001b[0m \u001b[0m\r\r\n\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m cars \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;16;48;5;231m myset \u001b[3A\u001b[12D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[48.041407, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[48.555102, "o", "\u001b[?25l\u001b[?7l\u001b[5D\u001b[0;38;5;71mkkk\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\r\r\n\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m myset \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;16;48;5;231m kkk \u001b[4A\u001b[14D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[48.559223, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[48.744603, "o", "\u001b[?25l\u001b[?7l\u001b[3D\u001b[0;38;5;71msomestream\u001b[0m \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m kkk \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;16;48;5;231m somestream \u001b[5A\u001b[7D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[48.751509, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[48.895622, "o", "\u001b[?25l\u001b[?7l\u001b[10D\u001b[0;38;5;71maf\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[21C\u001b[0;38;5;231;48;5;30m somestream \u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;16;48;5;231m af \u001b[6A\u001b[15D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[48.900841, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[50.400124, "o", "\u001b[?25l\u001b[?7l\u001b[23D\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0;38;5;28;1mtype\u001b[0m \u001b[0;38;5;71maf\u001b[0m \r\u001b[65C \r\u001b[0m\r\r\n\u001b[J\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h\u001b[?2004l"]
|
||||||
|
[50.41034, "o", "\u001b[0m\u001b[?7h\u001b[0m\"string\"\u001b[0m\u001b[0m"]
|
||||||
|
[50.412186, "o", "\u001b[0m\u001b[?7h\u001b[0m\r\r\n\u001b[0m"]
|
||||||
|
[50.41283, "o", "\u001b[?1l"]
|
||||||
|
[50.41295, "o", "\u001b[6n"]
|
||||||
|
[50.415152, "o", "\u001b[?2004h\u001b[?25l\u001b[0m\u001b[?7l\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0m \r\u001b[65C \r\u001b[7A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[50.419664, "o", "\u001b[?25l\u001b[?7l\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;248;48;5;235mCtrl-D to exit; \r\u001b[65C \r\u001b[8A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[51.542536, "o", "\u001b[?25l\u001b[?7l\u001b[0mg \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m GET \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m GETSET \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m GETBIT \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m GEOPOS \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m GEOADD \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m GEOHASH \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m GEODIST \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[20D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[51.555077, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241met foo\u001b[0m \u001b[7D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[51.630144, "o", "\u001b[?25l\u001b[?7l\u001b[0me \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m GET\u001b[14C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m GETSET\u001b[11C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m GETBIT\u001b[11C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m GEOPOS\u001b[11C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m GEOA\u001b[CD\u001b[11C \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m GEOHASH\u001b[10C \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m GEODIST\u001b[10C \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[20D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[51.635334, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mt foo\u001b[0m \u001b[6D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[51.773743, "o", "\u001b[?25l\u001b[?7l\u001b[2D\u001b[0;38;5;28;1mget\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m GET\u001b[6C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m GETSET\u001b[3C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m GETBIT\u001b[3C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m GETRANGE \u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0m\r\r\n\u001b[0;38;5;167;48;5;235;1m(string) \u001b[0;38;5;28;48;5;235;1mGET\u001b[0;38;5;71;48;5;235m key\u001b[0;38;5;136;48;5;235m since: 1.0.0\u001b[0;38;5;241;48;5;235m complexity:O(1)\u001b[8A\u001b[28D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[51.77765, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m foo\u001b[0m \u001b[5D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[51.91592, "o", "\u001b[?25l\u001b[?7l\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m af\u001b[6C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m list:animals \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m cars \u001b[3C \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m myset \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[20C\u001b[0;38;5;231;48;5;30m kkk \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[20C\u001b[0;38;5;231;48;5;30m somestream \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[20C\u001b[0;38;5;231;48;5;30m hash2 \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[51.921861, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mfoo\u001b[0m \u001b[4D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[52.325226, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71ma\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30mf\u001b[13C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;231;48;5;30m \u001b[4C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30mbc\u001b[12C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mc\u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30mrs\u001b[11C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mh\u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30msh2\u001b[10C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mh\u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30msh3\u001b[0;38;5;231;48;5;30m \u001b[6C \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mh\u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30msh1"]
|
||||||
|
[52.32535, "o", "\u001b[10C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[52.329701, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[52.448681, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mf\u001b[0m \u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4maf\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[7A\u001b[17D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[52.453078, "o", "\u001b[?25l\u001b[?7l\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[A\u001b[21C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[52.892111, "o", "\u001b[?25l\u001b[?7l\u001b[22D\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0;38;5;28;1mget\u001b[0m \u001b[0;38;5;71maf\u001b[0m \r\u001b[65C \r\u001b[0m\r\r\n\u001b[J\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h\u001b[?2004l"]
|
||||||
|
[52.896263, "o", "\u001b[0m\u001b[?7h\u001b[0m\"asdf\"\u001b[0m\u001b[0m"]
|
||||||
|
[52.898075, "o", "\u001b[0m\u001b[?7h\u001b[0m\r\r\n\u001b[0m"]
|
||||||
|
[52.898599, "o", "\u001b[?1l\u001b[6n"]
|
||||||
|
[52.90232, "o", "\u001b[?2004h\u001b[?25l\u001b[0m\u001b[?7l\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0m \r\u001b[65C \r\u001b[7A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[52.905874, "o", "\u001b[?25l\u001b[?7l\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;248;48;5;235mCtrl-D to exit; \r\u001b[65C \r\u001b[8A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[53.943112, "o", "\u001b[?25l\u001b[?7l\u001b[0mg \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m GET \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m GETSET \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m GETBIT \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m GEOPOS \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m GEOADD \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m GEOHASH \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m GEODIST \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[20D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[53.9504, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241met af\u001b[0m \u001b[6D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[54.043955, "o", "\u001b[?25l\u001b[?7l\u001b[0me \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m GET\u001b[14C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m GETSET\u001b[11C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m GETBIT\u001b[11C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m GEOPOS\u001b[11C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m GEOA\u001b[CD\u001b[11C \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m GEOHASH\u001b[10C \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m GEODIST\u001b[10C \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[20D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[54.047839, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mt af\u001b[0m \u001b[5D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[54.192601, "o", "\u001b[?25l\u001b[?7l\u001b[2D\u001b[0;38;5;28;1mget\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m GET\u001b[6C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m GETSET\u001b[3C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m GETBIT\u001b[3C\u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m GETRANGE \u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0m\r\r\n\u001b[0;38;5;167;48;5;235;1m(string) \u001b[0;38;5;28;48;5;235;1mGET\u001b[0;38;5;71;48;5;235m key\u001b[0;38;5;136;48;5;235m since: 1.0.0\u001b[0;38;5;241;48;5;235m complexity:O(1)\u001b[8A\u001b[28D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[54.197235, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m af\u001b[0m \u001b[4D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[54.335864, "o", "\u001b[?25l\u001b[?7l\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m af\u001b[6C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m list:animals \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m cars \u001b[3C \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m myset \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[20C\u001b[0;38;5;231;48;5;30m kkk \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[20C\u001b[0;38;5;231;48;5;30m somestream \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[20C\u001b[0;38;5;231;48;5;30m hash2 \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[54.342312, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241maf\u001b[0m \u001b[3D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[54.76708, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71ma\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30mf\u001b[13C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;231;48;5;30m \u001b[4C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30mbc\u001b[12C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mc\u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30mrs\u001b[11C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mh\u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30msh2\u001b[10C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mh\u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30msh3\u001b[0;38;5;231;48;5;30m \u001b[6C \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mh\u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30msh1"]
|
||||||
|
[54.767202, "o", "\u001b[10C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[54.771478, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mf\u001b[0m \u001b[2D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[55.027261, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mf\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4maf\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0m \u001b[7A\u001b[17D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[55.03053, "o", "\u001b[?25l\u001b[?7l\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[A\u001b[21C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[55.228293, "o", "\u001b[?25l\u001b[?7l\u001b[C\u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[55.231931, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[55.573146, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;16;48;5;196mi\u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[55.577988, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[55.66374, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;16;48;5;196mn\u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[55.667964, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[55.984948, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;16;48;5;196mv\u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[55.98961, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[56.157229, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;16;48;5;196ma\u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[56.162529, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[56.423, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;16;48;5;196ml\u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[56.427638, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[56.586877, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;16;48;5;196mi\u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[56.593834, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[56.913957, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;16;48;5;196md\u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[56.918423, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[57.091248, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;16;48;5;196me\u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[57.095887, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[57.190026, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;16;48;5;196m \u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[57.1952, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[57.430926, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;16;48;5;196mi\u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[57.435255, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[57.500268, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;16;48;5;196mn\u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[57.506086, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[57.711636, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;16;48;5;196mp\u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[57.715911, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[57.984832, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;16;48;5;196mu\u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[57.992887, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[58.141271, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;16;48;5;196mt\u001b[0m \b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[58.145592, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.061523, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.073482, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.309508, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.315274, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.34596, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.350846, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.379097, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.38583, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.416019, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.431899, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.446868, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.45367, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.479192, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.483837, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.512893, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.519325, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.546693, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.550989, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.581696, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.586075, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.613113, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.617986, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.648799, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.652827, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.800181, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.805545, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.955905, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[59.959298, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.208194, "o", "\u001b[?25l\u001b[?7l\u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.212447, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.247807, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.254547, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.278, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.2824, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.309265, "o", "\u001b[?25l\u001b[?7l\u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.313357, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.342096, "o", "\u001b[?25l\u001b[?7l\u001b[3D\u001b[0mge \u001b[0m\u001b[K\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;248;48;5;235mCtrl-D to exit; \u001b[8A\u001b[29D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.352259, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.377642, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.38131, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.408313, "o", "\u001b[?25l\u001b[?7l\b\u001b[0m \u001b[0m\u001b[K\b\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.412424, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.449815, "o", "\u0007"]
|
||||||
|
[60.453021, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[60.473651, "o", "\u0007"]
|
||||||
|
[60.476542, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[62.514509, "o", "\u001b[?25l\u001b[?7l\u001b[0md \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m DEL \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m DECR \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m DUMP \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m DECRBY \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m DBSIZE \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m DISCARD \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0;38;5;231;48;5;30m DEBUG OBJECT \u001b[0;38;5;16;48;5;37m \u001b[7A\u001b[17D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[62.520623, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241mel _kombu.binding.celeryev\u001b[0m \u001b[27D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[62.683241, "o", "\u001b[?25l\u001b[?7l\u001b[0me \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m DEL\u001b[11C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m DECR\u001b[10C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m DECRBY\u001b[8C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m DEBUG\u001b[COBJECT\u001b[2C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[0;38;5;231;48;5;30m DEBUG SEGFAULT \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[17C\u001b[0m \u001b[7A\u001b[16D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[62.686861, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241ml _kombu.binding.celeryev\u001b[0m \u001b[26D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[62.885578, "o", "\u001b[?25l\u001b[?7l\u001b[2D\u001b[0;38;5;28;1mdel\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[18C\u001b[0m \u001b[0;38;5;231;48;5;30m DEL \u001b[0;38;5;16;48;5;238m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[C\u001b[0m\u001b[K\u001b[0m\r\r\n\r\r\n\r\r\n\u001b[0;38;5;167;48;5;235;1m(generic) \u001b[0;38;5;28;48;5;235;1mDEL\u001b[0;38;5;71;48;5;235m key\u001b[0;38;5;136;48;5;235m since: 1.0.0\u001b[0;38;5;241;48;5;235m complexity:O(N) where N is the n\r\u001b[65Cu\u001b[8A\r\u001b[19C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[62.890125, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m _kombu.binding.celeryev\u001b[0m \u001b[25D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[63.433741, "o", "\u001b[?25l\u001b[?7l\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[19C\u001b[0m \u001b[0;38;5;231;48;5;30m af \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[20C\u001b[0;38;5;231;48;5;30m list:animals \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[20C\u001b[0;38;5;231;48;5;30m cars \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[20C\u001b[0;38;5;231;48;5;30m myset \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[20C\u001b[0;38;5;231;48;5;30m kkk \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[20C\u001b[0;38;5;231;48;5;30m somestream \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[20C\u001b[0;38;5;231;48;5;30m hash2 \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[63.438223, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m_kombu.binding.celeryev\u001b[0m \u001b[24D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[64.652788, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71ma\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30mf\u001b[13C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;231;48;5;30m \u001b[4C \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30mbc\u001b[12C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mc\u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30mrs\u001b[11C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;238m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mh\u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30msh2\u001b[10C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;37m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mh\u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30msh3\u001b[0;38;5;231;48;5;30m \u001b[6C \u001b[0;38;5;16;48;5;248m \u001b[0m\r\r\n\u001b[20C\u001b[0m \u001b[0;38;5;231;48;5;30m \u001b[0;38;5;238;48;5;30mh\u001b[0;38;5;231;48;5;30;1;4ma\u001b[0;38;5;238;48;5;30msh1"]
|
||||||
|
[64.652904, "o", "\u001b[10C\u001b[0;38;5;231;48;5;30m \u001b[0;38;5;16;48;5;248m \u001b[7A\u001b[18D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[64.66165, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;241m\\\"bc\u001b[0m \u001b[5D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[65.254736, "o", "\u001b[?25l\u001b[?7l\u001b[0;38;5;71mf\u001b[0m \u001b[0m\u001b[K\u001b[0m\r\r\n\u001b[21C\u001b[0;38;5;16;48;5;231m \u001b[0;38;5;16;48;5;231;4ma\u001b[0;48;5;231mf\u001b[0;38;5;16;48;5;231m \u001b[A\u001b[16D\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[65.26029, "o", "\u001b[?25l\u001b[?7l\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[65.761821, "o", "\u001b[?25l\u001b[?7l\u001b[22D\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0;38;5;28;1mdel\u001b[0m \u001b[0;38;5;71maf\u001b[0m \r\u001b[65C \r\u001b[0m\r\r\n\u001b[J\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h\u001b[?2004l"]
|
||||||
|
[65.766399, "o", "\u001b[0m\u001b[?7h\u001b[0;38;5;102m(integer) \u001b[0m1\u001b[0m\u001b[0m"]
|
||||||
|
[65.768122, "o", "\u001b[0m\u001b[?7h\u001b[0m\r\r\n\u001b[0m"]
|
||||||
|
[65.768819, "o", "\u001b[?1l\u001b[6n"]
|
||||||
|
[65.771167, "o", "\u001b[?2004h\u001b[?25l\u001b[0m\u001b[?7l\u001b[0m\u001b[J\u001b[0m127.0.0.1:6379> \u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0m \r\u001b[65C \r\u001b[7A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[65.774133, "o", "\u001b[?25l\u001b[?7l\u001b[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\u001b[0;38;5;248;48;5;235mCtrl-D to exit; \r\u001b[65C \r\u001b[8A\u001b[16C\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h"]
|
||||||
|
[66.838972, "o", "\u001b[?25l\u001b[?7l\u001b[16D\u001b[0m\u001b[J\u001b[0;38;5;102m127.0.0.1:6379> \r\u001b[65C \r\u001b[0m\r\r\n\u001b[J\u001b[?7h\u001b[0m\u001b[?12l\u001b[?25h\u001b[?2004l"]
|
||||||
|
[66.839665, "o", "Goodbye!\r\n"]
|
||||||
|
[66.871374, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||||
|
[66.893132, "o", "\u001b]133;D;0\u0007\u001b]1337;RemoteHost=laixintao@Chico.local\u0007\u001b]1337;CurrentDir=/Users/laixintao\u0007"]
|
||||||
|
[66.896847, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b]133;A\u0007$ \u001b]133;B\u0007\u001b[K"]
|
||||||
|
[66.897062, "o", "\u001b[?1h\u001b="]
|
||||||
|
[66.897178, "o", "\u001b[?2004h"]
|
||||||
|
[67.725923, "o", "\u001b[?2004l\r\r\n"]
|
214
docs/assets/demo.svg
Normal file
214
docs/assets/demo.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 297 KiB |
BIN
docs/assets/logo.png
Normal file
BIN
docs/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
7
docs/assets/render.md
Normal file
7
docs/assets/render.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Render using [termtosvg](https://github.com/nbedos/termtosvg/blob/develop/man/termtosvg.md):
|
||||||
|
|
||||||
|
```
|
||||||
|
termtosvg render demo.cast demo1.svg -D 2 -m40 -M300 -t progress_bar
|
||||||
|
```
|
||||||
|
|
||||||
|
size: 66x20
|
3
docs/cloudshell/run-in-docker.txt
Normal file
3
docs/cloudshell/run-in-docker.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Try redis in docker(which contains a redis-server):
|
||||||
|
|
||||||
|
docker build -t iredis . && docker run -it iredis
|
9
docs/update-redis-doc.md
Normal file
9
docs/update-redis-doc.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# How to Catch Up with Latest Redis-doc
|
||||||
|
|
||||||
|
1. `git pull` in submodule.
|
||||||
|
2. Overwrite `iredis/data/commands.json`.
|
||||||
|
3. Diff with old `commands.json`, make the changes.
|
||||||
|
4. `mv redis-doc/commands/*.md iredis/data/commands`
|
||||||
|
5. `prettier --write --prose-wrap always iredis/data/commands/*.md`
|
||||||
|
|
||||||
|
Done!
|
1
iredis/__init__.py
Normal file
1
iredis/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
__version__ = "1.9.1"
|
35
iredis/bottom.py
Normal file
35
iredis/bottom.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import logging
|
||||||
|
from .commands import commands_summary
|
||||||
|
from .utils import command_syntax
|
||||||
|
|
||||||
|
BUTTOM_TEXT = "Ctrl-D to exit;"
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class BottomToolbar:
|
||||||
|
CHAR = "⣾⣷⣯⣟⡿⢿⣻⣽"
|
||||||
|
|
||||||
|
def __init__(self, command_holder):
|
||||||
|
self.index = 0
|
||||||
|
# BottomToolbar can only read this variable
|
||||||
|
self.command_holder = command_holder
|
||||||
|
|
||||||
|
def get_animation_char(self):
|
||||||
|
animation = self.CHAR[self.index]
|
||||||
|
|
||||||
|
self.index += 1
|
||||||
|
if self.index == len(self.CHAR):
|
||||||
|
self.index = 0
|
||||||
|
return animation
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
text = BUTTOM_TEXT
|
||||||
|
# add command help if valide
|
||||||
|
if self.command_holder.command:
|
||||||
|
try:
|
||||||
|
command_info = commands_summary[self.command_holder.command]
|
||||||
|
text = command_syntax(self.command_holder.command, command_info)
|
||||||
|
except KeyError as e:
|
||||||
|
logger.exception(e)
|
||||||
|
pass
|
||||||
|
return text
|
685
iredis/client.py
Normal file
685
iredis/client.py
Normal file
|
@ -0,0 +1,685 @@
|
||||||
|
"""
|
||||||
|
IRedis client.
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
from subprocess import run
|
||||||
|
from importlib_resources import read_text
|
||||||
|
from distutils.version import StrictVersion
|
||||||
|
|
||||||
|
import redis
|
||||||
|
from prompt_toolkit.shortcuts import clear
|
||||||
|
from prompt_toolkit.formatted_text import FormattedText
|
||||||
|
from redis.connection import Connection, SSLConnection, UnixDomainSocketConnection
|
||||||
|
from redis.exceptions import (
|
||||||
|
AuthenticationError,
|
||||||
|
ConnectionError,
|
||||||
|
TimeoutError,
|
||||||
|
ResponseError,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
from . import markdown, renders
|
||||||
|
from .data import commands as commands_data
|
||||||
|
from .commands import (
|
||||||
|
command2callback,
|
||||||
|
commands_summary,
|
||||||
|
command2syntax,
|
||||||
|
groups,
|
||||||
|
split_command_args,
|
||||||
|
split_unknown_args,
|
||||||
|
)
|
||||||
|
from .completers import IRedisCompleter
|
||||||
|
from .config import config
|
||||||
|
from .exceptions import NotRedisCommand, InvalidArguments, AmbiguousCommand, NotSupport
|
||||||
|
from .renders import OutputRender
|
||||||
|
from .utils import (
|
||||||
|
compose_command_syntax,
|
||||||
|
nativestr,
|
||||||
|
exit,
|
||||||
|
convert_formatted_text_to_bytes,
|
||||||
|
parse_url,
|
||||||
|
)
|
||||||
|
from .warning import confirm_dangerous_command
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
CLIENT_COMMANDS = groups["iredis"]
|
||||||
|
|
||||||
|
|
||||||
|
class Client:
|
||||||
|
"""
|
||||||
|
iRedis client, hold a redis-py Client to interact with Redis.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
host=None,
|
||||||
|
port=None,
|
||||||
|
db=0,
|
||||||
|
password=None,
|
||||||
|
path=None,
|
||||||
|
scheme="redis",
|
||||||
|
username=None,
|
||||||
|
):
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.db = db
|
||||||
|
self.path = path
|
||||||
|
# FIXME username is not using...
|
||||||
|
self.username = username
|
||||||
|
self.scheme = scheme
|
||||||
|
|
||||||
|
self.connection = self.create_connection(
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
db,
|
||||||
|
password,
|
||||||
|
path,
|
||||||
|
scheme,
|
||||||
|
username,
|
||||||
|
)
|
||||||
|
|
||||||
|
# all command upper case
|
||||||
|
self.answer_callbacks = command2callback
|
||||||
|
self.set_default_pager(config)
|
||||||
|
try:
|
||||||
|
self.connection.connect()
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e), file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
if not config.no_info:
|
||||||
|
try:
|
||||||
|
self.get_server_info()
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"[After Connection] {str(e)}")
|
||||||
|
config.no_version_reason = str(e)
|
||||||
|
else:
|
||||||
|
config.no_version_reason = "--no-info flag activated"
|
||||||
|
|
||||||
|
if config.version and re.match(r"([\d\.]+)", config.version):
|
||||||
|
self.auth_compat(config.version)
|
||||||
|
|
||||||
|
def create_connection(
|
||||||
|
self,
|
||||||
|
host=None,
|
||||||
|
port=None,
|
||||||
|
db=0,
|
||||||
|
password=None,
|
||||||
|
path=None,
|
||||||
|
scheme="redis",
|
||||||
|
username=None,
|
||||||
|
):
|
||||||
|
if scheme in ("redis", "rediss"):
|
||||||
|
connection_kwargs = {
|
||||||
|
"host": host,
|
||||||
|
"port": port,
|
||||||
|
"db": db,
|
||||||
|
"password": password,
|
||||||
|
"socket_keepalive": config.socket_keepalive,
|
||||||
|
}
|
||||||
|
if scheme == "rediss":
|
||||||
|
connection_class = SSLConnection
|
||||||
|
else:
|
||||||
|
connection_class = Connection
|
||||||
|
else:
|
||||||
|
connection_kwargs = {"db": db, "password": password, "path": path}
|
||||||
|
connection_class = UnixDomainSocketConnection
|
||||||
|
|
||||||
|
if config.decode:
|
||||||
|
connection_kwargs["encoding"] = config.decode
|
||||||
|
connection_kwargs["decode_responses"] = True
|
||||||
|
connection_kwargs["encoding_errors"] = "replace"
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
f"connection_class={connection_class}, connection_kwargs={connection_kwargs}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return connection_class(**connection_kwargs)
|
||||||
|
|
||||||
|
def auth_compat(self, redis_version: str):
|
||||||
|
with_username = StrictVersion(redis_version) >= StrictVersion("6.0.0")
|
||||||
|
if with_username:
|
||||||
|
command2syntax["AUTH"] = "command_usernamex_password"
|
||||||
|
|
||||||
|
def set_default_pager(self, config):
|
||||||
|
configured_pager = config.pager
|
||||||
|
os_environ_pager = os.environ.get("PAGER")
|
||||||
|
|
||||||
|
if configured_pager:
|
||||||
|
logger.info('Default pager found in config file: "%s"', configured_pager)
|
||||||
|
os.environ["PAGER"] = configured_pager
|
||||||
|
elif os_environ_pager:
|
||||||
|
logger.info(
|
||||||
|
'Default pager found in PAGER environment variable: "%s"',
|
||||||
|
os_environ_pager,
|
||||||
|
)
|
||||||
|
os.environ["PAGER"] = os_environ_pager
|
||||||
|
else:
|
||||||
|
logger.info("No default pager found in environment. Using os default pager")
|
||||||
|
|
||||||
|
# Set default set of less recommended options, if they are not already set.
|
||||||
|
# They are ignored if pager is different than less.
|
||||||
|
if not os.environ.get("LESS"):
|
||||||
|
os.environ["LESS"] = "-SRXF"
|
||||||
|
|
||||||
|
def get_server_info(self):
|
||||||
|
# safe to decode Redis's INFO response
|
||||||
|
info_resp = nativestr(self.execute("INFO"))
|
||||||
|
version = re.findall(r"^redis_version:([\d\.]+)\r\n", info_resp, re.MULTILINE)[
|
||||||
|
0
|
||||||
|
]
|
||||||
|
logger.debug(f"[Redis Version] {version}")
|
||||||
|
config.version = version
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.scheme == "unix":
|
||||||
|
prompt = f"redis {self.path}"
|
||||||
|
else:
|
||||||
|
prompt = f"{self.host}:{self.port}"
|
||||||
|
|
||||||
|
if self.db:
|
||||||
|
prompt = f"{prompt}[{self.db}]"
|
||||||
|
return prompt
|
||||||
|
|
||||||
|
def client_execute_command(self, command_name, *args):
|
||||||
|
command = command_name.upper()
|
||||||
|
if command == "HELP":
|
||||||
|
yield self.do_help(*args)
|
||||||
|
if command == "PEEK":
|
||||||
|
yield from self.do_peek(*args)
|
||||||
|
if command == "CLEAR":
|
||||||
|
clear()
|
||||||
|
if command == "EXIT":
|
||||||
|
exit()
|
||||||
|
|
||||||
|
def execute(self, *args, **kwargs):
|
||||||
|
logger.info(
|
||||||
|
f"execute: connection={self.connection} args={args}, kwargs={kwargs}"
|
||||||
|
)
|
||||||
|
return self.execute_by_connection(self.connection, *args, **kwargs)
|
||||||
|
|
||||||
|
def execute_by_connection(self, connection, command_name, *args, **options):
|
||||||
|
"""Execute a command and return a parsed response
|
||||||
|
Here we retry once for ConnectionError.
|
||||||
|
"""
|
||||||
|
logger.info(
|
||||||
|
f"execute by connection: connection={connection}, name={command_name}, {args}, {options}"
|
||||||
|
)
|
||||||
|
retry_times = config.retry_times # FIXME configureable
|
||||||
|
last_error = None
|
||||||
|
need_refresh_connection = False
|
||||||
|
|
||||||
|
while retry_times >= 0:
|
||||||
|
try:
|
||||||
|
if need_refresh_connection:
|
||||||
|
print(
|
||||||
|
f"{str(last_error)} retrying... retry left: {retry_times+1}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
connection.disconnect()
|
||||||
|
connection.connect()
|
||||||
|
logger.info(f"New connection created, retry on {connection}.")
|
||||||
|
logger.info(f"send_command: {command_name} , {args}")
|
||||||
|
connection.send_command(command_name, *args)
|
||||||
|
response = connection.read_response()
|
||||||
|
except AuthenticationError:
|
||||||
|
raise
|
||||||
|
except (ConnectionError, TimeoutError) as e:
|
||||||
|
logger.warning(f"Connection Error, got {e}, retrying...")
|
||||||
|
last_error = e
|
||||||
|
retry_times -= 1
|
||||||
|
need_refresh_connection = True
|
||||||
|
except (ResponseError) as e:
|
||||||
|
response_message = str(e)
|
||||||
|
if response_message.startswith("MOVED"):
|
||||||
|
return self.reissue_with_redirect(
|
||||||
|
response_message, command_name, *args, **options
|
||||||
|
)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
except redis.exceptions.ExecAbortError:
|
||||||
|
config.transaction = False
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
return response
|
||||||
|
raise last_error
|
||||||
|
|
||||||
|
def reissue_with_redirect(self, response, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
For redis cluster, when server response a "MOVE ..." response, we auto-
|
||||||
|
redirect to the target node, reissue the original command.
|
||||||
|
|
||||||
|
This feature is not supported for unix socket connection.
|
||||||
|
"""
|
||||||
|
# Redis Cluster only supports database zero.
|
||||||
|
_, slot, ip_port = response.split(" ")
|
||||||
|
ip, port = ip_port.split(":")
|
||||||
|
port = int(port)
|
||||||
|
|
||||||
|
print(response, file=sys.stderr)
|
||||||
|
|
||||||
|
connection = self.create_connection(ip, port)
|
||||||
|
# if user sets dsn for dest node
|
||||||
|
# use username and password from dsn settings
|
||||||
|
if config.alias_dsn:
|
||||||
|
for dsn_name, dsn_url in config.alias_dsn.items():
|
||||||
|
dsn = parse_url(dsn_url)
|
||||||
|
if dsn.host == ip and dsn.port == port:
|
||||||
|
print(
|
||||||
|
f"Connect {ip}:{port} via dns settings of {dsn_name}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
connection = self.create_connection(
|
||||||
|
dsn.host,
|
||||||
|
dsn.port,
|
||||||
|
dsn.db,
|
||||||
|
dsn.password,
|
||||||
|
dsn.path,
|
||||||
|
dsn.scheme,
|
||||||
|
dsn.username,
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
connection.connect()
|
||||||
|
return self.execute_by_connection(connection, *args, **kwargs)
|
||||||
|
|
||||||
|
def render_response(self, response, command_name):
|
||||||
|
"Parses a response from the Redis server"
|
||||||
|
logger.info(f"[Redis-Server] Response: {response}")
|
||||||
|
if config.raw:
|
||||||
|
callback = OutputRender.render_raw
|
||||||
|
# if in transaction, use queue render first
|
||||||
|
elif config.transaction:
|
||||||
|
callback = renders.OutputRender.render_transaction_queue
|
||||||
|
else:
|
||||||
|
callback = OutputRender.get_render(command_name=command_name)
|
||||||
|
rendered = callback(response)
|
||||||
|
logger.info(f"[render result] {rendered}")
|
||||||
|
return rendered
|
||||||
|
|
||||||
|
def monitor(self):
|
||||||
|
"""Redis' MONITOR command:
|
||||||
|
https://redis.io/commands/monitor
|
||||||
|
This command need to read from a stream resp, so
|
||||||
|
it's different
|
||||||
|
"""
|
||||||
|
while 1:
|
||||||
|
response = self.connection.read_response()
|
||||||
|
if config.raw:
|
||||||
|
yield OutputRender.render_raw(response)
|
||||||
|
else:
|
||||||
|
yield OutputRender.render_bulk_string_decode(response)
|
||||||
|
|
||||||
|
def subscribing(self):
|
||||||
|
while 1:
|
||||||
|
response = self.connection.read_response()
|
||||||
|
if config.raw:
|
||||||
|
yield OutputRender.render_raw(response)
|
||||||
|
else:
|
||||||
|
yield OutputRender.render_subscribe(response)
|
||||||
|
|
||||||
|
def unsubscribing(self):
|
||||||
|
"unsubscribe from all channels"
|
||||||
|
response = self.execute("UNSUBSCRIBE")
|
||||||
|
if config.raw:
|
||||||
|
yield OutputRender.render_raw(response)
|
||||||
|
else:
|
||||||
|
yield OutputRender.render_subscribe(response)
|
||||||
|
|
||||||
|
def split_command_and_pipeline(self, rawinput, completer: IRedisCompleter):
|
||||||
|
"""
|
||||||
|
split user raw input to redis command and shell pipeline.
|
||||||
|
eg:
|
||||||
|
GET json | jq .key
|
||||||
|
return: GET json, jq . key
|
||||||
|
"""
|
||||||
|
grammar = completer.get_completer(input_text=rawinput).compiled_grammar
|
||||||
|
matched = grammar.match(rawinput)
|
||||||
|
if not matched:
|
||||||
|
# invalide command!
|
||||||
|
return rawinput, None
|
||||||
|
variables = matched.variables()
|
||||||
|
shell_command = variables.get("shellcommand")
|
||||||
|
if shell_command:
|
||||||
|
redis_command = rawinput.replace(shell_command, "")
|
||||||
|
shell_command = shell_command.lstrip("| ")
|
||||||
|
return redis_command, shell_command
|
||||||
|
return rawinput, None
|
||||||
|
|
||||||
|
def send_command(self, raw_command, completer=None): # noqa
|
||||||
|
"""
|
||||||
|
Send raw_command to redis-server, return parsed response.
|
||||||
|
|
||||||
|
:param raw_command: text raw_command, not parsed
|
||||||
|
:param completer: RedisGrammarCompleter will update completer
|
||||||
|
based on redis response. eg: update key completer after ``keys``
|
||||||
|
raw_command
|
||||||
|
"""
|
||||||
|
if completer is None: # not in a tty
|
||||||
|
redis_command, shell_command = raw_command, None
|
||||||
|
else:
|
||||||
|
redis_command, shell_command = self.split_command_and_pipeline(
|
||||||
|
raw_command, completer
|
||||||
|
)
|
||||||
|
logger.info(f"[Prepare command] Redis: {redis_command}, Shell: {shell_command}")
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
command_name, args = split_command_args(redis_command)
|
||||||
|
except (InvalidArguments, AmbiguousCommand):
|
||||||
|
logger.warn(
|
||||||
|
"This is not a iredis known command, send to redis-server anyway..."
|
||||||
|
)
|
||||||
|
command_name, args = split_unknown_args(redis_command)
|
||||||
|
|
||||||
|
logger.info(f"[Split command] command: {command_name}, args: {args}")
|
||||||
|
input_command_upper = command_name.upper()
|
||||||
|
# Confirm for dangerous command
|
||||||
|
if config.warning:
|
||||||
|
confirm = confirm_dangerous_command(input_command_upper)
|
||||||
|
if confirm is True:
|
||||||
|
print("Your Call!!", file=sys.stderr)
|
||||||
|
elif confirm is False:
|
||||||
|
print("Canceled!", file=sys.stderr)
|
||||||
|
return
|
||||||
|
# None: continue...
|
||||||
|
|
||||||
|
self.pre_hook(raw_command, command_name, args, completer)
|
||||||
|
# if raw_command is not supposed to send to server
|
||||||
|
if input_command_upper in CLIENT_COMMANDS:
|
||||||
|
logger.info(f"{input_command_upper} is an iredis command.")
|
||||||
|
yield from self.client_execute_command(command_name, *args)
|
||||||
|
return
|
||||||
|
|
||||||
|
redis_resp = self.execute(command_name, *args)
|
||||||
|
# if shell_command and enable shell, do not render, just run in shell pipe and show the
|
||||||
|
# subcommand's stdout/stderr
|
||||||
|
if shell_command and config.shell:
|
||||||
|
# pass the raw response of redis to shell command
|
||||||
|
if isinstance(redis_resp, list):
|
||||||
|
# FIXME not handling nested list, use renders.render_raw
|
||||||
|
# instead
|
||||||
|
stdin = b"\n".join(redis_resp)
|
||||||
|
else:
|
||||||
|
stdin = redis_resp
|
||||||
|
run(shell_command, input=stdin, shell=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.after_hook(raw_command, command_name, args, completer, redis_resp)
|
||||||
|
yield self.render_response(redis_resp, command_name)
|
||||||
|
|
||||||
|
# FIXME generator response do not support pipeline
|
||||||
|
if input_command_upper == "MONITOR":
|
||||||
|
# TODO special render for monitor
|
||||||
|
try:
|
||||||
|
yield from self.monitor()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
elif input_command_upper in [
|
||||||
|
"SUBSCRIBE",
|
||||||
|
"PSUBSCRIBE",
|
||||||
|
]: # enter subscribe mode
|
||||||
|
try:
|
||||||
|
yield from self.subscribing()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
yield from self.unsubscribing()
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(e)
|
||||||
|
if config.raw:
|
||||||
|
render_callback = OutputRender.render_raw
|
||||||
|
else:
|
||||||
|
render_callback = OutputRender.render_error
|
||||||
|
yield render_callback(f"ERROR {str(e)}".encode())
|
||||||
|
finally:
|
||||||
|
config.withscores = False
|
||||||
|
|
||||||
|
def after_hook(self, command, command_name, args, completer, response):
|
||||||
|
# === After hook ===
|
||||||
|
# SELECT db on AUTH
|
||||||
|
if command_name.upper() == "AUTH":
|
||||||
|
if self.db:
|
||||||
|
select_result = self.execute("SELECT", self.db)
|
||||||
|
if nativestr(select_result) != "OK":
|
||||||
|
raise ConnectionError("Invalid Database")
|
||||||
|
# When the connection is TimeoutError or ConnectionError, reconnect the connection will use it
|
||||||
|
self.connection.password = args[0]
|
||||||
|
elif command_name.upper() == "SELECT":
|
||||||
|
logger.debug("[After hook] Command is SELECT, change self.db.")
|
||||||
|
self.db = int(args[0])
|
||||||
|
# When the connection is TimeoutError or ConnectionError, reconnect the connection will use it
|
||||||
|
self.connection.db = self.db
|
||||||
|
elif command_name.upper() == "MULTI":
|
||||||
|
logger.debug("[After hook] Command is MULTI, start transaction.")
|
||||||
|
config.transaction = True
|
||||||
|
|
||||||
|
if completer:
|
||||||
|
completer.update_completer_for_response(command_name, args, response)
|
||||||
|
|
||||||
|
def pre_hook(self, command, command_name, args, completer: IRedisCompleter):
|
||||||
|
"""
|
||||||
|
Before execute command, patch completers first.
|
||||||
|
Eg: When user run `GET foo`, key completer need to
|
||||||
|
touch foo.
|
||||||
|
|
||||||
|
Only works when compile-grammar thread is done.
|
||||||
|
"""
|
||||||
|
if command_name.upper() == "HELLO":
|
||||||
|
raise NotSupport("IRedis currently not support RESP3, sorry about that.")
|
||||||
|
# TRANSATION state chage
|
||||||
|
if command_name.upper() in ["EXEC", "DISCARD"]:
|
||||||
|
logger.debug(f"[After hook] Command is {command_name}, unset transaction.")
|
||||||
|
config.transaction = False
|
||||||
|
# score display for sorted set
|
||||||
|
if command_name.upper() in ["ZSCAN", "ZPOPMAX", "ZPOPMIN"]:
|
||||||
|
config.withscores = True
|
||||||
|
|
||||||
|
# not a tty
|
||||||
|
if not completer:
|
||||||
|
logger.warning(
|
||||||
|
"[Pre patch completer] Complter is None, not a tty, "
|
||||||
|
"not patch completers, not set withscores"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
completer.update_completer_for_input(command)
|
||||||
|
|
||||||
|
redis_grammar = completer.get_completer(command).compiled_grammar
|
||||||
|
m = redis_grammar.match(command)
|
||||||
|
if not m:
|
||||||
|
# invalide command!
|
||||||
|
return
|
||||||
|
variables = m.variables()
|
||||||
|
# zset withscores
|
||||||
|
withscores = variables.get("withscores")
|
||||||
|
if withscores:
|
||||||
|
config.withscores = True
|
||||||
|
|
||||||
|
def do_help(self, *args):
|
||||||
|
command_docs_name = "-".join(args).lower()
|
||||||
|
command_summary_name = " ".join(args).upper()
|
||||||
|
try:
|
||||||
|
doc = read_text(commands_data, f"{command_docs_name}.md")
|
||||||
|
except FileNotFoundError:
|
||||||
|
raise NotRedisCommand(
|
||||||
|
f"{command_summary_name} is not a valide Redis command."
|
||||||
|
)
|
||||||
|
rendered_detail = markdown.render(doc)
|
||||||
|
summary_dict = commands_summary[command_summary_name]
|
||||||
|
|
||||||
|
avaiable_version = summary_dict.get("since", "?")
|
||||||
|
server_version = config.version
|
||||||
|
# FIXME anything strange with single quotes?
|
||||||
|
logger.debug(f"[--version--] '{server_version}'")
|
||||||
|
try:
|
||||||
|
is_avaiable = StrictVersion(server_version) > StrictVersion(
|
||||||
|
avaiable_version
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(e)
|
||||||
|
is_avaiable = None
|
||||||
|
|
||||||
|
if is_avaiable:
|
||||||
|
avaiable_text = f"(Avaiable on your redis-server: {server_version})"
|
||||||
|
elif is_avaiable is False:
|
||||||
|
avaiable_text = f"(Not avaiable on your redis-server: {server_version})"
|
||||||
|
else:
|
||||||
|
avaiable_text = ""
|
||||||
|
since_text = f"{avaiable_version} {avaiable_text}"
|
||||||
|
|
||||||
|
summary = [
|
||||||
|
("", "\n"),
|
||||||
|
("class:doccommand", " " + command_summary_name),
|
||||||
|
("", "\n"),
|
||||||
|
("class:dockey", " summary: "),
|
||||||
|
("", summary_dict.get("summary", "No summary")),
|
||||||
|
("", "\n"),
|
||||||
|
("class:dockey", " complexity: "),
|
||||||
|
("", summary_dict.get("complexity", "?")),
|
||||||
|
("", "\n"),
|
||||||
|
("class:dockey", " since: "),
|
||||||
|
("", since_text),
|
||||||
|
("", "\n"),
|
||||||
|
("class:dockey", " group: "),
|
||||||
|
("", summary_dict.get("group", "?")),
|
||||||
|
("", "\n"),
|
||||||
|
("class:dockey", " syntax: "),
|
||||||
|
("", command_summary_name), # command
|
||||||
|
*compose_command_syntax(summary_dict, style_class=""), # command args
|
||||||
|
("", "\n\n"),
|
||||||
|
]
|
||||||
|
|
||||||
|
to_render = FormattedText(summary + rendered_detail)
|
||||||
|
if config.raw:
|
||||||
|
return convert_formatted_text_to_bytes(to_render)
|
||||||
|
return to_render
|
||||||
|
|
||||||
|
def do_peek(self, key):
|
||||||
|
"""
|
||||||
|
PEEK command implementation.
|
||||||
|
|
||||||
|
It's a generator, will run different redis commands based on the key's
|
||||||
|
type, yields FormattedText once a command reached result.
|
||||||
|
|
||||||
|
Redis current supported types:
|
||||||
|
string, list, set, zset, hash and stream.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _string(key):
|
||||||
|
strlen = self.execute("strlen", key)
|
||||||
|
yield FormattedText([("class:dockey", "strlen: "), ("", str(strlen))])
|
||||||
|
|
||||||
|
value = self.execute("GET", key)
|
||||||
|
yield FormattedText(
|
||||||
|
[
|
||||||
|
("class:dockey", "value: "),
|
||||||
|
("", renders.OutputRender.render_bulk_string(value)),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def _list(key):
|
||||||
|
llen = self.execute("llen", key)
|
||||||
|
yield FormattedText([("class:dockey", "llen: "), ("", str(llen))])
|
||||||
|
if llen <= 20:
|
||||||
|
contents = self.execute(f"LRANGE {key} 0 -1")
|
||||||
|
else:
|
||||||
|
first_10 = self.execute(f"LRANGE {key} 0 9")
|
||||||
|
last_10 = self.execute(f"LRANGE {key} -10 -1")
|
||||||
|
contents = first_10 + [f"{llen-20} elements was omitted ..."] + last_10
|
||||||
|
yield FormattedText([("class:dockey", "elements: ")])
|
||||||
|
yield renders.OutputRender.render_list(contents)
|
||||||
|
|
||||||
|
def _set(key):
|
||||||
|
cardinality = self.execute("scard", key)
|
||||||
|
yield FormattedText(
|
||||||
|
[("class:dockey", "cardinality: "), ("", str(cardinality))]
|
||||||
|
)
|
||||||
|
if cardinality <= 20:
|
||||||
|
contents = self.execute("smembers", key)
|
||||||
|
yield FormattedText([("class:dockey", "members: ")])
|
||||||
|
yield renders.OutputRender.render_list(contents)
|
||||||
|
else:
|
||||||
|
_, contents = self.execute(f"sscan {key} 0 count 20")
|
||||||
|
first_n = len(contents)
|
||||||
|
yield FormattedText([("class:dockey", f"members (first {first_n}): ")])
|
||||||
|
yield renders.OutputRender.render_members(contents)
|
||||||
|
# TODO update completers
|
||||||
|
|
||||||
|
def _zset(key):
|
||||||
|
count = self.execute(f"zcount {key} -inf +inf")
|
||||||
|
yield FormattedText([("class:dockey", "zcount: "), ("", str(count))])
|
||||||
|
if count <= 20:
|
||||||
|
contents = self.execute(f"zrange {key} 0 -1 withscores")
|
||||||
|
yield FormattedText([("class:dockey", "members: ")])
|
||||||
|
yield renders.OutputRender.render_members(contents)
|
||||||
|
else:
|
||||||
|
_, contents = self.execute(f"zscan {key} 0 count 20")
|
||||||
|
first_n = len(contents) // 2
|
||||||
|
yield FormattedText([("class:dockey", f"members (first {first_n}): ")])
|
||||||
|
config.withscores = True
|
||||||
|
output = renders.OutputRender.render_members(contents)
|
||||||
|
config.withscores = False
|
||||||
|
yield output
|
||||||
|
|
||||||
|
def _hash(key):
|
||||||
|
hlen = self.execute(f"hlen {key}")
|
||||||
|
yield FormattedText([("class:dockey", "hlen: "), ("", str(hlen))])
|
||||||
|
if hlen <= 20:
|
||||||
|
contents = self.execute(f"hgetall {key}")
|
||||||
|
yield FormattedText([("class:dockey", "fields: ")])
|
||||||
|
else:
|
||||||
|
_, contents = self.execute(f"hscan {key} 0 count 20")
|
||||||
|
first_n = len(contents) // 2
|
||||||
|
yield FormattedText([("class:dockey", f"fields (first {first_n}): ")])
|
||||||
|
yield renders.OutputRender.render_hash_pairs(contents)
|
||||||
|
|
||||||
|
def _stream(key):
|
||||||
|
xinfo = self.execute("xinfo stream", key)
|
||||||
|
yield FormattedText([("class:dockey", "XINFO: ")])
|
||||||
|
yield renders.OutputRender.render_list(xinfo)
|
||||||
|
|
||||||
|
# incase the result is too long, we yield only once so the outputer
|
||||||
|
# can pager it.
|
||||||
|
peek_response = []
|
||||||
|
key_type = nativestr(self.execute("type", key))
|
||||||
|
if key_type == "none":
|
||||||
|
yield f"{key} doesn't exist."
|
||||||
|
return
|
||||||
|
|
||||||
|
encoding = nativestr(self.execute("object encoding", key))
|
||||||
|
|
||||||
|
# use `memory usage` to get memory, this command available from redis4.0
|
||||||
|
mem = ""
|
||||||
|
if config.version and StrictVersion(config.version) >= StrictVersion("4.0.0"):
|
||||||
|
memory_usage_value = str(self.execute("memory usage", key))
|
||||||
|
mem = f" mem: {memory_usage_value} bytes"
|
||||||
|
|
||||||
|
ttl = str(self.execute("ttl", key))
|
||||||
|
|
||||||
|
key_info = f"{key_type} ({encoding}){mem}, ttl: {ttl}"
|
||||||
|
|
||||||
|
# FIXME raw write_result parse FormattedText
|
||||||
|
peek_response.append(FormattedText([("class:dockey", "key: "), ("", key_info)]))
|
||||||
|
|
||||||
|
detail_action_fun = {
|
||||||
|
"string": _string,
|
||||||
|
"list": _list,
|
||||||
|
"set": _set,
|
||||||
|
"zset": _zset,
|
||||||
|
"hash": _hash,
|
||||||
|
"stream": _stream,
|
||||||
|
}[key_type]
|
||||||
|
detail = list(detail_action_fun(key))
|
||||||
|
peek_response.extend(detail)
|
||||||
|
|
||||||
|
# merge them into only one FormattedText
|
||||||
|
flat_formatted_text_pair = []
|
||||||
|
for index, formatted_text in enumerate(peek_response):
|
||||||
|
for ft in formatted_text:
|
||||||
|
flat_formatted_text_pair.append(ft)
|
||||||
|
if index < len(peek_response) - 1:
|
||||||
|
flat_formatted_text_pair.append(renders.NEWLINE_TUPLE)
|
||||||
|
|
||||||
|
if config.raw:
|
||||||
|
yield convert_formatted_text_to_bytes(flat_formatted_text_pair)
|
||||||
|
return
|
||||||
|
yield FormattedText(flat_formatted_text_pair)
|
151
iredis/commands.py
Normal file
151
iredis/commands.py
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
import re
|
||||||
|
import csv
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import functools
|
||||||
|
from importlib_resources import read_text, open_text
|
||||||
|
|
||||||
|
from .utils import timer, strip_quote_args
|
||||||
|
from .exceptions import InvalidArguments, AmbiguousCommand
|
||||||
|
from . import data as project_data
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _load_command_summary():
|
||||||
|
commands_summary = json.loads(read_text(project_data, "commands.json"))
|
||||||
|
return commands_summary
|
||||||
|
|
||||||
|
|
||||||
|
def _load_command():
|
||||||
|
"""
|
||||||
|
load command informations from file.
|
||||||
|
:returns:
|
||||||
|
- original_commans: dict, command name : Command
|
||||||
|
- command_group: dict, group_name: command_names
|
||||||
|
"""
|
||||||
|
first_line = True
|
||||||
|
command2callback = {}
|
||||||
|
command2syntax = {}
|
||||||
|
groups = {}
|
||||||
|
with open_text(project_data, "command_syntax.csv") as command_syntax:
|
||||||
|
csvreader = csv.reader(command_syntax)
|
||||||
|
for line in csvreader:
|
||||||
|
if first_line:
|
||||||
|
first_line = False
|
||||||
|
continue
|
||||||
|
group, command, syntax, func_name = line
|
||||||
|
command2callback[command] = func_name
|
||||||
|
command2syntax[command] = syntax
|
||||||
|
groups.setdefault(group, []).append(command)
|
||||||
|
|
||||||
|
return command2callback, command2syntax, groups
|
||||||
|
|
||||||
|
|
||||||
|
def _load_dangerous():
|
||||||
|
"""
|
||||||
|
Load dangerous commands from csv file.
|
||||||
|
"""
|
||||||
|
first_line = True
|
||||||
|
dangerous_command = {}
|
||||||
|
with open_text(project_data, "dangerous_commands.csv") as dangerous_file:
|
||||||
|
csvreader = csv.reader(dangerous_file)
|
||||||
|
for line in csvreader:
|
||||||
|
if first_line:
|
||||||
|
first_line = False
|
||||||
|
continue
|
||||||
|
command, reason = line
|
||||||
|
dangerous_command[command] = reason
|
||||||
|
return dangerous_command
|
||||||
|
|
||||||
|
|
||||||
|
timer("[Loader] Start loading commands file...")
|
||||||
|
command2callback, command2syntax, groups = _load_command()
|
||||||
|
# all redis command strings, in UPPER case
|
||||||
|
# NOTE: Must sort by length, to match longest command first
|
||||||
|
all_commands = sorted(
|
||||||
|
list(command2callback.keys()) + ["HELP"], key=lambda x: len(x), reverse=True
|
||||||
|
)
|
||||||
|
# load commands information from redis-doc/commands.json
|
||||||
|
commands_summary = _load_command_summary()
|
||||||
|
# add iredis' commands' summary
|
||||||
|
commands_summary.update(
|
||||||
|
{
|
||||||
|
"HELP": {
|
||||||
|
"summary": "Show documents for a Redis command.",
|
||||||
|
"complexity": "O(1).",
|
||||||
|
"arguments": [{"name": "command", "type": "string"}],
|
||||||
|
"since": "1.0",
|
||||||
|
"group": "iredis",
|
||||||
|
},
|
||||||
|
"CLEAR": {
|
||||||
|
"summary": "Clear the screen like bash clear.",
|
||||||
|
"complexity": "O(1).",
|
||||||
|
"since": "1.0",
|
||||||
|
"group": "iredis",
|
||||||
|
},
|
||||||
|
"EXIT": {
|
||||||
|
"summary": "Exit iredis.",
|
||||||
|
"complexity": "O(1).",
|
||||||
|
"since": "1.0",
|
||||||
|
"group": "iredis",
|
||||||
|
},
|
||||||
|
"PEEK": {
|
||||||
|
"summary": "Get the key's type and value.",
|
||||||
|
"arguments": [{"name": "key", "type": "key"}],
|
||||||
|
"since": "1.0",
|
||||||
|
"complexity": "O(1).",
|
||||||
|
"since": "1.0",
|
||||||
|
"group": "iredis",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
timer("[Loader] Finished loading commands.")
|
||||||
|
dangerous_commands = _load_dangerous()
|
||||||
|
|
||||||
|
|
||||||
|
@functools.lru_cache(maxsize=2048)
|
||||||
|
def split_command_args(command):
|
||||||
|
"""
|
||||||
|
Split Redis command text into command and args.
|
||||||
|
|
||||||
|
:param command: redis command string, with args
|
||||||
|
"""
|
||||||
|
global all_commands
|
||||||
|
|
||||||
|
command = command.strip()
|
||||||
|
for command_name in all_commands:
|
||||||
|
# for command that is paritaly inputed, like `command in`, we should
|
||||||
|
# match with `command info`, otherwise, `command in` will result in
|
||||||
|
# `command` with `args` is ('in') which is an invalid case.
|
||||||
|
normalized_input_command = " ".join(command.split()).upper()
|
||||||
|
if (
|
||||||
|
re.search("\s", command)
|
||||||
|
and command_name.startswith(normalized_input_command)
|
||||||
|
and command_name != normalized_input_command
|
||||||
|
):
|
||||||
|
raise AmbiguousCommand("command is not finished")
|
||||||
|
# allow multiplt space in user inputed command
|
||||||
|
command_allow_multi_spaces = "[ ]+".join(command_name.split())
|
||||||
|
matcher = re.match(fr"({command_allow_multi_spaces})( |$)", command.upper())
|
||||||
|
if matcher:
|
||||||
|
matched_command_len = len(matcher.group(1))
|
||||||
|
input_command = command[:matched_command_len]
|
||||||
|
input_args = command[matcher.end() :]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise InvalidArguments(f"`{command}` is not a valide Redis Command")
|
||||||
|
|
||||||
|
args = list(strip_quote_args(input_args))
|
||||||
|
|
||||||
|
return input_command, args
|
||||||
|
|
||||||
|
|
||||||
|
def split_unknown_args(command):
|
||||||
|
"""
|
||||||
|
Split user's input into command and args.
|
||||||
|
"""
|
||||||
|
command = command.strip()
|
||||||
|
input_command, *input_args = command.split(" ")
|
||||||
|
return input_command, list(strip_quote_args(" ".join(input_args)))
|
359
iredis/completers.py
Normal file
359
iredis/completers.py
Normal file
|
@ -0,0 +1,359 @@
|
||||||
|
import logging
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
|
import pendulum
|
||||||
|
from prompt_toolkit.completion import (
|
||||||
|
CompleteEvent,
|
||||||
|
Completer,
|
||||||
|
Completion,
|
||||||
|
FuzzyWordCompleter,
|
||||||
|
WordCompleter,
|
||||||
|
)
|
||||||
|
from prompt_toolkit.contrib.regular_languages.completion import GrammarCompleter
|
||||||
|
from prompt_toolkit.document import Document
|
||||||
|
|
||||||
|
from .commands import split_command_args, commands_summary, all_commands
|
||||||
|
from .config import config
|
||||||
|
from .exceptions import InvalidArguments, AmbiguousCommand
|
||||||
|
from .redis_grammar import CONST, command_grammar, get_command_grammar
|
||||||
|
from .utils import strip_quote_args, ensure_str
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class MostRecentlyUsedFirstWordMixin:
|
||||||
|
"""
|
||||||
|
A Mixin for WordCompleter, with a `touch()` method can make latest used
|
||||||
|
word appears first. And evict old completion word when `max_words` reached.
|
||||||
|
|
||||||
|
Not thread safe.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, max_words, words, *args, **kwargs):
|
||||||
|
self.words = words
|
||||||
|
self.max_words = max_words
|
||||||
|
super().__init__(words, *args, **kwargs)
|
||||||
|
|
||||||
|
def touch(self, word):
|
||||||
|
"""
|
||||||
|
Make sure word is in the first place of the completer
|
||||||
|
list.
|
||||||
|
"""
|
||||||
|
if word in self.words:
|
||||||
|
self.words.remove(word)
|
||||||
|
else: # not in words
|
||||||
|
if len(self.words) == self.max_words: # full
|
||||||
|
self.words.pop()
|
||||||
|
self.words.insert(0, word)
|
||||||
|
|
||||||
|
def touch_words(self, words):
|
||||||
|
for word in words:
|
||||||
|
self.touch(word)
|
||||||
|
|
||||||
|
|
||||||
|
class MostRecentlyUsedFirstWordCompleter(
|
||||||
|
MostRecentlyUsedFirstWordMixin, FuzzyWordCompleter
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class IntegerTypeCompleter(MostRecentlyUsedFirstWordMixin, WordCompleter):
|
||||||
|
def __init__(self):
|
||||||
|
words = []
|
||||||
|
for i in range(1, 64):
|
||||||
|
words.append(f"i{i}") # signed integer, 64 bit max
|
||||||
|
words.append(f"u{i}") # unsigned integer, 63 bit max
|
||||||
|
words.append("i64")
|
||||||
|
super().__init__(len(words), list(reversed(words)))
|
||||||
|
|
||||||
|
|
||||||
|
class TimestampCompleter(Completer):
|
||||||
|
"""
|
||||||
|
Completer for timestamp based on input.
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* Auto complete humanize time, like 3 -> 3 minutes ago, 3 hours ago.
|
||||||
|
* Auto guess datetime, complete by its timestamp. 2020-01-01 12:00
|
||||||
|
-> 1577851200.
|
||||||
|
|
||||||
|
The timezone is read from system.
|
||||||
|
"""
|
||||||
|
|
||||||
|
when_lower_than = {
|
||||||
|
"year": 20,
|
||||||
|
"month": 12,
|
||||||
|
"day": 31,
|
||||||
|
"hour": 100,
|
||||||
|
"minute": 1000,
|
||||||
|
"second": 1000_000,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _completion_humanize_time(self, document: Document) -> Iterable[Completion]:
|
||||||
|
text = document.text
|
||||||
|
if not text.isnumeric():
|
||||||
|
return
|
||||||
|
current = int(text)
|
||||||
|
now = pendulum.now()
|
||||||
|
for unit, minium in self.when_lower_than.items():
|
||||||
|
if current <= minium:
|
||||||
|
dt = now.subtract(**{f"{unit}s": current})
|
||||||
|
meta = f"{text} {unit}{'s' if current > 1 else ''} ago ({dt.format('YYYY-MM-DD HH:mm:ss')})"
|
||||||
|
yield Completion(
|
||||||
|
str(dt.int_timestamp * 1000),
|
||||||
|
start_position=-len(document.text_before_cursor),
|
||||||
|
display_meta=meta,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _completion_formatted_time(self, document: Document) -> Iterable[Completion]:
|
||||||
|
text = document.text
|
||||||
|
try:
|
||||||
|
dt = pendulum.parse(text)
|
||||||
|
except Exception:
|
||||||
|
return
|
||||||
|
yield Completion(
|
||||||
|
str(dt.int_timestamp * 1000),
|
||||||
|
start_position=-len(document.text_before_cursor),
|
||||||
|
display_meta=str(dt),
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_completions(
|
||||||
|
self, document: Document, complete_event: CompleteEvent
|
||||||
|
) -> Iterable[Completion]:
|
||||||
|
completions = list(self._completion_humanize_time(document)) + list(
|
||||||
|
self._completion_formatted_time(document)
|
||||||
|
)
|
||||||
|
|
||||||
|
# here we yield bigger timestamp first.
|
||||||
|
for completion in sorted(completions, key=lambda a: a.text):
|
||||||
|
yield completion
|
||||||
|
|
||||||
|
|
||||||
|
class IRedisCompleter(Completer):
|
||||||
|
"""
|
||||||
|
Completer class that can dynamically returns any Completer.
|
||||||
|
|
||||||
|
:param get_completer: Callable that returns a :class:`.Completer` instance.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, hint=False, completion_casing="upper"):
|
||||||
|
super().__init__()
|
||||||
|
self.completer_mapping = self.get_completer_mapping(hint, completion_casing)
|
||||||
|
self.current_completer = self.root_completer = GrammarCompleter(
|
||||||
|
command_grammar, self.completer_mapping
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def key_completer(self) -> MostRecentlyUsedFirstWordCompleter:
|
||||||
|
return self.completer_mapping["key"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def member_completer(self) -> MostRecentlyUsedFirstWordCompleter:
|
||||||
|
return self.completer_mapping["member"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def field_completer(self) -> MostRecentlyUsedFirstWordCompleter:
|
||||||
|
return self.completer_mapping["field"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def group_completer(self) -> MostRecentlyUsedFirstWordCompleter:
|
||||||
|
return self.completer_mapping["group"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def catetoryname_completer(self) -> MostRecentlyUsedFirstWordCompleter:
|
||||||
|
return self.completer_mapping["categoryname"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def username_completer(self) -> MostRecentlyUsedFirstWordCompleter:
|
||||||
|
return self.completer_mapping["username"]
|
||||||
|
|
||||||
|
def get_completer(self, input_text):
|
||||||
|
try:
|
||||||
|
command, _ = split_command_args(input_text)
|
||||||
|
# here will compile grammar for this command
|
||||||
|
grammar = get_command_grammar(command)
|
||||||
|
completer = GrammarCompleter(
|
||||||
|
compiled_grammar=grammar, completers=self.completer_mapping
|
||||||
|
)
|
||||||
|
except (InvalidArguments, AmbiguousCommand):
|
||||||
|
completer = self.root_completer
|
||||||
|
|
||||||
|
return completer
|
||||||
|
|
||||||
|
def get_completions(
|
||||||
|
self, document: Document, complete_event: CompleteEvent
|
||||||
|
) -> Iterable[Completion]:
|
||||||
|
input_text = document.text
|
||||||
|
self.current_completer = self.get_completer(input_text)
|
||||||
|
return self.current_completer.get_completions(document, complete_event)
|
||||||
|
|
||||||
|
def update_completer_for_input(self, command):
|
||||||
|
completer = self.get_completer(command)
|
||||||
|
grammar = completer.compiled_grammar
|
||||||
|
m = grammar.match(command)
|
||||||
|
if not m:
|
||||||
|
# invalide command!
|
||||||
|
return
|
||||||
|
variables = m.variables()
|
||||||
|
|
||||||
|
# auto update completion words, if it's LRU strategy.
|
||||||
|
for _token, _completer in self.completer_mapping.items():
|
||||||
|
if not isinstance(_completer, MostRecentlyUsedFirstWordMixin):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# getall always returns a []
|
||||||
|
tokens_in_command = variables.getall(_token)
|
||||||
|
for _token_in_command in tokens_in_command:
|
||||||
|
# prompt_toolkit didn't support multi tokens
|
||||||
|
# like DEL key1 key2 key3
|
||||||
|
# so we have to split them manualy
|
||||||
|
for single_token in strip_quote_args(_token_in_command):
|
||||||
|
_completer.touch(single_token)
|
||||||
|
|
||||||
|
def update_completer_for_response(self, command_name, args, response):
|
||||||
|
command_name = " ".join(command_name.split()).upper()
|
||||||
|
logger.info(
|
||||||
|
f"Try update completer using response... command_name is {command_name}"
|
||||||
|
)
|
||||||
|
if response is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
response = ensure_str(response)
|
||||||
|
if command_name in ("HKEYS",):
|
||||||
|
self.field_completer.touch_words(response)
|
||||||
|
logger.debug(f"[Completer] field completer updated with {response}.")
|
||||||
|
|
||||||
|
if command_name in ("HGETALL",):
|
||||||
|
self.field_completer.touch_words(response[::2])
|
||||||
|
logger.debug(f"[Completer] field completer updated with {response[::2]}.")
|
||||||
|
|
||||||
|
if command_name in ("ZPOPMAX", "ZPOPMIN", "ZRANGE", "ZRANGE", "ZRANGEBYSCORE"):
|
||||||
|
if config.withscores:
|
||||||
|
self.member_completer.touch_words(response[::2])
|
||||||
|
logger.debug(
|
||||||
|
f"[Completer] member completer updated with {response[::2]}."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.member_completer.touch_words(response)
|
||||||
|
logger.debug(f"[Completer] member completer updated with {response}.")
|
||||||
|
|
||||||
|
if command_name in ("KEYS",):
|
||||||
|
self.key_completer.touch_words(response)
|
||||||
|
logger.debug(f"[Completer] key completer updated with {response}.")
|
||||||
|
|
||||||
|
if command_name in ("SCAN",):
|
||||||
|
self.key_completer.touch_words(response[1])
|
||||||
|
logger.debug(f"[Completer] key completer updated with {response[1]}.")
|
||||||
|
|
||||||
|
if command_name in ("SSCAN", "ZSCAN"):
|
||||||
|
self.member_completer.touch_words(response[1])
|
||||||
|
logger.debug(f"[Completer] member completer updated with {response[1]}.")
|
||||||
|
|
||||||
|
if command_name in ("HSCAN",):
|
||||||
|
self.field_completer.touch_words(response[1][::2])
|
||||||
|
logger.debug(
|
||||||
|
f"[Completer] field completer updated with {response[1][::2]}."
|
||||||
|
)
|
||||||
|
|
||||||
|
# only update categoryname completer when `ACL CAT` without args.
|
||||||
|
if command_name == "ACL CAT" and not args:
|
||||||
|
self.catetoryname_completer.touch_words(response)
|
||||||
|
if command_name == "ACL USERS":
|
||||||
|
self.username_completer.touch_words(response)
|
||||||
|
|
||||||
|
def _touch_members(self, items):
|
||||||
|
_step = 1
|
||||||
|
|
||||||
|
if config.withscores:
|
||||||
|
_step = 2
|
||||||
|
|
||||||
|
self.member_completer.touch_words(ensure_str(items)[::_step])
|
||||||
|
|
||||||
|
def _touch_hash_pairs(self, items):
|
||||||
|
self.field_completer.touch_words(ensure_str(items)[::2])
|
||||||
|
|
||||||
|
def _touch_keys(self, items):
|
||||||
|
self.key_completer.touch_words(ensure_str(items))
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return "DynamicCompleter(%r -> %r)" % (
|
||||||
|
self.get_completer,
|
||||||
|
self.current_completer,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_completer_mapping(self, hint_on, completion_casing):
|
||||||
|
completer_mapping = {}
|
||||||
|
completer_mapping.update(
|
||||||
|
{
|
||||||
|
key: WordCompleter(tokens.split(" "), ignore_case=True)
|
||||||
|
for key, tokens in CONST.items()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
key_completer = MostRecentlyUsedFirstWordCompleter(config.completer_max, [])
|
||||||
|
member_completer = MostRecentlyUsedFirstWordCompleter(config.completer_max, [])
|
||||||
|
field_completer = MostRecentlyUsedFirstWordCompleter(config.completer_max, [])
|
||||||
|
group_completer = MostRecentlyUsedFirstWordCompleter(config.completer_max, [])
|
||||||
|
username_completer = MostRecentlyUsedFirstWordCompleter(
|
||||||
|
config.completer_max, []
|
||||||
|
)
|
||||||
|
categoryname_completer = MostRecentlyUsedFirstWordCompleter(100, [])
|
||||||
|
timestamp_completer = TimestampCompleter()
|
||||||
|
integer_type_completer = IntegerTypeCompleter()
|
||||||
|
|
||||||
|
completer_mapping.update(
|
||||||
|
{
|
||||||
|
# all key related completers share the same completer
|
||||||
|
"keys": key_completer,
|
||||||
|
"key": key_completer,
|
||||||
|
"destination": key_completer,
|
||||||
|
"newkey": key_completer,
|
||||||
|
# member
|
||||||
|
"member": member_completer,
|
||||||
|
"members": member_completer,
|
||||||
|
# zmember
|
||||||
|
# TODO sperate sorted set and set
|
||||||
|
# hash fields
|
||||||
|
"field": field_completer,
|
||||||
|
"fields": field_completer,
|
||||||
|
# stream groups
|
||||||
|
"group": group_completer,
|
||||||
|
# stream id
|
||||||
|
"stream_id": timestamp_completer,
|
||||||
|
"inttype": integer_type_completer,
|
||||||
|
"categoryname": categoryname_completer,
|
||||||
|
"username": username_completer,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# command completer
|
||||||
|
if hint_on:
|
||||||
|
command_hint = {
|
||||||
|
key: info["summary"] for key, info in commands_summary.items()
|
||||||
|
}
|
||||||
|
hint = {
|
||||||
|
command: command_hint.get(command.upper()) for command in all_commands
|
||||||
|
}
|
||||||
|
hint.update(
|
||||||
|
{
|
||||||
|
command.lower(): command_hint.get(command.upper())
|
||||||
|
for command in all_commands
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
hint = {}
|
||||||
|
|
||||||
|
upper_commands = all_commands[::-1]
|
||||||
|
lower_commands = [command.lower() for command in all_commands[::-1]]
|
||||||
|
auto_commands = upper_commands + lower_commands
|
||||||
|
|
||||||
|
ignore_case = completion_casing != "auto"
|
||||||
|
|
||||||
|
command_completions = {
|
||||||
|
"auto": auto_commands,
|
||||||
|
"upper": upper_commands,
|
||||||
|
"lower": lower_commands,
|
||||||
|
}.get(completion_casing)
|
||||||
|
|
||||||
|
completer_mapping["command"] = WordCompleter(
|
||||||
|
command_completions, ignore_case=ignore_case, sentence=True, meta_dict=hint
|
||||||
|
)
|
||||||
|
return completer_mapping
|
130
iredis/config.py
Normal file
130
iredis/config.py
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
from importlib_resources import path
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from configobj import ConfigObj, ConfigObjError
|
||||||
|
from . import data as project_data
|
||||||
|
|
||||||
|
# TODO verbose logger to print to stdout
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
system_config_file = "/etc/iredisrc"
|
||||||
|
pwd_config_file = os.path.join(os.getcwd(), ".iredisrc")
|
||||||
|
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
"""
|
||||||
|
Global config, set once on start, then
|
||||||
|
become readonly, never change again.
|
||||||
|
|
||||||
|
:param raw: weather write raw bytes to stdout without any
|
||||||
|
decoding.
|
||||||
|
:param decode: How to decode bytes response.(For display and
|
||||||
|
Completers)
|
||||||
|
default is None, means show literal bytes. But completers
|
||||||
|
will try use utf-8 decoding.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.raw = None
|
||||||
|
self.completer_max = None
|
||||||
|
# show command hint?
|
||||||
|
self.newbie_mode = None
|
||||||
|
self.rainbow = None
|
||||||
|
self.retry_times = 2
|
||||||
|
self.socket_keepalive = None
|
||||||
|
self.decode = None
|
||||||
|
self.no_info = None
|
||||||
|
self.bottom_bar = None
|
||||||
|
self.shell = None
|
||||||
|
self.enable_pager = None
|
||||||
|
self.pager = None
|
||||||
|
|
||||||
|
self.warning = True
|
||||||
|
|
||||||
|
self.no_version_reason = None
|
||||||
|
self.log_location = None
|
||||||
|
self.history_location = None
|
||||||
|
self.completion_casing = None
|
||||||
|
self.alias_dsn = None
|
||||||
|
|
||||||
|
# ===bad code===
|
||||||
|
# below are not configs, it's global state, it's wrong to write this
|
||||||
|
# please do not add more global state.
|
||||||
|
# FIXME this should be removed.
|
||||||
|
# use client attributes instead.
|
||||||
|
# use kwargs in render functions.
|
||||||
|
|
||||||
|
# for transaction render
|
||||||
|
self.queued_commands = []
|
||||||
|
self.transaction = False
|
||||||
|
# display zset withscores?
|
||||||
|
self.withscores = False
|
||||||
|
self.version = "Unknown"
|
||||||
|
|
||||||
|
def __setter__(self, name, value):
|
||||||
|
# for every time start a transaction
|
||||||
|
# clear the queued commands first
|
||||||
|
if name == "transaction" and value is True:
|
||||||
|
self.queued_commands = []
|
||||||
|
super().__setattr__(name, value)
|
||||||
|
|
||||||
|
|
||||||
|
config = Config()
|
||||||
|
|
||||||
|
|
||||||
|
def read_config_file(f):
|
||||||
|
"""Read a config file."""
|
||||||
|
|
||||||
|
if isinstance(f, str):
|
||||||
|
f = os.path.expanduser(f)
|
||||||
|
|
||||||
|
try:
|
||||||
|
config = ConfigObj(f, interpolation=False, encoding="utf8")
|
||||||
|
except ConfigObjError as e:
|
||||||
|
logger.error(
|
||||||
|
"Unable to parse line {0} of config file " "'{1}'.".format(e.line_number, f)
|
||||||
|
)
|
||||||
|
logger.error("Using successfully parsed config values.")
|
||||||
|
return e.config
|
||||||
|
except (IOError, OSError) as e:
|
||||||
|
logger.error(
|
||||||
|
"You don't have permission to read " "config file '{0}'.".format(e.filename)
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def load_config_files(iredisrc):
|
||||||
|
global config
|
||||||
|
|
||||||
|
with path(project_data, "iredisrc") as p:
|
||||||
|
config_obj = ConfigObj(str(p))
|
||||||
|
|
||||||
|
for _file in [system_config_file, iredisrc, pwd_config_file]:
|
||||||
|
_config = read_config_file(_file)
|
||||||
|
if bool(_config) is True:
|
||||||
|
config_obj.merge(_config)
|
||||||
|
config_obj.filename = _config.filename
|
||||||
|
|
||||||
|
config.raw = config_obj["main"].as_bool("raw")
|
||||||
|
config.completer_max = config_obj["main"].as_int("completer_max")
|
||||||
|
config.retry_times = config_obj["main"].as_int("retry_times")
|
||||||
|
config.newbie_mode = config_obj["main"].as_bool("newbie_mode")
|
||||||
|
config.rainbow = config_obj["main"].as_bool("rainbow")
|
||||||
|
config.socket_keepalive = config_obj["main"].as_bool("socket_keepalive")
|
||||||
|
config.no_info = config_obj["main"].as_bool("no_info")
|
||||||
|
config.bottom_bar = config_obj["main"].as_bool("bottom_bar")
|
||||||
|
config.warning = config_obj["main"].as_bool("warning")
|
||||||
|
config.decode = config_obj["main"]["decode"]
|
||||||
|
config.log_location = config_obj["main"]["log_location"]
|
||||||
|
config.completion_casing = config_obj["main"]["completion_casing"]
|
||||||
|
config.history_location = config_obj["main"]["history_location"]
|
||||||
|
config.alias_dsn = config_obj["alias_dsn"]
|
||||||
|
config.shell = config_obj["main"].as_bool("shell")
|
||||||
|
config.pager = config_obj["main"].get("pager")
|
||||||
|
config.enable_pager = config_obj["main"].as_bool("enable_pager")
|
||||||
|
|
||||||
|
return config_obj
|
0
iredis/data/__init__.py
Normal file
0
iredis/data/__init__.py
Normal file
264
iredis/data/command_syntax.csv
Normal file
264
iredis/data/command_syntax.csv
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
Group,Command,Syntax,Callback
|
||||||
|
cluster,CLUSTER ADDSLOTS,command_slots,render_simple_string
|
||||||
|
cluster,CLUSTER BUMPEPOCH,command,render_simple_string
|
||||||
|
cluster,CLUSTER COUNT-FAILURE-REPORTS,command_node,render_int
|
||||||
|
cluster,CLUSTER COUNTKEYSINSLOT,command_slot,render_int
|
||||||
|
cluster,CLUSTER DELSLOTS,command_slots,render_simple_string
|
||||||
|
cluster,CLUSTER FAILOVER,command_failoverchoice,render_simple_string
|
||||||
|
cluster,CLUSTER FLUSHSLOTS,command,render_simple_string
|
||||||
|
cluster,CLUSTER FORGET,command_node,render_simple_string
|
||||||
|
cluster,CLUSTER GETKEYSINSLOT,command_slot_count,render_list
|
||||||
|
cluster,CLUSTER INFO,command,render_bulk_string_decode
|
||||||
|
cluster,CLUSTER KEYSLOT,command_key,render_int
|
||||||
|
cluster,CLUSTER MEET,command_ip_port,render_simple_string
|
||||||
|
cluster,CLUSTER MYID,command,render_bulk_string_decode
|
||||||
|
cluster,CLUSTER NODES,command,render_bulk_string_decode
|
||||||
|
cluster,CLUSTER REPLICAS,command_node,render_bulk_string_decode
|
||||||
|
cluster,CLUSTER REPLICATE,command_node,render_simple_string
|
||||||
|
cluster,CLUSTER RESET,command_resetchoice,render_simple_string
|
||||||
|
cluster,CLUSTER SAVECONFIG,command,render_simple_string
|
||||||
|
cluster,CLUSTER SET-CONFIG-EPOCH,command_epoch,render_simple_string
|
||||||
|
cluster,CLUSTER SETSLOT,command_slot_slotsubcmd_nodex,render_simple_string
|
||||||
|
cluster,CLUSTER SLAVES,command_node,render_bulk_string_decode
|
||||||
|
cluster,CLUSTER SLOTS,command,render_list
|
||||||
|
cluster,READONLY,command,render_simple_string
|
||||||
|
cluster,READWRITE,command,render_simple_string
|
||||||
|
connection,AUTH,command_password,render_simple_string
|
||||||
|
connection,ECHO,command_message,render_bulk_string
|
||||||
|
connection,HELLO,command_any,render_list
|
||||||
|
connection,PING,command_messagex,render_bulk_string
|
||||||
|
connection,QUIT,command,render_simple_string
|
||||||
|
connection,SELECT,command_index,render_simple_string
|
||||||
|
connection,CLIENT CACHING,command_yes,render_simple_string
|
||||||
|
connection,CLIENT GETREDIR,command,render_int
|
||||||
|
connection,CLIENT TRACKING,command_client_tracking,render_simple_string
|
||||||
|
connection,CLIENT LIST,command_type_conntype_x,render_bulk_string_decode
|
||||||
|
connection,CLIENT GETNAME,command,render_bulk_string
|
||||||
|
connection,CLIENT ID,command,render_int
|
||||||
|
connection,CLIENT KILL,command_clientkill,render_string_or_int
|
||||||
|
connection,CLIENT PAUSE,command_timeout,render_simple_string
|
||||||
|
connection,CLIENT REPLY,command_switch,render_simple_string
|
||||||
|
connection,CLIENT SETNAME,command_value,render_simple_string
|
||||||
|
connection,CLIENT UNBLOCK,command_clientid_errorx,render_int
|
||||||
|
generic,DEL,command_keys,render_int
|
||||||
|
generic,DUMP,command_key,render_bulk_string
|
||||||
|
generic,EXISTS,command_keys,render_int
|
||||||
|
generic,EXPIRE,command_key_second,render_int
|
||||||
|
generic,EXPIREAT,command_key_timestamp,render_int
|
||||||
|
generic,KEYS,command_pattern,command_keys
|
||||||
|
generic,MIGRATE,command_migrate,render_simple_string
|
||||||
|
generic,MOVE,command_key_index,render_int
|
||||||
|
generic,OBJECT,command_object_key,render_string_or_int
|
||||||
|
generic,PERSIST,command_key,render_int
|
||||||
|
generic,PEXPIRE,command_key_millisecond,render_int
|
||||||
|
generic,PEXPIREAT,command_key_timestampms,render_int
|
||||||
|
generic,PTTL,command_key,render_int
|
||||||
|
generic,RANDOMKEY,command,render_bulk_string
|
||||||
|
generic,RENAME,command_key_newkey,render_simple_string
|
||||||
|
generic,RENAMENX,command_key_newkey,render_int
|
||||||
|
generic,RESTORE,command_restore,render_simple_string
|
||||||
|
generic,SCAN,command_cursor_match_pattern_count_type,command_scan
|
||||||
|
generic,SORT,command_any,render_list_or_string
|
||||||
|
generic,TOUCH,command_keys,render_int
|
||||||
|
generic,TTL,command_key,render_int
|
||||||
|
generic,TYPE,command_key,render_bulk_string
|
||||||
|
generic,UNLINK,command_keys,render_int
|
||||||
|
generic,WAIT,command_count_timeout,render_int
|
||||||
|
geo,GEOADD,command_key_longitude_latitude_members,render_int
|
||||||
|
geo,GEODIST,command_geodist,render_bulk_string
|
||||||
|
geo,GEOHASH,command_key_members,render_list
|
||||||
|
geo,GEOPOS,command_key_members,render_list
|
||||||
|
geo,GEORADIUS,command_radius,render_list_or_string
|
||||||
|
geo,GEORADIUSBYMEMBER,command_georadiusbymember,render_list_or_string
|
||||||
|
hash,HDEL,command_key_fields,render_int
|
||||||
|
hash,HEXISTS,command_key_field,render_int
|
||||||
|
hash,HGET,command_key_field,render_bulk_string
|
||||||
|
hash,HGETALL,command_key,render_hash_pairs
|
||||||
|
hash,HINCRBY,command_key_field_delta,render_int
|
||||||
|
hash,HINCRBYFLOAT,command_key_field_float,render_bulk_string
|
||||||
|
hash,HKEYS,command_key,command_hkeys
|
||||||
|
hash,HLEN,command_key,render_int
|
||||||
|
hash,HMGET,command_key_fields,render_list
|
||||||
|
hash,HMSET,command_key_fieldvalues,render_bulk_string
|
||||||
|
hash,HSCAN,command_key_cursor_match_pattern_count,command_hscan
|
||||||
|
hash,HSET,command_key_field_value,render_int
|
||||||
|
hash,HSETNX,command_key_field_value,render_int
|
||||||
|
hash,HSTRLEN,command_key_field,render_int
|
||||||
|
hash,HVALS,command_key,render_list
|
||||||
|
hyperloglog,PFADD,command_key_values,render_int
|
||||||
|
hyperloglog,PFCOUNT,command_keys,render_int
|
||||||
|
hyperloglog,PFMERGE,command_newkey_keys,render_simple_string
|
||||||
|
list,BLPOP,command_keys_timeout,render_list_or_string
|
||||||
|
list,BRPOP,command_keys_timeout,render_list_or_string
|
||||||
|
list,BRPOPLPUSH,command_key_newkey_timeout,render_bulk_string
|
||||||
|
list,LINDEX,command_key_position,render_bulk_string
|
||||||
|
list,LINSERT,command_key_positionchoice_pivot_value,render_int
|
||||||
|
list,LLEN,command_key,render_int
|
||||||
|
list,LPOS,command_lpos,render_list_or_string
|
||||||
|
list,LPOP,command_key,render_bulk_string
|
||||||
|
list,LPUSH,command_key_values,render_int
|
||||||
|
list,LPUSHX,command_key_values,render_int
|
||||||
|
list,LRANGE,command_key_start_end,render_list
|
||||||
|
list,LREM,command_key_position_value,render_int
|
||||||
|
list,LSET,command_key_position_value,render_simple_string
|
||||||
|
list,LTRIM,command_key_start_end,render_simple_string
|
||||||
|
list,RPOP,command_key,render_bulk_string
|
||||||
|
list,RPOPLPUSH,command_key_newkey,render_bulk_string
|
||||||
|
list,RPUSH,command_key_values,render_int
|
||||||
|
list,RPUSHX,command_key_value,render_int
|
||||||
|
pubsub,PSUBSCRIBE,command_channels,render_subscribe
|
||||||
|
pubsub,PUBLISH,command_channel_message,render_int
|
||||||
|
pubsub,PUBSUB,command_pubsubcmd_channels,render_list_or_string
|
||||||
|
pubsub,PUNSUBSCRIBE,command_channels,render_subscribe
|
||||||
|
pubsub,SUBSCRIBE,command_channels,render_subscribe
|
||||||
|
pubsub,UNSUBSCRIBE,command_channels,render_subscribe
|
||||||
|
scripting,EVAL,command_lua_any,render_list_or_string
|
||||||
|
scripting,EVALSHA,command_any,render_list_or_string
|
||||||
|
scripting,SCRIPT DEBUG,command_scriptdebug,render_simple_string
|
||||||
|
scripting,SCRIPT EXISTS,command_any,render_list
|
||||||
|
scripting,SCRIPT FLUSH,command,render_simple_string
|
||||||
|
scripting,SCRIPT KILL,command,render_simple_string
|
||||||
|
scripting,SCRIPT LOAD,command_lua_any,render_bulk_string_decode
|
||||||
|
server,ACL CAT,command_categorynamex,render_list
|
||||||
|
server,ACL DELUSER,command_usernames,render_int
|
||||||
|
server,ACL GENPASS,command_countx,render_bulk_string
|
||||||
|
server,ACL GETUSER,command_username,render_list
|
||||||
|
server,ACL HELP,command,render_list
|
||||||
|
server,ACL LIST,command,render_list
|
||||||
|
server,ACL LOAD,command,render_simple_string
|
||||||
|
server,ACL LOG,command_count_or_resetx,render_list_or_string
|
||||||
|
server,ACL SAVE,command,render_simple_string
|
||||||
|
server,ACL SETUSER,command_username_rules,render_simple_string
|
||||||
|
server,ACL USERS,command,render_list
|
||||||
|
server,ACL WHOAMI,command,render_bulk_string
|
||||||
|
server,SWAPDB,command_index_index,render_simple_string
|
||||||
|
server,BGREWRITEAOF,command,render_simple_string
|
||||||
|
server,BGSAVE,command_schedulex,render_simple_string
|
||||||
|
server,COMMAND,command,render_list
|
||||||
|
server,COMMAND COUNT,command,render_int
|
||||||
|
server,COMMAND GETKEYS,command_any,render_list
|
||||||
|
server,COMMAND INFO,command_commandname,render_list
|
||||||
|
server,CONFIG GET,command_parameter,render_nested_pair
|
||||||
|
server,CONFIG RESETSTAT,command,render_simple_string
|
||||||
|
server,CONFIG REWRITE,command,render_simple_string
|
||||||
|
server,CONFIG SET,command_parameter_value,render_simple_string
|
||||||
|
server,DBSIZE,command,render_int
|
||||||
|
server,DEBUG OBJECT,command_key,render_simple_string
|
||||||
|
server,DEBUG SEGFAULT,command,render_simple_string
|
||||||
|
server,FLUSHALL,command_asyncx,render_simple_string
|
||||||
|
server,FLUSHDB,command_asyncx,render_simple_string
|
||||||
|
server,INFO,command_sectionx,render_bulk_string_decode
|
||||||
|
server,LOLWUT,command_version,render_bytes
|
||||||
|
server,LASTSAVE,command,render_unixtime
|
||||||
|
server,LATENCY DOCTOR,command,render_bulk_string_decode
|
||||||
|
server,LATENCY GRAPH,command_graphevent,render_bulk_string_decode
|
||||||
|
server,LATENCY HELP,command,render_list
|
||||||
|
server,LATENCY HISTORY,command_graphevent,render_list
|
||||||
|
server,LATENCY LATEST,command,render_list
|
||||||
|
server,LATENCY RESET,command_graphevents,render_int
|
||||||
|
server,MEMORY DOCTOR,command,render_bulk_string_decode
|
||||||
|
server,MEMORY HELP,command,render_list
|
||||||
|
server,MEMORY MALLOC-STATS,command,render_bulk_string_decode
|
||||||
|
server,MEMORY PURGE,command,render_simple_string
|
||||||
|
server,MEMORY STATS,command,render_nested_pair
|
||||||
|
server,MEMORY USAGE,command_key_samples_count,render_int
|
||||||
|
server,MODULE LIST,command,render_list
|
||||||
|
server,MODULE LOAD,command_any,render_simple_string
|
||||||
|
server,MODULE UNLOAD,command_any,render_simple_string
|
||||||
|
server,MONITOR,command,render_simple_string
|
||||||
|
server,PSYNC,command_replicationid_offset,render_bulk_string_decode
|
||||||
|
server,REPLICAOF,command_any,render_simple_string
|
||||||
|
server,ROLE,command,render_list
|
||||||
|
server,SAVE,command,render_simple_string
|
||||||
|
server,SHUTDOWN,command_shutdown,render_simple_string
|
||||||
|
server,SLAVEOF,command_any,render_simple_string
|
||||||
|
server,SLOWLOG,command_slowlog,render_slowlog
|
||||||
|
server,SYNC,command,render_bulk_string
|
||||||
|
server,TIME,command,render_time
|
||||||
|
set,SADD,command_key_members,render_int
|
||||||
|
set,SCARD,command_key,render_int
|
||||||
|
set,SDIFF,command_keys,render_list
|
||||||
|
set,SDIFFSTORE,command_destination_keys,render_int
|
||||||
|
set,SINTER,command_keys,render_list
|
||||||
|
set,SINTERSTORE,command_destination_keys,render_int
|
||||||
|
set,SISMEMBER,command_key_member,render_int
|
||||||
|
set,SMEMBERS,command_key,render_list
|
||||||
|
set,SMOVE,command_key_newkey_member,render_int
|
||||||
|
set,SPOP,command_key_count_x,render_list_or_string
|
||||||
|
set,SRANDMEMBER,command_key_count_x,render_list_or_string
|
||||||
|
set,SREM,command_key_members,render_int
|
||||||
|
set,SSCAN,command_key_cursor_match_pattern_count,command_sscan
|
||||||
|
set,SUNION,command_keys,render_list
|
||||||
|
set,SUNIONSTORE,command_destination_keys,render_int
|
||||||
|
sorted_set,BZPOPMAX,command_keys_timeout,render_list_or_string
|
||||||
|
sorted_set,BZPOPMIN,command_keys_timeout,render_list_or_string
|
||||||
|
sorted_set,ZADD,command_key_condition_changed_incr_score_members,render_string_or_int
|
||||||
|
sorted_set,ZCARD,command_key,render_int
|
||||||
|
sorted_set,ZCOUNT,command_key_min_max,render_int
|
||||||
|
sorted_set,ZINCRBY,command_key_float_member,render_bulk_string
|
||||||
|
sorted_set,ZINTERSTORE,command_any,render_int
|
||||||
|
sorted_set,ZLEXCOUNT,command_key_lexmin_lexmax,render_int
|
||||||
|
sorted_set,ZPOPMAX,command_key_count_x,render_members
|
||||||
|
sorted_set,ZPOPMIN,command_key_count_x,render_members
|
||||||
|
sorted_set,ZRANGE,command_key_start_end_withscores_x,render_members
|
||||||
|
sorted_set,ZRANGEBYLEX,command_key_lexmin_lexmax_limit_offset_count,render_list
|
||||||
|
sorted_set,ZRANGEBYSCORE,command_key_min_max_withscore_x_limit_offset_count_x,render_members
|
||||||
|
sorted_set,ZRANK,command_key_member,render_int
|
||||||
|
sorted_set,ZREM,command_key_members,render_int
|
||||||
|
sorted_set,ZREMRANGEBYLEX,command_key_lexmin_lexmax,render_int
|
||||||
|
sorted_set,ZREMRANGEBYRANK,command_key_start_end,render_int
|
||||||
|
sorted_set,ZREMRANGEBYSCORE,command_key_min_max,render_int
|
||||||
|
sorted_set,ZREVRANGE,command_key_start_end_withscores_x,render_list
|
||||||
|
sorted_set,ZREVRANGEBYLEX,command_key_lexmin_lexmax_limit_offset_count,render_list
|
||||||
|
sorted_set,ZREVRANGEBYSCORE,command_key_min_max_withscore_x_limit_offset_count_x,render_list
|
||||||
|
sorted_set,ZREVRANK,command_key_member,render_int
|
||||||
|
sorted_set,ZSCAN,command_key_cursor_match_pattern_count,command_sscan
|
||||||
|
sorted_set,ZSCORE,command_key_member,render_bulk_string
|
||||||
|
sorted_set,ZUNIONSTORE,command_any,render_int
|
||||||
|
stream,XACK,command_key_group_ids,render_int
|
||||||
|
stream,XADD,command_xadd,render_bulk_string
|
||||||
|
stream,XCLAIM,command_xclaim,render_list
|
||||||
|
stream,XDEL,command_key_ids,render_int
|
||||||
|
stream,XGROUP,command_xgroup,render_string_or_int
|
||||||
|
stream,XINFO,command_xinfo,render_list
|
||||||
|
stream,XLEN,command_key,render_int
|
||||||
|
stream,XPENDING,command_xpending,render_list
|
||||||
|
stream,XRANGE,command_key_start_end_countx,render_list
|
||||||
|
stream,XREAD,command_xread,render_list
|
||||||
|
stream,XREADGROUP,command_xreadgroup,render_list
|
||||||
|
stream,XREVRANGE,command_key_start_end_countx,render_list
|
||||||
|
stream,XTRIM,command_key_maxlen,render_int
|
||||||
|
string,APPEND,command_key_value,render_int
|
||||||
|
string,BITCOUNT,command_key_start_end_x,render_int
|
||||||
|
string,BITFIELD,command_bitfield,render_list
|
||||||
|
string,BITOP,command_operation_key_keys,render_int
|
||||||
|
string,BITPOS,command_key_bit_start_end,render_int
|
||||||
|
string,DECR,command_key,render_int
|
||||||
|
string,DECRBY,command_key_delta,render_int
|
||||||
|
string,GET,command_key,render_bulk_string
|
||||||
|
string,GETBIT,command_key_offset,render_int
|
||||||
|
string,GETRANGE,command_key_start_end,render_bulk_string
|
||||||
|
string,GETSET,command_key_value,render_bulk_string
|
||||||
|
string,INCR,command_key,render_int
|
||||||
|
string,INCRBY,command_key_delta,render_int
|
||||||
|
string,INCRBYFLOAT,command_key_float,render_bulk_string
|
||||||
|
string,MGET,command_keys,render_list
|
||||||
|
string,MSET,command_key_valuess,render_simple_string
|
||||||
|
string,MSETNX,command_key_valuess,render_int
|
||||||
|
string,PSETEX,command_key_millisecond_value,render_bulk_string
|
||||||
|
string,SET,command_set,render_simple_string
|
||||||
|
string,SETBIT,command_key_offset_bit,render_int
|
||||||
|
string,SETEX,command_key_second_value,render_bulk_string
|
||||||
|
string,SETNX,command_key_value,render_int
|
||||||
|
string,SETRANGE,command_key_offset_value,render_int
|
||||||
|
string,STRALGO,command_stralgo,render_list_or_string
|
||||||
|
string,STRLEN,command_key,render_int
|
||||||
|
transactions,DISCARD,command,render_simple_string
|
||||||
|
transactions,EXEC,command,render_list_or_string
|
||||||
|
transactions,MULTI,command,render_simple_string
|
||||||
|
transactions,UNWATCH,command,render_simple_string
|
||||||
|
transactions,WATCH,command_keys,render_simple_string
|
||||||
|
iredis,HELP,command_command,
|
||||||
|
iredis,PEEK,command_key,
|
||||||
|
iredis,CLEAR,command,
|
||||||
|
iredis,EXIT,command,
|
|
4470
iredis/data/commands.json
Normal file
4470
iredis/data/commands.json
Normal file
File diff suppressed because it is too large
Load diff
0
iredis/data/commands/__init__.py
Normal file
0
iredis/data/commands/__init__.py
Normal file
84
iredis/data/commands/acl-cat.md
Normal file
84
iredis/data/commands/acl-cat.md
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
The command shows the available ACL categories if called without arguments. If a
|
||||||
|
category name is given, the command shows all the Redis commands in the
|
||||||
|
specified category.
|
||||||
|
|
||||||
|
ACL categories are very useful in order to create ACL rules that include or
|
||||||
|
exclude a large set of commands at once, without specifying every single
|
||||||
|
command. For instance, the following rule will let the user `karin` perform
|
||||||
|
everything but the most dangerous operations that may affect the server
|
||||||
|
stability:
|
||||||
|
|
||||||
|
ACL SETUSER karin on +@all -@dangerous
|
||||||
|
|
||||||
|
We first add all the commands to the set of commands that `karin` is able to
|
||||||
|
execute, but then we remove all the dangerous commands.
|
||||||
|
|
||||||
|
Checking for all the available categories is as simple as:
|
||||||
|
|
||||||
|
```
|
||||||
|
> ACL CAT
|
||||||
|
1) "keyspace"
|
||||||
|
2) "read"
|
||||||
|
3) "write"
|
||||||
|
4) "set"
|
||||||
|
5) "sortedset"
|
||||||
|
6) "list"
|
||||||
|
7) "hash"
|
||||||
|
8) "string"
|
||||||
|
9) "bitmap"
|
||||||
|
10) "hyperloglog"
|
||||||
|
11) "geo"
|
||||||
|
12) "stream"
|
||||||
|
13) "pubsub"
|
||||||
|
14) "admin"
|
||||||
|
15) "fast"
|
||||||
|
16) "slow"
|
||||||
|
17) "blocking"
|
||||||
|
18) "dangerous"
|
||||||
|
19) "connection"
|
||||||
|
20) "transaction"
|
||||||
|
21) "scripting"
|
||||||
|
```
|
||||||
|
|
||||||
|
Then we may want to know what commands are part of a given category:
|
||||||
|
|
||||||
|
```
|
||||||
|
> ACL CAT dangerous
|
||||||
|
1) "flushdb"
|
||||||
|
2) "acl"
|
||||||
|
3) "slowlog"
|
||||||
|
4) "debug"
|
||||||
|
5) "role"
|
||||||
|
6) "keys"
|
||||||
|
7) "pfselftest"
|
||||||
|
8) "client"
|
||||||
|
9) "bgrewriteaof"
|
||||||
|
10) "replicaof"
|
||||||
|
11) "monitor"
|
||||||
|
12) "restore-asking"
|
||||||
|
13) "latency"
|
||||||
|
14) "replconf"
|
||||||
|
15) "pfdebug"
|
||||||
|
16) "bgsave"
|
||||||
|
17) "sync"
|
||||||
|
18) "config"
|
||||||
|
19) "flushall"
|
||||||
|
20) "cluster"
|
||||||
|
21) "info"
|
||||||
|
22) "lastsave"
|
||||||
|
23) "slaveof"
|
||||||
|
24) "swapdb"
|
||||||
|
25) "module"
|
||||||
|
26) "restore"
|
||||||
|
27) "migrate"
|
||||||
|
28) "save"
|
||||||
|
29) "shutdown"
|
||||||
|
30) "psync"
|
||||||
|
31) "sort"
|
||||||
|
```
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@array-reply: a list of ACL categories or a list of commands inside a given
|
||||||
|
category. The command may return an error if an invalid category name is given
|
||||||
|
as argument.
|
17
iredis/data/commands/acl-deluser.md
Normal file
17
iredis/data/commands/acl-deluser.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
Delete all the specified ACL users and terminate all the connections that are
|
||||||
|
authenticated with such users. Note: the special `default` user cannot be
|
||||||
|
removed from the system, this is the default user that every new connection is
|
||||||
|
authenticated with. The list of users may include usernames that do not exist,
|
||||||
|
in such case no operation is performed for the non existing users.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply: The number of users that were deleted. This number will not
|
||||||
|
always match the number of arguments since certain users may not exist.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```
|
||||||
|
> ACL DELUSER antirez
|
||||||
|
1
|
||||||
|
```
|
43
iredis/data/commands/acl-genpass.md
Normal file
43
iredis/data/commands/acl-genpass.md
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
ACL users need a solid password in order to authenticate to the server without
|
||||||
|
security risks. Such password does not need to be remembered by humans, but only
|
||||||
|
by computers, so it can be very long and strong (unguessable by an external
|
||||||
|
attacker). The `ACL GENPASS` command generates a password starting from
|
||||||
|
/dev/urandom if available, otherwise (in systems without /dev/urandom) it uses a
|
||||||
|
weaker system that is likely still better than picking a weak password by hand.
|
||||||
|
|
||||||
|
By default (if /dev/urandom is available) the password is strong and can be used
|
||||||
|
for other uses in the context of a Redis application, for instance in order to
|
||||||
|
create unique session identifiers or other kind of unguessable and not colliding
|
||||||
|
IDs. The password generation is also very cheap because we don't really ask
|
||||||
|
/dev/urandom for bits at every execution. At startup Redis creates a seed using
|
||||||
|
/dev/urandom, then it will use SHA256 in counter mode, with
|
||||||
|
HMAC-SHA256(seed,counter) as primitive, in order to create more random bytes as
|
||||||
|
needed. This means that the application developer should be feel free to abuse
|
||||||
|
`ACL GENPASS` to create as many secure pseudorandom strings as needed.
|
||||||
|
|
||||||
|
The command output is an hexadecimal representation of a binary string. By
|
||||||
|
default it emits 256 bits (so 64 hex characters). The user can provide an
|
||||||
|
argument in form of number of bits to emit from 1 to 1024 to change the output
|
||||||
|
length. Note that the number of bits provided is always rounded to the next
|
||||||
|
multiple of 4. So for instance asking for just 1 bit password will result in 4
|
||||||
|
bits to be emitted, in the form of a single hex character.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@bulk-string-reply: by default 64 bytes string representing 256 bits of
|
||||||
|
pseudorandom data. Otherwise if an argument if needed, the output string length
|
||||||
|
is the number of specified bits (rounded to the next multiple of 4) divided
|
||||||
|
by 4.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```
|
||||||
|
> ACL GENPASS
|
||||||
|
"dd721260bfe1b3d9601e7fbab36de6d04e2e67b0ef1c53de59d45950db0dd3cc"
|
||||||
|
|
||||||
|
> ACL GENPASS 32
|
||||||
|
"355ef3dd"
|
||||||
|
|
||||||
|
> ACL GENPASS 5
|
||||||
|
"90"
|
||||||
|
```
|
28
iredis/data/commands/acl-getuser.md
Normal file
28
iredis/data/commands/acl-getuser.md
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
The command returns all the rules defined for an existing ACL user.
|
||||||
|
|
||||||
|
Specifically, it lists the user's ACL flags, password hashes and key name
|
||||||
|
patterns. Note that command rules are returned as a string in the same format
|
||||||
|
used with the `ACL SETUSER` command. This description of command rules reflects
|
||||||
|
the user's effective permissions, so while it may not be identical to the set of
|
||||||
|
rules used to configure the user, it is still functionally identical.
|
||||||
|
|
||||||
|
@array-reply: a list of ACL rule definitions for the user.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
Here's the default configuration for the default user:
|
||||||
|
|
||||||
|
```
|
||||||
|
> ACL GETUSER default
|
||||||
|
1) "flags"
|
||||||
|
2) 1) "on"
|
||||||
|
2) "allkeys"
|
||||||
|
3) "allcommands"
|
||||||
|
4) "nopass"
|
||||||
|
3) "passwords"
|
||||||
|
4) (empty array)
|
||||||
|
5) "commands"
|
||||||
|
6) "+@all"
|
||||||
|
7) "keys"
|
||||||
|
8) 1) "*"
|
||||||
|
```
|
6
iredis/data/commands/acl-help.md
Normal file
6
iredis/data/commands/acl-help.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
The `ACL HELP` command returns a helpful text describing the different
|
||||||
|
subcommands.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@array-reply: a list of subcommands and their descriptions
|
17
iredis/data/commands/acl-list.md
Normal file
17
iredis/data/commands/acl-list.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
The command shows the currently active ACL rules in the Redis server. Each line
|
||||||
|
in the returned array defines a different user, and the format is the same used
|
||||||
|
in the redis.conf file or the external ACL file, so you can cut and paste what
|
||||||
|
is returned by the ACL LIST command directly inside a configuration file if you
|
||||||
|
wish (but make sure to check `ACL SAVE`).
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
An array of strings.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```
|
||||||
|
> ACL LIST
|
||||||
|
1) "user antirez on #9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 ~objects:* +@all -@admin -@dangerous"
|
||||||
|
2) "user default on nopass ~* +@all"
|
||||||
|
```
|
27
iredis/data/commands/acl-load.md
Normal file
27
iredis/data/commands/acl-load.md
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
When Redis is configured to use an ACL file (with the `aclfile` configuration
|
||||||
|
option), this command will reload the ACLs from the file, replacing all the
|
||||||
|
current ACL rules with the ones defined in the file. The command makes sure to
|
||||||
|
have an _all or nothing_ behavior, that is:
|
||||||
|
|
||||||
|
- If every line in the file is valid, all the ACLs are loaded.
|
||||||
|
- If one or more line in the file is not valid, nothing is loaded, and the old
|
||||||
|
ACL rules defined in the server memory continue to be used.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` on success.
|
||||||
|
|
||||||
|
The command may fail with an error for several reasons: if the file is not
|
||||||
|
readable, if there is an error inside the file, and in such case the error will
|
||||||
|
be reported to the user in the error. Finally the command will fail if the
|
||||||
|
server is not configured to use an external ACL file.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```
|
||||||
|
> ACL LOAD
|
||||||
|
+OK
|
||||||
|
|
||||||
|
> ACL LOAD
|
||||||
|
-ERR /tmp/foo:1: Unknown command or category name in ACL...
|
||||||
|
```
|
41
iredis/data/commands/acl-log.md
Normal file
41
iredis/data/commands/acl-log.md
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
The command shows a list of recent ACL security events:
|
||||||
|
|
||||||
|
1. Failures to authenticate their connections with `AUTH` or `HELLO`.
|
||||||
|
2. Commands denied because against the current ACL rules.
|
||||||
|
3. Commands denied because accessing keys not allowed in the current ACL rules.
|
||||||
|
|
||||||
|
The optional argument specifies how many entries to show. By default up to ten
|
||||||
|
failures are returned. The special `RESET` argument clears the log. Entries are
|
||||||
|
displayed starting from the most recent.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
When called to show security events:
|
||||||
|
|
||||||
|
@array-reply: a list of ACL security events.
|
||||||
|
|
||||||
|
When called with `RESET`:
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` if the security log was cleared.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```
|
||||||
|
> AUTH someuser wrongpassword
|
||||||
|
(error) WRONGPASS invalid username-password pair
|
||||||
|
> ACL LOG 1
|
||||||
|
1) 1) "count"
|
||||||
|
2) (integer) 1
|
||||||
|
3) "reason"
|
||||||
|
4) "auth"
|
||||||
|
5) "context"
|
||||||
|
6) "toplevel"
|
||||||
|
7) "object"
|
||||||
|
8) "AUTH"
|
||||||
|
9) "username"
|
||||||
|
10) "someuser"
|
||||||
|
11) "age-seconds"
|
||||||
|
12) "4.0960000000000001"
|
||||||
|
13) "client-info"
|
||||||
|
14) "id=6 addr=127.0.0.1:63026 fd=8 name= age=9 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=48 qbuf-free=32720 obl=0 oll=0 omem=0 events=r cmd=auth user=default"
|
||||||
|
```
|
20
iredis/data/commands/acl-save.md
Normal file
20
iredis/data/commands/acl-save.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
When Redis is configured to use an ACL file (with the `aclfile` configuration
|
||||||
|
option), this command will save the currently defined ACLs from the server
|
||||||
|
memory to the ACL file.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` on success.
|
||||||
|
|
||||||
|
The command may fail with an error for several reasons: if the file cannot be
|
||||||
|
written or if the server is not configured to use an external ACL file.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```
|
||||||
|
> ACL SAVE
|
||||||
|
+OK
|
||||||
|
|
||||||
|
> ACL SAVE
|
||||||
|
-ERR There was an error trying to save the ACLs. Please check the server logs for more information
|
||||||
|
```
|
115
iredis/data/commands/acl-setuser.md
Normal file
115
iredis/data/commands/acl-setuser.md
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
Create an ACL user with the specified rules or modify the rules of an existing
|
||||||
|
user. This is the main interface in order to manipulate Redis ACL users
|
||||||
|
interactively: if the username does not exist, the command creates the username
|
||||||
|
without any privilege, then reads from left to right all the rules provided as
|
||||||
|
successive arguments, setting the user ACL rules as specified.
|
||||||
|
|
||||||
|
If the user already exists, the provided ACL rules are simply applied _in
|
||||||
|
addition_ to the rules already set. For example:
|
||||||
|
|
||||||
|
ACL SETUSER virginia on allkeys +set
|
||||||
|
|
||||||
|
The above command will create a user called `virginia` that is active (the on
|
||||||
|
rule), can access any key (allkeys rule), and can call the set command (+set
|
||||||
|
rule). Then another SETUSER call can modify the user rules:
|
||||||
|
|
||||||
|
ACL SETUSER virginia +get
|
||||||
|
|
||||||
|
The above rule will not apply the new rule to the user virginia, so other than
|
||||||
|
`SET`, the user virginia will now be able to also use the `GET` command.
|
||||||
|
|
||||||
|
When we want to be sure to define an user from scratch, without caring if it had
|
||||||
|
previously defined rules associated, we can use the special rule `reset` as
|
||||||
|
first rule, in order to flush all the other existing rules:
|
||||||
|
|
||||||
|
ACL SETUSER antirez reset [... other rules ...]
|
||||||
|
|
||||||
|
After resetting an user, it returns back to the status it has when it was just
|
||||||
|
created: non active (off rule), can't execute any command, can't access any key:
|
||||||
|
|
||||||
|
> ACL SETUSER antirez reset
|
||||||
|
+OK
|
||||||
|
> ACL LIST
|
||||||
|
1) "user antirez off -@all"
|
||||||
|
|
||||||
|
ACL rules are either words like "on", "off", "reset", "allkeys", or are special
|
||||||
|
rules that start with a special character, and are followed by another string
|
||||||
|
(without any space in between), like "+SET".
|
||||||
|
|
||||||
|
The following documentation is a reference manual about the capabilities of this
|
||||||
|
command, however our [ACL tutorial](/topics/acl) may be a more gentle
|
||||||
|
introduction to how the ACL system works in general.
|
||||||
|
|
||||||
|
## List of rules
|
||||||
|
|
||||||
|
This is a list of all the supported Redis ACL rules:
|
||||||
|
|
||||||
|
- `on`: set the user as active, it will be possible to authenticate as this user
|
||||||
|
using `AUTH <username> <password>`.
|
||||||
|
- `off`: set user as not active, it will be impossible to log as this user.
|
||||||
|
Please note that if a user gets disabled (set to off) after there are
|
||||||
|
connections already authenticated with such a user, the connections will
|
||||||
|
continue to work as expected. To also kill the old connections you can use
|
||||||
|
`CLIENT KILL` with the user option. An alternative is to delete the user with
|
||||||
|
`ACL DELUSER`, that will result in all the connections authenticated as the
|
||||||
|
deleted user to be disconnected.
|
||||||
|
- `~<pattern>`: add the specified key pattern (glob style pattern, like in the
|
||||||
|
`KEYS` command), to the list of key patterns accessible by the user. You can
|
||||||
|
add as many key patterns you want to the same user. Example: `~objects:*`
|
||||||
|
- `allkeys`: alias for `~*`, it allows the user to access all the keys.
|
||||||
|
- `resetkey`: removes all the key patterns from the list of key patterns the
|
||||||
|
user can access.
|
||||||
|
- `+<command>`: add this command to the list of the commands the user can call.
|
||||||
|
Example: `+zadd`.
|
||||||
|
- `+@<category>`: add all the commands in the specified category to the list of
|
||||||
|
commands the user is able to execute. Example: `+@string` (adds all the string
|
||||||
|
commands). For a list of categories check the `ACL CAT` command.
|
||||||
|
- `+<command>|<subcommand>`: add the specified command to the list of the
|
||||||
|
commands the user can execute, but only for the specified subcommand. Example:
|
||||||
|
`+config|get`. Generates an error if the specified command is already allowed
|
||||||
|
in its full version for the specified user. Note: there is no symmetrical
|
||||||
|
command to remove subcommands, you need to remove the whole command and re-add
|
||||||
|
the subcommands you want to allow. This is much safer than removing
|
||||||
|
subcommands, in the future Redis may add new dangerous subcommands, so
|
||||||
|
configuring by subtraction is not good.
|
||||||
|
- `allcommands`: alias of `+@all`. Adds all the commands there are in the
|
||||||
|
server, including _future commands_ loaded via module, to be executed by this
|
||||||
|
user.
|
||||||
|
- `-<command>`. Like `+<command>` but removes the command instead of adding it.
|
||||||
|
- `-@<category>`: Like `+@<category>` but removes all the commands in the
|
||||||
|
category instead of adding them.
|
||||||
|
- `nocommands`: alias for `-@all`. Removes all the commands, the user will no
|
||||||
|
longer be able to execute anything.
|
||||||
|
- `nopass`: the user is set as a "no password" user. It means that it will be
|
||||||
|
possible to authenticate as such user with any password. By default, the
|
||||||
|
`default` special user is set as "nopass". The `nopass` rule will also reset
|
||||||
|
all the configured passwords for the user.
|
||||||
|
- `>password`: Add the specified clear text password as an hashed password in
|
||||||
|
the list of the users passwords. Every user can have many active passwords, so
|
||||||
|
that password rotation will be simpler. The specified password is not stored
|
||||||
|
in cleartext inside the server. Example: `>mypassword`.
|
||||||
|
- `#<hashedpassword>`: Add the specified hashed password to the list of user
|
||||||
|
passwords. A Redis hashed password is hashed with SHA256 and translated into a
|
||||||
|
hexadecimal string. Example:
|
||||||
|
`#c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2`.
|
||||||
|
- `<password`: Like `>password` but removes the password instead of adding it.
|
||||||
|
- `!<hashedpassword>`: Like `#<hashedpassword>` but removes the password instead
|
||||||
|
of adding it.
|
||||||
|
- reset: Remove any capability from the user. It is set to off, without
|
||||||
|
passwords, unable to execute any command, unable to access any key.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` on success.
|
||||||
|
|
||||||
|
If the rules contain errors, the error is returned.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```
|
||||||
|
> ACL SETUSER alan allkeys +@string +@set -SADD >alanpassword
|
||||||
|
+OK
|
||||||
|
|
||||||
|
> ACL SETUSER antirez heeyyyy
|
||||||
|
(error) ERR Error in ACL SETUSER modifier 'heeyyyy': Syntax error
|
||||||
|
```
|
15
iredis/data/commands/acl-users.md
Normal file
15
iredis/data/commands/acl-users.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
The command shows a list of all the usernames of the currently configured users
|
||||||
|
in the Redis ACL system.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
An array of strings.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```
|
||||||
|
> ACL USERS
|
||||||
|
1) "anna"
|
||||||
|
2) "antirez"
|
||||||
|
3) "default"
|
||||||
|
```
|
14
iredis/data/commands/acl-whoami.md
Normal file
14
iredis/data/commands/acl-whoami.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
Return the username the current connection is authenticated with. New
|
||||||
|
connections are authenticated with the "default" user. They can change user
|
||||||
|
using `AUTH`.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@bulk-string-reply: the username of the current connection.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```
|
||||||
|
> ACL WHOAMI
|
||||||
|
"default"
|
||||||
|
```
|
55
iredis/data/commands/append.md
Normal file
55
iredis/data/commands/append.md
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
If `key` already exists and is a string, this command appends the `value` at the
|
||||||
|
end of the string. If `key` does not exist it is created and set as an empty
|
||||||
|
string, so `APPEND` will be similar to `SET` in this special case.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply: the length of the string after the append operation.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```cli
|
||||||
|
EXISTS mykey
|
||||||
|
APPEND mykey "Hello"
|
||||||
|
APPEND mykey " World"
|
||||||
|
GET mykey
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pattern: Time series
|
||||||
|
|
||||||
|
The `APPEND` command can be used to create a very compact representation of a
|
||||||
|
list of fixed-size samples, usually referred as _time series_. Every time a new
|
||||||
|
sample arrives we can store it using the command
|
||||||
|
|
||||||
|
```
|
||||||
|
APPEND timeseries "fixed-size sample"
|
||||||
|
```
|
||||||
|
|
||||||
|
Accessing individual elements in the time series is not hard:
|
||||||
|
|
||||||
|
- `STRLEN` can be used in order to obtain the number of samples.
|
||||||
|
- `GETRANGE` allows for random access of elements. If our time series have
|
||||||
|
associated time information we can easily implement a binary search to get
|
||||||
|
range combining `GETRANGE` with the Lua scripting engine available in Redis
|
||||||
|
2.6.
|
||||||
|
- `SETRANGE` can be used to overwrite an existing time series.
|
||||||
|
|
||||||
|
The limitation of this pattern is that we are forced into an append-only mode of
|
||||||
|
operation, there is no way to cut the time series to a given size easily because
|
||||||
|
Redis currently lacks a command able to trim string objects. However the space
|
||||||
|
efficiency of time series stored in this way is remarkable.
|
||||||
|
|
||||||
|
Hint: it is possible to switch to a different key based on the current Unix
|
||||||
|
time, in this way it is possible to have just a relatively small amount of
|
||||||
|
samples per key, to avoid dealing with very big keys, and to make this pattern
|
||||||
|
more friendly to be distributed across many Redis instances.
|
||||||
|
|
||||||
|
An example sampling the temperature of a sensor using fixed-size strings (using
|
||||||
|
a binary format is better in real implementations).
|
||||||
|
|
||||||
|
```cli
|
||||||
|
APPEND ts "0043"
|
||||||
|
APPEND ts "0035"
|
||||||
|
GETRANGE ts 0 3
|
||||||
|
GETRANGE ts 4 7
|
||||||
|
```
|
42
iredis/data/commands/auth.md
Normal file
42
iredis/data/commands/auth.md
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
The AUTH command authenticates the current connection in two cases:
|
||||||
|
|
||||||
|
1. If the Redis server is password protected via the `requirepass` option.
|
||||||
|
2. If a Redis 6.0 instance, or greater, is using the
|
||||||
|
[Redis ACL system](/topics/acl).
|
||||||
|
|
||||||
|
Redis versions prior of Redis 6 were only able to understand the one argument
|
||||||
|
version of the command:
|
||||||
|
|
||||||
|
AUTH <password>
|
||||||
|
|
||||||
|
This form just authenticates against the password set with `requirepass`. In
|
||||||
|
this configuration Redis will deny any command executed by the just connected
|
||||||
|
clients, unless the connection gets authenticated via `AUTH`.
|
||||||
|
|
||||||
|
If the password provided via AUTH matches the password in the configuration
|
||||||
|
file, the server replies with the `OK` status code and starts accepting
|
||||||
|
commands. Otherwise, an error is returned and the clients needs to try a new
|
||||||
|
password.
|
||||||
|
|
||||||
|
When Redis ACLs are used, the command should be given in an extended way:
|
||||||
|
|
||||||
|
AUTH <username> <password>
|
||||||
|
|
||||||
|
In order to authenticate the current connection with one of the connections
|
||||||
|
defined in the ACL list (see `ACL SETUSER`) and the official
|
||||||
|
[ACL guide](/topics/acl) for more information.
|
||||||
|
|
||||||
|
When ACLs are used, the single argument form of the command, where only the
|
||||||
|
password is specified, assumes that the implicit username is "default".
|
||||||
|
|
||||||
|
## Security notice
|
||||||
|
|
||||||
|
Because of the high performance nature of Redis, it is possible to try a lot of
|
||||||
|
passwords in parallel in very short time, so make sure to generate a strong and
|
||||||
|
very long password so that this attack is infeasible. A good way to generate
|
||||||
|
strong passwords is via the `ACL GENPASS` command.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply or an error if the password, or username/password pair, is
|
||||||
|
invalid.
|
37
iredis/data/commands/bgrewriteaof.md
Normal file
37
iredis/data/commands/bgrewriteaof.md
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
Instruct Redis to start an [Append Only File][tpaof] rewrite process. The
|
||||||
|
rewrite will create a small optimized version of the current Append Only File.
|
||||||
|
|
||||||
|
[tpaof]: /topics/persistence#append-only-file
|
||||||
|
|
||||||
|
If `BGREWRITEAOF` fails, no data gets lost as the old AOF will be untouched.
|
||||||
|
|
||||||
|
The rewrite will be only triggered by Redis if there is not already a background
|
||||||
|
process doing persistence.
|
||||||
|
|
||||||
|
Specifically:
|
||||||
|
|
||||||
|
- If a Redis child is creating a snapshot on disk, the AOF rewrite is
|
||||||
|
_scheduled_ but not started until the saving child producing the RDB file
|
||||||
|
terminates. In this case the `BGREWRITEAOF` will still return an positive
|
||||||
|
status reply, but with an appropriate message. You can check if an AOF rewrite
|
||||||
|
is scheduled looking at the `INFO` command as of Redis 2.6 or successive
|
||||||
|
versions.
|
||||||
|
- If an AOF rewrite is already in progress the command returns an error and no
|
||||||
|
AOF rewrite will be scheduled for a later time.
|
||||||
|
- If the AOF rewrite could start, but the attempt at starting it fails (for
|
||||||
|
instance because of an error in creating the child process), an error is
|
||||||
|
returned to the caller.
|
||||||
|
|
||||||
|
Since Redis 2.4 the AOF rewrite is automatically triggered by Redis, however the
|
||||||
|
`BGREWRITEAOF` command can be used to trigger a rewrite at any time.
|
||||||
|
|
||||||
|
Please refer to the [persistence documentation][tp] for detailed information.
|
||||||
|
|
||||||
|
[tp]: /topics/persistence
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: A simple string reply indicating that the rewriting
|
||||||
|
started or is about to start ASAP, when the call is executed with success.
|
||||||
|
|
||||||
|
The command may reply with an error in certain cases, as documented above.
|
28
iredis/data/commands/bgsave.md
Normal file
28
iredis/data/commands/bgsave.md
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
Save the DB in background.
|
||||||
|
|
||||||
|
Normally the OK code is immediately returned. Redis forks, the parent continues
|
||||||
|
to serve the clients, the child saves the DB on disk then exits.
|
||||||
|
|
||||||
|
An error is returned if there is already a background save running or if there
|
||||||
|
is another non-background-save process running, specifically an in-progress AOF
|
||||||
|
rewrite.
|
||||||
|
|
||||||
|
If `BGSAVE SCHEDULE` is used, the command will immediately return `OK` when an
|
||||||
|
AOF rewrite is in progress and schedule the background save to run at the next
|
||||||
|
opportunity.
|
||||||
|
|
||||||
|
A client may be able to check if the operation succeeded using the `LASTSAVE`
|
||||||
|
command.
|
||||||
|
|
||||||
|
Please refer to the [persistence documentation][tp] for detailed information.
|
||||||
|
|
||||||
|
[tp]: /topics/persistence
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `Background saving started` if `BGSAVE` started correctly
|
||||||
|
or `Background saving scheduled` when used with the `SCHEDULE` subcommand.
|
||||||
|
|
||||||
|
@history
|
||||||
|
|
||||||
|
- `>= 3.2.2`: Added the `SCHEDULE` option.
|
66
iredis/data/commands/bitcount.md
Normal file
66
iredis/data/commands/bitcount.md
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
Count the number of set bits (population counting) in a string.
|
||||||
|
|
||||||
|
By default all the bytes contained in the string are examined. It is possible to
|
||||||
|
specify the counting operation only in an interval passing the additional
|
||||||
|
arguments _start_ and _end_.
|
||||||
|
|
||||||
|
Like for the `GETRANGE` command start and end can contain negative values in
|
||||||
|
order to index bytes starting from the end of the string, where -1 is the last
|
||||||
|
byte, -2 is the penultimate, and so forth.
|
||||||
|
|
||||||
|
Non-existent keys are treated as empty strings, so the command will return zero.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply
|
||||||
|
|
||||||
|
The number of bits set to 1.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```cli
|
||||||
|
SET mykey "foobar"
|
||||||
|
BITCOUNT mykey
|
||||||
|
BITCOUNT mykey 0 0
|
||||||
|
BITCOUNT mykey 1 1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pattern: real-time metrics using bitmaps
|
||||||
|
|
||||||
|
Bitmaps are a very space-efficient representation of certain kinds of
|
||||||
|
information. One example is a Web application that needs the history of user
|
||||||
|
visits, so that for instance it is possible to determine what users are good
|
||||||
|
targets of beta features.
|
||||||
|
|
||||||
|
Using the `SETBIT` command this is trivial to accomplish, identifying every day
|
||||||
|
with a small progressive integer. For instance day 0 is the first day the
|
||||||
|
application was put online, day 1 the next day, and so forth.
|
||||||
|
|
||||||
|
Every time a user performs a page view, the application can register that in the
|
||||||
|
current day the user visited the web site using the `SETBIT` command setting the
|
||||||
|
bit corresponding to the current day.
|
||||||
|
|
||||||
|
Later it will be trivial to know the number of single days the user visited the
|
||||||
|
web site simply calling the `BITCOUNT` command against the bitmap.
|
||||||
|
|
||||||
|
A similar pattern where user IDs are used instead of days is described in the
|
||||||
|
article called "[Fast easy realtime metrics using Redis
|
||||||
|
bitmaps][hbgc212fermurb]".
|
||||||
|
|
||||||
|
[hbgc212fermurb]:
|
||||||
|
http://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps
|
||||||
|
|
||||||
|
## Performance considerations
|
||||||
|
|
||||||
|
In the above example of counting days, even after 10 years the application is
|
||||||
|
online we still have just `365*10` bits of data per user, that is just 456 bytes
|
||||||
|
per user. With this amount of data `BITCOUNT` is still as fast as any other O(1)
|
||||||
|
Redis command like `GET` or `INCR`.
|
||||||
|
|
||||||
|
When the bitmap is big, there are two alternatives:
|
||||||
|
|
||||||
|
- Taking a separated key that is incremented every time the bitmap is modified.
|
||||||
|
This can be very efficient and atomic using a small Redis Lua script.
|
||||||
|
- Running the bitmap incrementally using the `BITCOUNT` _start_ and _end_
|
||||||
|
optional parameters, accumulating the results client-side, and optionally
|
||||||
|
caching the result into a key.
|
155
iredis/data/commands/bitfield.md
Normal file
155
iredis/data/commands/bitfield.md
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
The command treats a Redis string as a array of bits, and is capable of
|
||||||
|
addressing specific integer fields of varying bit widths and arbitrary non
|
||||||
|
(necessary) aligned offset. In practical terms using this command you can set,
|
||||||
|
for example, a signed 5 bits integer at bit offset 1234 to a specific value,
|
||||||
|
retrieve a 31 bit unsigned integer from offset 4567. Similarly the command
|
||||||
|
handles increments and decrements of the specified integers, providing
|
||||||
|
guaranteed and well specified overflow and underflow behavior that the user can
|
||||||
|
configure.
|
||||||
|
|
||||||
|
`BITFIELD` is able to operate with multiple bit fields in the same command call.
|
||||||
|
It takes a list of operations to perform, and returns an array of replies, where
|
||||||
|
each array matches the corresponding operation in the list of arguments.
|
||||||
|
|
||||||
|
For example the following command increments an 5 bit signed integer at bit
|
||||||
|
offset 100, and gets the value of the 4 bit unsigned integer at bit offset 0:
|
||||||
|
|
||||||
|
> BITFIELD mykey INCRBY i5 100 1 GET u4 0
|
||||||
|
1) (integer) 1
|
||||||
|
2) (integer) 0
|
||||||
|
|
||||||
|
Note that:
|
||||||
|
|
||||||
|
1. Addressing with `GET` bits outside the current string length (including the
|
||||||
|
case the key does not exist at all), results in the operation to be performed
|
||||||
|
like the missing part all consists of bits set to 0.
|
||||||
|
2. Addressing with `SET` or `INCRBY` bits outside the current string length will
|
||||||
|
enlarge the string, zero-padding it, as needed, for the minimal length
|
||||||
|
needed, according to the most far bit touched.
|
||||||
|
|
||||||
|
## Supported subcommands and integer types
|
||||||
|
|
||||||
|
The following is the list of supported commands.
|
||||||
|
|
||||||
|
- **GET** `<type>` `<offset>` -- Returns the specified bit field.
|
||||||
|
- **SET** `<type>` `<offset>` `<value>` -- Set the specified bit field and
|
||||||
|
returns its old value.
|
||||||
|
- **INCRBY** `<type>` `<offset>` `<increment>` -- Increments or decrements (if a
|
||||||
|
negative increment is given) the specified bit field and returns the new
|
||||||
|
value.
|
||||||
|
|
||||||
|
There is another subcommand that only changes the behavior of successive
|
||||||
|
`INCRBY` subcommand calls by setting the overflow behavior:
|
||||||
|
|
||||||
|
- **OVERFLOW** `[WRAP|SAT|FAIL]`
|
||||||
|
|
||||||
|
Where an integer type is expected, it can be composed by prefixing with `i` for
|
||||||
|
signed integers and `u` for unsigned integers with the number of bits of our
|
||||||
|
integer type. So for example `u8` is an unsigned integer of 8 bits and `i16` is
|
||||||
|
a signed integer of 16 bits.
|
||||||
|
|
||||||
|
The supported types are up to 64 bits for signed integers, and up to 63 bits for
|
||||||
|
unsigned integers. This limitation with unsigned integers is due to the fact
|
||||||
|
that currently the Redis protocol is unable to return 64 bit unsigned integers
|
||||||
|
as replies.
|
||||||
|
|
||||||
|
## Bits and positional offsets
|
||||||
|
|
||||||
|
There are two ways in order to specify offsets in the bitfield command. If a
|
||||||
|
number without any prefix is specified, it is used just as a zero based bit
|
||||||
|
offset inside the string.
|
||||||
|
|
||||||
|
However if the offset is prefixed with a `#` character, the specified offset is
|
||||||
|
multiplied by the integer type width, so for example:
|
||||||
|
|
||||||
|
BITFIELD mystring SET i8 #0 100 SET i8 #1 200
|
||||||
|
|
||||||
|
Will set the first i8 integer at offset 0 and the second at offset 8. This way
|
||||||
|
you don't have to do the math yourself inside your client if what you want is a
|
||||||
|
plain array of integers of a given size.
|
||||||
|
|
||||||
|
## Overflow control
|
||||||
|
|
||||||
|
Using the `OVERFLOW` command the user is able to fine-tune the behavior of the
|
||||||
|
increment or decrement overflow (or underflow) by specifying one of the
|
||||||
|
following behaviors:
|
||||||
|
|
||||||
|
- **WRAP**: wrap around, both with signed and unsigned integers. In the case of
|
||||||
|
unsigned integers, wrapping is like performing the operation modulo the
|
||||||
|
maximum value the integer can contain (the C standard behavior). With signed
|
||||||
|
integers instead wrapping means that overflows restart towards the most
|
||||||
|
negative value and underflows towards the most positive ones, so for example
|
||||||
|
if an `i8` integer is set to the value 127, incrementing it by 1 will yield
|
||||||
|
`-128`.
|
||||||
|
- **SAT**: uses saturation arithmetic, that is, on underflows the value is set
|
||||||
|
to the minimum integer value, and on overflows to the maximum integer value.
|
||||||
|
For example incrementing an `i8` integer starting from value 120 with an
|
||||||
|
increment of 10, will result into the value 127, and further increments will
|
||||||
|
always keep the value at 127. The same happens on underflows, but towards the
|
||||||
|
value is blocked at the most negative value.
|
||||||
|
- **FAIL**: in this mode no operation is performed on overflows or underflows
|
||||||
|
detected. The corresponding return value is set to NULL to signal the
|
||||||
|
condition to the caller.
|
||||||
|
|
||||||
|
Note that each `OVERFLOW` statement only affects the `INCRBY` commands that
|
||||||
|
follow it in the list of subcommands, up to the next `OVERFLOW` statement.
|
||||||
|
|
||||||
|
By default, **WRAP** is used if not otherwise specified.
|
||||||
|
|
||||||
|
> BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1
|
||||||
|
1) (integer) 1
|
||||||
|
2) (integer) 1
|
||||||
|
> BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1
|
||||||
|
1) (integer) 2
|
||||||
|
2) (integer) 2
|
||||||
|
> BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1
|
||||||
|
1) (integer) 3
|
||||||
|
2) (integer) 3
|
||||||
|
> BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1
|
||||||
|
1) (integer) 0
|
||||||
|
2) (integer) 3
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
The command returns an array with each entry being the corresponding result of
|
||||||
|
the sub command given at the same position. `OVERFLOW` subcommands don't count
|
||||||
|
as generating a reply.
|
||||||
|
|
||||||
|
The following is an example of `OVERFLOW FAIL` returning NULL.
|
||||||
|
|
||||||
|
> BITFIELD mykey OVERFLOW FAIL incrby u2 102 1
|
||||||
|
1) (nil)
|
||||||
|
|
||||||
|
## Motivations
|
||||||
|
|
||||||
|
The motivation for this command is that the ability to store many small integers
|
||||||
|
as a single large bitmap (or segmented over a few keys to avoid having huge
|
||||||
|
keys) is extremely memory efficient, and opens new use cases for Redis to be
|
||||||
|
applied, especially in the field of real time analytics. This use cases are
|
||||||
|
supported by the ability to specify the overflow in a controlled way.
|
||||||
|
|
||||||
|
Fun fact: Reddit's 2017 April fools' project
|
||||||
|
[r/place](https://reddit.com/r/place) was
|
||||||
|
[built using the Redis BITFIELD command](https://redditblog.com/2017/04/13/how-we-built-rplace/)
|
||||||
|
in order to take an in-memory representation of the collaborative canvas.
|
||||||
|
|
||||||
|
## Performance considerations
|
||||||
|
|
||||||
|
Usually `BITFIELD` is a fast command, however note that addressing far bits of
|
||||||
|
currently short strings will trigger an allocation that may be more costly than
|
||||||
|
executing the command on bits already existing.
|
||||||
|
|
||||||
|
## Orders of bits
|
||||||
|
|
||||||
|
The representation used by `BITFIELD` considers the bitmap as having the bit
|
||||||
|
number 0 to be the most significant bit of the first byte, and so forth, so for
|
||||||
|
example setting a 5 bits unsigned integer to value 23 at offset 7 into a bitmap
|
||||||
|
previously set to all zeroes, will produce the following representation:
|
||||||
|
|
||||||
|
+--------+--------+
|
||||||
|
|00000001|01110000|
|
||||||
|
+--------+--------+
|
||||||
|
|
||||||
|
When offsets and integer sizes are aligned to bytes boundaries, this is the same
|
||||||
|
as big endian, however when such alignment does not exist, its important to also
|
||||||
|
understand how the bits inside a byte are ordered.
|
61
iredis/data/commands/bitop.md
Normal file
61
iredis/data/commands/bitop.md
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
Perform a bitwise operation between multiple keys (containing string values) and
|
||||||
|
store the result in the destination key.
|
||||||
|
|
||||||
|
The `BITOP` command supports four bitwise operations: **AND**, **OR**, **XOR**
|
||||||
|
and **NOT**, thus the valid forms to call the command are:
|
||||||
|
|
||||||
|
- `BITOP AND destkey srckey1 srckey2 srckey3 ... srckeyN`
|
||||||
|
- `BITOP OR destkey srckey1 srckey2 srckey3 ... srckeyN`
|
||||||
|
- `BITOP XOR destkey srckey1 srckey2 srckey3 ... srckeyN`
|
||||||
|
- `BITOP NOT destkey srckey`
|
||||||
|
|
||||||
|
As you can see **NOT** is special as it only takes an input key, because it
|
||||||
|
performs inversion of bits so it only makes sense as an unary operator.
|
||||||
|
|
||||||
|
The result of the operation is always stored at `destkey`.
|
||||||
|
|
||||||
|
## Handling of strings with different lengths
|
||||||
|
|
||||||
|
When an operation is performed between strings having different lengths, all the
|
||||||
|
strings shorter than the longest string in the set are treated as if they were
|
||||||
|
zero-padded up to the length of the longest string.
|
||||||
|
|
||||||
|
The same holds true for non-existent keys, that are considered as a stream of
|
||||||
|
zero bytes up to the length of the longest string.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply
|
||||||
|
|
||||||
|
The size of the string stored in the destination key, that is equal to the size
|
||||||
|
of the longest input string.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```cli
|
||||||
|
SET key1 "foobar"
|
||||||
|
SET key2 "abcdef"
|
||||||
|
BITOP AND dest key1 key2
|
||||||
|
GET dest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pattern: real time metrics using bitmaps
|
||||||
|
|
||||||
|
`BITOP` is a good complement to the pattern documented in the `BITCOUNT` command
|
||||||
|
documentation. Different bitmaps can be combined in order to obtain a target
|
||||||
|
bitmap where the population counting operation is performed.
|
||||||
|
|
||||||
|
See the article called "[Fast easy realtime metrics using Redis
|
||||||
|
bitmaps][hbgc212fermurb]" for a interesting use cases.
|
||||||
|
|
||||||
|
[hbgc212fermurb]:
|
||||||
|
http://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps
|
||||||
|
|
||||||
|
## Performance considerations
|
||||||
|
|
||||||
|
`BITOP` is a potentially slow command as it runs in O(N) time. Care should be
|
||||||
|
taken when running it against long input strings.
|
||||||
|
|
||||||
|
For real-time metrics and statistics involving large inputs a good approach is
|
||||||
|
to use a replica (with read-only option disabled) where the bit-wise operations
|
||||||
|
are performed to avoid blocking the master instance.
|
60
iredis/data/commands/bitpos.md
Normal file
60
iredis/data/commands/bitpos.md
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
Return the position of the first bit set to 1 or 0 in a string.
|
||||||
|
|
||||||
|
The position is returned, thinking of the string as an array of bits from left
|
||||||
|
to right, where the first byte's most significant bit is at position 0, the
|
||||||
|
second byte's most significant bit is at position 8, and so forth.
|
||||||
|
|
||||||
|
The same bit position convention is followed by `GETBIT` and `SETBIT`.
|
||||||
|
|
||||||
|
By default, all the bytes contained in the string are examined. It is possible
|
||||||
|
to look for bits only in a specified interval passing the additional arguments
|
||||||
|
_start_ and _end_ (it is possible to just pass _start_, the operation will
|
||||||
|
assume that the end is the last byte of the string. However there are semantic
|
||||||
|
differences as explained later). The range is interpreted as a range of bytes
|
||||||
|
and not a range of bits, so `start=0` and `end=2` means to look at the first
|
||||||
|
three bytes.
|
||||||
|
|
||||||
|
Note that bit positions are returned always as absolute values starting from bit
|
||||||
|
zero even when _start_ and _end_ are used to specify a range.
|
||||||
|
|
||||||
|
Like for the `GETRANGE` command start and end can contain negative values in
|
||||||
|
order to index bytes starting from the end of the string, where -1 is the last
|
||||||
|
byte, -2 is the penultimate, and so forth.
|
||||||
|
|
||||||
|
Non-existent keys are treated as empty strings.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply
|
||||||
|
|
||||||
|
The command returns the position of the first bit set to 1 or 0 according to the
|
||||||
|
request.
|
||||||
|
|
||||||
|
If we look for set bits (the bit argument is 1) and the string is empty or
|
||||||
|
composed of just zero bytes, -1 is returned.
|
||||||
|
|
||||||
|
If we look for clear bits (the bit argument is 0) and the string only contains
|
||||||
|
bit set to 1, the function returns the first bit not part of the string on the
|
||||||
|
right. So if the string is three bytes set to the value `0xff` the command
|
||||||
|
`BITPOS key 0` will return 24, since up to bit 23 all the bits are 1.
|
||||||
|
|
||||||
|
Basically, the function considers the right of the string as padded with zeros
|
||||||
|
if you look for clear bits and specify no range or the _start_ argument
|
||||||
|
**only**.
|
||||||
|
|
||||||
|
However, this behavior changes if you are looking for clear bits and specify a
|
||||||
|
range with both **start** and **end**. If no clear bit is found in the specified
|
||||||
|
range, the function returns -1 as the user specified a clear range and there are
|
||||||
|
no 0 bits in that range.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```cli
|
||||||
|
SET mykey "\xff\xf0\x00"
|
||||||
|
BITPOS mykey 0
|
||||||
|
SET mykey "\x00\xff\xf0"
|
||||||
|
BITPOS mykey 1 0
|
||||||
|
BITPOS mykey 1 2
|
||||||
|
set mykey "\x00\x00\x00"
|
||||||
|
BITPOS mykey 1
|
||||||
|
```
|
182
iredis/data/commands/blpop.md
Normal file
182
iredis/data/commands/blpop.md
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
`BLPOP` is a blocking list pop primitive. It is the blocking version of `LPOP`
|
||||||
|
because it blocks the connection when there are no elements to pop from any of
|
||||||
|
the given lists. An element is popped from the head of the first list that is
|
||||||
|
non-empty, with the given keys being checked in the order that they are given.
|
||||||
|
|
||||||
|
## Non-blocking behavior
|
||||||
|
|
||||||
|
When `BLPOP` is called, if at least one of the specified keys contains a
|
||||||
|
non-empty list, an element is popped from the head of the list and returned to
|
||||||
|
the caller together with the `key` it was popped from.
|
||||||
|
|
||||||
|
Keys are checked in the order that they are given. Let's say that the key
|
||||||
|
`list1` doesn't exist and `list2` and `list3` hold non-empty lists. Consider the
|
||||||
|
following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
BLPOP list1 list2 list3 0
|
||||||
|
```
|
||||||
|
|
||||||
|
`BLPOP` guarantees to return an element from the list stored at `list2` (since
|
||||||
|
it is the first non empty list when checking `list1`, `list2` and `list3` in
|
||||||
|
that order).
|
||||||
|
|
||||||
|
## Blocking behavior
|
||||||
|
|
||||||
|
If none of the specified keys exist, `BLPOP` blocks the connection until another
|
||||||
|
client performs an `LPUSH` or `RPUSH` operation against one of the keys.
|
||||||
|
|
||||||
|
Once new data is present on one of the lists, the client returns with the name
|
||||||
|
of the key unblocking it and the popped value.
|
||||||
|
|
||||||
|
When `BLPOP` causes a client to block and a non-zero timeout is specified, the
|
||||||
|
client will unblock returning a `nil` multi-bulk value when the specified
|
||||||
|
timeout has expired without a push operation against at least one of the
|
||||||
|
specified keys.
|
||||||
|
|
||||||
|
**The timeout argument is interpreted as an integer value specifying the maximum
|
||||||
|
number of seconds to block**. A timeout of zero can be used to block
|
||||||
|
indefinitely.
|
||||||
|
|
||||||
|
## What key is served first? What client? What element? Priority ordering details.
|
||||||
|
|
||||||
|
- If the client tries to blocks for multiple keys, but at least one key contains
|
||||||
|
elements, the returned key / element pair is the first key from left to right
|
||||||
|
that has one or more elements. In this case the client is not blocked. So for
|
||||||
|
instance `BLPOP key1 key2 key3 key4 0`, assuming that both `key2` and `key4`
|
||||||
|
are non-empty, will always return an element from `key2`.
|
||||||
|
- If multiple clients are blocked for the same key, the first client to be
|
||||||
|
served is the one that was waiting for more time (the first that blocked for
|
||||||
|
the key). Once a client is unblocked it does not retain any priority, when it
|
||||||
|
blocks again with the next call to `BLPOP` it will be served accordingly to
|
||||||
|
the number of clients already blocked for the same key, that will all be
|
||||||
|
served before it (from the first to the last that blocked).
|
||||||
|
- When a client is blocking for multiple keys at the same time, and elements are
|
||||||
|
available at the same time in multiple keys (because of a transaction or a Lua
|
||||||
|
script added elements to multiple lists), the client will be unblocked using
|
||||||
|
the first key that received a push operation (assuming it has enough elements
|
||||||
|
to serve our client, as there may be other clients as well waiting for this
|
||||||
|
key). Basically after the execution of every command Redis will run a list of
|
||||||
|
all the keys that received data AND that have at least a client blocked. The
|
||||||
|
list is ordered by new element arrival time, from the first key that received
|
||||||
|
data to the last. For every key processed, Redis will serve all the clients
|
||||||
|
waiting for that key in a FIFO fashion, as long as there are elements in this
|
||||||
|
key. When the key is empty or there are no longer clients waiting for this
|
||||||
|
key, the next key that received new data in the previous command / transaction
|
||||||
|
/ script is processed, and so forth.
|
||||||
|
|
||||||
|
## Behavior of `!BLPOP` when multiple elements are pushed inside a list.
|
||||||
|
|
||||||
|
There are times when a list can receive multiple elements in the context of the
|
||||||
|
same conceptual command:
|
||||||
|
|
||||||
|
- Variadic push operations such as `LPUSH mylist a b c`.
|
||||||
|
- After an `EXEC` of a `MULTI` block with multiple push operations against the
|
||||||
|
same list.
|
||||||
|
- Executing a Lua Script with Redis 2.6 or newer.
|
||||||
|
|
||||||
|
When multiple elements are pushed inside a list where there are clients
|
||||||
|
blocking, the behavior is different for Redis 2.4 and Redis 2.6 or newer.
|
||||||
|
|
||||||
|
For Redis 2.6 what happens is that the command performing multiple pushes is
|
||||||
|
executed, and _only after_ the execution of the command the blocked clients are
|
||||||
|
served. Consider this sequence of commands.
|
||||||
|
|
||||||
|
Client A: BLPOP foo 0
|
||||||
|
Client B: LPUSH foo a b c
|
||||||
|
|
||||||
|
If the above condition happens using a Redis 2.6 server or greater, Client **A**
|
||||||
|
will be served with the `c` element, because after the `LPUSH` command the list
|
||||||
|
contains `c,b,a`, so taking an element from the left means to return `c`.
|
||||||
|
|
||||||
|
Instead Redis 2.4 works in a different way: clients are served _in the context_
|
||||||
|
of the push operation, so as long as `LPUSH foo a b c` starts pushing the first
|
||||||
|
element to the list, it will be delivered to the Client **A**, that will receive
|
||||||
|
`a` (the first element pushed).
|
||||||
|
|
||||||
|
The behavior of Redis 2.4 creates a lot of problems when replicating or
|
||||||
|
persisting data into the AOF file, so the much more generic and semantically
|
||||||
|
simpler behavior was introduced into Redis 2.6 to prevent problems.
|
||||||
|
|
||||||
|
Note that for the same reason a Lua script or a `MULTI/EXEC` block may push
|
||||||
|
elements into a list and afterward **delete the list**. In this case the blocked
|
||||||
|
clients will not be served at all and will continue to be blocked as long as no
|
||||||
|
data is present on the list after the execution of a single command,
|
||||||
|
transaction, or script.
|
||||||
|
|
||||||
|
## `!BLPOP` inside a `!MULTI` / `!EXEC` transaction
|
||||||
|
|
||||||
|
`BLPOP` can be used with pipelining (sending multiple commands and reading the
|
||||||
|
replies in batch), however this setup makes sense almost solely when it is the
|
||||||
|
last command of the pipeline.
|
||||||
|
|
||||||
|
Using `BLPOP` inside a `MULTI` / `EXEC` block does not make a lot of sense as it
|
||||||
|
would require blocking the entire server in order to execute the block
|
||||||
|
atomically, which in turn does not allow other clients to perform a push
|
||||||
|
operation. For this reason the behavior of `BLPOP` inside `MULTI` / `EXEC` when
|
||||||
|
the list is empty is to return a `nil` multi-bulk reply, which is the same thing
|
||||||
|
that happens when the timeout is reached.
|
||||||
|
|
||||||
|
If you like science fiction, think of time flowing at infinite speed inside a
|
||||||
|
`MULTI` / `EXEC` block...
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@array-reply: specifically:
|
||||||
|
|
||||||
|
- A `nil` multi-bulk when no element could be popped and the timeout expired.
|
||||||
|
- A two-element multi-bulk with the first element being the name of the key
|
||||||
|
where an element was popped and the second element being the value of the
|
||||||
|
popped element.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```
|
||||||
|
redis> DEL list1 list2
|
||||||
|
(integer) 0
|
||||||
|
redis> RPUSH list1 a b c
|
||||||
|
(integer) 3
|
||||||
|
redis> BLPOP list1 list2 0
|
||||||
|
1) "list1"
|
||||||
|
2) "a"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reliable queues
|
||||||
|
|
||||||
|
When `BLPOP` returns an element to the client, it also removes the element from
|
||||||
|
the list. This means that the element only exists in the context of the client:
|
||||||
|
if the client crashes while processing the returned element, it is lost forever.
|
||||||
|
|
||||||
|
This can be a problem with some application where we want a more reliable
|
||||||
|
messaging system. When this is the case, please check the `BRPOPLPUSH` command,
|
||||||
|
that is a variant of `BLPOP` that adds the returned element to a target list
|
||||||
|
before returning it to the client.
|
||||||
|
|
||||||
|
## Pattern: Event notification
|
||||||
|
|
||||||
|
Using blocking list operations it is possible to mount different blocking
|
||||||
|
primitives. For instance for some application you may need to block waiting for
|
||||||
|
elements into a Redis Set, so that as far as a new element is added to the Set,
|
||||||
|
it is possible to retrieve it without resort to polling. This would require a
|
||||||
|
blocking version of `SPOP` that is not available, but using blocking list
|
||||||
|
operations we can easily accomplish this task.
|
||||||
|
|
||||||
|
The consumer will do:
|
||||||
|
|
||||||
|
```
|
||||||
|
LOOP forever
|
||||||
|
WHILE SPOP(key) returns elements
|
||||||
|
... process elements ...
|
||||||
|
END
|
||||||
|
BRPOP helper_key
|
||||||
|
END
|
||||||
|
```
|
||||||
|
|
||||||
|
While in the producer side we'll use simply:
|
||||||
|
|
||||||
|
```
|
||||||
|
MULTI
|
||||||
|
SADD key element
|
||||||
|
LPUSH helper_key x
|
||||||
|
EXEC
|
||||||
|
```
|
31
iredis/data/commands/brpop.md
Normal file
31
iredis/data/commands/brpop.md
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
`BRPOP` is a blocking list pop primitive. It is the blocking version of `RPOP`
|
||||||
|
because it blocks the connection when there are no elements to pop from any of
|
||||||
|
the given lists. An element is popped from the tail of the first list that is
|
||||||
|
non-empty, with the given keys being checked in the order that they are given.
|
||||||
|
|
||||||
|
See the [BLPOP documentation][cb] for the exact semantics, since `BRPOP` is
|
||||||
|
identical to `BLPOP` with the only difference being that it pops elements from
|
||||||
|
the tail of a list instead of popping from the head.
|
||||||
|
|
||||||
|
[cb]: /commands/blpop
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@array-reply: specifically:
|
||||||
|
|
||||||
|
- A `nil` multi-bulk when no element could be popped and the timeout expired.
|
||||||
|
- A two-element multi-bulk with the first element being the name of the key
|
||||||
|
where an element was popped and the second element being the value of the
|
||||||
|
popped element.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```
|
||||||
|
redis> DEL list1 list2
|
||||||
|
(integer) 0
|
||||||
|
redis> RPUSH list1 a b c
|
||||||
|
(integer) 3
|
||||||
|
redis> BRPOP list1 list2 0
|
||||||
|
1) "list1"
|
||||||
|
2) "c"
|
||||||
|
```
|
21
iredis/data/commands/brpoplpush.md
Normal file
21
iredis/data/commands/brpoplpush.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
`BRPOPLPUSH` is the blocking variant of `RPOPLPUSH`. When `source` contains
|
||||||
|
elements, this command behaves exactly like `RPOPLPUSH`. When used inside a
|
||||||
|
`MULTI`/`EXEC` block, this command behaves exactly like `RPOPLPUSH`. When
|
||||||
|
`source` is empty, Redis will block the connection until another client pushes
|
||||||
|
to it or until `timeout` is reached. A `timeout` of zero can be used to block
|
||||||
|
indefinitely.
|
||||||
|
|
||||||
|
See `RPOPLPUSH` for more information.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@bulk-string-reply: the element being popped from `source` and pushed to
|
||||||
|
`destination`. If `timeout` is reached, a @nil-reply is returned.
|
||||||
|
|
||||||
|
## Pattern: Reliable queue
|
||||||
|
|
||||||
|
Please see the pattern description in the `RPOPLPUSH` documentation.
|
||||||
|
|
||||||
|
## Pattern: Circular list
|
||||||
|
|
||||||
|
Please see the pattern description in the `RPOPLPUSH` documentation.
|
37
iredis/data/commands/bzpopmax.md
Normal file
37
iredis/data/commands/bzpopmax.md
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
`BZPOPMAX` is the blocking variant of the sorted set `ZPOPMAX` primitive.
|
||||||
|
|
||||||
|
It is the blocking version because it blocks the connection when there are no
|
||||||
|
members to pop from any of the given sorted sets. A member with the highest
|
||||||
|
score is popped from first sorted set that is non-empty, with the given keys
|
||||||
|
being checked in the order that they are given.
|
||||||
|
|
||||||
|
The `timeout` argument is interpreted as an integer value specifying the maximum
|
||||||
|
number of seconds to block. A timeout of zero can be used to block indefinitely.
|
||||||
|
|
||||||
|
See the [BZPOPMIN documentation][cb] for the exact semantics, since `BZPOPMAX`
|
||||||
|
is identical to `BZPOPMIN` with the only difference being that it pops members
|
||||||
|
with the highest scores instead of popping the ones with the lowest scores.
|
||||||
|
|
||||||
|
[cb]: /commands/bzpopmin
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@array-reply: specifically:
|
||||||
|
|
||||||
|
- A `nil` multi-bulk when no element could be popped and the timeout expired.
|
||||||
|
- A three-element multi-bulk with the first element being the name of the key
|
||||||
|
where a member was popped, the second element is the popped member itself, and
|
||||||
|
the third element is the score of the popped element.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```
|
||||||
|
redis> DEL zset1 zset2
|
||||||
|
(integer) 0
|
||||||
|
redis> ZADD zset1 0 a 1 b 2 c
|
||||||
|
(integer) 3
|
||||||
|
redis> BZPOPMAX zset1 zset2 0
|
||||||
|
1) "zset1"
|
||||||
|
2) "c"
|
||||||
|
3) "2"
|
||||||
|
```
|
37
iredis/data/commands/bzpopmin.md
Normal file
37
iredis/data/commands/bzpopmin.md
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
`BZPOPMIN` is the blocking variant of the sorted set `ZPOPMIN` primitive.
|
||||||
|
|
||||||
|
It is the blocking version because it blocks the connection when there are no
|
||||||
|
members to pop from any of the given sorted sets. A member with the lowest score
|
||||||
|
is popped from first sorted set that is non-empty, with the given keys being
|
||||||
|
checked in the order that they are given.
|
||||||
|
|
||||||
|
The `timeout` argument is interpreted as an integer value specifying the maximum
|
||||||
|
number of seconds to block. A timeout of zero can be used to block indefinitely.
|
||||||
|
|
||||||
|
See the [BLPOP documentation][cl] for the exact semantics, since `BZPOPMIN` is
|
||||||
|
identical to `BLPOP` with the only difference being the data structure being
|
||||||
|
popped from.
|
||||||
|
|
||||||
|
[cl]: /commands/blpop
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@array-reply: specifically:
|
||||||
|
|
||||||
|
- A `nil` multi-bulk when no element could be popped and the timeout expired.
|
||||||
|
- A three-element multi-bulk with the first element being the name of the key
|
||||||
|
where a member was popped, the second element is the popped member itself, and
|
||||||
|
the third element is the score of the popped element.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```
|
||||||
|
redis> DEL zset1 zset2
|
||||||
|
(integer) 0
|
||||||
|
redis> ZADD zset1 0 a 1 b 2 c
|
||||||
|
(integer) 3
|
||||||
|
redis> BZPOPMIN zset1 zset2 0
|
||||||
|
1) "zset1"
|
||||||
|
2) "a"
|
||||||
|
3) "0"
|
||||||
|
```
|
19
iredis/data/commands/client-caching.md
Normal file
19
iredis/data/commands/client-caching.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
This command controls the tracking of the keys in the next command executed by
|
||||||
|
the connection, when tracking is enabled in `OPTIN` or `OPTOUT` mode. Please
|
||||||
|
check the [client side caching documentation](/topics/client-side-caching) for
|
||||||
|
background informations.
|
||||||
|
|
||||||
|
When tracking is enabled Redis, using the `CLIENT TRACKING` command, it is
|
||||||
|
possible to specify the `OPTIN` or `OPTOUT` options, so that keys in read only
|
||||||
|
commands are not automatically remembered by the server to be invalidated later.
|
||||||
|
When we are in `OPTIN` mode, we can enable the tracking of the keys in the next
|
||||||
|
command by calling `CLIENT CACHING yes` immediately before it. Similarly when we
|
||||||
|
are in `OPTOUT` mode, and keys are normally tracked, we can avoid the keys in
|
||||||
|
the next command to be tracked using `CLIENT CACHING no`.
|
||||||
|
|
||||||
|
Basically the command sets a state in the connection, that is valid only for the
|
||||||
|
next command execution, that will modify the behavior of client tracking.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` or an error if the argument is not yes or no.
|
7
iredis/data/commands/client-getname.md
Normal file
7
iredis/data/commands/client-getname.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
The `CLIENT GETNAME` returns the name of the current connection as set by
|
||||||
|
`CLIENT SETNAME`. Since every new connection starts without an associated name,
|
||||||
|
if no name was assigned a null bulk reply is returned.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@bulk-string-reply: The connection name, or a null bulk reply if no name is set.
|
13
iredis/data/commands/client-getredir.md
Normal file
13
iredis/data/commands/client-getredir.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
This command returns the client ID we are redirecting our
|
||||||
|
[tracking](/topics/client-side-caching) notifications to. We set a client to
|
||||||
|
redirect to when using `CLIENT TRACKING` to enable tracking. However in order to
|
||||||
|
avoid forcing client libraries implementations to remember the ID notifications
|
||||||
|
are redirected to, this command exists in order to improve introspection and
|
||||||
|
allow clients to check later if redirection is active and towards which client
|
||||||
|
ID.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply: the ID of the client we are redirecting the notifications to.
|
||||||
|
The command returns `-1` if client tracking is not enabled, or `0` if client
|
||||||
|
tracking is enabled but we are not redirecting the notifications to any client.
|
25
iredis/data/commands/client-id.md
Normal file
25
iredis/data/commands/client-id.md
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
The command just returns the ID of the current connection. Every connection ID
|
||||||
|
has certain guarantees:
|
||||||
|
|
||||||
|
1. It is never repeated, so if `CLIENT ID` returns the same number, the caller
|
||||||
|
can be sure that the underlying client did not disconnect and reconnect the
|
||||||
|
connection, but it is still the same connection.
|
||||||
|
2. The ID is monotonically incremental. If the ID of a connection is greater
|
||||||
|
than the ID of another connection, it is guaranteed that the second
|
||||||
|
connection was established with the server at a later time.
|
||||||
|
|
||||||
|
This command is especially useful together with `CLIENT UNBLOCK` which was
|
||||||
|
introduced also in Redis 5 together with `CLIENT ID`. Check the `CLIENT UNBLOCK`
|
||||||
|
command page for a pattern involving the two commands.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```cli
|
||||||
|
CLIENT ID
|
||||||
|
```
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply
|
||||||
|
|
||||||
|
The id of the client.
|
73
iredis/data/commands/client-kill.md
Normal file
73
iredis/data/commands/client-kill.md
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
The `CLIENT KILL` command closes a given client connection. Up to Redis 2.8.11
|
||||||
|
it was possible to close a connection only by client address, using the
|
||||||
|
following form:
|
||||||
|
|
||||||
|
CLIENT KILL addr:port
|
||||||
|
|
||||||
|
The `ip:port` should match a line returned by the `CLIENT LIST` command (`addr`
|
||||||
|
field).
|
||||||
|
|
||||||
|
However starting with Redis 2.8.12 or greater, the command accepts the following
|
||||||
|
form:
|
||||||
|
|
||||||
|
CLIENT KILL <filter> <value> ... ... <filter> <value>
|
||||||
|
|
||||||
|
With the new form it is possible to kill clients by different attributes instead
|
||||||
|
of killing just by address. The following filters are available:
|
||||||
|
|
||||||
|
- `CLIENT KILL ADDR ip:port`. This is exactly the same as the old
|
||||||
|
three-arguments behavior.
|
||||||
|
- `CLIENT KILL ID client-id`. Allows to kill a client by its unique `ID` field,
|
||||||
|
which was introduced in the `CLIENT LIST` command starting from Redis 2.8.12.
|
||||||
|
- `CLIENT KILL TYPE type`, where _type_ is one of `normal`, `master`, `slave`
|
||||||
|
and `pubsub` (the `master` type is available from v3.2). This closes the
|
||||||
|
connections of **all the clients** in the specified class. Note that clients
|
||||||
|
blocked into the `MONITOR` command are considered to belong to the `normal`
|
||||||
|
class.
|
||||||
|
- `CLIENT KILL USER username`. Closes all the connections that are authenticated
|
||||||
|
with the specified [ACL](/topics/acl) username, however it returns an error if
|
||||||
|
the username does not map to an existing ACL user.
|
||||||
|
- `CLIENT KILL SKIPME yes/no`. By default this option is set to `yes`, that is,
|
||||||
|
the client calling the command will not get killed, however setting this
|
||||||
|
option to `no` will have the effect of also killing the client calling the
|
||||||
|
command.
|
||||||
|
|
||||||
|
**Note: starting with Redis 5 the project is no longer using the slave word. You
|
||||||
|
can use `TYPE replica` instead, however the old form is still supported for
|
||||||
|
backward compatibility.**
|
||||||
|
|
||||||
|
It is possible to provide multiple filters at the same time. The command will
|
||||||
|
handle multiple filters via logical AND. For example:
|
||||||
|
|
||||||
|
CLIENT KILL addr 127.0.0.1:12345 type pubsub
|
||||||
|
|
||||||
|
is valid and will kill only a pubsub client with the specified address. This
|
||||||
|
format containing multiple filters is rarely useful currently.
|
||||||
|
|
||||||
|
When the new form is used the command no longer returns `OK` or an error, but
|
||||||
|
instead the number of killed clients, that may be zero.
|
||||||
|
|
||||||
|
## CLIENT KILL and Redis Sentinel
|
||||||
|
|
||||||
|
Recent versions of Redis Sentinel (Redis 2.8.12 or greater) use CLIENT KILL in
|
||||||
|
order to kill clients when an instance is reconfigured, in order to force
|
||||||
|
clients to perform the handshake with one Sentinel again and update its
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Due to the single-threaded nature of Redis, it is not possible to kill a client
|
||||||
|
connection while it is executing a command. From the client point of view, the
|
||||||
|
connection can never be closed in the middle of the execution of a command.
|
||||||
|
However, the client will notice the connection has been closed only when the
|
||||||
|
next command is sent (and results in network error).
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
When called with the three arguments format:
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` if the connection exists and has been closed
|
||||||
|
|
||||||
|
When called with the filter / value format:
|
||||||
|
|
||||||
|
@integer-reply: the number of clients killed.
|
70
iredis/data/commands/client-list.md
Normal file
70
iredis/data/commands/client-list.md
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
The `CLIENT LIST` command returns information and statistics about the client
|
||||||
|
connections server in a mostly human readable format.
|
||||||
|
|
||||||
|
As of v5.0, the optional `TYPE type` subcommand can be used to filter the list
|
||||||
|
by clients' type, where _type_ is one of `normal`, `master`, `replica` and
|
||||||
|
`pubsub`. Note that clients blocked into the `MONITOR` command are considered to
|
||||||
|
belong to the `normal` class.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@bulk-string-reply: a unique string, formatted as follows:
|
||||||
|
|
||||||
|
- One client connection per line (separated by LF)
|
||||||
|
- Each line is composed of a succession of `property=value` fields separated by
|
||||||
|
a space character.
|
||||||
|
|
||||||
|
Here is the meaning of the fields:
|
||||||
|
|
||||||
|
- `id`: an unique 64-bit client ID (introduced in Redis 2.8.12).
|
||||||
|
- `name`: the name set by the client with `CLIENT SETNAME`
|
||||||
|
- `addr`: address/port of the client
|
||||||
|
- `fd`: file descriptor corresponding to the socket
|
||||||
|
- `age`: total duration of the connection in seconds
|
||||||
|
- `idle`: idle time of the connection in seconds
|
||||||
|
- `flags`: client flags (see below)
|
||||||
|
- `db`: current database ID
|
||||||
|
- `sub`: number of channel subscriptions
|
||||||
|
- `psub`: number of pattern matching subscriptions
|
||||||
|
- `multi`: number of commands in a MULTI/EXEC context
|
||||||
|
- `qbuf`: query buffer length (0 means no query pending)
|
||||||
|
- `qbuf-free`: free space of the query buffer (0 means the buffer is full)
|
||||||
|
- `obl`: output buffer length
|
||||||
|
- `oll`: output list length (replies are queued in this list when the buffer is
|
||||||
|
full)
|
||||||
|
- `omem`: output buffer memory usage
|
||||||
|
- `events`: file descriptor events (see below)
|
||||||
|
- `cmd`: last command played
|
||||||
|
|
||||||
|
The client flags can be a combination of:
|
||||||
|
|
||||||
|
```
|
||||||
|
A: connection to be closed ASAP
|
||||||
|
b: the client is waiting in a blocking operation
|
||||||
|
c: connection to be closed after writing entire reply
|
||||||
|
d: a watched keys has been modified - EXEC will fail
|
||||||
|
i: the client is waiting for a VM I/O (deprecated)
|
||||||
|
M: the client is a master
|
||||||
|
N: no specific flag set
|
||||||
|
O: the client is a client in MONITOR mode
|
||||||
|
P: the client is a Pub/Sub subscriber
|
||||||
|
r: the client is in readonly mode against a cluster node
|
||||||
|
S: the client is a replica node connection to this instance
|
||||||
|
u: the client is unblocked
|
||||||
|
U: the client is connected via a Unix domain socket
|
||||||
|
x: the client is in a MULTI/EXEC context
|
||||||
|
```
|
||||||
|
|
||||||
|
The file descriptor events can be:
|
||||||
|
|
||||||
|
```
|
||||||
|
r: the client socket is readable (event loop)
|
||||||
|
w: the client socket is writable (event loop)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
New fields are regularly added for debugging purpose. Some could be removed in
|
||||||
|
the future. A version safe Redis client using this command should parse the
|
||||||
|
output accordingly (i.e. handling gracefully missing fields, skipping unknown
|
||||||
|
fields).
|
38
iredis/data/commands/client-pause.md
Normal file
38
iredis/data/commands/client-pause.md
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
`CLIENT PAUSE` is a connections control command able to suspend all the Redis
|
||||||
|
clients for the specified amount of time (in milliseconds).
|
||||||
|
|
||||||
|
The command performs the following actions:
|
||||||
|
|
||||||
|
- It stops processing all the pending commands from normal and pub/sub clients.
|
||||||
|
However interactions with replicas will continue normally.
|
||||||
|
- However it returns OK to the caller ASAP, so the `CLIENT PAUSE` command
|
||||||
|
execution is not paused by itself.
|
||||||
|
- When the specified amount of time has elapsed, all the clients are unblocked:
|
||||||
|
this will trigger the processing of all the commands accumulated in the query
|
||||||
|
buffer of every client during the pause.
|
||||||
|
|
||||||
|
This command is useful as it makes able to switch clients from a Redis instance
|
||||||
|
to another one in a controlled way. For example during an instance upgrade the
|
||||||
|
system administrator could do the following:
|
||||||
|
|
||||||
|
- Pause the clients using `CLIENT PAUSE`
|
||||||
|
- Wait a few seconds to make sure the replicas processed the latest replication
|
||||||
|
stream from the master.
|
||||||
|
- Turn one of the replicas into a master.
|
||||||
|
- Reconfigure clients to connect with the new master.
|
||||||
|
|
||||||
|
It is possible to send `CLIENT PAUSE` in a MULTI/EXEC block together with the
|
||||||
|
`INFO replication` command in order to get the current master offset at the time
|
||||||
|
the clients are blocked. This way it is possible to wait for a specific offset
|
||||||
|
in the replica side in order to make sure all the replication stream was
|
||||||
|
processed.
|
||||||
|
|
||||||
|
Since Redis 3.2.10 / 4.0.0, this command also prevents keys to be evicted or
|
||||||
|
expired during the time clients are paused. This way the dataset is guaranteed
|
||||||
|
to be static not just from the point of view of clients not being able to write,
|
||||||
|
but also from the point of view of internal operations.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: The command returns OK or an error if the timeout is
|
||||||
|
invalid.
|
21
iredis/data/commands/client-reply.md
Normal file
21
iredis/data/commands/client-reply.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
Sometimes it can be useful for clients to completely disable replies from the
|
||||||
|
Redis server. For example when the client sends fire and forget commands or
|
||||||
|
performs a mass loading of data, or in caching contexts where new data is
|
||||||
|
streamed constantly. In such contexts to use server time and bandwidth in order
|
||||||
|
to send back replies to clients, which are going to be ignored, is considered
|
||||||
|
wasteful.
|
||||||
|
|
||||||
|
The `CLIENT REPLY` command controls whether the server will reply the client's
|
||||||
|
commands. The following modes are available:
|
||||||
|
|
||||||
|
- `ON`. This is the default mode in which the server returns a reply to every
|
||||||
|
command.
|
||||||
|
- `OFF`. In this mode the server will not reply to client commands.
|
||||||
|
- `SKIP`. This mode skips the reply of command immediately after it.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
When called with either `OFF` or `SKIP` subcommands, no reply is made. When
|
||||||
|
called with `ON`:
|
||||||
|
|
||||||
|
@simple-string-reply: `OK`.
|
28
iredis/data/commands/client-setname.md
Normal file
28
iredis/data/commands/client-setname.md
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
The `CLIENT SETNAME` command assigns a name to the current connection.
|
||||||
|
|
||||||
|
The assigned name is displayed in the output of `CLIENT LIST` so that it is
|
||||||
|
possible to identify the client that performed a given connection.
|
||||||
|
|
||||||
|
For instance when Redis is used in order to implement a queue, producers and
|
||||||
|
consumers of messages may want to set the name of the connection according to
|
||||||
|
their role.
|
||||||
|
|
||||||
|
There is no limit to the length of the name that can be assigned if not the
|
||||||
|
usual limits of the Redis string type (512 MB). However it is not possible to
|
||||||
|
use spaces in the connection name as this would violate the format of the
|
||||||
|
`CLIENT LIST` reply.
|
||||||
|
|
||||||
|
It is possible to entirely remove the connection name setting it to the empty
|
||||||
|
string, that is not a valid connection name since it serves to this specific
|
||||||
|
purpose.
|
||||||
|
|
||||||
|
The connection name can be inspected using `CLIENT GETNAME`.
|
||||||
|
|
||||||
|
Every new connection starts without an assigned name.
|
||||||
|
|
||||||
|
Tip: setting names to connections is a good way to debug connection leaks due to
|
||||||
|
bugs in the application using Redis.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` if the connection name was successfully set.
|
54
iredis/data/commands/client-tracking.md
Normal file
54
iredis/data/commands/client-tracking.md
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
This command enables the tracking feature of the Redis server, that is used for
|
||||||
|
[server assisted client side caching](/topics/client-side-caching).
|
||||||
|
|
||||||
|
When tracking is enabled Redis remembers the keys that the connection requested,
|
||||||
|
in order to send later invalidation messages when such keys are modified.
|
||||||
|
Invalidation messages are sent in the same connection (only available when the
|
||||||
|
RESP3 protocol is used) or redirected in a different connection (available also
|
||||||
|
with RESP2 and Pub/Sub). A special _broadcasting_ mode is available where
|
||||||
|
clients participating in this protocol receive every notification just
|
||||||
|
subscribing to given key prefixes, regardless of the keys that they requested.
|
||||||
|
Given the complexity of the argument please refer to
|
||||||
|
[the main client side caching documentation](/topics/client-side-caching) for
|
||||||
|
the details. This manual page is only a reference for the options of this
|
||||||
|
subcommand.
|
||||||
|
|
||||||
|
In order to enable tracking, use:
|
||||||
|
|
||||||
|
CLIENT TRACKING on ... options ...
|
||||||
|
|
||||||
|
The feature will remain active in the current connection for all its life,
|
||||||
|
unless tracking is turned on with `CLIENT TRACKING off` at some point.
|
||||||
|
|
||||||
|
The following are the list of options that modify the behavior of the command
|
||||||
|
when enabling tracking:
|
||||||
|
|
||||||
|
- `REDIRECT <id>`: send redirection messages to the connection with the
|
||||||
|
specified ID. The connection must exist, you can get the ID of such connection
|
||||||
|
using `CLIENT ID`. If the connection we are redirecting to is terminated, when
|
||||||
|
in RESP3 mode the connection with tracking enabled will receive
|
||||||
|
`tracking-redir-broken` push messages in order to signal the condition.
|
||||||
|
- `BCAST`: enable tracking in broadcasting mode. In this mode invalidation
|
||||||
|
messages are reported for all the prefixes specified, regardless of the keys
|
||||||
|
requested by the connection. Instead when the broadcasting mode is not
|
||||||
|
enabled, Redis will track which keys are fetched using read-only commands, and
|
||||||
|
will report invalidation messages only for such keys.
|
||||||
|
- `PREFIX <prefix>`: for broadcasting, register a given key prefix, so that
|
||||||
|
notifications will be provided only for keys starting with this string. This
|
||||||
|
option can be given multiple times to register multiple prefixes. If
|
||||||
|
broadcasting is enabled without this option, Redis will send notifications for
|
||||||
|
every key.
|
||||||
|
- `OPTIN`: when broadcasting is NOT active, normally don't track keys in read
|
||||||
|
only commands, unless they are called immediately after a `CLIENT CACHING yes`
|
||||||
|
command.
|
||||||
|
- `OPTOUT`: when broadcasting is NOT active, normally track keys in read only
|
||||||
|
commands, unless they are called immediately after a `CLIENT CACHING no`
|
||||||
|
command.
|
||||||
|
- `NOLOOP`: don't send notifications about keys modified by this connection
|
||||||
|
itself.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` if the connection was successfully put in tracking
|
||||||
|
mode or if the tracking mode was successfully disabled. Otherwise an error is
|
||||||
|
returned.
|
63
iredis/data/commands/client-unblock.md
Normal file
63
iredis/data/commands/client-unblock.md
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
This command can unblock, from a different connection, a client blocked in a
|
||||||
|
blocking operation, such as for instance `BRPOP` or `XREAD` or `WAIT`.
|
||||||
|
|
||||||
|
By default the client is unblocked as if the timeout of the command was reached,
|
||||||
|
however if an additional (and optional) argument is passed, it is possible to
|
||||||
|
specify the unblocking behavior, that can be **TIMEOUT** (the default) or
|
||||||
|
**ERROR**. If **ERROR** is specified, the behavior is to unblock the client
|
||||||
|
returning as error the fact that the client was force-unblocked. Specifically
|
||||||
|
the client will receive the following error:
|
||||||
|
|
||||||
|
-UNBLOCKED client unblocked via CLIENT UNBLOCK
|
||||||
|
|
||||||
|
Note: of course as usually it is not guaranteed that the error text remains the
|
||||||
|
same, however the error code will remain `-UNBLOCKED`.
|
||||||
|
|
||||||
|
This command is useful especially when we are monitoring many keys with a
|
||||||
|
limited number of connections. For instance we may want to monitor multiple
|
||||||
|
streams with `XREAD` without using more than N connections. However at some
|
||||||
|
point the consumer process is informed that there is one more stream key to
|
||||||
|
monitor. In order to avoid using more connections, the best behavior would be to
|
||||||
|
stop the blocking command from one of the connections in the pool, add the new
|
||||||
|
key, and issue the blocking command again.
|
||||||
|
|
||||||
|
To obtain this behavior the following pattern is used. The process uses an
|
||||||
|
additional _control connection_ in order to send the `CLIENT UNBLOCK` command if
|
||||||
|
needed. In the meantime, before running the blocking operation on the other
|
||||||
|
connections, the process runs `CLIENT ID` in order to get the ID associated with
|
||||||
|
that connection. When a new key should be added, or when a key should no longer
|
||||||
|
be monitored, the relevant connection blocking command is aborted by sending
|
||||||
|
`CLIENT UNBLOCK` in the control connection. The blocking command will return and
|
||||||
|
can be finally reissued.
|
||||||
|
|
||||||
|
This example shows the application in the context of Redis streams, however the
|
||||||
|
pattern is a general one and can be applied to other cases.
|
||||||
|
|
||||||
|
@example
|
||||||
|
|
||||||
|
```
|
||||||
|
Connection A (blocking connection):
|
||||||
|
> CLIENT ID
|
||||||
|
2934
|
||||||
|
> BRPOP key1 key2 key3 0
|
||||||
|
(client is blocked)
|
||||||
|
|
||||||
|
... Now we want to add a new key ...
|
||||||
|
|
||||||
|
Connection B (control connection):
|
||||||
|
> CLIENT UNBLOCK 2934
|
||||||
|
1
|
||||||
|
|
||||||
|
Connection A (blocking connection):
|
||||||
|
... BRPOP reply with timeout ...
|
||||||
|
NULL
|
||||||
|
> BRPOP key1 key2 key3 key4 0
|
||||||
|
(client is blocked again)
|
||||||
|
```
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply, specifically:
|
||||||
|
|
||||||
|
- `1` if the client was unblocked successfully.
|
||||||
|
- `0` if the client wasn't unblocked.
|
55
iredis/data/commands/cluster-addslots.md
Normal file
55
iredis/data/commands/cluster-addslots.md
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
This command is useful in order to modify a node's view of the cluster
|
||||||
|
configuration. Specifically it assigns a set of hash slots to the node receiving
|
||||||
|
the command. If the command is successful, the node will map the specified hash
|
||||||
|
slots to itself, and will start broadcasting the new configuration.
|
||||||
|
|
||||||
|
However note that:
|
||||||
|
|
||||||
|
1. The command only works if all the specified slots are, from the point of view
|
||||||
|
of the node receiving the command, currently not assigned. A node will refuse
|
||||||
|
to take ownership for slots that already belong to some other node (including
|
||||||
|
itself).
|
||||||
|
2. The command fails if the same slot is specified multiple times.
|
||||||
|
3. As a side effect of the command execution, if a slot among the ones specified
|
||||||
|
as argument is set as `importing`, this state gets cleared once the node
|
||||||
|
assigns the (previously unbound) slot to itself.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
For example the following command assigns slots 1 2 3 to the node receiving the
|
||||||
|
command:
|
||||||
|
|
||||||
|
> CLUSTER ADDSLOTS 1 2 3
|
||||||
|
OK
|
||||||
|
|
||||||
|
However trying to execute it again results into an error since the slots are
|
||||||
|
already assigned:
|
||||||
|
|
||||||
|
> CLUSTER ADDSLOTS 1 2 3
|
||||||
|
ERR Slot 1 is already busy
|
||||||
|
|
||||||
|
## Usage in Redis Cluster
|
||||||
|
|
||||||
|
This command only works in cluster mode and is useful in the following Redis
|
||||||
|
Cluster operations:
|
||||||
|
|
||||||
|
1. To create a new cluster ADDSLOTS is used in order to initially setup master
|
||||||
|
nodes splitting the available hash slots among them.
|
||||||
|
2. In order to fix a broken cluster where certain slots are unassigned.
|
||||||
|
|
||||||
|
## Information about slots propagation and warnings
|
||||||
|
|
||||||
|
Note that once a node assigns a set of slots to itself, it will start
|
||||||
|
propagating this information in heartbeat packet headers. However the other
|
||||||
|
nodes will accept the information only if they have the slot as not already
|
||||||
|
bound with another node, or if the configuration epoch of the node advertising
|
||||||
|
the new hash slot, is greater than the node currently listed in the table.
|
||||||
|
|
||||||
|
This means that this command should be used with care only by applications
|
||||||
|
orchestrating Redis Cluster, like `redis-trib`, and the command if used out of
|
||||||
|
the right context can leave the cluster in a wrong state or cause data loss.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` if the command was successful. Otherwise an error is
|
||||||
|
returned.
|
15
iredis/data/commands/cluster-bumpepoch.md
Normal file
15
iredis/data/commands/cluster-bumpepoch.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
Advances the cluster config epoch.
|
||||||
|
|
||||||
|
The `CLUSTER BUMPEPOCH` command triggers an increment to the cluster's config
|
||||||
|
epoch from the connected node. The epoch will be incremented if the node's
|
||||||
|
config epoch is zero, or if it is less than the cluster's greatest epoch.
|
||||||
|
|
||||||
|
**Note:** config epoch management is performed internally by the cluster, and
|
||||||
|
relies on obtaining a consensus of nodes. The `CLUSTER BUMPEPOCH` attempts to
|
||||||
|
increment the config epoch **WITHOUT** getting the consensus, so using it may
|
||||||
|
violate the "last failover wins" rule. Use it with caution.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `BUMPED` if the epoch was incremented, or `STILL` if the
|
||||||
|
node already has the greatest config epoch in the cluster.
|
34
iredis/data/commands/cluster-count-failure-reports.md
Normal file
34
iredis/data/commands/cluster-count-failure-reports.md
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
The command returns the number of _failure reports_ for the specified node.
|
||||||
|
Failure reports are the way Redis Cluster uses in order to promote a `PFAIL`
|
||||||
|
state, that means a node is not reachable, to a `FAIL` state, that means that
|
||||||
|
the majority of masters in the cluster agreed within a window of time that the
|
||||||
|
node is not reachable.
|
||||||
|
|
||||||
|
A few more details:
|
||||||
|
|
||||||
|
- A node flags another node with `PFAIL` when the node is not reachable for a
|
||||||
|
time greater than the configured _node timeout_, which is a fundamental
|
||||||
|
configuration parameter of a Redis Cluster.
|
||||||
|
- Nodes in `PFAIL` state are provided in gossip sections of heartbeat packets.
|
||||||
|
- Every time a node processes gossip packets from other nodes, it creates (and
|
||||||
|
refreshes the TTL if needed) **failure reports**, remembering that a given
|
||||||
|
node said another given node is in `PFAIL` condition.
|
||||||
|
- Each failure report has a time to live of two times the _node timeout_ time.
|
||||||
|
- If at a given time a node has another node flagged with `PFAIL`, and at the
|
||||||
|
same time collected the majority of other master nodes _failure reports_ about
|
||||||
|
this node (including itself if it is a master), then it elevates the failure
|
||||||
|
state of the node from `PFAIL` to `FAIL`, and broadcasts a message forcing all
|
||||||
|
the nodes that can be reached to flag the node as `FAIL`.
|
||||||
|
|
||||||
|
This command returns the number of failure reports for the current node which
|
||||||
|
are currently not expired (so received within two times the _node timeout_
|
||||||
|
time). The count does not include what the node we are asking this count
|
||||||
|
believes about the node ID we pass as argument, the count _only_ includes the
|
||||||
|
failure reports the node received from other nodes.
|
||||||
|
|
||||||
|
This command is mainly useful for debugging, when the failure detector of Redis
|
||||||
|
Cluster is not operating as we believe it should.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply: the number of active failure reports for the node.
|
13
iredis/data/commands/cluster-countkeysinslot.md
Normal file
13
iredis/data/commands/cluster-countkeysinslot.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
Returns the number of keys in the specified Redis Cluster hash slot. The command
|
||||||
|
only queries the local data set, so contacting a node that is not serving the
|
||||||
|
specified hash slot will always result in a count of zero being returned.
|
||||||
|
|
||||||
|
```
|
||||||
|
> CLUSTER COUNTKEYSINSLOT 7000
|
||||||
|
(integer) 50341
|
||||||
|
```
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply: The number of keys in the specified hash slot, or an error if
|
||||||
|
the hash slot is invalid.
|
47
iredis/data/commands/cluster-delslots.md
Normal file
47
iredis/data/commands/cluster-delslots.md
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
In Redis Cluster, each node keeps track of which master is serving a particular
|
||||||
|
hash slot.
|
||||||
|
|
||||||
|
The `DELSLOTS` command asks a particular Redis Cluster node to forget which
|
||||||
|
master is serving the hash slots specified as arguments.
|
||||||
|
|
||||||
|
In the context of a node that has received a `DELSLOTS` command and has
|
||||||
|
consequently removed the associations for the passed hash slots, we say those
|
||||||
|
hash slots are _unbound_. Note that the existence of unbound hash slots occurs
|
||||||
|
naturally when a node has not been configured to handle them (something that can
|
||||||
|
be done with the `ADDSLOTS` command) and if it has not received any information
|
||||||
|
about who owns those hash slots (something that it can learn from heartbeat or
|
||||||
|
update messages).
|
||||||
|
|
||||||
|
If a node with unbound hash slots receives a heartbeat packet from another node
|
||||||
|
that claims to be the owner of some of those hash slots, the association is
|
||||||
|
established instantly. Moreover, if a heartbeat or update message is received
|
||||||
|
with a configuration epoch greater than the node's own, the association is
|
||||||
|
re-established.
|
||||||
|
|
||||||
|
However, note that:
|
||||||
|
|
||||||
|
1. The command only works if all the specified slots are already associated with
|
||||||
|
some node.
|
||||||
|
2. The command fails if the same slot is specified multiple times.
|
||||||
|
3. As a side effect of the command execution, the node may go into _down_ state
|
||||||
|
because not all hash slots are covered.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
The following command removes the association for slots 5000 and 5001 from the
|
||||||
|
node receiving the command:
|
||||||
|
|
||||||
|
> CLUSTER DELSLOTS 5000 5001
|
||||||
|
OK
|
||||||
|
|
||||||
|
## Usage in Redis Cluster
|
||||||
|
|
||||||
|
This command only works in cluster mode and may be useful for debugging and in
|
||||||
|
order to manually orchestrate a cluster configuration when a new cluster is
|
||||||
|
created. It is currently not used by `redis-trib`, and mainly exists for API
|
||||||
|
completeness.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` if the command was successful. Otherwise an error is
|
||||||
|
returned.
|
81
iredis/data/commands/cluster-failover.md
Normal file
81
iredis/data/commands/cluster-failover.md
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
This command, that can only be sent to a Redis Cluster replica node, forces the
|
||||||
|
replica to start a manual failover of its master instance.
|
||||||
|
|
||||||
|
A manual failover is a special kind of failover that is usually executed when
|
||||||
|
there are no actual failures, but we wish to swap the current master with one of
|
||||||
|
its replicas (which is the node we send the command to), in a safe way, without
|
||||||
|
any window for data loss. It works in the following way:
|
||||||
|
|
||||||
|
1. The replica tells the master to stop processing queries from clients.
|
||||||
|
2. The master replies to the replica with the current _replication offset_.
|
||||||
|
3. The replica waits for the replication offset to match on its side, to make
|
||||||
|
sure it processed all the data from the master before it continues.
|
||||||
|
4. The replica starts a failover, obtains a new configuration epoch from the
|
||||||
|
majority of the masters, and broadcasts the new configuration.
|
||||||
|
5. The old master receives the configuration update: unblocks its clients and
|
||||||
|
starts replying with redirection messages so that they'll continue the chat
|
||||||
|
with the new master.
|
||||||
|
|
||||||
|
This way clients are moved away from the old master to the new master atomically
|
||||||
|
and only when the replica that is turning into the new master has processed all
|
||||||
|
of the replication stream from the old master.
|
||||||
|
|
||||||
|
## FORCE option: manual failover when the master is down
|
||||||
|
|
||||||
|
The command behavior can be modified by two options: **FORCE** and **TAKEOVER**.
|
||||||
|
|
||||||
|
If the **FORCE** option is given, the replica does not perform any handshake
|
||||||
|
with the master, that may be not reachable, but instead just starts a failover
|
||||||
|
ASAP starting from point 4. This is useful when we want to start a manual
|
||||||
|
failover while the master is no longer reachable.
|
||||||
|
|
||||||
|
However using **FORCE** we still need the majority of masters to be available in
|
||||||
|
order to authorize the failover and generate a new configuration epoch for the
|
||||||
|
replica that is going to become master.
|
||||||
|
|
||||||
|
## TAKEOVER option: manual failover without cluster consensus
|
||||||
|
|
||||||
|
There are situations where this is not enough, and we want a replica to failover
|
||||||
|
without any agreement with the rest of the cluster. A real world use case for
|
||||||
|
this is to mass promote replicas in a different data center to masters in order
|
||||||
|
to perform a data center switch, while all the masters are down or partitioned
|
||||||
|
away.
|
||||||
|
|
||||||
|
The **TAKEOVER** option implies everything **FORCE** implies, but also does not
|
||||||
|
uses any cluster authorization in order to failover. A replica receiving
|
||||||
|
`CLUSTER FAILOVER TAKEOVER` will instead:
|
||||||
|
|
||||||
|
1. Generate a new `configEpoch` unilaterally, just taking the current greatest
|
||||||
|
epoch available and incrementing it if its local configuration epoch is not
|
||||||
|
already the greatest.
|
||||||
|
2. Assign itself all the hash slots of its master, and propagate the new
|
||||||
|
configuration to every node which is reachable ASAP, and eventually to every
|
||||||
|
other node.
|
||||||
|
|
||||||
|
Note that **TAKEOVER violates the last-failover-wins principle** of Redis
|
||||||
|
Cluster, since the configuration epoch generated by the replica violates the
|
||||||
|
normal generation of configuration epochs in several ways:
|
||||||
|
|
||||||
|
1. There is no guarantee that it is actually the higher configuration epoch,
|
||||||
|
since, for example, we can use the **TAKEOVER** option within a minority, nor
|
||||||
|
any message exchange is performed to generate the new configuration epoch.
|
||||||
|
2. If we generate a configuration epoch which happens to collide with another
|
||||||
|
instance, eventually our configuration epoch, or the one of another instance
|
||||||
|
with our same epoch, will be moved away using the _configuration epoch
|
||||||
|
collision resolution algorithm_.
|
||||||
|
|
||||||
|
Because of this the **TAKEOVER** option should be used with care.
|
||||||
|
|
||||||
|
## Implementation details and notes
|
||||||
|
|
||||||
|
`CLUSTER FAILOVER`, unless the **TAKEOVER** option is specified, does not
|
||||||
|
execute a failover synchronously, it only _schedules_ a manual failover,
|
||||||
|
bypassing the failure detection stage, so to check if the failover actually
|
||||||
|
happened, `CLUSTER NODES` or other means should be used in order to verify that
|
||||||
|
the state of the cluster changes after some time the command was sent.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` if the command was accepted and a manual failover is
|
||||||
|
going to be attempted. An error if the operation cannot be executed, for example
|
||||||
|
if we are talking with a node which is already a master.
|
8
iredis/data/commands/cluster-flushslots.md
Normal file
8
iredis/data/commands/cluster-flushslots.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
Deletes all slots from a node.
|
||||||
|
|
||||||
|
The `CLUSTER FLUSHSLOTS` deletes all information about slots from the connected
|
||||||
|
node. It can only be called when the database is empty.
|
||||||
|
|
||||||
|
@reply
|
||||||
|
|
||||||
|
@simple-string-reply: `OK`
|
59
iredis/data/commands/cluster-forget.md
Normal file
59
iredis/data/commands/cluster-forget.md
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
The command is used in order to remove a node, specified via its node ID, from
|
||||||
|
the set of _known nodes_ of the Redis Cluster node receiving the command. In
|
||||||
|
other words the specified node is removed from the _nodes table_ of the node
|
||||||
|
receiving the command.
|
||||||
|
|
||||||
|
Because when a given node is part of the cluster, all the other nodes
|
||||||
|
participating in the cluster knows about it, in order for a node to be
|
||||||
|
completely removed from a cluster, the `CLUSTER FORGET` command must be sent to
|
||||||
|
all the remaining nodes, regardless of the fact they are masters or replicas.
|
||||||
|
|
||||||
|
However the command cannot simply drop the node from the internal node table of
|
||||||
|
the node receiving the command, it also implements a ban-list, not allowing the
|
||||||
|
same node to be added again as a side effect of processing the _gossip section_
|
||||||
|
of the heartbeat packets received from other nodes.
|
||||||
|
|
||||||
|
## Details on why the ban-list is needed
|
||||||
|
|
||||||
|
In the following example we'll show why the command must not just remove a given
|
||||||
|
node from the nodes table, but also prevent it for being re-inserted again for
|
||||||
|
some time.
|
||||||
|
|
||||||
|
Let's assume we have four nodes, A, B, C and D. In order to end with just a
|
||||||
|
three nodes cluster A, B, C we may follow these steps:
|
||||||
|
|
||||||
|
1. Reshard all the hash slots from D to nodes A, B, C.
|
||||||
|
2. D is now empty, but still listed in the nodes table of A, B and C.
|
||||||
|
3. We contact A, and send `CLUSTER FORGET D`.
|
||||||
|
4. B sends node A a heartbeat packet, where node D is listed.
|
||||||
|
5. A does no longer known node D (see step 3), so it starts an handshake with D.
|
||||||
|
6. D ends re-added in the nodes table of A.
|
||||||
|
|
||||||
|
As you can see in this way removing a node is fragile, we need to send
|
||||||
|
`CLUSTER FORGET` commands to all the nodes ASAP hoping there are no gossip
|
||||||
|
sections processing in the meantime. Because of this problem the command
|
||||||
|
implements a ban-list with an expire time for each entry.
|
||||||
|
|
||||||
|
So what the command really does is:
|
||||||
|
|
||||||
|
1. The specified node gets removed from the nodes table.
|
||||||
|
2. The node ID of the removed node gets added to the ban-list, for 1 minute.
|
||||||
|
3. The node will skip all the node IDs listed in the ban-list when processing
|
||||||
|
gossip sections received in heartbeat packets from other nodes.
|
||||||
|
|
||||||
|
This way we have a 60 second window to inform all the nodes in the cluster that
|
||||||
|
we want to remove a node.
|
||||||
|
|
||||||
|
## Special conditions not allowing the command execution
|
||||||
|
|
||||||
|
The command does not succeed and returns an error in the following cases:
|
||||||
|
|
||||||
|
1. The specified node ID is not found in the nodes table.
|
||||||
|
2. The node receiving the command is a replica, and the specified node ID
|
||||||
|
identifies its current master.
|
||||||
|
3. The node ID identifies the same node we are sending the command to.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` if the command was executed successfully, otherwise
|
||||||
|
an error is returned.
|
20
iredis/data/commands/cluster-getkeysinslot.md
Normal file
20
iredis/data/commands/cluster-getkeysinslot.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
The command returns an array of keys names stored in the contacted node and
|
||||||
|
hashing to the specified hash slot. The maximum number of keys to return is
|
||||||
|
specified via the `count` argument, so that it is possible for the user of this
|
||||||
|
API to batch-processing keys.
|
||||||
|
|
||||||
|
The main usage of this command is during rehashing of cluster slots from one
|
||||||
|
node to another. The way the rehashing is performed is exposed in the Redis
|
||||||
|
Cluster specification, or in a more simple to digest form, as an appendix of the
|
||||||
|
`CLUSTER SETSLOT` command documentation.
|
||||||
|
|
||||||
|
```
|
||||||
|
> CLUSTER GETKEYSINSLOT 7000 3
|
||||||
|
"47344|273766|70329104160040|key_39015"
|
||||||
|
"47344|273766|70329104160040|key_89793"
|
||||||
|
"47344|273766|70329104160040|key_92937"
|
||||||
|
```
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@array-reply: From 0 to _count_ key names in a Redis array reply.
|
56
iredis/data/commands/cluster-info.md
Normal file
56
iredis/data/commands/cluster-info.md
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
`CLUSTER INFO` provides `INFO` style information about Redis Cluster vital
|
||||||
|
parameters. The following is a sample output, followed by the description of
|
||||||
|
each field reported.
|
||||||
|
|
||||||
|
```
|
||||||
|
cluster_state:ok
|
||||||
|
cluster_slots_assigned:16384
|
||||||
|
cluster_slots_ok:16384
|
||||||
|
cluster_slots_pfail:0
|
||||||
|
cluster_slots_fail:0
|
||||||
|
cluster_known_nodes:6
|
||||||
|
cluster_size:3
|
||||||
|
cluster_current_epoch:6
|
||||||
|
cluster_my_epoch:2
|
||||||
|
cluster_stats_messages_sent:1483972
|
||||||
|
cluster_stats_messages_received:1483968
|
||||||
|
```
|
||||||
|
|
||||||
|
- `cluster_state`: State is `ok` if the node is able to receive queries. `fail`
|
||||||
|
if there is at least one hash slot which is unbound (no node associated), in
|
||||||
|
error state (node serving it is flagged with FAIL flag), or if the majority of
|
||||||
|
masters can't be reached by this node.
|
||||||
|
- `cluster_slots_assigned`: Number of slots which are associated to some node
|
||||||
|
(not unbound). This number should be 16384 for the node to work properly,
|
||||||
|
which means that each hash slot should be mapped to a node.
|
||||||
|
- `cluster_slots_ok`: Number of hash slots mapping to a node not in `FAIL` or
|
||||||
|
`PFAIL` state.
|
||||||
|
- `cluster_slots_pfail`: Number of hash slots mapping to a node in `PFAIL`
|
||||||
|
state. Note that those hash slots still work correctly, as long as the `PFAIL`
|
||||||
|
state is not promoted to `FAIL` by the failure detection algorithm. `PFAIL`
|
||||||
|
only means that we are currently not able to talk with the node, but may be
|
||||||
|
just a transient error.
|
||||||
|
- `cluster_slots_fail`: Number of hash slots mapping to a node in `FAIL` state.
|
||||||
|
If this number is not zero the node is not able to serve queries unless
|
||||||
|
`cluster-require-full-coverage` is set to `no` in the configuration.
|
||||||
|
- `cluster_known_nodes`: The total number of known nodes in the cluster,
|
||||||
|
including nodes in `HANDSHAKE` state that may not currently be proper members
|
||||||
|
of the cluster.
|
||||||
|
- `cluster_size`: The number of master nodes serving at least one hash slot in
|
||||||
|
the cluster.
|
||||||
|
- `cluster_current_epoch`: The local `Current Epoch` variable. This is used in
|
||||||
|
order to create unique increasing version numbers during fail overs.
|
||||||
|
- `cluster_my_epoch`: The `Config Epoch` of the node we are talking with. This
|
||||||
|
is the current configuration version assigned to this node.
|
||||||
|
- `cluster_stats_messages_sent`: Number of messages sent via the cluster
|
||||||
|
node-to-node binary bus.
|
||||||
|
- `cluster_stats_messages_received`: Number of messages received via the cluster
|
||||||
|
node-to-node binary bus.
|
||||||
|
|
||||||
|
More information about the Current Epoch and Config Epoch variables are
|
||||||
|
available in the Redis Cluster specification document.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@bulk-string-reply: A map between named fields and values in the form of
|
||||||
|
`<field>:<value>` lines separated by newlines composed by the two bytes `CRLF`.
|
32
iredis/data/commands/cluster-keyslot.md
Normal file
32
iredis/data/commands/cluster-keyslot.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
Returns an integer identifying the hash slot the specified key hashes to. This
|
||||||
|
command is mainly useful for debugging and testing, since it exposes via an API
|
||||||
|
the underlying Redis implementation of the hashing algorithm. Example use cases
|
||||||
|
for this command:
|
||||||
|
|
||||||
|
1. Client libraries may use Redis in order to test their own hashing algorithm,
|
||||||
|
generating random keys and hashing them with both their local implementation
|
||||||
|
and using Redis `CLUSTER KEYSLOT` command, then checking if the result is the
|
||||||
|
same.
|
||||||
|
2. Humans may use this command in order to check what is the hash slot, and then
|
||||||
|
the associated Redis Cluster node, responsible for a given key.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```
|
||||||
|
> CLUSTER KEYSLOT somekey
|
||||||
|
11058
|
||||||
|
> CLUSTER KEYSLOT foo{hash_tag}
|
||||||
|
(integer) 2515
|
||||||
|
> CLUSTER KEYSLOT bar{hash_tag}
|
||||||
|
(integer) 2515
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the command implements the full hashing algorithm, including support
|
||||||
|
for **hash tags**, that is the special property of Redis Cluster key hashing
|
||||||
|
algorithm, of hashing just what is between `{` and `}` if such a pattern is
|
||||||
|
found inside the key name, in order to force multiple keys to be handled by the
|
||||||
|
same node.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply: The hash slot number.
|
55
iredis/data/commands/cluster-meet.md
Normal file
55
iredis/data/commands/cluster-meet.md
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
`CLUSTER MEET` is used in order to connect different Redis nodes with cluster
|
||||||
|
support enabled, into a working cluster.
|
||||||
|
|
||||||
|
The basic idea is that nodes by default don't trust each other, and are
|
||||||
|
considered unknown, so that it is unlikely that different cluster nodes will mix
|
||||||
|
into a single one because of system administration errors or network addresses
|
||||||
|
modifications.
|
||||||
|
|
||||||
|
So in order for a given node to accept another one into the list of nodes
|
||||||
|
composing a Redis Cluster, there are only two ways:
|
||||||
|
|
||||||
|
1. The system administrator sends a `CLUSTER MEET` command to force a node to
|
||||||
|
meet another one.
|
||||||
|
2. An already known node sends a list of nodes in the gossip section that we are
|
||||||
|
not aware of. If the receiving node trusts the sending node as a known node,
|
||||||
|
it will process the gossip section and send an handshake to the nodes that
|
||||||
|
are still not known.
|
||||||
|
|
||||||
|
Note that Redis Cluster needs to form a full mesh (each node is connected with
|
||||||
|
each other node), but in order to create a cluster, there is no need to send all
|
||||||
|
the `CLUSTER MEET` commands needed to form the full mesh. What matter is to send
|
||||||
|
enough `CLUSTER MEET` messages so that each node can reach each other node
|
||||||
|
through a _chain of known nodes_. Thanks to the exchange of gossip information
|
||||||
|
in heartbeat packets, the missing links will be created.
|
||||||
|
|
||||||
|
So, if we link node A with node B via `CLUSTER MEET`, and B with C, A and C will
|
||||||
|
find their ways to handshake and create a link.
|
||||||
|
|
||||||
|
Another example: if we imagine a cluster formed of the following four nodes
|
||||||
|
called A, B, C and D, we may send just the following set of commands to A:
|
||||||
|
|
||||||
|
1. `CLUSTER MEET B-ip B-port`
|
||||||
|
2. `CLUSTER MEET C-ip C-port`
|
||||||
|
3. `CLUSTER MEET D-ip D-port`
|
||||||
|
|
||||||
|
As a side effect of `A` knowing and being known by all the other nodes, it will
|
||||||
|
send gossip sections in the heartbeat packets that will allow each other node to
|
||||||
|
create a link with each other one, forming a full mesh in a matter of seconds,
|
||||||
|
even if the cluster is large.
|
||||||
|
|
||||||
|
Moreover `CLUSTER MEET` does not need to be reciprocal. If I send the command to
|
||||||
|
A in order to join B, I don't need to also send it to B in order to join A.
|
||||||
|
|
||||||
|
## Implementation details: MEET and PING packets
|
||||||
|
|
||||||
|
When a given node receives a `CLUSTER MEET` message, the node specified in the
|
||||||
|
command still does not know the node we sent the command to. So in order for the
|
||||||
|
node to force the receiver to accept it as a trusted node, it sends a `MEET`
|
||||||
|
packet instead of a `PING` packet. The two packets have exactly the same format,
|
||||||
|
but the former forces the receiver to acknowledge the node as trusted.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` if the command was successful. If the address or port
|
||||||
|
specified are invalid an error is returned.
|
8
iredis/data/commands/cluster-myid.md
Normal file
8
iredis/data/commands/cluster-myid.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
Returns the node's id.
|
||||||
|
|
||||||
|
The `CLUSTER MYID` command returns the unique, auto-generated identifier that is
|
||||||
|
associated with the connected cluster node.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@bulk-string-reply: The node id.
|
147
iredis/data/commands/cluster-nodes.md
Normal file
147
iredis/data/commands/cluster-nodes.md
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
Each node in a Redis Cluster has its view of the current cluster configuration,
|
||||||
|
given by the set of known nodes, the state of the connection we have with such
|
||||||
|
nodes, their flags, properties and assigned slots, and so forth.
|
||||||
|
|
||||||
|
`CLUSTER NODES` provides all this information, that is, the current cluster
|
||||||
|
configuration of the node we are contacting, in a serialization format which
|
||||||
|
happens to be exactly the same as the one used by Redis Cluster itself in order
|
||||||
|
to store on disk the cluster state (however the on disk cluster state has a few
|
||||||
|
additional info appended at the end).
|
||||||
|
|
||||||
|
Note that normally clients willing to fetch the map between Cluster hash slots
|
||||||
|
and node addresses should use `CLUSTER SLOTS` instead. `CLUSTER NODES`, that
|
||||||
|
provides more information, should be used for administrative tasks, debugging,
|
||||||
|
and configuration inspections. It is also used by `redis-trib` in order to
|
||||||
|
manage a cluster.
|
||||||
|
|
||||||
|
## Serialization format
|
||||||
|
|
||||||
|
The output of the command is just a space-separated CSV string, where each line
|
||||||
|
represents a node in the cluster. The following is an example of output:
|
||||||
|
|
||||||
|
```
|
||||||
|
07c37dfeb235213a872192d90877d0cd55635b91 127.0.0.1:30004@31004 slave e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 0 1426238317239 4 connected
|
||||||
|
67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 127.0.0.1:30002@31002 master - 0 1426238316232 2 connected 5461-10922
|
||||||
|
292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f 127.0.0.1:30003@31003 master - 0 1426238318243 3 connected 10923-16383
|
||||||
|
6ec23923021cf3ffec47632106199cb7f496ce01 127.0.0.1:30005@31005 slave 67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 0 1426238316232 5 connected
|
||||||
|
824fe116063bc5fcf9f4ffd895bc17aee7731ac3 127.0.0.1:30006@31006 slave 292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f 0 1426238317741 6 connected
|
||||||
|
e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 127.0.0.1:30001@31001 myself,master - 0 0 1 connected 0-5460
|
||||||
|
```
|
||||||
|
|
||||||
|
Each line is composed of the following fields:
|
||||||
|
|
||||||
|
```
|
||||||
|
<id> <ip:port@cport> <flags> <master> <ping-sent> <pong-recv> <config-epoch> <link-state> <slot> <slot> ... <slot>
|
||||||
|
```
|
||||||
|
|
||||||
|
The meaning of each filed is the following:
|
||||||
|
|
||||||
|
1. `id`: The node ID, a 40 characters random string generated when a node is
|
||||||
|
created and never changed again (unless `CLUSTER RESET HARD` is used).
|
||||||
|
2. `ip:port@cport`: The node address where clients should contact the node to
|
||||||
|
run queries.
|
||||||
|
3. `flags`: A list of comma separated flags: `myself`, `master`, `slave`,
|
||||||
|
`fail?`, `fail`, `handshake`, `noaddr`, `noflags`. Flags are explained in
|
||||||
|
detail in the next section.
|
||||||
|
4. `master`: If the node is a replica, and the master is known, the master node
|
||||||
|
ID, otherwise the "-" character.
|
||||||
|
5. `ping-sent`: Milliseconds unix time at which the currently active ping was
|
||||||
|
sent, or zero if there are no pending pings.
|
||||||
|
6. `pong-recv`: Milliseconds unix time the last pong was received.
|
||||||
|
7. `config-epoch`: The configuration epoch (or version) of the current node (or
|
||||||
|
of the current master if the node is a replica). Each time there is a
|
||||||
|
failover, a new, unique, monotonically increasing configuration epoch is
|
||||||
|
created. If multiple nodes claim to serve the same hash slots, the one with
|
||||||
|
higher configuration epoch wins.
|
||||||
|
8. `link-state`: The state of the link used for the node-to-node cluster bus. We
|
||||||
|
use this link to communicate with the node. Can be `connected` or
|
||||||
|
`disconnected`.
|
||||||
|
9. `slot`: A hash slot number or range. Starting from argument number 9, but
|
||||||
|
there may be up to 16384 entries in total (limit never reached). This is the
|
||||||
|
list of hash slots served by this node. If the entry is just a number, is
|
||||||
|
parsed as such. If it is a range, it is in the form `start-end`, and means
|
||||||
|
that the node is responsible for all the hash slots from `start` to `end`
|
||||||
|
including the start and end values.
|
||||||
|
|
||||||
|
Meaning of the flags (field number 3):
|
||||||
|
|
||||||
|
- `myself`: The node you are contacting.
|
||||||
|
- `master`: Node is a master.
|
||||||
|
- `slave`: Node is a replica.
|
||||||
|
- `fail?`: Node is in `PFAIL` state. Not reachable for the node you are
|
||||||
|
contacting, but still logically reachable (not in `FAIL` state).
|
||||||
|
- `fail`: Node is in `FAIL` state. It was not reachable for multiple nodes that
|
||||||
|
promoted the `PFAIL` state to `FAIL`.
|
||||||
|
- `handshake`: Untrusted node, we are handshaking.
|
||||||
|
- `noaddr`: No address known for this node.
|
||||||
|
- `noflags`: No flags at all.
|
||||||
|
|
||||||
|
## Notes on published config epochs
|
||||||
|
|
||||||
|
Replicas broadcast their master's config epochs (in order to get an `UPDATE`
|
||||||
|
message if they are found to be stale), so the real config epoch of the replica
|
||||||
|
(which is meaningless more or less, since they don't serve hash slots) can be
|
||||||
|
only obtained checking the node flagged as `myself`, which is the entry of the
|
||||||
|
node we are asking to generate `CLUSTER NODES` output. The other replicas epochs
|
||||||
|
reflect what they publish in heartbeat packets, which is, the configuration
|
||||||
|
epoch of the masters they are currently replicating.
|
||||||
|
|
||||||
|
## Special slot entries
|
||||||
|
|
||||||
|
Normally hash slots associated to a given node are in one of the following
|
||||||
|
formats, as already explained above:
|
||||||
|
|
||||||
|
1. Single number: 3894
|
||||||
|
2. Range: 3900-4000
|
||||||
|
|
||||||
|
However node hash slots can be in a special state, used in order to communicate
|
||||||
|
errors after a node restart (mismatch between the keys in the AOF/RDB file, and
|
||||||
|
the node hash slots configuration), or when there is a resharding operation in
|
||||||
|
progress. This two states are **importing** and **migrating**.
|
||||||
|
|
||||||
|
The meaning of the two states is explained in the Redis Specification, however
|
||||||
|
the gist of the two states is the following:
|
||||||
|
|
||||||
|
- **Importing** slots are yet not part of the nodes hash slot, there is a
|
||||||
|
migration in progress. The node will accept queries about these slots only if
|
||||||
|
the `ASK` command is used.
|
||||||
|
- **Migrating** slots are assigned to the node, but are being migrated to some
|
||||||
|
other node. The node will accept queries if all the keys in the command exist
|
||||||
|
already, otherwise it will emit what is called an **ASK redirection**, to
|
||||||
|
force new keys creation directly in the importing node.
|
||||||
|
|
||||||
|
Importing and migrating slots are emitted in the `CLUSTER NODES` output as
|
||||||
|
follows:
|
||||||
|
|
||||||
|
- **Importing slot:** `[slot_number-<-importing_from_node_id]`
|
||||||
|
- **Migrating slot:** `[slot_number->-migrating_to_node_id]`
|
||||||
|
|
||||||
|
The following are a few examples of importing and migrating slots:
|
||||||
|
|
||||||
|
- `[93-<-292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f]`
|
||||||
|
- `[1002-<-67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1]`
|
||||||
|
- `[77->-e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca]`
|
||||||
|
- `[16311->-292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f]`
|
||||||
|
|
||||||
|
Note that the format does not have any space, so `CLUSTER NODES` output format
|
||||||
|
is plain CSV with space as separator even when this special slots are emitted.
|
||||||
|
However a complete parser for the format should be able to handle them.
|
||||||
|
|
||||||
|
Note that:
|
||||||
|
|
||||||
|
1. Migration and importing slots are only added to the node flagged as `myself`.
|
||||||
|
This information is local to a node, for its own slots.
|
||||||
|
2. Importing and migrating slots are provided as **additional info**. If the
|
||||||
|
node has a given hash slot assigned, it will be also a plain number in the
|
||||||
|
list of hash slots, so clients that don't have a clue about hash slots
|
||||||
|
migrations can just skip this special fields.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@bulk-string-reply: The serialized cluster configuration.
|
||||||
|
|
||||||
|
**A note about the word slave used in this man page and command name**: Starting
|
||||||
|
with Redis 5, if not for backward compatibility, the Redis project no longer
|
||||||
|
uses the word slave. Unfortunately in this command the word slave is part of the
|
||||||
|
protocol, so we'll be able to remove such occurrences only when this API will be
|
||||||
|
naturally deprecated.
|
16
iredis/data/commands/cluster-replicas.md
Normal file
16
iredis/data/commands/cluster-replicas.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
The command provides a list of replica nodes replicating from the specified
|
||||||
|
master node. The list is provided in the same format used by `CLUSTER NODES`
|
||||||
|
(please refer to its documentation for the specification of the format).
|
||||||
|
|
||||||
|
The command will fail if the specified node is not known or if it is not a
|
||||||
|
master according to the node table of the node receiving the command.
|
||||||
|
|
||||||
|
Note that if a replica is added, moved, or removed from a given master node, and
|
||||||
|
we ask `CLUSTER REPLICAS` to a node that has not yet received the configuration
|
||||||
|
update, it may show stale information. However eventually (in a matter of
|
||||||
|
seconds if there are no network partitions) all the nodes will agree about the
|
||||||
|
set of nodes associated with a given master.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
The command returns data in the same format as `CLUSTER NODES`.
|
29
iredis/data/commands/cluster-replicate.md
Normal file
29
iredis/data/commands/cluster-replicate.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
The command reconfigures a node as a replica of the specified master. If the
|
||||||
|
node receiving the command is an _empty master_, as a side effect of the
|
||||||
|
command, the node role is changed from master to replica.
|
||||||
|
|
||||||
|
Once a node is turned into the replica of another master node, there is no need
|
||||||
|
to inform the other cluster nodes about the change: heartbeat packets exchanged
|
||||||
|
between nodes will propagate the new configuration automatically.
|
||||||
|
|
||||||
|
A replica will always accept the command, assuming that:
|
||||||
|
|
||||||
|
1. The specified node ID exists in its nodes table.
|
||||||
|
2. The specified node ID does not identify the instance we are sending the
|
||||||
|
command to.
|
||||||
|
3. The specified node ID is a master.
|
||||||
|
|
||||||
|
If the node receiving the command is not already a replica, but is a master, the
|
||||||
|
command will only succeed, and the node will be converted into a replica, only
|
||||||
|
if the following additional conditions are met:
|
||||||
|
|
||||||
|
1. The node is not serving any hash slots.
|
||||||
|
2. The node is empty, no keys are stored at all in the key space.
|
||||||
|
|
||||||
|
If the command succeeds the new replica will immediately try to contact its
|
||||||
|
master in order to replicate from it.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` if the command was executed successfully, otherwise
|
||||||
|
an error is returned.
|
29
iredis/data/commands/cluster-reset.md
Normal file
29
iredis/data/commands/cluster-reset.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
Reset a Redis Cluster node, in a more or less drastic way depending on the reset
|
||||||
|
type, that can be **hard** or **soft**. Note that this command **does not work
|
||||||
|
for masters if they hold one or more keys**, in that case to completely reset a
|
||||||
|
master node keys must be removed first, e.g. by using `FLUSHALL` first, and then
|
||||||
|
`CLUSTER RESET`.
|
||||||
|
|
||||||
|
Effects on the node:
|
||||||
|
|
||||||
|
1. All the other nodes in the cluster are forgotten.
|
||||||
|
2. All the assigned / open slots are reset, so the slots-to-nodes mapping is
|
||||||
|
totally cleared.
|
||||||
|
3. If the node is a replica it is turned into an (empty) master. Its dataset is
|
||||||
|
flushed, so at the end the node will be an empty master.
|
||||||
|
4. **Hard reset only**: a new Node ID is generated.
|
||||||
|
5. **Hard reset only**: `currentEpoch` and `configEpoch` vars are set to 0.
|
||||||
|
6. The new configuration is persisted on disk in the node cluster configuration
|
||||||
|
file.
|
||||||
|
|
||||||
|
This command is mainly useful to re-provision a Redis Cluster node in order to
|
||||||
|
be used in the context of a new, different cluster. The command is also
|
||||||
|
extensively used by the Redis Cluster testing framework in order to reset the
|
||||||
|
state of the cluster every time a new test unit is executed.
|
||||||
|
|
||||||
|
If no reset type is specified, the default is **soft**.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` if the command was successful. Otherwise an error is
|
||||||
|
returned.
|
15
iredis/data/commands/cluster-saveconfig.md
Normal file
15
iredis/data/commands/cluster-saveconfig.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
Forces a node to save the `nodes.conf` configuration on disk. Before to return
|
||||||
|
the command calls `fsync(2)` in order to make sure the configuration is flushed
|
||||||
|
on the computer disk.
|
||||||
|
|
||||||
|
This command is mainly used in the event a `nodes.conf` node state file gets
|
||||||
|
lost / deleted for some reason, and we want to generate it again from scratch.
|
||||||
|
It can also be useful in case of mundane alterations of a node cluster
|
||||||
|
configuration via the `CLUSTER` command in order to ensure the new configuration
|
||||||
|
is persisted on disk, however all the commands should normally be able to auto
|
||||||
|
schedule to persist the configuration on disk when it is important to do so for
|
||||||
|
the correctness of the system in the event of a restart.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` or an error if the operation fails.
|
25
iredis/data/commands/cluster-set-config-epoch.md
Normal file
25
iredis/data/commands/cluster-set-config-epoch.md
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
This command sets a specific _config epoch_ in a fresh node. It only works when:
|
||||||
|
|
||||||
|
1. The nodes table of the node is empty.
|
||||||
|
2. The node current _config epoch_ is zero.
|
||||||
|
|
||||||
|
These prerequisites are needed since usually, manually altering the
|
||||||
|
configuration epoch of a node is unsafe, we want to be sure that the node with
|
||||||
|
the higher configuration epoch value (that is the last that failed over) wins
|
||||||
|
over other nodes in claiming the hash slots ownership.
|
||||||
|
|
||||||
|
However there is an exception to this rule, and it is when a new cluster is
|
||||||
|
created from scratch. Redis Cluster _config epoch collision resolution_
|
||||||
|
algorithm can deal with new nodes all configured with the same configuration at
|
||||||
|
startup, but this process is slow and should be the exception, only to make sure
|
||||||
|
that whatever happens, two more nodes eventually always move away from the state
|
||||||
|
of having the same configuration epoch.
|
||||||
|
|
||||||
|
So, using `CONFIG SET-CONFIG-EPOCH`, when a new cluster is created, we can
|
||||||
|
assign a different progressive configuration epoch to each node before joining
|
||||||
|
the cluster together.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` if the command was executed successfully, otherwise
|
||||||
|
an error is returned.
|
132
iredis/data/commands/cluster-setslot.md
Normal file
132
iredis/data/commands/cluster-setslot.md
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
`CLUSTER SETSLOT` is responsible of changing the state of a hash slot in the
|
||||||
|
receiving node in different ways. It can, depending on the subcommand used:
|
||||||
|
|
||||||
|
1. `MIGRATING` subcommand: Set a hash slot in _migrating_ state.
|
||||||
|
2. `IMPORTING` subcommand: Set a hash slot in _importing_ state.
|
||||||
|
3. `STABLE` subcommand: Clear any importing / migrating state from hash slot.
|
||||||
|
4. `NODE` subcommand: Bind the hash slot to a different node.
|
||||||
|
|
||||||
|
The command with its set of subcommands is useful in order to start and end
|
||||||
|
cluster live resharding operations, which are accomplished by setting a hash
|
||||||
|
slot in migrating state in the source node, and importing state in the
|
||||||
|
destination node.
|
||||||
|
|
||||||
|
Each subcommand is documented below. At the end you'll find a description of how
|
||||||
|
live resharding is performed using this command and other related commands.
|
||||||
|
|
||||||
|
## CLUSTER SETSLOT `<slot>` MIGRATING `<destination-node-id>`
|
||||||
|
|
||||||
|
This subcommand sets a slot to _migrating_ state. In order to set a slot in this
|
||||||
|
state, the node receiving the command must be the hash slot owner, otherwise an
|
||||||
|
error is returned.
|
||||||
|
|
||||||
|
When a slot is set in migrating state, the node changes behavior in the
|
||||||
|
following way:
|
||||||
|
|
||||||
|
1. If a command is received about an existing key, the command is processed as
|
||||||
|
usually.
|
||||||
|
2. If a command is received about a key that does not exists, an `ASK`
|
||||||
|
redirection is emitted by the node, asking the client to retry only that
|
||||||
|
specific query into `destination-node`. In this case the client should not
|
||||||
|
update its hash slot to node mapping.
|
||||||
|
3. If the command contains multiple keys, in case none exist, the behavior is
|
||||||
|
the same as point 2, if all exist, it is the same as point 1, however if only
|
||||||
|
a partial number of keys exist, the command emits a `TRYAGAIN` error in order
|
||||||
|
for the keys interested to finish being migrated to the target node, so that
|
||||||
|
the multi keys command can be executed.
|
||||||
|
|
||||||
|
## CLUSTER SETSLOT `<slot>` IMPORTING `<source-node-id>`
|
||||||
|
|
||||||
|
This subcommand is the reverse of `MIGRATING`, and prepares the destination node
|
||||||
|
to import keys from the specified source node. The command only works if the
|
||||||
|
node is not already owner of the specified hash slot.
|
||||||
|
|
||||||
|
When a slot is set in importing state, the node changes behavior in the
|
||||||
|
following way:
|
||||||
|
|
||||||
|
1. Commands about this hash slot are refused and a `MOVED` redirection is
|
||||||
|
generated as usually, but in the case the command follows an `ASKING`
|
||||||
|
command, in this case the command is executed.
|
||||||
|
|
||||||
|
In this way when a node in migrating state generates an `ASK` redirection, the
|
||||||
|
client contacts the target node, sends `ASKING`, and immediately after sends the
|
||||||
|
command. This way commands about non-existing keys in the old node or keys
|
||||||
|
already migrated to the target node are executed in the target node, so that:
|
||||||
|
|
||||||
|
1. New keys are always created in the target node. During a hash slot migration
|
||||||
|
we'll have to move only old keys, not new ones.
|
||||||
|
2. Commands about keys already migrated are correctly processed in the context
|
||||||
|
of the node which is the target of the migration, the new hash slot owner, in
|
||||||
|
order to guarantee consistency.
|
||||||
|
3. Without `ASKING` the behavior is the same as usually. This guarantees that
|
||||||
|
clients with a broken hash slots mapping will not write for error in the
|
||||||
|
target node, creating a new version of a key that has yet to be migrated.
|
||||||
|
|
||||||
|
## CLUSTER SETSLOT `<slot>` STABLE
|
||||||
|
|
||||||
|
This subcommand just clears migrating / importing state from the slot. It is
|
||||||
|
mainly used to fix a cluster stuck in a wrong state by `redis-trib fix`.
|
||||||
|
Normally the two states are cleared automatically at the end of the migration
|
||||||
|
using the `SETSLOT ... NODE ...` subcommand as explained in the next section.
|
||||||
|
|
||||||
|
## CLUSTER SETSLOT `<slot>` NODE `<node-id>`
|
||||||
|
|
||||||
|
The `NODE` subcommand is the one with the most complex semantics. It associates
|
||||||
|
the hash slot with the specified node, however the command works only in
|
||||||
|
specific situations and has different side effects depending on the slot state.
|
||||||
|
The following is the set of pre-conditions and side effects of the command:
|
||||||
|
|
||||||
|
1. If the current hash slot owner is the node receiving the command, but for
|
||||||
|
effect of the command the slot would be assigned to a different node, the
|
||||||
|
command will return an error if there are still keys for that hash slot in
|
||||||
|
the node receiving the command.
|
||||||
|
2. If the slot is in _migrating_ state, the state gets cleared when the slot is
|
||||||
|
assigned to another node.
|
||||||
|
3. If the slot was in _importing_ state in the node receiving the command, and
|
||||||
|
the command assigns the slot to this node (which happens in the target node
|
||||||
|
at the end of the resharding of a hash slot from one node to another), the
|
||||||
|
command has the following side effects: A) the _importing_ state is cleared.
|
||||||
|
B) If the node config epoch is not already the greatest of the cluster, it
|
||||||
|
generates a new one and assigns the new config epoch to itself. This way its
|
||||||
|
new hash slot ownership will win over any past configuration created by
|
||||||
|
previous failovers or slot migrations.
|
||||||
|
|
||||||
|
It is important to note that step 3 is the only time when a Redis Cluster node
|
||||||
|
will create a new config epoch without agreement from other nodes. This only
|
||||||
|
happens when a manual configuration is operated. However it is impossible that
|
||||||
|
this creates a non-transient setup where two nodes have the same config epoch,
|
||||||
|
since Redis Cluster uses a config epoch collision resolution algorithm.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: All the subcommands return `OK` if the command was
|
||||||
|
successful. Otherwise an error is returned.
|
||||||
|
|
||||||
|
## Redis Cluster live resharding explained
|
||||||
|
|
||||||
|
The `CLUSTER SETSLOT` command is an important piece used by Redis Cluster in
|
||||||
|
order to migrate all the keys contained in one hash slot from one node to
|
||||||
|
another. This is how the migration is orchestrated, with the help of other
|
||||||
|
commands as well. We'll call the node that has the current ownership of the hash
|
||||||
|
slot the `source` node, and the node where we want to migrate the `destination`
|
||||||
|
node.
|
||||||
|
|
||||||
|
1. Set the destination node slot to _importing_ state using
|
||||||
|
`CLUSTER SETSLOT <slot> IMPORTING <source-node-id>`.
|
||||||
|
2. Set the source node slot to _migrating_ state using
|
||||||
|
`CLUSTER SETSLOT <slot> MIGRATING <destination-node-id>`.
|
||||||
|
3. Get keys from the source node with `CLUSTER GETKEYSINSLOT` command and move
|
||||||
|
them into the destination node using the `MIGRATE` command.
|
||||||
|
4. Use `CLUSTER SETSLOT <slot> NODE <destination-node-id>` in the source or
|
||||||
|
destination.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- The order of step 1 and 2 is important. We want the destination node to be
|
||||||
|
ready to accept `ASK` redirections when the source node is configured to
|
||||||
|
redirect.
|
||||||
|
- Step 4 does not technically need to use `SETSLOT` in the nodes not involved in
|
||||||
|
the resharding, since the configuration will eventually propagate itself,
|
||||||
|
however it is a good idea to do so in order to stop nodes from pointing to the
|
||||||
|
wrong node for the hash slot moved as soon as possible, resulting in less
|
||||||
|
redirections to find the right node.
|
22
iredis/data/commands/cluster-slaves.md
Normal file
22
iredis/data/commands/cluster-slaves.md
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
**A note about the word slave used in this man page and command name**: Starting
|
||||||
|
with Redis 5 this command: starting with Redis version 5, if not for backward
|
||||||
|
compatibility, the Redis project no longer uses the word slave. Please use the
|
||||||
|
new command `CLUSTER REPLICAS`. The command `SLAVEOF` will continue to work for
|
||||||
|
backward compatibility.
|
||||||
|
|
||||||
|
The command provides a list of replica nodes replicating from the specified
|
||||||
|
master node. The list is provided in the same format used by `CLUSTER NODES`
|
||||||
|
(please refer to its documentation for the specification of the format).
|
||||||
|
|
||||||
|
The command will fail if the specified node is not known or if it is not a
|
||||||
|
master according to the node table of the node receiving the command.
|
||||||
|
|
||||||
|
Note that if a replica is added, moved, or removed from a given master node, and
|
||||||
|
we ask `CLUSTER SLAVES` to a node that has not yet received the configuration
|
||||||
|
update, it may show stale information. However eventually (in a matter of
|
||||||
|
seconds if there are no network partitions) all the nodes will agree about the
|
||||||
|
set of nodes associated with a given master.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
The command returns data in the same format as `CLUSTER NODES`.
|
102
iredis/data/commands/cluster-slots.md
Normal file
102
iredis/data/commands/cluster-slots.md
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
`CLUSTER SLOTS` returns details about which cluster slots map to which Redis
|
||||||
|
instances. The command is suitable to be used by Redis Cluster client libraries
|
||||||
|
implementations in order to retrieve (or update when a redirection is received)
|
||||||
|
the map associating cluster _hash slots_ with actual nodes network coordinates
|
||||||
|
(composed of an IP address and a TCP port), so that when a command is received,
|
||||||
|
it can be sent to what is likely the right instance for the keys specified in
|
||||||
|
the command.
|
||||||
|
|
||||||
|
## Nested Result Array
|
||||||
|
|
||||||
|
Each nested result is:
|
||||||
|
|
||||||
|
- Start slot range
|
||||||
|
- End slot range
|
||||||
|
- Master for slot range represented as nested IP/Port array
|
||||||
|
- First replica of master for slot range
|
||||||
|
- Second replica
|
||||||
|
- ...continues until all replicas for this master are returned.
|
||||||
|
|
||||||
|
Each result includes all active replicas of the master instance for the listed
|
||||||
|
slot range. Failed replicas are not returned.
|
||||||
|
|
||||||
|
The third nested reply is guaranteed to be the IP/Port pair of the master
|
||||||
|
instance for the slot range. All IP/Port pairs after the third nested reply are
|
||||||
|
replicas of the master.
|
||||||
|
|
||||||
|
If a cluster instance has non-contiguous slots (e.g. 1-400,900,1800-6000) then
|
||||||
|
master and replica IP/Port results will be duplicated for each top-level slot
|
||||||
|
range reply.
|
||||||
|
|
||||||
|
**Warning:** Newer versions of Redis Cluster will output, for each Redis
|
||||||
|
instance, not just the IP and port, but also the node ID as third element of the
|
||||||
|
array. In future versions there could be more elements describing the node
|
||||||
|
better. In general a client implementation should just rely on the fact that
|
||||||
|
certain parameters are at fixed positions as specified, but more parameters may
|
||||||
|
follow and should be ignored. Similarly a client library should try if possible
|
||||||
|
to cope with the fact that older versions may just have the IP and port
|
||||||
|
parameter.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@array-reply: nested list of slot ranges with IP/Port mappings.
|
||||||
|
|
||||||
|
### Sample Output (old version)
|
||||||
|
|
||||||
|
```
|
||||||
|
127.0.0.1:7001> cluster slots
|
||||||
|
1) 1) (integer) 0
|
||||||
|
2) (integer) 4095
|
||||||
|
3) 1) "127.0.0.1"
|
||||||
|
2) (integer) 7000
|
||||||
|
4) 1) "127.0.0.1"
|
||||||
|
2) (integer) 7004
|
||||||
|
2) 1) (integer) 12288
|
||||||
|
2) (integer) 16383
|
||||||
|
3) 1) "127.0.0.1"
|
||||||
|
2) (integer) 7003
|
||||||
|
4) 1) "127.0.0.1"
|
||||||
|
2) (integer) 7007
|
||||||
|
3) 1) (integer) 4096
|
||||||
|
2) (integer) 8191
|
||||||
|
3) 1) "127.0.0.1"
|
||||||
|
2) (integer) 7001
|
||||||
|
4) 1) "127.0.0.1"
|
||||||
|
2) (integer) 7005
|
||||||
|
4) 1) (integer) 8192
|
||||||
|
2) (integer) 12287
|
||||||
|
3) 1) "127.0.0.1"
|
||||||
|
2) (integer) 7002
|
||||||
|
4) 1) "127.0.0.1"
|
||||||
|
2) (integer) 7006
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sample Output (new version, includes IDs)
|
||||||
|
|
||||||
|
```
|
||||||
|
127.0.0.1:30001> cluster slots
|
||||||
|
1) 1) (integer) 0
|
||||||
|
2) (integer) 5460
|
||||||
|
3) 1) "127.0.0.1"
|
||||||
|
2) (integer) 30001
|
||||||
|
3) "09dbe9720cda62f7865eabc5fd8857c5d2678366"
|
||||||
|
4) 1) "127.0.0.1"
|
||||||
|
2) (integer) 30004
|
||||||
|
3) "821d8ca00d7ccf931ed3ffc7e3db0599d2271abf"
|
||||||
|
2) 1) (integer) 5461
|
||||||
|
2) (integer) 10922
|
||||||
|
3) 1) "127.0.0.1"
|
||||||
|
2) (integer) 30002
|
||||||
|
3) "c9d93d9f2c0c524ff34cc11838c2003d8c29e013"
|
||||||
|
4) 1) "127.0.0.1"
|
||||||
|
2) (integer) 30005
|
||||||
|
3) "faadb3eb99009de4ab72ad6b6ed87634c7ee410f"
|
||||||
|
3) 1) (integer) 10923
|
||||||
|
2) (integer) 16383
|
||||||
|
3) 1) "127.0.0.1"
|
||||||
|
2) (integer) 30003
|
||||||
|
3) "044ec91f325b7595e76dbcb18cc688b6a5b434a1"
|
||||||
|
4) 1) "127.0.0.1"
|
||||||
|
2) (integer) 30006
|
||||||
|
3) "58e6e48d41228013e5d9c1c37c5060693925e97e"
|
||||||
|
```
|
11
iredis/data/commands/command-count.md
Normal file
11
iredis/data/commands/command-count.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
Returns @integer-reply of number of total commands in this Redis server.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply: number of commands returned by `COMMAND`
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```cli
|
||||||
|
COMMAND COUNT
|
||||||
|
```
|
21
iredis/data/commands/command-getkeys.md
Normal file
21
iredis/data/commands/command-getkeys.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
Returns @array-reply of keys from a full Redis command.
|
||||||
|
|
||||||
|
`COMMAND GETKEYS` is a helper command to let you find the keys from a full Redis
|
||||||
|
command.
|
||||||
|
|
||||||
|
`COMMAND` shows some commands as having movablekeys meaning the entire command
|
||||||
|
must be parsed to discover storage or retrieval keys. You can use
|
||||||
|
`COMMAND GETKEYS` to discover key positions directly from how Redis parses the
|
||||||
|
commands.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@array-reply: list of keys from your command.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```cli
|
||||||
|
COMMAND GETKEYS MSET a b c d e f
|
||||||
|
COMMAND GETKEYS EVAL "not consulted" 3 key1 key2 key3 arg1 arg2 arg3 argN
|
||||||
|
COMMAND GETKEYS SORT mylist ALPHA STORE outlist
|
||||||
|
```
|
18
iredis/data/commands/command-info.md
Normal file
18
iredis/data/commands/command-info.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
Returns @array-reply of details about multiple Redis commands.
|
||||||
|
|
||||||
|
Same result format as `COMMAND` except you can specify which commands get
|
||||||
|
returned.
|
||||||
|
|
||||||
|
If you request details about non-existing commands, their return position will
|
||||||
|
be nil.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@array-reply: nested list of command details.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```cli
|
||||||
|
COMMAND INFO get set eval
|
||||||
|
COMMAND INFO foo evalsha config bar
|
||||||
|
```
|
179
iredis/data/commands/command.md
Normal file
179
iredis/data/commands/command.md
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
Returns @array-reply of details about all Redis commands.
|
||||||
|
|
||||||
|
Cluster clients must be aware of key positions in commands so commands can go to
|
||||||
|
matching instances, but Redis commands vary between accepting one key, multiple
|
||||||
|
keys, or even multiple keys separated by other data.
|
||||||
|
|
||||||
|
You can use `COMMAND` to cache a mapping between commands and key positions for
|
||||||
|
each command to enable exact routing of commands to cluster instances.
|
||||||
|
|
||||||
|
## Nested Result Array
|
||||||
|
|
||||||
|
Each top-level result contains six nested results. Each nested result is:
|
||||||
|
|
||||||
|
- command name
|
||||||
|
- command arity specification
|
||||||
|
- nested @array-reply of command flags
|
||||||
|
- position of first key in argument list
|
||||||
|
- position of last key in argument list
|
||||||
|
- step count for locating repeating keys
|
||||||
|
|
||||||
|
### Command Name
|
||||||
|
|
||||||
|
Command name is the command returned as a lowercase string.
|
||||||
|
|
||||||
|
### Command Arity
|
||||||
|
|
||||||
|
<table style="width:50%">
|
||||||
|
<tr><td>
|
||||||
|
<pre>
|
||||||
|
<code>1) 1) "get"
|
||||||
|
2) (integer) 2
|
||||||
|
3) 1) readonly
|
||||||
|
4) (integer) 1
|
||||||
|
5) (integer) 1
|
||||||
|
6) (integer) 1
|
||||||
|
</code>
|
||||||
|
</pre>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<pre>
|
||||||
|
<code>1) 1) "mget"
|
||||||
|
2) (integer) -2
|
||||||
|
3) 1) readonly
|
||||||
|
4) (integer) 1
|
||||||
|
5) (integer) -1
|
||||||
|
6) (integer) 1
|
||||||
|
</code>
|
||||||
|
</pre>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
Command arity follows a simple pattern:
|
||||||
|
|
||||||
|
- positive if command has fixed number of required arguments.
|
||||||
|
- negative if command has minimum number of required arguments, but may have
|
||||||
|
more.
|
||||||
|
|
||||||
|
Command arity _includes_ counting the command name itself.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- `GET` arity is 2 since the command only accepts one argument and always has
|
||||||
|
the format `GET _key_`.
|
||||||
|
- `MGET` arity is -2 since the command accepts at a minimum one argument, but up
|
||||||
|
to an unlimited number: `MGET _key1_ [key2] [key3] ...`.
|
||||||
|
|
||||||
|
Also note with `MGET`, the -1 value for "last key position" means the list of
|
||||||
|
keys may have unlimited length.
|
||||||
|
|
||||||
|
### Flags
|
||||||
|
|
||||||
|
Command flags is @array-reply containing one or more status replies:
|
||||||
|
|
||||||
|
- _write_ - command may result in modifications
|
||||||
|
- _readonly_ - command will never modify keys
|
||||||
|
- _denyoom_ - reject command if currently OOM
|
||||||
|
- _admin_ - server admin command
|
||||||
|
- _pubsub_ - pubsub-related command
|
||||||
|
- _noscript_ - deny this command from scripts
|
||||||
|
- _random_ - command has random results, dangerous for scripts
|
||||||
|
- _sort_for_script_ - if called from script, sort output
|
||||||
|
- _loading_ - allow command while database is loading
|
||||||
|
- _stale_ - allow command while replica has stale data
|
||||||
|
- _skip_monitor_ - do not show this command in MONITOR
|
||||||
|
- _asking_ - cluster related - accept even if importing
|
||||||
|
- _fast_ - command operates in constant or log(N) time. Used for latency
|
||||||
|
monitoring.
|
||||||
|
- _movablekeys_ - keys have no pre-determined position. You must discover keys
|
||||||
|
yourself.
|
||||||
|
|
||||||
|
### Movable Keys
|
||||||
|
|
||||||
|
```
|
||||||
|
1) 1) "sort"
|
||||||
|
2) (integer) -2
|
||||||
|
3) 1) write
|
||||||
|
2) denyoom
|
||||||
|
3) movablekeys
|
||||||
|
4) (integer) 1
|
||||||
|
5) (integer) 1
|
||||||
|
6) (integer) 1
|
||||||
|
```
|
||||||
|
|
||||||
|
Some Redis commands have no predetermined key locations. For those commands,
|
||||||
|
flag `movablekeys` is added to the command flags @array-reply. Your Redis
|
||||||
|
Cluster client needs to parse commands marked `movablekeys` to locate all
|
||||||
|
relevant key positions.
|
||||||
|
|
||||||
|
Complete list of commands currently requiring key location parsing:
|
||||||
|
|
||||||
|
- `SORT` - optional `STORE` key, optional `BY` weights, optional `GET` keys
|
||||||
|
- `ZUNIONSTORE` - keys stop when `WEIGHT` or `AGGREGATE` starts
|
||||||
|
- `ZINTERSTORE` - keys stop when `WEIGHT` or `AGGREGATE` starts
|
||||||
|
- `EVAL` - keys stop after `numkeys` count arguments
|
||||||
|
- `EVALSHA` - keys stop after `numkeys` count arguments
|
||||||
|
|
||||||
|
Also see `COMMAND GETKEYS` for getting your Redis server tell you where keys are
|
||||||
|
in any given command.
|
||||||
|
|
||||||
|
### First Key in Argument List
|
||||||
|
|
||||||
|
For most commands the first key is position 1. Position 0 is always the command
|
||||||
|
name itself.
|
||||||
|
|
||||||
|
### Last Key in Argument List
|
||||||
|
|
||||||
|
Redis commands usually accept one key, two keys, or an unlimited number of keys.
|
||||||
|
|
||||||
|
If a command accepts one key, the first key and last key positions is 1.
|
||||||
|
|
||||||
|
If a command accepts two keys (e.g. `BRPOPLPUSH`, `SMOVE`, `RENAME`, ...) then
|
||||||
|
the last key position is the location of the last key in the argument list.
|
||||||
|
|
||||||
|
If a command accepts an unlimited number of keys, the last key position is -1.
|
||||||
|
|
||||||
|
### Step Count
|
||||||
|
|
||||||
|
<table style="width:50%">
|
||||||
|
<tr><td>
|
||||||
|
<pre>
|
||||||
|
<code>1) 1) "mset"
|
||||||
|
2) (integer) -3
|
||||||
|
3) 1) write
|
||||||
|
2) denyoom
|
||||||
|
4) (integer) 1
|
||||||
|
5) (integer) -1
|
||||||
|
6) (integer) 2
|
||||||
|
</code>
|
||||||
|
</pre>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<pre>
|
||||||
|
<code>1) 1) "mget"
|
||||||
|
2) (integer) -2
|
||||||
|
3) 1) readonly
|
||||||
|
4) (integer) 1
|
||||||
|
5) (integer) -1
|
||||||
|
6) (integer) 1
|
||||||
|
</code>
|
||||||
|
</pre>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
Key step count allows us to find key positions in commands like `MSET` where the
|
||||||
|
format is `MSET _key1_ _val1_ [key2] [val2] [key3] [val3]...`.
|
||||||
|
|
||||||
|
In the case of `MSET`, keys are every other position so the step value is 2.
|
||||||
|
Compare with `MGET` above where the step value is just 1.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@array-reply: nested list of command details. Commands are returned in random
|
||||||
|
order.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```cli
|
||||||
|
COMMAND
|
||||||
|
```
|
52
iredis/data/commands/config-get.md
Normal file
52
iredis/data/commands/config-get.md
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
The `CONFIG GET` command is used to read the configuration parameters of a
|
||||||
|
running Redis server. Not all the configuration parameters are supported in
|
||||||
|
Redis 2.4, while Redis 2.6 can read the whole configuration of a server using
|
||||||
|
this command.
|
||||||
|
|
||||||
|
The symmetric command used to alter the configuration at run time is
|
||||||
|
`CONFIG SET`.
|
||||||
|
|
||||||
|
`CONFIG GET` takes a single argument, which is a glob-style pattern. All the
|
||||||
|
configuration parameters matching this parameter are reported as a list of
|
||||||
|
key-value pairs. Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
redis> config get *max-*-entries*
|
||||||
|
1) "hash-max-zipmap-entries"
|
||||||
|
2) "512"
|
||||||
|
3) "list-max-ziplist-entries"
|
||||||
|
4) "512"
|
||||||
|
5) "set-max-intset-entries"
|
||||||
|
6) "512"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can obtain a list of all the supported configuration parameters by typing
|
||||||
|
`CONFIG GET *` in an open `redis-cli` prompt.
|
||||||
|
|
||||||
|
All the supported parameters have the same meaning of the equivalent
|
||||||
|
configuration parameter used in the [redis.conf][hgcarr22rc] file, with the
|
||||||
|
following important differences:
|
||||||
|
|
||||||
|
[hgcarr22rc]: http://github.com/redis/redis/raw/2.8/redis.conf
|
||||||
|
|
||||||
|
- Where bytes or other quantities are specified, it is not possible to use the
|
||||||
|
`redis.conf` abbreviated form (`10k`, `2gb` ... and so forth), everything
|
||||||
|
should be specified as a well-formed 64-bit integer, in the base unit of the
|
||||||
|
configuration directive.
|
||||||
|
- The save parameter is a single string of space-separated integers. Every pair
|
||||||
|
of integers represent a seconds/modifications threshold.
|
||||||
|
|
||||||
|
For instance what in `redis.conf` looks like:
|
||||||
|
|
||||||
|
```
|
||||||
|
save 900 1
|
||||||
|
save 300 10
|
||||||
|
```
|
||||||
|
|
||||||
|
that means, save after 900 seconds if there is at least 1 change to the dataset,
|
||||||
|
and after 300 seconds if there are at least 10 changes to the dataset, will be
|
||||||
|
reported by `CONFIG GET` as "900 1 300 10".
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
The return type of the command is a @array-reply.
|
16
iredis/data/commands/config-resetstat.md
Normal file
16
iredis/data/commands/config-resetstat.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
Resets the statistics reported by Redis using the `INFO` command.
|
||||||
|
|
||||||
|
These are the counters that are reset:
|
||||||
|
|
||||||
|
- Keyspace hits
|
||||||
|
- Keyspace misses
|
||||||
|
- Number of commands processed
|
||||||
|
- Number of connections received
|
||||||
|
- Number of expired keys
|
||||||
|
- Number of rejected connections
|
||||||
|
- Latest fork(2) time
|
||||||
|
- The `aof_delayed_fsync` counter
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: always `OK`.
|
37
iredis/data/commands/config-rewrite.md
Normal file
37
iredis/data/commands/config-rewrite.md
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
The `CONFIG REWRITE` command rewrites the `redis.conf` file the server was
|
||||||
|
started with, applying the minimal changes needed to make it reflect the
|
||||||
|
configuration currently used by the server, which may be different compared to
|
||||||
|
the original one because of the use of the `CONFIG SET` command.
|
||||||
|
|
||||||
|
The rewrite is performed in a very conservative way:
|
||||||
|
|
||||||
|
- Comments and the overall structure of the original redis.conf are preserved as
|
||||||
|
much as possible.
|
||||||
|
- If an option already exists in the old redis.conf file, it will be rewritten
|
||||||
|
at the same position (line number).
|
||||||
|
- If an option was not already present, but it is set to its default value, it
|
||||||
|
is not added by the rewrite process.
|
||||||
|
- If an option was not already present, but it is set to a non-default value, it
|
||||||
|
is appended at the end of the file.
|
||||||
|
- Non used lines are blanked. For instance if you used to have multiple `save`
|
||||||
|
directives, but the current configuration has fewer or none as you disabled
|
||||||
|
RDB persistence, all the lines will be blanked.
|
||||||
|
|
||||||
|
CONFIG REWRITE is also able to rewrite the configuration file from scratch if
|
||||||
|
the original one no longer exists for some reason. However if the server was
|
||||||
|
started without a configuration file at all, the CONFIG REWRITE will just return
|
||||||
|
an error.
|
||||||
|
|
||||||
|
## Atomic rewrite process
|
||||||
|
|
||||||
|
In order to make sure the redis.conf file is always consistent, that is, on
|
||||||
|
errors or crashes you always end with the old file, or the new one, the rewrite
|
||||||
|
is performed with a single `write(2)` call that has enough content to be at
|
||||||
|
least as big as the old file. Sometimes additional padding in the form of
|
||||||
|
comments is added in order to make sure the resulting file is big enough, and
|
||||||
|
later the file gets truncated to remove the padding at the end.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` when the configuration was rewritten properly.
|
||||||
|
Otherwise an error is returned.
|
56
iredis/data/commands/config-set.md
Normal file
56
iredis/data/commands/config-set.md
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
The `CONFIG SET` command is used in order to reconfigure the server at run time
|
||||||
|
without the need to restart Redis. You can change both trivial parameters or
|
||||||
|
switch from one to another persistence option using this command.
|
||||||
|
|
||||||
|
The list of configuration parameters supported by `CONFIG SET` can be obtained
|
||||||
|
issuing a `CONFIG GET *` command, that is the symmetrical command used to obtain
|
||||||
|
information about the configuration of a running Redis instance.
|
||||||
|
|
||||||
|
All the configuration parameters set using `CONFIG SET` are immediately loaded
|
||||||
|
by Redis and will take effect starting with the next command executed.
|
||||||
|
|
||||||
|
All the supported parameters have the same meaning of the equivalent
|
||||||
|
configuration parameter used in the [redis.conf][hgcarr22rc] file, with the
|
||||||
|
following important differences:
|
||||||
|
|
||||||
|
[hgcarr22rc]: http://github.com/redis/redis/raw/2.8/redis.conf
|
||||||
|
|
||||||
|
- In options where bytes or other quantities are specified, it is not possible
|
||||||
|
to use the `redis.conf` abbreviated form (`10k`, `2gb` ... and so forth),
|
||||||
|
everything should be specified as a well-formed 64-bit integer, in the base
|
||||||
|
unit of the configuration directive. However since Redis version 3.0 or
|
||||||
|
greater, it is possible to use `CONFIG SET` with memory units for `maxmemory`,
|
||||||
|
client output buffers, and replication backlog size.
|
||||||
|
- The save parameter is a single string of space-separated integers. Every pair
|
||||||
|
of integers represent a seconds/modifications threshold.
|
||||||
|
|
||||||
|
For instance what in `redis.conf` looks like:
|
||||||
|
|
||||||
|
```
|
||||||
|
save 900 1
|
||||||
|
save 300 10
|
||||||
|
```
|
||||||
|
|
||||||
|
that means, save after 900 seconds if there is at least 1 change to the dataset,
|
||||||
|
and after 300 seconds if there are at least 10 changes to the dataset, should be
|
||||||
|
set using `CONFIG SET SAVE "900 1 300 10"`.
|
||||||
|
|
||||||
|
It is possible to switch persistence from RDB snapshotting to append-only file
|
||||||
|
(and the other way around) using the `CONFIG SET` command. For more information
|
||||||
|
about how to do that please check the [persistence page][tp].
|
||||||
|
|
||||||
|
[tp]: /topics/persistence
|
||||||
|
|
||||||
|
In general what you should know is that setting the `appendonly` parameter to
|
||||||
|
`yes` will start a background process to save the initial append-only file
|
||||||
|
(obtained from the in memory data set), and will append all the subsequent
|
||||||
|
commands on the append-only file, thus obtaining exactly the same effect of a
|
||||||
|
Redis server that started with AOF turned on since the start.
|
||||||
|
|
||||||
|
You can have both the AOF enabled with RDB snapshotting if you want, the two
|
||||||
|
options are not mutually exclusive.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply: `OK` when the configuration was set properly. Otherwise an
|
||||||
|
error is returned.
|
5
iredis/data/commands/dbsize.md
Normal file
5
iredis/data/commands/dbsize.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
Return the number of keys in the currently-selected database.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply
|
6
iredis/data/commands/debug-object.md
Normal file
6
iredis/data/commands/debug-object.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
`DEBUG OBJECT` is a debugging command that should not be used by clients. Check
|
||||||
|
the `OBJECT` command instead.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply
|
6
iredis/data/commands/debug-segfault.md
Normal file
6
iredis/data/commands/debug-segfault.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
`DEBUG SEGFAULT` performs an invalid memory access that crashes Redis. It is
|
||||||
|
used to simulate bugs during the development.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@simple-string-reply
|
19
iredis/data/commands/decr.md
Normal file
19
iredis/data/commands/decr.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Decrements the number stored at `key` by one. If the key does not exist, it is
|
||||||
|
set to `0` before performing the operation. An error is returned if the key
|
||||||
|
contains a value of the wrong type or contains a string that can not be
|
||||||
|
represented as integer. This operation is limited to **64 bit signed integers**.
|
||||||
|
|
||||||
|
See `INCR` for extra information on increment/decrement operations.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply: the value of `key` after the decrement
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```cli
|
||||||
|
SET mykey "10"
|
||||||
|
DECR mykey
|
||||||
|
SET mykey "234293482390480948029348230948"
|
||||||
|
DECR mykey
|
||||||
|
```
|
17
iredis/data/commands/decrby.md
Normal file
17
iredis/data/commands/decrby.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
Decrements the number stored at `key` by `decrement`. If the key does not exist,
|
||||||
|
it is set to `0` before performing the operation. An error is returned if the
|
||||||
|
key contains a value of the wrong type or contains a string that can not be
|
||||||
|
represented as integer. This operation is limited to 64 bit signed integers.
|
||||||
|
|
||||||
|
See `INCR` for extra information on increment/decrement operations.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply: the value of `key` after the decrement
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```cli
|
||||||
|
SET mykey "10"
|
||||||
|
DECRBY mykey 3
|
||||||
|
```
|
13
iredis/data/commands/del.md
Normal file
13
iredis/data/commands/del.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
Removes the specified keys. A key is ignored if it does not exist.
|
||||||
|
|
||||||
|
@return
|
||||||
|
|
||||||
|
@integer-reply: The number of keys that were removed.
|
||||||
|
|
||||||
|
@examples
|
||||||
|
|
||||||
|
```cli
|
||||||
|
SET key1 "Hello"
|
||||||
|
SET key2 "World"
|
||||||
|
DEL key1 key2 key3
|
||||||
|
```
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue