Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f622330f41 | ||
|
|
3ad60b9c69 | ||
|
|
897a2035d9 | ||
|
|
351695a7c5 | ||
|
|
d527c4ab8f | ||
|
|
79840796f6 | ||
|
|
24507ed6b4 | ||
|
|
484bfa9ba0 | ||
|
|
8f8073ca3e | ||
|
|
7bcc407857 | ||
|
|
8c24a1d8c6 | ||
|
|
4ca2fffb33 | ||
|
|
d198741b37 | ||
|
|
afe6e5384d | ||
|
|
b6f8cf9155 | ||
|
|
9bac4fb257 | ||
|
|
7958db9c0d | ||
|
|
4869c146fe | ||
|
|
b26387ea9b | ||
|
|
76a660ebed |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,6 +1,3 @@
|
|||||||
.idea/**
|
/data/config.json
|
||||||
.vscode/**
|
/data/*_db
|
||||||
.vstags
|
/aleesa-irc-go
|
||||||
data/config.json
|
|
||||||
data/*_db
|
|
||||||
aleesa-irc-go
|
|
||||||
|
|||||||
83
.golangci.yml
Normal file
83
.golangci.yml
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
version: "2"
|
||||||
|
run:
|
||||||
|
go: "1.21"
|
||||||
|
modules-download-mode: readonly
|
||||||
|
tests: false
|
||||||
|
allow-parallel-runners: true
|
||||||
|
linters:
|
||||||
|
default: none
|
||||||
|
enable:
|
||||||
|
- asasalint
|
||||||
|
- asciicheck
|
||||||
|
- bodyclose
|
||||||
|
- containedctx
|
||||||
|
- contextcheck
|
||||||
|
- decorder
|
||||||
|
- durationcheck
|
||||||
|
- errcheck
|
||||||
|
- errchkjson
|
||||||
|
- errorlint
|
||||||
|
- exhaustive
|
||||||
|
- ginkgolinter
|
||||||
|
- gocheckcompilerdirectives
|
||||||
|
- gocritic
|
||||||
|
- godot
|
||||||
|
- gosec
|
||||||
|
- gosmopolitan
|
||||||
|
- govet
|
||||||
|
- grouper
|
||||||
|
- importas
|
||||||
|
- ineffassign
|
||||||
|
- ireturn
|
||||||
|
- makezero
|
||||||
|
- mirror
|
||||||
|
- nakedret
|
||||||
|
- nilerr
|
||||||
|
- nilnil
|
||||||
|
- nlreturn
|
||||||
|
- noctx
|
||||||
|
- nolintlint
|
||||||
|
- nonamedreturns
|
||||||
|
- predeclared
|
||||||
|
- promlinter
|
||||||
|
- reassign
|
||||||
|
- revive
|
||||||
|
- rowserrcheck
|
||||||
|
- staticcheck
|
||||||
|
- unconvert
|
||||||
|
- unused
|
||||||
|
- wastedassign
|
||||||
|
- whitespace
|
||||||
|
- wsl
|
||||||
|
settings:
|
||||||
|
gosec:
|
||||||
|
excludes:
|
||||||
|
# Оно не используется для повышения безопасности или каких-то таких секъюрных вещей, оно используется для выборки
|
||||||
|
# произвольного элемента из массива, так что ИДИ НАХУЙ с этой проверкой и не еби мозги липовой безопасностью.
|
||||||
|
- G404
|
||||||
|
# Не еби мозги. Сертификаты для локалхоста или для локальной сети могут быть невалидными, если ты сможешь
|
||||||
|
# выписать валидный серт для локалхоста, тебе миллионы разработчиков поставят золотой памятник. Просто потому
|
||||||
|
# что эти пидоры-безпасники всрали всё что только можно всрать своими дебильными политиками.
|
||||||
|
- G402
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
presets:
|
||||||
|
- comments
|
||||||
|
- common-false-positives
|
||||||
|
- legacy
|
||||||
|
- std-error-handling
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
formatters:
|
||||||
|
enable:
|
||||||
|
- gofmt
|
||||||
|
- goimports
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
- vendor$
|
||||||
12
DESIGN
Normal file
12
DESIGN
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Для irc admin-модуля:
|
||||||
|
|
||||||
|
1. берём pebbledb как бэкэнд сохранения настроек.
|
||||||
|
|
||||||
|
2. имплементировать trusted-участников чятика для настройки бота под конкретный канальчик.
|
||||||
|
Есть два варианта:
|
||||||
|
- ручной список в конфиге, где прописывается owner бота и доверенные лица (опционально доверенных можно прописывать в pebbledb admin_db)
|
||||||
|
- доверенными являются op-ы
|
||||||
|
|
||||||
|
Первый вариант - довольно гибкий, но у нас всегда bot owner является доверенным. Не то чтобы это неправильно. Но с другой стороны можно сделать owner-а суперадмином, а другим админам дать возможность добавлять user-ов для своего канальчика в список доверенных. И запретить удалять себя из списка доверенных.
|
||||||
|
|
||||||
|
Второй вариант проще. И выглядит более привычным в текущих реалиях. Но есть нюанс - у нас нету простой возможности вынуть список участников вместе с флагами (предположительно). Кроме того, необходимо писать обработчк ивентов для всех изменений состояний юзеров (join/part и op/deop) для поддержания списка участников в актуальном состоянии.
|
||||||
85
Makefile
85
Makefile
@@ -1,23 +1,88 @@
|
|||||||
#!/usr/bin/env gmake -f
|
#!/usr/bin/env gmake -f
|
||||||
|
|
||||||
GOOPTS=CGO_ENABLED=0
|
GOOPTS=CGO_ENABLED=0
|
||||||
BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l
|
BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l -trimpath -buildvcs=false
|
||||||
|
MYNAME=aleesa-irc-go
|
||||||
|
BINARY=${MYNAME}
|
||||||
|
UNIX_BINARY=${MYNAME}
|
||||||
|
WINDOWS_BINARY=${MYNAME}.exe
|
||||||
|
RMCMD=rm -rf
|
||||||
|
|
||||||
|
# На windows имя бинарника может зависеть не только от платформы, но и от выбранной цели, для linux-а суффикс .exe
|
||||||
|
# не нужен
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
ifdef GOOS
|
||||||
|
ifeq ($(GOOS),windows)
|
||||||
|
BINARY=${WINDOWS_BINARY}
|
||||||
|
else # not ifeq ($(GOOS),windows)
|
||||||
|
BINARY=${MYNAME}
|
||||||
|
endif # ifeq ($(GOOS),windows)
|
||||||
|
else # not ifdef GOOS
|
||||||
|
BINARY=${WINDOWS_BINARY}
|
||||||
|
endif # ifdef GOOS
|
||||||
|
ifeq ($(SHELL), sh.exe)
|
||||||
|
RMCMD=DEL /Q /F
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Явно определяем символ новой строки, чтобы избежать неоднозначности на windows
|
||||||
|
define IFS
|
||||||
|
|
||||||
|
endef
|
||||||
|
|
||||||
|
|
||||||
all: clean build
|
all: clean build
|
||||||
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
${GOOPTS} go build ${BUILDOPTS} -o aleesa-irc-go types.go globals.go collection.go irc-user-mode.go \
|
ifeq ($(OS),Windows_NT)
|
||||||
settings-db-util.go util.go msg-parsers.go aleesa-irc-go-lib.go main.go
|
# Looks like on windows gnu make explicitly set SHELL to sh.exe, if it was not set.
|
||||||
|
ifeq ($(SHELL), sh.exe)
|
||||||
|
# # Vanilla cmd.exe / powershell.
|
||||||
|
SET "CGO_ENABLED=0"
|
||||||
|
go build ${BUILDOPTS} -o ${BINARY} ./cmd/${MYNAME}
|
||||||
|
else ifeq (,$(findstring(Git/usr/bin/sh.exe, $(SHELL))))
|
||||||
|
# # git-bash
|
||||||
|
CGO_ENABLED=0 go build ${BUILDOPTS} -o ${BINARY} ./cmd/${MYNAME}
|
||||||
|
else # not ifeq (,$(findstring(Git/usr/bin/sh.exe, $(SHELL))))
|
||||||
|
# # Some other shell.
|
||||||
|
# # TODO: handle it.
|
||||||
|
$(info "-- Dunno how to handle this shell: ${SHELL}")
|
||||||
|
endif # ifeq (,$(findstring(Git/usr/bin/sh.exe, $(SHELL))))
|
||||||
|
else # not ($(OS),Windows_NT)
|
||||||
|
CGO_ENABLED=0 go build ${BUILDOPTS} -o ${BINARY} ./cmd/${MYNAME}
|
||||||
|
endif # ifeq ($(OS),Windows_NT)
|
||||||
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
go clean
|
ifeq ($(OS),Windows_NT)
|
||||||
|
ifeq ($(SHELL),sh.exe)
|
||||||
|
# # Vanilla cmd.exe / powershell.
|
||||||
|
if exist ${WINDOWS_BINARY} ${RMCMD} ${WINDOWS_BINARY}
|
||||||
|
if exist ${UNIX_BINARY} ${RMCMD} ${UNIX_BINARY}
|
||||||
|
else # not ifeq ($(SHELL),sh.exe)
|
||||||
|
${RMCMD} ./${WINDOWS_BINARY}
|
||||||
|
${RMCMD} ./${UNIX_BINARY}
|
||||||
|
endif # ifeq ($(SHELL),sh.exe)
|
||||||
|
else # not ifeq ($(OS),Windows_NT)
|
||||||
|
${RMCMD} ./${BINARY}
|
||||||
|
endif
|
||||||
|
|
||||||
wipe:
|
|
||||||
go clean
|
|
||||||
rm -rf go.{mod,sum}
|
|
||||||
|
|
||||||
prep:
|
upgrade:
|
||||||
go mod init main
|
ifeq ($(OS),Windows_NT)
|
||||||
go mod tidy -compat=1.18
|
ifeq ($(SHELL),sh.exe)
|
||||||
|
# # Vanilla cmd.exe / powershell.
|
||||||
|
if exist vendor DEL /F /S /Q vendor >nul
|
||||||
|
else # not ifeq ($(SHELL),sh.exe)
|
||||||
|
# # git-bash
|
||||||
|
$(RM) -r vendor
|
||||||
|
endif # ifeq ($(SHELL),sh.exe)
|
||||||
|
else # not ifeq ($(OS),Windows_NT)
|
||||||
|
$(RM) -r vendor
|
||||||
|
endif
|
||||||
|
go get -u ./...
|
||||||
|
go mod tidy
|
||||||
|
go mod vendor
|
||||||
|
|
||||||
# vim: set ft=make noet ai ts=4 sw=4 sts=4:
|
# vim: set ft=make noet ai ts=4 sw=4 sts=4:
|
||||||
|
|||||||
82
README
82
README
@@ -1,82 +0,0 @@
|
|||||||
aleesa-irc-go
|
|
||||||
-----------------------------------------------------------------------------------------------------------------------
|
|
||||||
Сервис для aleesa-bot, работающий с протоколом IRC. С основными сервисами бота работает по протоколу Redis pubsub.
|
|
||||||
|
|
||||||
|
|
||||||
Механика работы
|
|
||||||
-----------------------------------------------------------------------------------------------------------------------
|
|
||||||
Сервис подписан на pub-sub redis-канал irc. В этот канал пишут остальные сервисы aleesa-bot-а в ответ на запросы этого
|
|
||||||
сервиса.
|
|
||||||
|
|
||||||
Формат протокола общения по redis pub-sub - см README от aleesa-misc-go.
|
|
||||||
|
|
||||||
|
|
||||||
Как это собрать?
|
|
||||||
-----------------------------------------------------------------------------------------------------------------------
|
|
||||||
Понадобится golang версии 1.18 или более новая.
|
|
||||||
|
|
||||||
Сам процесс сборки простой, достаточно запустить:
|
|
||||||
make
|
|
||||||
|
|
||||||
и через некоторое время мы получаем бинарник aleesa-irc-go. Это и есть наше приложение.
|
|
||||||
|
|
||||||
|
|
||||||
Как это запустить?
|
|
||||||
-----------------------------------------------------------------------------------------------------------------------
|
|
||||||
Приложение тестировалось на alpine linux x86_64 3.16, но должно работать на любом (более свежем) linux-е.
|
|
||||||
|
|
||||||
После создания data/config.json из data/config_sample.json, можно запускать приложение.
|
|
||||||
|
|
||||||
Предполагается, что конфиг написан в формате human json (https://hjson.github.io), который допускает коментарии и
|
|
||||||
некоторые другие вольности в форматировании json-а. Конфиг ищется последовательно по следующим путям по принципу "кто
|
|
||||||
первый того и тапки":
|
|
||||||
~/.aleesa-irc-go.json
|
|
||||||
~/aleesa-irc-go.json
|
|
||||||
/etc/aleesa-irc-go.json
|
|
||||||
data/config.json
|
|
||||||
|
|
||||||
В каталоге contrib находится скрипт для alpine linux, системы инициализации openrc. Этот скрипт достаточно положить в
|
|
||||||
/etc/init.d/aleesa-irc-go. После чего скопировать бинарник в каталог /var/lib/aleesa-irc-go, положить конфиг в одну
|
|
||||||
из дефолтных локаций (тестировалась /var/lib/aleesa-irc-go/data/config.json) и после этого сервис запускается
|
|
||||||
командой:
|
|
||||||
service aleesa-irc-go start
|
|
||||||
|
|
||||||
В автозагрузку его можно прописать через команду:
|
|
||||||
rc-update add aleesa-irc-go default
|
|
||||||
|
|
||||||
|
|
||||||
Nota Bene
|
|
||||||
-----------------------------------------------------------------------------------------------------------------------
|
|
||||||
По механике, в процессе работы мы форкаем горутинки на каждое входящее сообщение, особо не контроллируя этот процесс.
|
|
||||||
Предполагается, что на это приложение не будет большой нагрузки - ограничения irc не должны дать возможности развить
|
|
||||||
достаточно большой message rate, чтобы об это споткнуться.
|
|
||||||
|
|
||||||
Формально, более удачным было бы иметь автоматический пул воркеров-горутинок и раскидывать им через channel-ы
|
|
||||||
сообщения. Внутри каждой горутинки можно было бы повиcнуть в бесконечном цикле на канале входящих сообщений и так и
|
|
||||||
работать. Но на практике оно того не стОит.
|
|
||||||
|
|
||||||
|
|
||||||
Nota Bene #2
|
|
||||||
-----------------------------------------------------------------------------------------------------------------------
|
|
||||||
Go не поддерживает системный вызов fork() из-за чего демонизация программ на гошке средствами самой гошки - это в
|
|
||||||
некотором роде проблема. Решается она с помощью утилиты daemonize (https://github.com/bmc/daemonize).
|
|
||||||
|
|
||||||
Соответствено, если для запуска этой программы предполагается использовать возможности демонизации, то можно
|
|
||||||
воспользоваться этой программой.
|
|
||||||
|
|
||||||
В alpine linux есть средства openrc для супервайзинга foreground-сервисов.
|
|
||||||
|
|
||||||
|
|
||||||
Nota Bene #3
|
|
||||||
-----------------------------------------------------------------------------------------------------------------------
|
|
||||||
К сожалению, в боте не подразумевается функциональность invite-ов. Причина банальна - слишком жёсткие ограничения у
|
|
||||||
многих irc-сетей на количество сообщений в единицу времени, а по механике в большинстве случаев ограничение работает не
|
|
||||||
client-client или client-channel, а глобально, client-server (за исключением случаев, когда на канале у бота есть +v
|
|
||||||
или +o), что позволяет абузить клиента, то есть в данном случае, бота.
|
|
||||||
|
|
||||||
Сообщения об invite-ах пишутся в лог с уровнем info.
|
|
||||||
|
|
||||||
|
|
||||||
Special Thanks
|
|
||||||
-----------------------------------------------------------------------------------------------------------------------
|
|
||||||
https://github.com/akyoto/cache за "простой" кэш, из которого я сделал collection.go
|
|
||||||
83
README.md
Normal file
83
README.md
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# aleesa-irc-go
|
||||||
|
|
||||||
|
Сервис для aleesa-bot, работающий с протоколом IRC. С основными сервисами бота работает по протоколу Redis pubsub.
|
||||||
|
|
||||||
|
## Механика работы
|
||||||
|
|
||||||
|
Сервис подписан на pub-sub redis-канал irc. В этот канал пишут остальные сервисы aleesa-bot-а в ответ на запросы этого
|
||||||
|
сервиса.
|
||||||
|
|
||||||
|
Формат протокола общения по redis pub-sub - см. документацию в репозитории aleesa-doc.
|
||||||
|
|
||||||
|
## Как это собрать?
|
||||||
|
|
||||||
|
Понадобится компилятор go версии 1.25.1 или более новый.
|
||||||
|
|
||||||
|
Сам процесс сборки простой, достаточно запустить make или gmake, в зависимости от платформы:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
и через некоторое время мы получаем бинарник **aleesa-irc-go**. Это и есть наше приложение.
|
||||||
|
|
||||||
|
|
||||||
|
## Как это запустить?
|
||||||
|
|
||||||
|
Приложение тестировалось на alpine linux x86_64 3.16, но должно работать на любом (более свежем) linux-е, windows-е,
|
||||||
|
freebsd, macos.
|
||||||
|
|
||||||
|
После создания **data/config.json** из **data/config_sample.json**, можно запускать приложение.
|
||||||
|
|
||||||
|
Предполагается, что конфиг написан в формате [human json](https://hjson.github.io), который допускает коментарии и
|
||||||
|
некоторые другие вольности в форматировании json-а. Конфиг ищется последовательно по следующим путям по принципу "кто
|
||||||
|
первый того и тапки":
|
||||||
|
|
||||||
|
```
|
||||||
|
~/.aleesa-irc-go.json
|
||||||
|
~/aleesa-irc-go.json
|
||||||
|
/etc/aleesa-irc-go.json
|
||||||
|
data/config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
В каталоге contrib находится скрипт для alpine linux, системы инициализации openrc. Этот скрипт достаточно положить в
|
||||||
|
**/etc/init.d/aleesa-irc-go**. После чего скопировать бинарник в каталог **/var/lib/aleesa-irc-go**, положить конфиг в
|
||||||
|
одну из дефолтных локаций (тестировалась **/var/lib/aleesa-irc-go/data/config.json**) и после этого сервис запускается
|
||||||
|
командой:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
service aleesa-irc-go start
|
||||||
|
```
|
||||||
|
|
||||||
|
В автозагрузку его можно прописать через команду:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rc-update add aleesa-irc-go default
|
||||||
|
```
|
||||||
|
|
||||||
|
## Nota Bene
|
||||||
|
|
||||||
|
Go не поддерживает системный вызов fork() из-за чего демонизация программ на гошке средствами самой гошки - это в
|
||||||
|
некотором роде проблема. Решается она с помощью утилиты [daemonize](https://github.com/bmc/daemonize).
|
||||||
|
|
||||||
|
Соответствено, если для запуска этой программы предполагается использовать возможности демонизации, то можно
|
||||||
|
воспользоваться этой программой.
|
||||||
|
|
||||||
|
В alpine linux есть средства openrc для супервайзинга foreground-сервисов.
|
||||||
|
|
||||||
|
Технически, можно было бы решить эту проблему fork()-а с помощью "github.com/sevlyar/go-daemon", но на текущий момент
|
||||||
|
она не стоит остро, поэтому решения не требуется.
|
||||||
|
|
||||||
|
## Nota Bene 2
|
||||||
|
|
||||||
|
К сожалению, в боте не подразумевается функциональность invite-ов. Причина банальна - слишком жёсткие ограничения у
|
||||||
|
многих irc-сетей на количество сообщений в единицу времени, а по механике в большинстве случаев ограничение работает не
|
||||||
|
client-client или client-channel, а глобально, client-server (за исключением случаев, когда на канале у бота есть +v
|
||||||
|
или +o), что позволяет абузить клиента, то есть в данном случае, бота.
|
||||||
|
|
||||||
|
Сообщения об invite-ах пишутся в лог с уровнем info.
|
||||||
|
|
||||||
|
|
||||||
|
## Special Thanks
|
||||||
|
|
||||||
|
https://github.com/akyoto/cache за "простой" кэш, из которого я сделал anycollection и boolcollection.
|
||||||
89
cmd/aleesa-irc-go/main.go
Normal file
89
cmd/aleesa-irc-go/main.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"aleesa-irc-go/internal/irc"
|
||||||
|
"aleesa-irc-go/internal/log"
|
||||||
|
"aleesa-irc-go/internal/setting"
|
||||||
|
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Производит некоторую инициализацию перед запуском main().
|
||||||
|
func init() {
|
||||||
|
irc.ReadConfig()
|
||||||
|
|
||||||
|
// Проинициализируем мапку MODE-ами пользователей произвольным значением и удалим его
|
||||||
|
var usersModes = map[string]map[string]map[string]bool{
|
||||||
|
"user": {
|
||||||
|
"mode": {
|
||||||
|
"v": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
irc.UserMode.Set("channel", usersModes)
|
||||||
|
irc.UserMode.Delete("channel")
|
||||||
|
|
||||||
|
setting.DataDir = irc.Config.DataDir
|
||||||
|
}
|
||||||
|
|
||||||
|
// Собственно, какбэ "точка входа" - основная процедура в нашем боте.
|
||||||
|
func main() {
|
||||||
|
// Main context
|
||||||
|
var (
|
||||||
|
ctx = context.Background()
|
||||||
|
logfile *os.File
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
// Откроем лог и скормим его логгеру
|
||||||
|
if irc.Config.Log != "" {
|
||||||
|
logfile, err = os.OpenFile(irc.Config.Log, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Unable to open log file %s: %s", irc.Config.Log, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logfile = os.Stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Init(irc.Config.Loglevel, logfile)
|
||||||
|
|
||||||
|
// Иницализируем redis-клиента
|
||||||
|
irc.RedisClient = redis.NewClient(&redis.Options{
|
||||||
|
Addr: fmt.Sprintf("%s:%d", irc.Config.Redis.Server, irc.Config.Redis.Port),
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Debugf("Lazy connect() to redis at %s:%d", irc.Config.Redis.Server, irc.Config.Redis.Port)
|
||||||
|
irc.Subscriber = irc.RedisClient.Subscribe(ctx, irc.Config.Redis.MyChannel)
|
||||||
|
redisMsgChan := irc.Subscriber.Channel()
|
||||||
|
|
||||||
|
go irc.IrcClientRun()
|
||||||
|
go irc.IrcSend()
|
||||||
|
go irc.IrcSendUnrestricted()
|
||||||
|
|
||||||
|
// Самое время поставить траппер сигналов
|
||||||
|
signal.Notify(irc.SigChan,
|
||||||
|
syscall.SIGINT,
|
||||||
|
syscall.SIGTERM,
|
||||||
|
syscall.SIGQUIT)
|
||||||
|
|
||||||
|
go irc.SigHandler()
|
||||||
|
|
||||||
|
// Обработчик событий от редиски
|
||||||
|
for msg := range redisMsgChan {
|
||||||
|
if irc.Shutdown {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
irc.RedisMsgParser(msg.Payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
45
go.mod
45
go.mod
@@ -1,32 +1,41 @@
|
|||||||
module aleesa-irc-go
|
module aleesa-irc-go
|
||||||
|
|
||||||
go 1.18
|
go 1.25.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/cockroachdb/errors v1.8.1
|
github.com/cockroachdb/errors v1.12.0
|
||||||
github.com/cockroachdb/pebble v0.0.0-20220729140737-245afda9c39f
|
github.com/cockroachdb/pebble v1.1.5
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
github.com/go-redis/redis/v8 v8.11.5
|
||||||
github.com/hjson/hjson-go v3.1.0+incompatible
|
github.com/hjson/hjson-go v3.3.0+incompatible
|
||||||
github.com/sirupsen/logrus v1.8.1
|
|
||||||
github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64
|
github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/DataDog/zstd v1.4.5 // indirect
|
github.com/DataDog/zstd v1.4.5 // indirect
|
||||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f // indirect
|
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
|
||||||
github.com/cockroachdb/redact v1.0.8 // indirect
|
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
||||||
github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 // indirect
|
github.com/cockroachdb/redact v1.1.5 // indirect
|
||||||
|
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/gogo/protobuf v1.3.1 // indirect
|
github.com/getsentry/sentry-go v0.27.0 // indirect
|
||||||
github.com/golang/snappy v0.0.3 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/klauspost/compress v1.11.7 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/kr/pretty v0.1.0 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/klauspost/compress v1.16.0 // indirect
|
||||||
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
golang.org/x/exp v0.0.0-20200513190911-00229845015e // indirect
|
github.com/prometheus/client_golang v1.15.0 // indirect
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
|
github.com/prometheus/client_model v0.3.0 // indirect
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
github.com/prometheus/common v0.42.0 // indirect
|
||||||
golang.org/x/text v0.3.6 // indirect
|
github.com/prometheus/procfs v0.9.0 // indirect
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
|
||||||
|
golang.org/x/net v0.38.0 // indirect
|
||||||
|
golang.org/x/sys v0.31.0 // indirect
|
||||||
|
golang.org/x/text v0.23.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
389
go.sum
389
go.sum
@@ -1,348 +1,137 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
|
||||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
|
||||||
github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw=
|
|
||||||
github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
|
|
||||||
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
|
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
|
||||||
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
|
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4=
|
||||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
|
||||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo=
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
github.com/cockroachdb/errors v1.12.0/go.mod h1:SvzfYNNBshAVbZ8wzNc/UPK3w1vf0dKDUP41ucAIf7g=
|
||||||
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
|
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
|
||||||
github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=
|
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
|
||||||
github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM=
|
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
|
||||||
github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y=
|
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
|
||||||
github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac=
|
|
||||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY=
|
|
||||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
|
||||||
github.com/cockroachdb/pebble v0.0.0-20220729140737-245afda9c39f h1:ZGX53mhE1cb3CoICFir642DOR6JzSSkft0M9L806Wgo=
|
|
||||||
github.com/cockroachdb/pebble v0.0.0-20220729140737-245afda9c39f/go.mod h1:890yq1fUb9b6dGNwssgeUO5vQV9qfXnCPxAJhBQfXw0=
|
|
||||||
github.com/cockroachdb/redact v1.0.8 h1:8QG/764wK+vmEYoOlfobpe12EQcS81ukx/a4hdVMxNw=
|
|
||||||
github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
|
|
||||||
github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeSfSaiCbEBZGKODaixqtHM=
|
|
||||||
github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ=
|
|
||||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
|
|
||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
|
||||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
|
||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
|
||||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
|
||||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
|
||||||
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
|
|
||||||
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
|
|
||||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
|
||||||
github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
|
|
||||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
|
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
|
||||||
github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
|
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||||
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
|
|
||||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
|
||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
|
||||||
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
|
|
||||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
|
||||||
github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
|
||||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
|
||||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
|
||||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
|
||||||
github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=
|
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
github.com/hjson/hjson-go v3.3.0+incompatible h1:Rqr+Ya+0aCJMjaE4s8E9YKvuJLuLVpEvz4ONum52vnI=
|
||||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/hjson/hjson-go v3.3.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio=
|
||||||
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
|
||||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
|
||||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
|
||||||
github.com/hjson/hjson-go v3.1.0+incompatible h1:DY/9yE8ey8Zv22bY+mHV1uk2yRy0h8tKhZ77hEdi0Aw=
|
|
||||||
github.com/hjson/hjson-go v3.1.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio=
|
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
|
||||||
github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
|
|
||||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
|
||||||
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
|
|
||||||
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
|
|
||||||
github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI=
|
|
||||||
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
|
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
|
||||||
github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
|
|
||||||
github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
|
||||||
github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
|
|
||||||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
|
||||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
|
|
||||||
github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk=
|
|
||||||
github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U=
|
|
||||||
github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
|
|
||||||
github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
|
|
||||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
|
||||||
github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
|
||||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
|
||||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
|
||||||
github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
|
|
||||||
github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
|
|
||||||
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
|
||||||
github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
|
|
||||||
github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
|
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
|
||||||
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
|
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
|
||||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
|
||||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
|
||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64 h1:l/T7dYuJEQZOwVOpjIXr1180aM9PZL/d1MnMVIxefX4=
|
github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64 h1:l/T7dYuJEQZOwVOpjIXr1180aM9PZL/d1MnMVIxefX4=
|
||||||
github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64/go.mod h1:Q1NAJOuRdQCqN/VIWdnaaEhV8LpeO2rtlBP7/iDJNII=
|
github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64/go.mod h1:Q1NAJOuRdQCqN/VIWdnaaEhV8LpeO2rtlBP7/iDJNII=
|
||||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
|
||||||
github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
|
|
||||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
|
||||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
|
||||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
|
||||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
|
||||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
|
||||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
|
||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
|
||||||
golang.org/x/exp v0.0.0-20200513190911-00229845015e h1:rMqLP+9XLy+LdbCXHjJHAmTfXCr93W7oruWA6Hq1Alc=
|
|
||||||
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
|
||||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
|
||||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210909193231-528a39cd75f3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
|
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
|
||||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
|
||||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
|
||||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
package main
|
package anycollection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Collection это sync.Map-ка, хранящая в себе произвольные итемы
|
// Collection это sync.Map-ка, хранящая в себе произвольные итемы.
|
||||||
type Collection struct {
|
type Collection struct {
|
||||||
items sync.Map
|
items sync.Map
|
||||||
close chan struct{}
|
close chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Item представляет собой интерфейс для хранения произвольных данных
|
// Item представляет собой интерфейс для хранения произвольных данных.
|
||||||
type item struct {
|
type item struct {
|
||||||
data interface{}
|
data any
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCollection создаёт инстанс коллекции
|
// NewCollection создаёт инстанс коллекции.
|
||||||
func NewCollection() *Collection {
|
func NewCollection() *Collection {
|
||||||
c := &Collection{
|
c := &Collection{
|
||||||
close: make(chan struct{}),
|
close: make(chan struct{}),
|
||||||
@@ -24,8 +24,8 @@ func NewCollection() *Collection {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get достаёт данные по заданному ключу из коллекции
|
// Get достаёт данные по заданному ключу из коллекции.
|
||||||
func (collection *Collection) Get(key interface{}) (interface{}, bool) {
|
func (collection *Collection) Get(key any) (any, bool) {
|
||||||
obj, exists := collection.items.Load(key)
|
obj, exists := collection.items.Load(key)
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
@@ -37,16 +37,16 @@ func (collection *Collection) Get(key interface{}) (interface{}, bool) {
|
|||||||
return item.data, true
|
return item.data, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set сохраняет данные с заданным ключом в коллекцию
|
// Set сохраняет данные с заданным ключом в коллекцию.
|
||||||
func (collection *Collection) Set(key interface{}, value interface{}) {
|
func (collection *Collection) Set(key any, value any) {
|
||||||
collection.items.Store(key, item{
|
collection.items.Store(key, item{
|
||||||
data: value,
|
data: value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Range применяет функцию f ко всем ключам в коллекции
|
// Range применяет функцию f ко всем ключам в коллекции.
|
||||||
func (collection *Collection) Range(f func(key, value interface{}) bool) {
|
func (collection *Collection) Range(f func(key, value any) bool) {
|
||||||
fn := func(key, value interface{}) bool {
|
fn := func(key, value any) bool {
|
||||||
item := value.(item)
|
item := value.(item)
|
||||||
|
|
||||||
return f(key, item.data)
|
return f(key, item.data)
|
||||||
@@ -55,12 +55,12 @@ func (collection *Collection) Range(f func(key, value interface{}) bool) {
|
|||||||
collection.items.Range(fn)
|
collection.items.Range(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete удаляет ключ и значение из коллекции данных
|
// Delete удаляет ключ и значение из коллекции данных.
|
||||||
func (collection *Collection) Delete(key interface{}) {
|
func (collection *Collection) Delete(key any) {
|
||||||
collection.items.Delete(key)
|
collection.items.Delete(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close очищает и высвобождает ресурсы, занятые коллекцией
|
// Close очищает и высвобождает ресурсы, занятые коллекцией.
|
||||||
func (collection *Collection) Close() {
|
func (collection *Collection) Close() {
|
||||||
collection.close <- struct{}{}
|
collection.close <- struct{}{}
|
||||||
collection.items = sync.Map{}
|
collection.items = sync.Map{}
|
||||||
58
internal/boolcollection/boolcollection.go
Normal file
58
internal/boolcollection/boolcollection.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package boolcollection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Collection это sync.Map-ка, хранящая в себе итемы типа bool.
|
||||||
|
type Collection struct {
|
||||||
|
items sync.Map
|
||||||
|
close chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Item представляет собой интерфейс для хранения данных типа bool.
|
||||||
|
type item struct {
|
||||||
|
data bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCollection создаёт инстанс коллекции.
|
||||||
|
func NewCollection() *Collection {
|
||||||
|
c := &Collection{
|
||||||
|
close: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get достаёт данные по заданному ключу из коллекции.
|
||||||
|
func (collection *Collection) Get(key string) (bool, bool) {
|
||||||
|
obj, exists := collection.items.Load(key)
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return false, false
|
||||||
|
}
|
||||||
|
|
||||||
|
item := obj.(item)
|
||||||
|
|
||||||
|
return item.data, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set сохраняет данные с заданным ключом в коллекцию.
|
||||||
|
func (collection *Collection) Set(key string, value bool) {
|
||||||
|
collection.items.Store(key, item{
|
||||||
|
data: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete удаляет ключ и значение из коллекции данных.
|
||||||
|
func (collection *Collection) Delete(key string) {
|
||||||
|
collection.items.Delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close очищает и высвобождает ресурсы, занятые коллекцией.
|
||||||
|
func (collection *Collection) Close() {
|
||||||
|
collection.close <- struct{}{}
|
||||||
|
collection.items = sync.Map{}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
@@ -1,38 +1,40 @@
|
|||||||
package main
|
package irc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
"aleesa-irc-go/internal/boolcollection"
|
||||||
|
"aleesa-irc-go/internal/log"
|
||||||
|
|
||||||
irc "github.com/thoj/go-ircevent"
|
irc "github.com/thoj/go-ircevent"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ircClientInit горутинка для работы с протоколом irc
|
// ircClientInit горутинка для работы с протоколом irc.
|
||||||
func ircClientRun() {
|
func IrcClientRun() {
|
||||||
for {
|
for !Shutdown {
|
||||||
if shutdown {
|
// Иницализируем irc-клиента.
|
||||||
// Если мы завершаем работу программы, то нам ничего обрабатывать не надо
|
// TODO: make use of capabilities: https://defs.ircdocs.horse/defs/clientcaps .
|
||||||
break
|
// TODO: make use of tags caps https://defs.ircdocs.horse/defs/tags .
|
||||||
}
|
serverString := fmt.Sprintf("%s:%d", Config.Irc.Server, Config.Irc.Port)
|
||||||
|
|
||||||
// Иницализируем irc-клиента
|
|
||||||
serverString := fmt.Sprintf("%s:%d", config.Irc.Server, config.Irc.Port)
|
|
||||||
log.Debugf("Preparing to connect to %s", serverString)
|
log.Debugf("Preparing to connect to %s", serverString)
|
||||||
log.Debugf("Using nick %s and username %s", config.Irc.Nick, config.Irc.User)
|
log.Debugf("Using nick %s and username %s", Config.Irc.Nick, Config.Irc.User)
|
||||||
ircClient = irc.IRC(config.Irc.Nick, config.Irc.User)
|
ircClient = irc.IRC(Config.Irc.Nick, Config.Irc.User)
|
||||||
ircClient.RealName = config.Irc.User
|
ircClient.RealName = Config.Irc.User
|
||||||
ircClient.Version = "Aleesa Bot v4.something"
|
ircClient.Version = "Aleesa Bot v4.something"
|
||||||
|
|
||||||
if config.Irc.Ssl {
|
if Config.Irc.Ssl {
|
||||||
log.Debug("Force use ssl for connection")
|
log.Debug("Force use ssl for connection")
|
||||||
|
|
||||||
ircClient.UseTLS = true
|
ircClient.UseTLS = true
|
||||||
|
|
||||||
if !config.Irc.SslVerify {
|
if !Config.Irc.SslVerify {
|
||||||
log.Debug("Skip server certificate validation")
|
log.Debug("Skip server certificate validation")
|
||||||
|
|
||||||
ircClient.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
ircClient.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
} else {
|
} else {
|
||||||
log.Debug("Force server certificate validation")
|
log.Debug("Force server certificate validation")
|
||||||
@@ -41,58 +43,97 @@ func ircClientRun() {
|
|||||||
log.Debug("Skip ssl for connection")
|
log.Debug("Skip ssl for connection")
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Irc.Sasl && config.Irc.Password != "" {
|
if Config.Irc.Sasl && Config.Irc.Password != "" {
|
||||||
ircClient.SASLLogin = config.Irc.User
|
ircClient.SASLLogin = Config.Irc.User
|
||||||
ircClient.SASLPassword = config.Irc.Password
|
ircClient.SASLPassword = Config.Irc.Password
|
||||||
ircClient.UseSASL = true
|
ircClient.UseSASL = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Навесим коллбэков на некоторые ответы сервера на наши запросы
|
// Навесим коллбэков на некоторые ответы сервера на наши запросы.
|
||||||
|
|
||||||
|
// 001 RPL_WELCOME уже есть в github.com/thoj/go-ircevent/irc_callback.go.
|
||||||
|
|
||||||
|
// Сделаем уже что-то полезное! Motd нам уже прислали и теперь можно авторизоваться и джойниться
|
||||||
|
ircClient.AddCallback("004", func(e *irc.Event) {
|
||||||
|
// Из это строки мы можем узнать, какие флаги для user MODE и channem MODE можно навешивать.
|
||||||
|
// Как минимум, эта строка нам нужна, чтобы выяснить, можем ли мы взять себе +B, мы же бот :).
|
||||||
|
e.Connection.Lock()
|
||||||
|
|
||||||
|
// Формат строки с MODE-ами https://datatracker.ietf.org/doc/html/rfc2812#section-5.1 .
|
||||||
|
availableUserModes = boolcollection.NewCollection()
|
||||||
|
availableChanModes = boolcollection.NewCollection()
|
||||||
|
|
||||||
|
log.Debugf("Add %s to list of available user modes", e.Arguments[3])
|
||||||
|
|
||||||
|
for _, mode := range strings.Split(e.Arguments[3], "") {
|
||||||
|
availableUserModes.Set(mode, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
availableUserModes.Set("announced", true)
|
||||||
|
|
||||||
|
log.Debugf("Add %s to list available channel modes", e.Arguments[4])
|
||||||
|
|
||||||
|
for _, mode := range strings.Split(e.Arguments[4], "") {
|
||||||
|
availableChanModes.Set(mode, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
availableChanModes.Set("announced", true)
|
||||||
|
|
||||||
|
e.Connection.Unlock()
|
||||||
|
})
|
||||||
|
|
||||||
|
ircClient.AddCallback("005", func(e *irc.Event) {
|
||||||
|
// TODO: Make use of it. https://defs.ircdocs.horse/defs/isupport
|
||||||
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("319", func(e *irc.Event) {
|
ircClient.AddCallback("319", func(e *irc.Event) {
|
||||||
/* Это одна из строк с данными, прилетающая в ответ на запрос whois на определённого юзера
|
/* Это одна из строк с данными, прилетающая в ответ на запрос whois на определённого юзера
|
||||||
* Из этой строки нас интересует, на каких каналах пользователь op (то есть с префиксом @) или имеет voice
|
* Из этой строки нас интересует, на каких каналах пользователь op (то есть с префиксом @) или имеет voice
|
||||||
* (то есть с префиксом +) чтобы внести его в свою базу mode-ов.
|
* (то есть с префиксом +) чтобы внести его в свою базу mode-ов.
|
||||||
*/
|
*/
|
||||||
log.Debugf("319 RPL_WHOISCHANNELS, %s", e.Raw)
|
|
||||||
channelsWithModes := strings.Split(e.Arguments[2], " ")
|
channelsWithModes := strings.Split(e.Arguments[2], " ")
|
||||||
dstNick := e.Arguments[1]
|
dstNick := e.Arguments[1]
|
||||||
|
|
||||||
for _, channelWithMode := range channelsWithModes {
|
for _, channelWithMode := range channelsWithModes {
|
||||||
for _, mychannel := range config.Irc.Channels {
|
for _, mychannel := range Config.Irc.Channels {
|
||||||
tmp := regexp.MustCompile("#").Split(channelWithMode, 2)
|
tmp := regexp.MustCompile("#").Split(channelWithMode, 2)
|
||||||
channel := tmp[1]
|
channel := tmp[1]
|
||||||
modes := tmp[0]
|
modes := tmp[0]
|
||||||
|
|
||||||
if channel == mychannel {
|
if channel == mychannel {
|
||||||
// Для каждого канала формат строго 1 из 4-х: #channel | @#channel | +#channel | @+#channel
|
// Для каждого канала формат строго 1 из 4-х: #channel | @#channel | +#channel | @+#channel
|
||||||
switch {
|
switch modes {
|
||||||
case modes == "@+":
|
case "@+":
|
||||||
userModeUpdateUser(channel, dstNick, "+o")
|
userModeUpdateUser(channel, dstNick, "+o")
|
||||||
userModeUpdateUser(channel, dstNick, "+v")
|
userModeUpdateUser(channel, dstNick, "+v")
|
||||||
case modes == "@":
|
case "@":
|
||||||
userModeUpdateUser(channel, dstNick, "+o")
|
userModeUpdateUser(channel, dstNick, "+o")
|
||||||
case modes == "+":
|
case "+":
|
||||||
userModeUpdateUser(channel, dstNick, "+v")
|
userModeUpdateUser(channel, dstNick, "+v")
|
||||||
default:
|
default:
|
||||||
userModeUpdateUser(channel, dstNick, "-o")
|
userModeUpdateUser(channel, dstNick, "-o")
|
||||||
userModeUpdateUser(channel, dstNick, "-v")
|
userModeUpdateUser(channel, dstNick, "-v")
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 433 ERR_NICKNAMEINUSE уже есть в github.com/thoj/go-ircevent/irc_callback.go.
|
||||||
|
|
||||||
ircClient.AddCallback("353", func(e *irc.Event) {
|
ircClient.AddCallback("353", func(e *irc.Event) {
|
||||||
// Это одна из строк данных, прилетающая в ответ на запрос names, на канале
|
// Это одна из строк данных, прилетающая в ответ на запрос names, на канале
|
||||||
log.Debugf("353 RPL_NAMREPLY, %s", e.Raw)
|
e.Connection.Lock()
|
||||||
namesString := e.Arguments[3]
|
namesString := e.Arguments[3]
|
||||||
channel := e.Arguments[2]
|
channel := e.Arguments[2]
|
||||||
|
|
||||||
for _, mychannel := range config.Irc.Channels {
|
for _, mychannel := range Config.Irc.Channels {
|
||||||
if channel == mychannel {
|
if channel == mychannel {
|
||||||
for _, name := range strings.Split(namesString, " ") {
|
for _, name := range strings.Split(namesString, " ") {
|
||||||
mode := name[:1]
|
mode := name[:1]
|
||||||
|
|
||||||
var nick string
|
var nick string
|
||||||
|
|
||||||
if mode == "@" || mode == "+" {
|
if mode == "@" || mode == "+" {
|
||||||
@@ -101,25 +142,58 @@ func ircClientRun() {
|
|||||||
nick = name
|
nick = name
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch mode {
|
||||||
case mode == "@":
|
case "@":
|
||||||
userModeUpdateUser(channel, nick, "+o")
|
userModeUpdateUser(channel, nick, "+o")
|
||||||
case mode == "+":
|
case "+":
|
||||||
userModeUpdateUser(channel, nick, "+v")
|
userModeUpdateUser(channel, nick, "+v")
|
||||||
default:
|
default:
|
||||||
userModeUpdateUser(channel, nick, "-o")
|
userModeUpdateUser(channel, nick, "-o")
|
||||||
userModeUpdateUser(channel, nick, "-v")
|
userModeUpdateUser(channel, nick, "-v")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
e.Connection.Unlock()
|
||||||
})
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("352", func(e *irc.Event) {
|
// Если сервер не может прочитать MOTD, то он может вернуть 422 ERR_NOMOTD, тоде самое навесим и туда тоже.
|
||||||
// Относительно бесполезная для нас команда. Не возвращает канала (в случае с ngircd), так что узнать mode
|
ircClient.AddCallback("376", func(e *irc.Event) {
|
||||||
// юзера на конкретном канале из этого невозможно. А остальные данные пока непонятно куда применять.
|
// TODO: make use of modes https://defs.ircdocs.horse/defs/usermodes
|
||||||
log.Warnf("352 RPL_WHOREPLY, %s", e.Raw)
|
// Если у нас есть доступный +B возьмём его себе, мы же бот.
|
||||||
|
announced, _ := availableUserModes.Get("announced")
|
||||||
|
botFlag, _ := availableUserModes.Get("B")
|
||||||
|
|
||||||
|
if announced {
|
||||||
|
if botFlag {
|
||||||
|
log.Info("Grabbing +B flag to mark me as bot")
|
||||||
|
ircClient.Mode(ircClient.GetNick(), "+B")
|
||||||
|
// TODO: Wait for it.
|
||||||
|
} else {
|
||||||
|
log.Info("Skip +B flag, server does not support it, noone will know that I am bot")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Этого по идее не должно быть.
|
||||||
|
log.Info("Skip +B flag, server did not announce modes (yet?), noone will know that I am bot")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Config.Irc.Sasl && Config.Irc.Password != "" && !nickIsUsed {
|
||||||
|
log.Info("Identifying via NickServ")
|
||||||
|
|
||||||
|
message := fmt.Sprintf("identify %s %s", Config.Irc.Nick, Config.Irc.Password)
|
||||||
|
imChan <- iMsg{ChatID: "NickServ", Text: message}
|
||||||
|
}
|
||||||
|
// TODO: wait until +R flag been set? could be implemented with waiting for channel message.
|
||||||
|
|
||||||
|
log.Debug("Trying to join to preconfigured channels")
|
||||||
|
|
||||||
|
for _, channel := range Config.Irc.Channels {
|
||||||
|
log.Infof("Joining to %s channel", channel)
|
||||||
|
ircClient.Join(channel)
|
||||||
|
}
|
||||||
|
// TODO: wait for join or join error.
|
||||||
})
|
})
|
||||||
|
|
||||||
// Навесим коллбэков на все возможные и невозможные error status code, которые мы можем получить и сдампим это
|
// Навесим коллбэков на все возможные и невозможные error status code, которые мы можем получить и сдампим это
|
||||||
@@ -157,6 +231,39 @@ func ircClientRun() {
|
|||||||
log.Errorf("421 ERR_UNKNOWNCOMMAND, %s", e.Raw)
|
log.Errorf("421 ERR_UNKNOWNCOMMAND, %s", e.Raw)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Аналогичный коллбэк висит на 376 RPL_ENDOFMOTD.
|
||||||
|
ircClient.AddCallback("422", func(e *irc.Event) {
|
||||||
|
// TODO: make use of modes https://defs.ircdocs.horse/defs/usermodes
|
||||||
|
// Если у нас есть доступный +B возьмём его себе, мы же бот.
|
||||||
|
botFlag, ok := availableUserModes.Get("B")
|
||||||
|
|
||||||
|
if ok && botFlag {
|
||||||
|
log.Info("Grabbing +B flag to mark me as bot")
|
||||||
|
ircClient.Mode(ircClient.GetNick(), "+B")
|
||||||
|
} else {
|
||||||
|
if ok {
|
||||||
|
log.Info("Skip +B flag, server does not support it, noone will know that I am bot")
|
||||||
|
} else {
|
||||||
|
log.Info("Skip +B flag, server did not announce modes (yet?), noone will know that I am bot")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Config.Irc.Sasl && Config.Irc.Password != "" && !nickIsUsed {
|
||||||
|
log.Info("Identifying via NickServ")
|
||||||
|
|
||||||
|
message := fmt.Sprintf("identify %s %s", Config.Irc.Nick, Config.Irc.Password)
|
||||||
|
imChan <- iMsg{ChatID: "NickServ", Text: message}
|
||||||
|
}
|
||||||
|
// TODO: wait until +R flag been set? could be implemented with waiting for channel message.
|
||||||
|
|
||||||
|
log.Debug("Trying to join to preconfigured channels")
|
||||||
|
|
||||||
|
for _, channel := range Config.Irc.Channels {
|
||||||
|
log.Infof("Joining to %s channel", channel)
|
||||||
|
ircClient.Join(channel)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("431", func(e *irc.Event) {
|
ircClient.AddCallback("431", func(e *irc.Event) {
|
||||||
log.Errorf("431 ERR_NONICKNAMEGIVEN, %s", e.Raw)
|
log.Errorf("431 ERR_NONICKNAMEGIVEN, %s", e.Raw)
|
||||||
})
|
})
|
||||||
@@ -167,9 +274,12 @@ func ircClientRun() {
|
|||||||
|
|
||||||
ircClient.AddCallback("433", func(e *irc.Event) {
|
ircClient.AddCallback("433", func(e *irc.Event) {
|
||||||
log.Errorf("433 ERR_NICKNAMEINUSE, %s", e.Raw)
|
log.Errorf("433 ERR_NICKNAMEINUSE, %s", e.Raw)
|
||||||
|
|
||||||
nickIsUsed = true
|
nickIsUsed = true
|
||||||
time.Sleep(60 * time.Second)
|
|
||||||
ircClient.Nick(config.Irc.Nick)
|
<-time.NewTimer(60 * time.Second).C // 1 minute sleep.
|
||||||
|
|
||||||
|
ircClient.Nick(Config.Irc.Nick)
|
||||||
})
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("436", func(e *irc.Event) {
|
ircClient.AddCallback("436", func(e *irc.Event) {
|
||||||
@@ -179,6 +289,7 @@ func ircClientRun() {
|
|||||||
log.Errorf("436 ERR_NICKCOLLISION, %s", e.Raw)
|
log.Errorf("436 ERR_NICKCOLLISION, %s", e.Raw)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 437: ERR_UNAVAILRESOURCE уже есть в github.com/thoj/go-ircevent/irc_callback.go.
|
||||||
ircClient.AddCallback("437", func(e *irc.Event) {
|
ircClient.AddCallback("437", func(e *irc.Event) {
|
||||||
log.Errorf("437 ERR_UNAVAILRESOURCE, %s", e.Raw)
|
log.Errorf("437 ERR_UNAVAILRESOURCE, %s", e.Raw)
|
||||||
})
|
})
|
||||||
@@ -191,6 +302,16 @@ func ircClientRun() {
|
|||||||
log.Warnf("442 ERR_NOTONCHANNEL, %s", e.Raw)
|
log.Warnf("442 ERR_NOTONCHANNEL, %s", e.Raw)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ircClient.AddCallback("443", func(e *irc.Event) {
|
||||||
|
// Returned when a client tries to invite a user to a channel they're already on.
|
||||||
|
log.Errorf("443 ERR_USERONCHANNEL, %s", e.Raw)
|
||||||
|
})
|
||||||
|
|
||||||
|
ircClient.AddCallback("446", func(e *irc.Event) {
|
||||||
|
// Returned by USERS when it has been disabled or not implemented.
|
||||||
|
log.Errorf("446 ERR_USERSDISABLED, %s", e.Raw)
|
||||||
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("451", func(e *irc.Event) {
|
ircClient.AddCallback("451", func(e *irc.Event) {
|
||||||
// Предполагается, что надо авторизоваться, перед тем как что-то делать на сервере
|
// Предполагается, что надо авторизоваться, перед тем как что-то делать на сервере
|
||||||
log.Errorf("451 ERR_NOTREGISTERED, %s", e.Raw)
|
log.Errorf("451 ERR_NOTREGISTERED, %s", e.Raw)
|
||||||
@@ -213,18 +334,19 @@ func ircClientRun() {
|
|||||||
log.Errorf("465 ERR_YOUREBANNEDCREEP, %s", e.Raw)
|
log.Errorf("465 ERR_YOUREBANNEDCREEP, %s", e.Raw)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ircClient.AddCallback("467", func(e *irc.Event) {
|
||||||
|
log.Errorf("467 ERR_KEYSET, %s", e.Raw)
|
||||||
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("471", func(e *irc.Event) {
|
ircClient.AddCallback("471", func(e *irc.Event) {
|
||||||
// Это значит, что народу на канале максимальное количество. Будем пробовать присунуться раз в 30 сек.
|
// Это значит, что народу на канале максимальное количество. Будем пробовать присунуться раз в 30 сек.
|
||||||
channel := e.Arguments[1]
|
channel := e.Arguments[1]
|
||||||
log.Warnf("471 ERR_CHANNELISFULL, %s", e.Raw)
|
log.Warnf("471 ERR_CHANNELISFULL, %s", e.Raw)
|
||||||
time.Sleep(30 * time.Second)
|
<-time.NewTimer(30 * time.Second).C
|
||||||
|
|
||||||
// Проверяем, а должны ли мы быть заджоенныеми к указанному, каналу, а то вдруг нет?
|
// Проверяем, а должны ли мы быть заджоенныеми к указанному, каналу, а то вдруг нет?
|
||||||
for _, ircChan := range config.Irc.Channels {
|
if slices.Contains(Config.Irc.Channels, channel) {
|
||||||
if ircChan == channel {
|
ircClient.Join(channel)
|
||||||
ircClient.Join(channel)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -237,14 +359,11 @@ func ircClientRun() {
|
|||||||
// По завершении работ +i снимают.
|
// По завершении работ +i снимают.
|
||||||
channel := e.Arguments[1]
|
channel := e.Arguments[1]
|
||||||
log.Errorf("473 ERR_INVITEONLYCHAN, %s", e.Raw)
|
log.Errorf("473 ERR_INVITEONLYCHAN, %s", e.Raw)
|
||||||
time.Sleep(30 * time.Second)
|
<-time.NewTimer(30 * time.Second).C
|
||||||
|
|
||||||
// Проверяем, а должны ли мы быть заджоенныеми к указанному, каналу, а то вдруг нет?
|
// Проверяем, а должны ли мы быть заджоенными к указанному, каналу, а то вдруг нет?
|
||||||
for _, ircChan := range config.Irc.Channels {
|
if slices.Contains(Config.Irc.Channels, channel) {
|
||||||
if ircChan == channel {
|
ircClient.Join(channel)
|
||||||
ircClient.Join(channel)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -254,18 +373,21 @@ func ircClientRun() {
|
|||||||
|
|
||||||
log.Errorf("474 ERR_BANNEDFROMCHAN, %s", e.Raw)
|
log.Errorf("474 ERR_BANNEDFROMCHAN, %s", e.Raw)
|
||||||
// Если нас забанили, то информацию о MODE-ах пользователей мы теряем
|
// Если нас забанили, то информацию о MODE-ах пользователей мы теряем
|
||||||
time.Sleep(30 * time.Second)
|
<-time.NewTimer(30 * time.Second).C
|
||||||
|
|
||||||
// Вдруг, нас забанили, но какбэ не навсегда?
|
// Вдруг, нас забанили, но какбэ не навсегда?
|
||||||
|
// TODO: вынести в настройки?
|
||||||
|
|
||||||
// Проверяем, а должны ли мы быть заджоенныеми к указанному, каналу, а то вдруг нет?
|
// Проверяем, а должны ли мы быть заджоенныеми к указанному, каналу, а то вдруг нет?
|
||||||
for _, ircChan := range config.Irc.Channels {
|
if slices.Contains(Config.Irc.Channels, channel) {
|
||||||
if ircChan == channel {
|
ircClient.Join(channel)
|
||||||
ircClient.Join(channel)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ircClient.AddCallback("475", func(e *irc.Event) {
|
||||||
|
log.Errorf("475 ERR_BADCHANNELKEY, %s", e.Raw)
|
||||||
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("477", func(e *irc.Event) {
|
ircClient.AddCallback("477", func(e *irc.Event) {
|
||||||
log.Errorf("477 ERR_NOCHANMODES, %s", e.Raw)
|
log.Errorf("477 ERR_NOCHANMODES, %s", e.Raw)
|
||||||
})
|
})
|
||||||
@@ -274,6 +396,10 @@ func ircClientRun() {
|
|||||||
log.Errorf("478 ERR_BANLISTFULL, %s", e.Raw)
|
log.Errorf("478 ERR_BANLISTFULL, %s", e.Raw)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ircClient.AddCallback("481", func(e *irc.Event) {
|
||||||
|
log.Errorf("481 ERR_NOPRIVILEGES, %s", e.Raw)
|
||||||
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("482", func(e *irc.Event) {
|
ircClient.AddCallback("482", func(e *irc.Event) {
|
||||||
log.Errorf("482 ERR_CHANOPRIVSNEEDED, %s", e.Raw)
|
log.Errorf("482 ERR_CHANOPRIVSNEEDED, %s", e.Raw)
|
||||||
})
|
})
|
||||||
@@ -286,6 +412,10 @@ func ircClientRun() {
|
|||||||
log.Errorf("485 ERR_UNIQOPPRIVSNEEDED, %s", e.Raw)
|
log.Errorf("485 ERR_UNIQOPPRIVSNEEDED, %s", e.Raw)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ircClient.AddCallback("491", func(e *irc.Event) {
|
||||||
|
log.Errorf("491 ERR_NOOPERHOST, %s", e.Raw)
|
||||||
|
})
|
||||||
|
|
||||||
ircClient.AddCallback("501", func(e *irc.Event) {
|
ircClient.AddCallback("501", func(e *irc.Event) {
|
||||||
log.Errorf("501 ERR_UMODEUNKNOWNFLAG, %s", e.Raw)
|
log.Errorf("501 ERR_UMODEUNKNOWNFLAG, %s", e.Raw)
|
||||||
})
|
})
|
||||||
@@ -294,22 +424,6 @@ func ircClientRun() {
|
|||||||
log.Errorf("502 ERR_USERSDONTMATCH, %s", e.Raw)
|
log.Errorf("502 ERR_USERSDONTMATCH, %s", e.Raw)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Сделаем уже что-то полезное! Motd нам уже прислали и теперь можно авторизоваться и джойниться
|
|
||||||
ircClient.AddCallback("001", func(e *irc.Event) {
|
|
||||||
if !config.Irc.Sasl && config.Irc.Password != "" && !nickIsUsed {
|
|
||||||
log.Info("Identifying via NickServ")
|
|
||||||
message := fmt.Sprintf("identify %s %s", config.Irc.Nick, config.Irc.Password)
|
|
||||||
imChan <- iMsg{ChatId: "NickServ", Text: message}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("Trying to join to preconfigured channels")
|
|
||||||
|
|
||||||
for _, channel := range config.Irc.Channels {
|
|
||||||
log.Infof("Joining to %s channel", channel)
|
|
||||||
ircClient.Join(channel)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Навесим коллбэков на другие, интересные нам события
|
// Навесим коллбэков на другие, интересные нам события
|
||||||
ircClient.AddCallback("KICK", func(e *irc.Event) {
|
ircClient.AddCallback("KICK", func(e *irc.Event) {
|
||||||
dstNick := e.Arguments[1]
|
dstNick := e.Arguments[1]
|
||||||
@@ -318,10 +432,11 @@ func ircClientRun() {
|
|||||||
|
|
||||||
if e.Arguments[1] == ircClient.GetNick() {
|
if e.Arguments[1] == ircClient.GetNick() {
|
||||||
// Нас кикнули с канала, и мы теряем информацию о MODE-ах пользователей
|
// Нас кикнули с канала, и мы теряем информацию о MODE-ах пользователей
|
||||||
userMode.Delete(channel)
|
UserMode.Delete(channel)
|
||||||
// TODO: reason?
|
// TODO: reason?
|
||||||
log.Warnf("%s kicks us from %s", srcFullNick, channel)
|
log.Warnf("%s kicks us from %s", srcFullNick, channel)
|
||||||
time.Sleep(10 * time.Second)
|
<-time.NewTimer(10 * time.Second).C
|
||||||
|
|
||||||
ircClient.Join(channel)
|
ircClient.Join(channel)
|
||||||
} else {
|
} else {
|
||||||
// Кого-то другого кикнули с канала
|
// Кого-то другого кикнули с канала
|
||||||
@@ -334,21 +449,22 @@ func ircClientRun() {
|
|||||||
srcNick := e.Nick
|
srcNick := e.Nick
|
||||||
dstNick := e.Arguments[0]
|
dstNick := e.Arguments[0]
|
||||||
|
|
||||||
if e.Nick == config.Irc.Nick {
|
if e.Nick == Config.Irc.Nick {
|
||||||
nickIsUsed = false
|
nickIsUsed = false
|
||||||
|
|
||||||
if config.Irc.Password != "" {
|
if Config.Irc.Password != "" {
|
||||||
log.Warn("I regain my nick")
|
log.Warn("I regain my nick")
|
||||||
} else {
|
} else {
|
||||||
log.Warn("I regain my nick, trying to identify myself via NickServ")
|
log.Warn("I regain my nick, trying to identify myself via NickServ")
|
||||||
message := fmt.Sprintf("identify %s %s", config.Irc.Nick, config.Irc.Password)
|
|
||||||
imChan <- iMsg{ChatId: "NickServ", Text: message}
|
message := fmt.Sprintf("identify %s %s", Config.Irc.Nick, Config.Irc.Password)
|
||||||
|
imChan <- iMsg{ChatID: "NickServ", Text: message}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Infof("%s renames themself to %s", srcNick, dstNick)
|
log.Infof("%s renames themself to %s", srcNick, dstNick)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Неважно чей ник сменился, надо забыть, что было и снова узнать mode-ы сменишего nick джентельмена
|
// Неважно чей ник сменился, надо забыть, что было и снова узнать mode-ы сменишего nick джентельмена.
|
||||||
// TODO: реализовать userModeRenameUser()
|
// TODO: реализовать userModeRenameUser()
|
||||||
userModePurgeUser(srcNick)
|
userModePurgeUser(srcNick)
|
||||||
ircClient.Whois(dstNick)
|
ircClient.Whois(dstNick)
|
||||||
@@ -360,12 +476,12 @@ func ircClientRun() {
|
|||||||
channel := e.Arguments[0]
|
channel := e.Arguments[0]
|
||||||
|
|
||||||
if nick == ircClient.GetNick() {
|
if nick == ircClient.GetNick() {
|
||||||
|
// Команда names отправляется автоматом.
|
||||||
log.Infof("I joined to %s", channel)
|
log.Infof("I joined to %s", channel)
|
||||||
// Команда names отправляется автоматом
|
|
||||||
} else {
|
} else {
|
||||||
log.Infof("%s joined to %s", fullNick, channel)
|
log.Infof("%s joined to %s", fullNick, channel)
|
||||||
// Технически, тут не надо спрашивать whois на пользователя, но мы спрашиваем, чтобы уточнить mode,
|
// Технически, тут не надо спрашивать whois на пользователя, но мы спрашиваем, чтобы уточнить mode,
|
||||||
// вдруг сервер проставляет mode заранее (хотя не должен)
|
// вдруг сервер проставляет mode заранее (хотя не должен).
|
||||||
ircClient.Whois(nick)
|
ircClient.Whois(nick)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -377,7 +493,7 @@ func ircClientRun() {
|
|||||||
|
|
||||||
if nick == ircClient.GetNick() {
|
if nick == ircClient.GetNick() {
|
||||||
log.Infof("I parted from %s", channel)
|
log.Infof("I parted from %s", channel)
|
||||||
userMode.Delete(channel)
|
UserMode.Delete(channel)
|
||||||
} else {
|
} else {
|
||||||
log.Infof("%s parted from %s", fullNick, channel)
|
log.Infof("%s parted from %s", fullNick, channel)
|
||||||
userModeDeleteUser(channel, nick)
|
userModeDeleteUser(channel, nick)
|
||||||
@@ -403,6 +519,7 @@ func ircClientRun() {
|
|||||||
channel := e.Arguments[0]
|
channel := e.Arguments[0]
|
||||||
fullSrcNick := e.Source
|
fullSrcNick := e.Source
|
||||||
srcNick := e.Nick
|
srcNick := e.Nick
|
||||||
|
|
||||||
var dstNick string
|
var dstNick string
|
||||||
|
|
||||||
if len(e.Arguments) >= 3 { // кого-то по-MODE-или на канале
|
if len(e.Arguments) >= 3 { // кого-то по-MODE-или на канале
|
||||||
@@ -415,6 +532,7 @@ func ircClientRun() {
|
|||||||
}
|
}
|
||||||
} else { // Установка mode-а при заходе на сервер
|
} else { // Установка mode-а при заходе на сервер
|
||||||
dstNick = e.Arguments[0]
|
dstNick = e.Arguments[0]
|
||||||
|
|
||||||
log.Infof("Server set my mode to %s", mode)
|
log.Infof("Server set my mode to %s", mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,17 +572,16 @@ func ircClientRun() {
|
|||||||
ircMsgParser(e.Arguments[0], e.Nick, e.User, e.Source, e.Arguments[1])
|
ircMsgParser(e.Arguments[0], e.Nick, e.User, e.Source, e.Arguments[1])
|
||||||
})
|
})
|
||||||
|
|
||||||
/*
|
ircClient.AddCallback("*", func(e *irc.Event) {
|
||||||
ircClient.AddCallback("*", func(e *irc.Event) {
|
log.Debugf("Incoming EVENT (Raw): %s", e.Raw)
|
||||||
log.Debugf("Incoming EVENT (Raw): %s", e.Raw)
|
})
|
||||||
})
|
|
||||||
*/
|
|
||||||
|
|
||||||
log.Debugf("Make an actual connection to irc")
|
log.Debugf("Make an actual connection to irc")
|
||||||
|
|
||||||
if err := ircClient.Connect(serverString); err != nil {
|
if err := ircClient.Connect(serverString); err != nil {
|
||||||
log.Fatalf("Unable to prepare irc connection: %s", err)
|
log.Errorf("Unable to prepare irc connection: %s", err)
|
||||||
time.Sleep(3 * time.Second)
|
<-time.NewTimer(3 * time.Second).C // Sleep for 3 seconds.
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,26 +591,27 @@ func ircClientRun() {
|
|||||||
|
|
||||||
// ircSend отправляет сообщение в irc. Якобы с применением ratelimit-ов, но на практике длинные сообщения разделяются
|
// ircSend отправляет сообщение в irc. Якобы с применением ratelimit-ов, но на практике длинные сообщения разделяются
|
||||||
// на более короткие и тут ratelimit не срабатывает. Если не повезёт, то сервер может за такое дело и кикнуть.
|
// на более короткие и тут ratelimit не срабатывает. Если не повезёт, то сервер может за такое дело и кикнуть.
|
||||||
func ircSend() {
|
func IrcSend() {
|
||||||
for {
|
for {
|
||||||
m := <-imChan
|
m := <-imChan
|
||||||
|
|
||||||
switch {
|
switch Config.Irc.RateLimit.Type {
|
||||||
case config.Irc.RateLimit.Type == "simple_delay":
|
case "simple_delay":
|
||||||
log.Debugf("Sending to chat %s message: %s", m.ChatId, m.Text)
|
log.Debugf("Sending to chat %s message: %s", m.ChatID, m.Text)
|
||||||
|
|
||||||
if m.Text[0:4] == "/me " {
|
if m.Text[0:4] == "/me " {
|
||||||
ircClient.Action(m.ChatId, m.Text)
|
ircClient.Action(m.ChatID, m.Text)
|
||||||
} else {
|
} else {
|
||||||
ircClient.Privmsg(m.ChatId, m.Text)
|
ircClient.Privmsg(m.ChatID, m.Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
sleepDelay := time.Duration(config.Irc.RateLimit.SimpleDelay) * time.Millisecond
|
sleepDelay := time.Duration(Config.Irc.RateLimit.SimpleDelay) * time.Millisecond
|
||||||
log.Debugf("Due to delay type simple_delay waiting for %d milliseconds", int(sleepDelay))
|
log.Debugf("Due to delay type simple_delay waiting for %d milliseconds", int(sleepDelay))
|
||||||
time.Sleep(sleepDelay)
|
<-time.NewTimer(sleepDelay).C
|
||||||
case config.Irc.RateLimit.Type == "token_bucket":
|
case "token_bucket":
|
||||||
var currentTimeMs = time.Now().UnixMilli()
|
var currentTimeMs = time.Now().UnixMilli()
|
||||||
var expirationTimeMs = config.Irc.RateLimit.TokenBucket.ExpirationTime * 1000
|
|
||||||
|
var expirationTimeMs = Config.Irc.RateLimit.TokenBucket.ExpirationTime * 1000
|
||||||
|
|
||||||
if len(msgBucket.Timestamps) > 0 {
|
if len(msgBucket.Timestamps) > 0 {
|
||||||
var newBucket bucket
|
var newBucket bucket
|
||||||
@@ -505,10 +623,11 @@ func ircSend() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bucketFill := len(newBucket.Timestamps)
|
bucketFill := len(newBucket.Timestamps)
|
||||||
log.Debugf("Message bucket filled with %d/%d messages", bucketFill, config.Irc.RateLimit.TokenBucket.Size)
|
log.Debugf("Message bucket filled with %d/%d messages", bucketFill, Config.Irc.RateLimit.TokenBucket.Size)
|
||||||
|
|
||||||
newBucket.Timestamps = append(newBucket.Timestamps, currentTimeMs)
|
newBucket.Timestamps = append(newBucket.Timestamps, currentTimeMs)
|
||||||
|
|
||||||
if bucketFill >= config.Irc.RateLimit.TokenBucket.Size {
|
if bucketFill >= Config.Irc.RateLimit.TokenBucket.Size {
|
||||||
newBucket.IsFull = true
|
newBucket.IsFull = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,52 +635,55 @@ func ircSend() {
|
|||||||
} else {
|
} else {
|
||||||
// Проставим время отправки сообщения
|
// Проставим время отправки сообщения
|
||||||
log.Debug("Message bucket is empty")
|
log.Debug("Message bucket is empty")
|
||||||
|
|
||||||
msgBucket.Timestamps = append(msgBucket.Timestamps, currentTimeMs)
|
msgBucket.Timestamps = append(msgBucket.Timestamps, currentTimeMs)
|
||||||
}
|
}
|
||||||
|
|
||||||
if msgBucket.IsFull {
|
if msgBucket.IsFull {
|
||||||
log.Debug("Message bucket overflowed, hitting ratelimit")
|
log.Debug("Message bucket overflowed, hitting ratelimit")
|
||||||
|
|
||||||
sleepPeriod := expirationTimeMs - (currentTimeMs - msgBucket.Timestamps[len(msgBucket.Timestamps)-1])
|
sleepPeriod := expirationTimeMs - (currentTimeMs - msgBucket.Timestamps[len(msgBucket.Timestamps)-1])
|
||||||
|
|
||||||
if config.Irc.RateLimit.TokenBucket.Limit > 0 {
|
if Config.Irc.RateLimit.TokenBucket.Limit > 0 {
|
||||||
sleepPeriod = sleepPeriod / int64(config.Irc.RateLimit.TokenBucket.Limit)
|
sleepPeriod /= int64(Config.Irc.RateLimit.TokenBucket.Limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Sleeping for %d milliseconds", sleepPeriod)
|
log.Debugf("Sleeping for %d milliseconds", sleepPeriod)
|
||||||
time.Sleep(time.Duration(sleepPeriod) * time.Millisecond)
|
<-time.NewTimer(time.Duration(sleepPeriod) * time.Millisecond).C
|
||||||
|
|
||||||
currentTimeMs = time.Now().UnixMilli()
|
currentTimeMs = time.Now().UnixMilli()
|
||||||
// Обновим время отправки сообщения
|
// Обновим время отправки сообщения
|
||||||
msgBucket.Timestamps[len(msgBucket.Timestamps)-1] = currentTimeMs
|
msgBucket.Timestamps[len(msgBucket.Timestamps)-1] = currentTimeMs
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Sending to chat %s message: %s", m.ChatId, m.Text)
|
log.Debugf("Sending to chat %s message: %s", m.ChatID, m.Text)
|
||||||
|
|
||||||
if len(m.Text) > 4 && m.Text[0:4] == "/me " {
|
if len(m.Text) > 4 && m.Text[0:4] == "/me " {
|
||||||
ircClient.Action(m.ChatId, m.Text[4:])
|
ircClient.Action(m.ChatID, m.Text[4:])
|
||||||
} else {
|
} else {
|
||||||
ircClient.Privmsg(m.ChatId, m.Text)
|
ircClient.Privmsg(m.ChatID, m.Text)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
log.Debugf("Sending to chat %s message: %s", m.ChatId, m.Text)
|
log.Debugf("Sending to chat %s message: %s", m.ChatID, m.Text)
|
||||||
|
|
||||||
if len(m.Text) > 4 && m.Text[0:4] == "/me " {
|
if len(m.Text) > 4 && m.Text[0:4] == "/me " {
|
||||||
ircClient.Action(m.ChatId, m.Text[4:])
|
ircClient.Action(m.ChatID, m.Text[4:])
|
||||||
} else {
|
} else {
|
||||||
ircClient.Privmsg(m.ChatId, m.Text)
|
ircClient.Privmsg(m.ChatID, m.Text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ircSendUnrestricted() {
|
func IrcSendUnrestricted() {
|
||||||
for {
|
for {
|
||||||
m := <-imChanUnrestricted
|
m := <-imChanUnrestricted
|
||||||
log.Debugf("Skipping ratelimit and sending to chat %s message: %s", m.ChatId, m.Text)
|
log.Debugf("Skipping ratelimit and sending to chat %s message: %s", m.ChatID, m.Text)
|
||||||
|
|
||||||
if len(m.Text) > 4 && m.Text[0:4] == "/me " {
|
if len(m.Text) > 4 && m.Text[0:4] == "/me " {
|
||||||
ircClient.Action(m.ChatId, m.Text[4:])
|
ircClient.Action(m.ChatID, m.Text[4:])
|
||||||
} else {
|
} else {
|
||||||
ircClient.Privmsg(m.ChatId, m.Text)
|
ircClient.Privmsg(m.ChatID, m.Text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,50 +1,55 @@
|
|||||||
package main
|
package irc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/cockroachdb/pebble"
|
"aleesa-irc-go/internal/anycollection"
|
||||||
|
"aleesa-irc-go/internal/boolcollection"
|
||||||
|
|
||||||
"github.com/go-redis/redis/v8"
|
"github.com/go-redis/redis/v8"
|
||||||
irc "github.com/thoj/go-ircevent"
|
irc "github.com/thoj/go-ircevent"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config - это у нас глобальная штука :)
|
// Config - это у нас глобальная штука :).
|
||||||
var config myConfig
|
var Config MyConfig
|
||||||
|
|
||||||
// To break circular message forwarding we must set some sane default, it can be overridden via config
|
// To break circular message forwarding we must set some sane default, it can be overridden via config.
|
||||||
var forwardMax int64 = 5
|
var forwardMax int64 = 5
|
||||||
|
|
||||||
// Объектики irc-клиента
|
// Объектики irc-клиента.
|
||||||
var ircClient *irc.Connection
|
var ircClient *irc.Connection
|
||||||
var nickIsUsed = false
|
var nickIsUsed = false
|
||||||
|
|
||||||
// Объектики клиента-редиски
|
// Объектики клиента-редиски.
|
||||||
var redisClient *redis.Client
|
var RedisClient *redis.Client
|
||||||
var subscriber *redis.PubSub
|
var Subscriber *redis.PubSub
|
||||||
|
|
||||||
// Main context
|
// Main context.
|
||||||
var ctx = context.Background()
|
var ctx = context.Background()
|
||||||
|
|
||||||
// Ставится в true, если мы получили сигнал на выключение
|
// Ставится в true, если мы получили сигнал на выключение.
|
||||||
var shutdown = false
|
var Shutdown = false
|
||||||
|
|
||||||
// Канал, в который приходят уведомления для хэндлера сигналов от траппера сигналов
|
// Канал, в который приходят уведомления для хэндлера сигналов от траппера сигналов.
|
||||||
var sigChan = make(chan os.Signal, 1)
|
var SigChan = make(chan os.Signal, 1)
|
||||||
|
|
||||||
// Канал, в который пишутся сообщения для отправки в IRC в обычном порядке
|
// Канал, в который пишутся сообщения для отправки в IRC в обычном порядке.
|
||||||
var imChan = make(chan iMsg, 10000)
|
var imChan = make(chan iMsg, 10000)
|
||||||
|
|
||||||
// Канал, в который пишутся сообщения для отправки в IRC, если ограничений нет (у бота +o или +v на канале)
|
// Канал, в который пишутся сообщения для отправки в IRC, если ограничений нет (у бота +o или +v на канале).
|
||||||
var imChanUnrestricted = make(chan iMsg, 100)
|
var imChanUnrestricted = make(chan iMsg, 100)
|
||||||
|
|
||||||
// "Ведёрко" с timestamp-ами последних отправленных сообщений
|
// "Ведёрко" с timestamp-ами последних отправленных сообщений.
|
||||||
var msgBucket bucket
|
var msgBucket bucket
|
||||||
|
|
||||||
// Мапка с открытыми дескрипторами баз с настройками
|
// "Базюлька" с MODE-ами пользователей на каналах.
|
||||||
var settingsDB = make(map[string]*pebble.DB)
|
var UserMode = anycollection.NewCollection()
|
||||||
|
|
||||||
// "Базюлька" с MODE-ами пользователей на каналах
|
// "Базюлька" с доступными MODE-ами пользователя.
|
||||||
var userMode = NewCollection()
|
var availableUserModes = boolcollection.NewCollection()
|
||||||
|
|
||||||
|
// "Базюлька" c с доступными MODE-ами каналов.
|
||||||
|
var availableChanModes = boolcollection.NewCollection()
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package irc
|
||||||
|
|
||||||
import "strings"
|
import "strings"
|
||||||
|
|
||||||
@@ -8,33 +8,34 @@ import "strings"
|
|||||||
активируют мягкий ratelimit на стороне сервера, без них, сервер просто рвёт соединение по превышению лимитов
|
активируют мягкий ratelimit на стороне сервера, без них, сервер просто рвёт соединение по превышению лимитов
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Удаляет сведения о MODE-ах пользователя для заданного канала
|
// Удаляет сведения о MODE-ах пользователя для заданного канала.
|
||||||
func userModeDeleteUser(ircChan string, nick string) {
|
func userModeDeleteUser(ircChan string, nick string) {
|
||||||
channel, ok := userMode.Get(ircChan)
|
channel, ok := UserMode.Get(ircChan)
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
channelData := channel.(map[string]map[string]bool)
|
channelData := channel.(map[string]map[string]bool)
|
||||||
delete(channelData, nick)
|
delete(channelData, nick)
|
||||||
|
|
||||||
if len(channelData) == 0 {
|
if len(channelData) == 0 {
|
||||||
userMode.Delete(ircChan)
|
UserMode.Delete(ircChan)
|
||||||
} else {
|
} else {
|
||||||
userMode.Set(ircChan, channelData)
|
UserMode.Set(ircChan, channelData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Удаляет сведения о MODE-ах пользователя для всех каналов, на которых есть бот
|
// Удаляет сведения о MODE-ах пользователя для всех каналов, на которых есть бот.
|
||||||
func userModePurgeUser(nick string) {
|
func userModePurgeUser(nick string) {
|
||||||
for _, channel := range config.Irc.Channels {
|
for _, channel := range Config.Irc.Channels {
|
||||||
userModeDeleteUser(channel, nick)
|
userModeDeleteUser(channel, nick)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновляет или создаёт запись о mode-ах пользователей
|
// Обновляет или создаёт запись о mode-ах пользователей.
|
||||||
func userModeUpdateUser(ircChan string, nick string, modes string) {
|
func userModeUpdateUser(ircChan string, nick string, modes string) {
|
||||||
var channelData map[string]map[string]bool
|
var channelData map[string]map[string]bool
|
||||||
channel, ok := userMode.Get(ircChan)
|
|
||||||
|
channel, ok := UserMode.Get(ircChan)
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
channelData = channel.(map[string]map[string]bool)
|
channelData = channel.(map[string]map[string]bool)
|
||||||
@@ -48,24 +49,25 @@ func userModeUpdateUser(ircChan string, nick string, modes string) {
|
|||||||
channelData[nick] = make(map[string]bool)
|
channelData[nick] = make(map[string]bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch modes[:1] {
|
||||||
case modes[:1] == "+":
|
case "+":
|
||||||
for _, mode := range strings.Split(modes[1:], "") {
|
for _, mode := range strings.Split(modes[1:], "") {
|
||||||
channelData[nick][mode] = true
|
channelData[nick][mode] = true
|
||||||
}
|
}
|
||||||
case modes[:1] == "-":
|
case "-":
|
||||||
for _, mode := range strings.Split(modes[1:], "") {
|
for _, mode := range strings.Split(modes[1:], "") {
|
||||||
channelData[nick][mode] = false
|
channelData[nick][mode] = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
userMode.Set(ircChan, channelData)
|
UserMode.Set(ircChan, channelData)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Возвращает значение MODE-а v для запрошенного ника
|
// Возвращает значение MODE-а v для запрошенного ника.
|
||||||
func userModeIsVoiced(ircChan string, nick string) bool {
|
func userModeIsVoiced(ircChan string, nick string) bool {
|
||||||
var channelData map[string]map[string]bool
|
var channelData map[string]map[string]bool
|
||||||
channel, ok := userMode.Get(ircChan)
|
|
||||||
|
channel, ok := UserMode.Get(ircChan)
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
channelData = channel.(map[string]map[string]bool)
|
channelData = channel.(map[string]map[string]bool)
|
||||||
@@ -88,10 +90,11 @@ func userModeIsVoiced(ircChan string, nick string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Возвращает значение MODE-а o для запрошенного ника
|
// Возвращает значение MODE-а o для запрошенного ника.
|
||||||
func userModeIsOped(ircChan string, nick string) bool {
|
func userModeIsOped(ircChan string, nick string) bool {
|
||||||
var channelData map[string]map[string]bool
|
var channelData map[string]map[string]bool
|
||||||
channel, ok := userMode.Get(ircChan)
|
|
||||||
|
channel, ok := UserMode.Get(ircChan)
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
channelData = channel.(map[string]map[string]bool)
|
channelData = channel.(map[string]map[string]bool)
|
||||||
@@ -116,7 +119,8 @@ func userModeIsOped(ircChan string, nick string) bool {
|
|||||||
|
|
||||||
func userModeIsHere(ircChan string, nick string) bool {
|
func userModeIsHere(ircChan string, nick string) bool {
|
||||||
var channelData map[string]map[string]bool
|
var channelData map[string]map[string]bool
|
||||||
channel, ok := userMode.Get(ircChan)
|
|
||||||
|
channel, ok := UserMode.Get(ircChan)
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
channelData = channel.(map[string]map[string]bool)
|
channelData = channel.(map[string]map[string]bool)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package irc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -6,15 +6,16 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
"aleesa-irc-go/internal/log"
|
||||||
|
"aleesa-irc-go/internal/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ircMsgParser парсит сообщения, прилетевшие из IRC-ки
|
// ircMsgParser парсит сообщения, прилетевшие из IRC-ки.
|
||||||
func ircMsgParser(channel string, nick string, user string, source string, msg string) {
|
func ircMsgParser(channel string, nick string, user string, source string, msg string) { //nolint: revive
|
||||||
// nick - это выбранный пользователем nick (если он занят, то его "нарисует" сервер)
|
// nick - это выбранный пользователем nick (если он занят, то его "нарисует" сервер)
|
||||||
// user - это короткое имя пользователя, под которым его видит сервер
|
// user - это короткое имя пользователя, под которым его видит сервер
|
||||||
// source - это длинное имя пользователя, оно содержит в себе помимо user, ещё и ip с которого пришёл пользователь
|
// source - это длинное имя пользователя, оно содержит в себе помимо user, ещё и ip с которого пришёл пользователь
|
||||||
if shutdown {
|
if Shutdown {
|
||||||
// Если мы завершаем работу программы, то нам ничего обрабатывать не надо
|
// Если мы завершаем работу программы, то нам ничего обрабатывать не надо
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -26,89 +27,90 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ловим команды и обрабатываем их
|
// Ловим команды и обрабатываем их
|
||||||
if (len(msg) > len(config.Csign)) && (msg[:len(config.Csign)] == config.Csign) {
|
if (len(msg) > len(Config.Csign)) && (msg[:len(Config.Csign)] == Config.Csign) {
|
||||||
var outgoingMessage string
|
var outgoingMessage string
|
||||||
|
|
||||||
var message sMsg
|
var message sMsg
|
||||||
message.From = config.Redis.MyChannel
|
message.From = Config.Redis.MyChannel
|
||||||
message.Userid = user // как его видит сервер
|
message.Userid = user // как его видит сервер
|
||||||
message.Chatid = channel // чятик, в который написал user
|
message.Chatid = channel // чятик, в который написал user
|
||||||
message.Plugin = config.Redis.MyChannel
|
message.Threadid = "" // тредиков в irc нету, поэтому это поле отправляем пустым
|
||||||
|
message.Plugin = Config.Redis.MyChannel
|
||||||
message.Mode = "public"
|
message.Mode = "public"
|
||||||
// Предполагается, что на команды бот автоматом отвечает
|
// Предполагается, что на команды бот автоматом отвечает
|
||||||
message.Misc.Answer = 1
|
message.Misc.Answer = 1
|
||||||
message.Misc.Fwdcnt = 0
|
message.Misc.Fwdcnt = 0
|
||||||
message.Misc.Csign = config.Csign
|
message.Misc.Csign = Config.Csign
|
||||||
message.Misc.Username = nick
|
message.Misc.Username = nick
|
||||||
message.Misc.Botnick = config.Irc.Nick
|
message.Misc.Botnick = Config.Irc.Nick
|
||||||
message.Misc.Msgformat = 0
|
message.Misc.Msgformat = 0
|
||||||
|
|
||||||
var cmd = msg[len(config.Csign):]
|
var cmd = msg[len(Config.Csign):]
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case cmd == "help" || msg == "помощь":
|
case cmd == "help" || msg == "помощь":
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%shelp | %sпомощь - это сообщение", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%shelp | %sпомощь - это сообщение", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sanek | %sанек | %sанекдот - рандомный анекдот с anekdot.ru", config.Csign, config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sanek | %sанек | %sанекдот - рандомный анекдот с anekdot.ru", Config.Csign, Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sbuni - комикс-стрип hapi buni", config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sbuni - комикс-стрип hapi buni", Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sbunny - кролик", config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sbunny - кролик", Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%srabbit | %sкролик - кролик", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%srabbit | %sкролик - кролик", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%scat | %sкис - кошечка", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%scat | %sкис - кошечка", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sdice | %sroll | %sкости - бросить кости", config.Csign, config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sdice | %sroll | %sкости - бросить кости", Config.Csign, Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sdig | %sкопать - заняться археологией", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sdig | %sкопать - заняться археологией", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sdrink | %sпраздник - какой сегодня праздник?", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sdrink | %sпраздник - какой сегодня праздник?", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sfish | %sfisher - порыбачить", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sfish | %sfisher - порыбачить", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sрыба | %sрыбка | %sрыбалка - порыбачить", config.Csign, config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sрыба | %sрыбка | %sрыбалка - порыбачить", Config.Csign, Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sf | %sф - рандомная фраза из сборника цитат fortune_mod", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sf | %sф - рандомная фраза из сборника цитат fortune_mod", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sfortune | %sфортунка - рандомная фраза из сборника цитат fortune_mod", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sfortune | %sфортунка - рандомная фраза из сборника цитат fortune_mod", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sfox | %sлис - лисичка", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sfox | %sлис - лисичка", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sfriday | %sпятница - а не пятница ли сегодня?", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sfriday | %sпятница - а не пятница ли сегодня?", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sfrog | %sлягушка - лягушка", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sfrog | %sлягушка - лягушка", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%shorse | %sлошадь | %sлошадка - лошадка", config.Csign, config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%shorse | %sлошадь | %sлошадка - лошадка", Config.Csign, Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%skarma фраза - посмотреть карму фразы", config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%skarma фраза - посмотреть карму фразы", Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sкарма фраза - посмотреть карму фразы", config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sкарма фраза - посмотреть карму фразы", Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintln("фраза++ | фраза-- - повысить или понизить карму фразы")}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintln("фраза++ | фраза-- - повысить или понизить карму фразы")}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%slat | %sлат - сгенерировать фразу из крылатого латинского выражения", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%slat | %sлат - сгенерировать фразу из крылатого латинского выражения", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%smonkeyuser - комикс-стрип MonkeyUser", config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%smonkeyuser - комикс-стрип MonkeyUser", Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sowl | %sсова - сова", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sowl | %sсова - сова", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sping | %sпинг - попинговать бота", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sping | %sпинг - попинговать бота", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sproverb | %sпословица - рандомная русская пословица", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sproverb | %sпословица - рандомная русская пословица", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%ssnail | %sулитка - улитка", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%ssnail | %sулитка - улитка", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%ssome_brew - выдать соответствующий напиток, бармен может налить rum, ром, vodka, водку, tequila, текила, whisky, виски, absinthe, абсент", config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%ssome_brew - выдать соответствующий напиток, бармен может налить rum, ром, vodka, водку, tequila, текила, whisky, виски, absinthe, абсент", Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sver | %sversion - написать что-то про версию ПО", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sver | %sversion - написать что-то про версию ПО", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sверсия - написать что-то про версию ПО", config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sверсия - написать что-то про версию ПО", Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sw город | %sп город - погода в городе", config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sw <город> | %sп <город> - погода в городе", Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sxkcd - комикс-стрип с xkcb.ru", config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sxkcd - комикс-стрип с xkcb.ru", Config.Csign)}
|
||||||
|
|
||||||
if userModeIsOped(channel, nick) {
|
if userModeIsOped(channel, nick) {
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sadmin - настройки некоторых плагинов бота для канала", config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sadmin - настройки некоторых плагинов бота для канала", Config.Csign)}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
case cmd == "admin":
|
case cmd == "admin":
|
||||||
if userModeIsOped(channel, nick) {
|
if userModeIsOped(channel, nick) {
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sadmin oboobs # - где 1 - вкл, 0 - выкл плагина oboobs", config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sadmin oboobs # - где 1 - вкл, 0 - выкл плагина oboobs", Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sadmin oboobs показываем ли сисечки по просьбе участников чата (команды %stits, %stities, %sboobs, %sboobies, %sсиси, %sсисечки)", config.Csign, config.Csign, config.Csign, config.Csign, config.Csign, config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sadmin oboobs показываем ли сисечки по просьбе участников чата (команды %stits, %stities, %sboobs, %sboobies, %sсиси, %sсисечки)", Config.Csign, Config.Csign, Config.Csign, Config.Csign, Config.Csign, Config.Csign, Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sadmin obutts # - где 1 - вкл, 0 - выкл плагина obutts", config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sadmin obutts # - где 1 - вкл, 0 - выкл плагина obutts", Config.Csign)}
|
||||||
imChan <- iMsg{ChatId: nick, Text: fmt.Sprintf("%sadmin obutts показываем ли попки по просьбе участников чата (команды %sass, %sbutt, %sbooty, %sпопа, %sпопка)", config.Csign, config.Csign, config.Csign, config.Csign, config.Csign, config.Csign)}
|
imChan <- iMsg{ChatID: nick, Text: fmt.Sprintf("%sadmin obutts показываем ли попки по просьбе участников чата (команды %sass, %sbutt, %sbooty, %sпопа, %sпопка)", Config.Csign, Config.Csign, Config.Csign, Config.Csign, Config.Csign, Config.Csign)}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
case cmd == "admin oboobs":
|
case cmd == "admin oboobs":
|
||||||
if userModeIsOped(channel, nick) {
|
if userModeIsOped(channel, nick) {
|
||||||
value := getSetting(channel, "oboobs")
|
value := setting.Get(channel, "oboobs")
|
||||||
|
|
||||||
switch {
|
switch value {
|
||||||
case value == "":
|
case "":
|
||||||
_ = saveSetting(channel, "oboobs", "0")
|
_ = setting.Set(channel, "oboobs", "0")
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs выключен"}
|
||||||
case value == "0":
|
case "0":
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs выключен"}
|
||||||
case value == "1":
|
case "1":
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs включен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs включен"}
|
||||||
default:
|
default:
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs выключен"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,12 +118,12 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
|
|
||||||
case cmd == "admin oboobs 1":
|
case cmd == "admin oboobs 1":
|
||||||
if userModeIsOped(channel, nick) {
|
if userModeIsOped(channel, nick) {
|
||||||
err := saveSetting(channel, "oboobs", "1")
|
err := setting.Set(channel, "oboobs", "1")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs всё ещё выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs всё ещё выключен"}
|
||||||
} else {
|
} else {
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs включен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs включен"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,26 +131,26 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
|
|
||||||
case cmd == "admin oboobs 0":
|
case cmd == "admin oboobs 0":
|
||||||
if userModeIsOped(channel, nick) {
|
if userModeIsOped(channel, nick) {
|
||||||
_ = saveSetting(channel, "oboobs", "0")
|
_ = setting.Set(channel, "oboobs", "0")
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs выключен"}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
case cmd == "admin obutts":
|
case cmd == "admin obutts":
|
||||||
if userModeIsOped(channel, nick) {
|
if userModeIsOped(channel, nick) {
|
||||||
value := getSetting(channel, "obutts")
|
value := setting.Get(channel, "obutts")
|
||||||
|
|
||||||
switch {
|
switch value {
|
||||||
case value == "":
|
case "":
|
||||||
_ = saveSetting(channel, "obutts", "0")
|
_ = setting.Set(channel, "obutts", "0")
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts выключен"}
|
||||||
case value == "0":
|
case "0":
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts выключен"}
|
||||||
case value == "1":
|
case "1":
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts включен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts включен"}
|
||||||
default:
|
default:
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts выключен"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,12 +158,12 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
|
|
||||||
case cmd == "admin obutts 1":
|
case cmd == "admin obutts 1":
|
||||||
if userModeIsOped(channel, nick) {
|
if userModeIsOped(channel, nick) {
|
||||||
err := saveSetting(channel, "obutts", "1")
|
err := setting.Set(channel, "obutts", "1")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts всё ещё выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts всё ещё выключен"}
|
||||||
} else {
|
} else {
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts включен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts включен"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,8 +171,8 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
|
|
||||||
case cmd == "admin obutts 0":
|
case cmd == "admin obutts 0":
|
||||||
if userModeIsOped(channel, nick) {
|
if userModeIsOped(channel, nick) {
|
||||||
_ = saveSetting(channel, "obutts", "0")
|
_ = setting.Set(channel, "obutts", "0")
|
||||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts выключен"}
|
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts выключен"}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -181,15 +183,17 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
// Общий список простых команд
|
// Общий список простых команд
|
||||||
cmds := []string{"ping", "пинг", "пинх", "pong", "понг", "понх", "coin", "монетка", "roll", "dice", "кости",
|
cmds := []string{"ping", "пинг", "пинх", "pong", "понг", "понх", "coin", "монетка", "roll", "dice", "кости",
|
||||||
"ver", "version", "версия", "хэлп", "halp", "kde", "кде", "lat", "лат", "friday", "пятница", "proverb",
|
"ver", "version", "версия", "хэлп", "halp", "kde", "кде", "lat", "лат", "friday", "пятница", "proverb",
|
||||||
"пословица", "fortune", "фортунка", "f", "ф", "anek", "анек", "анекдот", "buni", "cat", "кис", "drink",
|
"пословица", "пословиться", "fortune", "фортунка", "f", "ф", "anek", "анек", "анекдот", "buni", "cat",
|
||||||
"праздник", "fox", "лис", "frog", "лягушка", "horse", "лошадь", "лошадка", "monkeyuser", "owl", "сова",
|
"кис", "drink", "праздник", "fox", "лис", "frog", "лягушка", "horse", "лошадь", "лошадка", "monkeyuser",
|
||||||
"сыч", "rabbit", "bunny", "кролик", "snail", "улитка", "xkcd", "dig", "копать", "fish", "fishing", "рыба",
|
"owl", "сова", "сыч", "rabbit", "bunny", "кролик", "snail", "улитка", "xkcd", "dig", "копать", "fish",
|
||||||
"рыбка", "рыбалка", "karma", "карма"}
|
"fishing", "рыба", "рыбка", "рыбалка", "karma", "карма", "w", "п", "weather", "погода", "погодка",
|
||||||
|
"погадка"}
|
||||||
|
|
||||||
for _, command := range cmds {
|
for _, command := range cmds {
|
||||||
if cmd == command {
|
if cmd == command {
|
||||||
done = true
|
done = true
|
||||||
outgoingMessage = msg
|
outgoingMessage = msg
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,6 +208,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
if cmd == command {
|
if cmd == command {
|
||||||
done = true
|
done = true
|
||||||
outgoingMessage = msg
|
outgoingMessage = msg
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,7 +229,8 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
message.Misc.Username = strings.TrimSpace(pile[1])
|
message.Misc.Username = strings.TrimSpace(pile[1])
|
||||||
} else {
|
} else {
|
||||||
msg = fmt.Sprintf("Я тут не вижу участника с ником %s", userNick)
|
msg = fmt.Sprintf("Я тут не вижу участника с ником %s", userNick)
|
||||||
imChan <- iMsg{ChatId: channel, Text: msg}
|
imChan <- iMsg{ChatID: channel, Text: msg}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} // в противном случае юзер просто поставил пробел на конце команды, интерпретируем это как
|
} // в противном случае юзер просто поставил пробел на конце команды, интерпретируем это как
|
||||||
@@ -232,6 +238,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
|
|
||||||
outgoingMessage = msg
|
outgoingMessage = msg
|
||||||
done = true
|
done = true
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,6 +255,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
if cmdLen > len(command) && cmd[0:len(command)] == command {
|
if cmdLen > len(command) && cmd[0:len(command)] == command {
|
||||||
done = true
|
done = true
|
||||||
outgoingMessage = msg
|
outgoingMessage = msg
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -255,7 +263,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
|
|
||||||
// Отключаемые команды
|
// Отключаемые команды
|
||||||
if !done {
|
if !done {
|
||||||
value := getSetting(channel, "obutts")
|
value := setting.Get(channel, "obutts")
|
||||||
|
|
||||||
if value == "1" {
|
if value == "1" {
|
||||||
cmds := []string{"butt", "booty", "ass", "попа", "попка"}
|
cmds := []string{"butt", "booty", "ass", "попа", "попка"}
|
||||||
@@ -265,13 +273,14 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
if cmd[:cmdLen] == command {
|
if cmd[:cmdLen] == command {
|
||||||
done = true
|
done = true
|
||||||
outgoingMessage = msg
|
outgoingMessage = msg
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !done {
|
if !done {
|
||||||
value = getSetting(channel, "oboobs")
|
value = setting.Get(channel, "oboobs")
|
||||||
|
|
||||||
if value == "1" {
|
if value == "1" {
|
||||||
cmds := []string{"tits", "boobs", "tities", "boobies", "сиси", "сисечки"}
|
cmds := []string{"tits", "boobs", "tities", "boobies", "сиси", "сисечки"}
|
||||||
@@ -281,6 +290,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
if cmd[:cmdLen] == command {
|
if cmd[:cmdLen] == command {
|
||||||
// done = true
|
// done = true
|
||||||
outgoingMessage = msg
|
outgoingMessage = msg
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,24 +305,26 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Unable to to serialize message for redis: %s", err)
|
log.Warnf("Unable to to serialize message for redis: %s", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Заталкиваем наш json в редиску
|
// Заталкиваем наш json в редиску
|
||||||
if err := redisClient.Publish(ctx, config.Redis.Channel, data).Err(); err != nil {
|
if err := RedisClient.Publish(ctx, Config.Redis.Channel, data).Err(); err != nil {
|
||||||
log.Warnf("Unable to send data to redis channel %s: %s", config.Redis.Channel, err)
|
log.Warnf("Unable to send data to redis channel %s: %s", Config.Redis.Channel, err)
|
||||||
} else {
|
} else {
|
||||||
log.Debugf("Sent msg to redis channel %s: %s", config.Redis.Channel, string(data))
|
log.Debugf("Sent msg to redis channel %s: %s", Config.Redis.Channel, string(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Это уже просто трёп в чятике
|
// Это уже просто трёп в чятике
|
||||||
var message sMsg
|
var message sMsg
|
||||||
message.From = config.Redis.MyChannel
|
message.From = Config.Redis.MyChannel
|
||||||
message.Userid = user // как его видит сервер
|
message.Userid = user // как его видит сервер
|
||||||
message.Chatid = channel // чятик, в который написал user
|
message.Chatid = channel // чятик, в который написал user
|
||||||
|
message.Threadid = "" // тредиков в irc нету, поэтому это поле отправляем пустым
|
||||||
message.Message = msg
|
message.Message = msg
|
||||||
message.Plugin = config.Redis.MyChannel
|
message.Plugin = Config.Redis.MyChannel
|
||||||
message.Mode = "public"
|
message.Mode = "public"
|
||||||
|
|
||||||
// Если во фразе содержится ключевые слова, под это есть костыль в craniac-е - (предполагаем, что) он
|
// Если во фразе содержится ключевые слова, под это есть костыль в craniac-е - (предполагаем, что) он
|
||||||
@@ -339,32 +351,31 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
|||||||
}
|
}
|
||||||
|
|
||||||
message.Misc.Fwdcnt = 0
|
message.Misc.Fwdcnt = 0
|
||||||
message.Misc.Csign = config.Csign
|
message.Misc.Csign = Config.Csign
|
||||||
message.Misc.Username = nick
|
message.Misc.Username = nick
|
||||||
message.Misc.Botnick = config.Irc.Nick
|
message.Misc.Botnick = Config.Irc.Nick
|
||||||
message.Misc.Msgformat = 0
|
message.Misc.Msgformat = 0
|
||||||
|
|
||||||
data, err := json.Marshal(message)
|
data, err := json.Marshal(message)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Unable to to serialize message for redis: %s", err)
|
log.Warnf("Unable to to serialize message for redis: %s", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Заталкиваем наш json в редиску
|
// Заталкиваем наш json в редиску
|
||||||
if err := redisClient.Publish(ctx, config.Redis.Channel, data).Err(); err != nil {
|
if err := RedisClient.Publish(ctx, Config.Redis.Channel, data).Err(); err != nil {
|
||||||
log.Warnf("Unable to send data to redis channel %s: %s", config.Redis.Channel, err)
|
log.Warnf("Unable to send data to redis channel %s: %s", Config.Redis.Channel, err)
|
||||||
} else {
|
} else {
|
||||||
log.Debugf("Sent msg to redis channel %s: %s", config.Redis.Channel, string(data))
|
log.Debugf("Sent msg to redis channel %s: %s", Config.Redis.Channel, string(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// redisMsgParser парсит json-чики прилетевшие из REDIS-ки, причём, json-чики должны быть относительно валидными
|
// RedisMsgParser парсит json-чики прилетевшие из REDIS-ки, причём, json-чики должны быть относительно валидными.
|
||||||
func redisMsgParser(msg string) {
|
func RedisMsgParser(msg string) {
|
||||||
if shutdown {
|
if Shutdown {
|
||||||
// Если мы завершаем работу программы, то нам ничего обрабатывать не надо
|
// Если мы завершаем работу программы, то нам ничего обрабатывать не надо
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -375,50 +386,58 @@ func redisMsgParser(msg string) {
|
|||||||
|
|
||||||
if err := json.Unmarshal([]byte(msg), &j); err != nil {
|
if err := json.Unmarshal([]byte(msg), &j); err != nil {
|
||||||
log.Warnf("Unable to to parse message from redis channel: %s", err)
|
log.Warnf("Unable to to parse message from redis channel: %s", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate our j
|
// Validate our j
|
||||||
if exist := j.From; exist == "" {
|
if exist := j.From; exist == "" {
|
||||||
log.Warnf("Incorrect msg from redis, no from field: %s", msg)
|
log.Warnf("Incorrect msg from redis, no from field: %s", msg)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if exist := j.Chatid; exist == "" {
|
if exist := j.Chatid; exist == "" {
|
||||||
log.Warnf("Incorrect msg from redis, no chatid field: %s", msg)
|
log.Warnf("Incorrect msg from redis, no chatid field: %s", msg)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if exist := j.Userid; exist == "" {
|
if exist := j.Userid; exist == "" {
|
||||||
log.Warnf("Incorrect msg from redis, no userid field: %s", msg)
|
log.Warnf("Incorrect msg from redis, no userid field: %s", msg)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if exist := j.Message; exist == "" {
|
if exist := j.Message; exist == "" {
|
||||||
log.Warnf("Incorrect msg from redis, no message field: %s", msg)
|
log.Warnf("Incorrect msg from redis, no message field: %s", msg)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if exist := j.Plugin; exist == "" {
|
if exist := j.Plugin; exist == "" {
|
||||||
log.Warnf("Incorrect msg from redis, no plugin field: %s", msg)
|
log.Warnf("Incorrect msg from redis, no plugin field: %s", msg)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if exist := j.Mode; exist == "" {
|
if exist := j.Mode; exist == "" {
|
||||||
log.Warnf("Incorrect msg from redis, no mode field: %s", msg)
|
log.Warnf("Incorrect msg from redis, no mode field: %s", msg)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// j.Misc.Answer может и не быть, тогда ответа на такое сообщение не будет
|
// j.Misc.Answer может и не быть, тогда ответа на такое сообщение не будет
|
||||||
if j.Misc.Answer == 0 {
|
if j.Misc.Answer == 0 {
|
||||||
log.Debug("Field Misc->Answer = 0, skipping message")
|
log.Debug("Field Misc->Answer = 0, skipping message")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// j.Misc.BotNick тоже можно не передавать, тогда будет записана пустая строка
|
// j.Misc.BotNick тоже можно не передавать, тогда будет записана пустая строка
|
||||||
// j.Misc.CSign если нам его не передали, возьмём значение из конфига
|
// j.Misc.CSign если нам его не передали, возьмём значение из конфига
|
||||||
if exist := j.Misc.Csign; exist == "" {
|
if exist := j.Misc.Csign; exist == "" {
|
||||||
j.Misc.Csign = config.Csign
|
j.Misc.Csign = Config.Csign
|
||||||
}
|
}
|
||||||
|
|
||||||
// j.Misc.FwdCnt если нам его не передали, то будет 0
|
// j.Misc.FwdCnt если нам его не передали, то будет 0
|
||||||
@@ -435,13 +454,11 @@ func redisMsgParser(msg string) {
|
|||||||
|
|
||||||
for _, message := range lines {
|
for _, message := range lines {
|
||||||
if userModeIsOped(j.Chatid, ircClient.GetNick()) || userModeIsVoiced(j.Chatid, ircClient.GetNick()) {
|
if userModeIsOped(j.Chatid, ircClient.GetNick()) || userModeIsVoiced(j.Chatid, ircClient.GetNick()) {
|
||||||
imChanUnrestricted <- iMsg{ChatId: j.Chatid, Text: message}
|
imChanUnrestricted <- iMsg{ChatID: j.Chatid, Text: message}
|
||||||
} else {
|
} else {
|
||||||
imChan <- iMsg{ChatId: j.Chatid, Text: message}
|
imChan <- iMsg{ChatID: j.Chatid, Text: message}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package main
|
package irc
|
||||||
|
|
||||||
// Конфиг
|
// Конфиг.
|
||||||
type myConfig struct {
|
type MyConfig struct {
|
||||||
Redis struct {
|
Redis struct {
|
||||||
Server string `json:"server,omitempty"`
|
Server string `json:"server,omitempty"`
|
||||||
Port int `json:"port,omitempty"`
|
Port int `json:"port,omitempty"`
|
||||||
@@ -35,15 +35,16 @@ type myConfig struct {
|
|||||||
DataDir string `json:"data_dir,omitempty"`
|
DataDir string `json:"data_dir,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Входящее сообщение из pubsub-канала redis-ки
|
// Входящее сообщение из pubsub-канала redis-ки.
|
||||||
type rMsg struct {
|
type rMsg struct {
|
||||||
From string `json:"from,omitempty"`
|
From string `json:"from,omitempty"`
|
||||||
Chatid string `json:"chatid,omitempty"`
|
Chatid string `json:"chatid,omitempty"`
|
||||||
Userid string `json:"userid,omitempty"`
|
Userid string `json:"userid,omitempty"`
|
||||||
Message string `json:"message,omitempty"`
|
Threadid string `json:"threadid,omitempty"`
|
||||||
Plugin string `json:"plugin,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
Mode string `json:"mode,omitempty"`
|
Plugin string `json:"plugin,omitempty"`
|
||||||
Misc struct {
|
Mode string `json:"mode,omitempty"`
|
||||||
|
Misc struct {
|
||||||
Answer int64 `json:"answer,omitempty"`
|
Answer int64 `json:"answer,omitempty"`
|
||||||
Botnick string `json:"bot_nick,omitempty"`
|
Botnick string `json:"bot_nick,omitempty"`
|
||||||
Csign string `json:"csign,omitempty"`
|
Csign string `json:"csign,omitempty"`
|
||||||
@@ -54,15 +55,16 @@ type rMsg struct {
|
|||||||
} `json:"Misc"`
|
} `json:"Misc"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Исходящее сообщение в pubsub-канал redis-ки
|
// Исходящее сообщение в pubsub-канал redis-ки.
|
||||||
type sMsg struct {
|
type sMsg struct {
|
||||||
From string `json:"from"`
|
From string `json:"from"`
|
||||||
Chatid string `json:"chatid"`
|
Chatid string `json:"chatid"`
|
||||||
Userid string `json:"userid"`
|
Userid string `json:"userid"`
|
||||||
Message string `json:"message"`
|
Threadid string `json:"threadid"`
|
||||||
Plugin string `json:"plugin"`
|
Message string `json:"message"`
|
||||||
Mode string `json:"mode"`
|
Plugin string `json:"plugin"`
|
||||||
Misc struct {
|
Mode string `json:"mode"`
|
||||||
|
Misc struct {
|
||||||
Answer int64 `json:"answer"`
|
Answer int64 `json:"answer"`
|
||||||
Botnick string `json:"bot_nick"`
|
Botnick string `json:"bot_nick"`
|
||||||
Csign string `json:"csign"`
|
Csign string `json:"csign"`
|
||||||
@@ -73,14 +75,13 @@ type sMsg struct {
|
|||||||
} `json:"misc"`
|
} `json:"misc"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сообщение в irc
|
// TODO: Имплементировать тип сообщения - privmsg или action.
|
||||||
// TODO: Имплементировать тип сообщения - privmsg или action
|
|
||||||
type iMsg struct {
|
type iMsg struct {
|
||||||
ChatId string
|
ChatID string
|
||||||
Text string
|
Text string
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Ведёрко" для реализации ограничителя скорости отправки исходящих сообщений - Token Bucket
|
// "Ведёрко" для реализации ограничителя скорости отправки исходящих сообщений - Token Bucket.
|
||||||
type bucket struct {
|
type bucket struct {
|
||||||
Timestamps []int64
|
Timestamps []int64
|
||||||
IsFull bool
|
IsFull bool
|
||||||
@@ -1,24 +1,25 @@
|
|||||||
package main
|
package irc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"aleesa-irc-go/internal/log"
|
||||||
|
"aleesa-irc-go/internal/setting"
|
||||||
|
|
||||||
"github.com/hjson/hjson-go"
|
"github.com/hjson/hjson-go"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Читает и валидирует конфиг, а также выставляет некоторые default-ы, если значений для параметров в конфиге нет
|
// ReadConfig читает и валидирует конфиг, а также выставляет некоторые default-ы, если значений для параметров в конфиге нет.
|
||||||
func readConfig() {
|
func ReadConfig() {
|
||||||
configLoaded := false
|
configLoaded := false
|
||||||
executablePath, err := os.Executable()
|
executablePath, err := os.Executable()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to get current executable path: %s", err)
|
log.Error(fmt.Sprintf("Unable to get current executable path: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
configJSONPath := fmt.Sprintf("%s/data/config.json", filepath.Dir(executablePath))
|
configJSONPath := fmt.Sprintf("%s/data/config.json", filepath.Dir(executablePath))
|
||||||
@@ -41,27 +42,31 @@ func readConfig() {
|
|||||||
// Конфиг-файл длинноват для конфига, попробуем следующего кандидата
|
// Конфиг-файл длинноват для конфига, попробуем следующего кандидата
|
||||||
if fileInfo.Size() > 65535 {
|
if fileInfo.Size() > 65535 {
|
||||||
log.Warnf("Config file %s is too long for config, skipping", location)
|
log.Warnf("Config file %s is too long for config, skipping", location)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, err := ioutil.ReadFile(location)
|
buf, err := os.ReadFile(location)
|
||||||
|
|
||||||
// Не удалось прочитать, попробуем следующего кандидата
|
// Не удалось прочитать, попробуем следующего кандидата
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Skip reading config file %s: %s", location, err)
|
log.Warnf("Skip reading config file %s: %s", location, err)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Исходя из документации, hjson какбы умеет парсить "кривой" json, но парсит его в map-ку.
|
// Исходя из документации, hjson какбы умеет парсить "кривой" json, но парсит его в map-ку.
|
||||||
// Интереснее на выходе получить структурку: то есть мы вначале конфиг преобразуем в map-ку, затем эту map-ку
|
// Интереснее на выходе получить структурку: то есть мы вначале конфиг преобразуем в map-ку, затем эту map-ку
|
||||||
// сериализуем в json, а потом json преврщааем в стркутурку. Не очень эффективно, но он и не часто требуется.
|
// сериализуем в json, а потом json преврщааем в стркутурку. Не очень эффективно, но он и не часто требуется.
|
||||||
var sampleConfig myConfig
|
var sampleConfig MyConfig
|
||||||
|
|
||||||
var tmp map[string]interface{}
|
var tmp map[string]interface{}
|
||||||
err = hjson.Unmarshal(buf, &tmp)
|
err = hjson.Unmarshal(buf, &tmp)
|
||||||
|
|
||||||
// Не удалось распарсить - попробуем следующего кандидата
|
// Не удалось распарсить - попробуем следующего кандидата
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Skip parsing config file %s: %s", location, err)
|
log.Warnf("Skip parsing config file %s: %s", location, err)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,17 +75,20 @@ func readConfig() {
|
|||||||
// Не удалось преобразовать map-ку в json
|
// Не удалось преобразовать map-ку в json
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Skip parsing config file %s: %s", location, err)
|
log.Warnf("Skip parsing config file %s: %s", location, err)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(tmpjson, &sampleConfig); err != nil {
|
if err := json.Unmarshal(tmpjson, &sampleConfig); err != nil {
|
||||||
log.Warnf("Skip parsing config file %s: %s", location, err)
|
log.Warnf("Skip parsing config file %s: %s", location, err)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Валидируем значения из конфига
|
// Валидируем значения из конфига
|
||||||
if sampleConfig.Redis.Server == "" {
|
if sampleConfig.Redis.Server == "" {
|
||||||
sampleConfig.Redis.Server = "localhost"
|
sampleConfig.Redis.Server = "localhost"
|
||||||
|
|
||||||
log.Infof("Redis server is not defined in config, using localhost")
|
log.Infof("Redis server is not defined in config, using localhost")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,15 +109,17 @@ func readConfig() {
|
|||||||
// Значения для IRC-клиента
|
// Значения для IRC-клиента
|
||||||
if sampleConfig.Irc.Server == "" {
|
if sampleConfig.Irc.Server == "" {
|
||||||
sampleConfig.Irc.Server = "localhost"
|
sampleConfig.Irc.Server = "localhost"
|
||||||
|
|
||||||
log.Errorf("Irc server is not defined in config, using localhost")
|
log.Errorf("Irc server is not defined in config, using localhost")
|
||||||
}
|
}
|
||||||
|
|
||||||
if sampleConfig.Irc.Port == 0 {
|
if sampleConfig.Irc.Port == 0 {
|
||||||
sampleConfig.Irc.Port = 6667
|
sampleConfig.Irc.Port = 6667
|
||||||
|
|
||||||
log.Infof("Irc port is not defined in config, using 6667")
|
log.Infof("Irc port is not defined in config, using 6667")
|
||||||
}
|
}
|
||||||
|
|
||||||
if sampleConfig.Irc.Ssl != true {
|
if !sampleConfig.Irc.Ssl {
|
||||||
sampleConfig.Irc.SslVerify = false
|
sampleConfig.Irc.SslVerify = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,9 +190,11 @@ func readConfig() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
config = sampleConfig
|
Config = sampleConfig
|
||||||
configLoaded = true
|
configLoaded = true
|
||||||
|
|
||||||
log.Infof("Using %s as config file", location)
|
log.Infof("Using %s as config file", location)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,19 +204,19 @@ func readConfig() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Хэндлер сигналов закрывает все бд, все сетевые соединения и сваливает из приложения
|
// SigHandler хэндлер сигналов закрывает все бд, все сетевые соединения и сваливает из приложения.
|
||||||
func sigHandler() {
|
func SigHandler() {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var s = <-sigChan
|
var s = <-SigChan
|
||||||
switch s {
|
switch s {
|
||||||
case syscall.SIGINT:
|
case syscall.SIGINT:
|
||||||
log.Infoln("Got SIGINT, quitting")
|
log.Info("Got SIGINT, quitting")
|
||||||
case syscall.SIGTERM:
|
case syscall.SIGTERM:
|
||||||
log.Infoln("Got SIGTERM, quitting")
|
log.Info("Got SIGTERM, quitting")
|
||||||
case syscall.SIGQUIT:
|
case syscall.SIGQUIT:
|
||||||
log.Infoln("Got SIGQUIT, quitting")
|
log.Info("Got SIGQUIT, quitting")
|
||||||
|
|
||||||
// Заходим на новую итерацию, если у нас "неинтересный" сигнал
|
// Заходим на новую итерацию, если у нас "неинтересный" сигнал
|
||||||
default:
|
default:
|
||||||
@@ -212,16 +224,16 @@ func sigHandler() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Чтобы не срать в логи ошибками от редиски, проставим shutdown state приложения в true
|
// Чтобы не срать в логи ошибками от редиски, проставим shutdown state приложения в true
|
||||||
shutdown = true
|
Shutdown = true
|
||||||
|
|
||||||
// Отпишемся от всех каналов и закроем коннект к редиске
|
// Отпишемся от всех каналов и закроем коннект к редиске
|
||||||
if err = subscriber.Unsubscribe(ctx); err != nil {
|
if err = Subscriber.Unsubscribe(ctx); err != nil {
|
||||||
log.Errorf("Unable to unsubscribe from redis channels cleanly: %s", err)
|
log.Errorf("Unable to unsubscribe from redis channels cleanly: %s", err)
|
||||||
} else {
|
} else {
|
||||||
log.Debug("Unsubscribe from all redis channels")
|
log.Debug("Unsubscribe from all redis channels")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = subscriber.Close(); err != nil {
|
if err = Subscriber.Close(); err != nil {
|
||||||
log.Errorf("Unable to close redis connection cleanly: %s", err)
|
log.Errorf("Unable to close redis connection cleanly: %s", err)
|
||||||
} else {
|
} else {
|
||||||
log.Debug("Close redis connection")
|
log.Debug("Close redis connection")
|
||||||
@@ -229,16 +241,12 @@ func sigHandler() {
|
|||||||
|
|
||||||
log.Debug("Close irc connection")
|
log.Debug("Close irc connection")
|
||||||
ircClient.Quit()
|
ircClient.Quit()
|
||||||
|
|
||||||
log.Debug("Close userModeDB")
|
log.Debug("Close userModeDB")
|
||||||
userMode.Close()
|
UserMode.Close()
|
||||||
|
|
||||||
if len(settingsDB) > 0 {
|
log.Debug("Close settingsDB")
|
||||||
log.Debug("Closing runtime irc channel settings db")
|
setting.CloseAll()
|
||||||
|
|
||||||
for _, db := range settingsDB {
|
|
||||||
_ = db.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
106
internal/log/log.go
Normal file
106
internal/log/log.go
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init setup logger stuff.
|
||||||
|
// level can be error, warn, info, debug if something other supplied info level selected.
|
||||||
|
// fileDescriptor should be opened before supplying it to Init().
|
||||||
|
func Init(level string, fileDescriptor *os.File) {
|
||||||
|
var loglevel slog.Level
|
||||||
|
|
||||||
|
// no panic, no trace.
|
||||||
|
switch level {
|
||||||
|
case "error":
|
||||||
|
loglevel = slog.LevelError
|
||||||
|
|
||||||
|
case "warn":
|
||||||
|
loglevel = slog.LevelWarn
|
||||||
|
|
||||||
|
case "info":
|
||||||
|
loglevel = slog.LevelInfo
|
||||||
|
|
||||||
|
case "debug":
|
||||||
|
loglevel = slog.LevelDebug
|
||||||
|
|
||||||
|
default:
|
||||||
|
loglevel = slog.LevelInfo
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := &slog.HandlerOptions{
|
||||||
|
// Use the ReplaceAttr function on the handler options
|
||||||
|
// to be able to replace any single attribute in the log output
|
||||||
|
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
|
||||||
|
// check that we are handling the time key
|
||||||
|
if a.Key != slog.TimeKey {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
t := a.Value.Time()
|
||||||
|
|
||||||
|
// change the value from a time.Time to a String
|
||||||
|
// where the string has the correct time format.
|
||||||
|
a.Value = slog.StringValue(t.Format(time.DateTime))
|
||||||
|
|
||||||
|
return a
|
||||||
|
},
|
||||||
|
|
||||||
|
Level: loglevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.SetDefault(
|
||||||
|
slog.New(
|
||||||
|
slog.NewTextHandler(
|
||||||
|
fileDescriptor,
|
||||||
|
opts,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error passes message directly to slog.Error().
|
||||||
|
func Error(message string) {
|
||||||
|
slog.Error(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf logs message on error log level and allows to supply arguments in printf() style.
|
||||||
|
func Errorf(format string, a ...any) {
|
||||||
|
slog.Error(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn passes message directly to slog.Warn().
|
||||||
|
func Warn(message string) {
|
||||||
|
slog.Warn(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnf logs message on warn log level and allows to supply arguments in printf() style.
|
||||||
|
func Warnf(format string, a ...any) {
|
||||||
|
slog.Warn(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info passes message directly to slog.Info().
|
||||||
|
func Info(message string) {
|
||||||
|
slog.Info(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof logs message on info log level and allows to supply arguments in printf() style.
|
||||||
|
func Infof(format string, a ...any) {
|
||||||
|
slog.Info(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug passes message directly to slog.Debug().
|
||||||
|
func Debug(message string) {
|
||||||
|
slog.Debug(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugf logs message on debug log level and allows to supply arguments in printf() style.
|
||||||
|
func Debugf(format string, a ...any) {
|
||||||
|
slog.Debug(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
@@ -6,14 +6,20 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
|
||||||
|
"aleesa-irc-go/internal/log"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors/oserror"
|
"github.com/cockroachdb/errors/oserror"
|
||||||
"github.com/cockroachdb/pebble"
|
"github.com/cockroachdb/pebble"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// StoreKV сохраняет в указанной бд ключ и значение
|
// Мапка с открытыми дескрипторами баз с настройками.
|
||||||
|
var settingsDB = make(map[string]*pebble.DB)
|
||||||
|
var DataDir string
|
||||||
|
|
||||||
|
// StoreKV сохраняет в указанной бд ключ и значение.
|
||||||
func StoreKV(db *pebble.DB, key string, value string) error {
|
func StoreKV(db *pebble.DB, key string, value string) error {
|
||||||
var kArray = []byte(key)
|
var kArray = []byte(key)
|
||||||
|
|
||||||
var vArray = []byte(value)
|
var vArray = []byte(value)
|
||||||
|
|
||||||
err := db.Set(kArray, vArray, pebble.Sync)
|
err := db.Set(kArray, vArray, pebble.Sync)
|
||||||
@@ -21,11 +27,12 @@ func StoreKV(db *pebble.DB, key string, value string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchV достаёт значение по ключу
|
// FetchV достаёт значение по ключу.
|
||||||
func FetchV(db *pebble.DB, key string) (string, error) {
|
func FetchV(db *pebble.DB, key string) (string, error) {
|
||||||
var kArray = []byte(key)
|
var kArray = []byte(key)
|
||||||
|
|
||||||
var vArray []byte
|
var vArray []byte
|
||||||
|
|
||||||
var valueString = ""
|
var valueString = ""
|
||||||
|
|
||||||
vArray, closer, err := db.Get(kArray)
|
vArray, closer, err := db.Get(kArray)
|
||||||
@@ -40,8 +47,8 @@ func FetchV(db *pebble.DB, key string) (string, error) {
|
|||||||
return valueString, err
|
return valueString, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Достанем настройку из БД с настройками
|
// Достанем настройку из БД с настройками.
|
||||||
func getSetting(chatID string, setting string) string {
|
func Get(chatID string, setting string) string {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
chatHash := sha256.Sum256([]byte(chatID))
|
chatHash := sha256.Sum256([]byte(chatID))
|
||||||
@@ -54,10 +61,11 @@ func getSetting(chatID string, setting string) string {
|
|||||||
// настройками порождается огромное количество файлов. Умолчальное ограничение на количество файлов - 500 штук,
|
// настройками порождается огромное количество файлов. Умолчальное ограничение на количество файлов - 500 штук,
|
||||||
// что нас не устраивает, поэтому немного снизим эту цифру до более приемлемых значений
|
// что нас не устраивает, поэтому немного снизим эту цифру до более приемлемых значений
|
||||||
options.L0CompactionFileThreshold = 8
|
options.L0CompactionFileThreshold = 8
|
||||||
settingsDB[database], err = pebble.Open(config.DataDir+"/"+database, &options)
|
settingsDB[database], err = pebble.Open(DataDir+"/"+database, &options)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to open settings db, %s: %s\n", database, err)
|
log.Errorf("Unable to open settings db, %s: %s\n", database, err)
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,13 +74,14 @@ func getSetting(chatID string, setting string) string {
|
|||||||
|
|
||||||
// Если из базы ничего не вынулось, по каким-то причинам, то просто вернём пустую строку
|
// Если из базы ничего не вынулось, по каким-то причинам, то просто вернём пустую строку
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, pebble.ErrNotFound) {
|
switch {
|
||||||
|
case errors.Is(err, pebble.ErrNotFound):
|
||||||
log.Debugf("Unable to get value for %s: no record found in db %s", setting, database)
|
log.Debugf("Unable to get value for %s: no record found in db %s", setting, database)
|
||||||
} else if errors.Is(err, fs.ErrNotExist) {
|
case errors.Is(err, fs.ErrNotExist):
|
||||||
log.Debugf("Unable to get value for %s: db dir %s does not exist", setting, database)
|
log.Debugf("Unable to get value for %s: db dir %s does not exist", setting, database)
|
||||||
} else if errors.Is(err, oserror.ErrNotExist) {
|
case errors.Is(err, oserror.ErrNotExist):
|
||||||
log.Debugf("Unable to get value for %s: db dir %s does not exist", setting, database)
|
log.Debugf("Unable to get value for %s: db dir %s does not exist", setting, database)
|
||||||
} else {
|
default:
|
||||||
log.Errorf("Unable to get value for %s in db dir %s: %s", setting, database, err)
|
log.Errorf("Unable to get value for %s in db dir %s: %s", setting, database, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,10 +91,12 @@ func getSetting(chatID string, setting string) string {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сохраним настройку в БД с настройками
|
// Сохраним настройку в БД с настройками.
|
||||||
func saveSetting(chatID string, setting string, value string) error {
|
func Set(chatID string, setting string, value string) error {
|
||||||
var chatHash = sha256.Sum256([]byte(chatID))
|
var chatHash = sha256.Sum256([]byte(chatID))
|
||||||
|
|
||||||
var database = fmt.Sprintf("settings_db/%x", chatHash)
|
var database = fmt.Sprintf("settings_db/%x", chatHash)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Если БД не открыта, откроем её
|
// Если БД не открыта, откроем её
|
||||||
@@ -96,10 +107,11 @@ func saveSetting(chatID string, setting string, value string) error {
|
|||||||
// что нас не устраивает, поэтому немного снизим эту цифру до более приемлемых значений
|
// что нас не устраивает, поэтому немного снизим эту цифру до более приемлемых значений
|
||||||
options.L0CompactionFileThreshold = 8
|
options.L0CompactionFileThreshold = 8
|
||||||
|
|
||||||
settingsDB[database], err = pebble.Open(config.DataDir+"/"+database, &options)
|
settingsDB[database], err = pebble.Open(DataDir+"/"+database, &options)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to open settings db, %s: %s\n", database, err)
|
log.Errorf("Unable to open settings db, %s: %s\n", database, err)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,4 +123,15 @@ func saveSetting(chatID string, setting string, value string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Закрываем все инстансы баз с настройками.
|
||||||
|
func CloseAll() {
|
||||||
|
if len(settingsDB) > 0 {
|
||||||
|
log.Debug("Closing runtime irc channel settings db")
|
||||||
|
|
||||||
|
for _, db := range settingsDB {
|
||||||
|
_ = db.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
102
main.go
102
main.go
@@ -1,102 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/go-redis/redis/v8"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Производит некоторую инициализацию перед запуском main()
|
|
||||||
func init() {
|
|
||||||
log.SetFormatter(&log.TextFormatter{
|
|
||||||
DisableQuote: true,
|
|
||||||
DisableLevelTruncation: false,
|
|
||||||
DisableColors: true,
|
|
||||||
FullTimestamp: true,
|
|
||||||
TimestampFormat: "2006-01-02 15:04:05",
|
|
||||||
})
|
|
||||||
|
|
||||||
readConfig()
|
|
||||||
|
|
||||||
// no panic, no trace
|
|
||||||
switch config.Loglevel {
|
|
||||||
case "fatal":
|
|
||||||
log.SetLevel(log.FatalLevel)
|
|
||||||
case "error":
|
|
||||||
log.SetLevel(log.ErrorLevel)
|
|
||||||
case "warn":
|
|
||||||
log.SetLevel(log.WarnLevel)
|
|
||||||
case "info":
|
|
||||||
log.SetLevel(log.InfoLevel)
|
|
||||||
case "debug":
|
|
||||||
log.SetLevel(log.DebugLevel)
|
|
||||||
default:
|
|
||||||
log.SetLevel(log.InfoLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Проинициализируем мапку MODE-ами пользователей произвольным значением и удалим его
|
|
||||||
var usersModes = map[string]map[string]map[string]bool{
|
|
||||||
"user": {
|
|
||||||
"mode": {
|
|
||||||
"v": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
userMode.Set("channel", usersModes)
|
|
||||||
userMode.Delete("channel")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Собственно, какбэ "точка входа" - основная процедура в нашем боте
|
|
||||||
func main() {
|
|
||||||
// Main context
|
|
||||||
var ctx = context.Background()
|
|
||||||
|
|
||||||
// Откроем лог и скормим его логгеру
|
|
||||||
if config.Log != "" {
|
|
||||||
logfile, err := os.OpenFile(config.Log, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Unable to open log file %s: %s", config.Log, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.SetOutput(logfile)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Иницализируем redis-клиента
|
|
||||||
redisClient = redis.NewClient(&redis.Options{
|
|
||||||
Addr: fmt.Sprintf("%s:%d", config.Redis.Server, config.Redis.Port),
|
|
||||||
})
|
|
||||||
|
|
||||||
log.Debugf("Lazy connect() to redis at %s:%d", config.Redis.Server, config.Redis.Port)
|
|
||||||
subscriber = redisClient.Subscribe(ctx, config.Redis.MyChannel)
|
|
||||||
redisMsgChan := subscriber.Channel()
|
|
||||||
|
|
||||||
go ircClientRun()
|
|
||||||
go ircSend()
|
|
||||||
go ircSendUnrestricted()
|
|
||||||
|
|
||||||
// Самое время поставить траппер сигналов
|
|
||||||
signal.Notify(sigChan,
|
|
||||||
syscall.SIGINT,
|
|
||||||
syscall.SIGTERM,
|
|
||||||
syscall.SIGQUIT)
|
|
||||||
|
|
||||||
go sigHandler()
|
|
||||||
|
|
||||||
// Обработчик событий от редиски
|
|
||||||
for msg := range redisMsgChan {
|
|
||||||
if shutdown {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
redisMsgParser(msg.Payload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
|
||||||
45
vendor/github.com/HdrHistogram/hdrhistogram-go/Makefile
generated
vendored
45
vendor/github.com/HdrHistogram/hdrhistogram-go/Makefile
generated
vendored
@@ -1,45 +0,0 @@
|
|||||||
# Go parameters
|
|
||||||
GOCMD=GO111MODULE=on go
|
|
||||||
GOBUILD=$(GOCMD) build
|
|
||||||
GOINSTALL=$(GOCMD) install
|
|
||||||
GOCLEAN=$(GOCMD) clean
|
|
||||||
GOTEST=$(GOCMD) test
|
|
||||||
GOGET=$(GOCMD) get
|
|
||||||
GOMOD=$(GOCMD) mod
|
|
||||||
GOFMT=$(GOCMD) fmt
|
|
||||||
GODOC=godoc
|
|
||||||
|
|
||||||
.PHONY: all test coverage
|
|
||||||
all: test
|
|
||||||
|
|
||||||
checkfmt:
|
|
||||||
@echo 'Checking gofmt';\
|
|
||||||
bash -c "diff -u <(echo -n) <(gofmt -d .)";\
|
|
||||||
EXIT_CODE=$$?;\
|
|
||||||
if [ "$$EXIT_CODE" -ne 0 ]; then \
|
|
||||||
echo '$@: Go files must be formatted with gofmt'; \
|
|
||||||
fi && \
|
|
||||||
exit $$EXIT_CODE
|
|
||||||
|
|
||||||
lint:
|
|
||||||
$(GOGET) github.com/golangci/golangci-lint/cmd/golangci-lint
|
|
||||||
golangci-lint run
|
|
||||||
|
|
||||||
get:
|
|
||||||
$(GOGET) -v ./...
|
|
||||||
|
|
||||||
fmt:
|
|
||||||
$(GOFMT) ./...
|
|
||||||
|
|
||||||
test: get fmt
|
|
||||||
$(GOTEST) -count=1 ./...
|
|
||||||
|
|
||||||
coverage: get test
|
|
||||||
$(GOTEST) -count=1 -race -coverprofile=coverage.txt -covermode=atomic .
|
|
||||||
|
|
||||||
benchmark: get
|
|
||||||
$(GOTEST) -bench=. -benchmem
|
|
||||||
|
|
||||||
godoc:
|
|
||||||
$(GODOC)
|
|
||||||
|
|
||||||
78
vendor/github.com/HdrHistogram/hdrhistogram-go/README.md
generated
vendored
78
vendor/github.com/HdrHistogram/hdrhistogram-go/README.md
generated
vendored
@@ -1,78 +0,0 @@
|
|||||||
hdrhistogram-go
|
|
||||||
===============
|
|
||||||
|
|
||||||
<a href="https://pkg.go.dev/github.com/HdrHistogram/hdrhistogram-go"><img src="https://pkg.go.dev/badge/github.com/HdrHistogram/hdrhistogram-go" alt="PkgGoDev"></a>
|
|
||||||
[](https://gitter.im/HdrHistogram/HdrHistogram)
|
|
||||||

|
|
||||||
[](https://github.com/HdrHistogram/hdrhistogram-go/blob/master/LICENSE)
|
|
||||||
[](https://codecov.io/gh/HdrHistogram/hdrhistogram-go)
|
|
||||||
|
|
||||||
|
|
||||||
A pure Go implementation of the [HDR Histogram](https://github.com/HdrHistogram/HdrHistogram).
|
|
||||||
|
|
||||||
> A Histogram that supports recording and analyzing sampled data value counts
|
|
||||||
> across a configurable integer value range with configurable value precision
|
|
||||||
> within the range. Value precision is expressed as the number of significant
|
|
||||||
> digits in the value recording, and provides control over value quantization
|
|
||||||
> behavior across the value range and the subsequent value resolution at any
|
|
||||||
> given level.
|
|
||||||
|
|
||||||
For documentation, check [godoc](https://pkg.go.dev/github.com/HdrHistogram/hdrhistogram-go).
|
|
||||||
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
### Installing
|
|
||||||
Use `go get` to retrieve the hdrhistogram-go implementation and to add it to your `GOPATH` workspace, or project's Go module dependencies.
|
|
||||||
|
|
||||||
```go
|
|
||||||
go get github.com/HdrHistogram/hdrhistogram-go
|
|
||||||
```
|
|
||||||
|
|
||||||
To update the implementation use `go get -u` to retrieve the latest version of the hdrhistogram.
|
|
||||||
|
|
||||||
```go
|
|
||||||
go get github.com/HdrHistogram/hdrhistogram-go
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Go Modules
|
|
||||||
|
|
||||||
If you are using Go modules, your `go get` will default to the latest tagged
|
|
||||||
release version of the histogram. To get a specific release version, use
|
|
||||||
`@<tag>` in your `go get` command.
|
|
||||||
|
|
||||||
```go
|
|
||||||
go get github.com/HdrHistogram/hdrhistogram-go@v0.9.0
|
|
||||||
```
|
|
||||||
|
|
||||||
To get the latest HdrHistogram/hdrhistogram-go master repository change use `@latest`.
|
|
||||||
|
|
||||||
```go
|
|
||||||
go get github.com/HdrHistogram/hdrhistogram-go@latest
|
|
||||||
```
|
|
||||||
|
|
||||||
### Repo transfer and impact on go dependencies
|
|
||||||
-------------------------------------------
|
|
||||||
This repository has been transferred under the github HdrHstogram umbrella with the help from the orginal
|
|
||||||
author in Sept 2020. The main reasons are to group all implementations under the same roof and to provide more active contribution
|
|
||||||
from the community as the orginal repository was archived several years ago.
|
|
||||||
|
|
||||||
Unfortunately such URL change will break go applications that depend on this library
|
|
||||||
directly or indirectly, as discussed [here](https://github.com/HdrHistogram/hdrhistogram-go/issues/30#issuecomment-696365251).
|
|
||||||
|
|
||||||
The dependency URL should be modified to point to the new repository URL.
|
|
||||||
The tag "v0.9.0" was applied at the point of transfer and will reflect the exact code that was frozen in the
|
|
||||||
original repository.
|
|
||||||
|
|
||||||
If you are using Go modules, you can update to the exact point of transfter using the `@v0.9.0` tag in your `go get` command.
|
|
||||||
|
|
||||||
```
|
|
||||||
go mod edit -replace github.com/codahale/hdrhistogram=github.com/HdrHistogram/hdrhistogram-go@v0.9.0
|
|
||||||
```
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
-------
|
|
||||||
|
|
||||||
Many thanks for Coda Hale for contributing the initial implementation and transfering the repository here.
|
|
||||||
|
|
||||||
782
vendor/github.com/HdrHistogram/hdrhistogram-go/hdr.go
generated
vendored
782
vendor/github.com/HdrHistogram/hdrhistogram-go/hdr.go
generated
vendored
@@ -1,782 +0,0 @@
|
|||||||
// Package hdrhistogram provides an implementation of Gil Tene's HDR Histogram
|
|
||||||
// data structure. The HDR Histogram allows for fast and accurate analysis of
|
|
||||||
// the extreme ranges of data with non-normal distributions, like latency.
|
|
||||||
package hdrhistogram
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
"math/bits"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Bracket is a part of a cumulative distribution.
|
|
||||||
type Bracket struct {
|
|
||||||
Quantile float64
|
|
||||||
Count, ValueAt int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Snapshot is an exported view of a Histogram, useful for serializing them.
|
|
||||||
// A Histogram can be constructed from it by passing it to Import.
|
|
||||||
type Snapshot struct {
|
|
||||||
LowestTrackableValue int64
|
|
||||||
HighestTrackableValue int64
|
|
||||||
SignificantFigures int64
|
|
||||||
Counts []int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Histogram is a lossy data structure used to record the distribution of
|
|
||||||
// non-normally distributed data (like latency) with a high degree of accuracy
|
|
||||||
// and a bounded degree of precision.
|
|
||||||
type Histogram struct {
|
|
||||||
lowestDiscernibleValue int64
|
|
||||||
highestTrackableValue int64
|
|
||||||
unitMagnitude int64
|
|
||||||
significantFigures int64
|
|
||||||
subBucketHalfCountMagnitude int32
|
|
||||||
subBucketHalfCount int32
|
|
||||||
subBucketMask int64
|
|
||||||
subBucketCount int32
|
|
||||||
bucketCount int32
|
|
||||||
countsLen int32
|
|
||||||
totalCount int64
|
|
||||||
counts []int64
|
|
||||||
startTimeMs int64
|
|
||||||
endTimeMs int64
|
|
||||||
tag string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) Tag() string {
|
|
||||||
return h.tag
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) SetTag(tag string) {
|
|
||||||
h.tag = tag
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) EndTimeMs() int64 {
|
|
||||||
return h.endTimeMs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) SetEndTimeMs(endTimeMs int64) {
|
|
||||||
h.endTimeMs = endTimeMs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) StartTimeMs() int64 {
|
|
||||||
return h.startTimeMs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) SetStartTimeMs(startTimeMs int64) {
|
|
||||||
h.startTimeMs = startTimeMs
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct a Histogram given the Lowest and Highest values to be tracked and a number of significant decimal digits.
|
|
||||||
//
|
|
||||||
// Providing a lowestDiscernibleValue is useful in situations where the units used for the histogram's values are
|
|
||||||
// much smaller that the minimal accuracy required.
|
|
||||||
// E.g. when tracking time values stated in nanosecond units, where the minimal accuracy required is a microsecond,
|
|
||||||
// the proper value for lowestDiscernibleValue would be 1000.
|
|
||||||
//
|
|
||||||
// Note: the numberOfSignificantValueDigits must be [1,5]. If lower than 1 the numberOfSignificantValueDigits will be
|
|
||||||
// forced to 1, and if higher than 5 the numberOfSignificantValueDigits will be forced to 5.
|
|
||||||
func New(lowestDiscernibleValue, highestTrackableValue int64, numberOfSignificantValueDigits int) *Histogram {
|
|
||||||
if numberOfSignificantValueDigits < 1 {
|
|
||||||
numberOfSignificantValueDigits = 1
|
|
||||||
} else if numberOfSignificantValueDigits > 5 {
|
|
||||||
numberOfSignificantValueDigits = 5
|
|
||||||
}
|
|
||||||
if lowestDiscernibleValue < 1 {
|
|
||||||
lowestDiscernibleValue = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given a 3 decimal point accuracy, the expectation is obviously for "+/- 1 unit at 1000". It also means that
|
|
||||||
// it's "ok to be +/- 2 units at 2000". The "tricky" thing is that it is NOT ok to be +/- 2 units at 1999. Only
|
|
||||||
// starting at 2000. So internally, we need to maintain single unit resolution to 2x 10^decimalPoints.
|
|
||||||
largestValueWithSingleUnitResolution := 2 * math.Pow10(numberOfSignificantValueDigits)
|
|
||||||
|
|
||||||
// We need to maintain power-of-two subBucketCount (for clean direct indexing) that is large enough to
|
|
||||||
// provide unit resolution to at least largestValueWithSingleUnitResolution. So figure out
|
|
||||||
// largestValueWithSingleUnitResolution's nearest power-of-two (rounded up), and use that:
|
|
||||||
subBucketCountMagnitude := int32(math.Ceil(math.Log2(float64(largestValueWithSingleUnitResolution))))
|
|
||||||
subBucketHalfCountMagnitude := subBucketCountMagnitude
|
|
||||||
if subBucketHalfCountMagnitude < 1 {
|
|
||||||
subBucketHalfCountMagnitude = 1
|
|
||||||
}
|
|
||||||
subBucketHalfCountMagnitude--
|
|
||||||
|
|
||||||
unitMagnitude := int32(math.Floor(math.Log2(float64(lowestDiscernibleValue))))
|
|
||||||
if unitMagnitude < 0 {
|
|
||||||
unitMagnitude = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
subBucketCount := int32(math.Pow(2, float64(subBucketHalfCountMagnitude)+1))
|
|
||||||
|
|
||||||
subBucketHalfCount := subBucketCount / 2
|
|
||||||
subBucketMask := int64(subBucketCount-1) << uint(unitMagnitude)
|
|
||||||
|
|
||||||
// determine exponent range needed to support the trackable value with no
|
|
||||||
// overflow:
|
|
||||||
smallestUntrackableValue := int64(subBucketCount) << uint(unitMagnitude)
|
|
||||||
bucketsNeeded := getBucketsNeededToCoverValue(smallestUntrackableValue, highestTrackableValue)
|
|
||||||
|
|
||||||
bucketCount := bucketsNeeded
|
|
||||||
countsLen := (bucketCount + 1) * (subBucketCount / 2)
|
|
||||||
|
|
||||||
return &Histogram{
|
|
||||||
lowestDiscernibleValue: lowestDiscernibleValue,
|
|
||||||
highestTrackableValue: highestTrackableValue,
|
|
||||||
unitMagnitude: int64(unitMagnitude),
|
|
||||||
significantFigures: int64(numberOfSignificantValueDigits),
|
|
||||||
subBucketHalfCountMagnitude: subBucketHalfCountMagnitude,
|
|
||||||
subBucketHalfCount: subBucketHalfCount,
|
|
||||||
subBucketMask: subBucketMask,
|
|
||||||
subBucketCount: subBucketCount,
|
|
||||||
bucketCount: bucketCount,
|
|
||||||
countsLen: countsLen,
|
|
||||||
totalCount: 0,
|
|
||||||
counts: make([]int64, countsLen),
|
|
||||||
startTimeMs: 0,
|
|
||||||
endTimeMs: 0,
|
|
||||||
tag: "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBucketsNeededToCoverValue(smallestUntrackableValue int64, maxValue int64) int32 {
|
|
||||||
// always have at least 1 bucket
|
|
||||||
bucketsNeeded := int32(1)
|
|
||||||
for smallestUntrackableValue < maxValue {
|
|
||||||
if smallestUntrackableValue > (math.MaxInt64 / 2) {
|
|
||||||
// next shift will overflow, meaning that bucket could represent values up to ones greater than
|
|
||||||
// math.MaxInt64, so it's the last bucket
|
|
||||||
return bucketsNeeded + 1
|
|
||||||
}
|
|
||||||
smallestUntrackableValue <<= 1
|
|
||||||
bucketsNeeded++
|
|
||||||
}
|
|
||||||
return bucketsNeeded
|
|
||||||
}
|
|
||||||
|
|
||||||
// ByteSize returns an estimate of the amount of memory allocated to the
|
|
||||||
// histogram in bytes.
|
|
||||||
//
|
|
||||||
// N.B.: This does not take into account the overhead for slices, which are
|
|
||||||
// small, constant, and specific to the compiler version.
|
|
||||||
func (h *Histogram) ByteSize() int {
|
|
||||||
return 6*8 + 5*4 + len(h.counts)*8
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) getNormalizingIndexOffset() int32 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge merges the data stored in the given histogram with the receiver,
|
|
||||||
// returning the number of recorded values which had to be dropped.
|
|
||||||
func (h *Histogram) Merge(from *Histogram) (dropped int64) {
|
|
||||||
i := from.rIterator()
|
|
||||||
for i.next() {
|
|
||||||
v := i.valueFromIdx
|
|
||||||
c := i.countAtIdx
|
|
||||||
|
|
||||||
if h.RecordValues(v, c) != nil {
|
|
||||||
dropped += c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TotalCount returns total number of values recorded.
|
|
||||||
func (h *Histogram) TotalCount() int64 {
|
|
||||||
return h.totalCount
|
|
||||||
}
|
|
||||||
|
|
||||||
// Max returns the approximate maximum recorded value.
|
|
||||||
func (h *Histogram) Max() int64 {
|
|
||||||
var max int64
|
|
||||||
i := h.iterator()
|
|
||||||
for i.next() {
|
|
||||||
if i.countAtIdx != 0 {
|
|
||||||
max = i.highestEquivalentValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return h.highestEquivalentValue(max)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Min returns the approximate minimum recorded value.
|
|
||||||
func (h *Histogram) Min() int64 {
|
|
||||||
var min int64
|
|
||||||
i := h.iterator()
|
|
||||||
for i.next() {
|
|
||||||
if i.countAtIdx != 0 && min == 0 {
|
|
||||||
min = i.highestEquivalentValue
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return h.lowestEquivalentValue(min)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mean returns the approximate arithmetic mean of the recorded values.
|
|
||||||
func (h *Histogram) Mean() float64 {
|
|
||||||
if h.totalCount == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var total int64
|
|
||||||
i := h.iterator()
|
|
||||||
for i.next() {
|
|
||||||
if i.countAtIdx != 0 {
|
|
||||||
total += i.countAtIdx * h.medianEquivalentValue(i.valueFromIdx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return float64(total) / float64(h.totalCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StdDev returns the approximate standard deviation of the recorded values.
|
|
||||||
func (h *Histogram) StdDev() float64 {
|
|
||||||
if h.totalCount == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
mean := h.Mean()
|
|
||||||
geometricDevTotal := 0.0
|
|
||||||
|
|
||||||
i := h.iterator()
|
|
||||||
for i.next() {
|
|
||||||
if i.countAtIdx != 0 {
|
|
||||||
dev := float64(h.medianEquivalentValue(i.valueFromIdx)) - mean
|
|
||||||
geometricDevTotal += (dev * dev) * float64(i.countAtIdx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return math.Sqrt(geometricDevTotal / float64(h.totalCount))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset deletes all recorded values and restores the histogram to its original
|
|
||||||
// state.
|
|
||||||
func (h *Histogram) Reset() {
|
|
||||||
h.totalCount = 0
|
|
||||||
for i := range h.counts {
|
|
||||||
h.counts[i] = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RecordValue records the given value, returning an error if the value is out
|
|
||||||
// of range.
|
|
||||||
func (h *Histogram) RecordValue(v int64) error {
|
|
||||||
return h.RecordValues(v, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RecordCorrectedValue records the given value, correcting for stalls in the
|
|
||||||
// recording process. This only works for processes which are recording values
|
|
||||||
// at an expected interval (e.g., doing jitter analysis). Processes which are
|
|
||||||
// recording ad-hoc values (e.g., latency for incoming requests) can't take
|
|
||||||
// advantage of this.
|
|
||||||
func (h *Histogram) RecordCorrectedValue(v, expectedInterval int64) error {
|
|
||||||
if err := h.RecordValue(v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if expectedInterval <= 0 || v <= expectedInterval {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
missingValue := v - expectedInterval
|
|
||||||
for missingValue >= expectedInterval {
|
|
||||||
if err := h.RecordValue(missingValue); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
missingValue -= expectedInterval
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RecordValues records n occurrences of the given value, returning an error if
|
|
||||||
// the value is out of range.
|
|
||||||
func (h *Histogram) RecordValues(v, n int64) error {
|
|
||||||
idx := h.countsIndexFor(v)
|
|
||||||
if idx < 0 || int(h.countsLen) <= idx {
|
|
||||||
return fmt.Errorf("value %d is too large to be recorded", v)
|
|
||||||
}
|
|
||||||
h.setCountAtIndex(idx, n)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) setCountAtIndex(idx int, n int64) {
|
|
||||||
h.counts[idx] += n
|
|
||||||
h.totalCount += n
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValueAtQuantile returns the largest value that (100% - percentile) of the overall recorded value entries
|
|
||||||
// in the histogram are either larger than or equivalent to.
|
|
||||||
//
|
|
||||||
// The passed quantile must be a float64 value in [0.0 .. 100.0]
|
|
||||||
// Note that two values are "equivalent" if `ValuesAreEquivalent(value1,value2)` would return true.
|
|
||||||
//
|
|
||||||
// Returns 0 if no recorded values exist.
|
|
||||||
func (h *Histogram) ValueAtQuantile(q float64) int64 {
|
|
||||||
return h.ValueAtPercentile(q)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValueAtPercentile returns the largest value that (100% - percentile) of the overall recorded value entries
|
|
||||||
// in the histogram are either larger than or equivalent to.
|
|
||||||
//
|
|
||||||
// The passed percentile must be a float64 value in [0.0 .. 100.0]
|
|
||||||
// Note that two values are "equivalent" if `ValuesAreEquivalent(value1,value2)` would return true.
|
|
||||||
//
|
|
||||||
// Returns 0 if no recorded values exist.
|
|
||||||
func (h *Histogram) ValueAtPercentile(percentile float64) int64 {
|
|
||||||
if percentile > 100 {
|
|
||||||
percentile = 100
|
|
||||||
}
|
|
||||||
|
|
||||||
countAtPercentile := int64(((percentile / 100) * float64(h.totalCount)) + 0.5)
|
|
||||||
valueFromIdx := h.getValueFromIdxUpToCount(countAtPercentile)
|
|
||||||
if percentile == 0.0 {
|
|
||||||
return h.lowestEquivalentValue(valueFromIdx)
|
|
||||||
}
|
|
||||||
return h.highestEquivalentValue(valueFromIdx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) getValueFromIdxUpToCount(countAtPercentile int64) int64 {
|
|
||||||
var countToIdx int64
|
|
||||||
var valueFromIdx int64
|
|
||||||
var subBucketIdx int32 = -1
|
|
||||||
var bucketIdx int32
|
|
||||||
bucketBaseIdx := h.getBucketBaseIdx(bucketIdx)
|
|
||||||
|
|
||||||
for {
|
|
||||||
if countToIdx >= countAtPercentile {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// increment bucket
|
|
||||||
subBucketIdx++
|
|
||||||
if subBucketIdx >= h.subBucketCount {
|
|
||||||
subBucketIdx = h.subBucketHalfCount
|
|
||||||
bucketIdx++
|
|
||||||
bucketBaseIdx = h.getBucketBaseIdx(bucketIdx)
|
|
||||||
}
|
|
||||||
|
|
||||||
countToIdx += h.getCountAtIndexGivenBucketBaseIdx(bucketBaseIdx, subBucketIdx)
|
|
||||||
valueFromIdx = int64(subBucketIdx) << uint(int64(bucketIdx)+h.unitMagnitude)
|
|
||||||
}
|
|
||||||
return valueFromIdx
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValueAtPercentiles, given an slice of percentiles returns a map containing for each passed percentile,
|
|
||||||
// the largest value that (100% - percentile) of the overall recorded value entries
|
|
||||||
// in the histogram are either larger than or equivalent to.
|
|
||||||
//
|
|
||||||
// Each element in the given an slice of percentiles must be a float64 value in [0.0 .. 100.0]
|
|
||||||
// Note that two values are "equivalent" if `ValuesAreEquivalent(value1,value2)` would return true.
|
|
||||||
//
|
|
||||||
// Returns a map of 0's if no recorded values exist.
|
|
||||||
func (h *Histogram) ValueAtPercentiles(percentiles []float64) (values map[float64]int64) {
|
|
||||||
sort.Float64s(percentiles)
|
|
||||||
totalQuantilesToCalculate := len(percentiles)
|
|
||||||
values = make(map[float64]int64, totalQuantilesToCalculate)
|
|
||||||
countAtPercentiles := make([]int64, totalQuantilesToCalculate)
|
|
||||||
for i, percentile := range percentiles {
|
|
||||||
if percentile > 100 {
|
|
||||||
percentile = 100
|
|
||||||
}
|
|
||||||
values[percentile] = 0
|
|
||||||
countAtPercentiles[i] = int64(((percentile / 100) * float64(h.totalCount)) + 0.5)
|
|
||||||
}
|
|
||||||
|
|
||||||
total := int64(0)
|
|
||||||
currentQuantileSlicePos := 0
|
|
||||||
i := h.iterator()
|
|
||||||
for currentQuantileSlicePos < totalQuantilesToCalculate && i.nextCountAtIdx(h.totalCount) {
|
|
||||||
total += i.countAtIdx
|
|
||||||
for currentQuantileSlicePos < totalQuantilesToCalculate && total >= countAtPercentiles[currentQuantileSlicePos] {
|
|
||||||
currentPercentile := percentiles[currentQuantileSlicePos]
|
|
||||||
if currentPercentile == 0.0 {
|
|
||||||
values[currentPercentile] = h.lowestEquivalentValue(i.valueFromIdx)
|
|
||||||
} else {
|
|
||||||
values[currentPercentile] = h.highestEquivalentValue(i.valueFromIdx)
|
|
||||||
}
|
|
||||||
currentQuantileSlicePos++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if two values are equivalent with the histogram's resolution.
|
|
||||||
// Where "equivalent" means that value samples recorded for any two
|
|
||||||
// equivalent values are counted in a common total count.
|
|
||||||
func (h *Histogram) ValuesAreEquivalent(value1, value2 int64) (result bool) {
|
|
||||||
result = h.lowestEquivalentValue(value1) == h.lowestEquivalentValue(value2)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// CumulativeDistribution returns an ordered list of brackets of the
|
|
||||||
// distribution of recorded values.
|
|
||||||
func (h *Histogram) CumulativeDistribution() []Bracket {
|
|
||||||
var result []Bracket
|
|
||||||
|
|
||||||
i := h.pIterator(1)
|
|
||||||
for i.next() {
|
|
||||||
result = append(result, Bracket{
|
|
||||||
Quantile: i.percentile,
|
|
||||||
Count: i.countToIdx,
|
|
||||||
ValueAt: i.highestEquivalentValue,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignificantFigures returns the significant figures used to create the
|
|
||||||
// histogram
|
|
||||||
func (h *Histogram) SignificantFigures() int64 {
|
|
||||||
return h.significantFigures
|
|
||||||
}
|
|
||||||
|
|
||||||
// LowestTrackableValue returns the lower bound on values that will be added
|
|
||||||
// to the histogram
|
|
||||||
func (h *Histogram) LowestTrackableValue() int64 {
|
|
||||||
return h.lowestDiscernibleValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// HighestTrackableValue returns the upper bound on values that will be added
|
|
||||||
// to the histogram
|
|
||||||
func (h *Histogram) HighestTrackableValue() int64 {
|
|
||||||
return h.highestTrackableValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Histogram bar for plotting
|
|
||||||
type Bar struct {
|
|
||||||
From, To, Count int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pretty print as csv for easy plotting
|
|
||||||
func (b Bar) String() string {
|
|
||||||
return fmt.Sprintf("%v, %v, %v\n", b.From, b.To, b.Count)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Distribution returns an ordered list of bars of the
|
|
||||||
// distribution of recorded values, counts can be normalized to a probability
|
|
||||||
func (h *Histogram) Distribution() (result []Bar) {
|
|
||||||
i := h.iterator()
|
|
||||||
for i.next() {
|
|
||||||
result = append(result, Bar{
|
|
||||||
Count: i.countAtIdx,
|
|
||||||
From: h.lowestEquivalentValue(i.valueFromIdx),
|
|
||||||
To: i.highestEquivalentValue,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals returns true if the two Histograms are equivalent, false if not.
|
|
||||||
func (h *Histogram) Equals(other *Histogram) bool {
|
|
||||||
switch {
|
|
||||||
case
|
|
||||||
h.lowestDiscernibleValue != other.lowestDiscernibleValue,
|
|
||||||
h.highestTrackableValue != other.highestTrackableValue,
|
|
||||||
h.unitMagnitude != other.unitMagnitude,
|
|
||||||
h.significantFigures != other.significantFigures,
|
|
||||||
h.subBucketHalfCountMagnitude != other.subBucketHalfCountMagnitude,
|
|
||||||
h.subBucketHalfCount != other.subBucketHalfCount,
|
|
||||||
h.subBucketMask != other.subBucketMask,
|
|
||||||
h.subBucketCount != other.subBucketCount,
|
|
||||||
h.bucketCount != other.bucketCount,
|
|
||||||
h.countsLen != other.countsLen,
|
|
||||||
h.totalCount != other.totalCount:
|
|
||||||
return false
|
|
||||||
default:
|
|
||||||
for i, c := range h.counts {
|
|
||||||
if c != other.counts[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export returns a snapshot view of the Histogram. This can be later passed to
|
|
||||||
// Import to construct a new Histogram with the same state.
|
|
||||||
func (h *Histogram) Export() *Snapshot {
|
|
||||||
return &Snapshot{
|
|
||||||
LowestTrackableValue: h.lowestDiscernibleValue,
|
|
||||||
HighestTrackableValue: h.highestTrackableValue,
|
|
||||||
SignificantFigures: h.significantFigures,
|
|
||||||
Counts: append([]int64(nil), h.counts...), // copy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Import returns a new Histogram populated from the Snapshot data (which the
|
|
||||||
// caller must stop accessing).
|
|
||||||
func Import(s *Snapshot) *Histogram {
|
|
||||||
h := New(s.LowestTrackableValue, s.HighestTrackableValue, int(s.SignificantFigures))
|
|
||||||
h.counts = s.Counts
|
|
||||||
totalCount := int64(0)
|
|
||||||
for i := int32(0); i < h.countsLen; i++ {
|
|
||||||
countAtIndex := h.counts[i]
|
|
||||||
if countAtIndex > 0 {
|
|
||||||
totalCount += countAtIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h.totalCount = totalCount
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) iterator() *iterator {
|
|
||||||
return &iterator{
|
|
||||||
h: h,
|
|
||||||
subBucketIdx: -1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) rIterator() *rIterator {
|
|
||||||
return &rIterator{
|
|
||||||
iterator: iterator{
|
|
||||||
h: h,
|
|
||||||
subBucketIdx: -1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) pIterator(ticksPerHalfDistance int32) *pIterator {
|
|
||||||
return &pIterator{
|
|
||||||
iterator: iterator{
|
|
||||||
h: h,
|
|
||||||
subBucketIdx: -1,
|
|
||||||
},
|
|
||||||
ticksPerHalfDistance: ticksPerHalfDistance,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) sizeOfEquivalentValueRange(v int64) int64 {
|
|
||||||
bucketIdx := h.getBucketIndex(v)
|
|
||||||
return h.sizeOfEquivalentValueRangeGivenBucketIdx(v, bucketIdx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) sizeOfEquivalentValueRangeGivenBucketIdx(v int64, bucketIdx int32) int64 {
|
|
||||||
subBucketIdx := h.getSubBucketIdx(v, bucketIdx)
|
|
||||||
adjustedBucket := bucketIdx
|
|
||||||
if subBucketIdx >= h.subBucketCount {
|
|
||||||
adjustedBucket++
|
|
||||||
}
|
|
||||||
return int64(1) << uint(h.unitMagnitude+int64(adjustedBucket))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) valueFromIndex(bucketIdx, subBucketIdx int32) int64 {
|
|
||||||
return int64(subBucketIdx) << uint(int64(bucketIdx)+h.unitMagnitude)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) lowestEquivalentValue(v int64) int64 {
|
|
||||||
bucketIdx := h.getBucketIndex(v)
|
|
||||||
return h.lowestEquivalentValueGivenBucketIdx(v, bucketIdx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) lowestEquivalentValueGivenBucketIdx(v int64, bucketIdx int32) int64 {
|
|
||||||
subBucketIdx := h.getSubBucketIdx(v, bucketIdx)
|
|
||||||
return h.valueFromIndex(bucketIdx, subBucketIdx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) nextNonEquivalentValue(v int64) int64 {
|
|
||||||
bucketIdx := h.getBucketIndex(v)
|
|
||||||
return h.lowestEquivalentValueGivenBucketIdx(v, bucketIdx) + h.sizeOfEquivalentValueRangeGivenBucketIdx(v, bucketIdx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) highestEquivalentValue(v int64) int64 {
|
|
||||||
return h.nextNonEquivalentValue(v) - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) medianEquivalentValue(v int64) int64 {
|
|
||||||
return h.lowestEquivalentValue(v) + (h.sizeOfEquivalentValueRange(v) >> 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) getCountAtIndex(bucketIdx, subBucketIdx int32) int64 {
|
|
||||||
return h.counts[h.countsIndex(bucketIdx, subBucketIdx)]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) getCountAtIndexGivenBucketBaseIdx(bucketBaseIdx, subBucketIdx int32) int64 {
|
|
||||||
return h.counts[bucketBaseIdx+subBucketIdx-h.subBucketHalfCount]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) countsIndex(bucketIdx, subBucketIdx int32) int32 {
|
|
||||||
return h.getBucketBaseIdx(bucketIdx) + subBucketIdx - h.subBucketHalfCount
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) getBucketBaseIdx(bucketIdx int32) int32 {
|
|
||||||
return (bucketIdx + 1) << uint(h.subBucketHalfCountMagnitude)
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the lowest (and therefore highest precision) bucket index that can represent the value
|
|
||||||
// Calculates the number of powers of two by which the value is greater than the biggest value that fits in
|
|
||||||
// bucket 0. This is the bucket index since each successive bucket can hold a value 2x greater.
|
|
||||||
func (h *Histogram) getBucketIndex(v int64) int32 {
|
|
||||||
var pow2Ceiling = int64(64 - bits.LeadingZeros64(uint64(v|h.subBucketMask)))
|
|
||||||
return int32(pow2Ceiling - int64(h.unitMagnitude) -
|
|
||||||
int64(h.subBucketHalfCountMagnitude+1))
|
|
||||||
}
|
|
||||||
|
|
||||||
// For bucketIndex 0, this is just value, so it may be anywhere in 0 to subBucketCount.
|
|
||||||
// For other bucketIndex, this will always end up in the top half of subBucketCount: assume that for some bucket
|
|
||||||
// k > 0, this calculation will yield a value in the bottom half of 0 to subBucketCount. Then, because of how
|
|
||||||
// buckets overlap, it would have also been in the top half of bucket k-1, and therefore would have
|
|
||||||
// returned k-1 in getBucketIndex(). Since we would then shift it one fewer bits here, it would be twice as big,
|
|
||||||
// and therefore in the top half of subBucketCount.
|
|
||||||
func (h *Histogram) getSubBucketIdx(v int64, idx int32) int32 {
|
|
||||||
return int32(v >> uint(int64(idx)+int64(h.unitMagnitude)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) countsIndexFor(v int64) int {
|
|
||||||
bucketIdx := h.getBucketIndex(v)
|
|
||||||
subBucketIdx := h.getSubBucketIdx(v, bucketIdx)
|
|
||||||
return int(h.countsIndex(bucketIdx, subBucketIdx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) getIntegerToDoubleValueConversionRatio() float64 {
|
|
||||||
return 1.0
|
|
||||||
}
|
|
||||||
|
|
||||||
type iterator struct {
|
|
||||||
h *Histogram
|
|
||||||
bucketIdx, subBucketIdx int32
|
|
||||||
countAtIdx, countToIdx, valueFromIdx int64
|
|
||||||
highestEquivalentValue int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// nextCountAtIdx does not update the iterator highestEquivalentValue in order to optimize cpu usage.
|
|
||||||
func (i *iterator) nextCountAtIdx(limit int64) bool {
|
|
||||||
if i.countToIdx >= limit {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// increment bucket
|
|
||||||
i.subBucketIdx++
|
|
||||||
if i.subBucketIdx >= i.h.subBucketCount {
|
|
||||||
i.subBucketIdx = i.h.subBucketHalfCount
|
|
||||||
i.bucketIdx++
|
|
||||||
}
|
|
||||||
|
|
||||||
if i.bucketIdx >= i.h.bucketCount {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
i.countAtIdx = i.h.getCountAtIndex(i.bucketIdx, i.subBucketIdx)
|
|
||||||
i.countToIdx += i.countAtIdx
|
|
||||||
i.valueFromIdx = i.h.valueFromIndex(i.bucketIdx, i.subBucketIdx)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the next element in the iteration.
|
|
||||||
func (i *iterator) next() bool {
|
|
||||||
if !i.nextCountAtIdx(i.h.totalCount) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
i.highestEquivalentValue = i.h.highestEquivalentValue(i.valueFromIdx)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
type rIterator struct {
|
|
||||||
iterator
|
|
||||||
countAddedThisStep int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rIterator) next() bool {
|
|
||||||
for r.iterator.next() {
|
|
||||||
if r.countAtIdx != 0 {
|
|
||||||
r.countAddedThisStep = r.countAtIdx
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type pIterator struct {
|
|
||||||
iterator
|
|
||||||
seenLastValue bool
|
|
||||||
ticksPerHalfDistance int32
|
|
||||||
percentileToIteratorTo float64
|
|
||||||
percentile float64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pIterator) next() bool {
|
|
||||||
if !(p.countToIdx < p.h.totalCount) {
|
|
||||||
if p.seenLastValue {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
p.seenLastValue = true
|
|
||||||
p.percentile = 100
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.subBucketIdx == -1 && !p.iterator.next() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var done = false
|
|
||||||
for !done {
|
|
||||||
currentPercentile := (100.0 * float64(p.countToIdx)) / float64(p.h.totalCount)
|
|
||||||
if p.countAtIdx != 0 && p.percentileToIteratorTo <= currentPercentile {
|
|
||||||
p.percentile = p.percentileToIteratorTo
|
|
||||||
halfDistance := math.Trunc(math.Pow(2, math.Trunc(math.Log2(100.0/(100.0-p.percentileToIteratorTo)))+1))
|
|
||||||
percentileReportingTicks := float64(p.ticksPerHalfDistance) * halfDistance
|
|
||||||
p.percentileToIteratorTo += 100.0 / percentileReportingTicks
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
done = !p.iterator.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// CumulativeDistribution returns an ordered list of brackets of the
|
|
||||||
// distribution of recorded values.
|
|
||||||
func (h *Histogram) CumulativeDistributionWithTicks(ticksPerHalfDistance int32) []Bracket {
|
|
||||||
var result []Bracket
|
|
||||||
|
|
||||||
i := h.pIterator(ticksPerHalfDistance)
|
|
||||||
for i.next() {
|
|
||||||
result = append(result, Bracket{
|
|
||||||
Quantile: i.percentile,
|
|
||||||
Count: i.countToIdx,
|
|
||||||
ValueAt: int64(i.highestEquivalentValue),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output the percentiles distribution in a text format
|
|
||||||
func (h *Histogram) PercentilesPrint(writer io.Writer, ticksPerHalfDistance int32, valueScale float64) (outputWriter io.Writer, err error) {
|
|
||||||
outputWriter = writer
|
|
||||||
dist := h.CumulativeDistributionWithTicks(ticksPerHalfDistance)
|
|
||||||
_, err = outputWriter.Write([]byte(" Value\tPercentile\tTotalCount\t1/(1-Percentile)\n\n"))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, slice := range dist {
|
|
||||||
percentile := slice.Quantile / 100.0
|
|
||||||
inverted_percentile := 1.0 / (1.0 - percentile)
|
|
||||||
var inverted_percentile_string = fmt.Sprintf("%12.2f", inverted_percentile)
|
|
||||||
// Given that other language implementations display inf (instead of Go's +Inf)
|
|
||||||
// we want to be as close as possible to them
|
|
||||||
if math.IsInf(inverted_percentile, 1) {
|
|
||||||
inverted_percentile_string = fmt.Sprintf("%12s", "inf")
|
|
||||||
}
|
|
||||||
_, err = outputWriter.Write([]byte(fmt.Sprintf("%12.3f %12f %12d %s\n", float64(slice.ValueAt)/valueScale, percentile, slice.Count, inverted_percentile_string)))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
footer := fmt.Sprintf("#[Mean = %12.3f, StdDeviation = %12.3f]\n#[Max = %12.3f, Total count = %12d]\n#[Buckets = %12d, SubBuckets = %12d]\n",
|
|
||||||
h.Mean()/valueScale,
|
|
||||||
h.StdDev()/valueScale,
|
|
||||||
float64(h.Max())/valueScale,
|
|
||||||
h.TotalCount(),
|
|
||||||
h.bucketCount,
|
|
||||||
h.subBucketCount,
|
|
||||||
)
|
|
||||||
_, err = outputWriter.Write([]byte(footer))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
261
vendor/github.com/HdrHistogram/hdrhistogram-go/hdr_encoding.go
generated
vendored
261
vendor/github.com/HdrHistogram/hdrhistogram-go/hdr_encoding.go
generated
vendored
@@ -1,261 +0,0 @@
|
|||||||
// Histograms are encoded using the HdrHistogram V2 format which is based on an adapted ZigZag LEB128 encoding where:
|
|
||||||
// consecutive zero counters are encoded as a negative number representing the count of consecutive zeros
|
|
||||||
// non zero counter values are encoded as a positive number
|
|
||||||
// A typical histogram (2 digits precision 1 usec to 1 day range) can be encoded in less than the typical MTU size of 1500 bytes.
|
|
||||||
package hdrhistogram
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"compress/zlib"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
V2EncodingCookieBase int32 = 0x1c849303
|
|
||||||
V2CompressedEncodingCookieBase int32 = 0x1c849304
|
|
||||||
encodingCookie int32 = V2EncodingCookieBase | 0x10
|
|
||||||
compressedEncodingCookie int32 = V2CompressedEncodingCookieBase | 0x10
|
|
||||||
|
|
||||||
ENCODING_HEADER_SIZE = 40
|
|
||||||
)
|
|
||||||
|
|
||||||
// Encode returns a snapshot view of the Histogram.
|
|
||||||
// The snapshot is compact binary representations of the state of the histogram.
|
|
||||||
// They are intended to be used for archival or transmission to other systems for further analysis.
|
|
||||||
func (h *Histogram) Encode(version int32) (buffer []byte, err error) {
|
|
||||||
switch version {
|
|
||||||
case V2CompressedEncodingCookieBase:
|
|
||||||
buffer, err = h.dumpV2CompressedEncoding()
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("The provided enconding version %d is not supported.", version)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode returns a new Histogram by decoding it from a String containing
|
|
||||||
// a base64 encoded compressed histogram representation.
|
|
||||||
func Decode(encoded []byte) (rh *Histogram, err error) {
|
|
||||||
var decoded []byte
|
|
||||||
decoded, err = base64.StdEncoding.DecodeString(string(encoded))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rbuf := bytes.NewBuffer(decoded[0:8])
|
|
||||||
r32 := make([]int32, 2)
|
|
||||||
err = binary.Read(rbuf, binary.BigEndian, &r32)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Cookie := r32[0] & ^0xf0
|
|
||||||
lengthOfCompressedContents := r32[1]
|
|
||||||
if Cookie != V2CompressedEncodingCookieBase {
|
|
||||||
err = fmt.Errorf("Encoding not supported, only V2 is supported. Got %d want %d", Cookie, V2CompressedEncodingCookieBase)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
decodeLengthOfCompressedContents := int32(len(decoded[8:]))
|
|
||||||
if lengthOfCompressedContents > decodeLengthOfCompressedContents {
|
|
||||||
err = fmt.Errorf("The compressed contents buffer is smaller than the lengthOfCompressedContents. Got %d want %d", decodeLengthOfCompressedContents, lengthOfCompressedContents)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rh, err = decodeCompressedFormat(decoded[8:8+lengthOfCompressedContents], ENCODING_HEADER_SIZE)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// internal method to encode an histogram in V2 Compressed format
|
|
||||||
func (h *Histogram) dumpV2CompressedEncoding() (outBuffer []byte, err error) {
|
|
||||||
// final buffer
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
err = binary.Write(buf, binary.BigEndian, compressedEncodingCookie)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
toCompress, err := h.encodeIntoByteBuffer()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uncompressedBytes := toCompress.Bytes()
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
|
||||||
w, err := zlib.NewWriterLevel(&b, zlib.BestCompression)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = w.Write(uncompressedBytes)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Close()
|
|
||||||
|
|
||||||
// LengthOfCompressedContents
|
|
||||||
compressedContents := b.Bytes()
|
|
||||||
err = binary.Write(buf, binary.BigEndian, int32(len(compressedContents)))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = binary.Write(buf, binary.BigEndian, compressedContents)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
outBuffer = []byte(base64.StdEncoding.EncodeToString(buf.Bytes()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Histogram) encodeIntoByteBuffer() (*bytes.Buffer, error) {
|
|
||||||
|
|
||||||
countsBytes, err := h.fillBufferFromCountsArray()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
toCompress := new(bytes.Buffer)
|
|
||||||
err = binary.Write(toCompress, binary.BigEndian, encodingCookie) // 0-3
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = binary.Write(toCompress, binary.BigEndian, int32(len(countsBytes))) // 3-7
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = binary.Write(toCompress, binary.BigEndian, h.getNormalizingIndexOffset()) // 8-11
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = binary.Write(toCompress, binary.BigEndian, int32(h.significantFigures)) // 12-15
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = binary.Write(toCompress, binary.BigEndian, h.lowestDiscernibleValue) // 16-23
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = binary.Write(toCompress, binary.BigEndian, h.highestTrackableValue) // 24-31
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = binary.Write(toCompress, binary.BigEndian, h.getIntegerToDoubleValueConversionRatio()) // 32-39
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = binary.Write(toCompress, binary.BigEndian, countsBytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return toCompress, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeCompressedFormat(compressedContents []byte, headerSize int) (rh *Histogram, err error) {
|
|
||||||
b := bytes.NewReader(compressedContents)
|
|
||||||
z, err := zlib.NewReader(b)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer z.Close()
|
|
||||||
decompressedSlice, err := ioutil.ReadAll(z)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
decompressedSliceLen := int32(len(decompressedSlice))
|
|
||||||
cookie, PayloadLength, _, NumberOfSignificantValueDigits, LowestTrackableValue, HighestTrackableValue, _, err := decodeDeCompressedHeaderFormat(decompressedSlice[0:headerSize])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if cookie != V2EncodingCookieBase {
|
|
||||||
err = fmt.Errorf("Encoding not supported, only V2 is supported. Got %d want %d", cookie, V2EncodingCookieBase)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
actualPayloadLen := decompressedSliceLen - int32(headerSize)
|
|
||||||
if PayloadLength != actualPayloadLen {
|
|
||||||
err = fmt.Errorf("PayloadLength should have the same size of the actual payload. Got %d want %d", actualPayloadLen, PayloadLength)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rh = New(LowestTrackableValue, HighestTrackableValue, int(NumberOfSignificantValueDigits))
|
|
||||||
payload := decompressedSlice[headerSize:]
|
|
||||||
err = fillCountsArrayFromSourceBuffer(payload, rh)
|
|
||||||
return rh, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func fillCountsArrayFromSourceBuffer(payload []byte, rh *Histogram) (err error) {
|
|
||||||
var payloadSlicePos = 0
|
|
||||||
var dstIndex int64 = 0
|
|
||||||
var n int
|
|
||||||
var count int64
|
|
||||||
var zerosCount int64
|
|
||||||
for payloadSlicePos < len(payload) {
|
|
||||||
count, n, err = zig_zag_decode_i64(payload[payloadSlicePos:])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
payloadSlicePos += n
|
|
||||||
if count < 0 {
|
|
||||||
zerosCount = -count
|
|
||||||
dstIndex += zerosCount
|
|
||||||
} else {
|
|
||||||
rh.setCountAtIndex(int(dstIndex), count)
|
|
||||||
dstIndex += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rh *Histogram) fillBufferFromCountsArray() (buffer []byte, err error) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
// V2 encoding format uses a ZigZag LEB128-64b9B encoded long. Positive values are counts,
|
|
||||||
// while negative values indicate a repeat zero counts.
|
|
||||||
var countsLimit int32 = int32(rh.countsIndexFor(rh.Max()) + 1)
|
|
||||||
var srcIndex int32 = 0
|
|
||||||
for srcIndex < countsLimit {
|
|
||||||
count := rh.counts[srcIndex]
|
|
||||||
srcIndex++
|
|
||||||
|
|
||||||
var zeros int64 = 0
|
|
||||||
// check for contiguous zeros
|
|
||||||
if count == 0 {
|
|
||||||
zeros = 1
|
|
||||||
for srcIndex < countsLimit && 0 == rh.counts[srcIndex] {
|
|
||||||
zeros++
|
|
||||||
srcIndex++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if zeros > 1 {
|
|
||||||
err = binary.Write(buf, binary.BigEndian, zig_zag_encode_i64(-zeros))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = binary.Write(buf, binary.BigEndian, zig_zag_encode_i64(count))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffer = buf.Bytes()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeDeCompressedHeaderFormat(decoded []byte) (Cookie int32, PayloadLength int32, NormalizingIndexOffSet int32, NumberOfSignificantValueDigits int32, LowestTrackableValue int64, HighestTrackableValue int64, IntegerToDoubleConversionRatio float64, err error) {
|
|
||||||
rbuf := bytes.NewBuffer(decoded[0:40])
|
|
||||||
r32 := make([]int32, 4)
|
|
||||||
r64 := make([]int64, 2)
|
|
||||||
err = binary.Read(rbuf, binary.BigEndian, &r32)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = binary.Read(rbuf, binary.BigEndian, &r64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = binary.Read(rbuf, binary.BigEndian, &IntegerToDoubleConversionRatio)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Cookie = r32[0] & ^0xf0
|
|
||||||
PayloadLength = r32[1]
|
|
||||||
NormalizingIndexOffSet = r32[2]
|
|
||||||
NumberOfSignificantValueDigits = r32[3]
|
|
||||||
LowestTrackableValue = r64[0]
|
|
||||||
HighestTrackableValue = r64[1]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
209
vendor/github.com/HdrHistogram/hdrhistogram-go/log_reader.go
generated
vendored
209
vendor/github.com/HdrHistogram/hdrhistogram-go/log_reader.go
generated
vendored
@@ -1,209 +0,0 @@
|
|||||||
package hdrhistogram
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HistogramLogReader struct {
|
|
||||||
log *bufio.Reader
|
|
||||||
startTimeSec float64
|
|
||||||
observedStartTime bool
|
|
||||||
baseTimeSec float64
|
|
||||||
observedBaseTime bool
|
|
||||||
|
|
||||||
// scanner handling state
|
|
||||||
absolute bool
|
|
||||||
rangeStartTimeSec float64
|
|
||||||
rangeEndTimeSec float64
|
|
||||||
observedMax bool
|
|
||||||
rangeObservedMax int64
|
|
||||||
observedMin bool
|
|
||||||
rangeObservedMin int64
|
|
||||||
reStartTime *regexp.Regexp
|
|
||||||
reBaseTime *regexp.Regexp
|
|
||||||
reHistogramInterval *regexp.Regexp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hlr *HistogramLogReader) ObservedMin() bool {
|
|
||||||
return hlr.observedMin
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hlr *HistogramLogReader) ObservedMax() bool {
|
|
||||||
return hlr.observedMax
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the overall observed max limit ( up to the current point ) of the read histograms
|
|
||||||
func (hlr *HistogramLogReader) RangeObservedMax() int64 {
|
|
||||||
return hlr.rangeObservedMax
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the overall observed min limit ( up to the current point ) of the read histograms
|
|
||||||
func (hlr *HistogramLogReader) RangeObservedMin() int64 {
|
|
||||||
return hlr.rangeObservedMin
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHistogramLogReader(log io.Reader) *HistogramLogReader {
|
|
||||||
//# "#[StartTime: %f (seconds since epoch), %s]\n"
|
|
||||||
reStartTime, _ := regexp.Compile(`#\[StartTime: ([\d\.]*)`)
|
|
||||||
|
|
||||||
//# "#[BaseTime: %f (seconds since epoch)]\n"
|
|
||||||
reBaseTime, _ := regexp.Compile(`#\[BaseTime: ([\d\.]*)`)
|
|
||||||
|
|
||||||
//# 0.127,1.007,2.769,HISTFAAAAEV42pNpmSz...
|
|
||||||
//# Tag=A,0.127,1.007,2.769,HISTFAAAAEV42pNpmSz
|
|
||||||
//# "%f,%f,%f,%s\n"
|
|
||||||
reHistogramInterval, _ := regexp.Compile(`([\d\.]*),([\d\.]*),([\d\.]*),(.*)`)
|
|
||||||
//
|
|
||||||
reader := bufio.NewReader(log)
|
|
||||||
|
|
||||||
return &HistogramLogReader{log: reader,
|
|
||||||
startTimeSec: 0.0,
|
|
||||||
observedStartTime: false,
|
|
||||||
baseTimeSec: 0.0,
|
|
||||||
observedBaseTime: false,
|
|
||||||
reStartTime: reStartTime,
|
|
||||||
reBaseTime: reBaseTime,
|
|
||||||
reHistogramInterval: reHistogramInterval,
|
|
||||||
rangeObservedMin: math.MaxInt64,
|
|
||||||
observedMin: false,
|
|
||||||
rangeObservedMax: math.MinInt64,
|
|
||||||
observedMax: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hlr *HistogramLogReader) NextIntervalHistogram() (histogram *Histogram, err error) {
|
|
||||||
return hlr.NextIntervalHistogramWithRange(0.0, math.MaxFloat64, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hlr *HistogramLogReader) NextIntervalHistogramWithRange(rangeStartTimeSec, rangeEndTimeSec float64, absolute bool) (histogram *Histogram, err error) {
|
|
||||||
hlr.rangeStartTimeSec = rangeStartTimeSec
|
|
||||||
hlr.rangeEndTimeSec = rangeEndTimeSec
|
|
||||||
hlr.absolute = absolute
|
|
||||||
return hlr.decodeNextIntervalHistogram()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hlr *HistogramLogReader) decodeNextIntervalHistogram() (histogram *Histogram, err error) {
|
|
||||||
var line string
|
|
||||||
var tag string = ""
|
|
||||||
var logTimeStampInSec float64
|
|
||||||
var intervalLengthSec float64
|
|
||||||
for {
|
|
||||||
line, err = hlr.log.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if line[0] == '#' {
|
|
||||||
matchRes := hlr.reStartTime.FindStringSubmatch(line)
|
|
||||||
if len(matchRes) > 0 {
|
|
||||||
hlr.startTimeSec, err = strconv.ParseFloat(matchRes[1], 64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
hlr.observedStartTime = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
matchRes = hlr.reBaseTime.FindStringSubmatch(line)
|
|
||||||
if len(matchRes) > 0 {
|
|
||||||
hlr.baseTimeSec, err = strconv.ParseFloat(matchRes[1], 64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
hlr.observedBaseTime = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(line, "Tag=") {
|
|
||||||
commaPos := strings.Index(line, ",")
|
|
||||||
tag = line[4:commaPos]
|
|
||||||
line = line[commaPos+1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
matchRes := hlr.reHistogramInterval.FindStringSubmatch(line)
|
|
||||||
if len(matchRes) >= 1 {
|
|
||||||
// Decode: startTimestamp, intervalLength, maxTime, histogramPayload
|
|
||||||
// Timestamp is expected to be in seconds
|
|
||||||
logTimeStampInSec, err = strconv.ParseFloat(matchRes[1], 64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
intervalLengthSec, err = strconv.ParseFloat(matchRes[2], 64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cpayload := matchRes[4]
|
|
||||||
|
|
||||||
// No explicit start time noted. Use 1st observed time:
|
|
||||||
|
|
||||||
if !hlr.observedStartTime {
|
|
||||||
hlr.startTimeSec = logTimeStampInSec
|
|
||||||
hlr.observedStartTime = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// No explicit base time noted.
|
|
||||||
// Deduce from 1st observed time (compared to start time):
|
|
||||||
if !hlr.observedBaseTime {
|
|
||||||
// Criteria Note: if log timestamp is more than a year in
|
|
||||||
// the past (compared to StartTime),
|
|
||||||
// we assume that timestamps in the log are not absolute
|
|
||||||
if logTimeStampInSec < (hlr.startTimeSec - (365 * 24 * 3600.0)) {
|
|
||||||
hlr.baseTimeSec = hlr.startTimeSec
|
|
||||||
} else {
|
|
||||||
hlr.baseTimeSec = 0.0
|
|
||||||
}
|
|
||||||
hlr.observedBaseTime = true
|
|
||||||
}
|
|
||||||
|
|
||||||
absoluteStartTimeStampSec := logTimeStampInSec + hlr.baseTimeSec
|
|
||||||
offsetStartTimeStampSec := absoluteStartTimeStampSec + hlr.startTimeSec
|
|
||||||
|
|
||||||
// Timestamp length is expect to be in seconds
|
|
||||||
absoluteEndTimeStampSec := absoluteStartTimeStampSec + intervalLengthSec
|
|
||||||
|
|
||||||
var startTimeStampToCheckRangeOn float64
|
|
||||||
if hlr.absolute {
|
|
||||||
startTimeStampToCheckRangeOn = absoluteStartTimeStampSec
|
|
||||||
} else {
|
|
||||||
startTimeStampToCheckRangeOn = offsetStartTimeStampSec
|
|
||||||
}
|
|
||||||
|
|
||||||
if startTimeStampToCheckRangeOn < hlr.rangeStartTimeSec {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if startTimeStampToCheckRangeOn > hlr.rangeEndTimeSec {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
histogram, err = Decode([]byte(cpayload))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if histogram.Max() > hlr.rangeObservedMax {
|
|
||||||
hlr.rangeObservedMax = histogram.Max()
|
|
||||||
}
|
|
||||||
|
|
||||||
if histogram.Min() < hlr.rangeObservedMin {
|
|
||||||
hlr.rangeObservedMin = histogram.Min()
|
|
||||||
}
|
|
||||||
|
|
||||||
histogram.SetStartTimeMs(int64(absoluteStartTimeStampSec * 1000.0))
|
|
||||||
histogram.SetEndTimeMs(int64(absoluteEndTimeStampSec * 1000.0))
|
|
||||||
if tag != "" {
|
|
||||||
histogram.SetTag(tag)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
141
vendor/github.com/HdrHistogram/hdrhistogram-go/log_writer.go
generated
vendored
141
vendor/github.com/HdrHistogram/hdrhistogram-go/log_writer.go
generated
vendored
@@ -1,141 +0,0 @@
|
|||||||
//The log format encodes into a single file, multiple histograms with optional shared meta data.
|
|
||||||
package hdrhistogram
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"regexp"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const HISTOGRAM_LOG_FORMAT_VERSION = "1.3"
|
|
||||||
const MsToNsRatio float64 = 1000000.0
|
|
||||||
|
|
||||||
type HistogramLogOptions struct {
|
|
||||||
startTimeStampSec float64
|
|
||||||
endTimeStampSec float64
|
|
||||||
maxValueUnitRatio float64
|
|
||||||
}
|
|
||||||
|
|
||||||
func DefaultHistogramLogOptions() *HistogramLogOptions {
|
|
||||||
return &HistogramLogOptions{0, 0, MsToNsRatio}
|
|
||||||
}
|
|
||||||
|
|
||||||
type HistogramLogWriter struct {
|
|
||||||
baseTime int64
|
|
||||||
log io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the current base time offset
|
|
||||||
func (lw *HistogramLogWriter) BaseTime() int64 {
|
|
||||||
return lw.baseTime
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set a base time to subtract from supplied histogram start/end timestamps when
|
|
||||||
// logging based on histogram timestamps.
|
|
||||||
// baseTime is expected to be in msec since the epoch, as histogram start/end times
|
|
||||||
// are typically stamped with absolute times in msec since the epoch.
|
|
||||||
func (lw *HistogramLogWriter) SetBaseTime(baseTime int64) {
|
|
||||||
lw.baseTime = baseTime
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHistogramLogWriter(log io.Writer) *HistogramLogWriter {
|
|
||||||
return &HistogramLogWriter{baseTime: 0, log: log}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output an interval histogram, using the start/end timestamp indicated in the histogram, and the [optional] tag associated with the histogram.
|
|
||||||
// The histogram start and end timestamps are assumed to be in msec units
|
|
||||||
//
|
|
||||||
// By convention, histogram start/end time are generally stamped with absolute times in msec
|
|
||||||
// since the epoch. For logging with absolute time stamps, the base time would remain zero ( default ).
|
|
||||||
// For logging with relative time stamps (time since a start point), the base time should be set with SetBaseTime(baseTime int64)
|
|
||||||
//
|
|
||||||
// The max value in the histogram will be reported scaled down by a default maxValueUnitRatio of 1000000.0 (which is the msec : nsec ratio).
|
|
||||||
// If you need to specify a different start/end timestamp or a different maxValueUnitRatio you should use OutputIntervalHistogramWithLogOptions(histogram *Histogram, logOptions *HistogramLogOptions)
|
|
||||||
func (lw *HistogramLogWriter) OutputIntervalHistogram(histogram *Histogram) (err error) {
|
|
||||||
return lw.OutputIntervalHistogramWithLogOptions(histogram, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output an interval histogram, with the given timestamp information and the [optional] tag associated with the histogram
|
|
||||||
//
|
|
||||||
// If you specify non-nil logOptions, and non-zero start timestamp, the the specified timestamp information will be used, and the start timestamp information in the actual histogram will be ignored.
|
|
||||||
// If you specify non-nil logOptions, and non-zero start timestamp, the the specified timestamp information will be used, and the end timestamp information in the actual histogram will be ignored.
|
|
||||||
// If you specify non-nil logOptions, The max value reported with the interval line will be scaled by the given maxValueUnitRatio,
|
|
||||||
// otherwise a default maxValueUnitRatio of 1,000,000 (which is the msec : nsec ratio) will be used.
|
|
||||||
//
|
|
||||||
// By convention, histogram start/end time are generally stamped with absolute times in msec
|
|
||||||
// since the epoch. For logging with absolute time stamps, the base time would remain zero ( default ).
|
|
||||||
// For logging with relative time stamps (time since a start point), the base time should be set with SetBaseTime(baseTime int64)
|
|
||||||
func (lw *HistogramLogWriter) OutputIntervalHistogramWithLogOptions(histogram *Histogram, logOptions *HistogramLogOptions) (err error) {
|
|
||||||
tag := histogram.Tag()
|
|
||||||
var match bool
|
|
||||||
tagStr := ""
|
|
||||||
if tag != "" {
|
|
||||||
match, err = regexp.MatchString(".[, \\r\\n].", tag)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if match {
|
|
||||||
err = fmt.Errorf("Tag string cannot contain commas, spaces, or line breaks. Used tag: %s", tag)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tagStr = fmt.Sprintf("Tag=%s,", tag)
|
|
||||||
}
|
|
||||||
var usedStartTime float64 = float64(histogram.StartTimeMs())
|
|
||||||
var usedEndTime float64 = float64(histogram.EndTimeMs())
|
|
||||||
var maxValueUnitRatio float64 = MsToNsRatio
|
|
||||||
if logOptions != nil {
|
|
||||||
if logOptions.startTimeStampSec != 0 {
|
|
||||||
usedStartTime = logOptions.startTimeStampSec
|
|
||||||
}
|
|
||||||
if logOptions.endTimeStampSec != 0 {
|
|
||||||
usedEndTime = logOptions.endTimeStampSec
|
|
||||||
}
|
|
||||||
maxValueUnitRatio = logOptions.maxValueUnitRatio
|
|
||||||
}
|
|
||||||
startTime := usedStartTime - float64(lw.baseTime)/1000.0
|
|
||||||
endTime := usedEndTime - float64(lw.baseTime)/1000.0
|
|
||||||
maxValueAsDouble := float64(histogram.Max()) / maxValueUnitRatio
|
|
||||||
cpayload, err := histogram.Encode(V2CompressedEncodingCookieBase)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = lw.log.Write([]byte(fmt.Sprintf("%s%f,%f,%f,%s\n", tagStr, startTime, endTime, maxValueAsDouble, string(cpayload))))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log a start time in the log.
|
|
||||||
// Start time is represented as seconds since epoch with up to 3 decimal places. Line starts with the leading text '#[StartTime:'
|
|
||||||
func (lw *HistogramLogWriter) OutputStartTime(start_time_msec int64) (err error) {
|
|
||||||
secs := start_time_msec / 1000
|
|
||||||
iso_str := time.Unix(secs, start_time_msec%int64(1000)*int64(1000000000)).Format(time.RFC3339)
|
|
||||||
_, err = lw.log.Write([]byte(fmt.Sprintf("#[StartTime: %d (seconds since epoch), %s]\n", secs, iso_str)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log a base time in the log.
|
|
||||||
// Base time is represented as seconds since epoch with up to 3 decimal places. Line starts with the leading text '#[BaseTime:'
|
|
||||||
func (lw *HistogramLogWriter) OutputBaseTime(base_time_msec int64) (err error) {
|
|
||||||
secs := base_time_msec / 1000
|
|
||||||
_, err = lw.log.Write([]byte(fmt.Sprintf("#[Basetime: %d (seconds since epoch)]\n", secs)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log a comment to the log.
|
|
||||||
// A comment is any line that leads with '#' that is not matched by the BaseTime or StartTime formats. Comments are ignored when parsed.
|
|
||||||
func (lw *HistogramLogWriter) OutputComment(comment string) (err error) {
|
|
||||||
_, err = lw.log.Write([]byte(fmt.Sprintf("#%s\n", comment)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output a legend line to the log.
|
|
||||||
// Human readable column headers. Ignored when parsed.
|
|
||||||
func (lw *HistogramLogWriter) OutputLegend() (err error) {
|
|
||||||
_, err = lw.log.Write([]byte("\"StartTimestamp\",\"Interval_Length\",\"Interval_Max\",\"Interval_Compressed_Histogram\"\n"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output a log format version to the log.
|
|
||||||
func (lw *HistogramLogWriter) OutputLogFormatVersion() (err error) {
|
|
||||||
return lw.OutputComment(fmt.Sprintf("[Histogram log format version %s]", HISTOGRAM_LOG_FORMAT_VERSION))
|
|
||||||
}
|
|
||||||
45
vendor/github.com/HdrHistogram/hdrhistogram-go/window.go
generated
vendored
45
vendor/github.com/HdrHistogram/hdrhistogram-go/window.go
generated
vendored
@@ -1,45 +0,0 @@
|
|||||||
package hdrhistogram
|
|
||||||
|
|
||||||
// A WindowedHistogram combines histograms to provide windowed statistics.
|
|
||||||
type WindowedHistogram struct {
|
|
||||||
idx int
|
|
||||||
h []Histogram
|
|
||||||
m *Histogram
|
|
||||||
|
|
||||||
Current *Histogram
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWindowed creates a new WindowedHistogram with N underlying histograms with
|
|
||||||
// the given parameters.
|
|
||||||
func NewWindowed(n int, minValue, maxValue int64, sigfigs int) *WindowedHistogram {
|
|
||||||
w := WindowedHistogram{
|
|
||||||
idx: -1,
|
|
||||||
h: make([]Histogram, n),
|
|
||||||
m: New(minValue, maxValue, sigfigs),
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range w.h {
|
|
||||||
w.h[i] = *New(minValue, maxValue, sigfigs)
|
|
||||||
}
|
|
||||||
w.Rotate()
|
|
||||||
|
|
||||||
return &w
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge returns a histogram which includes the recorded values from all the
|
|
||||||
// sections of the window.
|
|
||||||
func (w *WindowedHistogram) Merge() *Histogram {
|
|
||||||
w.m.Reset()
|
|
||||||
for _, h := range w.h {
|
|
||||||
w.m.Merge(&h)
|
|
||||||
}
|
|
||||||
return w.m
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rotate resets the oldest histogram and rotates it to be used as the current
|
|
||||||
// histogram.
|
|
||||||
func (w *WindowedHistogram) Rotate() {
|
|
||||||
w.idx++
|
|
||||||
w.Current = &w.h[w.idx%len(w.h)]
|
|
||||||
w.Current.Reset()
|
|
||||||
}
|
|
||||||
133
vendor/github.com/HdrHistogram/hdrhistogram-go/zigzag.go
generated
vendored
133
vendor/github.com/HdrHistogram/hdrhistogram-go/zigzag.go
generated
vendored
@@ -1,133 +0,0 @@
|
|||||||
package hdrhistogram
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
const truncatedErrStr = "Truncated compressed histogram decode. Expected minimum length of %d bytes and got %d."
|
|
||||||
|
|
||||||
// Read an LEB128 ZigZag encoded long value from the given buffer
|
|
||||||
func zig_zag_decode_i64(buf []byte) (signedValue int64, n int, err error) {
|
|
||||||
buflen := len(buf)
|
|
||||||
if buflen < 1 {
|
|
||||||
return 0, 0, nil
|
|
||||||
}
|
|
||||||
var value = uint64(buf[0]) & 0x7f
|
|
||||||
n = 1
|
|
||||||
if (buf[0] & 0x80) != 0 {
|
|
||||||
if buflen < 2 {
|
|
||||||
err = fmt.Errorf(truncatedErrStr, 2, buflen)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value |= uint64(buf[1]) & 0x7f << 7
|
|
||||||
n = 2
|
|
||||||
if (buf[1] & 0x80) != 0 {
|
|
||||||
if buflen < 3 {
|
|
||||||
err = fmt.Errorf(truncatedErrStr, 3, buflen)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value |= uint64(buf[2]) & 0x7f << 14
|
|
||||||
n = 3
|
|
||||||
if (buf[2] & 0x80) != 0 {
|
|
||||||
if buflen < 4 {
|
|
||||||
err = fmt.Errorf(truncatedErrStr, 4, buflen)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value |= uint64(buf[3]) & 0x7f << 21
|
|
||||||
n = 4
|
|
||||||
if (buf[3] & 0x80) != 0 {
|
|
||||||
if buflen < 5 {
|
|
||||||
err = fmt.Errorf(truncatedErrStr, 5, buflen)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value |= uint64(buf[4]) & 0x7f << 28
|
|
||||||
n = 5
|
|
||||||
if (buf[4] & 0x80) != 0 {
|
|
||||||
if buflen < 6 {
|
|
||||||
err = fmt.Errorf(truncatedErrStr, 6, buflen)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value |= uint64(buf[5]) & 0x7f << 35
|
|
||||||
n = 6
|
|
||||||
if (buf[5] & 0x80) != 0 {
|
|
||||||
if buflen < 7 {
|
|
||||||
err = fmt.Errorf(truncatedErrStr, 7, buflen)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value |= uint64(buf[6]) & 0x7f << 42
|
|
||||||
n = 7
|
|
||||||
if (buf[6] & 0x80) != 0 {
|
|
||||||
if buflen < 8 {
|
|
||||||
err = fmt.Errorf(truncatedErrStr, 8, buflen)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value |= uint64(buf[7]) & 0x7f << 49
|
|
||||||
n = 8
|
|
||||||
if (buf[7] & 0x80) != 0 {
|
|
||||||
if buflen < 9 {
|
|
||||||
err = fmt.Errorf(truncatedErrStr, 9, buflen)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value |= uint64(buf[8]) << 56
|
|
||||||
n = 9
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
signedValue = int64((value >> 1) ^ -(value & 1))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writes a int64_t value to the given buffer in LEB128 ZigZag encoded format
|
|
||||||
// ZigZag encoding maps signed integers to unsigned integers so that numbers with a small
|
|
||||||
// absolute value (for instance, -1) have a small varint encoded value too.
|
|
||||||
// It does this in a way that "zig-zags" back and forth through the positive and negative integers,
|
|
||||||
// so that -1 is encoded as 1, 1 is encoded as 2, -2 is encoded as 3, and so on.
|
|
||||||
func zig_zag_encode_i64(signedValue int64) (buffer []byte) {
|
|
||||||
buffer = make([]byte, 0)
|
|
||||||
var value = uint64((signedValue << 1) ^ (signedValue >> 63))
|
|
||||||
if value>>7 == 0 {
|
|
||||||
buffer = append(buffer, byte(value))
|
|
||||||
} else {
|
|
||||||
buffer = append(buffer, byte((value&0x7F)|0x80))
|
|
||||||
if value>>14 == 0 {
|
|
||||||
buffer = append(buffer, byte(value>>7))
|
|
||||||
} else {
|
|
||||||
buffer = append(buffer, byte((value>>7)|0x80))
|
|
||||||
if value>>21 == 0 {
|
|
||||||
buffer = append(buffer, byte(value>>14))
|
|
||||||
} else {
|
|
||||||
buffer = append(buffer, byte((value>>14)|0x80))
|
|
||||||
if value>>28 == 0 {
|
|
||||||
buffer = append(buffer, byte(value>>21))
|
|
||||||
} else {
|
|
||||||
buffer = append(buffer, byte((value>>21)|0x80))
|
|
||||||
if value>>35 == 0 {
|
|
||||||
buffer = append(buffer, byte(value>>28))
|
|
||||||
} else {
|
|
||||||
buffer = append(buffer, byte((value>>28)|0x80))
|
|
||||||
if value>>42 == 0 {
|
|
||||||
buffer = append(buffer, byte(value>>35))
|
|
||||||
} else {
|
|
||||||
buffer = append(buffer, byte((value>>35)|0x80))
|
|
||||||
if value>>49 == 0 {
|
|
||||||
buffer = append(buffer, byte(value>>42))
|
|
||||||
} else {
|
|
||||||
buffer = append(buffer, byte((value>>42)|0x80))
|
|
||||||
if value>>56 == 0 {
|
|
||||||
buffer = append(buffer, byte(value>>49))
|
|
||||||
} else {
|
|
||||||
buffer = append(buffer, byte((value>>49)|0x80))
|
|
||||||
buffer = append(buffer, byte(value>>56))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
20
vendor/github.com/beorn7/perks/LICENSE
generated
vendored
Normal file
20
vendor/github.com/beorn7/perks/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
Copyright (C) 2013 Blake Mizerany
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
2388
vendor/github.com/beorn7/perks/quantile/exampledata.txt
generated
vendored
Normal file
2388
vendor/github.com/beorn7/perks/quantile/exampledata.txt
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
316
vendor/github.com/beorn7/perks/quantile/stream.go
generated
vendored
Normal file
316
vendor/github.com/beorn7/perks/quantile/stream.go
generated
vendored
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
// Package quantile computes approximate quantiles over an unbounded data
|
||||||
|
// stream within low memory and CPU bounds.
|
||||||
|
//
|
||||||
|
// A small amount of accuracy is traded to achieve the above properties.
|
||||||
|
//
|
||||||
|
// Multiple streams can be merged before calling Query to generate a single set
|
||||||
|
// of results. This is meaningful when the streams represent the same type of
|
||||||
|
// data. See Merge and Samples.
|
||||||
|
//
|
||||||
|
// For more detailed information about the algorithm used, see:
|
||||||
|
//
|
||||||
|
// Effective Computation of Biased Quantiles over Data Streams
|
||||||
|
//
|
||||||
|
// http://www.cs.rutgers.edu/~muthu/bquant.pdf
|
||||||
|
package quantile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sample holds an observed value and meta information for compression. JSON
|
||||||
|
// tags have been added for convenience.
|
||||||
|
type Sample struct {
|
||||||
|
Value float64 `json:",string"`
|
||||||
|
Width float64 `json:",string"`
|
||||||
|
Delta float64 `json:",string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Samples represents a slice of samples. It implements sort.Interface.
|
||||||
|
type Samples []Sample
|
||||||
|
|
||||||
|
func (a Samples) Len() int { return len(a) }
|
||||||
|
func (a Samples) Less(i, j int) bool { return a[i].Value < a[j].Value }
|
||||||
|
func (a Samples) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
|
||||||
|
type invariant func(s *stream, r float64) float64
|
||||||
|
|
||||||
|
// NewLowBiased returns an initialized Stream for low-biased quantiles
|
||||||
|
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
|
||||||
|
// error guarantees can still be given even for the lower ranks of the data
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// The provided epsilon is a relative error, i.e. the true quantile of a value
|
||||||
|
// returned by a query is guaranteed to be within (1±Epsilon)*Quantile.
|
||||||
|
//
|
||||||
|
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
|
||||||
|
// properties.
|
||||||
|
func NewLowBiased(epsilon float64) *Stream {
|
||||||
|
ƒ := func(s *stream, r float64) float64 {
|
||||||
|
return 2 * epsilon * r
|
||||||
|
}
|
||||||
|
return newStream(ƒ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHighBiased returns an initialized Stream for high-biased quantiles
|
||||||
|
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
|
||||||
|
// error guarantees can still be given even for the higher ranks of the data
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// The provided epsilon is a relative error, i.e. the true quantile of a value
|
||||||
|
// returned by a query is guaranteed to be within 1-(1±Epsilon)*(1-Quantile).
|
||||||
|
//
|
||||||
|
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
|
||||||
|
// properties.
|
||||||
|
func NewHighBiased(epsilon float64) *Stream {
|
||||||
|
ƒ := func(s *stream, r float64) float64 {
|
||||||
|
return 2 * epsilon * (s.n - r)
|
||||||
|
}
|
||||||
|
return newStream(ƒ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTargeted returns an initialized Stream concerned with a particular set of
|
||||||
|
// quantile values that are supplied a priori. Knowing these a priori reduces
|
||||||
|
// space and computation time. The targets map maps the desired quantiles to
|
||||||
|
// their absolute errors, i.e. the true quantile of a value returned by a query
|
||||||
|
// is guaranteed to be within (Quantile±Epsilon).
|
||||||
|
//
|
||||||
|
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
|
||||||
|
func NewTargeted(targetMap map[float64]float64) *Stream {
|
||||||
|
// Convert map to slice to avoid slow iterations on a map.
|
||||||
|
// ƒ is called on the hot path, so converting the map to a slice
|
||||||
|
// beforehand results in significant CPU savings.
|
||||||
|
targets := targetMapToSlice(targetMap)
|
||||||
|
|
||||||
|
ƒ := func(s *stream, r float64) float64 {
|
||||||
|
var m = math.MaxFloat64
|
||||||
|
var f float64
|
||||||
|
for _, t := range targets {
|
||||||
|
if t.quantile*s.n <= r {
|
||||||
|
f = (2 * t.epsilon * r) / t.quantile
|
||||||
|
} else {
|
||||||
|
f = (2 * t.epsilon * (s.n - r)) / (1 - t.quantile)
|
||||||
|
}
|
||||||
|
if f < m {
|
||||||
|
m = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
return newStream(ƒ)
|
||||||
|
}
|
||||||
|
|
||||||
|
type target struct {
|
||||||
|
quantile float64
|
||||||
|
epsilon float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func targetMapToSlice(targetMap map[float64]float64) []target {
|
||||||
|
targets := make([]target, 0, len(targetMap))
|
||||||
|
|
||||||
|
for quantile, epsilon := range targetMap {
|
||||||
|
t := target{
|
||||||
|
quantile: quantile,
|
||||||
|
epsilon: epsilon,
|
||||||
|
}
|
||||||
|
targets = append(targets, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
return targets
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stream computes quantiles for a stream of float64s. It is not thread-safe by
|
||||||
|
// design. Take care when using across multiple goroutines.
|
||||||
|
type Stream struct {
|
||||||
|
*stream
|
||||||
|
b Samples
|
||||||
|
sorted bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStream(ƒ invariant) *Stream {
|
||||||
|
x := &stream{ƒ: ƒ}
|
||||||
|
return &Stream{x, make(Samples, 0, 500), true}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert inserts v into the stream.
|
||||||
|
func (s *Stream) Insert(v float64) {
|
||||||
|
s.insert(Sample{Value: v, Width: 1})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) insert(sample Sample) {
|
||||||
|
s.b = append(s.b, sample)
|
||||||
|
s.sorted = false
|
||||||
|
if len(s.b) == cap(s.b) {
|
||||||
|
s.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query returns the computed qth percentiles value. If s was created with
|
||||||
|
// NewTargeted, and q is not in the set of quantiles provided a priori, Query
|
||||||
|
// will return an unspecified result.
|
||||||
|
func (s *Stream) Query(q float64) float64 {
|
||||||
|
if !s.flushed() {
|
||||||
|
// Fast path when there hasn't been enough data for a flush;
|
||||||
|
// this also yields better accuracy for small sets of data.
|
||||||
|
l := len(s.b)
|
||||||
|
if l == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
i := int(math.Ceil(float64(l) * q))
|
||||||
|
if i > 0 {
|
||||||
|
i -= 1
|
||||||
|
}
|
||||||
|
s.maybeSort()
|
||||||
|
return s.b[i].Value
|
||||||
|
}
|
||||||
|
s.flush()
|
||||||
|
return s.stream.query(q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge merges samples into the underlying streams samples. This is handy when
|
||||||
|
// merging multiple streams from separate threads, database shards, etc.
|
||||||
|
//
|
||||||
|
// ATTENTION: This method is broken and does not yield correct results. The
|
||||||
|
// underlying algorithm is not capable of merging streams correctly.
|
||||||
|
func (s *Stream) Merge(samples Samples) {
|
||||||
|
sort.Sort(samples)
|
||||||
|
s.stream.merge(samples)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset reinitializes and clears the list reusing the samples buffer memory.
|
||||||
|
func (s *Stream) Reset() {
|
||||||
|
s.stream.reset()
|
||||||
|
s.b = s.b[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Samples returns stream samples held by s.
|
||||||
|
func (s *Stream) Samples() Samples {
|
||||||
|
if !s.flushed() {
|
||||||
|
return s.b
|
||||||
|
}
|
||||||
|
s.flush()
|
||||||
|
return s.stream.samples()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count returns the total number of samples observed in the stream
|
||||||
|
// since initialization.
|
||||||
|
func (s *Stream) Count() int {
|
||||||
|
return len(s.b) + s.stream.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) flush() {
|
||||||
|
s.maybeSort()
|
||||||
|
s.stream.merge(s.b)
|
||||||
|
s.b = s.b[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) maybeSort() {
|
||||||
|
if !s.sorted {
|
||||||
|
s.sorted = true
|
||||||
|
sort.Sort(s.b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) flushed() bool {
|
||||||
|
return len(s.stream.l) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type stream struct {
|
||||||
|
n float64
|
||||||
|
l []Sample
|
||||||
|
ƒ invariant
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) reset() {
|
||||||
|
s.l = s.l[:0]
|
||||||
|
s.n = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) insert(v float64) {
|
||||||
|
s.merge(Samples{{v, 1, 0}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) merge(samples Samples) {
|
||||||
|
// TODO(beorn7): This tries to merge not only individual samples, but
|
||||||
|
// whole summaries. The paper doesn't mention merging summaries at
|
||||||
|
// all. Unittests show that the merging is inaccurate. Find out how to
|
||||||
|
// do merges properly.
|
||||||
|
var r float64
|
||||||
|
i := 0
|
||||||
|
for _, sample := range samples {
|
||||||
|
for ; i < len(s.l); i++ {
|
||||||
|
c := s.l[i]
|
||||||
|
if c.Value > sample.Value {
|
||||||
|
// Insert at position i.
|
||||||
|
s.l = append(s.l, Sample{})
|
||||||
|
copy(s.l[i+1:], s.l[i:])
|
||||||
|
s.l[i] = Sample{
|
||||||
|
sample.Value,
|
||||||
|
sample.Width,
|
||||||
|
math.Max(sample.Delta, math.Floor(s.ƒ(s, r))-1),
|
||||||
|
// TODO(beorn7): How to calculate delta correctly?
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
goto inserted
|
||||||
|
}
|
||||||
|
r += c.Width
|
||||||
|
}
|
||||||
|
s.l = append(s.l, Sample{sample.Value, sample.Width, 0})
|
||||||
|
i++
|
||||||
|
inserted:
|
||||||
|
s.n += sample.Width
|
||||||
|
r += sample.Width
|
||||||
|
}
|
||||||
|
s.compress()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) count() int {
|
||||||
|
return int(s.n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) query(q float64) float64 {
|
||||||
|
t := math.Ceil(q * s.n)
|
||||||
|
t += math.Ceil(s.ƒ(s, t) / 2)
|
||||||
|
p := s.l[0]
|
||||||
|
var r float64
|
||||||
|
for _, c := range s.l[1:] {
|
||||||
|
r += p.Width
|
||||||
|
if r+c.Width+c.Delta > t {
|
||||||
|
return p.Value
|
||||||
|
}
|
||||||
|
p = c
|
||||||
|
}
|
||||||
|
return p.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) compress() {
|
||||||
|
if len(s.l) < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
x := s.l[len(s.l)-1]
|
||||||
|
xi := len(s.l) - 1
|
||||||
|
r := s.n - 1 - x.Width
|
||||||
|
|
||||||
|
for i := len(s.l) - 2; i >= 0; i-- {
|
||||||
|
c := s.l[i]
|
||||||
|
if c.Width+x.Width+x.Delta <= s.ƒ(s, r) {
|
||||||
|
x.Width += c.Width
|
||||||
|
s.l[xi] = x
|
||||||
|
// Remove element at i.
|
||||||
|
copy(s.l[i:], s.l[i+1:])
|
||||||
|
s.l = s.l[:len(s.l)-1]
|
||||||
|
xi -= 1
|
||||||
|
} else {
|
||||||
|
x = c
|
||||||
|
xi = i
|
||||||
|
}
|
||||||
|
r -= c.Width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) samples() Samples {
|
||||||
|
samples := make(Samples, len(s.l))
|
||||||
|
copy(samples, s.l)
|
||||||
|
return samples
|
||||||
|
}
|
||||||
31
vendor/github.com/cespare/xxhash/v2/README.md
generated
vendored
31
vendor/github.com/cespare/xxhash/v2/README.md
generated
vendored
@@ -3,8 +3,7 @@
|
|||||||
[](https://pkg.go.dev/github.com/cespare/xxhash/v2)
|
[](https://pkg.go.dev/github.com/cespare/xxhash/v2)
|
||||||
[](https://github.com/cespare/xxhash/actions/workflows/test.yml)
|
[](https://github.com/cespare/xxhash/actions/workflows/test.yml)
|
||||||
|
|
||||||
xxhash is a Go implementation of the 64-bit
|
xxhash is a Go implementation of the 64-bit [xxHash] algorithm, XXH64. This is a
|
||||||
[xxHash](http://cyan4973.github.io/xxHash/) algorithm, XXH64. This is a
|
|
||||||
high-quality hashing algorithm that is much faster than anything in the Go
|
high-quality hashing algorithm that is much faster than anything in the Go
|
||||||
standard library.
|
standard library.
|
||||||
|
|
||||||
@@ -25,8 +24,11 @@ func (*Digest) WriteString(string) (int, error)
|
|||||||
func (*Digest) Sum64() uint64
|
func (*Digest) Sum64() uint64
|
||||||
```
|
```
|
||||||
|
|
||||||
This implementation provides a fast pure-Go implementation and an even faster
|
The package is written with optimized pure Go and also contains even faster
|
||||||
assembly implementation for amd64.
|
assembly implementations for amd64 and arm64. If desired, the `purego` build tag
|
||||||
|
opts into using the Go code even on those architectures.
|
||||||
|
|
||||||
|
[xxHash]: http://cyan4973.github.io/xxHash/
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
@@ -45,19 +47,20 @@ I recommend using the latest release of Go.
|
|||||||
Here are some quick benchmarks comparing the pure-Go and assembly
|
Here are some quick benchmarks comparing the pure-Go and assembly
|
||||||
implementations of Sum64.
|
implementations of Sum64.
|
||||||
|
|
||||||
| input size | purego | asm |
|
| input size | purego | asm |
|
||||||
| --- | --- | --- |
|
| ---------- | --------- | --------- |
|
||||||
| 5 B | 979.66 MB/s | 1291.17 MB/s |
|
| 4 B | 1.3 GB/s | 1.2 GB/s |
|
||||||
| 100 B | 7475.26 MB/s | 7973.40 MB/s |
|
| 16 B | 2.9 GB/s | 3.5 GB/s |
|
||||||
| 4 KB | 17573.46 MB/s | 17602.65 MB/s |
|
| 100 B | 6.9 GB/s | 8.1 GB/s |
|
||||||
| 10 MB | 17131.46 MB/s | 17142.16 MB/s |
|
| 4 KB | 11.7 GB/s | 16.7 GB/s |
|
||||||
|
| 10 MB | 12.0 GB/s | 17.3 GB/s |
|
||||||
|
|
||||||
These numbers were generated on Ubuntu 18.04 with an Intel i7-8700K CPU using
|
These numbers were generated on Ubuntu 20.04 with an Intel Xeon Platinum 8252C
|
||||||
the following commands under Go 1.11.2:
|
CPU using the following commands under Go 1.19.2:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ go test -tags purego -benchtime 10s -bench '/xxhash,direct,bytes'
|
benchstat <(go test -tags purego -benchtime 500ms -count 15 -bench 'Sum64$')
|
||||||
$ go test -benchtime 10s -bench '/xxhash,direct,bytes'
|
benchstat <(go test -benchtime 500ms -count 15 -bench 'Sum64$')
|
||||||
```
|
```
|
||||||
|
|
||||||
## Projects using this package
|
## Projects using this package
|
||||||
|
|||||||
10
vendor/github.com/cespare/xxhash/v2/testall.sh
generated
vendored
Normal file
10
vendor/github.com/cespare/xxhash/v2/testall.sh
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -eu -o pipefail
|
||||||
|
|
||||||
|
# Small convenience script for running the tests with various combinations of
|
||||||
|
# arch/tags. This assumes we're running on amd64 and have qemu available.
|
||||||
|
|
||||||
|
go test ./...
|
||||||
|
go test -tags purego ./...
|
||||||
|
GOARCH=arm64 go test
|
||||||
|
GOARCH=arm64 go test -tags purego
|
||||||
47
vendor/github.com/cespare/xxhash/v2/xxhash.go
generated
vendored
47
vendor/github.com/cespare/xxhash/v2/xxhash.go
generated
vendored
@@ -16,19 +16,11 @@ const (
|
|||||||
prime5 uint64 = 2870177450012600261
|
prime5 uint64 = 2870177450012600261
|
||||||
)
|
)
|
||||||
|
|
||||||
// NOTE(caleb): I'm using both consts and vars of the primes. Using consts where
|
// Store the primes in an array as well.
|
||||||
// possible in the Go code is worth a small (but measurable) performance boost
|
//
|
||||||
// by avoiding some MOVQs. Vars are needed for the asm and also are useful for
|
// The consts are used when possible in Go code to avoid MOVs but we need a
|
||||||
// convenience in the Go code in a few places where we need to intentionally
|
// contiguous array of the assembly code.
|
||||||
// avoid constant arithmetic (e.g., v1 := prime1 + prime2 fails because the
|
var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5}
|
||||||
// result overflows a uint64).
|
|
||||||
var (
|
|
||||||
prime1v = prime1
|
|
||||||
prime2v = prime2
|
|
||||||
prime3v = prime3
|
|
||||||
prime4v = prime4
|
|
||||||
prime5v = prime5
|
|
||||||
)
|
|
||||||
|
|
||||||
// Digest implements hash.Hash64.
|
// Digest implements hash.Hash64.
|
||||||
type Digest struct {
|
type Digest struct {
|
||||||
@@ -50,10 +42,10 @@ func New() *Digest {
|
|||||||
|
|
||||||
// Reset clears the Digest's state so that it can be reused.
|
// Reset clears the Digest's state so that it can be reused.
|
||||||
func (d *Digest) Reset() {
|
func (d *Digest) Reset() {
|
||||||
d.v1 = prime1v + prime2
|
d.v1 = primes[0] + prime2
|
||||||
d.v2 = prime2
|
d.v2 = prime2
|
||||||
d.v3 = 0
|
d.v3 = 0
|
||||||
d.v4 = -prime1v
|
d.v4 = -primes[0]
|
||||||
d.total = 0
|
d.total = 0
|
||||||
d.n = 0
|
d.n = 0
|
||||||
}
|
}
|
||||||
@@ -69,21 +61,23 @@ func (d *Digest) Write(b []byte) (n int, err error) {
|
|||||||
n = len(b)
|
n = len(b)
|
||||||
d.total += uint64(n)
|
d.total += uint64(n)
|
||||||
|
|
||||||
|
memleft := d.mem[d.n&(len(d.mem)-1):]
|
||||||
|
|
||||||
if d.n+n < 32 {
|
if d.n+n < 32 {
|
||||||
// This new data doesn't even fill the current block.
|
// This new data doesn't even fill the current block.
|
||||||
copy(d.mem[d.n:], b)
|
copy(memleft, b)
|
||||||
d.n += n
|
d.n += n
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.n > 0 {
|
if d.n > 0 {
|
||||||
// Finish off the partial block.
|
// Finish off the partial block.
|
||||||
copy(d.mem[d.n:], b)
|
c := copy(memleft, b)
|
||||||
d.v1 = round(d.v1, u64(d.mem[0:8]))
|
d.v1 = round(d.v1, u64(d.mem[0:8]))
|
||||||
d.v2 = round(d.v2, u64(d.mem[8:16]))
|
d.v2 = round(d.v2, u64(d.mem[8:16]))
|
||||||
d.v3 = round(d.v3, u64(d.mem[16:24]))
|
d.v3 = round(d.v3, u64(d.mem[16:24]))
|
||||||
d.v4 = round(d.v4, u64(d.mem[24:32]))
|
d.v4 = round(d.v4, u64(d.mem[24:32]))
|
||||||
b = b[32-d.n:]
|
b = b[c:]
|
||||||
d.n = 0
|
d.n = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,21 +127,20 @@ func (d *Digest) Sum64() uint64 {
|
|||||||
|
|
||||||
h += d.total
|
h += d.total
|
||||||
|
|
||||||
i, end := 0, d.n
|
b := d.mem[:d.n&(len(d.mem)-1)]
|
||||||
for ; i+8 <= end; i += 8 {
|
for ; len(b) >= 8; b = b[8:] {
|
||||||
k1 := round(0, u64(d.mem[i:i+8]))
|
k1 := round(0, u64(b[:8]))
|
||||||
h ^= k1
|
h ^= k1
|
||||||
h = rol27(h)*prime1 + prime4
|
h = rol27(h)*prime1 + prime4
|
||||||
}
|
}
|
||||||
if i+4 <= end {
|
if len(b) >= 4 {
|
||||||
h ^= uint64(u32(d.mem[i:i+4])) * prime1
|
h ^= uint64(u32(b[:4])) * prime1
|
||||||
h = rol23(h)*prime2 + prime3
|
h = rol23(h)*prime2 + prime3
|
||||||
i += 4
|
b = b[4:]
|
||||||
}
|
}
|
||||||
for i < end {
|
for ; len(b) > 0; b = b[1:] {
|
||||||
h ^= uint64(d.mem[i]) * prime5
|
h ^= uint64(b[0]) * prime5
|
||||||
h = rol11(h) * prime1
|
h = rol11(h) * prime1
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h ^= h >> 33
|
h ^= h >> 33
|
||||||
|
|||||||
308
vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s
generated
vendored
308
vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s
generated
vendored
@@ -1,215 +1,209 @@
|
|||||||
|
//go:build !appengine && gc && !purego
|
||||||
// +build !appengine
|
// +build !appengine
|
||||||
// +build gc
|
// +build gc
|
||||||
// +build !purego
|
// +build !purego
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
// Register allocation:
|
// Registers:
|
||||||
// AX h
|
#define h AX
|
||||||
// SI pointer to advance through b
|
#define d AX
|
||||||
// DX n
|
#define p SI // pointer to advance through b
|
||||||
// BX loop end
|
#define n DX
|
||||||
// R8 v1, k1
|
#define end BX // loop end
|
||||||
// R9 v2
|
#define v1 R8
|
||||||
// R10 v3
|
#define v2 R9
|
||||||
// R11 v4
|
#define v3 R10
|
||||||
// R12 tmp
|
#define v4 R11
|
||||||
// R13 prime1v
|
#define x R12
|
||||||
// R14 prime2v
|
#define prime1 R13
|
||||||
// DI prime4v
|
#define prime2 R14
|
||||||
|
#define prime4 DI
|
||||||
|
|
||||||
// round reads from and advances the buffer pointer in SI.
|
#define round(acc, x) \
|
||||||
// It assumes that R13 has prime1v and R14 has prime2v.
|
IMULQ prime2, x \
|
||||||
#define round(r) \
|
ADDQ x, acc \
|
||||||
MOVQ (SI), R12 \
|
ROLQ $31, acc \
|
||||||
ADDQ $8, SI \
|
IMULQ prime1, acc
|
||||||
IMULQ R14, R12 \
|
|
||||||
ADDQ R12, r \
|
|
||||||
ROLQ $31, r \
|
|
||||||
IMULQ R13, r
|
|
||||||
|
|
||||||
// mergeRound applies a merge round on the two registers acc and val.
|
// round0 performs the operation x = round(0, x).
|
||||||
// It assumes that R13 has prime1v, R14 has prime2v, and DI has prime4v.
|
#define round0(x) \
|
||||||
#define mergeRound(acc, val) \
|
IMULQ prime2, x \
|
||||||
IMULQ R14, val \
|
ROLQ $31, x \
|
||||||
ROLQ $31, val \
|
IMULQ prime1, x
|
||||||
IMULQ R13, val \
|
|
||||||
XORQ val, acc \
|
// mergeRound applies a merge round on the two registers acc and x.
|
||||||
IMULQ R13, acc \
|
// It assumes that prime1, prime2, and prime4 have been loaded.
|
||||||
ADDQ DI, acc
|
#define mergeRound(acc, x) \
|
||||||
|
round0(x) \
|
||||||
|
XORQ x, acc \
|
||||||
|
IMULQ prime1, acc \
|
||||||
|
ADDQ prime4, acc
|
||||||
|
|
||||||
|
// blockLoop processes as many 32-byte blocks as possible,
|
||||||
|
// updating v1, v2, v3, and v4. It assumes that there is at least one block
|
||||||
|
// to process.
|
||||||
|
#define blockLoop() \
|
||||||
|
loop: \
|
||||||
|
MOVQ +0(p), x \
|
||||||
|
round(v1, x) \
|
||||||
|
MOVQ +8(p), x \
|
||||||
|
round(v2, x) \
|
||||||
|
MOVQ +16(p), x \
|
||||||
|
round(v3, x) \
|
||||||
|
MOVQ +24(p), x \
|
||||||
|
round(v4, x) \
|
||||||
|
ADDQ $32, p \
|
||||||
|
CMPQ p, end \
|
||||||
|
JLE loop
|
||||||
|
|
||||||
// func Sum64(b []byte) uint64
|
// func Sum64(b []byte) uint64
|
||||||
TEXT ·Sum64(SB), NOSPLIT, $0-32
|
TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32
|
||||||
// Load fixed primes.
|
// Load fixed primes.
|
||||||
MOVQ ·prime1v(SB), R13
|
MOVQ ·primes+0(SB), prime1
|
||||||
MOVQ ·prime2v(SB), R14
|
MOVQ ·primes+8(SB), prime2
|
||||||
MOVQ ·prime4v(SB), DI
|
MOVQ ·primes+24(SB), prime4
|
||||||
|
|
||||||
// Load slice.
|
// Load slice.
|
||||||
MOVQ b_base+0(FP), SI
|
MOVQ b_base+0(FP), p
|
||||||
MOVQ b_len+8(FP), DX
|
MOVQ b_len+8(FP), n
|
||||||
LEAQ (SI)(DX*1), BX
|
LEAQ (p)(n*1), end
|
||||||
|
|
||||||
// The first loop limit will be len(b)-32.
|
// The first loop limit will be len(b)-32.
|
||||||
SUBQ $32, BX
|
SUBQ $32, end
|
||||||
|
|
||||||
// Check whether we have at least one block.
|
// Check whether we have at least one block.
|
||||||
CMPQ DX, $32
|
CMPQ n, $32
|
||||||
JLT noBlocks
|
JLT noBlocks
|
||||||
|
|
||||||
// Set up initial state (v1, v2, v3, v4).
|
// Set up initial state (v1, v2, v3, v4).
|
||||||
MOVQ R13, R8
|
MOVQ prime1, v1
|
||||||
ADDQ R14, R8
|
ADDQ prime2, v1
|
||||||
MOVQ R14, R9
|
MOVQ prime2, v2
|
||||||
XORQ R10, R10
|
XORQ v3, v3
|
||||||
XORQ R11, R11
|
XORQ v4, v4
|
||||||
SUBQ R13, R11
|
SUBQ prime1, v4
|
||||||
|
|
||||||
// Loop until SI > BX.
|
blockLoop()
|
||||||
blockLoop:
|
|
||||||
round(R8)
|
|
||||||
round(R9)
|
|
||||||
round(R10)
|
|
||||||
round(R11)
|
|
||||||
|
|
||||||
CMPQ SI, BX
|
MOVQ v1, h
|
||||||
JLE blockLoop
|
ROLQ $1, h
|
||||||
|
MOVQ v2, x
|
||||||
|
ROLQ $7, x
|
||||||
|
ADDQ x, h
|
||||||
|
MOVQ v3, x
|
||||||
|
ROLQ $12, x
|
||||||
|
ADDQ x, h
|
||||||
|
MOVQ v4, x
|
||||||
|
ROLQ $18, x
|
||||||
|
ADDQ x, h
|
||||||
|
|
||||||
MOVQ R8, AX
|
mergeRound(h, v1)
|
||||||
ROLQ $1, AX
|
mergeRound(h, v2)
|
||||||
MOVQ R9, R12
|
mergeRound(h, v3)
|
||||||
ROLQ $7, R12
|
mergeRound(h, v4)
|
||||||
ADDQ R12, AX
|
|
||||||
MOVQ R10, R12
|
|
||||||
ROLQ $12, R12
|
|
||||||
ADDQ R12, AX
|
|
||||||
MOVQ R11, R12
|
|
||||||
ROLQ $18, R12
|
|
||||||
ADDQ R12, AX
|
|
||||||
|
|
||||||
mergeRound(AX, R8)
|
|
||||||
mergeRound(AX, R9)
|
|
||||||
mergeRound(AX, R10)
|
|
||||||
mergeRound(AX, R11)
|
|
||||||
|
|
||||||
JMP afterBlocks
|
JMP afterBlocks
|
||||||
|
|
||||||
noBlocks:
|
noBlocks:
|
||||||
MOVQ ·prime5v(SB), AX
|
MOVQ ·primes+32(SB), h
|
||||||
|
|
||||||
afterBlocks:
|
afterBlocks:
|
||||||
ADDQ DX, AX
|
ADDQ n, h
|
||||||
|
|
||||||
// Right now BX has len(b)-32, and we want to loop until SI > len(b)-8.
|
ADDQ $24, end
|
||||||
ADDQ $24, BX
|
CMPQ p, end
|
||||||
|
JG try4
|
||||||
|
|
||||||
CMPQ SI, BX
|
loop8:
|
||||||
JG fourByte
|
MOVQ (p), x
|
||||||
|
ADDQ $8, p
|
||||||
|
round0(x)
|
||||||
|
XORQ x, h
|
||||||
|
ROLQ $27, h
|
||||||
|
IMULQ prime1, h
|
||||||
|
ADDQ prime4, h
|
||||||
|
|
||||||
wordLoop:
|
CMPQ p, end
|
||||||
// Calculate k1.
|
JLE loop8
|
||||||
MOVQ (SI), R8
|
|
||||||
ADDQ $8, SI
|
|
||||||
IMULQ R14, R8
|
|
||||||
ROLQ $31, R8
|
|
||||||
IMULQ R13, R8
|
|
||||||
|
|
||||||
XORQ R8, AX
|
try4:
|
||||||
ROLQ $27, AX
|
ADDQ $4, end
|
||||||
IMULQ R13, AX
|
CMPQ p, end
|
||||||
ADDQ DI, AX
|
JG try1
|
||||||
|
|
||||||
CMPQ SI, BX
|
MOVL (p), x
|
||||||
JLE wordLoop
|
ADDQ $4, p
|
||||||
|
IMULQ prime1, x
|
||||||
|
XORQ x, h
|
||||||
|
|
||||||
fourByte:
|
ROLQ $23, h
|
||||||
ADDQ $4, BX
|
IMULQ prime2, h
|
||||||
CMPQ SI, BX
|
ADDQ ·primes+16(SB), h
|
||||||
JG singles
|
|
||||||
|
|
||||||
MOVL (SI), R8
|
try1:
|
||||||
ADDQ $4, SI
|
ADDQ $4, end
|
||||||
IMULQ R13, R8
|
CMPQ p, end
|
||||||
XORQ R8, AX
|
|
||||||
|
|
||||||
ROLQ $23, AX
|
|
||||||
IMULQ R14, AX
|
|
||||||
ADDQ ·prime3v(SB), AX
|
|
||||||
|
|
||||||
singles:
|
|
||||||
ADDQ $4, BX
|
|
||||||
CMPQ SI, BX
|
|
||||||
JGE finalize
|
JGE finalize
|
||||||
|
|
||||||
singlesLoop:
|
loop1:
|
||||||
MOVBQZX (SI), R12
|
MOVBQZX (p), x
|
||||||
ADDQ $1, SI
|
ADDQ $1, p
|
||||||
IMULQ ·prime5v(SB), R12
|
IMULQ ·primes+32(SB), x
|
||||||
XORQ R12, AX
|
XORQ x, h
|
||||||
|
ROLQ $11, h
|
||||||
|
IMULQ prime1, h
|
||||||
|
|
||||||
ROLQ $11, AX
|
CMPQ p, end
|
||||||
IMULQ R13, AX
|
JL loop1
|
||||||
|
|
||||||
CMPQ SI, BX
|
|
||||||
JL singlesLoop
|
|
||||||
|
|
||||||
finalize:
|
finalize:
|
||||||
MOVQ AX, R12
|
MOVQ h, x
|
||||||
SHRQ $33, R12
|
SHRQ $33, x
|
||||||
XORQ R12, AX
|
XORQ x, h
|
||||||
IMULQ R14, AX
|
IMULQ prime2, h
|
||||||
MOVQ AX, R12
|
MOVQ h, x
|
||||||
SHRQ $29, R12
|
SHRQ $29, x
|
||||||
XORQ R12, AX
|
XORQ x, h
|
||||||
IMULQ ·prime3v(SB), AX
|
IMULQ ·primes+16(SB), h
|
||||||
MOVQ AX, R12
|
MOVQ h, x
|
||||||
SHRQ $32, R12
|
SHRQ $32, x
|
||||||
XORQ R12, AX
|
XORQ x, h
|
||||||
|
|
||||||
MOVQ AX, ret+24(FP)
|
MOVQ h, ret+24(FP)
|
||||||
RET
|
RET
|
||||||
|
|
||||||
// writeBlocks uses the same registers as above except that it uses AX to store
|
|
||||||
// the d pointer.
|
|
||||||
|
|
||||||
// func writeBlocks(d *Digest, b []byte) int
|
// func writeBlocks(d *Digest, b []byte) int
|
||||||
TEXT ·writeBlocks(SB), NOSPLIT, $0-40
|
TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40
|
||||||
// Load fixed primes needed for round.
|
// Load fixed primes needed for round.
|
||||||
MOVQ ·prime1v(SB), R13
|
MOVQ ·primes+0(SB), prime1
|
||||||
MOVQ ·prime2v(SB), R14
|
MOVQ ·primes+8(SB), prime2
|
||||||
|
|
||||||
// Load slice.
|
// Load slice.
|
||||||
MOVQ b_base+8(FP), SI
|
MOVQ b_base+8(FP), p
|
||||||
MOVQ b_len+16(FP), DX
|
MOVQ b_len+16(FP), n
|
||||||
LEAQ (SI)(DX*1), BX
|
LEAQ (p)(n*1), end
|
||||||
SUBQ $32, BX
|
SUBQ $32, end
|
||||||
|
|
||||||
// Load vN from d.
|
// Load vN from d.
|
||||||
MOVQ d+0(FP), AX
|
MOVQ s+0(FP), d
|
||||||
MOVQ 0(AX), R8 // v1
|
MOVQ 0(d), v1
|
||||||
MOVQ 8(AX), R9 // v2
|
MOVQ 8(d), v2
|
||||||
MOVQ 16(AX), R10 // v3
|
MOVQ 16(d), v3
|
||||||
MOVQ 24(AX), R11 // v4
|
MOVQ 24(d), v4
|
||||||
|
|
||||||
// We don't need to check the loop condition here; this function is
|
// We don't need to check the loop condition here; this function is
|
||||||
// always called with at least one block of data to process.
|
// always called with at least one block of data to process.
|
||||||
blockLoop:
|
blockLoop()
|
||||||
round(R8)
|
|
||||||
round(R9)
|
|
||||||
round(R10)
|
|
||||||
round(R11)
|
|
||||||
|
|
||||||
CMPQ SI, BX
|
|
||||||
JLE blockLoop
|
|
||||||
|
|
||||||
// Copy vN back to d.
|
// Copy vN back to d.
|
||||||
MOVQ R8, 0(AX)
|
MOVQ v1, 0(d)
|
||||||
MOVQ R9, 8(AX)
|
MOVQ v2, 8(d)
|
||||||
MOVQ R10, 16(AX)
|
MOVQ v3, 16(d)
|
||||||
MOVQ R11, 24(AX)
|
MOVQ v4, 24(d)
|
||||||
|
|
||||||
// The number of bytes written is SI minus the old base pointer.
|
// The number of bytes written is p minus the old base pointer.
|
||||||
SUBQ b_base+8(FP), SI
|
SUBQ b_base+8(FP), p
|
||||||
MOVQ SI, ret+32(FP)
|
MOVQ p, ret+32(FP)
|
||||||
|
|
||||||
RET
|
RET
|
||||||
|
|||||||
183
vendor/github.com/cespare/xxhash/v2/xxhash_arm64.s
generated
vendored
Normal file
183
vendor/github.com/cespare/xxhash/v2/xxhash_arm64.s
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
//go:build !appengine && gc && !purego
|
||||||
|
// +build !appengine
|
||||||
|
// +build gc
|
||||||
|
// +build !purego
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// Registers:
|
||||||
|
#define digest R1
|
||||||
|
#define h R2 // return value
|
||||||
|
#define p R3 // input pointer
|
||||||
|
#define n R4 // input length
|
||||||
|
#define nblocks R5 // n / 32
|
||||||
|
#define prime1 R7
|
||||||
|
#define prime2 R8
|
||||||
|
#define prime3 R9
|
||||||
|
#define prime4 R10
|
||||||
|
#define prime5 R11
|
||||||
|
#define v1 R12
|
||||||
|
#define v2 R13
|
||||||
|
#define v3 R14
|
||||||
|
#define v4 R15
|
||||||
|
#define x1 R20
|
||||||
|
#define x2 R21
|
||||||
|
#define x3 R22
|
||||||
|
#define x4 R23
|
||||||
|
|
||||||
|
#define round(acc, x) \
|
||||||
|
MADD prime2, acc, x, acc \
|
||||||
|
ROR $64-31, acc \
|
||||||
|
MUL prime1, acc
|
||||||
|
|
||||||
|
// round0 performs the operation x = round(0, x).
|
||||||
|
#define round0(x) \
|
||||||
|
MUL prime2, x \
|
||||||
|
ROR $64-31, x \
|
||||||
|
MUL prime1, x
|
||||||
|
|
||||||
|
#define mergeRound(acc, x) \
|
||||||
|
round0(x) \
|
||||||
|
EOR x, acc \
|
||||||
|
MADD acc, prime4, prime1, acc
|
||||||
|
|
||||||
|
// blockLoop processes as many 32-byte blocks as possible,
|
||||||
|
// updating v1, v2, v3, and v4. It assumes that n >= 32.
|
||||||
|
#define blockLoop() \
|
||||||
|
LSR $5, n, nblocks \
|
||||||
|
PCALIGN $16 \
|
||||||
|
loop: \
|
||||||
|
LDP.P 16(p), (x1, x2) \
|
||||||
|
LDP.P 16(p), (x3, x4) \
|
||||||
|
round(v1, x1) \
|
||||||
|
round(v2, x2) \
|
||||||
|
round(v3, x3) \
|
||||||
|
round(v4, x4) \
|
||||||
|
SUB $1, nblocks \
|
||||||
|
CBNZ nblocks, loop
|
||||||
|
|
||||||
|
// func Sum64(b []byte) uint64
|
||||||
|
TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32
|
||||||
|
LDP b_base+0(FP), (p, n)
|
||||||
|
|
||||||
|
LDP ·primes+0(SB), (prime1, prime2)
|
||||||
|
LDP ·primes+16(SB), (prime3, prime4)
|
||||||
|
MOVD ·primes+32(SB), prime5
|
||||||
|
|
||||||
|
CMP $32, n
|
||||||
|
CSEL LT, prime5, ZR, h // if n < 32 { h = prime5 } else { h = 0 }
|
||||||
|
BLT afterLoop
|
||||||
|
|
||||||
|
ADD prime1, prime2, v1
|
||||||
|
MOVD prime2, v2
|
||||||
|
MOVD $0, v3
|
||||||
|
NEG prime1, v4
|
||||||
|
|
||||||
|
blockLoop()
|
||||||
|
|
||||||
|
ROR $64-1, v1, x1
|
||||||
|
ROR $64-7, v2, x2
|
||||||
|
ADD x1, x2
|
||||||
|
ROR $64-12, v3, x3
|
||||||
|
ROR $64-18, v4, x4
|
||||||
|
ADD x3, x4
|
||||||
|
ADD x2, x4, h
|
||||||
|
|
||||||
|
mergeRound(h, v1)
|
||||||
|
mergeRound(h, v2)
|
||||||
|
mergeRound(h, v3)
|
||||||
|
mergeRound(h, v4)
|
||||||
|
|
||||||
|
afterLoop:
|
||||||
|
ADD n, h
|
||||||
|
|
||||||
|
TBZ $4, n, try8
|
||||||
|
LDP.P 16(p), (x1, x2)
|
||||||
|
|
||||||
|
round0(x1)
|
||||||
|
|
||||||
|
// NOTE: here and below, sequencing the EOR after the ROR (using a
|
||||||
|
// rotated register) is worth a small but measurable speedup for small
|
||||||
|
// inputs.
|
||||||
|
ROR $64-27, h
|
||||||
|
EOR x1 @> 64-27, h, h
|
||||||
|
MADD h, prime4, prime1, h
|
||||||
|
|
||||||
|
round0(x2)
|
||||||
|
ROR $64-27, h
|
||||||
|
EOR x2 @> 64-27, h, h
|
||||||
|
MADD h, prime4, prime1, h
|
||||||
|
|
||||||
|
try8:
|
||||||
|
TBZ $3, n, try4
|
||||||
|
MOVD.P 8(p), x1
|
||||||
|
|
||||||
|
round0(x1)
|
||||||
|
ROR $64-27, h
|
||||||
|
EOR x1 @> 64-27, h, h
|
||||||
|
MADD h, prime4, prime1, h
|
||||||
|
|
||||||
|
try4:
|
||||||
|
TBZ $2, n, try2
|
||||||
|
MOVWU.P 4(p), x2
|
||||||
|
|
||||||
|
MUL prime1, x2
|
||||||
|
ROR $64-23, h
|
||||||
|
EOR x2 @> 64-23, h, h
|
||||||
|
MADD h, prime3, prime2, h
|
||||||
|
|
||||||
|
try2:
|
||||||
|
TBZ $1, n, try1
|
||||||
|
MOVHU.P 2(p), x3
|
||||||
|
AND $255, x3, x1
|
||||||
|
LSR $8, x3, x2
|
||||||
|
|
||||||
|
MUL prime5, x1
|
||||||
|
ROR $64-11, h
|
||||||
|
EOR x1 @> 64-11, h, h
|
||||||
|
MUL prime1, h
|
||||||
|
|
||||||
|
MUL prime5, x2
|
||||||
|
ROR $64-11, h
|
||||||
|
EOR x2 @> 64-11, h, h
|
||||||
|
MUL prime1, h
|
||||||
|
|
||||||
|
try1:
|
||||||
|
TBZ $0, n, finalize
|
||||||
|
MOVBU (p), x4
|
||||||
|
|
||||||
|
MUL prime5, x4
|
||||||
|
ROR $64-11, h
|
||||||
|
EOR x4 @> 64-11, h, h
|
||||||
|
MUL prime1, h
|
||||||
|
|
||||||
|
finalize:
|
||||||
|
EOR h >> 33, h
|
||||||
|
MUL prime2, h
|
||||||
|
EOR h >> 29, h
|
||||||
|
MUL prime3, h
|
||||||
|
EOR h >> 32, h
|
||||||
|
|
||||||
|
MOVD h, ret+24(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func writeBlocks(d *Digest, b []byte) int
|
||||||
|
TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40
|
||||||
|
LDP ·primes+0(SB), (prime1, prime2)
|
||||||
|
|
||||||
|
// Load state. Assume v[1-4] are stored contiguously.
|
||||||
|
MOVD d+0(FP), digest
|
||||||
|
LDP 0(digest), (v1, v2)
|
||||||
|
LDP 16(digest), (v3, v4)
|
||||||
|
|
||||||
|
LDP b_base+8(FP), (p, n)
|
||||||
|
|
||||||
|
blockLoop()
|
||||||
|
|
||||||
|
// Store updated state.
|
||||||
|
STP (v1, v2), 0(digest)
|
||||||
|
STP (v3, v4), 16(digest)
|
||||||
|
|
||||||
|
BIC $31, n
|
||||||
|
MOVD n, ret+32(FP)
|
||||||
|
RET
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build (amd64 || arm64) && !appengine && gc && !purego
|
||||||
|
// +build amd64 arm64
|
||||||
// +build !appengine
|
// +build !appengine
|
||||||
// +build gc
|
// +build gc
|
||||||
// +build !purego
|
// +build !purego
|
||||||
22
vendor/github.com/cespare/xxhash/v2/xxhash_other.go
generated
vendored
22
vendor/github.com/cespare/xxhash/v2/xxhash_other.go
generated
vendored
@@ -1,4 +1,5 @@
|
|||||||
// +build !amd64 appengine !gc purego
|
//go:build (!amd64 && !arm64) || appengine || !gc || purego
|
||||||
|
// +build !amd64,!arm64 appengine !gc purego
|
||||||
|
|
||||||
package xxhash
|
package xxhash
|
||||||
|
|
||||||
@@ -14,10 +15,10 @@ func Sum64(b []byte) uint64 {
|
|||||||
var h uint64
|
var h uint64
|
||||||
|
|
||||||
if n >= 32 {
|
if n >= 32 {
|
||||||
v1 := prime1v + prime2
|
v1 := primes[0] + prime2
|
||||||
v2 := prime2
|
v2 := prime2
|
||||||
v3 := uint64(0)
|
v3 := uint64(0)
|
||||||
v4 := -prime1v
|
v4 := -primes[0]
|
||||||
for len(b) >= 32 {
|
for len(b) >= 32 {
|
||||||
v1 = round(v1, u64(b[0:8:len(b)]))
|
v1 = round(v1, u64(b[0:8:len(b)]))
|
||||||
v2 = round(v2, u64(b[8:16:len(b)]))
|
v2 = round(v2, u64(b[8:16:len(b)]))
|
||||||
@@ -36,19 +37,18 @@ func Sum64(b []byte) uint64 {
|
|||||||
|
|
||||||
h += uint64(n)
|
h += uint64(n)
|
||||||
|
|
||||||
i, end := 0, len(b)
|
for ; len(b) >= 8; b = b[8:] {
|
||||||
for ; i+8 <= end; i += 8 {
|
k1 := round(0, u64(b[:8]))
|
||||||
k1 := round(0, u64(b[i:i+8:len(b)]))
|
|
||||||
h ^= k1
|
h ^= k1
|
||||||
h = rol27(h)*prime1 + prime4
|
h = rol27(h)*prime1 + prime4
|
||||||
}
|
}
|
||||||
if i+4 <= end {
|
if len(b) >= 4 {
|
||||||
h ^= uint64(u32(b[i:i+4:len(b)])) * prime1
|
h ^= uint64(u32(b[:4])) * prime1
|
||||||
h = rol23(h)*prime2 + prime3
|
h = rol23(h)*prime2 + prime3
|
||||||
i += 4
|
b = b[4:]
|
||||||
}
|
}
|
||||||
for ; i < end; i++ {
|
for ; len(b) > 0; b = b[1:] {
|
||||||
h ^= uint64(b[i]) * prime5
|
h ^= uint64(b[0]) * prime5
|
||||||
h = rol11(h) * prime1
|
h = rol11(h) * prime1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
vendor/github.com/cespare/xxhash/v2/xxhash_safe.go
generated
vendored
1
vendor/github.com/cespare/xxhash/v2/xxhash_safe.go
generated
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build appengine
|
||||||
// +build appengine
|
// +build appengine
|
||||||
|
|
||||||
// This file contains the safe implementations of otherwise unsafe-using code.
|
// This file contains the safe implementations of otherwise unsafe-using code.
|
||||||
|
|||||||
3
vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go
generated
vendored
3
vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go
generated
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build !appengine
|
||||||
// +build !appengine
|
// +build !appengine
|
||||||
|
|
||||||
// This file encapsulates usage of unsafe.
|
// This file encapsulates usage of unsafe.
|
||||||
@@ -11,7 +12,7 @@ import (
|
|||||||
|
|
||||||
// In the future it's possible that compiler optimizations will make these
|
// In the future it's possible that compiler optimizations will make these
|
||||||
// XxxString functions unnecessary by realizing that calls such as
|
// XxxString functions unnecessary by realizing that calls such as
|
||||||
// Sum64([]byte(s)) don't need to copy s. See https://golang.org/issue/2205.
|
// Sum64([]byte(s)) don't need to copy s. See https://go.dev/issue/2205.
|
||||||
// If that happens, even if we keep these functions they can be replaced with
|
// If that happens, even if we keep these functions they can be replaced with
|
||||||
// the trivial safe code.
|
// the trivial safe code.
|
||||||
|
|
||||||
|
|||||||
6
vendor/github.com/cockroachdb/errors/.travis.yml
generated
vendored
6
vendor/github.com/cockroachdb/errors/.travis.yml
generated
vendored
@@ -1,6 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.13.x
|
|
||||||
- 1.14.x
|
|
||||||
- 1.15.x
|
|
||||||
45
vendor/github.com/cockroachdb/errors/Makefile.update-protos
generated
vendored
45
vendor/github.com/cockroachdb/errors/Makefile.update-protos
generated
vendored
@@ -6,13 +6,16 @@
|
|||||||
# go get github.com/gogo/protobuf/types
|
# go get github.com/gogo/protobuf/types
|
||||||
# go get github.com/gogo/protobuf/protoc-gen-gogo
|
# go get github.com/gogo/protobuf/protoc-gen-gogo
|
||||||
#
|
#
|
||||||
# Note: as of 2020-11-01, it is not (yet) possible to use gogoproto
|
# Note: as of 2021-04-13, we like to use a custom protoc-gen-gogo
|
||||||
# v1.3.x to generate .pb.go files so that they are compatible with
|
# with additional options, to stabilize the marshalled
|
||||||
# CockroachDB. This is because CockroachDB hardcodes gogoproto at
|
# encoding of objects (so that they are deterministic
|
||||||
# version v1.2, and thus does not provide v1.3's
|
# across marshal/unmarshal cycles) and reduce the memory footprint
|
||||||
# proto.GoGoProtoPackageIsVersion3.
|
# of objects:
|
||||||
#
|
#
|
||||||
# For details, see https://github.com/cockroachdb/cockroach/issues/56378
|
# vanity.TurnOnStable_MarshalerAll,
|
||||||
|
# vanity.TurnOffGoUnrecognizedAll,
|
||||||
|
# vanity.TurnOffGoUnkeyedAll,
|
||||||
|
# vanity.TurnOffGoSizecacheAll,
|
||||||
#
|
#
|
||||||
# Until this is resolved, the "go get" commands above are not
|
# Until this is resolved, the "go get" commands above are not
|
||||||
# adequate; instead:
|
# adequate; instead:
|
||||||
@@ -23,21 +26,33 @@
|
|||||||
#
|
#
|
||||||
# 2. run `make -f Makefile.update-protos` with this PATH active.
|
# 2. run `make -f Makefile.update-protos` with this PATH active.
|
||||||
|
|
||||||
PROTOS := $(wildcard errorspb/*.proto)
|
export SHELL := env PWD=$(CURDIR) bash
|
||||||
|
|
||||||
|
PROTOS := $(wildcard \
|
||||||
|
errbase/internal/*.proto \
|
||||||
|
errorspb/*.proto \
|
||||||
|
extgrpc/*.proto \
|
||||||
|
exthttp/*.proto \
|
||||||
|
grpc/*.proto \
|
||||||
|
markers/internal/*.proto \
|
||||||
|
)
|
||||||
GO_SOURCES = $(PROTOS:.proto=.pb.go)
|
GO_SOURCES = $(PROTOS:.proto=.pb.go)
|
||||||
|
|
||||||
SED = sed
|
SED = sed
|
||||||
SED_INPLACE := $(shell $(SED) --version 2>&1 | grep -q GNU && echo -i || echo "-i ''")
|
SED_INPLACE := $(shell $(SED) --version 2>&1 | grep -q GNU && echo -i || echo "-i ''")
|
||||||
|
|
||||||
all: $(PROTOS)
|
all: $(PROTOS)
|
||||||
protoc \
|
set -e; for dir in $(sort $(dir $(PROTOS))); do \
|
||||||
-I$$GOPATH/src/ \
|
protoc \
|
||||||
-I$$GOPATH/src/github.com \
|
-I. \
|
||||||
-I$$GOPATH/src/github.com/gogo/protobuf \
|
-I$$GOPATH/src/ \
|
||||||
-I$$GOPATH/src/github.com/gogo/protobuf/protobuf \
|
-I$$GOPATH/src/github.com \
|
||||||
--gogoroach_out=Mgoogle/protobuf/any.proto=github.com/gogo/protobuf/types,plugins=grpc,import_prefix=:. \
|
-I$$GOPATH/src/github.com/cockroachdb/errors \
|
||||||
$(PROTOS:%=$(GOPATH)/src/github.com/cockroachdb/errors/%)
|
-I$$GOPATH/src/github.com/gogo/protobuf \
|
||||||
mv -f github.com/cockroachdb/errors/errorspb/*.pb.go errorspb/
|
-I$$GOPATH/src/github.com/gogo/protobuf/protobuf \
|
||||||
|
--gogoroach_out=Mgoogle/protobuf/any.proto=github.com/gogo/protobuf/types,plugins=grpc,import_prefix=:. \
|
||||||
|
$$dir/*.proto; \
|
||||||
|
done
|
||||||
$(SED) $(SED_INPLACE) -E \
|
$(SED) $(SED_INPLACE) -E \
|
||||||
-e '/import _ /d' \
|
-e '/import _ /d' \
|
||||||
-e 's!import (fmt|math) "github.com/(fmt|math)"! !g' \
|
-e 's!import (fmt|math) "github.com/(fmt|math)"! !g' \
|
||||||
|
|||||||
25
vendor/github.com/cockroachdb/errors/README.md
generated
vendored
25
vendor/github.com/cockroachdb/errors/README.md
generated
vendored
@@ -11,7 +11,8 @@ automatically formats error details and strips them of PII.
|
|||||||
|
|
||||||
See also [the design RFC](https://github.com/cockroachdb/cockroach/blob/master/docs/RFCS/20190318_error_handling.md).
|
See also [the design RFC](https://github.com/cockroachdb/cockroach/blob/master/docs/RFCS/20190318_error_handling.md).
|
||||||
|
|
||||||
[](https://travis-ci.org/cockroachdb/errors)
|

|
||||||
|
[](https://pkg.go.dev/github.com/cockroachdb/errors)
|
||||||
|
|
||||||
Table of contents:
|
Table of contents:
|
||||||
|
|
||||||
@@ -86,7 +87,7 @@ older version of the package.
|
|||||||
| detail, eg `WithDetail()` | not visible | visible | no | type only |
|
| detail, eg `WithDetail()` | not visible | visible | no | type only |
|
||||||
| assertion failure annotation, eg `WithAssertionFailure()` | not visible | visible | no | type only |
|
| assertion failure annotation, eg `WithAssertionFailure()` | not visible | visible | no | type only |
|
||||||
| issue links, eg `WithIssueLink()`, `UnimplementedError()` | not visible | visible | yes | full |
|
| issue links, eg `WithIssueLink()`, `UnimplementedError()` | not visible | visible | yes | full |
|
||||||
| safe details, eg `WithSafeDetails()` | not visible | not visible | yes | full |
|
| safe details, eg `WithSafeDetails()` | not visible | visible | yes | full |
|
||||||
| telemetry keys, eg. `WithTelemetryKey()` | not visible | visible | yes | full |
|
| telemetry keys, eg. `WithTelemetryKey()` | not visible | visible | yes | full |
|
||||||
| secondary errors, eg. `WithSecondaryError()`, `CombineErrors()` | not visible | visible | redacted, recursively | redacted, recursively |
|
| secondary errors, eg. `WithSecondaryError()`, `CombineErrors()` | not visible | visible | redacted, recursively | redacted, recursively |
|
||||||
| barrier origins, eg. `Handled()` | not visible | visible | redacted, recursively | redacted, recursively |
|
| barrier origins, eg. `Handled()` | not visible | visible | redacted, recursively | redacted, recursively |
|
||||||
@@ -208,12 +209,12 @@ return errors.Wrap(foo(), "foo")
|
|||||||
- how to access the detail: `Error()`, regular Go formatting, Sentry Report.
|
- how to access the detail: `Error()`, regular Go formatting, Sentry Report.
|
||||||
|
|
||||||
- `WithDetail(error, string) error`, `WithDetailf(error, string, ...interface{}) error`, user-facing detail with contextual information.
|
- `WithDetail(error, string) error`, `WithDetailf(error, string, ...interface{}) error`, user-facing detail with contextual information.
|
||||||
- **when to use: need to embark a message string to output when the error is presented to a human.**
|
- **when to use: need to embark a message string to output when the error is presented to a developer.**
|
||||||
- what it does: captures detail strings.
|
- what it does: captures detail strings.
|
||||||
- how to access the detail: `errors.GetAllDetails()`, `errors.FlattenDetails()` (all details are preserved), format with `%+v`. Not included in Sentry reports.
|
- how to access the detail: `errors.GetAllDetails()`, `errors.FlattenDetails()` (all details are preserved), format with `%+v`. Not included in Sentry reports.
|
||||||
|
|
||||||
- `WithHint(error, string) error`, `WithHintf(error, string, ...interface{}) error`: user-facing detail with suggestion for action to take.
|
- `WithHint(error, string) error`, `WithHintf(error, string, ...interface{}) error`: user-facing detail with suggestion for action to take.
|
||||||
- **when to use: need to embark a message string to output when the error is presented to a human.**
|
- **when to use: need to embark a message string to output when the error is presented to an end user.**
|
||||||
- what it does: captures hint strings.
|
- what it does: captures hint strings.
|
||||||
- how to access the detail: `errors.GetAllHints()`, `errors.FlattenHints()` (hints are de-duplicated), format with `%+v`. Not included in Sentry reports.
|
- how to access the detail: `errors.GetAllHints()`, `errors.FlattenHints()` (hints are de-duplicated), format with `%+v`. Not included in Sentry reports.
|
||||||
|
|
||||||
@@ -422,10 +423,10 @@ func myLeafDecoder(_ string, details []string, _ proto.Message) error {
|
|||||||
|
|
||||||
(For an example, see the `withTelemetry` type in [`telemetry/with_telemetry.go`](telemetry/with_telemetry.go).)
|
(For an example, see the `withTelemetry` type in [`telemetry/with_telemetry.go`](telemetry/with_telemetry.go).)
|
||||||
|
|
||||||
**The only case where you need a custom encoder is when your error
|
__The only case where you need a custom encoder is when your error
|
||||||
type contains some fields that are not reflected in the error message
|
type contains some fields that are not reflected in the error message
|
||||||
(so you can't extract them back from there), and are not PII-free and
|
(so you can't extract them back from there), and are not PII-free and
|
||||||
thus cannot be reported as "safe details".**
|
thus cannot be reported as "safe details".__
|
||||||
|
|
||||||
To take inspiration from examples, see the following types in the
|
To take inspiration from examples, see the following types in the
|
||||||
library that need a custom encoder:
|
library that need a custom encoder:
|
||||||
@@ -535,9 +536,13 @@ Example use:
|
|||||||
| `WrapWithDepthf` | `WithMessagef` + `WithStackDepth` |
|
| `WrapWithDepthf` | `WithMessagef` + `WithStackDepth` |
|
||||||
| `AssertionFailedWithDepthf` | `NewWithDepthf` + `WithAssertionFailure` |
|
| `AssertionFailedWithDepthf` | `NewWithDepthf` + `WithAssertionFailure` |
|
||||||
| `NewAssertionErrorWithWrappedErrf` | `HandledWithMessagef` (barrier) + `WrapWithDepthf` + `WithAssertionFailure` |
|
| `NewAssertionErrorWithWrappedErrf` | `HandledWithMessagef` (barrier) + `WrapWithDepthf` + `WithAssertionFailure` |
|
||||||
|
| `Join` | `JoinWithDepth` (see below) |
|
||||||
|
| `JoinWithDepth` | multi-cause wrapper + `WithStackDepth` |
|
||||||
## API (not constructing error objects)
|
## API (not constructing error objects)
|
||||||
|
|
||||||
|
The following is a summary of the non-constructor API functions, grouped by category.
|
||||||
|
Detailed documentation can be found at: https://pkg.go.dev/github.com/cockroachdb/errors
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Access causes.
|
// Access causes.
|
||||||
func UnwrapAll(err error) error
|
func UnwrapAll(err error) error
|
||||||
@@ -569,10 +574,16 @@ func RegisterLeafDecoder(typeName TypeKey, decoder LeafDecoder)
|
|||||||
func RegisterLeafEncoder(typeName TypeKey, encoder LeafEncoder)
|
func RegisterLeafEncoder(typeName TypeKey, encoder LeafEncoder)
|
||||||
func RegisterWrapperDecoder(typeName TypeKey, decoder WrapperDecoder)
|
func RegisterWrapperDecoder(typeName TypeKey, decoder WrapperDecoder)
|
||||||
func RegisterWrapperEncoder(typeName TypeKey, encoder WrapperEncoder)
|
func RegisterWrapperEncoder(typeName TypeKey, encoder WrapperEncoder)
|
||||||
|
func RegisterWrapperEncoderWithMessageOverride (typeName TypeKey, encoder WrapperEncoderWithMessageOverride)
|
||||||
|
func RegisterMultiCauseEncoder(theType TypeKey, encoder MultiCauseEncoder)
|
||||||
|
func RegisterMultiCauseDecoder(theType TypeKey, decoder MultiCauseDecoder)
|
||||||
type LeafEncoder = func(ctx context.Context, err error) (msg string, safeDetails []string, payload proto.Message)
|
type LeafEncoder = func(ctx context.Context, err error) (msg string, safeDetails []string, payload proto.Message)
|
||||||
type LeafDecoder = func(ctx context.Context, msg string, safeDetails []string, payload proto.Message) error
|
type LeafDecoder = func(ctx context.Context, msg string, safeDetails []string, payload proto.Message) error
|
||||||
type WrapperEncoder = func(ctx context.Context, err error) (msgPrefix string, safeDetails []string, payload proto.Message)
|
type WrapperEncoder = func(ctx context.Context, err error) (msgPrefix string, safeDetails []string, payload proto.Message)
|
||||||
|
type WrapperEncoderWithMessageOverride = func(ctx context.Context, err error) (msgPrefix string, safeDetails []string, payload proto.Message, overrideError bool)
|
||||||
type WrapperDecoder = func(ctx context.Context, cause error, msgPrefix string, safeDetails []string, payload proto.Message) error
|
type WrapperDecoder = func(ctx context.Context, cause error, msgPrefix string, safeDetails []string, payload proto.Message) error
|
||||||
|
type MultiCauseEncoder = func(ctx context.Context, err error) (msg string, safeDetails []string, payload proto.Message)
|
||||||
|
type MultiCauseDecoder = func(ctx context.Context, causes []error, msgPrefix string, safeDetails []string, payload proto.Message) error
|
||||||
|
|
||||||
// Registering package renames for custom error types.
|
// Registering package renames for custom error types.
|
||||||
func RegisterTypeMigration(previousPkgPath, previousTypeName string, newType error)
|
func RegisterTypeMigration(previousPkgPath, previousTypeName string, newType error)
|
||||||
|
|||||||
67
vendor/github.com/cockroachdb/errors/barriers/barriers.go
generated
vendored
67
vendor/github.com/cockroachdb/errors/barriers/barriers.go
generated
vendored
@@ -19,6 +19,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors/errbase"
|
"github.com/cockroachdb/errors/errbase"
|
||||||
|
"github.com/cockroachdb/redact"
|
||||||
"github.com/gogo/protobuf/proto"
|
"github.com/gogo/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ func Handled(err error) error {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return HandledWithMessage(err, err.Error())
|
return HandledWithSafeMessage(err, redact.Sprint(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandledWithMessage is like Handled except the message is overridden.
|
// HandledWithMessage is like Handled except the message is overridden.
|
||||||
@@ -46,7 +47,14 @@ func HandledWithMessage(err error, msg string) error {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &barrierError{maskedErr: err, msg: msg}
|
return HandledWithSafeMessage(err, redact.Sprint(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandledWithSafeMessage is like Handled except the message is overridden.
|
||||||
|
// This can be used e.g. to hide message details or to prevent
|
||||||
|
// downstream code to make assertions on the message's contents.
|
||||||
|
func HandledWithSafeMessage(err error, msg redact.RedactableString) error {
|
||||||
|
return &barrierErr{maskedErr: err, smsg: msg}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandledWithMessagef is like HandledWithMessagef except the message
|
// HandledWithMessagef is like HandledWithMessagef except the message
|
||||||
@@ -55,47 +63,48 @@ func HandledWithMessagef(err error, format string, args ...interface{}) error {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &barrierError{maskedErr: err, msg: fmt.Sprintf(format, args...)}
|
return &barrierErr{maskedErr: err, smsg: redact.Sprintf(format, args...)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// barrierError is a leaf error type. It encapsulates a chain of
|
// barrierErr is a leaf error type. It encapsulates a chain of
|
||||||
// original causes, but these causes are hidden so that they inhibit
|
// original causes, but these causes are hidden so that they inhibit
|
||||||
// matching via Is() and the Cause()/Unwrap() recursions.
|
// matching via Is() and the Cause()/Unwrap() recursions.
|
||||||
type barrierError struct {
|
type barrierErr struct {
|
||||||
// Message for the barrier itself.
|
// Message for the barrier itself.
|
||||||
// In the common case, the message from the masked error
|
// In the common case, the message from the masked error
|
||||||
// is used as-is (see Handled() above) however it is
|
// is used as-is (see Handled() above) however it is
|
||||||
// useful to cache it here since the masked error may
|
// useful to cache it here since the masked error may
|
||||||
// have a long chain of wrappers and its Error() call
|
// have a long chain of wrappers and its Error() call
|
||||||
// may be expensive.
|
// may be expensive.
|
||||||
msg string
|
smsg redact.RedactableString
|
||||||
// Masked error chain.
|
// Masked error chain.
|
||||||
maskedErr error
|
maskedErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ error = (*barrierError)(nil)
|
var _ error = (*barrierErr)(nil)
|
||||||
var _ errbase.SafeDetailer = (*barrierError)(nil)
|
var _ errbase.SafeDetailer = (*barrierErr)(nil)
|
||||||
var _ errbase.SafeFormatter = (*barrierError)(nil)
|
var _ errbase.SafeFormatter = (*barrierErr)(nil)
|
||||||
var _ fmt.Formatter = (*barrierError)(nil)
|
var _ fmt.Formatter = (*barrierErr)(nil)
|
||||||
|
|
||||||
// barrierError is an error.
|
// barrierErr is an error.
|
||||||
func (e *barrierError) Error() string { return e.msg }
|
func (e *barrierErr) Error() string { return e.smsg.StripMarkers() }
|
||||||
|
|
||||||
// SafeDetails reports the PII-free details from the masked error.
|
// SafeDetails reports the PII-free details from the masked error.
|
||||||
func (e *barrierError) SafeDetails() []string {
|
func (e *barrierErr) SafeDetails() []string {
|
||||||
var details []string
|
var details []string
|
||||||
for err := e.maskedErr; err != nil; err = errbase.UnwrapOnce(err) {
|
for err := e.maskedErr; err != nil; err = errbase.UnwrapOnce(err) {
|
||||||
sd := errbase.GetSafeDetails(err)
|
sd := errbase.GetSafeDetails(err)
|
||||||
details = sd.Fill(details)
|
details = sd.Fill(details)
|
||||||
}
|
}
|
||||||
|
details = append(details, redact.Sprintf("masked error: %+v", e.maskedErr).Redact().StripMarkers())
|
||||||
return details
|
return details
|
||||||
}
|
}
|
||||||
|
|
||||||
// Printing a barrier reveals the details.
|
// Printing a barrier reveals the details.
|
||||||
func (e *barrierError) Format(s fmt.State, verb rune) { errbase.FormatError(e, s, verb) }
|
func (e *barrierErr) Format(s fmt.State, verb rune) { errbase.FormatError(e, s, verb) }
|
||||||
|
|
||||||
func (e *barrierError) SafeFormatError(p errbase.Printer) (next error) {
|
func (e *barrierErr) SafeFormatError(p errbase.Printer) (next error) {
|
||||||
p.Print(e.msg)
|
p.Print(e.smsg)
|
||||||
if p.Detail() {
|
if p.Detail() {
|
||||||
p.Printf("-- cause hidden behind barrier\n%+v", e.maskedErr)
|
p.Printf("-- cause hidden behind barrier\n%+v", e.maskedErr)
|
||||||
}
|
}
|
||||||
@@ -106,19 +115,37 @@ func (e *barrierError) SafeFormatError(p errbase.Printer) (next error) {
|
|||||||
func encodeBarrier(
|
func encodeBarrier(
|
||||||
ctx context.Context, err error,
|
ctx context.Context, err error,
|
||||||
) (msg string, details []string, payload proto.Message) {
|
) (msg string, details []string, payload proto.Message) {
|
||||||
e := err.(*barrierError)
|
e := err.(*barrierErr)
|
||||||
enc := errbase.EncodeError(ctx, e.maskedErr)
|
enc := errbase.EncodeError(ctx, e.maskedErr)
|
||||||
return e.msg, e.SafeDetails(), &enc
|
return string(e.smsg), e.SafeDetails(), &enc
|
||||||
}
|
}
|
||||||
|
|
||||||
// A barrier error is decoded exactly.
|
// A barrier error is decoded exactly.
|
||||||
func decodeBarrier(ctx context.Context, msg string, _ []string, payload proto.Message) error {
|
func decodeBarrier(ctx context.Context, msg string, _ []string, payload proto.Message) error {
|
||||||
enc := payload.(*errbase.EncodedError)
|
enc := payload.(*errbase.EncodedError)
|
||||||
return &barrierError{msg: msg, maskedErr: errbase.DecodeError(ctx, *enc)}
|
return &barrierErr{smsg: redact.RedactableString(msg), maskedErr: errbase.DecodeError(ctx, *enc)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Previous versions of barrier errors.
|
||||||
|
func decodeBarrierPrev(ctx context.Context, msg string, _ []string, payload proto.Message) error {
|
||||||
|
enc := payload.(*errbase.EncodedError)
|
||||||
|
return &barrierErr{smsg: redact.Sprint(msg), maskedErr: errbase.DecodeError(ctx, *enc)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// barrierError is the "old" type name of barrierErr. We use a new
|
||||||
|
// name now to ensure a different decode function is used when
|
||||||
|
// importing barriers from the previous structure, where the
|
||||||
|
// message is not redactable.
|
||||||
|
type barrierError struct {
|
||||||
|
msg string
|
||||||
|
maskedErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *barrierError) Error() string { return "" }
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
tn := errbase.GetTypeKey((*barrierError)(nil))
|
errbase.RegisterLeafDecoder(errbase.GetTypeKey((*barrierError)(nil)), decodeBarrierPrev)
|
||||||
|
tn := errbase.GetTypeKey((*barrierErr)(nil))
|
||||||
errbase.RegisterLeafDecoder(tn, decodeBarrier)
|
errbase.RegisterLeafDecoder(tn, decodeBarrier)
|
||||||
errbase.RegisterLeafEncoder(tn, encodeBarrier)
|
errbase.RegisterLeafEncoder(tn, encodeBarrier)
|
||||||
}
|
}
|
||||||
|
|||||||
2
vendor/github.com/cockroachdb/errors/contexttags/contexttags.go
generated
vendored
2
vendor/github.com/cockroachdb/errors/contexttags/contexttags.go
generated
vendored
@@ -25,7 +25,7 @@ import (
|
|||||||
// WithContextTags captures the k/v pairs stored in the context via the
|
// WithContextTags captures the k/v pairs stored in the context via the
|
||||||
// `logtags` package and annotates them on the error.
|
// `logtags` package and annotates them on the error.
|
||||||
//
|
//
|
||||||
// Only the stromg representation of values remains available. This is
|
// Only the strong representation of values remains available. This is
|
||||||
// because the library cannot guarantee that the underlying value is
|
// because the library cannot guarantee that the underlying value is
|
||||||
// preserved across the network. To avoid creating a stateful interface
|
// preserved across the network. To avoid creating a stateful interface
|
||||||
// (where the user code needs to know whether an error has traveled
|
// (where the user code needs to know whether an error has traveled
|
||||||
|
|||||||
2
vendor/github.com/cockroachdb/errors/contexttags_api.go
generated
vendored
2
vendor/github.com/cockroachdb/errors/contexttags_api.go
generated
vendored
@@ -24,7 +24,7 @@ import (
|
|||||||
// WithContextTags captures the k/v pairs stored in the context via the
|
// WithContextTags captures the k/v pairs stored in the context via the
|
||||||
// `logtags` package and annotates them on the error.
|
// `logtags` package and annotates them on the error.
|
||||||
//
|
//
|
||||||
// Only the stromg representation of values remains available. This is
|
// Only the strong representation of values remains available. This is
|
||||||
// because the library cannot guarantee that the underlying value is
|
// because the library cannot guarantee that the underlying value is
|
||||||
// preserved across the network. To avoid creating a stateful interface
|
// preserved across the network. To avoid creating a stateful interface
|
||||||
// (where the user code needs to know whether an error has traveled
|
// (where the user code needs to know whether an error has traveled
|
||||||
|
|||||||
9
vendor/github.com/cockroachdb/errors/errbase/adapters.go
generated
vendored
9
vendor/github.com/cockroachdb/errors/errbase/adapters.go
generated
vendored
@@ -37,6 +37,11 @@ func decodeErrorString(_ context.Context, msg string, _ []string, _ proto.Messag
|
|||||||
return goErr.New(msg)
|
return goErr.New(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// context.DeadlineExceeded uses a custom type.
|
||||||
|
func decodeDeadlineExceeded(_ context.Context, _ string, _ []string, _ proto.Message) error {
|
||||||
|
return context.DeadlineExceeded
|
||||||
|
}
|
||||||
|
|
||||||
// errors.fundamental from github.com/pkg/errors cannot be encoded
|
// errors.fundamental from github.com/pkg/errors cannot be encoded
|
||||||
// exactly because it includes a non-serializable stack trace
|
// exactly because it includes a non-serializable stack trace
|
||||||
// object. In order to work with it, we encode it by dumping
|
// object. In order to work with it, we encode it by dumping
|
||||||
@@ -183,6 +188,8 @@ func init() {
|
|||||||
baseErr := goErr.New("")
|
baseErr := goErr.New("")
|
||||||
RegisterLeafDecoder(GetTypeKey(baseErr), decodeErrorString)
|
RegisterLeafDecoder(GetTypeKey(baseErr), decodeErrorString)
|
||||||
|
|
||||||
|
RegisterLeafDecoder(GetTypeKey(context.DeadlineExceeded), decodeDeadlineExceeded)
|
||||||
|
|
||||||
pkgE := pkgErr.New("")
|
pkgE := pkgErr.New("")
|
||||||
RegisterLeafEncoder(GetTypeKey(pkgE), encodePkgFundamental)
|
RegisterLeafEncoder(GetTypeKey(pkgE), encodePkgFundamental)
|
||||||
|
|
||||||
@@ -191,9 +198,11 @@ func init() {
|
|||||||
ws := pkgErr.WithStack(baseErr)
|
ws := pkgErr.WithStack(baseErr)
|
||||||
RegisterWrapperEncoder(GetTypeKey(ws), encodePkgWithStack)
|
RegisterWrapperEncoder(GetTypeKey(ws), encodePkgWithStack)
|
||||||
|
|
||||||
|
registerOsPathErrorMigration() // Needed for Go 1.16.
|
||||||
pKey := GetTypeKey(&os.PathError{})
|
pKey := GetTypeKey(&os.PathError{})
|
||||||
RegisterWrapperEncoder(pKey, encodePathError)
|
RegisterWrapperEncoder(pKey, encodePathError)
|
||||||
RegisterWrapperDecoder(pKey, decodePathError)
|
RegisterWrapperDecoder(pKey, decodePathError)
|
||||||
|
|
||||||
pKey = GetTypeKey(&os.LinkError{})
|
pKey = GetTypeKey(&os.LinkError{})
|
||||||
RegisterWrapperEncoder(pKey, encodeLinkError)
|
RegisterWrapperEncoder(pKey, encodeLinkError)
|
||||||
RegisterWrapperDecoder(pKey, decodeLinkError)
|
RegisterWrapperDecoder(pKey, decodeLinkError)
|
||||||
|
|||||||
54
vendor/github.com/cockroachdb/errors/errbase/decode.go
generated
vendored
54
vendor/github.com/cockroachdb/errors/errbase/decode.go
generated
vendored
@@ -23,6 +23,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// DecodeError decodes an error.
|
// DecodeError decodes an error.
|
||||||
|
//
|
||||||
|
// Can only be called if the EncodedError is set (see IsSet()).
|
||||||
func DecodeError(ctx context.Context, enc EncodedError) error {
|
func DecodeError(ctx context.Context, enc EncodedError) error {
|
||||||
if w := enc.GetWrapper(); w != nil {
|
if w := enc.GetWrapper(); w != nil {
|
||||||
return decodeWrapper(ctx, w)
|
return decodeWrapper(ctx, w)
|
||||||
@@ -55,6 +57,15 @@ func decodeLeaf(ctx context.Context, enc *errorspb.EncodedErrorLeaf) error {
|
|||||||
return genErr
|
return genErr
|
||||||
}
|
}
|
||||||
// Decoding failed, we'll drop through to opaqueLeaf{} below.
|
// Decoding failed, we'll drop through to opaqueLeaf{} below.
|
||||||
|
} else if decoder, ok := multiCauseDecoders[typeKey]; ok {
|
||||||
|
causes := make([]error, len(enc.MultierrorCauses))
|
||||||
|
for i, e := range enc.MultierrorCauses {
|
||||||
|
causes[i] = DecodeError(ctx, *e)
|
||||||
|
}
|
||||||
|
genErr := decoder(ctx, causes, enc.Message, enc.Details.ReportablePayload, payload)
|
||||||
|
if genErr != nil {
|
||||||
|
return genErr
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Shortcut for non-registered proto-encodable error types:
|
// Shortcut for non-registered proto-encodable error types:
|
||||||
// if it already implements `error`, it's good to go.
|
// if it already implements `error`, it's good to go.
|
||||||
@@ -64,6 +75,19 @@ func decodeLeaf(ctx context.Context, enc *errorspb.EncodedErrorLeaf) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(enc.MultierrorCauses) > 0 {
|
||||||
|
causes := make([]error, len(enc.MultierrorCauses))
|
||||||
|
for i, e := range enc.MultierrorCauses {
|
||||||
|
causes[i] = DecodeError(ctx, *e)
|
||||||
|
}
|
||||||
|
leaf := &opaqueLeafCauses{
|
||||||
|
causes: causes,
|
||||||
|
}
|
||||||
|
leaf.msg = enc.Message
|
||||||
|
leaf.details = enc.Details
|
||||||
|
return leaf
|
||||||
|
}
|
||||||
|
|
||||||
// No decoder and no error type: we'll keep what we received and
|
// No decoder and no error type: we'll keep what we received and
|
||||||
// make it ready to re-encode exactly (if the error leaves over the
|
// make it ready to re-encode exactly (if the error leaves over the
|
||||||
// network again).
|
// network again).
|
||||||
@@ -95,7 +119,7 @@ func decodeWrapper(ctx context.Context, enc *errorspb.EncodedWrapper) error {
|
|||||||
typeKey := TypeKey(enc.Details.ErrorTypeMark.FamilyName)
|
typeKey := TypeKey(enc.Details.ErrorTypeMark.FamilyName)
|
||||||
if decoder, ok := decoders[typeKey]; ok {
|
if decoder, ok := decoders[typeKey]; ok {
|
||||||
// Yes, use it.
|
// Yes, use it.
|
||||||
genErr := decoder(ctx, cause, enc.MessagePrefix, enc.Details.ReportablePayload, payload)
|
genErr := decoder(ctx, cause, enc.Message, enc.Details.ReportablePayload, payload)
|
||||||
if genErr != nil {
|
if genErr != nil {
|
||||||
// Decoding succeeded. Use this.
|
// Decoding succeeded. Use this.
|
||||||
return genErr
|
return genErr
|
||||||
@@ -105,9 +129,10 @@ func decodeWrapper(ctx context.Context, enc *errorspb.EncodedWrapper) error {
|
|||||||
|
|
||||||
// Otherwise, preserve all details about the original object.
|
// Otherwise, preserve all details about the original object.
|
||||||
return &opaqueWrapper{
|
return &opaqueWrapper{
|
||||||
cause: cause,
|
cause: cause,
|
||||||
prefix: enc.MessagePrefix,
|
prefix: enc.Message,
|
||||||
details: enc.Details,
|
details: enc.Details,
|
||||||
|
messageType: MessageType(enc.MessageType),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,3 +183,24 @@ type WrapperDecoder = func(ctx context.Context, cause error, msgPrefix string, s
|
|||||||
|
|
||||||
// registry for RegisterWrapperType.
|
// registry for RegisterWrapperType.
|
||||||
var decoders = map[TypeKey]WrapperDecoder{}
|
var decoders = map[TypeKey]WrapperDecoder{}
|
||||||
|
|
||||||
|
// MultiCauseDecoder is to be provided (via RegisterMultiCauseDecoder
|
||||||
|
// above) by additional multi-cause wrapper types not yet known by the
|
||||||
|
// library. A nil return indicates that decoding was not successful.
|
||||||
|
type MultiCauseDecoder = func(ctx context.Context, causes []error, msgPrefix string, safeDetails []string, payload proto.Message) error
|
||||||
|
|
||||||
|
// registry for RegisterMultiCauseDecoder.
|
||||||
|
var multiCauseDecoders = map[TypeKey]MultiCauseDecoder{}
|
||||||
|
|
||||||
|
// RegisterMultiCauseDecoder can be used to register new multi-cause
|
||||||
|
// wrapper types to the library. Registered wrappers will be decoded
|
||||||
|
// using their own Go type when an error is decoded. Multi-cause
|
||||||
|
// wrappers that have not been registered will be decoded using the
|
||||||
|
// opaqueWrapper type.
|
||||||
|
func RegisterMultiCauseDecoder(theType TypeKey, decoder MultiCauseDecoder) {
|
||||||
|
if decoder == nil {
|
||||||
|
delete(multiCauseDecoders, theType)
|
||||||
|
} else {
|
||||||
|
multiCauseDecoders[theType] = decoder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
166
vendor/github.com/cockroachdb/errors/errbase/encode.go
generated
vendored
166
vendor/github.com/cockroachdb/errors/errbase/encode.go
generated
vendored
@@ -33,18 +33,24 @@ func EncodeError(ctx context.Context, err error) EncodedError {
|
|||||||
if cause := UnwrapOnce(err); cause != nil {
|
if cause := UnwrapOnce(err); cause != nil {
|
||||||
return encodeWrapper(ctx, err, cause)
|
return encodeWrapper(ctx, err, cause)
|
||||||
}
|
}
|
||||||
// Not a causer.
|
return encodeLeaf(ctx, err, UnwrapMulti(err))
|
||||||
return encodeLeaf(ctx, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// encodeLeaf encodes a leaf error.
|
// encodeLeaf encodes a leaf error. This function accepts a `causes`
|
||||||
func encodeLeaf(ctx context.Context, err error) EncodedError {
|
// argument because we encode multi-cause errors using the Leaf
|
||||||
|
// protobuf. This was done to enable backwards compatibility when
|
||||||
|
// introducing this functionality since the Wrapper type already has a
|
||||||
|
// required single `cause` field.
|
||||||
|
func encodeLeaf(ctx context.Context, err error, causes []error) EncodedError {
|
||||||
var msg string
|
var msg string
|
||||||
var details errorspb.EncodedErrorDetails
|
var details errorspb.EncodedErrorDetails
|
||||||
|
|
||||||
if e, ok := err.(*opaqueLeaf); ok {
|
if e, ok := err.(*opaqueLeaf); ok {
|
||||||
msg = e.msg
|
msg = e.msg
|
||||||
details = e.details
|
details = e.details
|
||||||
|
} else if e, ok := err.(*opaqueLeafCauses); ok {
|
||||||
|
msg = e.msg
|
||||||
|
details = e.details
|
||||||
} else {
|
} else {
|
||||||
details.OriginalTypeName, details.ErrorTypeMark.FamilyName, details.ErrorTypeMark.Extension = getTypeDetails(err, false /*onlyFamily*/)
|
details.OriginalTypeName, details.ErrorTypeMark.FamilyName, details.ErrorTypeMark.Extension = getTypeDetails(err, false /*onlyFamily*/)
|
||||||
|
|
||||||
@@ -74,11 +80,21 @@ func encodeLeaf(ctx context.Context, err error) EncodedError {
|
|||||||
details.FullDetails = encodeAsAny(ctx, err, payload)
|
details.FullDetails = encodeAsAny(ctx, err, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cs []*EncodedError
|
||||||
|
if len(causes) > 0 {
|
||||||
|
cs = make([]*EncodedError, len(causes))
|
||||||
|
for i, ee := range causes {
|
||||||
|
ee := EncodeError(ctx, ee)
|
||||||
|
cs[i] = &ee
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return EncodedError{
|
return EncodedError{
|
||||||
Error: &errorspb.EncodedError_Leaf{
|
Error: &errorspb.EncodedError_Leaf{
|
||||||
Leaf: &errorspb.EncodedErrorLeaf{
|
Leaf: &errorspb.EncodedErrorLeaf{
|
||||||
Message: msg,
|
Message: msg,
|
||||||
Details: details,
|
Details: details,
|
||||||
|
MultierrorCauses: cs,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -115,10 +131,18 @@ func encodeAsAny(ctx context.Context, err error, payload proto.Message) *types.A
|
|||||||
func encodeWrapper(ctx context.Context, err, cause error) EncodedError {
|
func encodeWrapper(ctx context.Context, err, cause error) EncodedError {
|
||||||
var msg string
|
var msg string
|
||||||
var details errorspb.EncodedErrorDetails
|
var details errorspb.EncodedErrorDetails
|
||||||
|
messageType := Prefix
|
||||||
|
|
||||||
if e, ok := err.(*opaqueWrapper); ok {
|
if e, ok := err.(*opaqueWrapper); ok {
|
||||||
|
// We delegate all knowledge of the error string
|
||||||
|
// to the original encoder and do not try to re-engineer
|
||||||
|
// the prefix out of the error. This helps maintain
|
||||||
|
// backward compatibility with earlier versions of the
|
||||||
|
// encoder which don't have any understanding of
|
||||||
|
// error string ownership by the wrapper.
|
||||||
msg = e.prefix
|
msg = e.prefix
|
||||||
details = e.details
|
details = e.details
|
||||||
|
messageType = e.messageType
|
||||||
} else {
|
} else {
|
||||||
details.OriginalTypeName, details.ErrorTypeMark.FamilyName, details.ErrorTypeMark.Extension = getTypeDetails(err, false /*onlyFamily*/)
|
details.OriginalTypeName, details.ErrorTypeMark.FamilyName, details.ErrorTypeMark.Extension = getTypeDetails(err, false /*onlyFamily*/)
|
||||||
|
|
||||||
@@ -127,12 +151,12 @@ func encodeWrapper(ctx context.Context, err, cause error) EncodedError {
|
|||||||
// If we have a manually registered encoder, use that.
|
// If we have a manually registered encoder, use that.
|
||||||
typeKey := TypeKey(details.ErrorTypeMark.FamilyName)
|
typeKey := TypeKey(details.ErrorTypeMark.FamilyName)
|
||||||
if enc, ok := encoders[typeKey]; ok {
|
if enc, ok := encoders[typeKey]; ok {
|
||||||
msg, details.ReportablePayload, payload = enc(ctx, err)
|
msg, details.ReportablePayload, payload, messageType = enc(ctx, err)
|
||||||
} else {
|
} else {
|
||||||
// No encoder.
|
// No encoder.
|
||||||
// In that case, we'll try to compute a message prefix
|
// In that case, we'll try to compute a message prefix
|
||||||
// manually.
|
// manually.
|
||||||
msg = extractPrefix(err, cause)
|
msg, messageType = extractPrefix(err, cause)
|
||||||
|
|
||||||
// If there are known safe details, use them.
|
// If there are known safe details, use them.
|
||||||
if s, ok := err.(SafeDetailer); ok {
|
if s, ok := err.(SafeDetailer); ok {
|
||||||
@@ -148,9 +172,10 @@ func encodeWrapper(ctx context.Context, err, cause error) EncodedError {
|
|||||||
return EncodedError{
|
return EncodedError{
|
||||||
Error: &errorspb.EncodedError_Wrapper{
|
Error: &errorspb.EncodedError_Wrapper{
|
||||||
Wrapper: &errorspb.EncodedWrapper{
|
Wrapper: &errorspb.EncodedWrapper{
|
||||||
Cause: EncodeError(ctx, cause),
|
Cause: EncodeError(ctx, cause),
|
||||||
MessagePrefix: msg,
|
Message: msg,
|
||||||
Details: details,
|
Details: details,
|
||||||
|
MessageType: errorspb.MessageType(messageType),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -158,21 +183,36 @@ func encodeWrapper(ctx context.Context, err, cause error) EncodedError {
|
|||||||
|
|
||||||
// extractPrefix extracts the prefix from a wrapper's error message.
|
// extractPrefix extracts the prefix from a wrapper's error message.
|
||||||
// For example,
|
// For example,
|
||||||
// err := errors.New("bar")
|
//
|
||||||
// err = errors.Wrap(err, "foo")
|
// err := errors.New("bar")
|
||||||
// extractPrefix(err)
|
// err = errors.Wrap(err, "foo")
|
||||||
|
// extractPrefix(err)
|
||||||
|
//
|
||||||
// returns "foo".
|
// returns "foo".
|
||||||
func extractPrefix(err, cause error) string {
|
//
|
||||||
|
// If a presumed wrapper does not have a message prefix, it is assumed
|
||||||
|
// to override the entire error message and `extractPrefix` returns
|
||||||
|
// the entire message and the boolean `true` to signify that the causes
|
||||||
|
// should not be appended to it.
|
||||||
|
func extractPrefix(err, cause error) (string, MessageType) {
|
||||||
causeSuffix := cause.Error()
|
causeSuffix := cause.Error()
|
||||||
errMsg := err.Error()
|
errMsg := err.Error()
|
||||||
|
|
||||||
if strings.HasSuffix(errMsg, causeSuffix) {
|
if strings.HasSuffix(errMsg, causeSuffix) {
|
||||||
prefix := errMsg[:len(errMsg)-len(causeSuffix)]
|
prefix := errMsg[:len(errMsg)-len(causeSuffix)]
|
||||||
|
// If error msg matches exactly then this is a wrapper
|
||||||
|
// with no message of its own.
|
||||||
|
if len(prefix) == 0 {
|
||||||
|
return "", Prefix
|
||||||
|
}
|
||||||
if strings.HasSuffix(prefix, ": ") {
|
if strings.HasSuffix(prefix, ": ") {
|
||||||
return prefix[:len(prefix)-2]
|
return prefix[:len(prefix)-2], Prefix
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
// If we don't have the cause as a suffix, then we have
|
||||||
|
// some other string as our error msg, preserve that and
|
||||||
|
// mark as override
|
||||||
|
return errMsg, FullMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTypeDetails(
|
func getTypeDetails(
|
||||||
@@ -183,6 +223,8 @@ func getTypeDetails(
|
|||||||
switch t := err.(type) {
|
switch t := err.(type) {
|
||||||
case *opaqueLeaf:
|
case *opaqueLeaf:
|
||||||
return t.details.OriginalTypeName, t.details.ErrorTypeMark.FamilyName, t.details.ErrorTypeMark.Extension
|
return t.details.OriginalTypeName, t.details.ErrorTypeMark.FamilyName, t.details.ErrorTypeMark.Extension
|
||||||
|
case *opaqueLeafCauses:
|
||||||
|
return t.details.OriginalTypeName, t.details.ErrorTypeMark.FamilyName, t.details.ErrorTypeMark.Extension
|
||||||
case *opaqueWrapper:
|
case *opaqueWrapper:
|
||||||
return t.details.OriginalTypeName, t.details.ErrorTypeMark.FamilyName, t.details.ErrorTypeMark.Extension
|
return t.details.OriginalTypeName, t.details.ErrorTypeMark.FamilyName, t.details.ErrorTypeMark.Extension
|
||||||
}
|
}
|
||||||
@@ -286,6 +328,28 @@ type LeafEncoder = func(ctx context.Context, err error) (msg string, safeDetails
|
|||||||
// registry for RegisterLeafEncoder.
|
// registry for RegisterLeafEncoder.
|
||||||
var leafEncoders = map[TypeKey]LeafEncoder{}
|
var leafEncoders = map[TypeKey]LeafEncoder{}
|
||||||
|
|
||||||
|
// RegisterMultiCauseEncoder can be used to register new multi-cause
|
||||||
|
// error types to the library. Registered types will be encoded using
|
||||||
|
// their own Go type when an error is encoded. Multi-cause wrappers
|
||||||
|
// that have not been registered will be encoded using the
|
||||||
|
// opaqueWrapper type.
|
||||||
|
func RegisterMultiCauseEncoder(theType TypeKey, encoder MultiCauseEncoder) {
|
||||||
|
// This implementation is a simple wrapper around `LeafEncoder`
|
||||||
|
// because we implemented multi-cause error wrapper encoding into a
|
||||||
|
// `Leaf` instead of a `Wrapper` for smoother backwards
|
||||||
|
// compatibility support. Exposing this detail to consumers of the
|
||||||
|
// API is confusing and hence avoided. The causes of the error are
|
||||||
|
// encoded separately regardless of this encoder's implementation.
|
||||||
|
RegisterLeafEncoder(theType, encoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiCauseEncoder is to be provided (via RegisterMultiCauseEncoder
|
||||||
|
// above) by additional multi-cause wrapper types not yet known to this
|
||||||
|
// library. The encoder will automatically extract and encode the
|
||||||
|
// causes of this error by calling `Unwrap()` and expecting a slice of
|
||||||
|
// errors.
|
||||||
|
type MultiCauseEncoder = func(ctx context.Context, err error) (msg string, safeDetails []string, payload proto.Message)
|
||||||
|
|
||||||
// RegisterWrapperEncoder can be used to register new wrapper types to
|
// RegisterWrapperEncoder can be used to register new wrapper types to
|
||||||
// the library. Registered wrappers will be encoded using their own
|
// the library. Registered wrappers will be encoded using their own
|
||||||
// Go type when an error is encoded. Wrappers that have not been
|
// Go type when an error is encoded. Wrappers that have not been
|
||||||
@@ -295,6 +359,35 @@ var leafEncoders = map[TypeKey]LeafEncoder{}
|
|||||||
// or a different type, ensure that RegisterTypeMigration() was called
|
// or a different type, ensure that RegisterTypeMigration() was called
|
||||||
// prior to RegisterWrapperEncoder().
|
// prior to RegisterWrapperEncoder().
|
||||||
func RegisterWrapperEncoder(theType TypeKey, encoder WrapperEncoder) {
|
func RegisterWrapperEncoder(theType TypeKey, encoder WrapperEncoder) {
|
||||||
|
RegisterWrapperEncoderWithMessageType(
|
||||||
|
theType,
|
||||||
|
func(ctx context.Context, err error) (
|
||||||
|
msgPrefix string,
|
||||||
|
safeDetails []string,
|
||||||
|
payload proto.Message,
|
||||||
|
messageType MessageType,
|
||||||
|
) {
|
||||||
|
prefix, details, payload := encoder(ctx, err)
|
||||||
|
return prefix, details, payload, messageType
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterWrapperEncoderWithMessageType can be used to register
|
||||||
|
// new wrapper types to the library. Registered wrappers will be
|
||||||
|
// encoded using their own Go type when an error is encoded. Wrappers
|
||||||
|
// that have not been registered will be encoded using the
|
||||||
|
// opaqueWrapper type.
|
||||||
|
//
|
||||||
|
// This function differs from RegisterWrapperEncoder by allowing the
|
||||||
|
// caller to explicitly decide whether the wrapper owns the entire
|
||||||
|
// error message or not. Otherwise, the relationship is inferred.
|
||||||
|
//
|
||||||
|
// Note: if the error type has been migrated from a previous location
|
||||||
|
// or a different type, ensure that RegisterTypeMigration() was called
|
||||||
|
// prior to RegisterWrapperEncoder().
|
||||||
|
func RegisterWrapperEncoderWithMessageType(
|
||||||
|
theType TypeKey, encoder WrapperEncoderWithMessageType,
|
||||||
|
) {
|
||||||
if encoder == nil {
|
if encoder == nil {
|
||||||
delete(encoders, theType)
|
delete(encoders, theType)
|
||||||
} else {
|
} else {
|
||||||
@@ -304,7 +397,42 @@ func RegisterWrapperEncoder(theType TypeKey, encoder WrapperEncoder) {
|
|||||||
|
|
||||||
// WrapperEncoder is to be provided (via RegisterWrapperEncoder above)
|
// WrapperEncoder is to be provided (via RegisterWrapperEncoder above)
|
||||||
// by additional wrapper types not yet known to this library.
|
// by additional wrapper types not yet known to this library.
|
||||||
type WrapperEncoder = func(ctx context.Context, err error) (msgPrefix string, safeDetails []string, payload proto.Message)
|
type WrapperEncoder func(ctx context.Context, err error) (
|
||||||
|
msgPrefix string,
|
||||||
|
safeDetails []string,
|
||||||
|
payload proto.Message,
|
||||||
|
)
|
||||||
|
|
||||||
|
// MessageType is used to encode information about an error message
|
||||||
|
// within a wrapper error type. This information is used to affect
|
||||||
|
// display logic.
|
||||||
|
type MessageType errorspb.MessageType
|
||||||
|
|
||||||
|
// Values below should match the ones in errorspb.MessageType for
|
||||||
|
// direct conversion.
|
||||||
|
const (
|
||||||
|
// Prefix denotes an error message that should be prepended to the
|
||||||
|
// message of its cause.
|
||||||
|
Prefix MessageType = MessageType(errorspb.MessageType_PREFIX)
|
||||||
|
// FullMessage denotes an error message that contains the text of its
|
||||||
|
// causes and can be displayed standalone.
|
||||||
|
FullMessage = MessageType(errorspb.MessageType_FULL_MESSAGE)
|
||||||
|
)
|
||||||
|
|
||||||
|
// WrapperEncoderWithMessageType is to be provided (via
|
||||||
|
// RegisterWrapperEncoderWithMessageType above) by additional wrapper
|
||||||
|
// types not yet known to this library. This encoder returns an
|
||||||
|
// additional enum which indicates whether the wrapper owns the error
|
||||||
|
// message completely instead of simply being a prefix with the error
|
||||||
|
// message of its causes appended to it. This information is encoded
|
||||||
|
// along with the prefix in order to provide context during error
|
||||||
|
// display.
|
||||||
|
type WrapperEncoderWithMessageType func(ctx context.Context, err error) (
|
||||||
|
msgPrefix string,
|
||||||
|
safeDetails []string,
|
||||||
|
payload proto.Message,
|
||||||
|
messageType MessageType,
|
||||||
|
)
|
||||||
|
|
||||||
// registry for RegisterWrapperType.
|
// registry for RegisterWrapperType.
|
||||||
var encoders = map[TypeKey]WrapperEncoder{}
|
var encoders = map[TypeKey]WrapperEncoderWithMessageType{}
|
||||||
|
|||||||
245
vendor/github.com/cockroachdb/errors/errbase/format_error.go
generated
vendored
245
vendor/github.com/cockroachdb/errors/errbase/format_error.go
generated
vendored
@@ -102,7 +102,13 @@ func formatErrorInternal(err error, s fmt.State, verb rune, redactableOutput boo
|
|||||||
// to enable stack trace de-duplication. This requires a
|
// to enable stack trace de-duplication. This requires a
|
||||||
// post-order traversal. Since we have a linked list, the best we
|
// post-order traversal. Since we have a linked list, the best we
|
||||||
// can do is a recursion.
|
// can do is a recursion.
|
||||||
p.formatRecursive(err, true /* isOutermost */, true /* withDetail */)
|
p.formatRecursive(
|
||||||
|
err,
|
||||||
|
true, /* isOutermost */
|
||||||
|
true, /* withDetail */
|
||||||
|
false, /* withDepth */
|
||||||
|
0, /* depth */
|
||||||
|
)
|
||||||
|
|
||||||
// We now have all the data, we can render the result.
|
// We now have all the data, we can render the result.
|
||||||
p.formatEntries(err)
|
p.formatEntries(err)
|
||||||
@@ -146,7 +152,13 @@ func formatErrorInternal(err error, s fmt.State, verb rune, redactableOutput boo
|
|||||||
// by calling FormatError(), in which case we'd get an infinite
|
// by calling FormatError(), in which case we'd get an infinite
|
||||||
// recursion. So we have no choice but to peel the data
|
// recursion. So we have no choice but to peel the data
|
||||||
// and then assemble the pieces ourselves.
|
// and then assemble the pieces ourselves.
|
||||||
p.formatRecursive(err, true /* isOutermost */, false /* withDetail */)
|
p.formatRecursive(
|
||||||
|
err,
|
||||||
|
true, /* isOutermost */
|
||||||
|
false, /* withDetail */
|
||||||
|
false, /* withDepth */
|
||||||
|
0, /* depth */
|
||||||
|
)
|
||||||
p.formatSingleLineOutput()
|
p.formatSingleLineOutput()
|
||||||
p.finishDisplay(verb)
|
p.finishDisplay(verb)
|
||||||
|
|
||||||
@@ -195,7 +207,19 @@ func (s *state) formatEntries(err error) {
|
|||||||
// Wraps: (N) <details>
|
// Wraps: (N) <details>
|
||||||
//
|
//
|
||||||
for i, j := len(s.entries)-2, 2; i >= 0; i, j = i-1, j+1 {
|
for i, j := len(s.entries)-2, 2; i >= 0; i, j = i-1, j+1 {
|
||||||
fmt.Fprintf(&s.finalBuf, "\nWraps: (%d)", j)
|
s.finalBuf.WriteByte('\n')
|
||||||
|
// Extra indentation starts at depth==2 because the direct
|
||||||
|
// children of the root error area already printed on separate
|
||||||
|
// newlines.
|
||||||
|
for m := 0; m < s.entries[i].depth-1; m += 1 {
|
||||||
|
if m == s.entries[i].depth-2 {
|
||||||
|
s.finalBuf.WriteString("└─ ")
|
||||||
|
} else {
|
||||||
|
s.finalBuf.WriteByte(' ')
|
||||||
|
s.finalBuf.WriteByte(' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&s.finalBuf, "Wraps: (%d)", j)
|
||||||
entry := s.entries[i]
|
entry := s.entries[i]
|
||||||
s.printEntry(entry)
|
s.printEntry(entry)
|
||||||
}
|
}
|
||||||
@@ -278,8 +302,9 @@ func (s *state) printEntry(entry formatEntry) {
|
|||||||
//
|
//
|
||||||
// This function is used both when FormatError() is called indirectly
|
// This function is used both when FormatError() is called indirectly
|
||||||
// from .Error(), e.g. in:
|
// from .Error(), e.g. in:
|
||||||
// (e *myType) Error() { return fmt.Sprintf("%v", e) }
|
//
|
||||||
// (e *myType) Format(s fmt.State, verb rune) { errors.FormatError(s, verb, e) }
|
// (e *myType) Error() { return fmt.Sprintf("%v", e) }
|
||||||
|
// (e *myType) Format(s fmt.State, verb rune) { errors.FormatError(s, verb, e) }
|
||||||
//
|
//
|
||||||
// and also to print the first line in the output of a %+v format.
|
// and also to print the first line in the output of a %+v format.
|
||||||
//
|
//
|
||||||
@@ -329,13 +354,35 @@ func (s *state) formatSingleLineOutput() {
|
|||||||
// s.finalBuf is untouched. The conversion of s.entries
|
// s.finalBuf is untouched. The conversion of s.entries
|
||||||
// to s.finalBuf is done by formatSingleLineOutput() and/or
|
// to s.finalBuf is done by formatSingleLineOutput() and/or
|
||||||
// formatEntries().
|
// formatEntries().
|
||||||
func (s *state) formatRecursive(err error, isOutermost, withDetail bool) {
|
//
|
||||||
|
// `withDepth` and `depth` are used to tag subtrees of multi-cause
|
||||||
|
// errors for added indentation during printing. Once a multi-cause
|
||||||
|
// error is encountered, all subsequent calls with set `withDepth` to
|
||||||
|
// true, and increment `depth` during recursion. This information is
|
||||||
|
// persisted into the generated entries and used later to display the
|
||||||
|
// error with increased indentation based in the depth.
|
||||||
|
func (s *state) formatRecursive(err error, isOutermost, withDetail, withDepth bool, depth int) int {
|
||||||
cause := UnwrapOnce(err)
|
cause := UnwrapOnce(err)
|
||||||
|
numChildren := 0
|
||||||
if cause != nil {
|
if cause != nil {
|
||||||
// Recurse first.
|
// Recurse first, which populates entries list starting from innermost
|
||||||
s.formatRecursive(cause, false /*isOutermost*/, withDetail)
|
// entry. If we've previously seen a multi-cause wrapper, `withDepth`
|
||||||
|
// will be true, and we'll record the depth below ensuring that extra
|
||||||
|
// indentation is applied to this inner cause during printing.
|
||||||
|
// Otherwise, we maintain "straight" vertical formatting by keeping the
|
||||||
|
// parent callers `withDepth` value of `false` by default.
|
||||||
|
numChildren += s.formatRecursive(cause, false, withDetail, withDepth, depth+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
causes := UnwrapMulti(err)
|
||||||
|
for _, c := range causes {
|
||||||
|
// Override `withDepth` to true for all child entries ensuring they have
|
||||||
|
// indentation applied during formatting to distinguish them from
|
||||||
|
// parents.
|
||||||
|
numChildren += s.formatRecursive(c, false, withDetail, true, depth+1)
|
||||||
|
}
|
||||||
|
// inserted := len(s.entries) - 1 - startChildren
|
||||||
|
|
||||||
// Reinitialize the state for this stage of wrapping.
|
// Reinitialize the state for this stage of wrapping.
|
||||||
s.wantDetail = withDetail
|
s.wantDetail = withDetail
|
||||||
s.needSpace = false
|
s.needSpace = false
|
||||||
@@ -349,75 +396,94 @@ func (s *state) formatRecursive(err error, isOutermost, withDetail bool) {
|
|||||||
|
|
||||||
bufIsRedactable := false
|
bufIsRedactable := false
|
||||||
|
|
||||||
printDone := false
|
switch v := err.(type) {
|
||||||
for _, fn := range specialCases {
|
case SafeFormatter:
|
||||||
if handled, desiredShortening := fn(err, (*safePrinter)(s), cause == nil /* leaf */); handled {
|
bufIsRedactable = true
|
||||||
printDone = true
|
desiredShortening := v.SafeFormatError((*safePrinter)(s))
|
||||||
bufIsRedactable = true
|
if desiredShortening == nil {
|
||||||
if desiredShortening == nil {
|
// The error wants to elide the short messages from inner causes.
|
||||||
// The error wants to elide the short messages from inner
|
// Read backwards through list of entries up to the number of new
|
||||||
// causes. Do it.
|
// entries created "under" this one amount and mark `elideShort`
|
||||||
for i := range s.entries {
|
// true.
|
||||||
s.entries[i].elideShort = true
|
s.elideShortChildren(numChildren)
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if !printDone {
|
case Formatter:
|
||||||
switch v := err.(type) {
|
desiredShortening := v.FormatError((*printer)(s))
|
||||||
case SafeFormatter:
|
if desiredShortening == nil {
|
||||||
bufIsRedactable = true
|
// The error wants to elide the short messages from inner
|
||||||
desiredShortening := v.SafeFormatError((*safePrinter)(s))
|
// causes. Do it.
|
||||||
if desiredShortening == nil {
|
s.elideShortChildren(numChildren)
|
||||||
|
}
|
||||||
|
|
||||||
|
case fmt.Formatter:
|
||||||
|
// We can only use a fmt.Formatter when both the following
|
||||||
|
// conditions are true:
|
||||||
|
// - when it is the leaf error, because a fmt.Formatter
|
||||||
|
// on a wrapper also recurses.
|
||||||
|
// - when it is not the outermost wrapper, because
|
||||||
|
// the Format() method is likely to be calling FormatError()
|
||||||
|
// to do its job and we want to avoid an infinite recursion.
|
||||||
|
if !isOutermost && cause == nil {
|
||||||
|
v.Format(s, 'v')
|
||||||
|
if st, ok := err.(StackTraceProvider); ok {
|
||||||
|
// This is likely a leaf error from github/pkg/errors.
|
||||||
|
// The thing probably printed its stack trace on its own.
|
||||||
|
seenTrace = true
|
||||||
|
// We'll subsequently simplify stack traces in wrappers.
|
||||||
|
s.lastStack = st.StackTrace()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if elideCauseMsg := s.formatSimple(err, cause); elideCauseMsg {
|
||||||
// The error wants to elide the short messages from inner
|
// The error wants to elide the short messages from inner
|
||||||
// causes. Do it.
|
// causes. Do it.
|
||||||
for i := range s.entries {
|
s.elideShortChildren(numChildren)
|
||||||
s.entries[i].elideShort = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case Formatter:
|
default:
|
||||||
desiredShortening := v.FormatError((*printer)(s))
|
// Handle the special case overrides for context.Canceled,
|
||||||
if desiredShortening == nil {
|
// os.PathError, etc for which we know how to extract some safe
|
||||||
// The error wants to elide the short messages from inner
|
// strings.
|
||||||
// causes. Do it.
|
//
|
||||||
for i := range s.entries {
|
// We need to do this in the `default` branch, instead of doing
|
||||||
s.entries[i].elideShort = true
|
// this above the switch, because the special handler could call a
|
||||||
|
// .Error() that delegates its implementation to fmt.Formatter,
|
||||||
|
// errors.Safeformatter or errors.Formattable, which brings us
|
||||||
|
// back to this method in a call cycle. So we need to handle the
|
||||||
|
// various interfaces first.
|
||||||
|
printDone := false
|
||||||
|
for _, fn := range specialCases {
|
||||||
|
if handled, desiredShortening := fn(err, (*safePrinter)(s), cause == nil /* leaf */); handled {
|
||||||
|
printDone = true
|
||||||
|
bufIsRedactable = true
|
||||||
|
if desiredShortening == nil {
|
||||||
|
// The error wants to elide the short messages from inner
|
||||||
|
// causes. Do it.
|
||||||
|
s.elideShortChildren(numChildren)
|
||||||
}
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
case fmt.Formatter:
|
if !printDone {
|
||||||
// We can only use a fmt.Formatter when both the following
|
|
||||||
// conditions are true:
|
|
||||||
// - when it is the leaf error, because a fmt.Formatter
|
|
||||||
// on a wrapper also recurses.
|
|
||||||
// - when it is not the outermost wrapper, because
|
|
||||||
// the Format() method is likely to be calling FormatError()
|
|
||||||
// to do its job and we want to avoid an infinite recursion.
|
|
||||||
if !isOutermost && cause == nil {
|
|
||||||
v.Format(s, 'v')
|
|
||||||
if st, ok := err.(StackTraceProvider); ok {
|
|
||||||
// This is likely a leaf error from github/pkg/errors.
|
|
||||||
// The thing probably printed its stack trace on its own.
|
|
||||||
seenTrace = true
|
|
||||||
// We'll subsequently simplify stack traces in wrappers.
|
|
||||||
s.lastStack = st.StackTrace()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s.formatSimple(err, cause)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// If the error did not implement errors.Formatter nor
|
// If the error did not implement errors.Formatter nor
|
||||||
// fmt.Formatter, but it is a wrapper, still attempt best effort:
|
// fmt.Formatter, but it is a wrapper, still attempt best effort:
|
||||||
// print what we can at this level.
|
// print what we can at this level.
|
||||||
s.formatSimple(err, cause)
|
elideChildren := s.formatSimple(err, cause)
|
||||||
|
// always elideChildren when dealing with multi-cause errors.
|
||||||
|
if len(causes) > 0 {
|
||||||
|
elideChildren = true
|
||||||
|
}
|
||||||
|
if elideChildren {
|
||||||
|
// The error wants to elide the short messages from inner
|
||||||
|
// causes. Do it.
|
||||||
|
s.elideShortChildren(numChildren)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect the result.
|
// Collect the result.
|
||||||
entry := s.collectEntry(err, bufIsRedactable)
|
entry := s.collectEntry(err, bufIsRedactable, withDepth, depth)
|
||||||
|
|
||||||
// If there's an embedded stack trace, also collect it.
|
// If there's an embedded stack trace, also collect it.
|
||||||
// This will get either a stack from pkg/errors, or ours.
|
// This will get either a stack from pkg/errors, or ours.
|
||||||
@@ -431,9 +497,22 @@ func (s *state) formatRecursive(err error, isOutermost, withDetail bool) {
|
|||||||
// Remember the entry for later rendering.
|
// Remember the entry for later rendering.
|
||||||
s.entries = append(s.entries, entry)
|
s.entries = append(s.entries, entry)
|
||||||
s.buf = bytes.Buffer{}
|
s.buf = bytes.Buffer{}
|
||||||
|
|
||||||
|
return numChildren + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state) collectEntry(err error, bufIsRedactable bool) formatEntry {
|
// elideShortChildren takes a number of entries to set `elideShort` to
|
||||||
|
// false. The reason a number of entries is needed is that we may be
|
||||||
|
// eliding a subtree of causes in the case of a multi-cause error. In
|
||||||
|
// the multi-cause case, we need to know how many of the prior errors
|
||||||
|
// in the list of entries is a child of this subtree.
|
||||||
|
func (s *state) elideShortChildren(newEntries int) {
|
||||||
|
for i := 0; i < newEntries; i++ {
|
||||||
|
s.entries[len(s.entries)-1-i].elideShort = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state) collectEntry(err error, bufIsRedactable bool, withDepth bool, depth int) formatEntry {
|
||||||
entry := formatEntry{err: err}
|
entry := formatEntry{err: err}
|
||||||
if s.wantDetail {
|
if s.wantDetail {
|
||||||
// The buffer has been populated as a result of formatting with
|
// The buffer has been populated as a result of formatting with
|
||||||
@@ -470,6 +549,10 @@ func (s *state) collectEntry(err error, bufIsRedactable bool) formatEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if withDepth {
|
||||||
|
entry.depth = depth
|
||||||
|
}
|
||||||
|
|
||||||
return entry
|
return entry
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,16 +573,23 @@ func RegisterSpecialCasePrinter(fn safeErrorPrinterFn) {
|
|||||||
// formatSimple performs a best effort at extracting the details at a
|
// formatSimple performs a best effort at extracting the details at a
|
||||||
// given level of wrapping when the error object does not implement
|
// given level of wrapping when the error object does not implement
|
||||||
// the Formatter interface.
|
// the Formatter interface.
|
||||||
func (s *state) formatSimple(err, cause error) {
|
// Returns true if we want to elide errors from causal chain.
|
||||||
|
func (s *state) formatSimple(err, cause error) bool {
|
||||||
var pref string
|
var pref string
|
||||||
|
elideCauses := false
|
||||||
if cause != nil {
|
if cause != nil {
|
||||||
pref = extractPrefix(err, cause)
|
var messageType MessageType
|
||||||
|
pref, messageType = extractPrefix(err, cause)
|
||||||
|
if messageType == FullMessage {
|
||||||
|
elideCauses = true
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
pref = err.Error()
|
pref = err.Error()
|
||||||
}
|
}
|
||||||
if len(pref) > 0 {
|
if len(pref) > 0 {
|
||||||
s.Write([]byte(pref))
|
s.Write([]byte(pref))
|
||||||
}
|
}
|
||||||
|
return elideCauses
|
||||||
}
|
}
|
||||||
|
|
||||||
// finishDisplay renders s.finalBuf into s.State.
|
// finishDisplay renders s.finalBuf into s.State.
|
||||||
@@ -558,7 +648,7 @@ type state struct {
|
|||||||
|
|
||||||
// entries collect the result of formatRecursive(). They are
|
// entries collect the result of formatRecursive(). They are
|
||||||
// consumed by formatSingleLineOutput() and formatEntries() to
|
// consumed by formatSingleLineOutput() and formatEntries() to
|
||||||
// procude the contents of finalBuf.
|
// produce the contents of finalBuf.
|
||||||
entries []formatEntry
|
entries []formatEntry
|
||||||
|
|
||||||
// buf collects the details of the current error object at a given
|
// buf collects the details of the current error object at a given
|
||||||
@@ -680,6 +770,11 @@ type formatEntry struct {
|
|||||||
// truncated to avoid duplication of entries. This is used to
|
// truncated to avoid duplication of entries. This is used to
|
||||||
// display a truncation indicator during verbose rendering.
|
// display a truncation indicator during verbose rendering.
|
||||||
elidedStackTrace bool
|
elidedStackTrace bool
|
||||||
|
|
||||||
|
// depth, if positive, represents a nesting depth of this error as
|
||||||
|
// a causer of others. This is used with verbose printing to
|
||||||
|
// illustrate the nesting depth for multi-cause error wrappers.
|
||||||
|
depth int
|
||||||
}
|
}
|
||||||
|
|
||||||
// String is used for debugging only.
|
// String is used for debugging only.
|
||||||
@@ -701,6 +796,12 @@ func (s *state) Write(b []byte) (n int, err error) {
|
|||||||
|
|
||||||
for i, c := range b {
|
for i, c := range b {
|
||||||
if c == '\n' {
|
if c == '\n' {
|
||||||
|
//if s.needNewline > 0 {
|
||||||
|
// for i := 0; i < s.needNewline-1; i++ {
|
||||||
|
// s.buf.Write(detailSep[:len(sep)-1])
|
||||||
|
// }
|
||||||
|
// s.needNewline = 0
|
||||||
|
//}
|
||||||
// Flush all the bytes seen so far.
|
// Flush all the bytes seen so far.
|
||||||
s.buf.Write(b[k:i])
|
s.buf.Write(b[k:i])
|
||||||
// Don't print the newline itself; instead, prepare the state so
|
// Don't print the newline itself; instead, prepare the state so
|
||||||
@@ -730,6 +831,11 @@ func (s *state) Write(b []byte) (n int, err error) {
|
|||||||
s.notEmpty = true
|
s.notEmpty = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//if s.needNewline > 0 {
|
||||||
|
// for i := 0; i < s.needNewline-1; i++ {
|
||||||
|
// s.buf.Write(detailSep[:len(sep)-1])
|
||||||
|
// }
|
||||||
|
//}
|
||||||
s.buf.Write(b[k:])
|
s.buf.Write(b[k:])
|
||||||
return len(b), nil
|
return len(b), nil
|
||||||
}
|
}
|
||||||
@@ -756,6 +862,9 @@ func (p *state) switchOver() {
|
|||||||
p.buf = bytes.Buffer{}
|
p.buf = bytes.Buffer{}
|
||||||
p.notEmpty = false
|
p.notEmpty = false
|
||||||
p.hasDetail = true
|
p.hasDetail = true
|
||||||
|
|
||||||
|
// One of the newlines is accounted for in the switch over.
|
||||||
|
// p.needNewline -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *printer) Detail() bool {
|
func (s *printer) Detail() bool {
|
||||||
@@ -819,7 +928,7 @@ func (s *safePrinter) enhanceArgs(args []interface{}) {
|
|||||||
args[i] = redact.Safe(thisStack)
|
args[i] = redact.Safe(thisStack)
|
||||||
lastSeen = st
|
lastSeen = st
|
||||||
}
|
}
|
||||||
// In contrast with (*printer).enhanceArgs(), we dont use a
|
// In contrast with (*printer).enhanceArgs(), we don't use a
|
||||||
// special case for `error` here, because the redact package
|
// special case for `error` here, because the redact package
|
||||||
// already helps us recursing into a safe print for
|
// already helps us recursing into a safe print for
|
||||||
// error objects.
|
// error objects.
|
||||||
|
|||||||
40
vendor/github.com/cockroachdb/errors/errbase/opaque.go
generated
vendored
40
vendor/github.com/cockroachdb/errors/errbase/opaque.go
generated
vendored
@@ -30,19 +30,39 @@ type opaqueLeaf struct {
|
|||||||
details errorspb.EncodedErrorDetails
|
details errorspb.EncodedErrorDetails
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// opaqueLeafCauses is used when receiving an unknown multi-cause
|
||||||
|
// wrapper type. Its important property is that if it is communicated
|
||||||
|
// back to some network system that _does_ know about the type, the
|
||||||
|
// original object can be restored. We encode multi-cause errors as
|
||||||
|
// leaf nodes over the network, in order to support backwards
|
||||||
|
// compatibility with existing single-cause wrapper messages.
|
||||||
|
//
|
||||||
|
// This struct *must* be initialized with a non-nil causes value in
|
||||||
|
// order to comply with go stdlib expectations for `Unwrap()`.
|
||||||
|
type opaqueLeafCauses struct {
|
||||||
|
opaqueLeaf
|
||||||
|
causes []error
|
||||||
|
}
|
||||||
|
|
||||||
var _ error = (*opaqueLeaf)(nil)
|
var _ error = (*opaqueLeaf)(nil)
|
||||||
var _ SafeDetailer = (*opaqueLeaf)(nil)
|
var _ SafeDetailer = (*opaqueLeaf)(nil)
|
||||||
var _ fmt.Formatter = (*opaqueLeaf)(nil)
|
var _ fmt.Formatter = (*opaqueLeaf)(nil)
|
||||||
var _ SafeFormatter = (*opaqueLeaf)(nil)
|
var _ SafeFormatter = (*opaqueLeaf)(nil)
|
||||||
|
|
||||||
|
var _ error = (*opaqueLeafCauses)(nil)
|
||||||
|
var _ SafeDetailer = (*opaqueLeafCauses)(nil)
|
||||||
|
var _ fmt.Formatter = (*opaqueLeafCauses)(nil)
|
||||||
|
var _ SafeFormatter = (*opaqueLeafCauses)(nil)
|
||||||
|
|
||||||
// opaqueWrapper is used when receiving an unknown wrapper type.
|
// opaqueWrapper is used when receiving an unknown wrapper type.
|
||||||
// Its important property is that if it is communicated
|
// Its important property is that if it is communicated
|
||||||
// back to some network system that _does_ know about
|
// back to some network system that _does_ know about
|
||||||
// the type, the original object can be restored.
|
// the type, the original object can be restored.
|
||||||
type opaqueWrapper struct {
|
type opaqueWrapper struct {
|
||||||
cause error
|
cause error
|
||||||
prefix string
|
prefix string
|
||||||
details errorspb.EncodedErrorDetails
|
details errorspb.EncodedErrorDetails
|
||||||
|
messageType MessageType
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ error = (*opaqueWrapper)(nil)
|
var _ error = (*opaqueWrapper)(nil)
|
||||||
@@ -53,6 +73,9 @@ var _ SafeFormatter = (*opaqueWrapper)(nil)
|
|||||||
func (e *opaqueLeaf) Error() string { return e.msg }
|
func (e *opaqueLeaf) Error() string { return e.msg }
|
||||||
|
|
||||||
func (e *opaqueWrapper) Error() string {
|
func (e *opaqueWrapper) Error() string {
|
||||||
|
if e.messageType == FullMessage {
|
||||||
|
return e.prefix
|
||||||
|
}
|
||||||
if e.prefix == "" {
|
if e.prefix == "" {
|
||||||
return e.cause.Error()
|
return e.cause.Error()
|
||||||
}
|
}
|
||||||
@@ -66,8 +89,12 @@ func (e *opaqueWrapper) Unwrap() error { return e.cause }
|
|||||||
func (e *opaqueLeaf) SafeDetails() []string { return e.details.ReportablePayload }
|
func (e *opaqueLeaf) SafeDetails() []string { return e.details.ReportablePayload }
|
||||||
func (e *opaqueWrapper) SafeDetails() []string { return e.details.ReportablePayload }
|
func (e *opaqueWrapper) SafeDetails() []string { return e.details.ReportablePayload }
|
||||||
|
|
||||||
func (e *opaqueLeaf) Format(s fmt.State, verb rune) { FormatError(e, s, verb) }
|
func (e *opaqueLeaf) Format(s fmt.State, verb rune) { FormatError(e, s, verb) }
|
||||||
func (e *opaqueWrapper) Format(s fmt.State, verb rune) { FormatError(e, s, verb) }
|
func (e *opaqueLeafCauses) Format(s fmt.State, verb rune) { FormatError(e, s, verb) }
|
||||||
|
func (e *opaqueWrapper) Format(s fmt.State, verb rune) { FormatError(e, s, verb) }
|
||||||
|
|
||||||
|
// opaqueLeafCauses is a multi-cause wrapper
|
||||||
|
func (e *opaqueLeafCauses) Unwrap() []error { return e.causes }
|
||||||
|
|
||||||
func (e *opaqueLeaf) SafeFormatError(p Printer) (next error) {
|
func (e *opaqueLeaf) SafeFormatError(p Printer) (next error) {
|
||||||
p.Print(e.msg)
|
p.Print(e.msg)
|
||||||
@@ -103,5 +130,8 @@ func (e *opaqueWrapper) SafeFormatError(p Printer) (next error) {
|
|||||||
p.Printf("\npayload type: %s", redact.Safe(e.details.FullDetails.TypeUrl))
|
p.Printf("\npayload type: %s", redact.Safe(e.details.FullDetails.TypeUrl))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if e.messageType == FullMessage {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return e.cause
|
return e.cause
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2019 The Cockroach Authors.
|
// Copyright 2021 The Cockroach Authors.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -12,25 +12,13 @@
|
|||||||
// implied. See the License for the specific language governing
|
// implied. See the License for the specific language governing
|
||||||
// permissions and limitations under the License.
|
// permissions and limitations under the License.
|
||||||
|
|
||||||
|
// +build go1.16
|
||||||
|
|
||||||
package errbase
|
package errbase
|
||||||
|
|
||||||
import "io"
|
import "io/fs"
|
||||||
|
|
||||||
// formatSimpleError is a helper used by FormatError() for the top
|
func registerOsPathErrorMigration() {
|
||||||
// level error/wrapper argument, if it does not implement the
|
// The os.PathError type was migrated to io.fs.PathError in Go 1.16.
|
||||||
// errors.Formatter interface.
|
RegisterTypeMigration("os", "*os.PathError", &fs.PathError{})
|
||||||
func formatSimpleError(err error, p *state, sep string) error {
|
|
||||||
if cause := UnwrapOnce(err); cause != nil {
|
|
||||||
pref := extractPrefix(err, cause)
|
|
||||||
p.buf.WriteString(pref)
|
|
||||||
if pref != "" {
|
|
||||||
p.buf.WriteByte(':')
|
|
||||||
p.buf.WriteString(sep)
|
|
||||||
}
|
|
||||||
err = cause
|
|
||||||
} else {
|
|
||||||
io.WriteString(&p.buf, err.Error())
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
19
vendor/github.com/cockroachdb/errors/errbase/oserror_pre116.go
generated
vendored
Normal file
19
vendor/github.com/cockroachdb/errors/errbase/oserror_pre116.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright 2021 The Cockroach Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
// implied. See the License for the specific language governing
|
||||||
|
// permissions and limitations under the License.
|
||||||
|
|
||||||
|
// +build !go1.16
|
||||||
|
|
||||||
|
package errbase
|
||||||
|
|
||||||
|
func registerOsPathErrorMigration() {}
|
||||||
20
vendor/github.com/cockroachdb/errors/errbase/unwrap.go
generated
vendored
20
vendor/github.com/cockroachdb/errors/errbase/unwrap.go
generated
vendored
@@ -27,6 +27,16 @@ package errbase
|
|||||||
// It supports both errors implementing causer (`Cause()` method, from
|
// It supports both errors implementing causer (`Cause()` method, from
|
||||||
// github.com/pkg/errors) and `Wrapper` (`Unwrap()` method, from the
|
// github.com/pkg/errors) and `Wrapper` (`Unwrap()` method, from the
|
||||||
// Go 2 error proposal).
|
// Go 2 error proposal).
|
||||||
|
//
|
||||||
|
// UnwrapOnce treats multi-errors (those implementing the
|
||||||
|
// `Unwrap() []error` interface as leaf-nodes since they cannot
|
||||||
|
// reasonably be iterated through to a single cause. These errors
|
||||||
|
// are typically constructed as a result of `fmt.Errorf` which results
|
||||||
|
// in a `wrapErrors` instance that contains an interpolated error
|
||||||
|
// string along with a list of causes.
|
||||||
|
//
|
||||||
|
// The go stdlib does not define output on `Unwrap()` for a multi-cause
|
||||||
|
// error, so we default to nil here.
|
||||||
func UnwrapOnce(err error) (cause error) {
|
func UnwrapOnce(err error) (cause error) {
|
||||||
switch e := err.(type) {
|
switch e := err.(type) {
|
||||||
case interface{ Cause() error }:
|
case interface{ Cause() error }:
|
||||||
@@ -39,6 +49,7 @@ func UnwrapOnce(err error) (cause error) {
|
|||||||
|
|
||||||
// UnwrapAll accesses the root cause object of the error.
|
// UnwrapAll accesses the root cause object of the error.
|
||||||
// If the error has no cause (leaf error), it is returned directly.
|
// If the error has no cause (leaf error), it is returned directly.
|
||||||
|
// UnwrapAll treats multi-errors as leaf nodes.
|
||||||
func UnwrapAll(err error) error {
|
func UnwrapAll(err error) error {
|
||||||
for {
|
for {
|
||||||
if cause := UnwrapOnce(err); cause != nil {
|
if cause := UnwrapOnce(err); cause != nil {
|
||||||
@@ -49,3 +60,12 @@ func UnwrapAll(err error) error {
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnwrapMulti access the slice of causes that an error contains, if it is a
|
||||||
|
// multi-error.
|
||||||
|
func UnwrapMulti(err error) []error {
|
||||||
|
if me, ok := err.(interface{ Unwrap() []error }); ok {
|
||||||
|
return me.Unwrap()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
52
vendor/github.com/cockroachdb/errors/errbase_api.go
generated
vendored
52
vendor/github.com/cockroachdb/errors/errbase_api.go
generated
vendored
@@ -86,6 +86,20 @@ func GetTypeKey(err error) TypeKey { return errbase.GetTypeKey(err) }
|
|||||||
// A nil return indicates that decoding was not successful.
|
// A nil return indicates that decoding was not successful.
|
||||||
type LeafDecoder = errbase.LeafDecoder
|
type LeafDecoder = errbase.LeafDecoder
|
||||||
|
|
||||||
|
// MultiCauseDecoder is to be provided (via RegisterMultiCauseDecoder
|
||||||
|
// above) by additional multi-cause wrapper types not yet known by the
|
||||||
|
// library. A nil return indicates that decoding was not successful.
|
||||||
|
type MultiCauseDecoder = errbase.MultiCauseDecoder
|
||||||
|
|
||||||
|
// RegisterMultiCauseDecoder can be used to register new multi-cause
|
||||||
|
// wrapper types to the library. Registered wrappers will be decoded
|
||||||
|
// using their own Go type when an error is decoded. Multi-cause
|
||||||
|
// wrappers that have not been registered will be decoded using the
|
||||||
|
// opaqueWrapper type.
|
||||||
|
func RegisterMultiCauseDecoder(theType TypeKey, decoder MultiCauseDecoder) {
|
||||||
|
errbase.RegisterMultiCauseDecoder(theType, decoder)
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterWrapperDecoder can be used to register new wrapper types to
|
// RegisterWrapperDecoder can be used to register new wrapper types to
|
||||||
// the library. Registered wrappers will be decoded using their own
|
// the library. Registered wrappers will be decoded using their own
|
||||||
// Go type when an error is decoded. Wrappers that have not been
|
// Go type when an error is decoded. Wrappers that have not been
|
||||||
@@ -135,6 +149,41 @@ func RegisterWrapperEncoder(typeName TypeKey, encoder WrapperEncoder) {
|
|||||||
// by additional wrapper types not yet known to this library.
|
// by additional wrapper types not yet known to this library.
|
||||||
type WrapperEncoder = errbase.WrapperEncoder
|
type WrapperEncoder = errbase.WrapperEncoder
|
||||||
|
|
||||||
|
// RegisterWrapperEncoderWithMessageType can be used to register new wrapper
|
||||||
|
// types to the library. These wrappers can optionally override the child error
|
||||||
|
// messages with their own error string instead of relying on iterative
|
||||||
|
// concatenation. Registered wrappers will be encoded using their own Go type
|
||||||
|
// when an error is encoded. Wrappers that have not been registered will be
|
||||||
|
// encoded using the opaqueWrapper type.
|
||||||
|
//
|
||||||
|
// Note: if the error type has been migrated from a previous location
|
||||||
|
// or a different type, ensure that RegisterTypeMigration() was called
|
||||||
|
// prior to RegisterWrapperEncoder().
|
||||||
|
func RegisterWrapperEncoderWithMessageType(typeName TypeKey, encoder WrapperEncoderWithMessageType) {
|
||||||
|
errbase.RegisterWrapperEncoderWithMessageType(typeName, encoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapperEncoderWithMessageType is to be provided (via
|
||||||
|
// RegisterWrapperEncoderWithMessageType) by additional wrapper
|
||||||
|
// types not yet known to this library.
|
||||||
|
type WrapperEncoderWithMessageType = errbase.WrapperEncoderWithMessageType
|
||||||
|
|
||||||
|
// RegisterMultiCauseEncoder can be used to register new multi-cause
|
||||||
|
// error types to the library. Registered types will be encoded using
|
||||||
|
// their own Go type when an error is encoded. Multi-cause wrappers
|
||||||
|
// that have not been registered will be encoded using the
|
||||||
|
// opaqueWrapper type.
|
||||||
|
func RegisterMultiCauseEncoder(typeName TypeKey, encoder MultiCauseEncoder) {
|
||||||
|
errbase.RegisterMultiCauseEncoder(typeName, encoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiCauseEncoder is to be provided (via RegisterMultiCauseEncoder
|
||||||
|
// above) by additional multi-cause wrapper types not yet known to this
|
||||||
|
// library. The encoder will automatically extract and encode the
|
||||||
|
// causes of this error by calling `Unwrap()` and expecting a slice of
|
||||||
|
// errors.
|
||||||
|
type MultiCauseEncoder = errbase.MultiCauseEncoder
|
||||||
|
|
||||||
// SetWarningFn enables configuration of the warning function.
|
// SetWarningFn enables configuration of the warning function.
|
||||||
func SetWarningFn(fn func(context.Context, string, ...interface{})) { errbase.SetWarningFn(fn) }
|
func SetWarningFn(fn func(context.Context, string, ...interface{})) { errbase.SetWarningFn(fn) }
|
||||||
|
|
||||||
@@ -185,7 +234,8 @@ func Formattable(err error) fmt.Formatter { return errbase.Formattable(err) }
|
|||||||
// The value of previousTypeName must be the result of calling
|
// The value of previousTypeName must be the result of calling
|
||||||
// reflect.TypeOf(err).String() on the original error object.
|
// reflect.TypeOf(err).String() on the original error object.
|
||||||
// This is usually composed as follows:
|
// This is usually composed as follows:
|
||||||
// [*]<shortpackage>.<errortype>
|
//
|
||||||
|
// [*]<shortpackage>.<errortype>
|
||||||
//
|
//
|
||||||
// For example, Go's standard error type has name "*errors.errorString".
|
// For example, Go's standard error type has name "*errors.errorString".
|
||||||
// The asterisk indicates that `errorString` implements the `error`
|
// The asterisk indicates that `errorString` implements the `error`
|
||||||
|
|||||||
21
vendor/github.com/cockroachdb/errors/errorspb/errors.go
generated
vendored
Normal file
21
vendor/github.com/cockroachdb/errors/errorspb/errors.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2021 The Cockroach Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
// implied. See the License for the specific language governing
|
||||||
|
// permissions and limitations under the License.
|
||||||
|
|
||||||
|
package errorspb
|
||||||
|
|
||||||
|
// IsSet returns true if the EncodedError contains an error, or false if it is
|
||||||
|
// empty.
|
||||||
|
func (m *EncodedError) IsSet() bool {
|
||||||
|
return m.Error != nil
|
||||||
|
}
|
||||||
984
vendor/github.com/cockroachdb/errors/errorspb/errors.pb.go
generated
vendored
984
vendor/github.com/cockroachdb/errors/errorspb/errors.pb.go
generated
vendored
File diff suppressed because it is too large
Load Diff
34
vendor/github.com/cockroachdb/errors/errorspb/errors.proto
generated
vendored
34
vendor/github.com/cockroachdb/errors/errorspb/errors.proto
generated
vendored
@@ -17,7 +17,7 @@ message EncodedError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EncodedErrorLeaf is the wire-encodable representation
|
// EncodedErrorLeaf is the wire-encodable representation
|
||||||
// of an error leaf.
|
// of an error leaf or a multi-cause wrapper from go 1.20+
|
||||||
message EncodedErrorLeaf {
|
message EncodedErrorLeaf {
|
||||||
// The main error message (mandatory), that can be printed to human
|
// The main error message (mandatory), that can be printed to human
|
||||||
// users and may contain PII. This contains the value of the leaf
|
// users and may contain PII. This contains the value of the leaf
|
||||||
@@ -26,6 +26,12 @@ message EncodedErrorLeaf {
|
|||||||
|
|
||||||
// The error details.
|
// The error details.
|
||||||
EncodedErrorDetails details = 2 [(gogoproto.nullable) = false];
|
EncodedErrorDetails details = 2 [(gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// multierror_causes is a list of errors that contain the causal tree
|
||||||
|
// of this leaf. If this field is not empty, then this leaf encodes
|
||||||
|
// an error from go 1.20 or later that encodes multiple causes in its
|
||||||
|
// chain.
|
||||||
|
repeated EncodedError multierror_causes = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message EncodedErrorDetails {
|
message EncodedErrorDetails {
|
||||||
@@ -60,24 +66,42 @@ message EncodedErrorDetails {
|
|||||||
google.protobuf.Any full_details = 4;
|
google.protobuf.Any full_details = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MessageType encodes information regarding the interpretation of an
|
||||||
|
// error's message string.
|
||||||
|
enum MessageType {
|
||||||
|
// Keeping zero value as default for backwards compatibility.
|
||||||
|
// PREFIX is a message that should be prepended to its cause.
|
||||||
|
PREFIX = 0;
|
||||||
|
// FULL_MESSAGE is a complete error message that can be displayed
|
||||||
|
// without its cause.
|
||||||
|
FULL_MESSAGE = 1;
|
||||||
|
}
|
||||||
|
|
||||||
// EncodedWrapper is the wire-encodable representation
|
// EncodedWrapper is the wire-encodable representation
|
||||||
// of an error wrapper.
|
// of an error wrapper.
|
||||||
message EncodedWrapper {
|
message EncodedWrapper {
|
||||||
// The cause error. Mandatory.
|
// The cause error. Mandatory.
|
||||||
EncodedError cause = 1 [(gogoproto.nullable) = false];
|
EncodedError cause = 1 [(gogoproto.nullable) = false];
|
||||||
|
|
||||||
// The wrapper message prefix (which may be empty). This
|
// The wrapper message. This could either be a full error message
|
||||||
// isbprinted before the cause's own message when
|
// that can be printed independently, or a (potentially empty) prefix
|
||||||
// constructing a full message. This may contain PII.
|
// which is printed before the cause's own message to construct the
|
||||||
|
// full message. This may contain PII.
|
||||||
//
|
//
|
||||||
// This is extracted automatically:
|
// This is extracted automatically:
|
||||||
//
|
//
|
||||||
// - for wrappers that have a registered encoder,
|
// - for wrappers that have a registered encoder,
|
||||||
// - otherwise, when the wrapper's Error() has its cause's Error() as suffix.
|
// - otherwise, when the wrapper's Error() has its cause's Error() as suffix.
|
||||||
string message_prefix = 2;
|
string message = 2;
|
||||||
|
|
||||||
// The error details.
|
// The error details.
|
||||||
EncodedErrorDetails details = 3 [(gogoproto.nullable) = false];
|
EncodedErrorDetails details = 3 [(gogoproto.nullable) = false];
|
||||||
|
|
||||||
|
// message_type encodes the interpretation of `message`. Prior
|
||||||
|
// versions will not set this field and it will be left as `PREFIX`.
|
||||||
|
// This retains backwards compatibility since the new behavior is
|
||||||
|
// only enabled when this enum is set to `FULL_MESSAGE`.
|
||||||
|
MessageType message_type = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorTypeMark identifies an error type for the purpose of determining
|
// ErrorTypeMark identifies an error type for the purpose of determining
|
||||||
|
|||||||
153
vendor/github.com/cockroachdb/errors/errorspb/hintdetail.pb.go
generated
vendored
153
vendor/github.com/cockroachdb/errors/errorspb/hintdetail.pb.go
generated
vendored
@@ -1,13 +1,15 @@
|
|||||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||||
// source: github.com/cockroachdb/errors/errorspb/hintdetail.proto
|
// source: errorspb/hintdetail.proto
|
||||||
|
|
||||||
package errorspb
|
package errorspb
|
||||||
|
|
||||||
import proto "github.com/gogo/protobuf/proto"
|
import (
|
||||||
import fmt "fmt"
|
fmt "fmt"
|
||||||
import math "math"
|
proto "github.com/gogo/protobuf/proto"
|
||||||
|
io "io"
|
||||||
import io "io"
|
math "math"
|
||||||
|
math_bits "math/bits"
|
||||||
|
)
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ = proto.Marshal
|
var _ = proto.Marshal
|
||||||
@@ -18,7 +20,7 @@ var _ = math.Inf
|
|||||||
// is compatible with the proto package it is being compiled against.
|
// is compatible with the proto package it is being compiled against.
|
||||||
// A compilation error at this line likely means your copy of the
|
// A compilation error at this line likely means your copy of the
|
||||||
// proto package needs to be updated.
|
// proto package needs to be updated.
|
||||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
type StringPayload struct {
|
type StringPayload struct {
|
||||||
Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"`
|
Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"`
|
||||||
@@ -28,21 +30,21 @@ func (m *StringPayload) Reset() { *m = StringPayload{} }
|
|||||||
func (m *StringPayload) String() string { return proto.CompactTextString(m) }
|
func (m *StringPayload) String() string { return proto.CompactTextString(m) }
|
||||||
func (*StringPayload) ProtoMessage() {}
|
func (*StringPayload) ProtoMessage() {}
|
||||||
func (*StringPayload) Descriptor() ([]byte, []int) {
|
func (*StringPayload) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_hintdetail_429641491537a364, []int{0}
|
return fileDescriptor_d99b88ec9cc4bc22, []int{0}
|
||||||
}
|
}
|
||||||
func (m *StringPayload) XXX_Unmarshal(b []byte) error {
|
func (m *StringPayload) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
}
|
}
|
||||||
func (m *StringPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
func (m *StringPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
b = b[:cap(b)]
|
b = b[:cap(b)]
|
||||||
n, err := m.MarshalTo(b)
|
n, err := m.MarshalToSizedBuffer(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return b[:n], nil
|
return b[:n], nil
|
||||||
}
|
}
|
||||||
func (dst *StringPayload) XXX_Merge(src proto.Message) {
|
func (m *StringPayload) XXX_Merge(src proto.Message) {
|
||||||
xxx_messageInfo_StringPayload.Merge(dst, src)
|
xxx_messageInfo_StringPayload.Merge(m, src)
|
||||||
}
|
}
|
||||||
func (m *StringPayload) XXX_Size() int {
|
func (m *StringPayload) XXX_Size() int {
|
||||||
return m.Size()
|
return m.Size()
|
||||||
@@ -56,10 +58,26 @@ var xxx_messageInfo_StringPayload proto.InternalMessageInfo
|
|||||||
func init() {
|
func init() {
|
||||||
proto.RegisterType((*StringPayload)(nil), "cockroach.errorspb.StringPayload")
|
proto.RegisterType((*StringPayload)(nil), "cockroach.errorspb.StringPayload")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("errorspb/hintdetail.proto", fileDescriptor_d99b88ec9cc4bc22) }
|
||||||
|
|
||||||
|
var fileDescriptor_d99b88ec9cc4bc22 = []byte{
|
||||||
|
// 143 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4c, 0x2d, 0x2a, 0xca,
|
||||||
|
0x2f, 0x2a, 0x2e, 0x48, 0xd2, 0xcf, 0xc8, 0xcc, 0x2b, 0x49, 0x49, 0x2d, 0x49, 0xcc, 0xcc, 0xd1,
|
||||||
|
0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x4a, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce,
|
||||||
|
0xd0, 0x83, 0x29, 0x52, 0x52, 0xe4, 0xe2, 0x0d, 0x2e, 0x29, 0xca, 0xcc, 0x4b, 0x0f, 0x48, 0xac,
|
||||||
|
0xcc, 0xc9, 0x4f, 0x4c, 0x11, 0x12, 0xe0, 0x62, 0xce, 0x2d, 0x4e, 0x97, 0x60, 0x54, 0x60, 0xd4,
|
||||||
|
0xe0, 0x0c, 0x02, 0x31, 0x9d, 0xb4, 0x4e, 0x3c, 0x94, 0x63, 0x38, 0xf1, 0x48, 0x8e, 0xf1, 0xc2,
|
||||||
|
0x23, 0x39, 0xc6, 0x1b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1,
|
||||||
|
0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x38, 0x60, 0xc6, 0x25, 0xb1, 0x81, 0x6d,
|
||||||
|
0x32, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xe2, 0xbd, 0x3a, 0x3e, 0x86, 0x00, 0x00, 0x00,
|
||||||
|
}
|
||||||
|
|
||||||
func (m *StringPayload) Marshal() (dAtA []byte, err error) {
|
func (m *StringPayload) Marshal() (dAtA []byte, err error) {
|
||||||
size := m.Size()
|
size := m.Size()
|
||||||
dAtA = make([]byte, size)
|
dAtA = make([]byte, size)
|
||||||
n, err := m.MarshalTo(dAtA)
|
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -67,27 +85,35 @@ func (m *StringPayload) Marshal() (dAtA []byte, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *StringPayload) MarshalTo(dAtA []byte) (int, error) {
|
func (m *StringPayload) MarshalTo(dAtA []byte) (int, error) {
|
||||||
var i int
|
size := m.Size()
|
||||||
|
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StringPayload) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||||
|
i := len(dAtA)
|
||||||
_ = i
|
_ = i
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = l
|
||||||
if len(m.Msg) > 0 {
|
if len(m.Msg) > 0 {
|
||||||
dAtA[i] = 0xa
|
i -= len(m.Msg)
|
||||||
i++
|
copy(dAtA[i:], m.Msg)
|
||||||
i = encodeVarintHintdetail(dAtA, i, uint64(len(m.Msg)))
|
i = encodeVarintHintdetail(dAtA, i, uint64(len(m.Msg)))
|
||||||
i += copy(dAtA[i:], m.Msg)
|
i--
|
||||||
|
dAtA[i] = 0xa
|
||||||
}
|
}
|
||||||
return i, nil
|
return len(dAtA) - i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeVarintHintdetail(dAtA []byte, offset int, v uint64) int {
|
func encodeVarintHintdetail(dAtA []byte, offset int, v uint64) int {
|
||||||
|
offset -= sovHintdetail(v)
|
||||||
|
base := offset
|
||||||
for v >= 1<<7 {
|
for v >= 1<<7 {
|
||||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||||
v >>= 7
|
v >>= 7
|
||||||
offset++
|
offset++
|
||||||
}
|
}
|
||||||
dAtA[offset] = uint8(v)
|
dAtA[offset] = uint8(v)
|
||||||
return offset + 1
|
return base
|
||||||
}
|
}
|
||||||
func (m *StringPayload) Size() (n int) {
|
func (m *StringPayload) Size() (n int) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
@@ -103,14 +129,7 @@ func (m *StringPayload) Size() (n int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sovHintdetail(x uint64) (n int) {
|
func sovHintdetail(x uint64) (n int) {
|
||||||
for {
|
return (math_bits.Len64(x|1) + 6) / 7
|
||||||
n++
|
|
||||||
x >>= 7
|
|
||||||
if x == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
}
|
||||||
func sozHintdetail(x uint64) (n int) {
|
func sozHintdetail(x uint64) (n int) {
|
||||||
return sovHintdetail(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
return sovHintdetail(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
@@ -130,7 +149,7 @@ func (m *StringPayload) Unmarshal(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
b := dAtA[iNdEx]
|
b := dAtA[iNdEx]
|
||||||
iNdEx++
|
iNdEx++
|
||||||
wire |= (uint64(b) & 0x7F) << shift
|
wire |= uint64(b&0x7F) << shift
|
||||||
if b < 0x80 {
|
if b < 0x80 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -158,7 +177,7 @@ func (m *StringPayload) Unmarshal(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
b := dAtA[iNdEx]
|
b := dAtA[iNdEx]
|
||||||
iNdEx++
|
iNdEx++
|
||||||
stringLen |= (uint64(b) & 0x7F) << shift
|
stringLen |= uint64(b&0x7F) << shift
|
||||||
if b < 0x80 {
|
if b < 0x80 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -168,6 +187,9 @@ func (m *StringPayload) Unmarshal(dAtA []byte) error {
|
|||||||
return ErrInvalidLengthHintdetail
|
return ErrInvalidLengthHintdetail
|
||||||
}
|
}
|
||||||
postIndex := iNdEx + intStringLen
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthHintdetail
|
||||||
|
}
|
||||||
if postIndex > l {
|
if postIndex > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
@@ -179,7 +201,7 @@ func (m *StringPayload) Unmarshal(dAtA []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if skippy < 0 {
|
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||||
return ErrInvalidLengthHintdetail
|
return ErrInvalidLengthHintdetail
|
||||||
}
|
}
|
||||||
if (iNdEx + skippy) > l {
|
if (iNdEx + skippy) > l {
|
||||||
@@ -197,6 +219,7 @@ func (m *StringPayload) Unmarshal(dAtA []byte) error {
|
|||||||
func skipHintdetail(dAtA []byte) (n int, err error) {
|
func skipHintdetail(dAtA []byte) (n int, err error) {
|
||||||
l := len(dAtA)
|
l := len(dAtA)
|
||||||
iNdEx := 0
|
iNdEx := 0
|
||||||
|
depth := 0
|
||||||
for iNdEx < l {
|
for iNdEx < l {
|
||||||
var wire uint64
|
var wire uint64
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
@@ -228,10 +251,8 @@ func skipHintdetail(dAtA []byte) (n int, err error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return iNdEx, nil
|
|
||||||
case 1:
|
case 1:
|
||||||
iNdEx += 8
|
iNdEx += 8
|
||||||
return iNdEx, nil
|
|
||||||
case 2:
|
case 2:
|
||||||
var length int
|
var length int
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
@@ -248,72 +269,34 @@ func skipHintdetail(dAtA []byte) (n int, err error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iNdEx += length
|
|
||||||
if length < 0 {
|
if length < 0 {
|
||||||
return 0, ErrInvalidLengthHintdetail
|
return 0, ErrInvalidLengthHintdetail
|
||||||
}
|
}
|
||||||
return iNdEx, nil
|
iNdEx += length
|
||||||
case 3:
|
case 3:
|
||||||
for {
|
depth++
|
||||||
var innerWire uint64
|
|
||||||
var start int = iNdEx
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return 0, ErrIntOverflowHintdetail
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
innerWire |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
innerWireType := int(innerWire & 0x7)
|
|
||||||
if innerWireType == 4 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
next, err := skipHintdetail(dAtA[start:])
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
iNdEx = start + next
|
|
||||||
}
|
|
||||||
return iNdEx, nil
|
|
||||||
case 4:
|
case 4:
|
||||||
return iNdEx, nil
|
if depth == 0 {
|
||||||
|
return 0, ErrUnexpectedEndOfGroupHintdetail
|
||||||
|
}
|
||||||
|
depth--
|
||||||
case 5:
|
case 5:
|
||||||
iNdEx += 4
|
iNdEx += 4
|
||||||
return iNdEx, nil
|
|
||||||
default:
|
default:
|
||||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||||
}
|
}
|
||||||
|
if iNdEx < 0 {
|
||||||
|
return 0, ErrInvalidLengthHintdetail
|
||||||
|
}
|
||||||
|
if depth == 0 {
|
||||||
|
return iNdEx, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
return 0, io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidLengthHintdetail = fmt.Errorf("proto: negative length found during unmarshaling")
|
ErrInvalidLengthHintdetail = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||||
ErrIntOverflowHintdetail = fmt.Errorf("proto: integer overflow")
|
ErrIntOverflowHintdetail = fmt.Errorf("proto: integer overflow")
|
||||||
|
ErrUnexpectedEndOfGroupHintdetail = fmt.Errorf("proto: unexpected end of group")
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterFile("github.com/cockroachdb/errors/errorspb/hintdetail.proto", fileDescriptor_hintdetail_429641491537a364)
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileDescriptor_hintdetail_429641491537a364 = []byte{
|
|
||||||
// 161 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x4f, 0xcf, 0x2c, 0xc9,
|
|
||||||
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce,
|
|
||||||
0x48, 0x49, 0xd2, 0x4f, 0x2d, 0x2a, 0xca, 0x2f, 0x2a, 0x86, 0x52, 0x05, 0x49, 0xfa, 0x19, 0x99,
|
|
||||||
0x79, 0x25, 0x29, 0xa9, 0x25, 0x89, 0x99, 0x39, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x42,
|
|
||||||
0x70, 0xd5, 0x7a, 0x30, 0x45, 0x4a, 0x8a, 0x5c, 0xbc, 0xc1, 0x25, 0x45, 0x99, 0x79, 0xe9, 0x01,
|
|
||||||
0x89, 0x95, 0x39, 0xf9, 0x89, 0x29, 0x42, 0x02, 0x5c, 0xcc, 0xb9, 0xc5, 0xe9, 0x12, 0x8c, 0x0a,
|
|
||||||
0x8c, 0x1a, 0x9c, 0x41, 0x20, 0xa6, 0x93, 0xd6, 0x89, 0x87, 0x72, 0x0c, 0x27, 0x1e, 0xc9, 0x31,
|
|
||||||
0x5e, 0x78, 0x24, 0xc7, 0x78, 0xe3, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb,
|
|
||||||
0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x14, 0x07, 0xcc, 0xb8, 0x24, 0x36,
|
|
||||||
0xb0, 0x4d, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe0, 0x71, 0x36, 0x88, 0xa4, 0x00, 0x00,
|
|
||||||
0x00,
|
|
||||||
}
|
|
||||||
|
|||||||
190
vendor/github.com/cockroachdb/errors/errorspb/markers.pb.go
generated
vendored
190
vendor/github.com/cockroachdb/errors/errorspb/markers.pb.go
generated
vendored
@@ -1,13 +1,16 @@
|
|||||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||||
// source: github.com/cockroachdb/errors/errorspb/markers.proto
|
// source: errorspb/markers.proto
|
||||||
|
|
||||||
package errorspb
|
package errorspb
|
||||||
|
|
||||||
import proto "github.com/gogo/protobuf/proto"
|
import (
|
||||||
import fmt "fmt"
|
fmt "fmt"
|
||||||
import math "math"
|
_ "github.com/gogo/protobuf/gogoproto"
|
||||||
|
proto "github.com/gogo/protobuf/proto"
|
||||||
import io "io"
|
io "io"
|
||||||
|
math "math"
|
||||||
|
math_bits "math/bits"
|
||||||
|
)
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ = proto.Marshal
|
var _ = proto.Marshal
|
||||||
@@ -18,7 +21,7 @@ var _ = math.Inf
|
|||||||
// is compatible with the proto package it is being compiled against.
|
// is compatible with the proto package it is being compiled against.
|
||||||
// A compilation error at this line likely means your copy of the
|
// A compilation error at this line likely means your copy of the
|
||||||
// proto package needs to be updated.
|
// proto package needs to be updated.
|
||||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
// MarkPayload is the error payload for a forced marker.
|
// MarkPayload is the error payload for a forced marker.
|
||||||
// See errors/markers/markers.go and the RFC on
|
// See errors/markers/markers.go and the RFC on
|
||||||
@@ -32,21 +35,21 @@ func (m *MarkPayload) Reset() { *m = MarkPayload{} }
|
|||||||
func (m *MarkPayload) String() string { return proto.CompactTextString(m) }
|
func (m *MarkPayload) String() string { return proto.CompactTextString(m) }
|
||||||
func (*MarkPayload) ProtoMessage() {}
|
func (*MarkPayload) ProtoMessage() {}
|
||||||
func (*MarkPayload) Descriptor() ([]byte, []int) {
|
func (*MarkPayload) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_markers_2a35e57f2b6b5666, []int{0}
|
return fileDescriptor_84c3fd24ba37816d, []int{0}
|
||||||
}
|
}
|
||||||
func (m *MarkPayload) XXX_Unmarshal(b []byte) error {
|
func (m *MarkPayload) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
}
|
}
|
||||||
func (m *MarkPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
func (m *MarkPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
b = b[:cap(b)]
|
b = b[:cap(b)]
|
||||||
n, err := m.MarshalTo(b)
|
n, err := m.MarshalToSizedBuffer(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return b[:n], nil
|
return b[:n], nil
|
||||||
}
|
}
|
||||||
func (dst *MarkPayload) XXX_Merge(src proto.Message) {
|
func (m *MarkPayload) XXX_Merge(src proto.Message) {
|
||||||
xxx_messageInfo_MarkPayload.Merge(dst, src)
|
xxx_messageInfo_MarkPayload.Merge(m, src)
|
||||||
}
|
}
|
||||||
func (m *MarkPayload) XXX_Size() int {
|
func (m *MarkPayload) XXX_Size() int {
|
||||||
return m.Size()
|
return m.Size()
|
||||||
@@ -60,10 +63,30 @@ var xxx_messageInfo_MarkPayload proto.InternalMessageInfo
|
|||||||
func init() {
|
func init() {
|
||||||
proto.RegisterType((*MarkPayload)(nil), "cockroach.errorspb.MarkPayload")
|
proto.RegisterType((*MarkPayload)(nil), "cockroach.errorspb.MarkPayload")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("errorspb/markers.proto", fileDescriptor_84c3fd24ba37816d) }
|
||||||
|
|
||||||
|
var fileDescriptor_84c3fd24ba37816d = []byte{
|
||||||
|
// 198 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4b, 0x2d, 0x2a, 0xca,
|
||||||
|
0x2f, 0x2a, 0x2e, 0x48, 0xd2, 0xcf, 0x4d, 0x2c, 0xca, 0x4e, 0x2d, 0x2a, 0xd6, 0x2b, 0x28, 0xca,
|
||||||
|
0x2f, 0xc9, 0x17, 0x12, 0x4a, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce, 0xd0, 0x83, 0xa9,
|
||||||
|
0x90, 0x12, 0x85, 0xab, 0x85, 0x30, 0x20, 0x4a, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x4c,
|
||||||
|
0x7d, 0x10, 0x0b, 0x22, 0xaa, 0x14, 0xc7, 0xc5, 0xed, 0x9b, 0x58, 0x94, 0x1d, 0x90, 0x58, 0x99,
|
||||||
|
0x93, 0x9f, 0x98, 0x22, 0x24, 0xc0, 0xc5, 0x9c, 0x5b, 0x9c, 0x2e, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1,
|
||||||
|
0x19, 0x04, 0x62, 0x0a, 0xd9, 0x72, 0xb1, 0x96, 0x54, 0x16, 0xa4, 0x16, 0x4b, 0x30, 0x29, 0x30,
|
||||||
|
0x6b, 0x70, 0x1b, 0x29, 0xea, 0x61, 0xda, 0xa8, 0xe7, 0x0a, 0x62, 0x84, 0x54, 0x16, 0xa4, 0x82,
|
||||||
|
0x8c, 0x72, 0x62, 0x39, 0x71, 0x4f, 0x9e, 0x21, 0x08, 0xa2, 0xcb, 0x49, 0xeb, 0xc4, 0x43, 0x39,
|
||||||
|
0x86, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0xbc, 0xf1, 0x48, 0x8e, 0xf1, 0xc1, 0x23,
|
||||||
|
0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x8a,
|
||||||
|
0x03, 0x66, 0x4c, 0x12, 0x1b, 0xd8, 0x49, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x43,
|
||||||
|
0x45, 0xef, 0xed, 0x00, 0x00, 0x00,
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MarkPayload) Marshal() (dAtA []byte, err error) {
|
func (m *MarkPayload) Marshal() (dAtA []byte, err error) {
|
||||||
size := m.Size()
|
size := m.Size()
|
||||||
dAtA = make([]byte, size)
|
dAtA = make([]byte, size)
|
||||||
n, err := m.MarshalTo(dAtA)
|
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -71,39 +94,49 @@ func (m *MarkPayload) Marshal() (dAtA []byte, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MarkPayload) MarshalTo(dAtA []byte) (int, error) {
|
func (m *MarkPayload) MarshalTo(dAtA []byte) (int, error) {
|
||||||
var i int
|
size := m.Size()
|
||||||
|
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MarkPayload) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||||
|
i := len(dAtA)
|
||||||
_ = i
|
_ = i
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = l
|
||||||
if len(m.Msg) > 0 {
|
|
||||||
dAtA[i] = 0xa
|
|
||||||
i++
|
|
||||||
i = encodeVarintMarkers(dAtA, i, uint64(len(m.Msg)))
|
|
||||||
i += copy(dAtA[i:], m.Msg)
|
|
||||||
}
|
|
||||||
if len(m.Types) > 0 {
|
if len(m.Types) > 0 {
|
||||||
for _, msg := range m.Types {
|
for iNdEx := len(m.Types) - 1; iNdEx >= 0; iNdEx-- {
|
||||||
dAtA[i] = 0x12
|
{
|
||||||
i++
|
size, err := m.Types[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
||||||
i = encodeVarintMarkers(dAtA, i, uint64(msg.Size()))
|
if err != nil {
|
||||||
n, err := msg.MarshalTo(dAtA[i:])
|
return 0, err
|
||||||
if err != nil {
|
}
|
||||||
return 0, err
|
i -= size
|
||||||
|
i = encodeVarintMarkers(dAtA, i, uint64(size))
|
||||||
}
|
}
|
||||||
i += n
|
i--
|
||||||
|
dAtA[i] = 0x12
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return i, nil
|
if len(m.Msg) > 0 {
|
||||||
|
i -= len(m.Msg)
|
||||||
|
copy(dAtA[i:], m.Msg)
|
||||||
|
i = encodeVarintMarkers(dAtA, i, uint64(len(m.Msg)))
|
||||||
|
i--
|
||||||
|
dAtA[i] = 0xa
|
||||||
|
}
|
||||||
|
return len(dAtA) - i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeVarintMarkers(dAtA []byte, offset int, v uint64) int {
|
func encodeVarintMarkers(dAtA []byte, offset int, v uint64) int {
|
||||||
|
offset -= sovMarkers(v)
|
||||||
|
base := offset
|
||||||
for v >= 1<<7 {
|
for v >= 1<<7 {
|
||||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||||
v >>= 7
|
v >>= 7
|
||||||
offset++
|
offset++
|
||||||
}
|
}
|
||||||
dAtA[offset] = uint8(v)
|
dAtA[offset] = uint8(v)
|
||||||
return offset + 1
|
return base
|
||||||
}
|
}
|
||||||
func (m *MarkPayload) Size() (n int) {
|
func (m *MarkPayload) Size() (n int) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
@@ -125,14 +158,7 @@ func (m *MarkPayload) Size() (n int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sovMarkers(x uint64) (n int) {
|
func sovMarkers(x uint64) (n int) {
|
||||||
for {
|
return (math_bits.Len64(x|1) + 6) / 7
|
||||||
n++
|
|
||||||
x >>= 7
|
|
||||||
if x == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
}
|
||||||
func sozMarkers(x uint64) (n int) {
|
func sozMarkers(x uint64) (n int) {
|
||||||
return sovMarkers(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
return sovMarkers(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
@@ -152,7 +178,7 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
b := dAtA[iNdEx]
|
b := dAtA[iNdEx]
|
||||||
iNdEx++
|
iNdEx++
|
||||||
wire |= (uint64(b) & 0x7F) << shift
|
wire |= uint64(b&0x7F) << shift
|
||||||
if b < 0x80 {
|
if b < 0x80 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -180,7 +206,7 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
b := dAtA[iNdEx]
|
b := dAtA[iNdEx]
|
||||||
iNdEx++
|
iNdEx++
|
||||||
stringLen |= (uint64(b) & 0x7F) << shift
|
stringLen |= uint64(b&0x7F) << shift
|
||||||
if b < 0x80 {
|
if b < 0x80 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -190,6 +216,9 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
|||||||
return ErrInvalidLengthMarkers
|
return ErrInvalidLengthMarkers
|
||||||
}
|
}
|
||||||
postIndex := iNdEx + intStringLen
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthMarkers
|
||||||
|
}
|
||||||
if postIndex > l {
|
if postIndex > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
@@ -209,7 +238,7 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
b := dAtA[iNdEx]
|
b := dAtA[iNdEx]
|
||||||
iNdEx++
|
iNdEx++
|
||||||
msglen |= (int(b) & 0x7F) << shift
|
msglen |= int(b&0x7F) << shift
|
||||||
if b < 0x80 {
|
if b < 0x80 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -218,6 +247,9 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
|||||||
return ErrInvalidLengthMarkers
|
return ErrInvalidLengthMarkers
|
||||||
}
|
}
|
||||||
postIndex := iNdEx + msglen
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthMarkers
|
||||||
|
}
|
||||||
if postIndex > l {
|
if postIndex > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
@@ -232,7 +264,7 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if skippy < 0 {
|
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||||
return ErrInvalidLengthMarkers
|
return ErrInvalidLengthMarkers
|
||||||
}
|
}
|
||||||
if (iNdEx + skippy) > l {
|
if (iNdEx + skippy) > l {
|
||||||
@@ -250,6 +282,7 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
|||||||
func skipMarkers(dAtA []byte) (n int, err error) {
|
func skipMarkers(dAtA []byte) (n int, err error) {
|
||||||
l := len(dAtA)
|
l := len(dAtA)
|
||||||
iNdEx := 0
|
iNdEx := 0
|
||||||
|
depth := 0
|
||||||
for iNdEx < l {
|
for iNdEx < l {
|
||||||
var wire uint64
|
var wire uint64
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
@@ -281,10 +314,8 @@ func skipMarkers(dAtA []byte) (n int, err error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return iNdEx, nil
|
|
||||||
case 1:
|
case 1:
|
||||||
iNdEx += 8
|
iNdEx += 8
|
||||||
return iNdEx, nil
|
|
||||||
case 2:
|
case 2:
|
||||||
var length int
|
var length int
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
@@ -301,75 +332,34 @@ func skipMarkers(dAtA []byte) (n int, err error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iNdEx += length
|
|
||||||
if length < 0 {
|
if length < 0 {
|
||||||
return 0, ErrInvalidLengthMarkers
|
return 0, ErrInvalidLengthMarkers
|
||||||
}
|
}
|
||||||
return iNdEx, nil
|
iNdEx += length
|
||||||
case 3:
|
case 3:
|
||||||
for {
|
depth++
|
||||||
var innerWire uint64
|
|
||||||
var start int = iNdEx
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return 0, ErrIntOverflowMarkers
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
innerWire |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
innerWireType := int(innerWire & 0x7)
|
|
||||||
if innerWireType == 4 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
next, err := skipMarkers(dAtA[start:])
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
iNdEx = start + next
|
|
||||||
}
|
|
||||||
return iNdEx, nil
|
|
||||||
case 4:
|
case 4:
|
||||||
return iNdEx, nil
|
if depth == 0 {
|
||||||
|
return 0, ErrUnexpectedEndOfGroupMarkers
|
||||||
|
}
|
||||||
|
depth--
|
||||||
case 5:
|
case 5:
|
||||||
iNdEx += 4
|
iNdEx += 4
|
||||||
return iNdEx, nil
|
|
||||||
default:
|
default:
|
||||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||||
}
|
}
|
||||||
|
if iNdEx < 0 {
|
||||||
|
return 0, ErrInvalidLengthMarkers
|
||||||
|
}
|
||||||
|
if depth == 0 {
|
||||||
|
return iNdEx, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
return 0, io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidLengthMarkers = fmt.Errorf("proto: negative length found during unmarshaling")
|
ErrInvalidLengthMarkers = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||||
ErrIntOverflowMarkers = fmt.Errorf("proto: integer overflow")
|
ErrIntOverflowMarkers = fmt.Errorf("proto: integer overflow")
|
||||||
|
ErrUnexpectedEndOfGroupMarkers = fmt.Errorf("proto: unexpected end of group")
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterFile("github.com/cockroachdb/errors/errorspb/markers.proto", fileDescriptor_markers_2a35e57f2b6b5666)
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileDescriptor_markers_2a35e57f2b6b5666 = []byte{
|
|
||||||
// 216 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x49, 0xcf, 0x2c, 0xc9,
|
|
||||||
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce,
|
|
||||||
0x48, 0x49, 0xd2, 0x4f, 0x2d, 0x2a, 0xca, 0x2f, 0x2a, 0x86, 0x52, 0x05, 0x49, 0xfa, 0xb9, 0x89,
|
|
||||||
0x45, 0xd9, 0xa9, 0x45, 0xc5, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x42, 0x70, 0xa5, 0x7a,
|
|
||||||
0x30, 0x15, 0x52, 0xc6, 0x44, 0x9a, 0x04, 0x61, 0x40, 0x0c, 0x92, 0x12, 0x49, 0xcf, 0x4f, 0xcf,
|
|
||||||
0x07, 0x33, 0xf5, 0x41, 0x2c, 0x88, 0xa8, 0x52, 0x1c, 0x17, 0xb7, 0x6f, 0x62, 0x51, 0x76, 0x40,
|
|
||||||
0x62, 0x65, 0x4e, 0x7e, 0x62, 0x8a, 0x90, 0x00, 0x17, 0x73, 0x6e, 0x71, 0xba, 0x04, 0xa3, 0x02,
|
|
||||||
0xa3, 0x06, 0x67, 0x10, 0x88, 0x29, 0x64, 0xcb, 0xc5, 0x5a, 0x52, 0x59, 0x90, 0x5a, 0x2c, 0xc1,
|
|
||||||
0xa4, 0xc0, 0xac, 0xc1, 0x6d, 0xa4, 0xa8, 0x87, 0xe9, 0x1e, 0x3d, 0x57, 0x10, 0x23, 0xa4, 0xb2,
|
|
||||||
0x20, 0x15, 0x64, 0x94, 0x13, 0xcb, 0x89, 0x7b, 0xf2, 0x0c, 0x41, 0x10, 0x5d, 0x4e, 0x5a, 0x27,
|
|
||||||
0x1e, 0xca, 0x31, 0x9c, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x8d, 0x47, 0x72, 0x8c,
|
|
||||||
0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72,
|
|
||||||
0x0c, 0x51, 0x1c, 0x30, 0x63, 0x92, 0xd8, 0xc0, 0x4e, 0x32, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff,
|
|
||||||
0xdc, 0x2d, 0x59, 0x6b, 0x29, 0x01, 0x00, 0x00,
|
|
||||||
}
|
|
||||||
|
|||||||
2
vendor/github.com/cockroachdb/errors/errorspb/markers.proto
generated
vendored
2
vendor/github.com/cockroachdb/errors/errorspb/markers.proto
generated
vendored
@@ -2,7 +2,7 @@ syntax = "proto3";
|
|||||||
package cockroach.errorspb;
|
package cockroach.errorspb;
|
||||||
option go_package = "errorspb";
|
option go_package = "errorspb";
|
||||||
|
|
||||||
import "github.com/cockroachdb/errors/errorspb/errors.proto";
|
import "errorspb/errors.proto";
|
||||||
import "gogoproto/gogo.proto";
|
import "gogoproto/gogo.proto";
|
||||||
|
|
||||||
// MarkPayload is the error payload for a forced marker.
|
// MarkPayload is the error payload for a forced marker.
|
||||||
|
|||||||
225
vendor/github.com/cockroachdb/errors/errorspb/tags.pb.go
generated
vendored
225
vendor/github.com/cockroachdb/errors/errorspb/tags.pb.go
generated
vendored
@@ -1,13 +1,16 @@
|
|||||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||||
// source: github.com/cockroachdb/errors/errorspb/tags.proto
|
// source: errorspb/tags.proto
|
||||||
|
|
||||||
package errorspb
|
package errorspb
|
||||||
|
|
||||||
import proto "github.com/gogo/protobuf/proto"
|
import (
|
||||||
import fmt "fmt"
|
fmt "fmt"
|
||||||
import math "math"
|
_ "github.com/gogo/protobuf/gogoproto"
|
||||||
|
proto "github.com/gogo/protobuf/proto"
|
||||||
import io "io"
|
io "io"
|
||||||
|
math "math"
|
||||||
|
math_bits "math/bits"
|
||||||
|
)
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ = proto.Marshal
|
var _ = proto.Marshal
|
||||||
@@ -18,7 +21,7 @@ var _ = math.Inf
|
|||||||
// is compatible with the proto package it is being compiled against.
|
// is compatible with the proto package it is being compiled against.
|
||||||
// A compilation error at this line likely means your copy of the
|
// A compilation error at this line likely means your copy of the
|
||||||
// proto package needs to be updated.
|
// proto package needs to be updated.
|
||||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
// TagsPayload is the error payload for a WithContext
|
// TagsPayload is the error payload for a WithContext
|
||||||
// marker.
|
// marker.
|
||||||
@@ -32,21 +35,21 @@ func (m *TagsPayload) Reset() { *m = TagsPayload{} }
|
|||||||
func (m *TagsPayload) String() string { return proto.CompactTextString(m) }
|
func (m *TagsPayload) String() string { return proto.CompactTextString(m) }
|
||||||
func (*TagsPayload) ProtoMessage() {}
|
func (*TagsPayload) ProtoMessage() {}
|
||||||
func (*TagsPayload) Descriptor() ([]byte, []int) {
|
func (*TagsPayload) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_tags_c75c5e322ef6b31c, []int{0}
|
return fileDescriptor_2f0d1dc5c54e9f63, []int{0}
|
||||||
}
|
}
|
||||||
func (m *TagsPayload) XXX_Unmarshal(b []byte) error {
|
func (m *TagsPayload) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
}
|
}
|
||||||
func (m *TagsPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
func (m *TagsPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
b = b[:cap(b)]
|
b = b[:cap(b)]
|
||||||
n, err := m.MarshalTo(b)
|
n, err := m.MarshalToSizedBuffer(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return b[:n], nil
|
return b[:n], nil
|
||||||
}
|
}
|
||||||
func (dst *TagsPayload) XXX_Merge(src proto.Message) {
|
func (m *TagsPayload) XXX_Merge(src proto.Message) {
|
||||||
xxx_messageInfo_TagsPayload.Merge(dst, src)
|
xxx_messageInfo_TagsPayload.Merge(m, src)
|
||||||
}
|
}
|
||||||
func (m *TagsPayload) XXX_Size() int {
|
func (m *TagsPayload) XXX_Size() int {
|
||||||
return m.Size()
|
return m.Size()
|
||||||
@@ -66,21 +69,21 @@ func (m *TagPayload) Reset() { *m = TagPayload{} }
|
|||||||
func (m *TagPayload) String() string { return proto.CompactTextString(m) }
|
func (m *TagPayload) String() string { return proto.CompactTextString(m) }
|
||||||
func (*TagPayload) ProtoMessage() {}
|
func (*TagPayload) ProtoMessage() {}
|
||||||
func (*TagPayload) Descriptor() ([]byte, []int) {
|
func (*TagPayload) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_tags_c75c5e322ef6b31c, []int{1}
|
return fileDescriptor_2f0d1dc5c54e9f63, []int{1}
|
||||||
}
|
}
|
||||||
func (m *TagPayload) XXX_Unmarshal(b []byte) error {
|
func (m *TagPayload) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
}
|
}
|
||||||
func (m *TagPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
func (m *TagPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
b = b[:cap(b)]
|
b = b[:cap(b)]
|
||||||
n, err := m.MarshalTo(b)
|
n, err := m.MarshalToSizedBuffer(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return b[:n], nil
|
return b[:n], nil
|
||||||
}
|
}
|
||||||
func (dst *TagPayload) XXX_Merge(src proto.Message) {
|
func (m *TagPayload) XXX_Merge(src proto.Message) {
|
||||||
xxx_messageInfo_TagPayload.Merge(dst, src)
|
xxx_messageInfo_TagPayload.Merge(m, src)
|
||||||
}
|
}
|
||||||
func (m *TagPayload) XXX_Size() int {
|
func (m *TagPayload) XXX_Size() int {
|
||||||
return m.Size()
|
return m.Size()
|
||||||
@@ -95,10 +98,30 @@ func init() {
|
|||||||
proto.RegisterType((*TagsPayload)(nil), "cockroach.errorspb.TagsPayload")
|
proto.RegisterType((*TagsPayload)(nil), "cockroach.errorspb.TagsPayload")
|
||||||
proto.RegisterType((*TagPayload)(nil), "cockroach.errorspb.TagPayload")
|
proto.RegisterType((*TagPayload)(nil), "cockroach.errorspb.TagPayload")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("errorspb/tags.proto", fileDescriptor_2f0d1dc5c54e9f63) }
|
||||||
|
|
||||||
|
var fileDescriptor_2f0d1dc5c54e9f63 = []byte{
|
||||||
|
// 201 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0x2d, 0x2a, 0xca,
|
||||||
|
0x2f, 0x2a, 0x2e, 0x48, 0xd2, 0x2f, 0x49, 0x4c, 0x2f, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17,
|
||||||
|
0x12, 0x4a, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce, 0xd0, 0x83, 0x49, 0x4b, 0x89, 0xa4,
|
||||||
|
0xe7, 0xa7, 0xe7, 0x83, 0xa5, 0xf5, 0x41, 0x2c, 0x88, 0x4a, 0x25, 0x77, 0x2e, 0xee, 0x90, 0xc4,
|
||||||
|
0xf4, 0xe2, 0x80, 0xc4, 0xca, 0x9c, 0xfc, 0xc4, 0x14, 0x21, 0x0b, 0x2e, 0x16, 0x90, 0x31, 0x12,
|
||||||
|
0x8c, 0x0a, 0xcc, 0x1a, 0xdc, 0x46, 0x72, 0x7a, 0x98, 0xe6, 0xe8, 0x85, 0x24, 0xa6, 0x43, 0x55,
|
||||||
|
0x3b, 0xb1, 0x9c, 0xb8, 0x27, 0xcf, 0x10, 0x04, 0xd6, 0xa1, 0x64, 0xc2, 0xc5, 0x85, 0x90, 0x11,
|
||||||
|
0x12, 0xe0, 0x62, 0x2e, 0x49, 0x4c, 0x97, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0x31, 0x85,
|
||||||
|
0x44, 0xb8, 0x58, 0xcb, 0x12, 0x73, 0x4a, 0x53, 0x25, 0x98, 0xc0, 0x62, 0x10, 0x8e, 0x93, 0xd6,
|
||||||
|
0x89, 0x87, 0x72, 0x0c, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0x78, 0xe3, 0x91, 0x1c,
|
||||||
|
0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1,
|
||||||
|
0x1c, 0x43, 0x14, 0x07, 0xcc, 0xe2, 0x24, 0x36, 0xb0, 0x8b, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff,
|
||||||
|
0xff, 0x0a, 0x90, 0xd1, 0xc9, 0xf2, 0x00, 0x00, 0x00,
|
||||||
|
}
|
||||||
|
|
||||||
func (m *TagsPayload) Marshal() (dAtA []byte, err error) {
|
func (m *TagsPayload) Marshal() (dAtA []byte, err error) {
|
||||||
size := m.Size()
|
size := m.Size()
|
||||||
dAtA = make([]byte, size)
|
dAtA = make([]byte, size)
|
||||||
n, err := m.MarshalTo(dAtA)
|
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -106,29 +129,36 @@ func (m *TagsPayload) Marshal() (dAtA []byte, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *TagsPayload) MarshalTo(dAtA []byte) (int, error) {
|
func (m *TagsPayload) MarshalTo(dAtA []byte) (int, error) {
|
||||||
var i int
|
size := m.Size()
|
||||||
|
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TagsPayload) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||||
|
i := len(dAtA)
|
||||||
_ = i
|
_ = i
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = l
|
||||||
if len(m.Tags) > 0 {
|
if len(m.Tags) > 0 {
|
||||||
for _, msg := range m.Tags {
|
for iNdEx := len(m.Tags) - 1; iNdEx >= 0; iNdEx-- {
|
||||||
dAtA[i] = 0xa
|
{
|
||||||
i++
|
size, err := m.Tags[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
||||||
i = encodeVarintTags(dAtA, i, uint64(msg.Size()))
|
if err != nil {
|
||||||
n, err := msg.MarshalTo(dAtA[i:])
|
return 0, err
|
||||||
if err != nil {
|
}
|
||||||
return 0, err
|
i -= size
|
||||||
|
i = encodeVarintTags(dAtA, i, uint64(size))
|
||||||
}
|
}
|
||||||
i += n
|
i--
|
||||||
|
dAtA[i] = 0xa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return i, nil
|
return len(dAtA) - i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *TagPayload) Marshal() (dAtA []byte, err error) {
|
func (m *TagPayload) Marshal() (dAtA []byte, err error) {
|
||||||
size := m.Size()
|
size := m.Size()
|
||||||
dAtA = make([]byte, size)
|
dAtA = make([]byte, size)
|
||||||
n, err := m.MarshalTo(dAtA)
|
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -136,33 +166,42 @@ func (m *TagPayload) Marshal() (dAtA []byte, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *TagPayload) MarshalTo(dAtA []byte) (int, error) {
|
func (m *TagPayload) MarshalTo(dAtA []byte) (int, error) {
|
||||||
var i int
|
size := m.Size()
|
||||||
|
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TagPayload) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||||
|
i := len(dAtA)
|
||||||
_ = i
|
_ = i
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = l
|
||||||
if len(m.Tag) > 0 {
|
|
||||||
dAtA[i] = 0xa
|
|
||||||
i++
|
|
||||||
i = encodeVarintTags(dAtA, i, uint64(len(m.Tag)))
|
|
||||||
i += copy(dAtA[i:], m.Tag)
|
|
||||||
}
|
|
||||||
if len(m.Value) > 0 {
|
if len(m.Value) > 0 {
|
||||||
dAtA[i] = 0x12
|
i -= len(m.Value)
|
||||||
i++
|
copy(dAtA[i:], m.Value)
|
||||||
i = encodeVarintTags(dAtA, i, uint64(len(m.Value)))
|
i = encodeVarintTags(dAtA, i, uint64(len(m.Value)))
|
||||||
i += copy(dAtA[i:], m.Value)
|
i--
|
||||||
|
dAtA[i] = 0x12
|
||||||
}
|
}
|
||||||
return i, nil
|
if len(m.Tag) > 0 {
|
||||||
|
i -= len(m.Tag)
|
||||||
|
copy(dAtA[i:], m.Tag)
|
||||||
|
i = encodeVarintTags(dAtA, i, uint64(len(m.Tag)))
|
||||||
|
i--
|
||||||
|
dAtA[i] = 0xa
|
||||||
|
}
|
||||||
|
return len(dAtA) - i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeVarintTags(dAtA []byte, offset int, v uint64) int {
|
func encodeVarintTags(dAtA []byte, offset int, v uint64) int {
|
||||||
|
offset -= sovTags(v)
|
||||||
|
base := offset
|
||||||
for v >= 1<<7 {
|
for v >= 1<<7 {
|
||||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||||
v >>= 7
|
v >>= 7
|
||||||
offset++
|
offset++
|
||||||
}
|
}
|
||||||
dAtA[offset] = uint8(v)
|
dAtA[offset] = uint8(v)
|
||||||
return offset + 1
|
return base
|
||||||
}
|
}
|
||||||
func (m *TagsPayload) Size() (n int) {
|
func (m *TagsPayload) Size() (n int) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
@@ -197,14 +236,7 @@ func (m *TagPayload) Size() (n int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sovTags(x uint64) (n int) {
|
func sovTags(x uint64) (n int) {
|
||||||
for {
|
return (math_bits.Len64(x|1) + 6) / 7
|
||||||
n++
|
|
||||||
x >>= 7
|
|
||||||
if x == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
}
|
||||||
func sozTags(x uint64) (n int) {
|
func sozTags(x uint64) (n int) {
|
||||||
return sovTags(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
return sovTags(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
@@ -224,7 +256,7 @@ func (m *TagsPayload) Unmarshal(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
b := dAtA[iNdEx]
|
b := dAtA[iNdEx]
|
||||||
iNdEx++
|
iNdEx++
|
||||||
wire |= (uint64(b) & 0x7F) << shift
|
wire |= uint64(b&0x7F) << shift
|
||||||
if b < 0x80 {
|
if b < 0x80 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -252,7 +284,7 @@ func (m *TagsPayload) Unmarshal(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
b := dAtA[iNdEx]
|
b := dAtA[iNdEx]
|
||||||
iNdEx++
|
iNdEx++
|
||||||
msglen |= (int(b) & 0x7F) << shift
|
msglen |= int(b&0x7F) << shift
|
||||||
if b < 0x80 {
|
if b < 0x80 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -261,6 +293,9 @@ func (m *TagsPayload) Unmarshal(dAtA []byte) error {
|
|||||||
return ErrInvalidLengthTags
|
return ErrInvalidLengthTags
|
||||||
}
|
}
|
||||||
postIndex := iNdEx + msglen
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthTags
|
||||||
|
}
|
||||||
if postIndex > l {
|
if postIndex > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
@@ -275,7 +310,7 @@ func (m *TagsPayload) Unmarshal(dAtA []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if skippy < 0 {
|
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||||
return ErrInvalidLengthTags
|
return ErrInvalidLengthTags
|
||||||
}
|
}
|
||||||
if (iNdEx + skippy) > l {
|
if (iNdEx + skippy) > l {
|
||||||
@@ -305,7 +340,7 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
b := dAtA[iNdEx]
|
b := dAtA[iNdEx]
|
||||||
iNdEx++
|
iNdEx++
|
||||||
wire |= (uint64(b) & 0x7F) << shift
|
wire |= uint64(b&0x7F) << shift
|
||||||
if b < 0x80 {
|
if b < 0x80 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -333,7 +368,7 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
b := dAtA[iNdEx]
|
b := dAtA[iNdEx]
|
||||||
iNdEx++
|
iNdEx++
|
||||||
stringLen |= (uint64(b) & 0x7F) << shift
|
stringLen |= uint64(b&0x7F) << shift
|
||||||
if b < 0x80 {
|
if b < 0x80 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -343,6 +378,9 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
|||||||
return ErrInvalidLengthTags
|
return ErrInvalidLengthTags
|
||||||
}
|
}
|
||||||
postIndex := iNdEx + intStringLen
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthTags
|
||||||
|
}
|
||||||
if postIndex > l {
|
if postIndex > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
@@ -362,7 +400,7 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
b := dAtA[iNdEx]
|
b := dAtA[iNdEx]
|
||||||
iNdEx++
|
iNdEx++
|
||||||
stringLen |= (uint64(b) & 0x7F) << shift
|
stringLen |= uint64(b&0x7F) << shift
|
||||||
if b < 0x80 {
|
if b < 0x80 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -372,6 +410,9 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
|||||||
return ErrInvalidLengthTags
|
return ErrInvalidLengthTags
|
||||||
}
|
}
|
||||||
postIndex := iNdEx + intStringLen
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthTags
|
||||||
|
}
|
||||||
if postIndex > l {
|
if postIndex > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
@@ -383,7 +424,7 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if skippy < 0 {
|
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||||
return ErrInvalidLengthTags
|
return ErrInvalidLengthTags
|
||||||
}
|
}
|
||||||
if (iNdEx + skippy) > l {
|
if (iNdEx + skippy) > l {
|
||||||
@@ -401,6 +442,7 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
|||||||
func skipTags(dAtA []byte) (n int, err error) {
|
func skipTags(dAtA []byte) (n int, err error) {
|
||||||
l := len(dAtA)
|
l := len(dAtA)
|
||||||
iNdEx := 0
|
iNdEx := 0
|
||||||
|
depth := 0
|
||||||
for iNdEx < l {
|
for iNdEx < l {
|
||||||
var wire uint64
|
var wire uint64
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
@@ -432,10 +474,8 @@ func skipTags(dAtA []byte) (n int, err error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return iNdEx, nil
|
|
||||||
case 1:
|
case 1:
|
||||||
iNdEx += 8
|
iNdEx += 8
|
||||||
return iNdEx, nil
|
|
||||||
case 2:
|
case 2:
|
||||||
var length int
|
var length int
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
@@ -452,75 +492,34 @@ func skipTags(dAtA []byte) (n int, err error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iNdEx += length
|
|
||||||
if length < 0 {
|
if length < 0 {
|
||||||
return 0, ErrInvalidLengthTags
|
return 0, ErrInvalidLengthTags
|
||||||
}
|
}
|
||||||
return iNdEx, nil
|
iNdEx += length
|
||||||
case 3:
|
case 3:
|
||||||
for {
|
depth++
|
||||||
var innerWire uint64
|
|
||||||
var start int = iNdEx
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return 0, ErrIntOverflowTags
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
innerWire |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
innerWireType := int(innerWire & 0x7)
|
|
||||||
if innerWireType == 4 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
next, err := skipTags(dAtA[start:])
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
iNdEx = start + next
|
|
||||||
}
|
|
||||||
return iNdEx, nil
|
|
||||||
case 4:
|
case 4:
|
||||||
return iNdEx, nil
|
if depth == 0 {
|
||||||
|
return 0, ErrUnexpectedEndOfGroupTags
|
||||||
|
}
|
||||||
|
depth--
|
||||||
case 5:
|
case 5:
|
||||||
iNdEx += 4
|
iNdEx += 4
|
||||||
return iNdEx, nil
|
|
||||||
default:
|
default:
|
||||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||||
}
|
}
|
||||||
|
if iNdEx < 0 {
|
||||||
|
return 0, ErrInvalidLengthTags
|
||||||
|
}
|
||||||
|
if depth == 0 {
|
||||||
|
return iNdEx, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
return 0, io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidLengthTags = fmt.Errorf("proto: negative length found during unmarshaling")
|
ErrInvalidLengthTags = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||||
ErrIntOverflowTags = fmt.Errorf("proto: integer overflow")
|
ErrIntOverflowTags = fmt.Errorf("proto: integer overflow")
|
||||||
|
ErrUnexpectedEndOfGroupTags = fmt.Errorf("proto: unexpected end of group")
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterFile("github.com/cockroachdb/errors/errorspb/tags.proto", fileDescriptor_tags_c75c5e322ef6b31c)
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileDescriptor_tags_c75c5e322ef6b31c = []byte{
|
|
||||||
// 218 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x4c, 0xcf, 0x2c, 0xc9,
|
|
||||||
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce,
|
|
||||||
0x48, 0x49, 0xd2, 0x4f, 0x2d, 0x2a, 0xca, 0x2f, 0x2a, 0x86, 0x52, 0x05, 0x49, 0xfa, 0x25, 0x89,
|
|
||||||
0xe9, 0xc5, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x42, 0x70, 0x75, 0x7a, 0x30, 0x69, 0x29,
|
|
||||||
0x91, 0xf4, 0xfc, 0xf4, 0x7c, 0xb0, 0xb4, 0x3e, 0x88, 0x05, 0x51, 0xa9, 0xe4, 0xce, 0xc5, 0x1d,
|
|
||||||
0x92, 0x98, 0x5e, 0x1c, 0x90, 0x58, 0x99, 0x93, 0x9f, 0x98, 0x22, 0x64, 0xc1, 0xc5, 0x02, 0x32,
|
|
||||||
0x46, 0x82, 0x51, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x4e, 0x0f, 0xd3, 0x1c, 0xbd, 0x90, 0xc4, 0x74,
|
|
||||||
0xa8, 0x6a, 0x27, 0x96, 0x13, 0xf7, 0xe4, 0x19, 0x82, 0xc0, 0x3a, 0x94, 0x4c, 0xb8, 0xb8, 0x10,
|
|
||||||
0x32, 0x42, 0x02, 0x5c, 0xcc, 0x25, 0x89, 0xe9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x20,
|
|
||||||
0xa6, 0x90, 0x08, 0x17, 0x6b, 0x59, 0x62, 0x4e, 0x69, 0xaa, 0x04, 0x13, 0x58, 0x0c, 0xc2, 0x71,
|
|
||||||
0xd2, 0x3a, 0xf1, 0x50, 0x8e, 0xe1, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x6f, 0x3c,
|
|
||||||
0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e,
|
|
||||||
0x3c, 0x96, 0x63, 0x88, 0xe2, 0x80, 0x59, 0x9c, 0xc4, 0x06, 0x76, 0xb1, 0x31, 0x20, 0x00, 0x00,
|
|
||||||
0xff, 0xff, 0xa6, 0x1b, 0x01, 0x30, 0x10, 0x01, 0x00, 0x00,
|
|
||||||
}
|
|
||||||
|
|||||||
138
vendor/github.com/cockroachdb/errors/errorspb/testing.pb.go
generated
vendored
138
vendor/github.com/cockroachdb/errors/errorspb/testing.pb.go
generated
vendored
@@ -1,13 +1,15 @@
|
|||||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||||
// source: github.com/cockroachdb/errors/errorspb/testing.proto
|
// source: errorspb/testing.proto
|
||||||
|
|
||||||
package errorspb
|
package errorspb
|
||||||
|
|
||||||
import proto "github.com/gogo/protobuf/proto"
|
import (
|
||||||
import fmt "fmt"
|
fmt "fmt"
|
||||||
import math "math"
|
proto "github.com/gogo/protobuf/proto"
|
||||||
|
io "io"
|
||||||
import io "io"
|
math "math"
|
||||||
|
math_bits "math/bits"
|
||||||
|
)
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ = proto.Marshal
|
var _ = proto.Marshal
|
||||||
@@ -18,7 +20,7 @@ var _ = math.Inf
|
|||||||
// is compatible with the proto package it is being compiled against.
|
// is compatible with the proto package it is being compiled against.
|
||||||
// A compilation error at this line likely means your copy of the
|
// A compilation error at this line likely means your copy of the
|
||||||
// proto package needs to be updated.
|
// proto package needs to be updated.
|
||||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
// TestError is meant for use in testing only.
|
// TestError is meant for use in testing only.
|
||||||
type TestError struct {
|
type TestError struct {
|
||||||
@@ -28,21 +30,21 @@ func (m *TestError) Reset() { *m = TestError{} }
|
|||||||
func (m *TestError) String() string { return proto.CompactTextString(m) }
|
func (m *TestError) String() string { return proto.CompactTextString(m) }
|
||||||
func (*TestError) ProtoMessage() {}
|
func (*TestError) ProtoMessage() {}
|
||||||
func (*TestError) Descriptor() ([]byte, []int) {
|
func (*TestError) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_testing_d9751a0a5e86c096, []int{0}
|
return fileDescriptor_0551f0d913d6118f, []int{0}
|
||||||
}
|
}
|
||||||
func (m *TestError) XXX_Unmarshal(b []byte) error {
|
func (m *TestError) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
}
|
}
|
||||||
func (m *TestError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
func (m *TestError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
b = b[:cap(b)]
|
b = b[:cap(b)]
|
||||||
n, err := m.MarshalTo(b)
|
n, err := m.MarshalToSizedBuffer(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return b[:n], nil
|
return b[:n], nil
|
||||||
}
|
}
|
||||||
func (dst *TestError) XXX_Merge(src proto.Message) {
|
func (m *TestError) XXX_Merge(src proto.Message) {
|
||||||
xxx_messageInfo_TestError.Merge(dst, src)
|
xxx_messageInfo_TestError.Merge(m, src)
|
||||||
}
|
}
|
||||||
func (m *TestError) XXX_Size() int {
|
func (m *TestError) XXX_Size() int {
|
||||||
return m.Size()
|
return m.Size()
|
||||||
@@ -56,10 +58,25 @@ var xxx_messageInfo_TestError proto.InternalMessageInfo
|
|||||||
func init() {
|
func init() {
|
||||||
proto.RegisterType((*TestError)(nil), "cockroach.errorspb.TestError")
|
proto.RegisterType((*TestError)(nil), "cockroach.errorspb.TestError")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("errorspb/testing.proto", fileDescriptor_0551f0d913d6118f) }
|
||||||
|
|
||||||
|
var fileDescriptor_0551f0d913d6118f = []byte{
|
||||||
|
// 118 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4b, 0x2d, 0x2a, 0xca,
|
||||||
|
0x2f, 0x2a, 0x2e, 0x48, 0xd2, 0x2f, 0x49, 0x2d, 0x2e, 0xc9, 0xcc, 0x4b, 0xd7, 0x2b, 0x28, 0xca,
|
||||||
|
0x2f, 0xc9, 0x17, 0x12, 0x4a, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce, 0xd0, 0x83, 0xa9,
|
||||||
|
0x50, 0xe2, 0xe6, 0xe2, 0x0c, 0x49, 0x2d, 0x2e, 0x71, 0x05, 0xf1, 0x9d, 0xb4, 0x4e, 0x3c, 0x94,
|
||||||
|
0x63, 0x38, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x1b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c,
|
||||||
|
0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2,
|
||||||
|
0x38, 0x60, 0x1a, 0x93, 0xd8, 0xc0, 0x66, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x4b, 0xf3,
|
||||||
|
0x3e, 0x92, 0x6d, 0x00, 0x00, 0x00,
|
||||||
|
}
|
||||||
|
|
||||||
func (m *TestError) Marshal() (dAtA []byte, err error) {
|
func (m *TestError) Marshal() (dAtA []byte, err error) {
|
||||||
size := m.Size()
|
size := m.Size()
|
||||||
dAtA = make([]byte, size)
|
dAtA = make([]byte, size)
|
||||||
n, err := m.MarshalTo(dAtA)
|
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -67,21 +84,28 @@ func (m *TestError) Marshal() (dAtA []byte, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *TestError) MarshalTo(dAtA []byte) (int, error) {
|
func (m *TestError) MarshalTo(dAtA []byte) (int, error) {
|
||||||
var i int
|
size := m.Size()
|
||||||
|
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TestError) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||||
|
i := len(dAtA)
|
||||||
_ = i
|
_ = i
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = l
|
||||||
return i, nil
|
return len(dAtA) - i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeVarintTesting(dAtA []byte, offset int, v uint64) int {
|
func encodeVarintTesting(dAtA []byte, offset int, v uint64) int {
|
||||||
|
offset -= sovTesting(v)
|
||||||
|
base := offset
|
||||||
for v >= 1<<7 {
|
for v >= 1<<7 {
|
||||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||||
v >>= 7
|
v >>= 7
|
||||||
offset++
|
offset++
|
||||||
}
|
}
|
||||||
dAtA[offset] = uint8(v)
|
dAtA[offset] = uint8(v)
|
||||||
return offset + 1
|
return base
|
||||||
}
|
}
|
||||||
func (m *TestError) Size() (n int) {
|
func (m *TestError) Size() (n int) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
@@ -93,14 +117,7 @@ func (m *TestError) Size() (n int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sovTesting(x uint64) (n int) {
|
func sovTesting(x uint64) (n int) {
|
||||||
for {
|
return (math_bits.Len64(x|1) + 6) / 7
|
||||||
n++
|
|
||||||
x >>= 7
|
|
||||||
if x == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
}
|
||||||
func sozTesting(x uint64) (n int) {
|
func sozTesting(x uint64) (n int) {
|
||||||
return sovTesting(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
return sovTesting(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
@@ -120,7 +137,7 @@ func (m *TestError) Unmarshal(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
b := dAtA[iNdEx]
|
b := dAtA[iNdEx]
|
||||||
iNdEx++
|
iNdEx++
|
||||||
wire |= (uint64(b) & 0x7F) << shift
|
wire |= uint64(b&0x7F) << shift
|
||||||
if b < 0x80 {
|
if b < 0x80 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -140,7 +157,7 @@ func (m *TestError) Unmarshal(dAtA []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if skippy < 0 {
|
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||||
return ErrInvalidLengthTesting
|
return ErrInvalidLengthTesting
|
||||||
}
|
}
|
||||||
if (iNdEx + skippy) > l {
|
if (iNdEx + skippy) > l {
|
||||||
@@ -158,6 +175,7 @@ func (m *TestError) Unmarshal(dAtA []byte) error {
|
|||||||
func skipTesting(dAtA []byte) (n int, err error) {
|
func skipTesting(dAtA []byte) (n int, err error) {
|
||||||
l := len(dAtA)
|
l := len(dAtA)
|
||||||
iNdEx := 0
|
iNdEx := 0
|
||||||
|
depth := 0
|
||||||
for iNdEx < l {
|
for iNdEx < l {
|
||||||
var wire uint64
|
var wire uint64
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
@@ -189,10 +207,8 @@ func skipTesting(dAtA []byte) (n int, err error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return iNdEx, nil
|
|
||||||
case 1:
|
case 1:
|
||||||
iNdEx += 8
|
iNdEx += 8
|
||||||
return iNdEx, nil
|
|
||||||
case 2:
|
case 2:
|
||||||
var length int
|
var length int
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
@@ -209,70 +225,34 @@ func skipTesting(dAtA []byte) (n int, err error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iNdEx += length
|
|
||||||
if length < 0 {
|
if length < 0 {
|
||||||
return 0, ErrInvalidLengthTesting
|
return 0, ErrInvalidLengthTesting
|
||||||
}
|
}
|
||||||
return iNdEx, nil
|
iNdEx += length
|
||||||
case 3:
|
case 3:
|
||||||
for {
|
depth++
|
||||||
var innerWire uint64
|
|
||||||
var start int = iNdEx
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return 0, ErrIntOverflowTesting
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
innerWire |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
innerWireType := int(innerWire & 0x7)
|
|
||||||
if innerWireType == 4 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
next, err := skipTesting(dAtA[start:])
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
iNdEx = start + next
|
|
||||||
}
|
|
||||||
return iNdEx, nil
|
|
||||||
case 4:
|
case 4:
|
||||||
return iNdEx, nil
|
if depth == 0 {
|
||||||
|
return 0, ErrUnexpectedEndOfGroupTesting
|
||||||
|
}
|
||||||
|
depth--
|
||||||
case 5:
|
case 5:
|
||||||
iNdEx += 4
|
iNdEx += 4
|
||||||
return iNdEx, nil
|
|
||||||
default:
|
default:
|
||||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||||
}
|
}
|
||||||
|
if iNdEx < 0 {
|
||||||
|
return 0, ErrInvalidLengthTesting
|
||||||
|
}
|
||||||
|
if depth == 0 {
|
||||||
|
return iNdEx, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
return 0, io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidLengthTesting = fmt.Errorf("proto: negative length found during unmarshaling")
|
ErrInvalidLengthTesting = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||||
ErrIntOverflowTesting = fmt.Errorf("proto: integer overflow")
|
ErrIntOverflowTesting = fmt.Errorf("proto: integer overflow")
|
||||||
|
ErrUnexpectedEndOfGroupTesting = fmt.Errorf("proto: unexpected end of group")
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterFile("github.com/cockroachdb/errors/errorspb/testing.proto", fileDescriptor_testing_d9751a0a5e86c096)
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileDescriptor_testing_d9751a0a5e86c096 = []byte{
|
|
||||||
// 136 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x49, 0xcf, 0x2c, 0xc9,
|
|
||||||
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce,
|
|
||||||
0x48, 0x49, 0xd2, 0x4f, 0x2d, 0x2a, 0xca, 0x2f, 0x2a, 0x86, 0x52, 0x05, 0x49, 0xfa, 0x25, 0xa9,
|
|
||||||
0xc5, 0x25, 0x99, 0x79, 0xe9, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x42, 0x70, 0xa5, 0x7a,
|
|
||||||
0x30, 0x15, 0x4a, 0xdc, 0x5c, 0x9c, 0x21, 0xa9, 0xc5, 0x25, 0xae, 0x20, 0xbe, 0x93, 0xd6, 0x89,
|
|
||||||
0x87, 0x72, 0x0c, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0x78, 0xe3, 0x91, 0x1c, 0xe3,
|
|
||||||
0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c,
|
|
||||||
0x43, 0x14, 0x07, 0x4c, 0x63, 0x12, 0x1b, 0xd8, 0x4c, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff,
|
|
||||||
0x8a, 0x2c, 0x50, 0x5f, 0x8b, 0x00, 0x00, 0x00,
|
|
||||||
}
|
|
||||||
|
|||||||
9
vendor/github.com/cockroachdb/errors/errutil/as.go
generated
vendored
9
vendor/github.com/cockroachdb/errors/errutil/as.go
generated
vendored
@@ -58,7 +58,16 @@ func As(err error, target interface{}) bool {
|
|||||||
if x, ok := c.(interface{ As(interface{}) bool }); ok && x.As(target) {
|
if x, ok := c.(interface{ As(interface{}) bool }); ok && x.As(target) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If at any point in the single cause chain including the top,
|
||||||
|
// we encounter a multi-cause chain, recursively explore it.
|
||||||
|
for _, cause := range errbase.UnwrapMulti(c) {
|
||||||
|
if As(cause, target) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
vendor/github.com/cockroachdb/errors/errutil/doc.go
generated
vendored
2
vendor/github.com/cockroachdb/errors/errutil/doc.go
generated
vendored
@@ -14,6 +14,6 @@
|
|||||||
|
|
||||||
// Package errutil combines primitives from the library, the Go errors
|
// Package errutil combines primitives from the library, the Go errors
|
||||||
// package and github.com/pkg/errors. It aims to serve as drop-in
|
// package and github.com/pkg/errors. It aims to serve as drop-in
|
||||||
// replacement to gitub.com/pkg/errors and replace the legacy
|
// replacement to github.com/pkg/errors and replace the legacy
|
||||||
// implementation of `pgerrors`.
|
// implementation of `pgerrors`.
|
||||||
package errutil
|
package errutil
|
||||||
|
|||||||
8
vendor/github.com/cockroachdb/errors/errutil/utilities.go
generated
vendored
8
vendor/github.com/cockroachdb/errors/errutil/utilities.go
generated
vendored
@@ -15,6 +15,7 @@
|
|||||||
package errutil
|
package errutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/cockroachdb/errors/join"
|
||||||
"github.com/cockroachdb/errors/secondary"
|
"github.com/cockroachdb/errors/secondary"
|
||||||
"github.com/cockroachdb/errors/withstack"
|
"github.com/cockroachdb/errors/withstack"
|
||||||
"github.com/cockroachdb/redact"
|
"github.com/cockroachdb/redact"
|
||||||
@@ -158,3 +159,10 @@ func WrapWithDepthf(depth int, err error, format string, args ...interface{}) er
|
|||||||
err = withstack.WithStackDepth(err, depth+1)
|
err = withstack.WithStackDepth(err, depth+1)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JoinWithDepth constructs a Join error with the provided list of
|
||||||
|
// errors as arguments, and wraps it in a `WithStackDepth` to capture a
|
||||||
|
// stacktrace alongside.
|
||||||
|
func JoinWithDepth(depth int, errs ...error) error {
|
||||||
|
return withstack.WithStackDepth(join.Join(errs...), depth+1)
|
||||||
|
}
|
||||||
|
|||||||
16
vendor/github.com/cockroachdb/errors/errutil_api.go
generated
vendored
16
vendor/github.com/cockroachdb/errors/errutil_api.go
generated
vendored
@@ -192,3 +192,19 @@ func HandleAsAssertionFailureDepth(depth int, origErr error) error {
|
|||||||
// - it also supports recursing through causes with Cause().
|
// - it also supports recursing through causes with Cause().
|
||||||
// - if it detects an API use error, its panic object is a valid error.
|
// - if it detects an API use error, its panic object is a valid error.
|
||||||
func As(err error, target interface{}) bool { return errutil.As(err, target) }
|
func As(err error, target interface{}) bool { return errutil.As(err, target) }
|
||||||
|
|
||||||
|
// Join returns an error that wraps the given errors.
|
||||||
|
// Any nil error values are discarded.
|
||||||
|
// Join returns nil if errs contains no non-nil values.
|
||||||
|
// The error formats as the concatenation of the strings obtained
|
||||||
|
// by calling the Error method of each element of errs, with a newline
|
||||||
|
// between each string. A stack trace is also retained.
|
||||||
|
func Join(errs ...error) error {
|
||||||
|
return errutil.JoinWithDepth(1, errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinWithDepth is like Join but the depth at which the call stack is
|
||||||
|
// captured can be specified.
|
||||||
|
func JoinWithDepth(depth int, errs ...error) error {
|
||||||
|
return errutil.JoinWithDepth(depth+1, errs...)
|
||||||
|
}
|
||||||
|
|||||||
5
vendor/github.com/cockroachdb/errors/hintdetail/hintdetail.go
generated
vendored
5
vendor/github.com/cockroachdb/errors/hintdetail/hintdetail.go
generated
vendored
@@ -23,6 +23,7 @@ import (
|
|||||||
|
|
||||||
// WithHint decorates an error with a textual hint.
|
// WithHint decorates an error with a textual hint.
|
||||||
// The hint may contain PII and thus will not reportable.
|
// The hint may contain PII and thus will not reportable.
|
||||||
|
// The suggested use case for hint is to relay information to end users.
|
||||||
//
|
//
|
||||||
// Hint is shown:
|
// Hint is shown:
|
||||||
// - when formatting with `%+v`.
|
// - when formatting with `%+v`.
|
||||||
@@ -97,10 +98,12 @@ type ErrorHinter interface {
|
|||||||
|
|
||||||
// WithDetail decorates an error with a textual detail.
|
// WithDetail decorates an error with a textual detail.
|
||||||
// The detail may contain PII and thus will not reportable.
|
// The detail may contain PII and thus will not reportable.
|
||||||
|
// The suggested use case for detail is to augment errors with information
|
||||||
|
// useful for debugging.
|
||||||
//
|
//
|
||||||
// Detail is shown:
|
// Detail is shown:
|
||||||
// - when formatting with `%+v`.
|
// - when formatting with `%+v`.
|
||||||
// - with `GetAllHints()` / `FlattenHints()` below.
|
// - with `GetAllDetails()` / `FlattenDetails()` below.
|
||||||
//
|
//
|
||||||
// Note: the detail does not appear in the main error message returned
|
// Note: the detail does not appear in the main error message returned
|
||||||
// with Error(). Use GetAllDetails() or FlattenDetails() to retrieve
|
// with Error(). Use GetAllDetails() or FlattenDetails() to retrieve
|
||||||
|
|||||||
5
vendor/github.com/cockroachdb/errors/hintdetail_api.go
generated
vendored
5
vendor/github.com/cockroachdb/errors/hintdetail_api.go
generated
vendored
@@ -27,6 +27,7 @@ type ErrorDetailer = hintdetail.ErrorDetailer
|
|||||||
|
|
||||||
// WithHint decorates an error with a textual hint.
|
// WithHint decorates an error with a textual hint.
|
||||||
// The hint may contain PII and thus will not reportable.
|
// The hint may contain PII and thus will not reportable.
|
||||||
|
// The suggested use case for hint is to relay information to end users.
|
||||||
//
|
//
|
||||||
// Hint is shown:
|
// Hint is shown:
|
||||||
// - when formatting with `%+v`.
|
// - when formatting with `%+v`.
|
||||||
@@ -44,10 +45,12 @@ func WithHintf(err error, format string, args ...interface{}) error {
|
|||||||
|
|
||||||
// WithDetail decorates an error with a textual detail.
|
// WithDetail decorates an error with a textual detail.
|
||||||
// The detail may contain PII and thus will not reportable.
|
// The detail may contain PII and thus will not reportable.
|
||||||
|
// The suggested use case for detail is to augment errors with information
|
||||||
|
// useful for debugging.
|
||||||
//
|
//
|
||||||
// Detail is shown:
|
// Detail is shown:
|
||||||
// - when formatting with `%+v`.
|
// - when formatting with `%+v`.
|
||||||
// - with `GetAllHints()` / `FlattenHints()` below.
|
// - with `GetAllDetails()` / `FlattenDetails()` below.
|
||||||
//
|
//
|
||||||
// Note: the detail does not appear in the main error message returned
|
// Note: the detail does not appear in the main error message returned
|
||||||
// with Error(). Use GetAllDetails() or FlattenDetails() to retrieve
|
// with Error(). Use GetAllDetails() or FlattenDetails() to retrieve
|
||||||
|
|||||||
105
vendor/github.com/cockroachdb/errors/join/join.go
generated
vendored
Normal file
105
vendor/github.com/cockroachdb/errors/join/join.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
// Copyright 2019 The Cockroach Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
// implied. See the License for the specific language governing
|
||||||
|
// permissions and limitations under the License.
|
||||||
|
|
||||||
|
package join
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/cockroachdb/errors/errbase"
|
||||||
|
"github.com/cockroachdb/redact"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Join returns an error that wraps the given errors.
|
||||||
|
// Any nil error values are discarded.
|
||||||
|
// Join returns nil if errs contains no non-nil values.
|
||||||
|
// The error formats as the concatenation of the strings obtained
|
||||||
|
// by calling the Error method of each element of errs, with a newline
|
||||||
|
// between each string.
|
||||||
|
func Join(errs ...error) error {
|
||||||
|
n := 0
|
||||||
|
for _, err := range errs {
|
||||||
|
if err != nil {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
e := &joinError{
|
||||||
|
errs: make([]error, 0, n),
|
||||||
|
}
|
||||||
|
for _, err := range errs {
|
||||||
|
if err != nil {
|
||||||
|
e.errs = append(e.errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
type joinError struct {
|
||||||
|
errs []error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = (*joinError)(nil)
|
||||||
|
var _ fmt.Formatter = (*joinError)(nil)
|
||||||
|
var _ errbase.SafeFormatter = (*joinError)(nil)
|
||||||
|
|
||||||
|
func (e *joinError) Error() string {
|
||||||
|
return redact.Sprint(e).StripMarkers()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *joinError) Unwrap() []error {
|
||||||
|
return e.errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *joinError) SafeFormatError(p errbase.Printer) error {
|
||||||
|
for i, err := range e.errs {
|
||||||
|
if i > 0 {
|
||||||
|
p.Print("\n")
|
||||||
|
}
|
||||||
|
p.Print(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *joinError) Format(s fmt.State, verb rune) {
|
||||||
|
errbase.FormatError(e, s, verb)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
errbase.RegisterMultiCauseEncoder(
|
||||||
|
errbase.GetTypeKey(&joinError{}),
|
||||||
|
func(
|
||||||
|
ctx context.Context,
|
||||||
|
err error,
|
||||||
|
) (msg string, safeDetails []string, payload proto.Message) {
|
||||||
|
return "", nil, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
errbase.RegisterMultiCauseDecoder(
|
||||||
|
errbase.GetTypeKey(&joinError{}),
|
||||||
|
func(
|
||||||
|
ctx context.Context,
|
||||||
|
causes []error,
|
||||||
|
msgPrefix string,
|
||||||
|
safeDetails []string,
|
||||||
|
payload proto.Message,
|
||||||
|
) error {
|
||||||
|
return Join(causes...)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
77
vendor/github.com/cockroachdb/errors/markers/markers.go
generated
vendored
77
vendor/github.com/cockroachdb/errors/markers/markers.go
generated
vendored
@@ -28,20 +28,44 @@ import (
|
|||||||
// Is determines whether one of the causes of the given error or any
|
// Is determines whether one of the causes of the given error or any
|
||||||
// of its causes is equivalent to some reference error.
|
// of its causes is equivalent to some reference error.
|
||||||
//
|
//
|
||||||
|
// As in the Go standard library, an error is considered to match a
|
||||||
|
// reference error if it is equal to that target or if it implements a
|
||||||
|
// method Is(error) bool such that Is(reference) returns true.
|
||||||
|
//
|
||||||
|
// Note: the inverse is not true - making an Is(reference) method
|
||||||
|
// return false does not imply that errors.Is() also returns
|
||||||
|
// false. Errors can be equal because their network equality marker is
|
||||||
|
// the same. To force errors to appear different to Is(), use
|
||||||
|
// errors.Mark().
|
||||||
|
//
|
||||||
// Note: if any of the error types has been migrated from a previous
|
// Note: if any of the error types has been migrated from a previous
|
||||||
// package location or a different type, ensure that
|
// package location or a different type, ensure that
|
||||||
// RegisterTypeMigration() was called prior to Is().
|
// RegisterTypeMigration() was called prior to Is().
|
||||||
func Is(err, reference error) bool {
|
func Is(err, reference error) bool {
|
||||||
if reference == nil {
|
if reference == nil {
|
||||||
return err == reference
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isComparable := reflect.TypeOf(reference).Comparable()
|
||||||
|
|
||||||
// Direct reference comparison is the fastest, and most
|
// Direct reference comparison is the fastest, and most
|
||||||
// likely to be true, so do this first.
|
// likely to be true, so do this first.
|
||||||
for c := err; c != nil; c = errbase.UnwrapOnce(c) {
|
for c := err; c != nil; c = errbase.UnwrapOnce(c) {
|
||||||
if c == reference {
|
if isComparable && c == reference {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// Compatibility with std go errors: if the error object itself
|
||||||
|
// implements Is(), try to use that.
|
||||||
|
if tryDelegateToIsMethod(c, reference) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively try multi-error causes, if applicable.
|
||||||
|
for _, me := range errbase.UnwrapMulti(c) {
|
||||||
|
if Is(me, reference) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -51,8 +75,8 @@ func Is(err, reference error) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not directly equal. Try harder, using error marks. We don't this
|
// Not directly equal. Try harder, using error marks. We don't do
|
||||||
// during the loop above as it may be more expensive.
|
// this during the loop above as it may be more expensive.
|
||||||
//
|
//
|
||||||
// Note: there is a more effective recursive algorithm that ensures
|
// Note: there is a more effective recursive algorithm that ensures
|
||||||
// that any pair of string only gets compared once. Should the
|
// that any pair of string only gets compared once. Should the
|
||||||
@@ -67,6 +91,13 @@ func Is(err, reference error) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tryDelegateToIsMethod(err, reference error) bool {
|
||||||
|
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(reference) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// HasType returns true iff err contains an error whose concrete type
|
// HasType returns true iff err contains an error whose concrete type
|
||||||
// matches that of referenceType.
|
// matches that of referenceType.
|
||||||
func HasType(err error, referenceType error) bool {
|
func HasType(err error, referenceType error) bool {
|
||||||
@@ -119,21 +150,12 @@ func If(err error, pred func(err error) (interface{}, bool)) (interface{}, bool)
|
|||||||
// package location or a different type, ensure that
|
// package location or a different type, ensure that
|
||||||
// RegisterTypeMigration() was called prior to IsAny().
|
// RegisterTypeMigration() was called prior to IsAny().
|
||||||
func IsAny(err error, references ...error) bool {
|
func IsAny(err error, references ...error) bool {
|
||||||
// First try using direct reference comparison.
|
if err == nil {
|
||||||
for c := err; ; c = errbase.UnwrapOnce(c) {
|
|
||||||
for _, refErr := range references {
|
for _, refErr := range references {
|
||||||
if c == refErr {
|
if refErr == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c == nil {
|
|
||||||
// This special case is to support a comparison to a nil
|
|
||||||
// reference.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
// The mark-based comparison below will never match anything if
|
// The mark-based comparison below will never match anything if
|
||||||
// the error is nil, so don't bother with computing the marks in
|
// the error is nil, so don't bother with computing the marks in
|
||||||
// that case. This avoids the computational expense of computing
|
// that case. This avoids the computational expense of computing
|
||||||
@@ -141,6 +163,31 @@ func IsAny(err error, references ...error) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First try using direct reference comparison.
|
||||||
|
for c := err; c != nil; c = errbase.UnwrapOnce(c) {
|
||||||
|
for _, refErr := range references {
|
||||||
|
if refErr == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
isComparable := reflect.TypeOf(refErr).Comparable()
|
||||||
|
if isComparable && c == refErr {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Compatibility with std go errors: if the error object itself
|
||||||
|
// implements Is(), try to use that.
|
||||||
|
if tryDelegateToIsMethod(c, refErr) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively try multi-error causes, if applicable.
|
||||||
|
for _, me := range errbase.UnwrapMulti(c) {
|
||||||
|
if IsAny(me, references...) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Try harder with marks.
|
// Try harder with marks.
|
||||||
// Note: there is a more effective recursive algorithm that ensures
|
// Note: there is a more effective recursive algorithm that ensures
|
||||||
// that any pair of string only gets compared once. Should this
|
// that any pair of string only gets compared once. Should this
|
||||||
|
|||||||
10
vendor/github.com/cockroachdb/errors/markers_api.go
generated
vendored
10
vendor/github.com/cockroachdb/errors/markers_api.go
generated
vendored
@@ -19,6 +19,16 @@ import "github.com/cockroachdb/errors/markers"
|
|||||||
// Is determines whether one of the causes of the given error or any
|
// Is determines whether one of the causes of the given error or any
|
||||||
// of its causes is equivalent to some reference error.
|
// of its causes is equivalent to some reference error.
|
||||||
//
|
//
|
||||||
|
// As in the Go standard library, an error is considered to match a
|
||||||
|
// reference error if it is equal to that target or if it implements a
|
||||||
|
// method Is(error) bool such that Is(reference) returns true.
|
||||||
|
//
|
||||||
|
// Note: the inverse is not true - making an Is(reference) method
|
||||||
|
// return false does not imply that errors.Is() also returns
|
||||||
|
// false. Errors can be equal because their network equality marker is
|
||||||
|
// the same. To force errors to appear different to Is(), use
|
||||||
|
// errors.Mark().
|
||||||
|
//
|
||||||
// Note: if any of the error types has been migrated from a previous
|
// Note: if any of the error types has been migrated from a previous
|
||||||
// package location or a different type, ensure that
|
// package location or a different type, ensure that
|
||||||
// RegisterTypeMigration() was called prior to Is().
|
// RegisterTypeMigration() was called prior to Is().
|
||||||
|
|||||||
105
vendor/github.com/cockroachdb/errors/report/report.go
generated
vendored
105
vendor/github.com/cockroachdb/errors/report/report.go
generated
vendored
@@ -15,7 +15,6 @@
|
|||||||
package report
|
package report
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -23,7 +22,7 @@ import (
|
|||||||
"github.com/cockroachdb/errors/errbase"
|
"github.com/cockroachdb/errors/errbase"
|
||||||
"github.com/cockroachdb/errors/withstack"
|
"github.com/cockroachdb/errors/withstack"
|
||||||
"github.com/cockroachdb/redact"
|
"github.com/cockroachdb/redact"
|
||||||
"github.com/cockroachdb/sentry-go"
|
sentry "github.com/getsentry/sentry-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BuildSentryReport builds the components of a sentry report. This
|
// BuildSentryReport builds the components of a sentry report. This
|
||||||
@@ -35,36 +34,39 @@ import (
|
|||||||
//
|
//
|
||||||
// A Sentry report is displayed visually in the Sentry UI as follows:
|
// A Sentry report is displayed visually in the Sentry UI as follows:
|
||||||
//
|
//
|
||||||
////////////////
|
// //////////////
|
||||||
// Title: (1) some prefix in bold (2) one line for a stack trace
|
// Title: (1) some prefix in bold (2) one line for a stack trace
|
||||||
// (3) a single-line subtitle
|
// (3) a single-line subtitle
|
||||||
//
|
//
|
||||||
// (4) the tags, as a tag soup (concatenated in a single paragrah,
|
// (4) the tags, as a tag soup (concatenated in a single paragraph,
|
||||||
// unsorted)
|
// unsorted)
|
||||||
//
|
//
|
||||||
// (5) a "message"
|
// (5) a "message"
|
||||||
//
|
//
|
||||||
// (6) zero or more "exceptions", each composed of:
|
// (6) zero or more "exceptions", each composed of:
|
||||||
// (7) a bold title
|
//
|
||||||
// (8) some freeform text
|
// (7) a bold title
|
||||||
// (9) a stack trace
|
// (8) some freeform text
|
||||||
|
// (9) a stack trace
|
||||||
//
|
//
|
||||||
// (10) metadata fields: environment, arch, etc
|
// (10) metadata fields: environment, arch, etc
|
||||||
//
|
//
|
||||||
// (11) "Additional data" fields
|
// (11) "Additional data" fields
|
||||||
//
|
//
|
||||||
// (12) SDK version
|
// (12) SDK version
|
||||||
/////////////////
|
// ///////////////
|
||||||
//
|
//
|
||||||
// These visual items map to the Sentry Event object as follows:
|
// These visual items map to the Sentry Event object as follows:
|
||||||
//
|
//
|
||||||
// (1) the Type field of the 1st Exception object, if any
|
// (1) the Type field of the 1st Exception object, if any
|
||||||
// otherwise the Message field
|
//
|
||||||
|
// otherwise the Message field
|
||||||
|
//
|
||||||
// (2) the topmost entry from the Stacktrace field of the 1st Exception object, if any
|
// (2) the topmost entry from the Stacktrace field of the 1st Exception object, if any
|
||||||
// (3) the Value field of the 1st Exception object, if any, unwrapped as a single line
|
// (3) the Value field of the 1st Exception object, if any, unwrapped as a single line
|
||||||
// (4) the Tags field
|
// (4) the Tags field
|
||||||
// (5) the Message field
|
// (5) the Message field
|
||||||
// (7) the Type field (same as (1) for 1st execption)
|
// (7) the Type field (same as (1) for 1st exception)
|
||||||
// (8) the Value field (same as (3) for 1st exception)
|
// (8) the Value field (same as (3) for 1st exception)
|
||||||
// (9) the Stacktrace field (input to (2) on 1st exception)
|
// (9) the Stacktrace field (input to (2) on 1st exception)
|
||||||
// (10) the other fields on the Event object
|
// (10) the other fields on the Event object
|
||||||
@@ -76,13 +78,15 @@ import (
|
|||||||
// Given this mapping, an error object is decomposed as follows:
|
// Given this mapping, an error object is decomposed as follows:
|
||||||
//
|
//
|
||||||
// (1)/(7): <filename>:<lineno> (<functionname>)
|
// (1)/(7): <filename>:<lineno> (<functionname>)
|
||||||
// (3)/(8): <error type>: <first safe detail line, if any>
|
// (3)/(8): first line of verbose error printout
|
||||||
// (4): not populated in this function, caller is to manage this
|
// (4): not populated in this function, caller is to manage this
|
||||||
// (5): detailed structure of the entire error object, with references to "additional data"
|
// (5): detailed structure of the entire error object, with references to
|
||||||
// and additional "exception" objects
|
//
|
||||||
|
// additional "exception" objects
|
||||||
|
//
|
||||||
// (9): generated from innermost stack trace
|
// (9): generated from innermost stack trace
|
||||||
// (6): every exception object after the 1st reports additional stack trace contexts
|
// (6): every exception object after the 1st reports additional stack trace contexts
|
||||||
// (11): "additional data" populated from safe detail payloads
|
// (11): the detailed error types and their error mark.
|
||||||
//
|
//
|
||||||
// If there is no stack trace in the error, a synthetic Exception
|
// If there is no stack trace in the error, a synthetic Exception
|
||||||
// object is still produced to provide visual detail in the Sentry UI.
|
// object is still produced to provide visual detail in the Sentry UI.
|
||||||
@@ -93,7 +97,6 @@ import (
|
|||||||
// is included in the Sentry report. This does not affect error types
|
// is included in the Sentry report. This does not affect error types
|
||||||
// provided by the library, but could impact error types defined by
|
// provided by the library, but could impact error types defined by
|
||||||
// 3rd parties. This limitation may be lifted in a later version.
|
// 3rd parties. This limitation may be lifted in a later version.
|
||||||
//
|
|
||||||
func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]interface{}) {
|
func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]interface{}) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// No error: do nothing.
|
// No error: do nothing.
|
||||||
@@ -103,13 +106,13 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
|||||||
// First step is to collect the details.
|
// First step is to collect the details.
|
||||||
var stacks []*withstack.ReportableStackTrace
|
var stacks []*withstack.ReportableStackTrace
|
||||||
var details []errbase.SafeDetailPayload
|
var details []errbase.SafeDetailPayload
|
||||||
for c := err; c != nil; c = errbase.UnwrapOnce(c) {
|
visitAllMulti(err, func(c error) {
|
||||||
st := withstack.GetReportableStackTrace(c)
|
st := withstack.GetReportableStackTrace(c)
|
||||||
stacks = append(stacks, st)
|
stacks = append(stacks, st)
|
||||||
|
|
||||||
sd := errbase.GetSafeDetails(c)
|
sd := errbase.GetSafeDetails(c)
|
||||||
details = append(details, sd)
|
details = append(details, sd)
|
||||||
}
|
})
|
||||||
module := string(domains.GetDomain(err))
|
module := string(domains.GetDomain(err))
|
||||||
|
|
||||||
// firstDetailLine is the first detail string encountered.
|
// firstDetailLine is the first detail string encountered.
|
||||||
@@ -119,17 +122,22 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
|||||||
var firstDetailLine string
|
var firstDetailLine string
|
||||||
|
|
||||||
// longMsgBuf will become the Message field, which contains the full
|
// longMsgBuf will become the Message field, which contains the full
|
||||||
// structure of the error with cross-references to "Exception" and
|
// structure of the error.
|
||||||
// "Additional data" fields.
|
|
||||||
var longMsgBuf strings.Builder
|
var longMsgBuf strings.Builder
|
||||||
redactedErrStr := redact.Sprint(err).Redact()
|
if f, l, _, ok := withstack.GetOneLineSource(err); ok {
|
||||||
if redactedErrStr != redactedMarker {
|
fmt.Fprintf(&longMsgBuf, "%s:%d: ", f, l)
|
||||||
if f, l, _, ok := withstack.GetOneLineSource(err); ok {
|
|
||||||
fmt.Fprintf(&longMsgBuf, "%s:%d: ", f, l)
|
|
||||||
}
|
|
||||||
firstDetailLine = redactedErrStr.StripMarkers()
|
|
||||||
fmt.Fprintf(&longMsgBuf, "%v\n--\n", firstDetailLine)
|
|
||||||
}
|
}
|
||||||
|
// Include the verbose error printout, with sensitive bits redacted out.
|
||||||
|
verboseErr := redact.Sprintf("%+v", err).Redact().StripMarkers()
|
||||||
|
if verboseErr != redactedMarker {
|
||||||
|
idx := strings.IndexByte(verboseErr, '\n')
|
||||||
|
if idx == -1 {
|
||||||
|
firstDetailLine = verboseErr
|
||||||
|
} else {
|
||||||
|
firstDetailLine = verboseErr[:idx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprint(&longMsgBuf, verboseErr)
|
||||||
|
|
||||||
// sep is used to separate the entries in the longMsgBuf / Message
|
// sep is used to separate the entries in the longMsgBuf / Message
|
||||||
// payload.
|
// payload.
|
||||||
@@ -159,6 +167,7 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
|||||||
// layer. We iterate in this order because we want to describe the
|
// layer. We iterate in this order because we want to describe the
|
||||||
// error from innermost to outermost layer in longMsgBuf and
|
// error from innermost to outermost layer in longMsgBuf and
|
||||||
// typesBuf.
|
// typesBuf.
|
||||||
|
longMsgBuf.WriteString("\n-- report composition:\n")
|
||||||
for i := len(details) - 1; i >= 0; i-- {
|
for i := len(details) - 1; i >= 0; i-- {
|
||||||
// Collect the type name for this layer of error wrapping, towards
|
// Collect the type name for this layer of error wrapping, towards
|
||||||
// the "error types" additional data field.
|
// the "error types" additional data field.
|
||||||
@@ -204,10 +213,6 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
|||||||
// Now decide what kind of payload we want to add to the Event
|
// Now decide what kind of payload we want to add to the Event
|
||||||
// object.
|
// object.
|
||||||
|
|
||||||
// genExtra will remember whether we are adding an
|
|
||||||
// additional payload or not.
|
|
||||||
var genExtra bool
|
|
||||||
|
|
||||||
// Is there a stack trace?
|
// Is there a stack trace?
|
||||||
if st := stacks[i]; st != nil {
|
if st := stacks[i]; st != nil {
|
||||||
var excType strings.Builder
|
var excType strings.Builder
|
||||||
@@ -245,17 +250,21 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
|||||||
exceptions = append(exceptions, exc)
|
exceptions = append(exceptions, exc)
|
||||||
} else {
|
} else {
|
||||||
// No stack trace.
|
// No stack trace.
|
||||||
// Are there safe details? If so, print them.
|
// Are there safe details? If so, print the first safe detail
|
||||||
|
// string (we're assuming that all the important bits will
|
||||||
|
// also be included in the verbose printout, so there's no
|
||||||
|
// need to dig out more safe strings here.)
|
||||||
|
//
|
||||||
|
// TODO(knz): the SafeDetails API is not really meant for Sentry
|
||||||
|
// reporting. Once we have more experience to prove that the
|
||||||
|
// verbose printout is sufficient, we can remove the SafeDetails
|
||||||
|
// from sentry reports.
|
||||||
//
|
//
|
||||||
// Note: we only print the details if no stack trace was found
|
// Note: we only print the details if no stack trace was found
|
||||||
// at that level. This is because stack trace annotations also
|
// at that level. This is because stack trace annotations also
|
||||||
// produce the stack trace as safe detail string.
|
// produce the stack trace as safe detail string.
|
||||||
genExtra = len(details[i].SafeDetails) > 1
|
|
||||||
if len(details[i].SafeDetails) > 0 {
|
if len(details[i].SafeDetails) > 0 {
|
||||||
d := details[i].SafeDetails[0]
|
d := details[i].SafeDetails[0]
|
||||||
if d != "" {
|
|
||||||
genExtra = true
|
|
||||||
}
|
|
||||||
if j := strings.IndexByte(d, '\n'); j >= 0 {
|
if j := strings.IndexByte(d, '\n'); j >= 0 {
|
||||||
d = d[:j]
|
d = d[:j]
|
||||||
}
|
}
|
||||||
@@ -269,18 +278,6 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are we generating another extra for the safe detail strings?
|
|
||||||
if genExtra {
|
|
||||||
stKey := fmt.Sprintf("%d: details", extraNum)
|
|
||||||
var extraStr bytes.Buffer
|
|
||||||
for _, d := range details[i].SafeDetails {
|
|
||||||
fmt.Fprintln(&extraStr, strings.ReplaceAll(d, "\n", "\n "))
|
|
||||||
}
|
|
||||||
extras[stKey] = extraStr.String()
|
|
||||||
fmt.Fprintf(&longMsgBuf, " (%d)", extraNum)
|
|
||||||
extraNum++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if extraNum > 1 {
|
if extraNum > 1 {
|
||||||
@@ -299,7 +296,7 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
|||||||
event.Message = longMsgBuf.String()
|
event.Message = longMsgBuf.String()
|
||||||
event.Exception = exceptions
|
event.Exception = exceptions
|
||||||
|
|
||||||
// If there is no exception payload, synthetize one.
|
// If there is no exception payload, synthesize one.
|
||||||
if len(event.Exception) == 0 {
|
if len(event.Exception) == 0 {
|
||||||
// We know we don't have a stack trace to extract line/function
|
// We know we don't have a stack trace to extract line/function
|
||||||
// info from (if we had, we'd have an Exception payload at that
|
// info from (if we had, we'd have an Exception payload at that
|
||||||
@@ -354,7 +351,7 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
|||||||
return event, extras
|
return event, extras
|
||||||
}
|
}
|
||||||
|
|
||||||
var redactedMarker = redact.RedactableString(redact.RedactedMarker())
|
var redactedMarker = redact.RedactableString(redact.RedactedMarker()).StripMarkers()
|
||||||
|
|
||||||
// ReportError reports the given error to Sentry. The caller is responsible for
|
// ReportError reports the given error to Sentry. The caller is responsible for
|
||||||
// checking whether telemetry is enabled, and calling the sentry.Flush()
|
// checking whether telemetry is enabled, and calling the sentry.Flush()
|
||||||
@@ -404,3 +401,13 @@ func reverseExceptionOrder(ex []sentry.Exception) {
|
|||||||
ex[i], ex[len(ex)-i-1] = ex[len(ex)-i-1], ex[i]
|
ex[i], ex[len(ex)-i-1] = ex[len(ex)-i-1], ex[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func visitAllMulti(err error, f func(error)) {
|
||||||
|
f(err)
|
||||||
|
if e := errbase.UnwrapOnce(err); e != nil {
|
||||||
|
visitAllMulti(e, f)
|
||||||
|
}
|
||||||
|
for _, e := range errbase.UnwrapMulti(err) {
|
||||||
|
visitAllMulti(e, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
2
vendor/github.com/cockroachdb/errors/report/reportables.go
generated
vendored
2
vendor/github.com/cockroachdb/errors/report/reportables.go
generated
vendored
@@ -18,7 +18,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/cockroachdb/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StackTrace is an object suitable for inclusion in errors that can
|
// StackTrace is an object suitable for inclusion in errors that can
|
||||||
|
|||||||
6
vendor/github.com/cockroachdb/errors/report_api.go
generated
vendored
6
vendor/github.com/cockroachdb/errors/report_api.go
generated
vendored
@@ -16,7 +16,7 @@ package errors
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/cockroachdb/errors/report"
|
"github.com/cockroachdb/errors/report"
|
||||||
"github.com/cockroachdb/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BuildSentryReport builds the components of a sentry report. This
|
// BuildSentryReport builds the components of a sentry report. This
|
||||||
@@ -32,7 +32,7 @@ import (
|
|||||||
// Title: (1) some prefix in bold (2) one line for a stack trace
|
// Title: (1) some prefix in bold (2) one line for a stack trace
|
||||||
// (3) a single-line subtitle
|
// (3) a single-line subtitle
|
||||||
//
|
//
|
||||||
// (4) the tags, as a tag soup (concatenated in a single paragrah,
|
// (4) the tags, as a tag soup (concatenated in a single paragraph,
|
||||||
// unsorted)
|
// unsorted)
|
||||||
//
|
//
|
||||||
// (5) a "message"
|
// (5) a "message"
|
||||||
@@ -57,7 +57,7 @@ import (
|
|||||||
// (3) the Value field of the 1st Exception object, if any, unwrapped as a single line
|
// (3) the Value field of the 1st Exception object, if any, unwrapped as a single line
|
||||||
// (4) the Tags field
|
// (4) the Tags field
|
||||||
// (5) the Message field
|
// (5) the Message field
|
||||||
// (7) the Type field (same as (1) for 1st execption)
|
// (7) the Type field (same as (1) for 1st exception)
|
||||||
// (8) the Value field (same as (3) for 1st exception)
|
// (8) the Value field (same as (3) for 1st exception)
|
||||||
// (9) the Stacktrace field (input to (2) on 1st exception)
|
// (9) the Stacktrace field (input to (2) on 1st exception)
|
||||||
// (10) the other fields on the Event object
|
// (10) the other fields on the Event object
|
||||||
|
|||||||
2
vendor/github.com/cockroachdb/errors/withstack/reportable.go
generated
vendored
2
vendor/github.com/cockroachdb/errors/withstack/reportable.go
generated
vendored
@@ -23,7 +23,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors/errbase"
|
"github.com/cockroachdb/errors/errbase"
|
||||||
"github.com/cockroachdb/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
pkgErr "github.com/pkg/errors"
|
pkgErr "github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
.vscode/
|
# If you prefer the allow list template instead of the deny list, see community template:
|
||||||
.idea/
|
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||||
.DS_Store
|
#
|
||||||
|
|
||||||
coverage.txt
|
|
||||||
|
|
||||||
# Binaries for programs and plugins
|
# Binaries for programs and plugins
|
||||||
*.exe
|
*.exe
|
||||||
*.exe~
|
*.exe~
|
||||||
@@ -14,12 +11,15 @@ coverage.txt
|
|||||||
# Test binary, built with `go test -c`
|
# Test binary, built with `go test -c`
|
||||||
*.test
|
*.test
|
||||||
|
|
||||||
# Test example output
|
|
||||||
example.logV2.hlog
|
|
||||||
|
|
||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
*.out
|
*.out
|
||||||
|
|
||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
# vendor/
|
# vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
go.work.sum
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
|
||||||
201
vendor/github.com/cockroachdb/fifo/LICENSE
generated
vendored
Normal file
201
vendor/github.com/cockroachdb/fifo/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
21
vendor/github.com/cockroachdb/fifo/invariants_off.go
generated
vendored
Normal file
21
vendor/github.com/cockroachdb/fifo/invariants_off.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2024 The Cockroach Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
// implied. See the License for the specific language governing
|
||||||
|
// permissions and limitations under the License.
|
||||||
|
|
||||||
|
//go:build !fifo_invariants
|
||||||
|
|
||||||
|
package fifo
|
||||||
|
|
||||||
|
// invariants is false if we were not built with the "fifo_invariants" build
|
||||||
|
// tag.
|
||||||
|
const invariants = false
|
||||||
20
vendor/github.com/cockroachdb/fifo/invariants_on.go
generated
vendored
Normal file
20
vendor/github.com/cockroachdb/fifo/invariants_on.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2024 The Cockroach Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
// implied. See the License for the specific language governing
|
||||||
|
// permissions and limitations under the License.
|
||||||
|
|
||||||
|
//go:build fifo_invariants
|
||||||
|
|
||||||
|
package fifo
|
||||||
|
|
||||||
|
// invariants is true if we were built with the "fifo_invariants" build tag.
|
||||||
|
const invariants = true
|
||||||
162
vendor/github.com/cockroachdb/fifo/queue.go
generated
vendored
Normal file
162
vendor/github.com/cockroachdb/fifo/queue.go
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
// Copyright 2024 The Cockroach Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
// implied. See the License for the specific language governing
|
||||||
|
// permissions and limitations under the License.
|
||||||
|
|
||||||
|
package fifo
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
// Queue implements an allocation efficient FIFO queue. It is not safe for
|
||||||
|
// concurrent access.
|
||||||
|
//
|
||||||
|
// Note that the queue provides pointer access to the internal storage (via
|
||||||
|
// PeekFront and PushBack) so it must be used with care. These pointers must not
|
||||||
|
// be used once the respective element is popped out of the queue.
|
||||||
|
//
|
||||||
|
// -- Implementation --
|
||||||
|
//
|
||||||
|
// The queue is implemented as a linked list of nodes, where each node is a
|
||||||
|
// small ring buffer. The nodes are allocated using a sync.Pool (a single pool
|
||||||
|
// is created for any given type and is used for all queues of that type).
|
||||||
|
type Queue[T any] struct {
|
||||||
|
len int
|
||||||
|
head, tail *queueNode[T]
|
||||||
|
|
||||||
|
pool *QueueBackingPool[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeQueue constructs a new Queue.
|
||||||
|
//
|
||||||
|
// The pool should be a singleton object initialized with MakeQueueBackingPool.
|
||||||
|
// A single pool can and should be used by all queues of that type.
|
||||||
|
func MakeQueue[T any](pool *QueueBackingPool[T]) Queue[T] {
|
||||||
|
return Queue[T]{
|
||||||
|
pool: pool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the current length of the queue.
|
||||||
|
func (q *Queue[T]) Len() int {
|
||||||
|
return q.len
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBack adds t to the end of the queue.
|
||||||
|
//
|
||||||
|
// The returned pointer can be used to modify the element while it is in the
|
||||||
|
// queue; it is valid until the element is removed from the queue.
|
||||||
|
func (q *Queue[T]) PushBack(t T) *T {
|
||||||
|
if q.head == nil {
|
||||||
|
q.head = q.pool.get()
|
||||||
|
q.tail = q.head
|
||||||
|
} else if q.tail.IsFull() {
|
||||||
|
newTail := q.pool.get()
|
||||||
|
q.tail.next = newTail
|
||||||
|
q.tail = newTail
|
||||||
|
}
|
||||||
|
q.len++
|
||||||
|
return q.tail.PushBack(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeekFront returns the current head of the queue, or nil if the queue is
|
||||||
|
// empty.
|
||||||
|
//
|
||||||
|
// The result is only valid until the next call to PopFront.
|
||||||
|
func (q *Queue[T]) PeekFront() *T {
|
||||||
|
if q.len == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return q.head.PeekFront()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PopFront removes the current head of the queue.
|
||||||
|
//
|
||||||
|
// It is illegal to call PopFront on an empty queue.
|
||||||
|
func (q *Queue[T]) PopFront() {
|
||||||
|
q.head.PopFront()
|
||||||
|
// If this is the only node, we don't want to release it; otherwise we would
|
||||||
|
// allocate/free a node every time we transition between the queue being empty
|
||||||
|
// and non-empty.
|
||||||
|
if q.head.len == 0 && q.head.next != nil {
|
||||||
|
oldHead := q.head
|
||||||
|
q.head = oldHead.next
|
||||||
|
q.pool.put(oldHead)
|
||||||
|
}
|
||||||
|
q.len--
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueueBackingPool is a sync.Pool that used to allocate internal nodes
|
||||||
|
// for Queue[T].
|
||||||
|
type QueueBackingPool[T any] struct {
|
||||||
|
pool sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeQueueBackingPool makes a queue backing pool. It is intented to be used to
|
||||||
|
// initialize a singleton (global) variable. A single pool can and should be
|
||||||
|
// used by all queues of that type.
|
||||||
|
func MakeQueueBackingPool[T any]() QueueBackingPool[T] {
|
||||||
|
return QueueBackingPool[T]{
|
||||||
|
pool: sync.Pool{
|
||||||
|
New: func() interface{} { return &queueNode[T]{} },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp *QueueBackingPool[T]) get() *queueNode[T] {
|
||||||
|
return qp.pool.Get().(*queueNode[T])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp *QueueBackingPool[T]) put(n *queueNode[T]) {
|
||||||
|
*n = queueNode[T]{}
|
||||||
|
qp.pool.Put(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We batch the allocation of this many queue objects. The value was chosen
|
||||||
|
// without experimentation - it provides a reasonable amount of amortization
|
||||||
|
// without a very large increase in memory overhead if T is large.
|
||||||
|
const queueNodeSize = 8
|
||||||
|
|
||||||
|
type queueNode[T any] struct {
|
||||||
|
buf [queueNodeSize]T
|
||||||
|
head, len int32
|
||||||
|
next *queueNode[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qn *queueNode[T]) IsFull() bool {
|
||||||
|
return qn.len == queueNodeSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qn *queueNode[T]) PushBack(t T) *T {
|
||||||
|
if invariants && qn.len >= queueNodeSize {
|
||||||
|
panic("cannot push back into a full node")
|
||||||
|
}
|
||||||
|
i := (qn.head + qn.len) % queueNodeSize
|
||||||
|
qn.buf[i] = t
|
||||||
|
qn.len++
|
||||||
|
return &qn.buf[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qn *queueNode[T]) PeekFront() *T {
|
||||||
|
return &qn.buf[qn.head]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qn *queueNode[T]) PopFront() T {
|
||||||
|
if invariants && qn.len == 0 {
|
||||||
|
panic("cannot pop from empty queue")
|
||||||
|
}
|
||||||
|
t := qn.buf[qn.head]
|
||||||
|
var zero T
|
||||||
|
qn.buf[qn.head] = zero
|
||||||
|
qn.head = (qn.head + 1) % queueNodeSize
|
||||||
|
qn.len--
|
||||||
|
return t
|
||||||
|
}
|
||||||
239
vendor/github.com/cockroachdb/fifo/semaphore.go
generated
vendored
Normal file
239
vendor/github.com/cockroachdb/fifo/semaphore.go
generated
vendored
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
// Copyright 2024 The Cockroach Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
// implied. See the License for the specific language governing
|
||||||
|
// permissions and limitations under the License.
|
||||||
|
|
||||||
|
package fifo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Semaphore implements a weighted, dynamically reconfigurable semaphore which
|
||||||
|
// respects context cancellation.
|
||||||
|
//
|
||||||
|
// The semaphore implements a FIFO policy, where Acquire requests are satisfied
|
||||||
|
// in order. This policy provides fairness and prevents starvation but is
|
||||||
|
// susceptible to head-of-line blocking, where a large request that can't be
|
||||||
|
// satisfied blocks many other small requests that could be.
|
||||||
|
type Semaphore struct {
|
||||||
|
mu struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
capacity int64
|
||||||
|
// outstanding can exceed capacity if the capacity is dynamically decreased.
|
||||||
|
outstanding int64
|
||||||
|
|
||||||
|
waiters Queue[semaWaiter]
|
||||||
|
|
||||||
|
// numCanceled is the number of waiters in the waiters queue which have been
|
||||||
|
// canceled. It is used to determine the current number of active waiters in
|
||||||
|
// the queue which is waiters.Len() minus this value.
|
||||||
|
numCanceled int
|
||||||
|
|
||||||
|
// numHadToWait accumulates the total number of Acquire requests which had
|
||||||
|
// to wait because the semaphore was exhausted.
|
||||||
|
numHadToWait int64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSemaphore creates a new semaphore with the given capacity.
|
||||||
|
func NewSemaphore(capacity int64) *Semaphore {
|
||||||
|
if capacity <= 0 {
|
||||||
|
panic("invalid capacity")
|
||||||
|
}
|
||||||
|
s := &Semaphore{}
|
||||||
|
s.mu.capacity = capacity
|
||||||
|
s.mu.waiters = MakeQueue[semaWaiter](&semaQueuePool)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
var semaQueuePool = MakeQueueBackingPool[semaWaiter]()
|
||||||
|
|
||||||
|
var ErrRequestExceedsCapacity = errors.New("request exceeds semaphore capacity")
|
||||||
|
|
||||||
|
// TryAcquire attempts to acquire n units from the semaphore without waiting. On
|
||||||
|
// success, returns true and the caller must later Release the units.
|
||||||
|
func (s *Semaphore) TryAcquire(n int64) bool {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
if s.numWaitersLocked() == 0 && s.mu.outstanding+n <= s.mu.capacity {
|
||||||
|
s.mu.outstanding += n
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acquire n units from the semaphore, waiting if necessary.
|
||||||
|
//
|
||||||
|
// If the context is canceled while we are waiting, returns the context error.
|
||||||
|
// If n exceeds the current capacity, returns ErrRequestExceedsCapacity.
|
||||||
|
// On success, the caller must later Release the units.
|
||||||
|
func (s *Semaphore) Acquire(ctx context.Context, n int64) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
|
||||||
|
// Fast path.
|
||||||
|
if s.numWaitersLocked() == 0 && s.mu.outstanding+n <= s.mu.capacity {
|
||||||
|
s.mu.outstanding += n
|
||||||
|
s.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if n > s.mu.capacity {
|
||||||
|
s.mu.Unlock()
|
||||||
|
return ErrRequestExceedsCapacity
|
||||||
|
}
|
||||||
|
|
||||||
|
c := chanSyncPool.Get().(chan error)
|
||||||
|
defer chanSyncPool.Put(c)
|
||||||
|
w := s.mu.waiters.PushBack(semaWaiter{n: n, c: c})
|
||||||
|
s.mu.numHadToWait++
|
||||||
|
s.mu.Unlock()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
// We need to check if we raced with a channel notify (which happens under
|
||||||
|
// the lock).
|
||||||
|
select {
|
||||||
|
case err := <-c:
|
||||||
|
// We actually fulfilled or failed the request.
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
// Mark the request as canceled.
|
||||||
|
w.c = nil
|
||||||
|
s.mu.numCanceled++
|
||||||
|
// If we are the head of the queue, we may be able to fulfill other waiters.
|
||||||
|
s.processWaitersLocked()
|
||||||
|
return ctx.Err()
|
||||||
|
|
||||||
|
case err := <-c:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release n units back. These must be units that were acquired by a previous
|
||||||
|
// Acquire call. It is legal to split up or coalesce units when releasing.
|
||||||
|
func (s *Semaphore) Release(n int64) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
s.mu.outstanding -= n
|
||||||
|
if s.mu.outstanding < 0 {
|
||||||
|
panic("releasing more than was acquired")
|
||||||
|
}
|
||||||
|
s.processWaitersLocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateCapacity changes the capacity of the semaphore. If the new capacity is
|
||||||
|
// smaller, the already outstanding acquisitions might exceed the new capacity
|
||||||
|
// until they are released.
|
||||||
|
//
|
||||||
|
// If there are Acquire calls that are waiting which are requesting more than
|
||||||
|
// the new capacity, they will error out with ErrRequestExceedsCapacity.
|
||||||
|
func (s *Semaphore) UpdateCapacity(capacity int64) {
|
||||||
|
if capacity <= 0 {
|
||||||
|
panic("invalid capacity")
|
||||||
|
}
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
s.mu.capacity = capacity
|
||||||
|
s.processWaitersLocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats returns the current state of the semaphore.
|
||||||
|
func (s *Semaphore) Stats() SemaphoreStats {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return SemaphoreStats{
|
||||||
|
Capacity: s.mu.capacity,
|
||||||
|
Outstanding: s.mu.outstanding,
|
||||||
|
NumHadToWait: s.mu.numHadToWait,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SemaphoreStats contains information about the current state of the semaphore.
|
||||||
|
type SemaphoreStats struct {
|
||||||
|
// Capacity is the current capacity of the semaphore.
|
||||||
|
Capacity int64
|
||||||
|
// Outstanding is the number of units that have been acquired. Note that this
|
||||||
|
// can exceed Capacity if the capacity was recently decreased.
|
||||||
|
Outstanding int64
|
||||||
|
// NumHadToWait is the total number of Acquire calls (since the semaphore was
|
||||||
|
// created) that had to wait because the semaphore was exhausted. Useful for
|
||||||
|
// cumulative metrics.
|
||||||
|
NumHadToWait int64
|
||||||
|
|
||||||
|
// TODO(radu): consider keeping track of the total amount of time the
|
||||||
|
// semaphore was exhausted (i.e. there were waiters queued).
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss SemaphoreStats) String() string {
|
||||||
|
return fmt.Sprintf("capacity: %d, outstanding: %d, num-had-to-wait: %d",
|
||||||
|
ss.Capacity, ss.Outstanding, ss.NumHadToWait)
|
||||||
|
}
|
||||||
|
|
||||||
|
type semaWaiter struct {
|
||||||
|
// n is the amount that the waiter is trying to acquire.
|
||||||
|
n int64
|
||||||
|
// c is the channel on which Acquire is blocked. If the request is canceled,
|
||||||
|
// it is set to nil.
|
||||||
|
c chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
// numWaitersLocked returns how many requests (that have not been canceled) are
|
||||||
|
// waiting in the queue.
|
||||||
|
func (s *Semaphore) numWaitersLocked() int {
|
||||||
|
return s.mu.waiters.Len() - s.mu.numCanceled
|
||||||
|
}
|
||||||
|
|
||||||
|
// processWaitersLocked processes and notifies as many waiters from the head of
|
||||||
|
// the queue as possible.
|
||||||
|
func (s *Semaphore) processWaitersLocked() {
|
||||||
|
for ; s.mu.waiters.Len() > 0; s.mu.waiters.PopFront() {
|
||||||
|
switch w := s.mu.waiters.PeekFront(); {
|
||||||
|
case w.c == nil:
|
||||||
|
// Request was canceled, we can just clean it up.
|
||||||
|
s.mu.numCanceled--
|
||||||
|
if invariants && s.mu.numCanceled < 0 {
|
||||||
|
panic("negative numCanceled")
|
||||||
|
}
|
||||||
|
|
||||||
|
case s.mu.outstanding+w.n <= s.mu.capacity:
|
||||||
|
// Request can be fulfilled.
|
||||||
|
s.mu.outstanding += w.n
|
||||||
|
w.c <- nil
|
||||||
|
|
||||||
|
case w.n > s.mu.capacity:
|
||||||
|
// Request must be failed. This can happen if the capacity was decreased
|
||||||
|
// while the element was queued.
|
||||||
|
w.c <- ErrRequestExceedsCapacity
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Head of the queue needs to wait some more.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// chanSyncPool is used to pool allocations of the channels used to notify
|
||||||
|
// goroutines waiting in Acquire.
|
||||||
|
var chanSyncPool = sync.Pool{
|
||||||
|
New: func() interface{} { return make(chan error, 1) },
|
||||||
|
}
|
||||||
59
vendor/github.com/cockroachdb/logtags/buffer.go
generated
vendored
59
vendor/github.com/cockroachdb/logtags/buffer.go
generated
vendored
@@ -38,6 +38,20 @@ func (b *Buffer) Get() []Tag {
|
|||||||
return b.tags
|
return b.tags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTag returns the tag corresponding to the given key. If the tag doesn't
|
||||||
|
// exist, the bool return value will be false.
|
||||||
|
func (b *Buffer) GetTag(key string) (Tag, bool) {
|
||||||
|
if b == nil {
|
||||||
|
return Tag{}, false
|
||||||
|
}
|
||||||
|
for _, t := range b.tags {
|
||||||
|
if t.Key() == key {
|
||||||
|
return t, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Tag{}, false
|
||||||
|
}
|
||||||
|
|
||||||
// Add returns a new buffer with one more tag. If the tag has the same key as an
|
// Add returns a new buffer with one more tag. If the tag has the same key as an
|
||||||
// earlier tag, that tag is overwritten.
|
// earlier tag, that tag is overwritten.
|
||||||
// The receiver can be nil.
|
// The receiver can be nil.
|
||||||
@@ -52,6 +66,25 @@ func (b *Buffer) Add(key string, value interface{}) *Buffer {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove returns a new Buffer with the tag with key `key` removed. If the tag
|
||||||
|
// does not exist, the receiver is returned (unchanged). The bool return value
|
||||||
|
// is true if the tag existed.
|
||||||
|
func (b *Buffer) Remove(key string) (*Buffer, bool) {
|
||||||
|
if b == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
for i, t := range b.tags {
|
||||||
|
if t.Key() == key {
|
||||||
|
res := &Buffer{}
|
||||||
|
res.init(len(b.tags)-1, 0 /* capacityHint */)
|
||||||
|
copy(res.tags, b.tags[:i])
|
||||||
|
copy(res.tags[i:], b.tags[i+1:])
|
||||||
|
return res, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b, false
|
||||||
|
}
|
||||||
|
|
||||||
// Merge returns a new buffer which contains tags from the receiver, followed by
|
// Merge returns a new buffer which contains tags from the receiver, followed by
|
||||||
// the tags from <other>.
|
// the tags from <other>.
|
||||||
//
|
//
|
||||||
@@ -67,7 +100,7 @@ func (b *Buffer) Merge(other *Buffer) *Buffer {
|
|||||||
if b == nil || len(b.tags) == 0 {
|
if b == nil || len(b.tags) == 0 {
|
||||||
return other
|
return other
|
||||||
}
|
}
|
||||||
if len(other.tags) == 0 {
|
if other == nil || len(other.tags) == 0 {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,12 +154,12 @@ func (b *Buffer) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FormatToString emits the k/v pairs to a strings.Builder.
|
// FormatToString emits the k/v pairs to a strings.Builder.
|
||||||
// - the k/v pairs are separated by commas (& no spaces).
|
// - the k/v pairs are separated by commas (& no spaces).
|
||||||
// - if there is no value, only the key is printed.
|
// - if there is no value, only the key is printed.
|
||||||
// - if there is a value, and the key is just 1 character long,
|
// - if there is a value, and the key is just 1 character long,
|
||||||
// the key and the value are concatenated.
|
// the key and the value are concatenated.
|
||||||
// This supports e.g. printing k="n", v=123 as "n123".
|
// This supports e.g. printing k="n", v=123 as "n123".
|
||||||
// - otherwise, it prints "k=v".
|
// - otherwise, it prints "k=v".
|
||||||
func (b *Buffer) FormatToString(buf *strings.Builder) {
|
func (b *Buffer) FormatToString(buf *strings.Builder) {
|
||||||
comma := ""
|
comma := ""
|
||||||
for _, t := range b.Get() {
|
for _, t := range b.Get() {
|
||||||
@@ -142,13 +175,19 @@ func (b *Buffer) FormatToString(buf *strings.Builder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Buffer) init(length, maxLenHint int) {
|
// init initializes b.tags to a slice of len `length` (filled with zero values).
|
||||||
|
// The slice may have capacity capacityHint. If capacityHint is less than
|
||||||
|
// length, it is ignored.
|
||||||
|
func (b *Buffer) init(length, capacityHint int) {
|
||||||
if length <= staticSlots {
|
if length <= staticSlots {
|
||||||
// Even if maxLenHint is larger that staticSlots, we still want to try to
|
// Even if capacityHint is larger that staticSlots, we still want to try to
|
||||||
// avoid the allocation (especially since tags frequently get deduplicated).
|
// avoid the allocation (especially since tags frequently get deduplicated).
|
||||||
b.tags = b.prealloc[:length]
|
b.tags = b.prealloc[:length]
|
||||||
} else {
|
} else {
|
||||||
b.tags = make([]Tag, length, maxLenHint)
|
if capacityHint < length {
|
||||||
|
capacityHint = length
|
||||||
|
}
|
||||||
|
b.tags = make([]Tag, length, capacityHint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
vendor/github.com/cockroachdb/logtags/context.go
generated
vendored
12
vendor/github.com/cockroachdb/logtags/context.go
generated
vendored
@@ -41,6 +41,18 @@ func AddTag(ctx context.Context, key string, value interface{}) context.Context
|
|||||||
return WithTags(ctx, b.Add(key, value))
|
return WithTags(ctx, b.Add(key, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveTag returns a context that has the tags in the given context except the
|
||||||
|
// tag with key `key`. If such a tag does not exist, the given context is
|
||||||
|
// returned.
|
||||||
|
func RemoveTag(ctx context.Context, key string) context.Context {
|
||||||
|
b := FromContext(ctx)
|
||||||
|
newB, ok := b.Remove(key)
|
||||||
|
if !ok {
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
return WithTags(ctx, newB)
|
||||||
|
}
|
||||||
|
|
||||||
// AddTags returns a context that has the tags in the given context plus another
|
// AddTags returns a context that has the tags in the given context plus another
|
||||||
// set of tags. Tags are deduplicated (see Buffer.AddTags).
|
// set of tags. Tags are deduplicated (see Buffer.AddTags).
|
||||||
func AddTags(ctx context.Context, tags *Buffer) context.Context {
|
func AddTags(ctx context.Context, tags *Buffer) context.Context {
|
||||||
|
|||||||
5
vendor/github.com/cockroachdb/pebble/.gitignore
generated
vendored
5
vendor/github.com/cockroachdb/pebble/.gitignore
generated
vendored
@@ -1,4 +1,9 @@
|
|||||||
|
# Github action artifacts.
|
||||||
|
artifacts
|
||||||
# Profiling artifacts.
|
# Profiling artifacts.
|
||||||
cpu.*.prof
|
cpu.*.prof
|
||||||
heap.prof
|
heap.prof
|
||||||
mutex.prof
|
mutex.prof
|
||||||
|
coverprofile.out
|
||||||
|
# Testing artifacts
|
||||||
|
meta.*.test
|
||||||
|
|||||||
58
vendor/github.com/cockroachdb/pebble/Makefile
generated
vendored
58
vendor/github.com/cockroachdb/pebble/Makefile
generated
vendored
@@ -4,7 +4,8 @@ GOFLAGS :=
|
|||||||
STRESSFLAGS :=
|
STRESSFLAGS :=
|
||||||
TAGS := invariants
|
TAGS := invariants
|
||||||
TESTS := .
|
TESTS := .
|
||||||
LATEST_RELEASE := $(shell git fetch origin && git branch -r --list '*/crl-release-*' | grep -o 'crl-release-.*$$' | sort | tail -1)
|
PREV_RELEASE := crl-release-23.1
|
||||||
|
COVER_PROFILE := coverprofile.out
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all:
|
all:
|
||||||
@@ -15,18 +16,40 @@ all:
|
|||||||
@echo " make stressrace"
|
@echo " make stressrace"
|
||||||
@echo " make stressmeta"
|
@echo " make stressmeta"
|
||||||
@echo " make crossversion-meta"
|
@echo " make crossversion-meta"
|
||||||
|
@echo " make testcoverage"
|
||||||
@echo " make mod-update"
|
@echo " make mod-update"
|
||||||
|
@echo " make generate"
|
||||||
|
@echo " make generate-test-data"
|
||||||
@echo " make clean"
|
@echo " make clean"
|
||||||
|
|
||||||
override testflags :=
|
override testflags :=
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
${GO} test -mod=vendor -tags '$(TAGS)' ${testflags} -run ${TESTS} ${PKG}
|
${GO} test -tags '$(TAGS)' ${testflags} -run ${TESTS} ${PKG}
|
||||||
|
|
||||||
|
.PHONY: testcoverage
|
||||||
|
testcoverage:
|
||||||
|
${GO} test -tags '$(TAGS)' ${testflags} -run ${TESTS} ${PKG} -coverprofile ${COVER_PROFILE}
|
||||||
|
|
||||||
.PHONY: testrace
|
.PHONY: testrace
|
||||||
testrace: testflags += -race -timeout 20m
|
testrace: testflags += -race -timeout 20m
|
||||||
testrace: test
|
testrace: test
|
||||||
|
|
||||||
|
testasan: testflags += -asan -timeout 20m
|
||||||
|
testasan: test
|
||||||
|
|
||||||
|
testmsan: export CC=clang
|
||||||
|
testmsan: testflags += -msan -timeout 20m
|
||||||
|
testmsan: test
|
||||||
|
|
||||||
|
.PHONY: testobjiotracing
|
||||||
|
testobjiotracing:
|
||||||
|
${GO} test -tags '$(TAGS) pebble_obj_io_tracing' ${testflags} -run ${TESTS} ./objstorage/objstorageprovider/objiotracing
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint:
|
||||||
|
${GO} test -tags '$(TAGS)' ${testflags} -run ${TESTS} ./internal/lint
|
||||||
|
|
||||||
.PHONY: stress stressrace
|
.PHONY: stress stressrace
|
||||||
stressrace: testflags += -race
|
stressrace: testflags += -race
|
||||||
stress stressrace: testflags += -exec 'stress ${STRESSFLAGS}' -timeout 0 -test.v
|
stress stressrace: testflags += -exec 'stress ${STRESSFLAGS}' -timeout 0 -test.v
|
||||||
@@ -40,20 +63,34 @@ stressmeta: stress
|
|||||||
|
|
||||||
.PHONY: crossversion-meta
|
.PHONY: crossversion-meta
|
||||||
crossversion-meta:
|
crossversion-meta:
|
||||||
git checkout ${LATEST_RELEASE}; \
|
git checkout ${PREV_RELEASE}; \
|
||||||
${GO} test -c ./internal/metamorphic -o './internal/metamorphic/crossversion/${LATEST_RELEASE}.test'; \
|
${GO} test -c ./internal/metamorphic -o './internal/metamorphic/crossversion/${PREV_RELEASE}.test'; \
|
||||||
git checkout -; \
|
git checkout -; \
|
||||||
${GO} test -c ./internal/metamorphic -o './internal/metamorphic/crossversion/head.test'; \
|
${GO} test -c ./internal/metamorphic -o './internal/metamorphic/crossversion/head.test'; \
|
||||||
${GO} test -tags '$(TAGS)' ${testflags} -v -run 'TestMetaCrossVersion' ./internal/metamorphic/crossversion --version '${LATEST_RELEASE},${LATEST_RELEASE},${LATEST_RELEASE}.test' --version 'HEAD,HEAD,./head.test'
|
${GO} test -tags '$(TAGS)' ${testflags} -v -run 'TestMetaCrossVersion' ./internal/metamorphic/crossversion --version '${PREV_RELEASE},${PREV_RELEASE},${PREV_RELEASE}.test' --version 'HEAD,HEAD,./head.test'
|
||||||
|
|
||||||
|
.PHONY: stress-crossversion
|
||||||
|
stress-crossversion:
|
||||||
|
STRESS=1 ./scripts/run-crossversion-meta.sh crl-release-21.2 crl-release-22.1 crl-release-22.2 crl-release-23.1 master
|
||||||
|
|
||||||
.PHONY: generate
|
.PHONY: generate
|
||||||
generate:
|
generate:
|
||||||
${GO} generate -mod=vendor ${PKG}
|
${GO} generate ${PKG}
|
||||||
|
|
||||||
|
generate:
|
||||||
|
|
||||||
|
# Note that the output of generate-test-data is not deterministic. This should
|
||||||
|
# only be run manually as needed.
|
||||||
|
.PHONY: generate-test-data
|
||||||
|
generate-test-data:
|
||||||
|
${GO} run -tags make_incorrect_manifests ./tool/make_incorrect_manifests.go
|
||||||
|
${GO} run -tags make_test_find_db ./tool/make_test_find_db.go
|
||||||
|
${GO} run -tags make_test_sstables ./tool/make_test_sstables.go
|
||||||
|
${GO} run -tags make_test_remotecat ./tool/make_test_remotecat.go
|
||||||
|
|
||||||
mod-update:
|
mod-update:
|
||||||
${GO} get -u
|
${GO} get -u
|
||||||
${GO} mod tidy
|
${GO} mod tidy
|
||||||
${GO} mod vendor
|
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
@@ -78,11 +115,11 @@ endif
|
|||||||
@${GO} mod tidy
|
@${GO} mod tidy
|
||||||
$(MAKE) git-clean-check
|
$(MAKE) git-clean-check
|
||||||
|
|
||||||
|
# TODO(radu): switch back to @latest once bogus doc changes are
|
||||||
|
# addressed; see https://github.com/cockroachdb/crlfmt/pull/44
|
||||||
.PHONY: format
|
.PHONY: format
|
||||||
format:
|
format:
|
||||||
for _file in $$(gofmt -s -l . | grep -vE '^vendor/'); do \
|
go install github.com/cockroachdb/crlfmt@44a36ec7 && crlfmt -w -tab 2 .
|
||||||
gofmt -s -w $$_file ; \
|
|
||||||
done
|
|
||||||
|
|
||||||
.PHONY: format-check
|
.PHONY: format-check
|
||||||
format-check:
|
format-check:
|
||||||
@@ -90,4 +127,5 @@ ifneq ($(git_dirty),)
|
|||||||
$(error format-check must be invoked on a clean repository)
|
$(error format-check must be invoked on a clean repository)
|
||||||
endif
|
endif
|
||||||
$(MAKE) format
|
$(MAKE) format
|
||||||
|
git diff
|
||||||
$(MAKE) git-clean-check
|
$(MAKE) git-clean-check
|
||||||
|
|||||||
84
vendor/github.com/cockroachdb/pebble/README.md
generated
vendored
84
vendor/github.com/cockroachdb/pebble/README.md
generated
vendored
@@ -1,4 +1,4 @@
|
|||||||
# Pebble [](https://github.com/cockroachdb/pebble/actions/workflows/ci.yaml) [](https://godoc.org/github.com/cockroachdb/pebble)
|
# Pebble [](https://github.com/cockroachdb/pebble/actions/workflows/ci.yaml) [](https://godoc.org/github.com/cockroachdb/pebble) <sup><sub><sub>[Coverage](https://storage.googleapis.com/crl-codecover-public/pebble/index.html)</sub></sub></sup>
|
||||||
|
|
||||||
#### [Nightly benchmarks](https://cockroachdb.github.io/pebble/)
|
#### [Nightly benchmarks](https://cockroachdb.github.io/pebble/)
|
||||||
|
|
||||||
@@ -8,9 +8,8 @@ RocksDB file formats and a few extensions such as range deletion
|
|||||||
tombstones, table-level bloom filters, and updates to the MANIFEST
|
tombstones, table-level bloom filters, and updates to the MANIFEST
|
||||||
format.
|
format.
|
||||||
|
|
||||||
Pebble intentionally does not aspire to include every feature in
|
Pebble intentionally does not aspire to include every feature in RocksDB and
|
||||||
RocksDB and is specifically targetting the use case and feature set
|
specifically targets the use case and feature set needed by CockroachDB:
|
||||||
needed by CockroachDB:
|
|
||||||
|
|
||||||
* Block-based tables
|
* Block-based tables
|
||||||
* Checkpoints
|
* Checkpoints
|
||||||
@@ -68,6 +67,17 @@ Pebble offers several improvements over RocksDB:
|
|||||||
* Faster commit pipeline that achieves better concurrency.
|
* Faster commit pipeline that achieves better concurrency.
|
||||||
* Seamless merged iteration of indexed batches. The mutations in the
|
* Seamless merged iteration of indexed batches. The mutations in the
|
||||||
batch conceptually occupy another memtable level.
|
batch conceptually occupy another memtable level.
|
||||||
|
* L0 sublevels and flush splitting for concurrent compactions out of L0 and
|
||||||
|
reduced read-amplification during heavy write load.
|
||||||
|
* Faster LSM edits in LSMs with large numbers of sstables through use of a
|
||||||
|
copy-on-write B-tree to hold file metadata.
|
||||||
|
* Delete-only compactions that drop whole sstables that fall within the bounds
|
||||||
|
of a range deletion.
|
||||||
|
* Block-property collectors and filters that enable iterators to skip tables,
|
||||||
|
index blocks and data blocks that are irrelevant, according to user-defined
|
||||||
|
properties over key-value pairs.
|
||||||
|
* Range keys API, allowing KV pairs defined over a range of keyspace with
|
||||||
|
user-defined semantics and interleaved during iteration.
|
||||||
* Smaller, more approachable code base.
|
* Smaller, more approachable code base.
|
||||||
|
|
||||||
See the [Pebble vs RocksDB: Implementation
|
See the [Pebble vs RocksDB: Implementation
|
||||||
@@ -100,10 +110,68 @@ known incompatibilities.
|
|||||||
format.
|
format.
|
||||||
* Plain table format. Pebble does not support the plain table sstable
|
* Plain table format. Pebble does not support the plain table sstable
|
||||||
format.
|
format.
|
||||||
* SSTable format version 3 and 4. Pebble does not currently support
|
* SSTable format version 3 and 4. Pebble does not support version 3
|
||||||
version 3 and version 4 format sstables. The sstable format version
|
and version 4 format sstables. The sstable format version is
|
||||||
is controlled by the `BlockBasedTableOptions::format_version`
|
controlled by the `BlockBasedTableOptions::format_version` option.
|
||||||
option. See [#97](https://github.com/cockroachdb/pebble/issues/97).
|
See [#97](https://github.com/cockroachdb/pebble/issues/97).
|
||||||
|
|
||||||
|
## Format major versions
|
||||||
|
|
||||||
|
Over time Pebble has introduced new physical file formats. Backwards
|
||||||
|
incompatible changes are made through the introduction of 'format major
|
||||||
|
versions'. By default, when Pebble opens a database, it defaults to
|
||||||
|
`FormatMostCompatible`. This version is bi-directionally compatible with RocksDB
|
||||||
|
6.2.1 (with the caveats described above).
|
||||||
|
|
||||||
|
To opt into new formats, a user may set `FormatMajorVersion` on the
|
||||||
|
[`Options`](https://pkg.go.dev/github.com/cockroachdb/pebble#Options)
|
||||||
|
supplied to
|
||||||
|
[`Open`](https://pkg.go.dev/github.com/cockroachdb/pebble#Open), or
|
||||||
|
upgrade the format major version at runtime using
|
||||||
|
[`DB.RatchetFormatMajorVersion`](https://pkg.go.dev/github.com/cockroachdb/pebble#DB.RatchetFormatMajorVersion).
|
||||||
|
Format major version upgrades are permanent; There is no option to
|
||||||
|
return to an earlier format.
|
||||||
|
|
||||||
|
The table below outlines the history of format major versions:
|
||||||
|
|
||||||
|
| Name | Value | Migration |
|
||||||
|
|------------------------------------|-------|------------|
|
||||||
|
| FormatMostCompatible | 1 | No |
|
||||||
|
| FormatVersioned | 3 | No |
|
||||||
|
| FormatSetWithDelete | 4 | No |
|
||||||
|
| FormatBlockPropertyCollector | 5 | No |
|
||||||
|
| FormatSplitUserKeysMarked | 6 | Background |
|
||||||
|
| FormatSplitUserKeysMarkedCompacted | 7 | Blocking |
|
||||||
|
| FormatRangeKeys | 8 | No |
|
||||||
|
| FormatMinTableFormatPebblev1 | 9 | No |
|
||||||
|
| FormatPrePebblev1Marked | 10 | Background |
|
||||||
|
| FormatSSTableValueBlocks | 12 | No |
|
||||||
|
| FormatFlushableIngest | 13 | No |
|
||||||
|
| FormatPrePebblev1MarkedCompacted | 14 | Blocking |
|
||||||
|
| FormatDeleteSizedAndObsolete | 15 | No |
|
||||||
|
| FormatVirtualSSTables | 16 | No |
|
||||||
|
|
||||||
|
Upgrading to a format major version with 'Background' in the migration
|
||||||
|
column may trigger background activity to rewrite physical file
|
||||||
|
formats, typically through compactions. Upgrading to a format major
|
||||||
|
version with 'Blocking' in the migration column will block until a
|
||||||
|
migration is complete. The database may continue to serve reads and
|
||||||
|
writes if upgrading a live database through
|
||||||
|
`RatchetFormatMajorVersion`, but the method call will not return until
|
||||||
|
the migration is complete.
|
||||||
|
|
||||||
|
For reference, the table below lists the range of supported Pebble format major
|
||||||
|
versions for CockroachDB releases.
|
||||||
|
|
||||||
|
| CockroachDB release | Earliest supported | Latest supported |
|
||||||
|
|---------------------|------------------------------------|---------------------------|
|
||||||
|
| 20.1 through 21.1 | FormatMostCompatible | FormatMostCompatible |
|
||||||
|
| 21.2 | FormatMostCompatible | FormatSetWithDelete |
|
||||||
|
| 21.2 | FormatMostCompatible | FormatSetWithDelete |
|
||||||
|
| 22.1 | FormatMostCompatible | FormatSplitUserKeysMarked |
|
||||||
|
| 22.2 | FormatMostCompatible | FormatPrePebblev1Marked |
|
||||||
|
| 23.1 | FormatSplitUserKeysMarkedCompacted | FormatFlushableIngest |
|
||||||
|
| 23.2 | FormatSplitUserKeysMarkedCompacted | FormatVirtualSSTables |
|
||||||
|
|
||||||
## Pedigree
|
## Pedigree
|
||||||
|
|
||||||
|
|||||||
680
vendor/github.com/cockroachdb/pebble/batch.go
generated
vendored
680
vendor/github.com/cockroachdb/pebble/batch.go
generated
vendored
File diff suppressed because it is too large
Load Diff
6
vendor/github.com/cockroachdb/pebble/cache.go
generated
vendored
6
vendor/github.com/cockroachdb/pebble/cache.go
generated
vendored
@@ -15,9 +15,9 @@ type Cache = cache.Cache
|
|||||||
// creator of the cache should usually release their reference after the DB is
|
// creator of the cache should usually release their reference after the DB is
|
||||||
// created.
|
// created.
|
||||||
//
|
//
|
||||||
// c := pebble.NewCache(...)
|
// c := pebble.NewCache(...)
|
||||||
// defer c.Unref()
|
// defer c.Unref()
|
||||||
// d, err := pebble.Open(pebble.Options{Cache: c})
|
// d, err := pebble.Open(pebble.Options{Cache: c})
|
||||||
func NewCache(size int64) *cache.Cache {
|
func NewCache(size int64) *cache.Cache {
|
||||||
return cache.New(size)
|
return cache.New(size)
|
||||||
}
|
}
|
||||||
|
|||||||
251
vendor/github.com/cockroachdb/pebble/checkpoint.go
generated
vendored
251
vendor/github.com/cockroachdb/pebble/checkpoint.go
generated
vendored
@@ -5,10 +5,12 @@
|
|||||||
package pebble
|
package pebble
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors/oserror"
|
"github.com/cockroachdb/errors/oserror"
|
||||||
"github.com/cockroachdb/pebble/internal/base"
|
"github.com/cockroachdb/pebble/internal/base"
|
||||||
|
"github.com/cockroachdb/pebble/record"
|
||||||
"github.com/cockroachdb/pebble/vfs"
|
"github.com/cockroachdb/pebble/vfs"
|
||||||
"github.com/cockroachdb/pebble/vfs/atomicfs"
|
"github.com/cockroachdb/pebble/vfs/atomicfs"
|
||||||
)
|
)
|
||||||
@@ -19,6 +21,9 @@ type checkpointOptions struct {
|
|||||||
// flushWAL set to true will force a flush and sync of the WAL prior to
|
// flushWAL set to true will force a flush and sync of the WAL prior to
|
||||||
// checkpointing.
|
// checkpointing.
|
||||||
flushWAL bool
|
flushWAL bool
|
||||||
|
|
||||||
|
// If set, any SSTs that don't overlap with these spans are excluded from a checkpoint.
|
||||||
|
restrictToSpans []CheckpointSpan
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckpointOption set optional parameters used by `DB.Checkpoint`.
|
// CheckpointOption set optional parameters used by `DB.Checkpoint`.
|
||||||
@@ -39,6 +44,43 @@ func WithFlushedWAL() CheckpointOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithRestrictToSpans specifies spans of interest for the checkpoint. Any SSTs
|
||||||
|
// that don't overlap with any of these spans are excluded from the checkpoint.
|
||||||
|
//
|
||||||
|
// Note that the checkpoint can still surface keys outside of these spans (from
|
||||||
|
// the WAL and from SSTs that partially overlap with these spans). Moreover,
|
||||||
|
// these surface keys aren't necessarily "valid" in that they could have been
|
||||||
|
// modified but the SST containing the modification is excluded.
|
||||||
|
func WithRestrictToSpans(spans []CheckpointSpan) CheckpointOption {
|
||||||
|
return func(opt *checkpointOptions) {
|
||||||
|
opt.restrictToSpans = spans
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointSpan is a key range [Start, End) (inclusive on Start, exclusive on
|
||||||
|
// End) of interest for a checkpoint.
|
||||||
|
type CheckpointSpan struct {
|
||||||
|
Start []byte
|
||||||
|
End []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// excludeFromCheckpoint returns true if an SST file should be excluded from the
|
||||||
|
// checkpoint because it does not overlap with the spans of interest
|
||||||
|
// (opt.restrictToSpans).
|
||||||
|
func excludeFromCheckpoint(f *fileMetadata, opt *checkpointOptions, cmp Compare) bool {
|
||||||
|
if len(opt.restrictToSpans) == 0 {
|
||||||
|
// Option not set; don't exclude anything.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, s := range opt.restrictToSpans {
|
||||||
|
if f.Overlaps(cmp, s.Start, s.End, true /* exclusiveEnd */) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// None of the restrictToSpans overlapped; we can exclude this file.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// mkdirAllAndSyncParents creates destDir and any of its missing parents.
|
// mkdirAllAndSyncParents creates destDir and any of its missing parents.
|
||||||
// Those missing parents, as well as the closest existing ancestor, are synced.
|
// Those missing parents, as well as the closest existing ancestor, are synced.
|
||||||
// Returns a handle to the directory created at destDir.
|
// Returns a handle to the directory created at destDir.
|
||||||
@@ -46,23 +88,20 @@ func mkdirAllAndSyncParents(fs vfs.FS, destDir string) (vfs.File, error) {
|
|||||||
// Collect paths for all directories between destDir (excluded) and its
|
// Collect paths for all directories between destDir (excluded) and its
|
||||||
// closest existing ancestor (included).
|
// closest existing ancestor (included).
|
||||||
var parentPaths []string
|
var parentPaths []string
|
||||||
foundExistingAncestor := false
|
for parentPath := fs.PathDir(destDir); ; parentPath = fs.PathDir(parentPath) {
|
||||||
for parentPath := fs.PathDir(destDir); parentPath != "."; parentPath = fs.PathDir(parentPath) {
|
|
||||||
parentPaths = append(parentPaths, parentPath)
|
parentPaths = append(parentPaths, parentPath)
|
||||||
|
if fs.PathDir(parentPath) == parentPath {
|
||||||
|
break
|
||||||
|
}
|
||||||
_, err := fs.Stat(parentPath)
|
_, err := fs.Stat(parentPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Exit loop at the closest existing ancestor.
|
// Exit loop at the closest existing ancestor.
|
||||||
foundExistingAncestor = true
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if !oserror.IsNotExist(err) {
|
if !oserror.IsNotExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Handle empty filesystem edge case.
|
|
||||||
if !foundExistingAncestor {
|
|
||||||
parentPaths = append(parentPaths, "")
|
|
||||||
}
|
|
||||||
// Create destDir and any of its missing parents.
|
// Create destDir and any of its missing parents.
|
||||||
if err := fs.MkdirAll(destDir, 0755); err != nil {
|
if err := fs.MkdirAll(destDir, 0755); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -142,11 +181,14 @@ func (d *DB) Checkpoint(
|
|||||||
// file number.
|
// file number.
|
||||||
memQueue := d.mu.mem.queue
|
memQueue := d.mu.mem.queue
|
||||||
current := d.mu.versions.currentVersion()
|
current := d.mu.versions.currentVersion()
|
||||||
formatVers := d.mu.formatVers.vers
|
formatVers := d.FormatMajorVersion()
|
||||||
manifestFileNum := d.mu.versions.manifestFileNum
|
manifestFileNum := d.mu.versions.manifestFileNum
|
||||||
manifestSize := d.mu.versions.manifest.Size()
|
manifestSize := d.mu.versions.manifest.Size()
|
||||||
optionsFileNum := d.optionsFileNum
|
optionsFileNum := d.optionsFileNum
|
||||||
|
virtualBackingFiles := make(map[base.DiskFileNum]struct{})
|
||||||
|
for diskFileNum := range d.mu.versions.backingState.fileBackingMap {
|
||||||
|
virtualBackingFiles[diskFileNum] = struct{}{}
|
||||||
|
}
|
||||||
// Release the manifest and DB.mu so we don't block other operations on
|
// Release the manifest and DB.mu so we don't block other operations on
|
||||||
// the database.
|
// the database.
|
||||||
d.mu.versions.logUnlock()
|
d.mu.versions.logUnlock()
|
||||||
@@ -154,13 +196,10 @@ func (d *DB) Checkpoint(
|
|||||||
|
|
||||||
// Wrap the normal filesystem with one which wraps newly created files with
|
// Wrap the normal filesystem with one which wraps newly created files with
|
||||||
// vfs.NewSyncingFile.
|
// vfs.NewSyncingFile.
|
||||||
fs := syncingFS{
|
fs := vfs.NewSyncingFS(d.opts.FS, vfs.SyncingFileOptions{
|
||||||
FS: d.opts.FS,
|
NoSyncOnClose: d.opts.NoSyncOnClose,
|
||||||
syncOpts: vfs.SyncingFileOptions{
|
BytesPerSync: d.opts.BytesPerSync,
|
||||||
NoSyncOnClose: d.opts.NoSyncOnClose,
|
})
|
||||||
BytesPerSync: d.opts.BytesPerSync,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the dir and its parents (if necessary), and sync them.
|
// Create the dir and its parents (if necessary), and sync them.
|
||||||
var dir vfs.File
|
var dir vfs.File
|
||||||
@@ -170,11 +209,7 @@ func (d *DB) Checkpoint(
|
|||||||
}
|
}
|
||||||
if ckErr != nil {
|
if ckErr != nil {
|
||||||
// Attempt to cleanup on error.
|
// Attempt to cleanup on error.
|
||||||
paths, _ := fs.List(destDir)
|
_ = fs.RemoveAll(destDir)
|
||||||
for _, path := range paths {
|
|
||||||
_ = fs.Remove(path)
|
|
||||||
}
|
|
||||||
_ = fs.Remove(destDir)
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
dir, ckErr = mkdirAllAndSyncParents(fs, destDir)
|
dir, ckErr = mkdirAllAndSyncParents(fs, destDir)
|
||||||
@@ -214,45 +249,34 @@ func (d *DB) Checkpoint(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
var excludedFiles map[deletedFileEntry]*fileMetadata
|
||||||
// Copy the MANIFEST, and create a pointer to it. We copy rather
|
// Set of FileBacking.DiskFileNum which will be required by virtual sstables
|
||||||
// than link because additional version edits added to the
|
// in the checkpoint.
|
||||||
// MANIFEST after we took our snapshot of the sstables will
|
requiredVirtualBackingFiles := make(map[base.DiskFileNum]struct{})
|
||||||
// reference sstables that aren't in our checkpoint. For a
|
|
||||||
// similar reason, we need to limit how much of the MANIFEST we
|
|
||||||
// copy.
|
|
||||||
srcPath := base.MakeFilepath(fs, d.dirname, fileTypeManifest, manifestFileNum)
|
|
||||||
destPath := fs.PathJoin(destDir, fs.PathBase(srcPath))
|
|
||||||
ckErr = vfs.LimitedCopy(fs, srcPath, destPath, manifestSize)
|
|
||||||
if ckErr != nil {
|
|
||||||
return ckErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recent format versions use an atomic marker for setting the
|
|
||||||
// active manifest. Older versions use the CURRENT file. The
|
|
||||||
// setCurrentFunc function will return a closure that will
|
|
||||||
// take the appropriate action for the database's format
|
|
||||||
// version.
|
|
||||||
var manifestMarker *atomicfs.Marker
|
|
||||||
manifestMarker, _, ckErr = atomicfs.LocateMarker(fs, destDir, manifestMarkerName)
|
|
||||||
if ckErr != nil {
|
|
||||||
return ckErr
|
|
||||||
}
|
|
||||||
ckErr = setCurrentFunc(formatVers, manifestMarker, fs, destDir, dir)(manifestFileNum)
|
|
||||||
if ckErr != nil {
|
|
||||||
return ckErr
|
|
||||||
}
|
|
||||||
ckErr = manifestMarker.Close()
|
|
||||||
if ckErr != nil {
|
|
||||||
return ckErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Link or copy the sstables.
|
// Link or copy the sstables.
|
||||||
for l := range current.Levels {
|
for l := range current.Levels {
|
||||||
iter := current.Levels[l].Iter()
|
iter := current.Levels[l].Iter()
|
||||||
for f := iter.First(); f != nil; f = iter.Next() {
|
for f := iter.First(); f != nil; f = iter.Next() {
|
||||||
srcPath := base.MakeFilepath(fs, d.dirname, fileTypeTable, f.FileNum)
|
if excludeFromCheckpoint(f, opt, d.cmp) {
|
||||||
|
if excludedFiles == nil {
|
||||||
|
excludedFiles = make(map[deletedFileEntry]*fileMetadata)
|
||||||
|
}
|
||||||
|
excludedFiles[deletedFileEntry{
|
||||||
|
Level: l,
|
||||||
|
FileNum: f.FileNum,
|
||||||
|
}] = f
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fileBacking := f.FileBacking
|
||||||
|
if f.Virtual {
|
||||||
|
if _, ok := requiredVirtualBackingFiles[fileBacking.DiskFileNum]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
requiredVirtualBackingFiles[fileBacking.DiskFileNum] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
srcPath := base.MakeFilepath(fs, d.dirname, fileTypeTable, fileBacking.DiskFileNum)
|
||||||
destPath := fs.PathJoin(destDir, fs.PathBase(srcPath))
|
destPath := fs.PathJoin(destDir, fs.PathBase(srcPath))
|
||||||
ckErr = vfs.LinkOrCopy(fs, srcPath, destPath)
|
ckErr = vfs.LinkOrCopy(fs, srcPath, destPath)
|
||||||
if ckErr != nil {
|
if ckErr != nil {
|
||||||
@@ -261,6 +285,23 @@ func (d *DB) Checkpoint(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var removeBackingTables []base.DiskFileNum
|
||||||
|
for diskFileNum := range virtualBackingFiles {
|
||||||
|
if _, ok := requiredVirtualBackingFiles[diskFileNum]; !ok {
|
||||||
|
// The backing sstable associated with fileNum is no longer
|
||||||
|
// required.
|
||||||
|
removeBackingTables = append(removeBackingTables, diskFileNum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ckErr = d.writeCheckpointManifest(
|
||||||
|
fs, formatVers, destDir, dir, manifestFileNum.DiskFileNum(), manifestSize,
|
||||||
|
excludedFiles, removeBackingTables,
|
||||||
|
)
|
||||||
|
if ckErr != nil {
|
||||||
|
return ckErr
|
||||||
|
}
|
||||||
|
|
||||||
// Copy the WAL files. We copy rather than link because WAL file recycling
|
// Copy the WAL files. We copy rather than link because WAL file recycling
|
||||||
// will cause the WAL files to be reused which would invalidate the
|
// will cause the WAL files to be reused which would invalidate the
|
||||||
// checkpoint.
|
// checkpoint.
|
||||||
@@ -269,7 +310,7 @@ func (d *DB) Checkpoint(
|
|||||||
if logNum == 0 {
|
if logNum == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
srcPath := base.MakeFilepath(fs, d.walDirname, fileTypeLog, logNum)
|
srcPath := base.MakeFilepath(fs, d.walDirname, fileTypeLog, logNum.DiskFileNum())
|
||||||
destPath := fs.PathJoin(destDir, fs.PathBase(srcPath))
|
destPath := fs.PathJoin(destDir, fs.PathBase(srcPath))
|
||||||
ckErr = vfs.Copy(fs, srcPath, destPath)
|
ckErr = vfs.Copy(fs, srcPath, destPath)
|
||||||
if ckErr != nil {
|
if ckErr != nil {
|
||||||
@@ -286,3 +327,99 @@ func (d *DB) Checkpoint(
|
|||||||
dir = nil
|
dir = nil
|
||||||
return ckErr
|
return ckErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DB) writeCheckpointManifest(
|
||||||
|
fs vfs.FS,
|
||||||
|
formatVers FormatMajorVersion,
|
||||||
|
destDirPath string,
|
||||||
|
destDir vfs.File,
|
||||||
|
manifestFileNum base.DiskFileNum,
|
||||||
|
manifestSize int64,
|
||||||
|
excludedFiles map[deletedFileEntry]*fileMetadata,
|
||||||
|
removeBackingTables []base.DiskFileNum,
|
||||||
|
) error {
|
||||||
|
// Copy the MANIFEST, and create a pointer to it. We copy rather
|
||||||
|
// than link because additional version edits added to the
|
||||||
|
// MANIFEST after we took our snapshot of the sstables will
|
||||||
|
// reference sstables that aren't in our checkpoint. For a
|
||||||
|
// similar reason, we need to limit how much of the MANIFEST we
|
||||||
|
// copy.
|
||||||
|
// If some files are excluded from the checkpoint, also append a block that
|
||||||
|
// records those files as deleted.
|
||||||
|
if err := func() error {
|
||||||
|
srcPath := base.MakeFilepath(fs, d.dirname, fileTypeManifest, manifestFileNum)
|
||||||
|
destPath := fs.PathJoin(destDirPath, fs.PathBase(srcPath))
|
||||||
|
src, err := fs.Open(srcPath, vfs.SequentialReadsOption)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer src.Close()
|
||||||
|
|
||||||
|
dst, err := fs.Create(destPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dst.Close()
|
||||||
|
|
||||||
|
// Copy all existing records. We need to copy at the record level in case we
|
||||||
|
// need to append another record with the excluded files (we cannot simply
|
||||||
|
// append a record after a raw data copy; see
|
||||||
|
// https://github.com/cockroachdb/cockroach/issues/100935).
|
||||||
|
r := record.NewReader(&io.LimitedReader{R: src, N: manifestSize}, manifestFileNum.FileNum())
|
||||||
|
w := record.NewWriter(dst)
|
||||||
|
for {
|
||||||
|
rr, err := r.Next()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rw, err := w.Next()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(rw, rr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(excludedFiles) > 0 {
|
||||||
|
// Write out an additional VersionEdit that deletes the excluded SST files.
|
||||||
|
ve := versionEdit{
|
||||||
|
DeletedFiles: excludedFiles,
|
||||||
|
RemovedBackingTables: removeBackingTables,
|
||||||
|
}
|
||||||
|
|
||||||
|
rw, err := w.Next()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ve.Encode(rw); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := w.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return dst.Sync()
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recent format versions use an atomic marker for setting the
|
||||||
|
// active manifest. Older versions use the CURRENT file. The
|
||||||
|
// setCurrentFunc function will return a closure that will
|
||||||
|
// take the appropriate action for the database's format
|
||||||
|
// version.
|
||||||
|
var manifestMarker *atomicfs.Marker
|
||||||
|
manifestMarker, _, err := atomicfs.LocateMarker(fs, destDirPath, manifestMarkerName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := setCurrentFunc(formatVers, manifestMarker, fs, destDirPath, destDir)(manifestFileNum.FileNum()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return manifestMarker.Close()
|
||||||
|
}
|
||||||
|
|||||||
281
vendor/github.com/cockroachdb/pebble/cleaner.go
generated
vendored
281
vendor/github.com/cockroachdb/pebble/cleaner.go
generated
vendored
@@ -4,7 +4,18 @@
|
|||||||
|
|
||||||
package pebble
|
package pebble
|
||||||
|
|
||||||
import "github.com/cockroachdb/pebble/internal/base"
|
import (
|
||||||
|
"context"
|
||||||
|
"runtime/pprof"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cockroachdb/errors/oserror"
|
||||||
|
"github.com/cockroachdb/pebble/internal/base"
|
||||||
|
"github.com/cockroachdb/pebble/internal/invariants"
|
||||||
|
"github.com/cockroachdb/pebble/objstorage"
|
||||||
|
"github.com/cockroachdb/tokenbucket"
|
||||||
|
)
|
||||||
|
|
||||||
// Cleaner exports the base.Cleaner type.
|
// Cleaner exports the base.Cleaner type.
|
||||||
type Cleaner = base.Cleaner
|
type Cleaner = base.Cleaner
|
||||||
@@ -14,3 +25,271 @@ type DeleteCleaner = base.DeleteCleaner
|
|||||||
|
|
||||||
// ArchiveCleaner exports the base.ArchiveCleaner type.
|
// ArchiveCleaner exports the base.ArchiveCleaner type.
|
||||||
type ArchiveCleaner = base.ArchiveCleaner
|
type ArchiveCleaner = base.ArchiveCleaner
|
||||||
|
|
||||||
|
type cleanupManager struct {
|
||||||
|
opts *Options
|
||||||
|
objProvider objstorage.Provider
|
||||||
|
onTableDeleteFn func(fileSize uint64)
|
||||||
|
deletePacer *deletionPacer
|
||||||
|
|
||||||
|
// jobsCh is used as the cleanup job queue.
|
||||||
|
jobsCh chan *cleanupJob
|
||||||
|
// waitGroup is used to wait for the background goroutine to exit.
|
||||||
|
waitGroup sync.WaitGroup
|
||||||
|
|
||||||
|
mu struct {
|
||||||
|
sync.Mutex
|
||||||
|
// totalJobs is the total number of enqueued jobs (completed or in progress).
|
||||||
|
totalJobs int
|
||||||
|
completedJobs int
|
||||||
|
completedJobsCond sync.Cond
|
||||||
|
jobsQueueWarningIssued bool
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can queue this many jobs before we have to block EnqueueJob.
|
||||||
|
const jobsQueueDepth = 1000
|
||||||
|
|
||||||
|
// obsoleteFile holds information about a file that needs to be deleted soon.
|
||||||
|
type obsoleteFile struct {
|
||||||
|
dir string
|
||||||
|
fileNum base.DiskFileNum
|
||||||
|
fileType fileType
|
||||||
|
fileSize uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type cleanupJob struct {
|
||||||
|
jobID int
|
||||||
|
obsoleteFiles []obsoleteFile
|
||||||
|
}
|
||||||
|
|
||||||
|
// openCleanupManager creates a cleanupManager and starts its background goroutine.
|
||||||
|
// The cleanupManager must be Close()d.
|
||||||
|
func openCleanupManager(
|
||||||
|
opts *Options,
|
||||||
|
objProvider objstorage.Provider,
|
||||||
|
onTableDeleteFn func(fileSize uint64),
|
||||||
|
getDeletePacerInfo func() deletionPacerInfo,
|
||||||
|
) *cleanupManager {
|
||||||
|
cm := &cleanupManager{
|
||||||
|
opts: opts,
|
||||||
|
objProvider: objProvider,
|
||||||
|
onTableDeleteFn: onTableDeleteFn,
|
||||||
|
deletePacer: newDeletionPacer(time.Now(), int64(opts.TargetByteDeletionRate), getDeletePacerInfo),
|
||||||
|
jobsCh: make(chan *cleanupJob, jobsQueueDepth),
|
||||||
|
}
|
||||||
|
cm.mu.completedJobsCond.L = &cm.mu.Mutex
|
||||||
|
cm.waitGroup.Add(1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
pprof.Do(context.Background(), gcLabels, func(context.Context) {
|
||||||
|
cm.mainLoop()
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
|
||||||
|
return cm
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close stops the background goroutine, waiting until all queued jobs are completed.
|
||||||
|
// Delete pacing is disabled for the remaining jobs.
|
||||||
|
func (cm *cleanupManager) Close() {
|
||||||
|
close(cm.jobsCh)
|
||||||
|
cm.waitGroup.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnqueueJob adds a cleanup job to the manager's queue.
|
||||||
|
func (cm *cleanupManager) EnqueueJob(jobID int, obsoleteFiles []obsoleteFile) {
|
||||||
|
job := &cleanupJob{
|
||||||
|
jobID: jobID,
|
||||||
|
obsoleteFiles: obsoleteFiles,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report deleted bytes to the pacer, which can use this data to potentially
|
||||||
|
// increase the deletion rate to keep up. We want to do this at enqueue time
|
||||||
|
// rather than when we get to the job, otherwise the reported bytes will be
|
||||||
|
// subject to the throttling rate which defeats the purpose.
|
||||||
|
var pacingBytes uint64
|
||||||
|
for _, of := range obsoleteFiles {
|
||||||
|
if cm.needsPacing(of.fileType, of.fileNum) {
|
||||||
|
pacingBytes += of.fileSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pacingBytes > 0 {
|
||||||
|
cm.deletePacer.ReportDeletion(time.Now(), pacingBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
cm.mu.Lock()
|
||||||
|
cm.mu.totalJobs++
|
||||||
|
cm.maybeLogLocked()
|
||||||
|
cm.mu.Unlock()
|
||||||
|
|
||||||
|
if invariants.Enabled && len(cm.jobsCh) >= cap(cm.jobsCh)-2 {
|
||||||
|
panic("cleanup jobs queue full")
|
||||||
|
}
|
||||||
|
|
||||||
|
cm.jobsCh <- job
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until the completion of all jobs that were already queued.
|
||||||
|
//
|
||||||
|
// Does not wait for jobs that are enqueued during the call.
|
||||||
|
//
|
||||||
|
// Note that DB.mu should not be held while calling this method; the background
|
||||||
|
// goroutine needs to acquire DB.mu to update deleted table metrics.
|
||||||
|
func (cm *cleanupManager) Wait() {
|
||||||
|
cm.mu.Lock()
|
||||||
|
defer cm.mu.Unlock()
|
||||||
|
n := cm.mu.totalJobs
|
||||||
|
for cm.mu.completedJobs < n {
|
||||||
|
cm.mu.completedJobsCond.Wait()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mainLoop runs the manager's background goroutine.
|
||||||
|
func (cm *cleanupManager) mainLoop() {
|
||||||
|
defer cm.waitGroup.Done()
|
||||||
|
|
||||||
|
var tb tokenbucket.TokenBucket
|
||||||
|
// Use a token bucket with 1 token / second refill rate and 1 token burst.
|
||||||
|
tb.Init(1.0, 1.0)
|
||||||
|
for job := range cm.jobsCh {
|
||||||
|
for _, of := range job.obsoleteFiles {
|
||||||
|
if of.fileType != fileTypeTable {
|
||||||
|
path := base.MakeFilepath(cm.opts.FS, of.dir, of.fileType, of.fileNum)
|
||||||
|
cm.deleteObsoleteFile(of.fileType, job.jobID, path, of.fileNum, of.fileSize)
|
||||||
|
} else {
|
||||||
|
cm.maybePace(&tb, of.fileType, of.fileNum, of.fileSize)
|
||||||
|
cm.onTableDeleteFn(of.fileSize)
|
||||||
|
cm.deleteObsoleteObject(fileTypeTable, job.jobID, of.fileNum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cm.mu.Lock()
|
||||||
|
cm.mu.completedJobs++
|
||||||
|
cm.mu.completedJobsCond.Broadcast()
|
||||||
|
cm.maybeLogLocked()
|
||||||
|
cm.mu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *cleanupManager) needsPacing(fileType base.FileType, fileNum base.DiskFileNum) bool {
|
||||||
|
if fileType != fileTypeTable {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
meta, err := cm.objProvider.Lookup(fileType, fileNum)
|
||||||
|
if err != nil {
|
||||||
|
// The object was already removed from the provider; we won't actually
|
||||||
|
// delete anything, so we don't need to pace.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Don't throttle deletion of remote objects.
|
||||||
|
return !meta.IsRemote()
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybePace sleeps before deleting an object if appropriate. It is always
|
||||||
|
// called from the background goroutine.
|
||||||
|
func (cm *cleanupManager) maybePace(
|
||||||
|
tb *tokenbucket.TokenBucket, fileType base.FileType, fileNum base.DiskFileNum, fileSize uint64,
|
||||||
|
) {
|
||||||
|
if !cm.needsPacing(fileType, fileNum) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens := cm.deletePacer.PacingDelay(time.Now(), fileSize)
|
||||||
|
if tokens == 0.0 {
|
||||||
|
// The token bucket might be in debt; it could make us wait even for 0
|
||||||
|
// tokens. We don't want that if the pacer decided throttling should be
|
||||||
|
// disabled.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Wait for tokens. We use a token bucket instead of sleeping outright because
|
||||||
|
// the token bucket accumulates up to one second of unused tokens.
|
||||||
|
for {
|
||||||
|
ok, d := tb.TryToFulfill(tokenbucket.Tokens(tokens))
|
||||||
|
if ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteObsoleteFile deletes a (non-object) file that is no longer needed.
|
||||||
|
func (cm *cleanupManager) deleteObsoleteFile(
|
||||||
|
fileType fileType, jobID int, path string, fileNum base.DiskFileNum, fileSize uint64,
|
||||||
|
) {
|
||||||
|
// TODO(peter): need to handle this error, probably by re-adding the
|
||||||
|
// file that couldn't be deleted to one of the obsolete slices map.
|
||||||
|
err := cm.opts.Cleaner.Clean(cm.opts.FS, fileType, path)
|
||||||
|
if oserror.IsNotExist(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fileType {
|
||||||
|
case fileTypeLog:
|
||||||
|
cm.opts.EventListener.WALDeleted(WALDeleteInfo{
|
||||||
|
JobID: jobID,
|
||||||
|
Path: path,
|
||||||
|
FileNum: fileNum.FileNum(),
|
||||||
|
Err: err,
|
||||||
|
})
|
||||||
|
case fileTypeManifest:
|
||||||
|
cm.opts.EventListener.ManifestDeleted(ManifestDeleteInfo{
|
||||||
|
JobID: jobID,
|
||||||
|
Path: path,
|
||||||
|
FileNum: fileNum.FileNum(),
|
||||||
|
Err: err,
|
||||||
|
})
|
||||||
|
case fileTypeTable:
|
||||||
|
panic("invalid deletion of object file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *cleanupManager) deleteObsoleteObject(
|
||||||
|
fileType fileType, jobID int, fileNum base.DiskFileNum,
|
||||||
|
) {
|
||||||
|
if fileType != fileTypeTable {
|
||||||
|
panic("not an object")
|
||||||
|
}
|
||||||
|
|
||||||
|
var path string
|
||||||
|
meta, err := cm.objProvider.Lookup(fileType, fileNum)
|
||||||
|
if err != nil {
|
||||||
|
path = "<nil>"
|
||||||
|
} else {
|
||||||
|
path = cm.objProvider.Path(meta)
|
||||||
|
err = cm.objProvider.Remove(fileType, fileNum)
|
||||||
|
}
|
||||||
|
if cm.objProvider.IsNotExistError(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fileType {
|
||||||
|
case fileTypeTable:
|
||||||
|
cm.opts.EventListener.TableDeleted(TableDeleteInfo{
|
||||||
|
JobID: jobID,
|
||||||
|
Path: path,
|
||||||
|
FileNum: fileNum.FileNum(),
|
||||||
|
Err: err,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybeLogLocked issues a log if the job queue gets 75% full and issues a log
|
||||||
|
// when the job queue gets back to less than 10% full.
|
||||||
|
//
|
||||||
|
// Must be called with cm.mu locked.
|
||||||
|
func (cm *cleanupManager) maybeLogLocked() {
|
||||||
|
const highThreshold = jobsQueueDepth * 3 / 4
|
||||||
|
const lowThreshold = jobsQueueDepth / 10
|
||||||
|
|
||||||
|
jobsInQueue := cm.mu.totalJobs - cm.mu.completedJobs
|
||||||
|
|
||||||
|
if !cm.mu.jobsQueueWarningIssued && jobsInQueue > highThreshold {
|
||||||
|
cm.mu.jobsQueueWarningIssued = true
|
||||||
|
cm.opts.Logger.Infof("cleanup falling behind; job queue has over %d jobs", highThreshold)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cm.mu.jobsQueueWarningIssued && jobsInQueue < lowThreshold {
|
||||||
|
cm.mu.jobsQueueWarningIssued = false
|
||||||
|
cm.opts.Logger.Infof("cleanup back to normal; job queue has under %d jobs", lowThreshold)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
238
vendor/github.com/cockroachdb/pebble/commit.go
generated
vendored
238
vendor/github.com/cockroachdb/pebble/commit.go
generated
vendored
@@ -8,7 +8,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"unsafe"
|
"time"
|
||||||
|
|
||||||
"github.com/cockroachdb/pebble/record"
|
"github.com/cockroachdb/pebble/record"
|
||||||
)
|
)
|
||||||
@@ -32,13 +32,13 @@ type commitQueue struct {
|
|||||||
//
|
//
|
||||||
// The head index is stored in the most-significant bits so that we can
|
// The head index is stored in the most-significant bits so that we can
|
||||||
// atomically add to it and the overflow is harmless.
|
// atomically add to it and the overflow is harmless.
|
||||||
headTail uint64
|
headTail atomic.Uint64
|
||||||
|
|
||||||
// slots is a ring buffer of values stored in this queue. The size must be a
|
// slots is a ring buffer of values stored in this queue. The size must be a
|
||||||
// power of 2. A slot is in use until *both* the tail index has moved beyond
|
// power of 2. A slot is in use until *both* the tail index has moved beyond
|
||||||
// it and the slot value has been set to nil. The slot value is set to nil
|
// it and the slot value has been set to nil. The slot value is set to nil
|
||||||
// atomically by the consumer and read atomically by the producer.
|
// atomically by the consumer and read atomically by the producer.
|
||||||
slots [record.SyncConcurrency]unsafe.Pointer
|
slots [record.SyncConcurrency]atomic.Pointer[Batch]
|
||||||
}
|
}
|
||||||
|
|
||||||
const dequeueBits = 32
|
const dequeueBits = 32
|
||||||
@@ -57,17 +57,17 @@ func (q *commitQueue) pack(head, tail uint32) uint64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (q *commitQueue) enqueue(b *Batch) {
|
func (q *commitQueue) enqueue(b *Batch) {
|
||||||
ptrs := atomic.LoadUint64(&q.headTail)
|
ptrs := q.headTail.Load()
|
||||||
head, tail := q.unpack(ptrs)
|
head, tail := q.unpack(ptrs)
|
||||||
if (tail+uint32(len(q.slots)))&(1<<dequeueBits-1) == head {
|
if (tail+uint32(len(q.slots)))&(1<<dequeueBits-1) == head {
|
||||||
// Queue is full. This should never be reached because commitPipeline.sem
|
// Queue is full. This should never be reached because commitPipeline.commitQueueSem
|
||||||
// limits the number of concurrent operations.
|
// limits the number of concurrent operations.
|
||||||
panic("pebble: not reached")
|
panic("pebble: not reached")
|
||||||
}
|
}
|
||||||
slot := &q.slots[head&uint32(len(q.slots)-1)]
|
slot := &q.slots[head&uint32(len(q.slots)-1)]
|
||||||
|
|
||||||
// Check if the head slot has been released by dequeue.
|
// Check if the head slot has been released by dequeueApplied.
|
||||||
for atomic.LoadPointer(slot) != nil {
|
for slot.Load() != nil {
|
||||||
// Another goroutine is still cleaning up the tail, so the queue is
|
// Another goroutine is still cleaning up the tail, so the queue is
|
||||||
// actually still full. We spin because this should resolve itself
|
// actually still full. We spin because this should resolve itself
|
||||||
// momentarily.
|
// momentarily.
|
||||||
@@ -75,16 +75,20 @@ func (q *commitQueue) enqueue(b *Batch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The head slot is free, so we own it.
|
// The head slot is free, so we own it.
|
||||||
atomic.StorePointer(slot, unsafe.Pointer(b))
|
slot.Store(b)
|
||||||
|
|
||||||
// Increment head. This passes ownership of slot to dequeue and acts as a
|
// Increment head. This passes ownership of slot to dequeueApplied and acts as a
|
||||||
// store barrier for writing the slot.
|
// store barrier for writing the slot.
|
||||||
atomic.AddUint64(&q.headTail, 1<<dequeueBits)
|
q.headTail.Add(1 << dequeueBits)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *commitQueue) dequeue() *Batch {
|
// dequeueApplied removes the earliest enqueued Batch, if it is applied.
|
||||||
|
//
|
||||||
|
// Returns nil if the commit queue is empty or the earliest Batch is not yet
|
||||||
|
// applied.
|
||||||
|
func (q *commitQueue) dequeueApplied() *Batch {
|
||||||
for {
|
for {
|
||||||
ptrs := atomic.LoadUint64(&q.headTail)
|
ptrs := q.headTail.Load()
|
||||||
head, tail := q.unpack(ptrs)
|
head, tail := q.unpack(ptrs)
|
||||||
if tail == head {
|
if tail == head {
|
||||||
// Queue is empty.
|
// Queue is empty.
|
||||||
@@ -92,8 +96,8 @@ func (q *commitQueue) dequeue() *Batch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
slot := &q.slots[tail&uint32(len(q.slots)-1)]
|
slot := &q.slots[tail&uint32(len(q.slots)-1)]
|
||||||
b := (*Batch)(atomic.LoadPointer(slot))
|
b := slot.Load()
|
||||||
if b == nil || atomic.LoadUint32(&b.applied) == 0 {
|
if b == nil || !b.applied.Load() {
|
||||||
// The batch is not ready to be dequeued, or another goroutine has
|
// The batch is not ready to be dequeued, or another goroutine has
|
||||||
// already dequeued it.
|
// already dequeued it.
|
||||||
return nil
|
return nil
|
||||||
@@ -102,13 +106,13 @@ func (q *commitQueue) dequeue() *Batch {
|
|||||||
// Confirm head and tail (for our speculative check above) and increment
|
// Confirm head and tail (for our speculative check above) and increment
|
||||||
// tail. If this succeeds, then we own the slot at tail.
|
// tail. If this succeeds, then we own the slot at tail.
|
||||||
ptrs2 := q.pack(head, tail+1)
|
ptrs2 := q.pack(head, tail+1)
|
||||||
if atomic.CompareAndSwapUint64(&q.headTail, ptrs, ptrs2) {
|
if q.headTail.CompareAndSwap(ptrs, ptrs2) {
|
||||||
// We now own slot.
|
// We now own slot.
|
||||||
//
|
//
|
||||||
// Tell enqueue that we're done with this slot. Zeroing the slot is also
|
// Tell enqueue that we're done with this slot. Zeroing the slot is also
|
||||||
// important so we don't leave behind references that could keep this object
|
// important so we don't leave behind references that could keep this object
|
||||||
// live longer than necessary.
|
// live longer than necessary.
|
||||||
atomic.StorePointer(slot, nil)
|
slot.Store(nil)
|
||||||
// At this point enqueue owns the slot.
|
// At this point enqueue owns the slot.
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
@@ -121,10 +125,10 @@ func (q *commitQueue) dequeue() *Batch {
|
|||||||
type commitEnv struct {
|
type commitEnv struct {
|
||||||
// The next sequence number to give to a batch. Protected by
|
// The next sequence number to give to a batch. Protected by
|
||||||
// commitPipeline.mu.
|
// commitPipeline.mu.
|
||||||
logSeqNum *uint64
|
logSeqNum *atomic.Uint64
|
||||||
// The visible sequence number at which reads should be performed. Ratcheted
|
// The visible sequence number at which reads should be performed. Ratcheted
|
||||||
// upwards atomically as batches are applied to the memtable.
|
// upwards atomically as batches are applied to the memtable.
|
||||||
visibleSeqNum *uint64
|
visibleSeqNum *atomic.Uint64
|
||||||
|
|
||||||
// Apply the batch to the specified memtable. Called concurrently.
|
// Apply the batch to the specified memtable. Called concurrently.
|
||||||
apply func(b *Batch, mem *memTable) error
|
apply func(b *Batch, mem *memTable) error
|
||||||
@@ -140,8 +144,8 @@ type commitEnv struct {
|
|||||||
// (contained in a single Batch) atomically to the DB. The steps are
|
// (contained in a single Batch) atomically to the DB. The steps are
|
||||||
// conceptually:
|
// conceptually:
|
||||||
//
|
//
|
||||||
// 1. Write the batch to the WAL and optionally sync the WAL
|
// 1. Write the batch to the WAL and optionally sync the WAL
|
||||||
// 2. Apply the mutations in the batch to the memtable
|
// 2. Apply the mutations in the batch to the memtable
|
||||||
//
|
//
|
||||||
// These two simple steps are made complicated by the desire for high
|
// These two simple steps are made complicated by the desire for high
|
||||||
// performance. In the absence of concurrency, performance is limited by how
|
// performance. In the absence of concurrency, performance is limited by how
|
||||||
@@ -150,10 +154,10 @@ type commitEnv struct {
|
|||||||
// pipeline. Performance under concurrency is the primary concern of the commit
|
// pipeline. Performance under concurrency is the primary concern of the commit
|
||||||
// pipeline, though it also needs to maintain two invariants:
|
// pipeline, though it also needs to maintain two invariants:
|
||||||
//
|
//
|
||||||
// 1. Batches need to be written to the WAL in sequence number order.
|
// 1. Batches need to be written to the WAL in sequence number order.
|
||||||
// 2. Batches need to be made visible for reads in sequence number order. This
|
// 2. Batches need to be made visible for reads in sequence number order. This
|
||||||
// invariant arises from the use of a single sequence number which
|
// invariant arises from the use of a single sequence number which
|
||||||
// indicates which mutations are visible.
|
// indicates which mutations are visible.
|
||||||
//
|
//
|
||||||
// Taking these invariants into account, let's revisit the work the commit
|
// Taking these invariants into account, let's revisit the work the commit
|
||||||
// pipeline needs to perform. Writing the batch to the WAL is necessarily
|
// pipeline needs to perform. Writing the batch to the WAL is necessarily
|
||||||
@@ -173,14 +177,14 @@ type commitEnv struct {
|
|||||||
//
|
//
|
||||||
// The full outline of the commit pipeline operation is as follows:
|
// The full outline of the commit pipeline operation is as follows:
|
||||||
//
|
//
|
||||||
// with commitPipeline mutex locked:
|
// with commitPipeline mutex locked:
|
||||||
// assign batch sequence number
|
// assign batch sequence number
|
||||||
// write batch to WAL
|
// write batch to WAL
|
||||||
// (optionally) add batch to WAL sync list
|
// (optionally) add batch to WAL sync list
|
||||||
// apply batch to memtable (concurrently)
|
// apply batch to memtable (concurrently)
|
||||||
// wait for earlier batches to apply
|
// wait for earlier batches to apply
|
||||||
// ratchet read sequence number
|
// ratchet read sequence number
|
||||||
// (optionally) wait for the WAL to sync
|
// (optionally) wait for the WAL to sync
|
||||||
//
|
//
|
||||||
// As soon as a batch has been written to the WAL, the commitPipeline mutex is
|
// As soon as a batch has been written to the WAL, the commitPipeline mutex is
|
||||||
// released allowing another batch to write to the WAL. Each commit operation
|
// released allowing another batch to write to the WAL. Each commit operation
|
||||||
@@ -217,7 +221,28 @@ type commitPipeline struct {
|
|||||||
// Queue of pending batches to commit.
|
// Queue of pending batches to commit.
|
||||||
pending commitQueue
|
pending commitQueue
|
||||||
env commitEnv
|
env commitEnv
|
||||||
sem chan struct{}
|
// The commit path has two queues:
|
||||||
|
// - commitPipeline.pending contains batches whose seqnums have not yet been
|
||||||
|
// published. It is a lock-free single producer multi consumer queue.
|
||||||
|
// - LogWriter.flusher.syncQ contains state for batches that have asked for
|
||||||
|
// a sync. It is a lock-free single producer single consumer queue.
|
||||||
|
// These lock-free queues have a fixed capacity. And since they are
|
||||||
|
// lock-free, we cannot do blocking waits when pushing onto these queues, in
|
||||||
|
// case they are full. Additionally, adding to these queues happens while
|
||||||
|
// holding commitPipeline.mu, and we don't want to block while holding that
|
||||||
|
// mutex since it is also needed by other code.
|
||||||
|
//
|
||||||
|
// Popping from these queues is independent and for a particular batch can
|
||||||
|
// occur in either order, though it is more common that popping from the
|
||||||
|
// commitPipeline.pending will happen first.
|
||||||
|
//
|
||||||
|
// Due to these constraints, we reserve a unit of space in each queue before
|
||||||
|
// acquiring commitPipeline.mu, which also ensures that the push operation
|
||||||
|
// is guaranteed to have space in the queue. The commitQueueSem and
|
||||||
|
// logSyncQSem are used for this reservation.
|
||||||
|
commitQueueSem chan struct{}
|
||||||
|
logSyncQSem chan struct{}
|
||||||
|
ingestSem chan struct{}
|
||||||
// The mutex to use for synchronizing access to logSeqNum and serializing
|
// The mutex to use for synchronizing access to logSeqNum and serializing
|
||||||
// calls to commitEnv.write().
|
// calls to commitEnv.write().
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
@@ -226,23 +251,60 @@ type commitPipeline struct {
|
|||||||
func newCommitPipeline(env commitEnv) *commitPipeline {
|
func newCommitPipeline(env commitEnv) *commitPipeline {
|
||||||
p := &commitPipeline{
|
p := &commitPipeline{
|
||||||
env: env,
|
env: env,
|
||||||
|
// The capacity of both commitQueue.slots and syncQueue.slots is set to
|
||||||
|
// record.SyncConcurrency, which also determines the value of these
|
||||||
|
// semaphores. We used to have a single semaphore, which required that the
|
||||||
|
// capacity of these queues be the same. Now that we have two semaphores,
|
||||||
|
// the capacity of these queues could be changed to be different. Say half
|
||||||
|
// of the batches asked to be synced, but syncing took 5x the latency of
|
||||||
|
// adding to the memtable and publishing. Then syncQueue.slots could be
|
||||||
|
// sized as 0.5*5 of the commitQueue.slots. We can explore this if we find
|
||||||
|
// that LogWriterMetrics.SyncQueueLen has high utilization under some
|
||||||
|
// workloads.
|
||||||
|
//
|
||||||
// NB: the commit concurrency is one less than SyncConcurrency because we
|
// NB: the commit concurrency is one less than SyncConcurrency because we
|
||||||
// have to allow one "slot" for a concurrent WAL rotation which will close
|
// have to allow one "slot" for a concurrent WAL rotation which will close
|
||||||
// and sync the WAL.
|
// and sync the WAL.
|
||||||
sem: make(chan struct{}, record.SyncConcurrency-1),
|
commitQueueSem: make(chan struct{}, record.SyncConcurrency-1),
|
||||||
|
logSyncQSem: make(chan struct{}, record.SyncConcurrency-1),
|
||||||
|
ingestSem: make(chan struct{}, 1),
|
||||||
}
|
}
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// directWrite is used to directly write to the WAL. commitPipeline.mu must be
|
||||||
|
// held while this is called. DB.mu must not be held. directWrite will only
|
||||||
|
// return once the WAL sync is complete. Note that DirectWrite is a special case
|
||||||
|
// function which is currently only used when ingesting sstables as a flushable.
|
||||||
|
// Reason carefully about the correctness argument when calling this function
|
||||||
|
// from any context.
|
||||||
|
func (p *commitPipeline) directWrite(b *Batch) error {
|
||||||
|
var syncWG sync.WaitGroup
|
||||||
|
var syncErr error
|
||||||
|
syncWG.Add(1)
|
||||||
|
p.logSyncQSem <- struct{}{}
|
||||||
|
_, err := p.env.write(b, &syncWG, &syncErr)
|
||||||
|
syncWG.Wait()
|
||||||
|
err = firstError(err, syncErr)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Commit the specified batch, writing it to the WAL, optionally syncing the
|
// Commit the specified batch, writing it to the WAL, optionally syncing the
|
||||||
// WAL, and applying the batch to the memtable. Upon successful return the
|
// WAL, and applying the batch to the memtable. Upon successful return the
|
||||||
// batch's mutations will be visible for reading.
|
// batch's mutations will be visible for reading.
|
||||||
func (p *commitPipeline) Commit(b *Batch, syncWAL bool) error {
|
// REQUIRES: noSyncWait => syncWAL
|
||||||
|
func (p *commitPipeline) Commit(b *Batch, syncWAL bool, noSyncWait bool) error {
|
||||||
if b.Empty() {
|
if b.Empty() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
p.sem <- struct{}{}
|
commitStartTime := time.Now()
|
||||||
|
// Acquire semaphores.
|
||||||
|
p.commitQueueSem <- struct{}{}
|
||||||
|
if syncWAL {
|
||||||
|
p.logSyncQSem <- struct{}{}
|
||||||
|
}
|
||||||
|
b.commitStats.SemaphoreWaitDuration = time.Since(commitStartTime)
|
||||||
|
|
||||||
// Prepare the batch for committing: enqueuing the batch in the pending
|
// Prepare the batch for committing: enqueuing the batch in the pending
|
||||||
// queue, determining the batch sequence number and writing the data to the
|
// queue, determining the batch sequence number and writing the data to the
|
||||||
@@ -250,27 +312,43 @@ func (p *commitPipeline) Commit(b *Batch, syncWAL bool) error {
|
|||||||
//
|
//
|
||||||
// NB: We set Batch.commitErr on error so that the batch won't be a candidate
|
// NB: We set Batch.commitErr on error so that the batch won't be a candidate
|
||||||
// for reuse. See Batch.release().
|
// for reuse. See Batch.release().
|
||||||
mem, err := p.prepare(b, syncWAL)
|
mem, err := p.prepare(b, syncWAL, noSyncWait)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.db = nil // prevent batch reuse on error
|
b.db = nil // prevent batch reuse on error
|
||||||
|
// NB: we are not doing <-p.commitQueueSem since the batch is still
|
||||||
|
// sitting in the pending queue. We should consider fixing this by also
|
||||||
|
// removing the batch from the pending queue.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the batch to the memtable.
|
// Apply the batch to the memtable.
|
||||||
if err := p.env.apply(b, mem); err != nil {
|
if err := p.env.apply(b, mem); err != nil {
|
||||||
b.db = nil // prevent batch reuse on error
|
b.db = nil // prevent batch reuse on error
|
||||||
|
// NB: we are not doing <-p.commitQueueSem since the batch is still
|
||||||
|
// sitting in the pending queue. We should consider fixing this by also
|
||||||
|
// removing the batch from the pending queue.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publish the batch sequence number.
|
// Publish the batch sequence number.
|
||||||
p.publish(b)
|
p.publish(b)
|
||||||
|
|
||||||
<-p.sem
|
<-p.commitQueueSem
|
||||||
|
|
||||||
if b.commitErr != nil {
|
if !noSyncWait {
|
||||||
b.db = nil // prevent batch reuse on error
|
// Already waited for commit, so look at the error.
|
||||||
|
if b.commitErr != nil {
|
||||||
|
b.db = nil // prevent batch reuse on error
|
||||||
|
err = b.commitErr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return b.commitErr
|
// Else noSyncWait. The LogWriter can be concurrently writing to
|
||||||
|
// b.commitErr. We will read b.commitErr in Batch.SyncWait after the
|
||||||
|
// LogWriter is done writing.
|
||||||
|
|
||||||
|
b.commitStats.TotalDuration = time.Since(commitStartTime)
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllocateSeqNum allocates count sequence numbers, invokes the prepare
|
// AllocateSeqNum allocates count sequence numbers, invokes the prepare
|
||||||
@@ -280,7 +358,9 @@ func (p *commitPipeline) Commit(b *Batch, syncWAL bool) error {
|
|||||||
// sstable ingestion within the commit pipeline. The prepare callback is
|
// sstable ingestion within the commit pipeline. The prepare callback is
|
||||||
// invoked with commitPipeline.mu held, but note that DB.mu is not held and
|
// invoked with commitPipeline.mu held, but note that DB.mu is not held and
|
||||||
// must be locked if necessary.
|
// must be locked if necessary.
|
||||||
func (p *commitPipeline) AllocateSeqNum(count int, prepare func(), apply func(seqNum uint64)) {
|
func (p *commitPipeline) AllocateSeqNum(
|
||||||
|
count int, prepare func(seqNum uint64), apply func(seqNum uint64),
|
||||||
|
) {
|
||||||
// This method is similar to Commit and prepare. Be careful about trying to
|
// This method is similar to Commit and prepare. Be careful about trying to
|
||||||
// share additional code with those methods because Commit and prepare are
|
// share additional code with those methods because Commit and prepare are
|
||||||
// performance critical code paths.
|
// performance critical code paths.
|
||||||
@@ -294,7 +374,7 @@ func (p *commitPipeline) AllocateSeqNum(count int, prepare func(), apply func(se
|
|||||||
b.setCount(uint32(count))
|
b.setCount(uint32(count))
|
||||||
b.commit.Add(1)
|
b.commit.Add(1)
|
||||||
|
|
||||||
p.sem <- struct{}{}
|
p.commitQueueSem <- struct{}{}
|
||||||
|
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
|
|
||||||
@@ -306,12 +386,12 @@ func (p *commitPipeline) AllocateSeqNum(count int, prepare func(), apply func(se
|
|||||||
// Assign the batch a sequence number. Note that we use atomic operations
|
// Assign the batch a sequence number. Note that we use atomic operations
|
||||||
// here to handle concurrent reads of logSeqNum. commitPipeline.mu provides
|
// here to handle concurrent reads of logSeqNum. commitPipeline.mu provides
|
||||||
// mutual exclusion for other goroutines writing to logSeqNum.
|
// mutual exclusion for other goroutines writing to logSeqNum.
|
||||||
logSeqNum := atomic.AddUint64(p.env.logSeqNum, uint64(count)) - uint64(count)
|
logSeqNum := p.env.logSeqNum.Add(uint64(count)) - uint64(count)
|
||||||
seqNum := logSeqNum
|
seqNum := logSeqNum
|
||||||
if seqNum == 0 {
|
if seqNum == 0 {
|
||||||
// We can't use the value 0 for the global seqnum during ingestion, because
|
// We can't use the value 0 for the global seqnum during ingestion, because
|
||||||
// 0 indicates no global seqnum. So allocate one more seqnum.
|
// 0 indicates no global seqnum. So allocate one more seqnum.
|
||||||
atomic.AddUint64(p.env.logSeqNum, 1)
|
p.env.logSeqNum.Add(1)
|
||||||
seqNum++
|
seqNum++
|
||||||
}
|
}
|
||||||
b.setSeqNum(seqNum)
|
b.setSeqNum(seqNum)
|
||||||
@@ -321,7 +401,7 @@ func (p *commitPipeline) AllocateSeqNum(count int, prepare func(), apply func(se
|
|||||||
// writes that were sequenced before the ingestion. The spin loop is
|
// writes that were sequenced before the ingestion. The spin loop is
|
||||||
// unfortunate, but obviates the need for additional synchronization.
|
// unfortunate, but obviates the need for additional synchronization.
|
||||||
for {
|
for {
|
||||||
visibleSeqNum := atomic.LoadUint64(p.env.visibleSeqNum)
|
visibleSeqNum := p.env.visibleSeqNum.Load()
|
||||||
if visibleSeqNum == logSeqNum {
|
if visibleSeqNum == logSeqNum {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -331,7 +411,7 @@ func (p *commitPipeline) AllocateSeqNum(count int, prepare func(), apply func(se
|
|||||||
// Invoke the prepare callback. Note the lack of error reporting. Even if the
|
// Invoke the prepare callback. Note the lack of error reporting. Even if the
|
||||||
// callback internally fails, the sequence number needs to be published in
|
// callback internally fails, the sequence number needs to be published in
|
||||||
// order to allow the commit pipeline to proceed.
|
// order to allow the commit pipeline to proceed.
|
||||||
prepare()
|
prepare(b.SeqNum())
|
||||||
|
|
||||||
p.mu.Unlock()
|
p.mu.Unlock()
|
||||||
|
|
||||||
@@ -341,26 +421,33 @@ func (p *commitPipeline) AllocateSeqNum(count int, prepare func(), apply func(se
|
|||||||
// Publish the sequence number.
|
// Publish the sequence number.
|
||||||
p.publish(b)
|
p.publish(b)
|
||||||
|
|
||||||
<-p.sem
|
<-p.commitQueueSem
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *commitPipeline) prepare(b *Batch, syncWAL bool) (*memTable, error) {
|
func (p *commitPipeline) prepare(b *Batch, syncWAL bool, noSyncWait bool) (*memTable, error) {
|
||||||
n := uint64(b.Count())
|
n := uint64(b.Count())
|
||||||
if n == invalidBatchCount {
|
if n == invalidBatchCount {
|
||||||
return nil, ErrInvalidBatch
|
return nil, ErrInvalidBatch
|
||||||
}
|
}
|
||||||
count := 1
|
|
||||||
if syncWAL {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
// count represents the waiting needed for publish, and optionally the
|
|
||||||
// waiting needed for the WAL sync.
|
|
||||||
b.commit.Add(count)
|
|
||||||
|
|
||||||
var syncWG *sync.WaitGroup
|
var syncWG *sync.WaitGroup
|
||||||
var syncErr *error
|
var syncErr *error
|
||||||
if syncWAL {
|
switch {
|
||||||
syncWG, syncErr = &b.commit, &b.commitErr
|
case !syncWAL:
|
||||||
|
// Only need to wait for the publish.
|
||||||
|
b.commit.Add(1)
|
||||||
|
// Remaining cases represent syncWAL=true.
|
||||||
|
case noSyncWait:
|
||||||
|
syncErr = &b.commitErr
|
||||||
|
syncWG = &b.fsyncWait
|
||||||
|
// Only need to wait synchronously for the publish. The user will
|
||||||
|
// (asynchronously) wait on the batch's fsyncWait.
|
||||||
|
b.commit.Add(1)
|
||||||
|
b.fsyncWait.Add(1)
|
||||||
|
case !noSyncWait:
|
||||||
|
syncErr = &b.commitErr
|
||||||
|
syncWG = &b.commit
|
||||||
|
// Must wait for both the publish and the WAL fsync.
|
||||||
|
b.commit.Add(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
@@ -373,7 +460,7 @@ func (p *commitPipeline) prepare(b *Batch, syncWAL bool) (*memTable, error) {
|
|||||||
// Assign the batch a sequence number. Note that we use atomic operations
|
// Assign the batch a sequence number. Note that we use atomic operations
|
||||||
// here to handle concurrent reads of logSeqNum. commitPipeline.mu provides
|
// here to handle concurrent reads of logSeqNum. commitPipeline.mu provides
|
||||||
// mutual exclusion for other goroutines writing to logSeqNum.
|
// mutual exclusion for other goroutines writing to logSeqNum.
|
||||||
b.setSeqNum(atomic.AddUint64(p.env.logSeqNum, n) - n)
|
b.setSeqNum(p.env.logSeqNum.Add(n) - n)
|
||||||
|
|
||||||
// Write the data to the WAL.
|
// Write the data to the WAL.
|
||||||
mem, err := p.env.write(b, syncWG, syncErr)
|
mem, err := p.env.write(b, syncWG, syncErr)
|
||||||
@@ -385,24 +472,26 @@ func (p *commitPipeline) prepare(b *Batch, syncWAL bool) (*memTable, error) {
|
|||||||
|
|
||||||
func (p *commitPipeline) publish(b *Batch) {
|
func (p *commitPipeline) publish(b *Batch) {
|
||||||
// Mark the batch as applied.
|
// Mark the batch as applied.
|
||||||
atomic.StoreUint32(&b.applied, 1)
|
b.applied.Store(true)
|
||||||
|
|
||||||
// Loop dequeuing applied batches from the pending queue. If our batch was
|
// Loop dequeuing applied batches from the pending queue. If our batch was
|
||||||
// the head of the pending queue we are guaranteed that either we'll publish
|
// the head of the pending queue we are guaranteed that either we'll publish
|
||||||
// it or someone else will dequeue and publish it. If our batch is not the
|
// it or someone else will dequeueApplied and publish it. If our batch is not the
|
||||||
// head of the queue then either we'll dequeue applied batches and reach our
|
// head of the queue then either we'll dequeueApplied applied batches and reach our
|
||||||
// batch or there is an unapplied batch blocking us. When that unapplied
|
// batch or there is an unapplied batch blocking us. When that unapplied
|
||||||
// batch applies it will go through the same process and publish our batch
|
// batch applies it will go through the same process and publish our batch
|
||||||
// for us.
|
// for us.
|
||||||
for {
|
for {
|
||||||
t := p.pending.dequeue()
|
t := p.pending.dequeueApplied()
|
||||||
if t == nil {
|
if t == nil {
|
||||||
// Wait for another goroutine to publish us. We might also be waiting for
|
// Wait for another goroutine to publish us. We might also be waiting for
|
||||||
// the WAL sync to finish.
|
// the WAL sync to finish.
|
||||||
|
now := time.Now()
|
||||||
b.commit.Wait()
|
b.commit.Wait()
|
||||||
|
b.commitStats.CommitWaitDuration += time.Since(now)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if atomic.LoadUint32(&t.applied) != 1 {
|
if !t.applied.Load() {
|
||||||
panic("not reached")
|
panic("not reached")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,13 +500,13 @@ func (p *commitPipeline) publish(b *Batch) {
|
|||||||
// number for a subsequent batch. That's ok as all we're guaranteeing is
|
// number for a subsequent batch. That's ok as all we're guaranteeing is
|
||||||
// that the sequence number ratchets up.
|
// that the sequence number ratchets up.
|
||||||
for {
|
for {
|
||||||
curSeqNum := atomic.LoadUint64(p.env.visibleSeqNum)
|
curSeqNum := p.env.visibleSeqNum.Load()
|
||||||
newSeqNum := t.SeqNum() + uint64(t.Count())
|
newSeqNum := t.SeqNum() + uint64(t.Count())
|
||||||
if newSeqNum <= curSeqNum {
|
if newSeqNum <= curSeqNum {
|
||||||
// t's sequence number has already been published.
|
// t's sequence number has already been published.
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if atomic.CompareAndSwapUint64(p.env.visibleSeqNum, curSeqNum, newSeqNum) {
|
if p.env.visibleSeqNum.CompareAndSwap(curSeqNum, newSeqNum) {
|
||||||
// We successfully published t's sequence number.
|
// We successfully published t's sequence number.
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -426,18 +515,3 @@ func (p *commitPipeline) publish(b *Batch) {
|
|||||||
t.commit.Done()
|
t.commit.Done()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ratchetSeqNum allocates and marks visible all sequence numbers less than
|
|
||||||
// but excluding `nextSeqNum`.
|
|
||||||
func (p *commitPipeline) ratchetSeqNum(nextSeqNum uint64) {
|
|
||||||
p.mu.Lock()
|
|
||||||
defer p.mu.Unlock()
|
|
||||||
|
|
||||||
logSeqNum := atomic.LoadUint64(p.env.logSeqNum)
|
|
||||||
if logSeqNum >= nextSeqNum {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
count := nextSeqNum - logSeqNum
|
|
||||||
_ = atomic.AddUint64(p.env.logSeqNum, uint64(count)) - uint64(count)
|
|
||||||
atomic.StoreUint64(p.env.visibleSeqNum, nextSeqNum)
|
|
||||||
}
|
|
||||||
|
|||||||
2181
vendor/github.com/cockroachdb/pebble/compaction.go
generated
vendored
2181
vendor/github.com/cockroachdb/pebble/compaction.go
generated
vendored
File diff suppressed because it is too large
Load Diff
1132
vendor/github.com/cockroachdb/pebble/compaction_iter.go
generated
vendored
1132
vendor/github.com/cockroachdb/pebble/compaction_iter.go
generated
vendored
File diff suppressed because it is too large
Load Diff
899
vendor/github.com/cockroachdb/pebble/compaction_picker.go
generated
vendored
899
vendor/github.com/cockroachdb/pebble/compaction_picker.go
generated
vendored
File diff suppressed because it is too large
Load Diff
1677
vendor/github.com/cockroachdb/pebble/db.go
generated
vendored
1677
vendor/github.com/cockroachdb/pebble/db.go
generated
vendored
File diff suppressed because it is too large
Load Diff
36
vendor/github.com/cockroachdb/pebble/error_iter.go
generated
vendored
36
vendor/github.com/cockroachdb/pebble/error_iter.go
generated
vendored
@@ -14,40 +14,44 @@ type errorIter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// errorIter implements the base.InternalIterator interface.
|
// errorIter implements the base.InternalIterator interface.
|
||||||
var _ internalIteratorWithStats = (*errorIter)(nil)
|
var _ internalIterator = (*errorIter)(nil)
|
||||||
|
|
||||||
func newErrorIter(err error) *errorIter {
|
func newErrorIter(err error) *errorIter {
|
||||||
return &errorIter{err: err}
|
return &errorIter{err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *errorIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, []byte) {
|
func (c *errorIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, base.LazyValue) {
|
||||||
return nil, nil
|
return nil, base.LazyValue{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *errorIter) SeekPrefixGE(
|
func (c *errorIter) SeekPrefixGE(
|
||||||
prefix, key []byte, flags base.SeekGEFlags,
|
prefix, key []byte, flags base.SeekGEFlags,
|
||||||
) (*base.InternalKey, []byte) {
|
) (*base.InternalKey, base.LazyValue) {
|
||||||
return nil, nil
|
return nil, base.LazyValue{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *errorIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, []byte) {
|
func (c *errorIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, base.LazyValue) {
|
||||||
return nil, nil
|
return nil, base.LazyValue{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *errorIter) First() (*InternalKey, []byte) {
|
func (c *errorIter) First() (*InternalKey, base.LazyValue) {
|
||||||
return nil, nil
|
return nil, base.LazyValue{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *errorIter) Last() (*InternalKey, []byte) {
|
func (c *errorIter) Last() (*InternalKey, base.LazyValue) {
|
||||||
return nil, nil
|
return nil, base.LazyValue{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *errorIter) Next() (*InternalKey, []byte) {
|
func (c *errorIter) Next() (*InternalKey, base.LazyValue) {
|
||||||
return nil, nil
|
return nil, base.LazyValue{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *errorIter) Prev() (*InternalKey, []byte) {
|
func (c *errorIter) Prev() (*InternalKey, base.LazyValue) {
|
||||||
return nil, nil
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *errorIter) NextPrefix([]byte) (*InternalKey, base.LazyValue) {
|
||||||
|
return nil, base.LazyValue{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *errorIter) Error() error {
|
func (c *errorIter) Error() error {
|
||||||
@@ -63,8 +67,6 @@ func (c *errorIter) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *errorIter) SetBounds(lower, upper []byte) {}
|
func (c *errorIter) SetBounds(lower, upper []byte) {}
|
||||||
func (c *errorIter) Stats() InternalIteratorStats { return InternalIteratorStats{} }
|
|
||||||
func (c *errorIter) ResetStats() {}
|
|
||||||
|
|
||||||
type errorKeyspanIter struct {
|
type errorKeyspanIter struct {
|
||||||
err error
|
err error
|
||||||
|
|||||||
173
vendor/github.com/cockroachdb/pebble/event.go
generated
vendored
173
vendor/github.com/cockroachdb/pebble/event.go
generated
vendored
@@ -5,11 +5,15 @@
|
|||||||
package pebble
|
package pebble
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/cockroachdb/errors"
|
||||||
"github.com/cockroachdb/pebble/internal/humanize"
|
"github.com/cockroachdb/pebble/internal/humanize"
|
||||||
|
"github.com/cockroachdb/pebble/internal/invariants"
|
||||||
"github.com/cockroachdb/pebble/internal/manifest"
|
"github.com/cockroachdb/pebble/internal/manifest"
|
||||||
|
"github.com/cockroachdb/pebble/vfs"
|
||||||
"github.com/cockroachdb/redact"
|
"github.com/cockroachdb/redact"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -35,10 +39,11 @@ func formatFileNums(tables []TableInfo) string {
|
|||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// LevelInfo contains info pertaining to a partificular level.
|
// LevelInfo contains info pertaining to a particular level.
|
||||||
type LevelInfo struct {
|
type LevelInfo struct {
|
||||||
Level int
|
Level int
|
||||||
Tables []TableInfo
|
Tables []TableInfo
|
||||||
|
Score float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i LevelInfo) String() string {
|
func (i LevelInfo) String() string {
|
||||||
@@ -47,8 +52,11 @@ func (i LevelInfo) String() string {
|
|||||||
|
|
||||||
// SafeFormat implements redact.SafeFormatter.
|
// SafeFormat implements redact.SafeFormatter.
|
||||||
func (i LevelInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
func (i LevelInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
||||||
w.Printf("L%d [%s] (%s)", redact.Safe(i.Level), redact.Safe(formatFileNums(i.Tables)),
|
w.Printf("L%d [%s] (%s) Score=%.2f",
|
||||||
redact.Safe(humanize.Uint64(tablesTotalSize(i.Tables))))
|
redact.Safe(i.Level),
|
||||||
|
redact.Safe(formatFileNums(i.Tables)),
|
||||||
|
redact.Safe(humanize.Bytes.Uint64(tablesTotalSize(i.Tables))),
|
||||||
|
redact.Safe(i.Score))
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompactionInfo contains the info for a compaction event.
|
// CompactionInfo contains the info for a compaction event.
|
||||||
@@ -71,6 +79,27 @@ type CompactionInfo struct {
|
|||||||
TotalDuration time.Duration
|
TotalDuration time.Duration
|
||||||
Done bool
|
Done bool
|
||||||
Err error
|
Err error
|
||||||
|
|
||||||
|
SingleLevelOverlappingRatio float64
|
||||||
|
MultiLevelOverlappingRatio float64
|
||||||
|
|
||||||
|
// Annotations specifies additional info to appear in a compaction's event log line
|
||||||
|
Annotations compactionAnnotations
|
||||||
|
}
|
||||||
|
|
||||||
|
type compactionAnnotations []string
|
||||||
|
|
||||||
|
// SafeFormat implements redact.SafeFormatter.
|
||||||
|
func (ca compactionAnnotations) SafeFormat(w redact.SafePrinter, _ rune) {
|
||||||
|
if len(ca) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := range ca {
|
||||||
|
if i != 0 {
|
||||||
|
w.Print(" ")
|
||||||
|
}
|
||||||
|
w.Printf("%s", redact.SafeString(ca[i]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i CompactionInfo) String() string {
|
func (i CompactionInfo) String() string {
|
||||||
@@ -86,20 +115,25 @@ func (i CompactionInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !i.Done {
|
if !i.Done {
|
||||||
w.Printf("[JOB %d] compacting(%s) ", redact.Safe(i.JobID), redact.SafeString(i.Reason))
|
w.Printf("[JOB %d] compacting(%s) ",
|
||||||
w.Print(levelInfos(i.Input))
|
redact.Safe(i.JobID),
|
||||||
|
redact.SafeString(i.Reason))
|
||||||
|
w.Printf("%s", i.Annotations)
|
||||||
|
w.Printf("%s; ", levelInfos(i.Input))
|
||||||
|
w.Printf("OverlappingRatio: Single %.2f, Multi %.2f", i.SingleLevelOverlappingRatio, i.MultiLevelOverlappingRatio)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
outputSize := tablesTotalSize(i.Output.Tables)
|
outputSize := tablesTotalSize(i.Output.Tables)
|
||||||
w.Printf("[JOB %d] compacted(%s) ", redact.Safe(i.JobID), redact.SafeString(i.Reason))
|
w.Printf("[JOB %d] compacted(%s) ", redact.Safe(i.JobID), redact.SafeString(i.Reason))
|
||||||
|
w.Printf("%s", i.Annotations)
|
||||||
w.Print(levelInfos(i.Input))
|
w.Print(levelInfos(i.Input))
|
||||||
w.Printf(" -> L%d [%s] (%s), in %.1fs (%.1fs total), output rate %s/s",
|
w.Printf(" -> L%d [%s] (%s), in %.1fs (%.1fs total), output rate %s/s",
|
||||||
redact.Safe(i.Output.Level),
|
redact.Safe(i.Output.Level),
|
||||||
redact.Safe(formatFileNums(i.Output.Tables)),
|
redact.Safe(formatFileNums(i.Output.Tables)),
|
||||||
redact.Safe(humanize.Uint64(outputSize)),
|
redact.Safe(humanize.Bytes.Uint64(outputSize)),
|
||||||
redact.Safe(i.Duration.Seconds()),
|
redact.Safe(i.Duration.Seconds()),
|
||||||
redact.Safe(i.TotalDuration.Seconds()),
|
redact.Safe(i.TotalDuration.Seconds()),
|
||||||
redact.Safe(humanize.Uint64(uint64(float64(outputSize)/i.Duration.Seconds()))))
|
redact.Safe(humanize.Bytes.Uint64(uint64(float64(outputSize)/i.Duration.Seconds()))))
|
||||||
}
|
}
|
||||||
|
|
||||||
type levelInfos []LevelInfo
|
type levelInfos []LevelInfo
|
||||||
@@ -115,22 +149,7 @@ func (i levelInfos) SafeFormat(w redact.SafePrinter, _ rune) {
|
|||||||
|
|
||||||
// DiskSlowInfo contains the info for a disk slowness event when writing to a
|
// DiskSlowInfo contains the info for a disk slowness event when writing to a
|
||||||
// file.
|
// file.
|
||||||
type DiskSlowInfo struct {
|
type DiskSlowInfo = vfs.DiskSlowInfo
|
||||||
// Path of file being written to.
|
|
||||||
Path string
|
|
||||||
// Duration that has elapsed since this disk operation started.
|
|
||||||
Duration time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i DiskSlowInfo) String() string {
|
|
||||||
return redact.StringWithoutMarkers(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SafeFormat implements redact.SafeFormatter.
|
|
||||||
func (i DiskSlowInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
|
||||||
w.Printf("disk slowness detected: write to file %s has been ongoing for %0.1fs",
|
|
||||||
i.Path, redact.Safe(i.Duration.Seconds()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlushInfo contains the info for a flush event.
|
// FlushInfo contains the info for a flush event.
|
||||||
type FlushInfo struct {
|
type FlushInfo struct {
|
||||||
@@ -140,6 +159,9 @@ type FlushInfo struct {
|
|||||||
Reason string
|
Reason string
|
||||||
// Input contains the count of input memtables that were flushed.
|
// Input contains the count of input memtables that were flushed.
|
||||||
Input int
|
Input int
|
||||||
|
// InputBytes contains the total in-memory size of the memtable(s) that were
|
||||||
|
// flushed. This size includes skiplist indexing data structures.
|
||||||
|
InputBytes uint64
|
||||||
// Output contains the ouptut table generated by the flush. The output info
|
// Output contains the ouptut table generated by the flush. The output info
|
||||||
// is empty for the flush begin event.
|
// is empty for the flush begin event.
|
||||||
Output []TableInfo
|
Output []TableInfo
|
||||||
@@ -149,8 +171,14 @@ type FlushInfo struct {
|
|||||||
// TotalDuration is the total wall-time duration of the flush, including
|
// TotalDuration is the total wall-time duration of the flush, including
|
||||||
// applying the flush to the database. TotalDuration is always ≥ Duration.
|
// applying the flush to the database. TotalDuration is always ≥ Duration.
|
||||||
TotalDuration time.Duration
|
TotalDuration time.Duration
|
||||||
Done bool
|
// Ingest is set to true if the flush is handling tables that were added to
|
||||||
Err error
|
// the flushable queue via an ingestion operation.
|
||||||
|
Ingest bool
|
||||||
|
// IngestLevels are the output levels for each ingested table in the flush.
|
||||||
|
// This field is only populated when Ingest is true.
|
||||||
|
IngestLevels []int
|
||||||
|
Done bool
|
||||||
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i FlushInfo) String() string {
|
func (i FlushInfo) String() string {
|
||||||
@@ -169,20 +197,48 @@ func (i FlushInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
|||||||
plural = ""
|
plural = ""
|
||||||
}
|
}
|
||||||
if !i.Done {
|
if !i.Done {
|
||||||
w.Printf("[JOB %d] flushing %d memtable", redact.Safe(i.JobID), redact.Safe(i.Input))
|
w.Printf("[JOB %d] ", redact.Safe(i.JobID))
|
||||||
w.SafeString(plural)
|
if !i.Ingest {
|
||||||
w.Printf(" to L0")
|
w.Printf("flushing %d memtable", redact.Safe(i.Input))
|
||||||
|
w.SafeString(plural)
|
||||||
|
w.Printf(" (%s) to L0", redact.Safe(humanize.Bytes.Uint64(i.InputBytes)))
|
||||||
|
} else {
|
||||||
|
w.Printf("flushing %d ingested table%s", redact.Safe(i.Input), plural)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
outputSize := tablesTotalSize(i.Output)
|
outputSize := tablesTotalSize(i.Output)
|
||||||
w.Printf("[JOB %d] flushed %d memtable%s to L0 [%s] (%s), in %.1fs (%.1fs total), output rate %s/s",
|
if !i.Ingest {
|
||||||
redact.Safe(i.JobID), redact.Safe(i.Input), plural,
|
if invariants.Enabled && len(i.IngestLevels) > 0 {
|
||||||
redact.Safe(formatFileNums(i.Output)),
|
panic(errors.AssertionFailedf("pebble: expected len(IngestedLevels) == 0"))
|
||||||
redact.Safe(humanize.Uint64(outputSize)),
|
}
|
||||||
redact.Safe(i.Duration.Seconds()),
|
w.Printf("[JOB %d] flushed %d memtable%s (%s) to L0 [%s] (%s), in %.1fs (%.1fs total), output rate %s/s",
|
||||||
redact.Safe(i.TotalDuration.Seconds()),
|
redact.Safe(i.JobID), redact.Safe(i.Input), plural,
|
||||||
redact.Safe(humanize.Uint64(uint64(float64(outputSize)/i.Duration.Seconds()))))
|
redact.Safe(humanize.Bytes.Uint64(i.InputBytes)),
|
||||||
|
redact.Safe(formatFileNums(i.Output)),
|
||||||
|
redact.Safe(humanize.Bytes.Uint64(outputSize)),
|
||||||
|
redact.Safe(i.Duration.Seconds()),
|
||||||
|
redact.Safe(i.TotalDuration.Seconds()),
|
||||||
|
redact.Safe(humanize.Bytes.Uint64(uint64(float64(outputSize)/i.Duration.Seconds()))))
|
||||||
|
} else {
|
||||||
|
if invariants.Enabled && len(i.IngestLevels) == 0 {
|
||||||
|
panic(errors.AssertionFailedf("pebble: expected len(IngestedLevels) > 0"))
|
||||||
|
}
|
||||||
|
w.Printf("[JOB %d] flushed %d ingested flushable%s",
|
||||||
|
redact.Safe(i.JobID), redact.Safe(len(i.Output)), plural)
|
||||||
|
for j, level := range i.IngestLevels {
|
||||||
|
file := i.Output[j]
|
||||||
|
if j > 0 {
|
||||||
|
w.Printf(" +")
|
||||||
|
}
|
||||||
|
w.Printf(" L%d:%s (%s)", level, file.FileNum, humanize.Bytes.Uint64(file.Size))
|
||||||
|
}
|
||||||
|
w.Printf(" in %.1fs (%.1fs total), output rate %s/s",
|
||||||
|
redact.Safe(i.Duration.Seconds()),
|
||||||
|
redact.Safe(i.TotalDuration.Seconds()),
|
||||||
|
redact.Safe(humanize.Bytes.Uint64(uint64(float64(outputSize)/i.Duration.Seconds()))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManifestCreateInfo contains info about a manifest creation event.
|
// ManifestCreateInfo contains info about a manifest creation event.
|
||||||
@@ -205,7 +261,7 @@ func (i ManifestCreateInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
|||||||
w.Printf("[JOB %d] MANIFEST create error: %s", redact.Safe(i.JobID), i.Err)
|
w.Printf("[JOB %d] MANIFEST create error: %s", redact.Safe(i.JobID), i.Err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Printf("[JOB %d] MANIFEST created %s", redact.Safe(i.JobID), redact.Safe(i.FileNum))
|
w.Printf("[JOB %d] MANIFEST created %s", redact.Safe(i.JobID), i.FileNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManifestDeleteInfo contains the info for a Manifest deletion event.
|
// ManifestDeleteInfo contains the info for a Manifest deletion event.
|
||||||
@@ -227,7 +283,7 @@ func (i ManifestDeleteInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
|||||||
w.Printf("[JOB %d] MANIFEST delete error: %s", redact.Safe(i.JobID), i.Err)
|
w.Printf("[JOB %d] MANIFEST delete error: %s", redact.Safe(i.JobID), i.Err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Printf("[JOB %d] MANIFEST deleted %s", redact.Safe(i.JobID), redact.Safe(i.FileNum))
|
w.Printf("[JOB %d] MANIFEST deleted %s", redact.Safe(i.JobID), i.FileNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableCreateInfo contains the info for a table creation event.
|
// TableCreateInfo contains the info for a table creation event.
|
||||||
@@ -247,7 +303,7 @@ func (i TableCreateInfo) String() string {
|
|||||||
// SafeFormat implements redact.SafeFormatter.
|
// SafeFormat implements redact.SafeFormatter.
|
||||||
func (i TableCreateInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
func (i TableCreateInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
||||||
w.Printf("[JOB %d] %s: sstable created %s",
|
w.Printf("[JOB %d] %s: sstable created %s",
|
||||||
redact.Safe(i.JobID), redact.Safe(i.Reason), redact.Safe(i.FileNum))
|
redact.Safe(i.JobID), redact.Safe(i.Reason), i.FileNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableDeleteInfo contains the info for a table deletion event.
|
// TableDeleteInfo contains the info for a table deletion event.
|
||||||
@@ -266,10 +322,10 @@ func (i TableDeleteInfo) String() string {
|
|||||||
func (i TableDeleteInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
func (i TableDeleteInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
||||||
if i.Err != nil {
|
if i.Err != nil {
|
||||||
w.Printf("[JOB %d] sstable delete error %s: %s",
|
w.Printf("[JOB %d] sstable delete error %s: %s",
|
||||||
redact.Safe(i.JobID), redact.Safe(i.FileNum), i.Err)
|
redact.Safe(i.JobID), i.FileNum, i.Err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Printf("[JOB %d] sstable deleted %s", redact.Safe(i.JobID), redact.Safe(i.FileNum))
|
w.Printf("[JOB %d] sstable deleted %s", redact.Safe(i.JobID), i.FileNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableIngestInfo contains the info for a table ingestion event.
|
// TableIngestInfo contains the info for a table ingestion event.
|
||||||
@@ -283,7 +339,10 @@ type TableIngestInfo struct {
|
|||||||
// GlobalSeqNum is the sequence number that was assigned to all entries in
|
// GlobalSeqNum is the sequence number that was assigned to all entries in
|
||||||
// the ingested table.
|
// the ingested table.
|
||||||
GlobalSeqNum uint64
|
GlobalSeqNum uint64
|
||||||
Err error
|
// flushable indicates whether the ingested sstable was treated as a
|
||||||
|
// flushable.
|
||||||
|
flushable bool
|
||||||
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i TableIngestInfo) String() string {
|
func (i TableIngestInfo) String() string {
|
||||||
@@ -297,14 +356,23 @@ func (i TableIngestInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Printf("[JOB %d] ingested", redact.Safe(i.JobID))
|
if i.flushable {
|
||||||
|
w.Printf("[JOB %d] ingested as flushable", redact.Safe(i.JobID))
|
||||||
|
} else {
|
||||||
|
w.Printf("[JOB %d] ingested", redact.Safe(i.JobID))
|
||||||
|
}
|
||||||
|
|
||||||
for j := range i.Tables {
|
for j := range i.Tables {
|
||||||
t := &i.Tables[j]
|
t := &i.Tables[j]
|
||||||
if j > 0 {
|
if j > 0 {
|
||||||
w.Printf(",")
|
w.Printf(",")
|
||||||
}
|
}
|
||||||
w.Printf(" L%d:%s (%s)", redact.Safe(t.Level), redact.Safe(t.FileNum),
|
levelStr := ""
|
||||||
redact.Safe(humanize.Uint64(t.Size)))
|
if !i.flushable {
|
||||||
|
levelStr = fmt.Sprintf("L%d:", t.Level)
|
||||||
|
}
|
||||||
|
w.Printf(" %s%s (%s)", redact.Safe(levelStr), t.FileNum,
|
||||||
|
redact.Safe(humanize.Bytes.Uint64(t.Size)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,12 +433,12 @@ func (i WALCreateInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if i.RecycledFileNum == 0 {
|
if i.RecycledFileNum == 0 {
|
||||||
w.Printf("[JOB %d] WAL created %s", redact.Safe(i.JobID), redact.Safe(i.FileNum))
|
w.Printf("[JOB %d] WAL created %s", redact.Safe(i.JobID), i.FileNum)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Printf("[JOB %d] WAL created %s (recycled %s)",
|
w.Printf("[JOB %d] WAL created %s (recycled %s)",
|
||||||
redact.Safe(i.JobID), redact.Safe(i.FileNum), redact.Safe(i.RecycledFileNum))
|
redact.Safe(i.JobID), i.FileNum, i.RecycledFileNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WALDeleteInfo contains the info for a WAL deletion event.
|
// WALDeleteInfo contains the info for a WAL deletion event.
|
||||||
@@ -392,7 +460,7 @@ func (i WALDeleteInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
|||||||
w.Printf("[JOB %d] WAL delete error: %s", redact.Safe(i.JobID), i.Err)
|
w.Printf("[JOB %d] WAL delete error: %s", redact.Safe(i.JobID), i.Err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Printf("[JOB %d] WAL deleted %s", redact.Safe(i.JobID), redact.Safe(i.FileNum))
|
w.Printf("[JOB %d] WAL deleted %s", redact.Safe(i.JobID), i.FileNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteStallBeginInfo contains the info for a write stall begin event.
|
// WriteStallBeginInfo contains the info for a write stall begin event.
|
||||||
@@ -427,9 +495,14 @@ type EventListener struct {
|
|||||||
// has been installed.
|
// has been installed.
|
||||||
CompactionEnd func(CompactionInfo)
|
CompactionEnd func(CompactionInfo)
|
||||||
|
|
||||||
// DiskSlow is invoked after a disk write operation on a file created
|
// DiskSlow is invoked after a disk write operation on a file created with a
|
||||||
// with a disk health checking vfs.FS (see vfs.DefaultWithDiskHealthChecks)
|
// disk health checking vfs.FS (see vfs.DefaultWithDiskHealthChecks) is
|
||||||
// is observed to exceed the specified disk slowness threshold duration.
|
// observed to exceed the specified disk slowness threshold duration. DiskSlow
|
||||||
|
// is called on a goroutine that is monitoring slowness/stuckness. The callee
|
||||||
|
// MUST return without doing any IO, or blocking on anything (like a mutex)
|
||||||
|
// that is waiting on IO. This is imperative in order to reliably monitor for
|
||||||
|
// slowness, since if this goroutine gets stuck, the monitoring will stop
|
||||||
|
// working.
|
||||||
DiskSlow func(DiskSlowInfo)
|
DiskSlow func(DiskSlowInfo)
|
||||||
|
|
||||||
// FlushBegin is invoked after the inputs to a flush have been determined,
|
// FlushBegin is invoked after the inputs to a flush have been determined,
|
||||||
|
|||||||
472
vendor/github.com/cockroachdb/pebble/external_iterator.go
generated
vendored
472
vendor/github.com/cockroachdb/pebble/external_iterator.go
generated
vendored
@@ -5,6 +5,10 @@
|
|||||||
package pebble
|
package pebble
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
"github.com/cockroachdb/pebble/internal/base"
|
"github.com/cockroachdb/pebble/internal/base"
|
||||||
"github.com/cockroachdb/pebble/internal/keyspan"
|
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||||
@@ -12,12 +16,59 @@ import (
|
|||||||
"github.com/cockroachdb/pebble/sstable"
|
"github.com/cockroachdb/pebble/sstable"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewExternalIter takes an input set of sstable files which may overlap
|
// ExternalIterOption provide an interface to specify open-time options to
|
||||||
// arbitrarily and returns an Iterator over the merged contents of the sstables.
|
// NewExternalIter.
|
||||||
|
type ExternalIterOption interface {
|
||||||
|
// iterApply is called on the iterator during opening in order to set internal
|
||||||
|
// parameters.
|
||||||
|
iterApply(*Iterator)
|
||||||
|
// readerOptions returns any reader options added by this iter option.
|
||||||
|
readerOptions() []sstable.ReaderOption
|
||||||
|
}
|
||||||
|
|
||||||
|
type externalIterReaderOptions struct {
|
||||||
|
opts []sstable.ReaderOption
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *externalIterReaderOptions) iterApply(iterator *Iterator) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *externalIterReaderOptions) readerOptions() []sstable.ReaderOption {
|
||||||
|
return e.opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalIterReaderOptions returns an ExternalIterOption that specifies
|
||||||
|
// sstable.ReaderOptions to be applied on sstable readers in NewExternalIter.
|
||||||
|
func ExternalIterReaderOptions(opts ...sstable.ReaderOption) ExternalIterOption {
|
||||||
|
return &externalIterReaderOptions{opts: opts}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalIterForwardOnly is an ExternalIterOption that specifies this iterator
|
||||||
|
// will only be used for forward positioning operations (First, SeekGE, Next).
|
||||||
|
// This could enable optimizations that take advantage of this invariant.
|
||||||
|
// Behaviour when a reverse positioning operation is done on an iterator
|
||||||
|
// opened with this option is unpredictable, though in most cases it should.
|
||||||
|
type ExternalIterForwardOnly struct{}
|
||||||
|
|
||||||
|
func (e ExternalIterForwardOnly) iterApply(iter *Iterator) {
|
||||||
|
iter.forwardOnly = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ExternalIterForwardOnly) readerOptions() []sstable.ReaderOption {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewExternalIter takes an input 2d array of sstable files which may overlap
|
||||||
|
// across subarrays but not within a subarray (at least as far as points are
|
||||||
|
// concerned; range keys are allowed to overlap arbitrarily even within a
|
||||||
|
// subarray), and returns an Iterator over the merged contents of the sstables.
|
||||||
// Input sstables may contain point keys, range keys, range deletions, etc. The
|
// Input sstables may contain point keys, range keys, range deletions, etc. The
|
||||||
// input files slice must be sorted in reverse chronological ordering. A key in
|
// input files slice must be sorted in reverse chronological ordering. A key in a
|
||||||
// a file at a lower index will shadow a key with an identical user key
|
// file at a lower index subarray will shadow a key with an identical user key
|
||||||
// contained within a file at a higher index.
|
// contained within a file at a higher index subarray. Each subarray must be
|
||||||
|
// sorted in internal key order, where lower index files contain keys that sort
|
||||||
|
// left of files with higher indexes.
|
||||||
//
|
//
|
||||||
// Input sstables must only contain keys with the zero sequence number.
|
// Input sstables must only contain keys with the zero sequence number.
|
||||||
//
|
//
|
||||||
@@ -27,8 +78,20 @@ import (
|
|||||||
func NewExternalIter(
|
func NewExternalIter(
|
||||||
o *Options,
|
o *Options,
|
||||||
iterOpts *IterOptions,
|
iterOpts *IterOptions,
|
||||||
files []sstable.ReadableFile,
|
files [][]sstable.ReadableFile,
|
||||||
extraReaderOpts ...sstable.ReaderOption,
|
extraOpts ...ExternalIterOption,
|
||||||
|
) (it *Iterator, err error) {
|
||||||
|
return NewExternalIterWithContext(context.Background(), o, iterOpts, files, extraOpts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewExternalIterWithContext is like NewExternalIter, and additionally
|
||||||
|
// accepts a context for tracing.
|
||||||
|
func NewExternalIterWithContext(
|
||||||
|
ctx context.Context,
|
||||||
|
o *Options,
|
||||||
|
iterOpts *IterOptions,
|
||||||
|
files [][]sstable.ReadableFile,
|
||||||
|
extraOpts ...ExternalIterOption,
|
||||||
) (it *Iterator, err error) {
|
) (it *Iterator, err error) {
|
||||||
if iterOpts != nil {
|
if iterOpts != nil {
|
||||||
if err := validateExternalIterOpts(iterOpts); err != nil {
|
if err := validateExternalIterOpts(iterOpts); err != nil {
|
||||||
@@ -36,17 +99,32 @@ func NewExternalIter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var readers []*sstable.Reader
|
var readers [][]*sstable.Reader
|
||||||
|
|
||||||
// Ensure we close all the opened readers if we error out.
|
// Ensure we close all the opened readers if we error out.
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
for i := range readers {
|
for i := range readers {
|
||||||
_ = readers[i].Close()
|
for j := range readers[i] {
|
||||||
|
_ = readers[i][j].Close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
readers, err = openExternalTables(o, files, o.MakeReaderOptions(), extraReaderOpts...)
|
seqNumOffset := 0
|
||||||
|
var extraReaderOpts []sstable.ReaderOption
|
||||||
|
for i := range extraOpts {
|
||||||
|
extraReaderOpts = append(extraReaderOpts, extraOpts[i].readerOptions()...)
|
||||||
|
}
|
||||||
|
for _, levelFiles := range files {
|
||||||
|
seqNumOffset += len(levelFiles)
|
||||||
|
}
|
||||||
|
for _, levelFiles := range files {
|
||||||
|
var subReaders []*sstable.Reader
|
||||||
|
seqNumOffset -= len(levelFiles)
|
||||||
|
subReaders, err = openExternalTables(o, levelFiles, seqNumOffset, o.MakeReaderOptions(), extraReaderOpts...)
|
||||||
|
readers = append(readers, subReaders)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -54,11 +132,10 @@ func NewExternalIter(
|
|||||||
buf := iterAllocPool.Get().(*iterAlloc)
|
buf := iterAllocPool.Get().(*iterAlloc)
|
||||||
dbi := &buf.dbi
|
dbi := &buf.dbi
|
||||||
*dbi = Iterator{
|
*dbi = Iterator{
|
||||||
|
ctx: ctx,
|
||||||
alloc: buf,
|
alloc: buf,
|
||||||
cmp: o.Comparer.Compare,
|
|
||||||
equal: o.equal(),
|
|
||||||
merge: o.Merger.Merge,
|
merge: o.Merger.Merge,
|
||||||
split: o.Comparer.Split,
|
comparer: *o.Comparer,
|
||||||
readState: nil,
|
readState: nil,
|
||||||
keyBuf: buf.keyBuf,
|
keyBuf: buf.keyBuf,
|
||||||
prefixOrFullSeekKey: buf.prefixOrFullSeekKey,
|
prefixOrFullSeekKey: buf.prefixOrFullSeekKey,
|
||||||
@@ -67,7 +144,9 @@ func NewExternalIter(
|
|||||||
// Add the readers to the Iterator so that Close closes them, and
|
// Add the readers to the Iterator so that Close closes them, and
|
||||||
// SetOptions can re-construct iterators from them.
|
// SetOptions can re-construct iterators from them.
|
||||||
externalReaders: readers,
|
externalReaders: readers,
|
||||||
newIters: func(f *manifest.FileMetadata, opts *IterOptions, bytesIterated *uint64) (internalIterator, keyspan.FragmentIterator, error) {
|
newIters: func(
|
||||||
|
ctx context.Context, f *manifest.FileMetadata, opts *IterOptions,
|
||||||
|
internalOpts internalIterOpts) (internalIterator, keyspan.FragmentIterator, error) {
|
||||||
// NB: External iterators are currently constructed without any
|
// NB: External iterators are currently constructed without any
|
||||||
// `levelIters`. newIters should never be called. When we support
|
// `levelIters`. newIters should never be called. When we support
|
||||||
// organizing multiple non-overlapping files into a single level
|
// organizing multiple non-overlapping files into a single level
|
||||||
@@ -80,9 +159,12 @@ func NewExternalIter(
|
|||||||
}
|
}
|
||||||
if iterOpts != nil {
|
if iterOpts != nil {
|
||||||
dbi.opts = *iterOpts
|
dbi.opts = *iterOpts
|
||||||
dbi.saveBounds(iterOpts.LowerBound, iterOpts.UpperBound)
|
dbi.processBounds(iterOpts.LowerBound, iterOpts.UpperBound)
|
||||||
}
|
}
|
||||||
finishInitializingExternal(dbi)
|
for i := range extraOpts {
|
||||||
|
extraOpts[i].iterApply(dbi)
|
||||||
|
}
|
||||||
|
finishInitializingExternal(ctx, dbi)
|
||||||
return dbi, nil
|
return dbi, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,84 +184,372 @@ func validateExternalIterOpts(iterOpts *IterOptions) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func finishInitializingExternal(it *Iterator) {
|
func createExternalPointIter(ctx context.Context, it *Iterator) (internalIterator, error) {
|
||||||
// TODO(jackson): In some instances we could generate fewer levels by using
|
// TODO(jackson): In some instances we could generate fewer levels by using
|
||||||
// L0Sublevels code to organize nonoverlapping files into the same level.
|
// L0Sublevels code to organize nonoverlapping files into the same level.
|
||||||
// This would allow us to use levelIters and keep a smaller set of data and
|
// This would allow us to use levelIters and keep a smaller set of data and
|
||||||
// files in-memory. However, it would also require us to identify the bounds
|
// files in-memory. However, it would also require us to identify the bounds
|
||||||
// of all the files upfront.
|
// of all the files upfront.
|
||||||
|
|
||||||
mlevels := it.alloc.mlevels[:0]
|
|
||||||
if !it.opts.pointKeys() {
|
if !it.opts.pointKeys() {
|
||||||
it.pointIter = emptyIter
|
return emptyIter, nil
|
||||||
} else if it.pointIter == nil {
|
} else if it.pointIter != nil {
|
||||||
if len(it.externalReaders) > cap(mlevels) {
|
return it.pointIter, nil
|
||||||
mlevels = make([]mergingIterLevel, 0, len(it.externalReaders))
|
}
|
||||||
}
|
mlevels := it.alloc.mlevels[:0]
|
||||||
for _, r := range it.externalReaders {
|
|
||||||
|
if len(it.externalReaders) > cap(mlevels) {
|
||||||
|
mlevels = make([]mergingIterLevel, 0, len(it.externalReaders))
|
||||||
|
}
|
||||||
|
for _, readers := range it.externalReaders {
|
||||||
|
var combinedIters []internalIterator
|
||||||
|
for _, r := range readers {
|
||||||
var (
|
var (
|
||||||
rangeDelIter keyspan.FragmentIterator
|
rangeDelIter keyspan.FragmentIterator
|
||||||
pointIter internalIterator
|
pointIter internalIterator
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
pointIter, err = r.NewIter(it.opts.LowerBound, it.opts.UpperBound)
|
// We could set hideObsoletePoints=true, since we are reading at
|
||||||
if err == nil {
|
// InternalKeySeqNumMax, but we don't bother since these sstables should
|
||||||
rangeDelIter, err = r.NewRawRangeDelIter()
|
// not have obsolete points (so the performance optimization is
|
||||||
}
|
// unnecessary), and we don't want to bother constructing a
|
||||||
|
// BlockPropertiesFilterer that includes obsoleteKeyBlockPropertyFilter.
|
||||||
|
pointIter, err = r.NewIterWithBlockPropertyFiltersAndContextEtc(
|
||||||
|
ctx, it.opts.LowerBound, it.opts.UpperBound, nil, /* BlockPropertiesFilterer */
|
||||||
|
false /* hideObsoletePoints */, false, /* useFilterBlock */
|
||||||
|
&it.stats.InternalStats, sstable.TrivialReaderProvider{Reader: r})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pointIter = &errorIter{err: err}
|
return nil, err
|
||||||
rangeDelIter = &errorKeyspanIter{err: err}
|
}
|
||||||
|
rangeDelIter, err = r.NewRawRangeDelIter()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if rangeDelIter == nil && pointIter != nil && it.forwardOnly {
|
||||||
|
// TODO(bilal): Consider implementing range key pausing in
|
||||||
|
// simpleLevelIter so we can reduce mergingIterLevels even more by
|
||||||
|
// sending all sstable iterators to combinedIters, not just those
|
||||||
|
// corresponding to sstables without range deletes.
|
||||||
|
combinedIters = append(combinedIters, pointIter)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
mlevels = append(mlevels, mergingIterLevel{
|
mlevels = append(mlevels, mergingIterLevel{
|
||||||
iter: base.WrapIterWithStats(pointIter),
|
iter: pointIter,
|
||||||
rangeDelIter: rangeDelIter,
|
rangeDelIter: rangeDelIter,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
it.alloc.merging.init(&it.opts, it.cmp, it.split, mlevels...)
|
if len(combinedIters) == 1 {
|
||||||
it.alloc.merging.snapshot = base.InternalKeySeqNumMax
|
mlevels = append(mlevels, mergingIterLevel{
|
||||||
it.alloc.merging.elideRangeTombstones = true
|
iter: combinedIters[0],
|
||||||
it.pointIter = &it.alloc.merging
|
})
|
||||||
|
} else if len(combinedIters) > 1 {
|
||||||
|
sli := &simpleLevelIter{
|
||||||
|
cmp: it.cmp,
|
||||||
|
iters: combinedIters,
|
||||||
|
}
|
||||||
|
sli.init(it.opts)
|
||||||
|
mlevels = append(mlevels, mergingIterLevel{
|
||||||
|
iter: sli,
|
||||||
|
rangeDelIter: nil,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(mlevels) == 1 && mlevels[0].rangeDelIter == nil {
|
||||||
|
// Set closePointIterOnce to true. This is because we're bypassing the
|
||||||
|
// merging iter, which turns Close()s on it idempotent for any child
|
||||||
|
// iterators. The outer Iterator could call Close() on a point iter twice,
|
||||||
|
// which sstable iterators do not support (as they release themselves to
|
||||||
|
// a pool).
|
||||||
|
it.closePointIterOnce = true
|
||||||
|
return mlevels[0].iter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
it.alloc.merging.init(&it.opts, &it.stats.InternalStats, it.comparer.Compare, it.comparer.Split, mlevels...)
|
||||||
|
it.alloc.merging.snapshot = base.InternalKeySeqNumMax
|
||||||
|
if len(mlevels) <= cap(it.alloc.levelsPositioned) {
|
||||||
|
it.alloc.merging.levelsPositioned = it.alloc.levelsPositioned[:len(mlevels)]
|
||||||
|
}
|
||||||
|
return &it.alloc.merging, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func finishInitializingExternal(ctx context.Context, it *Iterator) {
|
||||||
|
pointIter, err := createExternalPointIter(ctx, it)
|
||||||
|
if err != nil {
|
||||||
|
it.pointIter = &errorIter{err: err}
|
||||||
|
} else {
|
||||||
|
it.pointIter = pointIter
|
||||||
}
|
}
|
||||||
it.iter = it.pointIter
|
it.iter = it.pointIter
|
||||||
|
|
||||||
if it.opts.rangeKeys() {
|
if it.opts.rangeKeys() {
|
||||||
|
it.rangeKeyMasking.init(it, it.comparer.Compare, it.comparer.Split)
|
||||||
|
var rangeKeyIters []keyspan.FragmentIterator
|
||||||
if it.rangeKey == nil {
|
if it.rangeKey == nil {
|
||||||
it.rangeKey = iterRangeKeyStateAllocPool.Get().(*iteratorRangeKeyState)
|
// We could take advantage of the lack of overlaps in range keys within
|
||||||
it.rangeKey.init(it.cmp, it.split, &it.opts)
|
// each slice in it.externalReaders, and generate keyspan.LevelIters
|
||||||
it.rangeKey.rangeKeyIter = it.rangeKey.iterConfig.Init(
|
// out of those. However, since range keys are expected to be sparse to
|
||||||
it.cmp,
|
// begin with, the performance gain might not be significant enough to
|
||||||
base.InternalKeySeqNumMax,
|
// warrant it.
|
||||||
)
|
//
|
||||||
for _, r := range it.externalReaders {
|
// TODO(bilal): Explore adding a simpleRangeKeyLevelIter that does not
|
||||||
if rki, err := r.NewRawRangeKeyIter(); err != nil {
|
// operate on FileMetadatas (similar to simpleLevelIter), and implements
|
||||||
it.rangeKey.iterConfig.AddLevel(&errorKeyspanIter{err: err})
|
// this optimization.
|
||||||
} else if rki != nil {
|
for _, readers := range it.externalReaders {
|
||||||
it.rangeKey.iterConfig.AddLevel(rki)
|
for _, r := range readers {
|
||||||
|
if rki, err := r.NewRawRangeKeyIter(); err != nil {
|
||||||
|
rangeKeyIters = append(rangeKeyIters, &errorKeyspanIter{err: err})
|
||||||
|
} else if rki != nil {
|
||||||
|
rangeKeyIters = append(rangeKeyIters, rki)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(rangeKeyIters) > 0 {
|
||||||
|
it.rangeKey = iterRangeKeyStateAllocPool.Get().(*iteratorRangeKeyState)
|
||||||
|
it.rangeKey.init(it.comparer.Compare, it.comparer.Split, &it.opts)
|
||||||
|
it.rangeKey.rangeKeyIter = it.rangeKey.iterConfig.Init(
|
||||||
|
&it.comparer,
|
||||||
|
base.InternalKeySeqNumMax,
|
||||||
|
it.opts.LowerBound, it.opts.UpperBound,
|
||||||
|
&it.hasPrefix, &it.prefixOrFullSeekKey,
|
||||||
|
false /* internalKeys */, &it.rangeKey.internal,
|
||||||
|
)
|
||||||
|
for i := range rangeKeyIters {
|
||||||
|
it.rangeKey.iterConfig.AddLevel(rangeKeyIters[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it.rangeKey.iiter.Init(it.cmp, it.iter, it.rangeKey.rangeKeyIter, it.rangeKey,
|
if it.rangeKey != nil {
|
||||||
it.opts.LowerBound, it.opts.UpperBound)
|
it.rangeKey.iiter.Init(&it.comparer, it.iter, it.rangeKey.rangeKeyIter,
|
||||||
it.iter = &it.rangeKey.iiter
|
keyspan.InterleavingIterOpts{
|
||||||
|
Mask: &it.rangeKeyMasking,
|
||||||
|
LowerBound: it.opts.LowerBound,
|
||||||
|
UpperBound: it.opts.UpperBound,
|
||||||
|
})
|
||||||
|
it.iter = &it.rangeKey.iiter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func openExternalTables(
|
func openExternalTables(
|
||||||
o *Options,
|
o *Options,
|
||||||
files []sstable.ReadableFile,
|
files []sstable.ReadableFile,
|
||||||
|
seqNumOffset int,
|
||||||
readerOpts sstable.ReaderOptions,
|
readerOpts sstable.ReaderOptions,
|
||||||
extraReaderOpts ...sstable.ReaderOption,
|
extraReaderOpts ...sstable.ReaderOption,
|
||||||
) (readers []*sstable.Reader, err error) {
|
) (readers []*sstable.Reader, err error) {
|
||||||
readers = make([]*sstable.Reader, 0, len(files))
|
readers = make([]*sstable.Reader, 0, len(files))
|
||||||
for i := range files {
|
for i := range files {
|
||||||
r, err := sstable.NewReader(files[i], readerOpts, extraReaderOpts...)
|
readable, err := sstable.NewSimpleReadable(files[i])
|
||||||
|
if err != nil {
|
||||||
|
return readers, err
|
||||||
|
}
|
||||||
|
r, err := sstable.NewReader(readable, readerOpts, extraReaderOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return readers, err
|
return readers, err
|
||||||
}
|
}
|
||||||
// Use the index of the file in files as the sequence number for all of
|
// Use the index of the file in files as the sequence number for all of
|
||||||
// its keys.
|
// its keys.
|
||||||
r.Properties.GlobalSeqNum = uint64(len(files) - i)
|
r.Properties.GlobalSeqNum = uint64(len(files) - i + seqNumOffset)
|
||||||
readers = append(readers, r)
|
readers = append(readers, r)
|
||||||
}
|
}
|
||||||
return readers, err
|
return readers, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// simpleLevelIter is similar to a levelIter in that it merges the points
|
||||||
|
// from multiple point iterators that are non-overlapping in the key ranges
|
||||||
|
// they return. It is only expected to support forward iteration and forward
|
||||||
|
// regular seeking; reverse iteration and prefix seeking is not supported.
|
||||||
|
// Intended to be a low-overhead, non-FileMetadata dependent option for
|
||||||
|
// NewExternalIter. To optimize seeking and forward iteration, it maintains
|
||||||
|
// two slices of child iterators; one of all iterators, and a subset of it that
|
||||||
|
// contains just the iterators that contain point keys within the current
|
||||||
|
// bounds.
|
||||||
|
//
|
||||||
|
// Note that this levelIter does not support pausing at file boundaries
|
||||||
|
// in case of range tombstones in this file that could apply to points outside
|
||||||
|
// of this file (and outside of this level). This is sufficient for optimizing
|
||||||
|
// the main use cases of NewExternalIter, however for completeness it would make
|
||||||
|
// sense to build this pausing functionality in.
|
||||||
|
type simpleLevelIter struct {
|
||||||
|
cmp Compare
|
||||||
|
err error
|
||||||
|
lowerBound []byte
|
||||||
|
iters []internalIterator
|
||||||
|
filtered []internalIterator
|
||||||
|
firstKeys [][]byte
|
||||||
|
firstKeysBuf []byte
|
||||||
|
currentIdx int
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ internalIterator = &simpleLevelIter{}
|
||||||
|
|
||||||
|
// init initializes this simpleLevelIter.
|
||||||
|
func (s *simpleLevelIter) init(opts IterOptions) {
|
||||||
|
s.currentIdx = 0
|
||||||
|
s.lowerBound = opts.LowerBound
|
||||||
|
s.resetFilteredIters()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleLevelIter) resetFilteredIters() {
|
||||||
|
s.filtered = s.filtered[:0]
|
||||||
|
s.firstKeys = s.firstKeys[:0]
|
||||||
|
s.firstKeysBuf = s.firstKeysBuf[:0]
|
||||||
|
s.err = nil
|
||||||
|
for i := range s.iters {
|
||||||
|
var iterKey *base.InternalKey
|
||||||
|
if s.lowerBound != nil {
|
||||||
|
iterKey, _ = s.iters[i].SeekGE(s.lowerBound, base.SeekGEFlagsNone)
|
||||||
|
} else {
|
||||||
|
iterKey, _ = s.iters[i].First()
|
||||||
|
}
|
||||||
|
if iterKey != nil {
|
||||||
|
s.filtered = append(s.filtered, s.iters[i])
|
||||||
|
bufStart := len(s.firstKeysBuf)
|
||||||
|
s.firstKeysBuf = append(s.firstKeysBuf, iterKey.UserKey...)
|
||||||
|
s.firstKeys = append(s.firstKeys, s.firstKeysBuf[bufStart:bufStart+len(iterKey.UserKey)])
|
||||||
|
} else if err := s.iters[i].Error(); err != nil {
|
||||||
|
s.err = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleLevelIter) SeekGE(
|
||||||
|
key []byte, flags base.SeekGEFlags,
|
||||||
|
) (*base.InternalKey, base.LazyValue) {
|
||||||
|
if s.err != nil {
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
// Find the first file that is entirely >= key. The file before that could
|
||||||
|
// contain the key we're looking for.
|
||||||
|
n := sort.Search(len(s.firstKeys), func(i int) bool {
|
||||||
|
return s.cmp(key, s.firstKeys[i]) <= 0
|
||||||
|
})
|
||||||
|
if n > 0 {
|
||||||
|
s.currentIdx = n - 1
|
||||||
|
} else {
|
||||||
|
s.currentIdx = n
|
||||||
|
}
|
||||||
|
if s.currentIdx < len(s.filtered) {
|
||||||
|
if iterKey, val := s.filtered[s.currentIdx].SeekGE(key, flags); iterKey != nil {
|
||||||
|
return iterKey, val
|
||||||
|
}
|
||||||
|
if err := s.filtered[s.currentIdx].Error(); err != nil {
|
||||||
|
s.err = err
|
||||||
|
}
|
||||||
|
s.currentIdx++
|
||||||
|
}
|
||||||
|
return s.skipEmptyFileForward(key, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleLevelIter) skipEmptyFileForward(
|
||||||
|
seekKey []byte, flags base.SeekGEFlags,
|
||||||
|
) (*base.InternalKey, base.LazyValue) {
|
||||||
|
var iterKey *base.InternalKey
|
||||||
|
var val base.LazyValue
|
||||||
|
for s.currentIdx >= 0 && s.currentIdx < len(s.filtered) && s.err == nil {
|
||||||
|
if seekKey != nil {
|
||||||
|
iterKey, val = s.filtered[s.currentIdx].SeekGE(seekKey, flags)
|
||||||
|
} else if s.lowerBound != nil {
|
||||||
|
iterKey, val = s.filtered[s.currentIdx].SeekGE(s.lowerBound, flags)
|
||||||
|
} else {
|
||||||
|
iterKey, val = s.filtered[s.currentIdx].First()
|
||||||
|
}
|
||||||
|
if iterKey != nil {
|
||||||
|
return iterKey, val
|
||||||
|
}
|
||||||
|
if err := s.filtered[s.currentIdx].Error(); err != nil {
|
||||||
|
s.err = err
|
||||||
|
}
|
||||||
|
s.currentIdx++
|
||||||
|
}
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleLevelIter) SeekPrefixGE(
|
||||||
|
prefix, key []byte, flags base.SeekGEFlags,
|
||||||
|
) (*base.InternalKey, base.LazyValue) {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleLevelIter) SeekLT(
|
||||||
|
key []byte, flags base.SeekLTFlags,
|
||||||
|
) (*base.InternalKey, base.LazyValue) {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleLevelIter) First() (*base.InternalKey, base.LazyValue) {
|
||||||
|
if s.err != nil {
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
s.currentIdx = 0
|
||||||
|
return s.skipEmptyFileForward(nil /* seekKey */, base.SeekGEFlagsNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleLevelIter) Last() (*base.InternalKey, base.LazyValue) {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleLevelIter) Next() (*base.InternalKey, base.LazyValue) {
|
||||||
|
if s.err != nil {
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
if s.currentIdx < 0 || s.currentIdx >= len(s.filtered) {
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
if iterKey, val := s.filtered[s.currentIdx].Next(); iterKey != nil {
|
||||||
|
return iterKey, val
|
||||||
|
}
|
||||||
|
s.currentIdx++
|
||||||
|
return s.skipEmptyFileForward(nil /* seekKey */, base.SeekGEFlagsNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleLevelIter) NextPrefix(succKey []byte) (*base.InternalKey, base.LazyValue) {
|
||||||
|
if s.err != nil {
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
if s.currentIdx < 0 || s.currentIdx >= len(s.filtered) {
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
if iterKey, val := s.filtered[s.currentIdx].NextPrefix(succKey); iterKey != nil {
|
||||||
|
return iterKey, val
|
||||||
|
}
|
||||||
|
s.currentIdx++
|
||||||
|
return s.skipEmptyFileForward(succKey /* seekKey */, base.SeekGEFlagsNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleLevelIter) Prev() (*base.InternalKey, base.LazyValue) {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleLevelIter) Error() error {
|
||||||
|
if s.currentIdx >= 0 && s.currentIdx < len(s.filtered) {
|
||||||
|
s.err = firstError(s.err, s.filtered[s.currentIdx].Error())
|
||||||
|
}
|
||||||
|
return s.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleLevelIter) Close() error {
|
||||||
|
var err error
|
||||||
|
for i := range s.iters {
|
||||||
|
err = firstError(err, s.iters[i].Close())
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleLevelIter) SetBounds(lower, upper []byte) {
|
||||||
|
s.currentIdx = -1
|
||||||
|
s.lowerBound = lower
|
||||||
|
for i := range s.iters {
|
||||||
|
s.iters[i].SetBounds(lower, upper)
|
||||||
|
}
|
||||||
|
s.resetFilteredIters()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleLevelIter) String() string {
|
||||||
|
if s.currentIdx < 0 || s.currentIdx >= len(s.filtered) {
|
||||||
|
return "simpleLevelIter: current=<nil>"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("simpleLevelIter: current=%s", s.filtered[s.currentIdx])
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ internalIterator = &simpleLevelIter{}
|
||||||
|
|||||||
2
vendor/github.com/cockroachdb/pebble/filenames.go
generated
vendored
2
vendor/github.com/cockroachdb/pebble/filenames.go
generated
vendored
@@ -33,7 +33,7 @@ const (
|
|||||||
// NB: This is a low-level routine and typically not what you want to
|
// NB: This is a low-level routine and typically not what you want to
|
||||||
// use. Newer versions of Pebble running newer format major versions do
|
// use. Newer versions of Pebble running newer format major versions do
|
||||||
// not use the CURRENT file. See setCurrentFunc in version_set.go.
|
// not use the CURRENT file. See setCurrentFunc in version_set.go.
|
||||||
func setCurrentFile(dirname string, fs vfs.FS, fileNum FileNum) error {
|
func setCurrentFile(dirname string, fs vfs.FS, fileNum base.DiskFileNum) error {
|
||||||
newFilename := base.MakeFilepath(fs, dirname, fileTypeCurrent, fileNum)
|
newFilename := base.MakeFilepath(fs, dirname, fileTypeCurrent, fileNum)
|
||||||
oldFilename := base.MakeFilepath(fs, dirname, fileTypeTemp, fileNum)
|
oldFilename := base.MakeFilepath(fs, dirname, fileTypeTemp, fileNum)
|
||||||
fs.Remove(oldFilename)
|
fs.Remove(oldFilename)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user