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/**
|
||||
.vscode/**
|
||||
.vstags
|
||||
data/config.json
|
||||
data/*_db
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
|
||||
build:
|
||||
${GOOPTS} go build ${BUILDOPTS} -o aleesa-irc-go types.go globals.go collection.go irc-user-mode.go \
|
||||
settings-db-util.go util.go msg-parsers.go aleesa-irc-go-lib.go main.go
|
||||
ifeq ($(OS),Windows_NT)
|
||||
# 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:
|
||||
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:
|
||||
go mod init main
|
||||
go mod tidy -compat=1.18
|
||||
upgrade:
|
||||
ifeq ($(OS),Windows_NT)
|
||||
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:
|
||||
|
||||
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
|
||||
|
||||
go 1.18
|
||||
go 1.25.1
|
||||
|
||||
require (
|
||||
github.com/cockroachdb/errors v1.8.1
|
||||
github.com/cockroachdb/pebble v0.0.0-20220729140737-245afda9c39f
|
||||
github.com/cockroachdb/errors v1.12.0
|
||||
github.com/cockroachdb/pebble v1.1.5
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/hjson/hjson-go v3.1.0+incompatible
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/hjson/hjson-go v3.3.0+incompatible
|
||||
github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/DataDog/zstd v1.4.5 // indirect
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f // indirect
|
||||
github.com/cockroachdb/redact v1.0.8 // indirect
|
||||
github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // 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/gogo/protobuf v1.3.1 // indirect
|
||||
github.com/golang/snappy v0.0.3 // indirect
|
||||
github.com/klauspost/compress v1.11.7 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.27.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // 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/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
golang.org/x/exp v0.0.0-20200513190911-00229845015e // indirect
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
github.com/prometheus/client_golang v1.15.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.42.0 // 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/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
|
||||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
||||
github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=
|
||||
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=
|
||||
github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM=
|
||||
github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y=
|
||||
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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4=
|
||||
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
|
||||
github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo=
|
||||
github.com/cockroachdb/errors v1.12.0/go.mod h1:SvzfYNNBshAVbZ8wzNc/UPK3w1vf0dKDUP41ucAIf7g=
|
||||
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4=
|
||||
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
|
||||
github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw=
|
||||
github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo=
|
||||
github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
|
||||
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
|
||||
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/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/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/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
|
||||
github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
|
||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||
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/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
|
||||
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
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/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
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.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
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/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/hjson/hjson-go v3.3.0+incompatible h1:Rqr+Ya+0aCJMjaE4s8E9YKvuJLuLVpEvz4ONum52vnI=
|
||||
github.com/hjson/hjson-go v3.3.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
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.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
|
||||
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
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/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
|
||||
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
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/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
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/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
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/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
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/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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
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/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM=
|
||||
github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
|
||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
|
||||
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
|
||||
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
|
||||
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
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/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
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=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
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/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
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/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
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-20190827160401-ba9fcec4b297/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-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
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/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/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/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
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-20190626221950-04f50cda93cb/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-20200930185726-fdedc70b468f/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-20210909193231-528a39cd75f3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.3/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-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
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/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
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-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=
|
||||
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
|
||||
gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=
|
||||
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
|
||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||
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=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
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/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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
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=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
package main
|
||||
package anycollection
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Collection это sync.Map-ка, хранящая в себе произвольные итемы
|
||||
// Collection это sync.Map-ка, хранящая в себе произвольные итемы.
|
||||
type Collection struct {
|
||||
items sync.Map
|
||||
close chan struct{}
|
||||
}
|
||||
|
||||
// Item представляет собой интерфейс для хранения произвольных данных
|
||||
// Item представляет собой интерфейс для хранения произвольных данных.
|
||||
type item struct {
|
||||
data interface{}
|
||||
data any
|
||||
}
|
||||
|
||||
// NewCollection создаёт инстанс коллекции
|
||||
// NewCollection создаёт инстанс коллекции.
|
||||
func NewCollection() *Collection {
|
||||
c := &Collection{
|
||||
close: make(chan struct{}),
|
||||
@@ -24,8 +24,8 @@ func NewCollection() *Collection {
|
||||
return c
|
||||
}
|
||||
|
||||
// Get достаёт данные по заданному ключу из коллекции
|
||||
func (collection *Collection) Get(key interface{}) (interface{}, bool) {
|
||||
// Get достаёт данные по заданному ключу из коллекции.
|
||||
func (collection *Collection) Get(key any) (any, bool) {
|
||||
obj, exists := collection.items.Load(key)
|
||||
|
||||
if !exists {
|
||||
@@ -37,16 +37,16 @@ func (collection *Collection) Get(key interface{}) (interface{}, bool) {
|
||||
return item.data, true
|
||||
}
|
||||
|
||||
// Set сохраняет данные с заданным ключом в коллекцию
|
||||
func (collection *Collection) Set(key interface{}, value interface{}) {
|
||||
// Set сохраняет данные с заданным ключом в коллекцию.
|
||||
func (collection *Collection) Set(key any, value any) {
|
||||
collection.items.Store(key, item{
|
||||
data: value,
|
||||
})
|
||||
}
|
||||
|
||||
// Range применяет функцию f ко всем ключам в коллекции
|
||||
func (collection *Collection) Range(f func(key, value interface{}) bool) {
|
||||
fn := func(key, value interface{}) bool {
|
||||
// Range применяет функцию f ко всем ключам в коллекции.
|
||||
func (collection *Collection) Range(f func(key, value any) bool) {
|
||||
fn := func(key, value any) bool {
|
||||
item := value.(item)
|
||||
|
||||
return f(key, item.data)
|
||||
@@ -55,12 +55,12 @@ func (collection *Collection) Range(f func(key, value interface{}) bool) {
|
||||
collection.items.Range(fn)
|
||||
}
|
||||
|
||||
// Delete удаляет ключ и значение из коллекции данных
|
||||
func (collection *Collection) Delete(key interface{}) {
|
||||
// Delete удаляет ключ и значение из коллекции данных.
|
||||
func (collection *Collection) Delete(key any) {
|
||||
collection.items.Delete(key)
|
||||
}
|
||||
|
||||
// Close очищает и высвобождает ресурсы, занятые коллекцией
|
||||
// Close очищает и высвобождает ресурсы, занятые коллекцией.
|
||||
func (collection *Collection) Close() {
|
||||
collection.close <- struct{}{}
|
||||
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 (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"aleesa-irc-go/internal/boolcollection"
|
||||
"aleesa-irc-go/internal/log"
|
||||
|
||||
irc "github.com/thoj/go-ircevent"
|
||||
)
|
||||
|
||||
// ircClientInit горутинка для работы с протоколом irc
|
||||
func ircClientRun() {
|
||||
for {
|
||||
if shutdown {
|
||||
// Если мы завершаем работу программы, то нам ничего обрабатывать не надо
|
||||
break
|
||||
}
|
||||
|
||||
// Иницализируем irc-клиента
|
||||
serverString := fmt.Sprintf("%s:%d", config.Irc.Server, config.Irc.Port)
|
||||
// ircClientInit горутинка для работы с протоколом irc.
|
||||
func IrcClientRun() {
|
||||
for !Shutdown {
|
||||
// Иницализируем irc-клиента.
|
||||
// TODO: make use of capabilities: https://defs.ircdocs.horse/defs/clientcaps .
|
||||
// TODO: make use of tags caps https://defs.ircdocs.horse/defs/tags .
|
||||
serverString := fmt.Sprintf("%s:%d", Config.Irc.Server, Config.Irc.Port)
|
||||
log.Debugf("Preparing to connect to %s", serverString)
|
||||
log.Debugf("Using nick %s and username %s", config.Irc.Nick, config.Irc.User)
|
||||
ircClient = irc.IRC(config.Irc.Nick, config.Irc.User)
|
||||
ircClient.RealName = 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.RealName = Config.Irc.User
|
||||
ircClient.Version = "Aleesa Bot v4.something"
|
||||
|
||||
if config.Irc.Ssl {
|
||||
if Config.Irc.Ssl {
|
||||
log.Debug("Force use ssl for connection")
|
||||
|
||||
ircClient.UseTLS = true
|
||||
|
||||
if !config.Irc.SslVerify {
|
||||
if !Config.Irc.SslVerify {
|
||||
log.Debug("Skip server certificate validation")
|
||||
|
||||
ircClient.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
} else {
|
||||
log.Debug("Force server certificate validation")
|
||||
@@ -41,58 +43,97 @@ func ircClientRun() {
|
||||
log.Debug("Skip ssl for connection")
|
||||
}
|
||||
|
||||
if config.Irc.Sasl && config.Irc.Password != "" {
|
||||
ircClient.SASLLogin = config.Irc.User
|
||||
ircClient.SASLPassword = config.Irc.Password
|
||||
if Config.Irc.Sasl && Config.Irc.Password != "" {
|
||||
ircClient.SASLLogin = Config.Irc.User
|
||||
ircClient.SASLPassword = Config.Irc.Password
|
||||
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) {
|
||||
/* Это одна из строк с данными, прилетающая в ответ на запрос whois на определённого юзера
|
||||
* Из этой строки нас интересует, на каких каналах пользователь op (то есть с префиксом @) или имеет voice
|
||||
* (то есть с префиксом +) чтобы внести его в свою базу mode-ов.
|
||||
*/
|
||||
log.Debugf("319 RPL_WHOISCHANNELS, %s", e.Raw)
|
||||
channelsWithModes := strings.Split(e.Arguments[2], " ")
|
||||
dstNick := e.Arguments[1]
|
||||
|
||||
for _, channelWithMode := range channelsWithModes {
|
||||
for _, mychannel := range config.Irc.Channels {
|
||||
for _, mychannel := range Config.Irc.Channels {
|
||||
tmp := regexp.MustCompile("#").Split(channelWithMode, 2)
|
||||
channel := tmp[1]
|
||||
modes := tmp[0]
|
||||
|
||||
if channel == mychannel {
|
||||
// Для каждого канала формат строго 1 из 4-х: #channel | @#channel | +#channel | @+#channel
|
||||
switch {
|
||||
case modes == "@+":
|
||||
switch modes {
|
||||
case "@+":
|
||||
userModeUpdateUser(channel, dstNick, "+o")
|
||||
userModeUpdateUser(channel, dstNick, "+v")
|
||||
case modes == "@":
|
||||
case "@":
|
||||
userModeUpdateUser(channel, dstNick, "+o")
|
||||
case modes == "+":
|
||||
case "+":
|
||||
userModeUpdateUser(channel, dstNick, "+v")
|
||||
default:
|
||||
userModeUpdateUser(channel, dstNick, "-o")
|
||||
userModeUpdateUser(channel, dstNick, "-v")
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 433 ERR_NICKNAMEINUSE уже есть в github.com/thoj/go-ircevent/irc_callback.go.
|
||||
|
||||
ircClient.AddCallback("353", func(e *irc.Event) {
|
||||
// Это одна из строк данных, прилетающая в ответ на запрос names, на канале
|
||||
log.Debugf("353 RPL_NAMREPLY, %s", e.Raw)
|
||||
e.Connection.Lock()
|
||||
namesString := e.Arguments[3]
|
||||
channel := e.Arguments[2]
|
||||
|
||||
for _, mychannel := range config.Irc.Channels {
|
||||
for _, mychannel := range Config.Irc.Channels {
|
||||
if channel == mychannel {
|
||||
for _, name := range strings.Split(namesString, " ") {
|
||||
mode := name[:1]
|
||||
|
||||
var nick string
|
||||
|
||||
if mode == "@" || mode == "+" {
|
||||
@@ -101,25 +142,58 @@ func ircClientRun() {
|
||||
nick = name
|
||||
}
|
||||
|
||||
switch {
|
||||
case mode == "@":
|
||||
switch mode {
|
||||
case "@":
|
||||
userModeUpdateUser(channel, nick, "+o")
|
||||
case mode == "+":
|
||||
case "+":
|
||||
userModeUpdateUser(channel, nick, "+v")
|
||||
default:
|
||||
userModeUpdateUser(channel, nick, "-o")
|
||||
userModeUpdateUser(channel, nick, "-v")
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
e.Connection.Unlock()
|
||||
})
|
||||
|
||||
ircClient.AddCallback("352", func(e *irc.Event) {
|
||||
// Относительно бесполезная для нас команда. Не возвращает канала (в случае с ngircd), так что узнать mode
|
||||
// юзера на конкретном канале из этого невозможно. А остальные данные пока непонятно куда применять.
|
||||
log.Warnf("352 RPL_WHOREPLY, %s", e.Raw)
|
||||
// Если сервер не может прочитать MOTD, то он может вернуть 422 ERR_NOMOTD, тоде самое навесим и туда тоже.
|
||||
ircClient.AddCallback("376", func(e *irc.Event) {
|
||||
// TODO: make use of modes https://defs.ircdocs.horse/defs/usermodes
|
||||
// Если у нас есть доступный +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, которые мы можем получить и сдампим это
|
||||
@@ -157,6 +231,39 @@ func ircClientRun() {
|
||||
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) {
|
||||
log.Errorf("431 ERR_NONICKNAMEGIVEN, %s", e.Raw)
|
||||
})
|
||||
@@ -167,9 +274,12 @@ func ircClientRun() {
|
||||
|
||||
ircClient.AddCallback("433", func(e *irc.Event) {
|
||||
log.Errorf("433 ERR_NICKNAMEINUSE, %s", e.Raw)
|
||||
|
||||
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) {
|
||||
@@ -179,6 +289,7 @@ func ircClientRun() {
|
||||
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) {
|
||||
log.Errorf("437 ERR_UNAVAILRESOURCE, %s", e.Raw)
|
||||
})
|
||||
@@ -191,6 +302,16 @@ func ircClientRun() {
|
||||
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) {
|
||||
// Предполагается, что надо авторизоваться, перед тем как что-то делать на сервере
|
||||
log.Errorf("451 ERR_NOTREGISTERED, %s", e.Raw)
|
||||
@@ -213,18 +334,19 @@ func ircClientRun() {
|
||||
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) {
|
||||
// Это значит, что народу на канале максимальное количество. Будем пробовать присунуться раз в 30 сек.
|
||||
channel := e.Arguments[1]
|
||||
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 ircChan == channel {
|
||||
ircClient.Join(channel)
|
||||
break
|
||||
}
|
||||
if slices.Contains(Config.Irc.Channels, channel) {
|
||||
ircClient.Join(channel)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -237,14 +359,11 @@ func ircClientRun() {
|
||||
// По завершении работ +i снимают.
|
||||
channel := e.Arguments[1]
|
||||
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 ircChan == channel {
|
||||
ircClient.Join(channel)
|
||||
break
|
||||
}
|
||||
// Проверяем, а должны ли мы быть заджоенными к указанному, каналу, а то вдруг нет?
|
||||
if slices.Contains(Config.Irc.Channels, channel) {
|
||||
ircClient.Join(channel)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -254,18 +373,21 @@ func ircClientRun() {
|
||||
|
||||
log.Errorf("474 ERR_BANNEDFROMCHAN, %s", e.Raw)
|
||||
// Если нас забанили, то информацию о MODE-ах пользователей мы теряем
|
||||
time.Sleep(30 * time.Second)
|
||||
<-time.NewTimer(30 * time.Second).C
|
||||
|
||||
// Вдруг, нас забанили, но какбэ не навсегда?
|
||||
// TODO: вынести в настройки?
|
||||
|
||||
// Проверяем, а должны ли мы быть заджоенныеми к указанному, каналу, а то вдруг нет?
|
||||
for _, ircChan := range config.Irc.Channels {
|
||||
if ircChan == channel {
|
||||
ircClient.Join(channel)
|
||||
break
|
||||
}
|
||||
if slices.Contains(Config.Irc.Channels, channel) {
|
||||
ircClient.Join(channel)
|
||||
}
|
||||
})
|
||||
|
||||
ircClient.AddCallback("475", func(e *irc.Event) {
|
||||
log.Errorf("475 ERR_BADCHANNELKEY, %s", e.Raw)
|
||||
})
|
||||
|
||||
ircClient.AddCallback("477", func(e *irc.Event) {
|
||||
log.Errorf("477 ERR_NOCHANMODES, %s", e.Raw)
|
||||
})
|
||||
@@ -274,6 +396,10 @@ func ircClientRun() {
|
||||
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) {
|
||||
log.Errorf("482 ERR_CHANOPRIVSNEEDED, %s", e.Raw)
|
||||
})
|
||||
@@ -286,6 +412,10 @@ func ircClientRun() {
|
||||
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) {
|
||||
log.Errorf("501 ERR_UMODEUNKNOWNFLAG, %s", e.Raw)
|
||||
})
|
||||
@@ -294,22 +424,6 @@ func ircClientRun() {
|
||||
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) {
|
||||
dstNick := e.Arguments[1]
|
||||
@@ -318,10 +432,11 @@ func ircClientRun() {
|
||||
|
||||
if e.Arguments[1] == ircClient.GetNick() {
|
||||
// Нас кикнули с канала, и мы теряем информацию о MODE-ах пользователей
|
||||
userMode.Delete(channel)
|
||||
UserMode.Delete(channel)
|
||||
// TODO: reason?
|
||||
log.Warnf("%s kicks us from %s", srcFullNick, channel)
|
||||
time.Sleep(10 * time.Second)
|
||||
<-time.NewTimer(10 * time.Second).C
|
||||
|
||||
ircClient.Join(channel)
|
||||
} else {
|
||||
// Кого-то другого кикнули с канала
|
||||
@@ -334,21 +449,22 @@ func ircClientRun() {
|
||||
srcNick := e.Nick
|
||||
dstNick := e.Arguments[0]
|
||||
|
||||
if e.Nick == config.Irc.Nick {
|
||||
if e.Nick == Config.Irc.Nick {
|
||||
nickIsUsed = false
|
||||
|
||||
if config.Irc.Password != "" {
|
||||
if Config.Irc.Password != "" {
|
||||
log.Warn("I regain my nick")
|
||||
} else {
|
||||
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 {
|
||||
log.Infof("%s renames themself to %s", srcNick, dstNick)
|
||||
}
|
||||
|
||||
// Неважно чей ник сменился, надо забыть, что было и снова узнать mode-ы сменишего nick джентельмена
|
||||
// Неважно чей ник сменился, надо забыть, что было и снова узнать mode-ы сменишего nick джентельмена.
|
||||
// TODO: реализовать userModeRenameUser()
|
||||
userModePurgeUser(srcNick)
|
||||
ircClient.Whois(dstNick)
|
||||
@@ -360,12 +476,12 @@ func ircClientRun() {
|
||||
channel := e.Arguments[0]
|
||||
|
||||
if nick == ircClient.GetNick() {
|
||||
// Команда names отправляется автоматом.
|
||||
log.Infof("I joined to %s", channel)
|
||||
// Команда names отправляется автоматом
|
||||
} else {
|
||||
log.Infof("%s joined to %s", fullNick, channel)
|
||||
// Технически, тут не надо спрашивать whois на пользователя, но мы спрашиваем, чтобы уточнить mode,
|
||||
// вдруг сервер проставляет mode заранее (хотя не должен)
|
||||
// вдруг сервер проставляет mode заранее (хотя не должен).
|
||||
ircClient.Whois(nick)
|
||||
}
|
||||
})
|
||||
@@ -377,7 +493,7 @@ func ircClientRun() {
|
||||
|
||||
if nick == ircClient.GetNick() {
|
||||
log.Infof("I parted from %s", channel)
|
||||
userMode.Delete(channel)
|
||||
UserMode.Delete(channel)
|
||||
} else {
|
||||
log.Infof("%s parted from %s", fullNick, channel)
|
||||
userModeDeleteUser(channel, nick)
|
||||
@@ -403,6 +519,7 @@ func ircClientRun() {
|
||||
channel := e.Arguments[0]
|
||||
fullSrcNick := e.Source
|
||||
srcNick := e.Nick
|
||||
|
||||
var dstNick string
|
||||
|
||||
if len(e.Arguments) >= 3 { // кого-то по-MODE-или на канале
|
||||
@@ -415,6 +532,7 @@ func ircClientRun() {
|
||||
}
|
||||
} else { // Установка mode-а при заходе на сервер
|
||||
dstNick = e.Arguments[0]
|
||||
|
||||
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])
|
||||
})
|
||||
|
||||
/*
|
||||
ircClient.AddCallback("*", func(e *irc.Event) {
|
||||
log.Debugf("Incoming EVENT (Raw): %s", e.Raw)
|
||||
})
|
||||
*/
|
||||
ircClient.AddCallback("*", func(e *irc.Event) {
|
||||
log.Debugf("Incoming EVENT (Raw): %s", e.Raw)
|
||||
})
|
||||
|
||||
log.Debugf("Make an actual connection to irc")
|
||||
|
||||
if err := ircClient.Connect(serverString); err != nil {
|
||||
log.Fatalf("Unable to prepare irc connection: %s", err)
|
||||
time.Sleep(3 * time.Second)
|
||||
log.Errorf("Unable to prepare irc connection: %s", err)
|
||||
<-time.NewTimer(3 * time.Second).C // Sleep for 3 seconds.
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -474,26 +591,27 @@ func ircClientRun() {
|
||||
|
||||
// ircSend отправляет сообщение в irc. Якобы с применением ratelimit-ов, но на практике длинные сообщения разделяются
|
||||
// на более короткие и тут ratelimit не срабатывает. Если не повезёт, то сервер может за такое дело и кикнуть.
|
||||
func ircSend() {
|
||||
func IrcSend() {
|
||||
for {
|
||||
m := <-imChan
|
||||
|
||||
switch {
|
||||
case config.Irc.RateLimit.Type == "simple_delay":
|
||||
log.Debugf("Sending to chat %s message: %s", m.ChatId, m.Text)
|
||||
switch Config.Irc.RateLimit.Type {
|
||||
case "simple_delay":
|
||||
log.Debugf("Sending to chat %s message: %s", m.ChatID, m.Text)
|
||||
|
||||
if m.Text[0:4] == "/me " {
|
||||
ircClient.Action(m.ChatId, m.Text)
|
||||
ircClient.Action(m.ChatID, m.Text)
|
||||
} 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))
|
||||
time.Sleep(sleepDelay)
|
||||
case config.Irc.RateLimit.Type == "token_bucket":
|
||||
<-time.NewTimer(sleepDelay).C
|
||||
case "token_bucket":
|
||||
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 {
|
||||
var newBucket bucket
|
||||
@@ -505,10 +623,11 @@ func ircSend() {
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
if bucketFill >= config.Irc.RateLimit.TokenBucket.Size {
|
||||
if bucketFill >= Config.Irc.RateLimit.TokenBucket.Size {
|
||||
newBucket.IsFull = true
|
||||
}
|
||||
|
||||
@@ -516,52 +635,55 @@ func ircSend() {
|
||||
} else {
|
||||
// Проставим время отправки сообщения
|
||||
log.Debug("Message bucket is empty")
|
||||
|
||||
msgBucket.Timestamps = append(msgBucket.Timestamps, currentTimeMs)
|
||||
}
|
||||
|
||||
if msgBucket.IsFull {
|
||||
log.Debug("Message bucket overflowed, hitting ratelimit")
|
||||
|
||||
sleepPeriod := expirationTimeMs - (currentTimeMs - msgBucket.Timestamps[len(msgBucket.Timestamps)-1])
|
||||
|
||||
if config.Irc.RateLimit.TokenBucket.Limit > 0 {
|
||||
sleepPeriod = sleepPeriod / int64(config.Irc.RateLimit.TokenBucket.Limit)
|
||||
if Config.Irc.RateLimit.TokenBucket.Limit > 0 {
|
||||
sleepPeriod /= int64(Config.Irc.RateLimit.TokenBucket.Limit)
|
||||
}
|
||||
|
||||
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()
|
||||
// Обновим время отправки сообщения
|
||||
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 " {
|
||||
ircClient.Action(m.ChatId, m.Text[4:])
|
||||
ircClient.Action(m.ChatID, m.Text[4:])
|
||||
} else {
|
||||
ircClient.Privmsg(m.ChatId, m.Text)
|
||||
ircClient.Privmsg(m.ChatID, m.Text)
|
||||
}
|
||||
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 " {
|
||||
ircClient.Action(m.ChatId, m.Text[4:])
|
||||
ircClient.Action(m.ChatID, m.Text[4:])
|
||||
} else {
|
||||
ircClient.Privmsg(m.ChatId, m.Text)
|
||||
ircClient.Privmsg(m.ChatID, m.Text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ircSendUnrestricted() {
|
||||
func IrcSendUnrestricted() {
|
||||
for {
|
||||
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 " {
|
||||
ircClient.Action(m.ChatId, m.Text[4:])
|
||||
ircClient.Action(m.ChatID, m.Text[4:])
|
||||
} else {
|
||||
ircClient.Privmsg(m.ChatId, m.Text)
|
||||
ircClient.Privmsg(m.ChatID, m.Text)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,55 @@
|
||||
package main
|
||||
package irc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/cockroachdb/pebble"
|
||||
"aleesa-irc-go/internal/anycollection"
|
||||
"aleesa-irc-go/internal/boolcollection"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
irc "github.com/thoj/go-ircevent"
|
||||
)
|
||||
|
||||
// Config - это у нас глобальная штука :)
|
||||
var config myConfig
|
||||
// Config - это у нас глобальная штука :).
|
||||
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
|
||||
|
||||
// Объектики irc-клиента
|
||||
// Объектики irc-клиента.
|
||||
var ircClient *irc.Connection
|
||||
var nickIsUsed = false
|
||||
|
||||
// Объектики клиента-редиски
|
||||
var redisClient *redis.Client
|
||||
var subscriber *redis.PubSub
|
||||
// Объектики клиента-редиски.
|
||||
var RedisClient *redis.Client
|
||||
var Subscriber *redis.PubSub
|
||||
|
||||
// Main context
|
||||
// Main context.
|
||||
var ctx = context.Background()
|
||||
|
||||
// Ставится в true, если мы получили сигнал на выключение
|
||||
var shutdown = false
|
||||
// Ставится в true, если мы получили сигнал на выключение.
|
||||
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)
|
||||
|
||||
// Канал, в который пишутся сообщения для отправки в IRC, если ограничений нет (у бота +o или +v на канале)
|
||||
// Канал, в который пишутся сообщения для отправки в IRC, если ограничений нет (у бота +o или +v на канале).
|
||||
var imChanUnrestricted = make(chan iMsg, 100)
|
||||
|
||||
// "Ведёрко" с timestamp-ами последних отправленных сообщений
|
||||
// "Ведёрко" с timestamp-ами последних отправленных сообщений.
|
||||
var msgBucket bucket
|
||||
|
||||
// Мапка с открытыми дескрипторами баз с настройками
|
||||
var settingsDB = make(map[string]*pebble.DB)
|
||||
// "Базюлька" с MODE-ами пользователей на каналах.
|
||||
var UserMode = anycollection.NewCollection()
|
||||
|
||||
// "Базюлька" с MODE-ами пользователей на каналах
|
||||
var userMode = NewCollection()
|
||||
// "Базюлька" с доступными MODE-ами пользователя.
|
||||
var availableUserModes = boolcollection.NewCollection()
|
||||
|
||||
// "Базюлька" c с доступными MODE-ами каналов.
|
||||
var availableChanModes = boolcollection.NewCollection()
|
||||
|
||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package irc
|
||||
|
||||
import "strings"
|
||||
|
||||
@@ -8,33 +8,34 @@ import "strings"
|
||||
активируют мягкий ratelimit на стороне сервера, без них, сервер просто рвёт соединение по превышению лимитов
|
||||
*/
|
||||
|
||||
// Удаляет сведения о MODE-ах пользователя для заданного канала
|
||||
// Удаляет сведения о MODE-ах пользователя для заданного канала.
|
||||
func userModeDeleteUser(ircChan string, nick string) {
|
||||
channel, ok := userMode.Get(ircChan)
|
||||
channel, ok := UserMode.Get(ircChan)
|
||||
|
||||
if ok {
|
||||
channelData := channel.(map[string]map[string]bool)
|
||||
delete(channelData, nick)
|
||||
|
||||
if len(channelData) == 0 {
|
||||
userMode.Delete(ircChan)
|
||||
UserMode.Delete(ircChan)
|
||||
} else {
|
||||
userMode.Set(ircChan, channelData)
|
||||
UserMode.Set(ircChan, channelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Удаляет сведения о MODE-ах пользователя для всех каналов, на которых есть бот
|
||||
// Удаляет сведения о MODE-ах пользователя для всех каналов, на которых есть бот.
|
||||
func userModePurgeUser(nick string) {
|
||||
for _, channel := range config.Irc.Channels {
|
||||
for _, channel := range Config.Irc.Channels {
|
||||
userModeDeleteUser(channel, nick)
|
||||
}
|
||||
}
|
||||
|
||||
// Обновляет или создаёт запись о mode-ах пользователей
|
||||
// Обновляет или создаёт запись о mode-ах пользователей.
|
||||
func userModeUpdateUser(ircChan string, nick string, modes string) {
|
||||
var channelData map[string]map[string]bool
|
||||
channel, ok := userMode.Get(ircChan)
|
||||
|
||||
channel, ok := UserMode.Get(ircChan)
|
||||
|
||||
if ok {
|
||||
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)
|
||||
}
|
||||
|
||||
switch {
|
||||
case modes[:1] == "+":
|
||||
switch modes[:1] {
|
||||
case "+":
|
||||
for _, mode := range strings.Split(modes[1:], "") {
|
||||
channelData[nick][mode] = true
|
||||
}
|
||||
case modes[:1] == "-":
|
||||
case "-":
|
||||
for _, mode := range strings.Split(modes[1:], "") {
|
||||
channelData[nick][mode] = false
|
||||
}
|
||||
}
|
||||
|
||||
userMode.Set(ircChan, channelData)
|
||||
UserMode.Set(ircChan, channelData)
|
||||
}
|
||||
|
||||
// Возвращает значение MODE-а v для запрошенного ника
|
||||
// Возвращает значение MODE-а v для запрошенного ника.
|
||||
func userModeIsVoiced(ircChan string, nick string) bool {
|
||||
var channelData map[string]map[string]bool
|
||||
channel, ok := userMode.Get(ircChan)
|
||||
|
||||
channel, ok := UserMode.Get(ircChan)
|
||||
|
||||
if ok {
|
||||
channelData = channel.(map[string]map[string]bool)
|
||||
@@ -88,10 +90,11 @@ func userModeIsVoiced(ircChan string, nick string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Возвращает значение MODE-а o для запрошенного ника
|
||||
// Возвращает значение MODE-а o для запрошенного ника.
|
||||
func userModeIsOped(ircChan string, nick string) bool {
|
||||
var channelData map[string]map[string]bool
|
||||
channel, ok := userMode.Get(ircChan)
|
||||
|
||||
channel, ok := UserMode.Get(ircChan)
|
||||
|
||||
if ok {
|
||||
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 {
|
||||
var channelData map[string]map[string]bool
|
||||
channel, ok := userMode.Get(ircChan)
|
||||
|
||||
channel, ok := UserMode.Get(ircChan)
|
||||
|
||||
if ok {
|
||||
channelData = channel.(map[string]map[string]bool)
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package irc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -6,15 +6,16 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"aleesa-irc-go/internal/log"
|
||||
"aleesa-irc-go/internal/setting"
|
||||
)
|
||||
|
||||
// ircMsgParser парсит сообщения, прилетевшие из IRC-ки
|
||||
func ircMsgParser(channel string, nick string, user string, source string, msg string) {
|
||||
// ircMsgParser парсит сообщения, прилетевшие из IRC-ки.
|
||||
func ircMsgParser(channel string, nick string, user string, source string, msg string) { //nolint: revive
|
||||
// nick - это выбранный пользователем nick (если он занят, то его "нарисует" сервер)
|
||||
// user - это короткое имя пользователя, под которым его видит сервер
|
||||
// source - это длинное имя пользователя, оно содержит в себе помимо user, ещё и ip с которого пришёл пользователь
|
||||
if shutdown {
|
||||
if Shutdown {
|
||||
// Если мы завершаем работу программы, то нам ничего обрабатывать не надо
|
||||
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 message sMsg
|
||||
message.From = config.Redis.MyChannel
|
||||
message.From = Config.Redis.MyChannel
|
||||
message.Userid = user // как его видит сервер
|
||||
message.Chatid = channel // чятик, в который написал user
|
||||
message.Plugin = config.Redis.MyChannel
|
||||
message.Threadid = "" // тредиков в irc нету, поэтому это поле отправляем пустым
|
||||
message.Plugin = Config.Redis.MyChannel
|
||||
message.Mode = "public"
|
||||
// Предполагается, что на команды бот автоматом отвечает
|
||||
message.Misc.Answer = 1
|
||||
message.Misc.Fwdcnt = 0
|
||||
message.Misc.Csign = config.Csign
|
||||
message.Misc.Csign = Config.Csign
|
||||
message.Misc.Username = nick
|
||||
message.Misc.Botnick = config.Irc.Nick
|
||||
message.Misc.Botnick = Config.Irc.Nick
|
||||
message.Misc.Msgformat = 0
|
||||
|
||||
var cmd = msg[len(config.Csign):]
|
||||
var cmd = msg[len(Config.Csign):]
|
||||
|
||||
switch {
|
||||
case cmd == "help" || msg == "помощь":
|
||||
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("%sbuni - комикс-стрип hapi buni", 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("%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("%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("%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("%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("%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("%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("%skarma фраза - посмотреть карму фразы", 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.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("%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("%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("%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("%sверсия - написать что-то про версию ПО", 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("%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("%sbuni - комикс-стрип hapi buni", 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("%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("%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("%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("%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("%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("%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("%skarma фраза - посмотреть карму фразы", 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.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("%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("%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("%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("%sверсия - написать что-то про версию ПО", 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)}
|
||||
|
||||
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
|
||||
|
||||
case cmd == "admin":
|
||||
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 показываем ли сисечки по просьбе участников чата (команды %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 показываем ли попки по просьбе участников чата (команды %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 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 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)}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
case cmd == "admin oboobs":
|
||||
if userModeIsOped(channel, nick) {
|
||||
value := getSetting(channel, "oboobs")
|
||||
value := setting.Get(channel, "oboobs")
|
||||
|
||||
switch {
|
||||
case value == "":
|
||||
_ = saveSetting(channel, "oboobs", "0")
|
||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs выключен"}
|
||||
case value == "0":
|
||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs выключен"}
|
||||
case value == "1":
|
||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs включен"}
|
||||
switch value {
|
||||
case "":
|
||||
_ = setting.Set(channel, "oboobs", "0")
|
||||
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs выключен"}
|
||||
case "0":
|
||||
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs выключен"}
|
||||
case "1":
|
||||
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs включен"}
|
||||
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":
|
||||
if userModeIsOped(channel, nick) {
|
||||
err := saveSetting(channel, "oboobs", "1")
|
||||
err := setting.Set(channel, "oboobs", "1")
|
||||
|
||||
if err != nil {
|
||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs всё ещё выключен"}
|
||||
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs всё ещё выключен"}
|
||||
} 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":
|
||||
if userModeIsOped(channel, nick) {
|
||||
_ = saveSetting(channel, "oboobs", "0")
|
||||
imChan <- iMsg{ChatId: nick, Text: "Плагин oboobs выключен"}
|
||||
_ = setting.Set(channel, "oboobs", "0")
|
||||
imChan <- iMsg{ChatID: nick, Text: "Плагин oboobs выключен"}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
case cmd == "admin obutts":
|
||||
if userModeIsOped(channel, nick) {
|
||||
value := getSetting(channel, "obutts")
|
||||
value := setting.Get(channel, "obutts")
|
||||
|
||||
switch {
|
||||
case value == "":
|
||||
_ = saveSetting(channel, "obutts", "0")
|
||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts выключен"}
|
||||
case value == "0":
|
||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts выключен"}
|
||||
case value == "1":
|
||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts включен"}
|
||||
switch value {
|
||||
case "":
|
||||
_ = setting.Set(channel, "obutts", "0")
|
||||
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts выключен"}
|
||||
case "0":
|
||||
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts выключен"}
|
||||
case "1":
|
||||
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts включен"}
|
||||
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":
|
||||
if userModeIsOped(channel, nick) {
|
||||
err := saveSetting(channel, "obutts", "1")
|
||||
err := setting.Set(channel, "obutts", "1")
|
||||
|
||||
if err != nil {
|
||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts всё ещё выключен"}
|
||||
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts всё ещё выключен"}
|
||||
} 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":
|
||||
if userModeIsOped(channel, nick) {
|
||||
_ = saveSetting(channel, "obutts", "0")
|
||||
imChan <- iMsg{ChatId: nick, Text: "Плагин obutts выключен"}
|
||||
_ = setting.Set(channel, "obutts", "0")
|
||||
imChan <- iMsg{ChatID: nick, Text: "Плагин obutts выключен"}
|
||||
}
|
||||
|
||||
return
|
||||
@@ -181,15 +183,17 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
||||
// Общий список простых команд
|
||||
cmds := []string{"ping", "пинг", "пинх", "pong", "понг", "понх", "coin", "монетка", "roll", "dice", "кости",
|
||||
"ver", "version", "версия", "хэлп", "halp", "kde", "кде", "lat", "лат", "friday", "пятница", "proverb",
|
||||
"пословица", "fortune", "фортунка", "f", "ф", "anek", "анек", "анекдот", "buni", "cat", "кис", "drink",
|
||||
"праздник", "fox", "лис", "frog", "лягушка", "horse", "лошадь", "лошадка", "monkeyuser", "owl", "сова",
|
||||
"сыч", "rabbit", "bunny", "кролик", "snail", "улитка", "xkcd", "dig", "копать", "fish", "fishing", "рыба",
|
||||
"рыбка", "рыбалка", "karma", "карма"}
|
||||
"пословица", "пословиться", "fortune", "фортунка", "f", "ф", "anek", "анек", "анекдот", "buni", "cat",
|
||||
"кис", "drink", "праздник", "fox", "лис", "frog", "лягушка", "horse", "лошадь", "лошадка", "monkeyuser",
|
||||
"owl", "сова", "сыч", "rabbit", "bunny", "кролик", "snail", "улитка", "xkcd", "dig", "копать", "fish",
|
||||
"fishing", "рыба", "рыбка", "рыбалка", "karma", "карма", "w", "п", "weather", "погода", "погодка",
|
||||
"погадка"}
|
||||
|
||||
for _, command := range cmds {
|
||||
if cmd == command {
|
||||
done = true
|
||||
outgoingMessage = msg
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -204,6 +208,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
||||
if cmd == command {
|
||||
done = true
|
||||
outgoingMessage = msg
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -224,7 +229,8 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
||||
message.Misc.Username = strings.TrimSpace(pile[1])
|
||||
} else {
|
||||
msg = fmt.Sprintf("Я тут не вижу участника с ником %s", userNick)
|
||||
imChan <- iMsg{ChatId: channel, Text: msg}
|
||||
imChan <- iMsg{ChatID: channel, Text: msg}
|
||||
|
||||
return
|
||||
}
|
||||
} // в противном случае юзер просто поставил пробел на конце команды, интерпретируем это как
|
||||
@@ -232,6 +238,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
||||
|
||||
outgoingMessage = msg
|
||||
done = true
|
||||
|
||||
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 {
|
||||
done = true
|
||||
outgoingMessage = msg
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -255,7 +263,7 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
||||
|
||||
// Отключаемые команды
|
||||
if !done {
|
||||
value := getSetting(channel, "obutts")
|
||||
value := setting.Get(channel, "obutts")
|
||||
|
||||
if value == "1" {
|
||||
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 {
|
||||
done = true
|
||||
outgoingMessage = msg
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !done {
|
||||
value = getSetting(channel, "oboobs")
|
||||
value = setting.Get(channel, "oboobs")
|
||||
|
||||
if value == "1" {
|
||||
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 {
|
||||
// done = true
|
||||
outgoingMessage = msg
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -295,24 +305,26 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Unable to to serialize message for redis: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Заталкиваем наш json в редиску
|
||||
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)
|
||||
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)
|
||||
} 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 {
|
||||
// Это уже просто трёп в чятике
|
||||
var message sMsg
|
||||
message.From = config.Redis.MyChannel
|
||||
message.From = Config.Redis.MyChannel
|
||||
message.Userid = user // как его видит сервер
|
||||
message.Chatid = channel // чятик, в который написал user
|
||||
message.Threadid = "" // тредиков в irc нету, поэтому это поле отправляем пустым
|
||||
message.Message = msg
|
||||
message.Plugin = config.Redis.MyChannel
|
||||
message.Plugin = Config.Redis.MyChannel
|
||||
message.Mode = "public"
|
||||
|
||||
// Если во фразе содержится ключевые слова, под это есть костыль в craniac-е - (предполагаем, что) он
|
||||
@@ -339,32 +351,31 @@ func ircMsgParser(channel string, nick string, user string, source string, msg s
|
||||
}
|
||||
|
||||
message.Misc.Fwdcnt = 0
|
||||
message.Misc.Csign = config.Csign
|
||||
message.Misc.Csign = Config.Csign
|
||||
message.Misc.Username = nick
|
||||
message.Misc.Botnick = config.Irc.Nick
|
||||
message.Misc.Botnick = Config.Irc.Nick
|
||||
message.Misc.Msgformat = 0
|
||||
|
||||
data, err := json.Marshal(message)
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Unable to to serialize message for redis: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Заталкиваем наш json в редиску
|
||||
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)
|
||||
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)
|
||||
} 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-чики должны быть относительно валидными
|
||||
func redisMsgParser(msg string) {
|
||||
if shutdown {
|
||||
// RedisMsgParser парсит json-чики прилетевшие из REDIS-ки, причём, json-чики должны быть относительно валидными.
|
||||
func RedisMsgParser(msg string) {
|
||||
if Shutdown {
|
||||
// Если мы завершаем работу программы, то нам ничего обрабатывать не надо
|
||||
return
|
||||
}
|
||||
@@ -375,50 +386,58 @@ func redisMsgParser(msg string) {
|
||||
|
||||
if err := json.Unmarshal([]byte(msg), &j); err != nil {
|
||||
log.Warnf("Unable to to parse message from redis channel: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Validate our j
|
||||
if exist := j.From; exist == "" {
|
||||
log.Warnf("Incorrect msg from redis, no from field: %s", msg)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if exist := j.Chatid; exist == "" {
|
||||
log.Warnf("Incorrect msg from redis, no chatid field: %s", msg)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if exist := j.Userid; exist == "" {
|
||||
log.Warnf("Incorrect msg from redis, no userid field: %s", msg)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if exist := j.Message; exist == "" {
|
||||
log.Warnf("Incorrect msg from redis, no message field: %s", msg)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if exist := j.Plugin; exist == "" {
|
||||
log.Warnf("Incorrect msg from redis, no plugin field: %s", msg)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if exist := j.Mode; exist == "" {
|
||||
log.Warnf("Incorrect msg from redis, no mode field: %s", msg)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// j.Misc.Answer может и не быть, тогда ответа на такое сообщение не будет
|
||||
if j.Misc.Answer == 0 {
|
||||
log.Debug("Field Misc->Answer = 0, skipping message")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// j.Misc.BotNick тоже можно не передавать, тогда будет записана пустая строка
|
||||
// j.Misc.CSign если нам его не передали, возьмём значение из конфига
|
||||
if exist := j.Misc.Csign; exist == "" {
|
||||
j.Misc.Csign = config.Csign
|
||||
j.Misc.Csign = Config.Csign
|
||||
}
|
||||
|
||||
// j.Misc.FwdCnt если нам его не передали, то будет 0
|
||||
@@ -435,13 +454,11 @@ func redisMsgParser(msg string) {
|
||||
|
||||
for _, message := range lines {
|
||||
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 {
|
||||
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: */
|
||||
@@ -1,7 +1,7 @@
|
||||
package main
|
||||
package irc
|
||||
|
||||
// Конфиг
|
||||
type myConfig struct {
|
||||
// Конфиг.
|
||||
type MyConfig struct {
|
||||
Redis struct {
|
||||
Server string `json:"server,omitempty"`
|
||||
Port int `json:"port,omitempty"`
|
||||
@@ -35,15 +35,16 @@ type myConfig struct {
|
||||
DataDir string `json:"data_dir,omitempty"`
|
||||
}
|
||||
|
||||
// Входящее сообщение из pubsub-канала redis-ки
|
||||
// Входящее сообщение из pubsub-канала redis-ки.
|
||||
type rMsg struct {
|
||||
From string `json:"from,omitempty"`
|
||||
Chatid string `json:"chatid,omitempty"`
|
||||
Userid string `json:"userid,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Plugin string `json:"plugin,omitempty"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
Misc struct {
|
||||
From string `json:"from,omitempty"`
|
||||
Chatid string `json:"chatid,omitempty"`
|
||||
Userid string `json:"userid,omitempty"`
|
||||
Threadid string `json:"threadid,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Plugin string `json:"plugin,omitempty"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
Misc struct {
|
||||
Answer int64 `json:"answer,omitempty"`
|
||||
Botnick string `json:"bot_nick,omitempty"`
|
||||
Csign string `json:"csign,omitempty"`
|
||||
@@ -54,15 +55,16 @@ type rMsg struct {
|
||||
} `json:"Misc"`
|
||||
}
|
||||
|
||||
// Исходящее сообщение в pubsub-канал redis-ки
|
||||
// Исходящее сообщение в pubsub-канал redis-ки.
|
||||
type sMsg struct {
|
||||
From string `json:"from"`
|
||||
Chatid string `json:"chatid"`
|
||||
Userid string `json:"userid"`
|
||||
Message string `json:"message"`
|
||||
Plugin string `json:"plugin"`
|
||||
Mode string `json:"mode"`
|
||||
Misc struct {
|
||||
From string `json:"from"`
|
||||
Chatid string `json:"chatid"`
|
||||
Userid string `json:"userid"`
|
||||
Threadid string `json:"threadid"`
|
||||
Message string `json:"message"`
|
||||
Plugin string `json:"plugin"`
|
||||
Mode string `json:"mode"`
|
||||
Misc struct {
|
||||
Answer int64 `json:"answer"`
|
||||
Botnick string `json:"bot_nick"`
|
||||
Csign string `json:"csign"`
|
||||
@@ -73,14 +75,13 @@ type sMsg struct {
|
||||
} `json:"misc"`
|
||||
}
|
||||
|
||||
// Сообщение в irc
|
||||
// TODO: Имплементировать тип сообщения - privmsg или action
|
||||
// TODO: Имплементировать тип сообщения - privmsg или action.
|
||||
type iMsg struct {
|
||||
ChatId string
|
||||
ChatID string
|
||||
Text string
|
||||
}
|
||||
|
||||
// "Ведёрко" для реализации ограничителя скорости отправки исходящих сообщений - Token Bucket
|
||||
// "Ведёрко" для реализации ограничителя скорости отправки исходящих сообщений - Token Bucket.
|
||||
type bucket struct {
|
||||
Timestamps []int64
|
||||
IsFull bool
|
||||
@@ -1,24 +1,25 @@
|
||||
package main
|
||||
package irc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"aleesa-irc-go/internal/log"
|
||||
"aleesa-irc-go/internal/setting"
|
||||
|
||||
"github.com/hjson/hjson-go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Читает и валидирует конфиг, а также выставляет некоторые default-ы, если значений для параметров в конфиге нет
|
||||
func readConfig() {
|
||||
// ReadConfig читает и валидирует конфиг, а также выставляет некоторые default-ы, если значений для параметров в конфиге нет.
|
||||
func ReadConfig() {
|
||||
configLoaded := false
|
||||
executablePath, err := os.Executable()
|
||||
|
||||
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))
|
||||
@@ -41,27 +42,31 @@ func readConfig() {
|
||||
// Конфиг-файл длинноват для конфига, попробуем следующего кандидата
|
||||
if fileInfo.Size() > 65535 {
|
||||
log.Warnf("Config file %s is too long for config, skipping", location)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadFile(location)
|
||||
buf, err := os.ReadFile(location)
|
||||
|
||||
// Не удалось прочитать, попробуем следующего кандидата
|
||||
if err != nil {
|
||||
log.Warnf("Skip reading config file %s: %s", location, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// Исходя из документации, hjson какбы умеет парсить "кривой" json, но парсит его в map-ку.
|
||||
// Интереснее на выходе получить структурку: то есть мы вначале конфиг преобразуем в map-ку, затем эту map-ку
|
||||
// сериализуем в json, а потом json преврщааем в стркутурку. Не очень эффективно, но он и не часто требуется.
|
||||
var sampleConfig myConfig
|
||||
var sampleConfig MyConfig
|
||||
|
||||
var tmp map[string]interface{}
|
||||
err = hjson.Unmarshal(buf, &tmp)
|
||||
|
||||
// Не удалось распарсить - попробуем следующего кандидата
|
||||
if err != nil {
|
||||
log.Warnf("Skip parsing config file %s: %s", location, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -70,17 +75,20 @@ func readConfig() {
|
||||
// Не удалось преобразовать map-ку в json
|
||||
if err != nil {
|
||||
log.Warnf("Skip parsing config file %s: %s", location, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(tmpjson, &sampleConfig); err != nil {
|
||||
log.Warnf("Skip parsing config file %s: %s", location, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// Валидируем значения из конфига
|
||||
if sampleConfig.Redis.Server == "" {
|
||||
sampleConfig.Redis.Server = "localhost"
|
||||
|
||||
log.Infof("Redis server is not defined in config, using localhost")
|
||||
}
|
||||
|
||||
@@ -101,15 +109,17 @@ func readConfig() {
|
||||
// Значения для IRC-клиента
|
||||
if sampleConfig.Irc.Server == "" {
|
||||
sampleConfig.Irc.Server = "localhost"
|
||||
|
||||
log.Errorf("Irc server is not defined in config, using localhost")
|
||||
}
|
||||
|
||||
if sampleConfig.Irc.Port == 0 {
|
||||
sampleConfig.Irc.Port = 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
|
||||
}
|
||||
|
||||
@@ -180,9 +190,11 @@ func readConfig() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
config = sampleConfig
|
||||
Config = sampleConfig
|
||||
configLoaded = true
|
||||
|
||||
log.Infof("Using %s as config file", location)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
@@ -192,19 +204,19 @@ func readConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
// Хэндлер сигналов закрывает все бд, все сетевые соединения и сваливает из приложения
|
||||
func sigHandler() {
|
||||
// SigHandler хэндлер сигналов закрывает все бд, все сетевые соединения и сваливает из приложения.
|
||||
func SigHandler() {
|
||||
var err error
|
||||
|
||||
for {
|
||||
var s = <-sigChan
|
||||
var s = <-SigChan
|
||||
switch s {
|
||||
case syscall.SIGINT:
|
||||
log.Infoln("Got SIGINT, quitting")
|
||||
log.Info("Got SIGINT, quitting")
|
||||
case syscall.SIGTERM:
|
||||
log.Infoln("Got SIGTERM, quitting")
|
||||
log.Info("Got SIGTERM, quitting")
|
||||
case syscall.SIGQUIT:
|
||||
log.Infoln("Got SIGQUIT, quitting")
|
||||
log.Info("Got SIGQUIT, quitting")
|
||||
|
||||
// Заходим на новую итерацию, если у нас "неинтересный" сигнал
|
||||
default:
|
||||
@@ -212,16 +224,16 @@ func sigHandler() {
|
||||
}
|
||||
|
||||
// Чтобы не срать в логи ошибками от редиски, проставим 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)
|
||||
} else {
|
||||
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)
|
||||
} else {
|
||||
log.Debug("Close redis connection")
|
||||
@@ -229,16 +241,12 @@ func sigHandler() {
|
||||
|
||||
log.Debug("Close irc connection")
|
||||
ircClient.Quit()
|
||||
|
||||
log.Debug("Close userModeDB")
|
||||
userMode.Close()
|
||||
UserMode.Close()
|
||||
|
||||
if len(settingsDB) > 0 {
|
||||
log.Debug("Closing runtime irc channel settings db")
|
||||
|
||||
for _, db := range settingsDB {
|
||||
_ = db.Close()
|
||||
}
|
||||
}
|
||||
log.Debug("Close settingsDB")
|
||||
setting.CloseAll()
|
||||
|
||||
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 (
|
||||
"crypto/sha256"
|
||||
@@ -6,14 +6,20 @@ import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
|
||||
"aleesa-irc-go/internal/log"
|
||||
|
||||
"github.com/cockroachdb/errors/oserror"
|
||||
"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 {
|
||||
var kArray = []byte(key)
|
||||
|
||||
var vArray = []byte(value)
|
||||
|
||||
err := db.Set(kArray, vArray, pebble.Sync)
|
||||
@@ -21,11 +27,12 @@ func StoreKV(db *pebble.DB, key string, value string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// FetchV достаёт значение по ключу
|
||||
// FetchV достаёт значение по ключу.
|
||||
func FetchV(db *pebble.DB, key string) (string, error) {
|
||||
var kArray = []byte(key)
|
||||
|
||||
var vArray []byte
|
||||
|
||||
var valueString = ""
|
||||
|
||||
vArray, closer, err := db.Get(kArray)
|
||||
@@ -40,8 +47,8 @@ func FetchV(db *pebble.DB, key string) (string, error) {
|
||||
return valueString, err
|
||||
}
|
||||
|
||||
// Достанем настройку из БД с настройками
|
||||
func getSetting(chatID string, setting string) string {
|
||||
// Достанем настройку из БД с настройками.
|
||||
func Get(chatID string, setting string) string {
|
||||
var err error
|
||||
|
||||
chatHash := sha256.Sum256([]byte(chatID))
|
||||
@@ -54,10 +61,11 @@ func getSetting(chatID string, setting string) string {
|
||||
// настройками порождается огромное количество файлов. Умолчальное ограничение на количество файлов - 500 штук,
|
||||
// что нас не устраивает, поэтому немного снизим эту цифру до более приемлемых значений
|
||||
options.L0CompactionFileThreshold = 8
|
||||
settingsDB[database], err = pebble.Open(config.DataDir+"/"+database, &options)
|
||||
settingsDB[database], err = pebble.Open(DataDir+"/"+database, &options)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Unable to open settings db, %s: %s\n", database, err)
|
||||
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -66,13 +74,14 @@ func getSetting(chatID string, setting string) string {
|
||||
|
||||
// Если из базы ничего не вынулось, по каким-то причинам, то просто вернём пустую строку
|
||||
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)
|
||||
} 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)
|
||||
} 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)
|
||||
} else {
|
||||
default:
|
||||
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
|
||||
}
|
||||
|
||||
// Сохраним настройку в БД с настройками
|
||||
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 database = fmt.Sprintf("settings_db/%x", chatHash)
|
||||
|
||||
var err error
|
||||
|
||||
// Если БД не открыта, откроем её
|
||||
@@ -96,10 +107,11 @@ func saveSetting(chatID string, setting string, value string) error {
|
||||
// что нас не устраивает, поэтому немного снизим эту цифру до более приемлемых значений
|
||||
options.L0CompactionFileThreshold = 8
|
||||
|
||||
settingsDB[database], err = pebble.Open(config.DataDir+"/"+database, &options)
|
||||
settingsDB[database], err = pebble.Open(DataDir+"/"+database, &options)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Unable to open settings db, %s: %s\n", database, err)
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -111,4 +123,15 @@ func saveSetting(chatID string, setting string, value string) error {
|
||||
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: */
|
||||
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://github.com/cespare/xxhash/actions/workflows/test.yml)
|
||||
|
||||
xxhash is a Go implementation of the 64-bit
|
||||
[xxHash](http://cyan4973.github.io/xxHash/) algorithm, XXH64. This is a
|
||||
xxhash is a Go implementation of the 64-bit [xxHash] algorithm, XXH64. This is a
|
||||
high-quality hashing algorithm that is much faster than anything in the Go
|
||||
standard library.
|
||||
|
||||
@@ -25,8 +24,11 @@ func (*Digest) WriteString(string) (int, error)
|
||||
func (*Digest) Sum64() uint64
|
||||
```
|
||||
|
||||
This implementation provides a fast pure-Go implementation and an even faster
|
||||
assembly implementation for amd64.
|
||||
The package is written with optimized pure Go and also contains even faster
|
||||
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
|
||||
|
||||
@@ -45,19 +47,20 @@ I recommend using the latest release of Go.
|
||||
Here are some quick benchmarks comparing the pure-Go and assembly
|
||||
implementations of Sum64.
|
||||
|
||||
| input size | purego | asm |
|
||||
| --- | --- | --- |
|
||||
| 5 B | 979.66 MB/s | 1291.17 MB/s |
|
||||
| 100 B | 7475.26 MB/s | 7973.40 MB/s |
|
||||
| 4 KB | 17573.46 MB/s | 17602.65 MB/s |
|
||||
| 10 MB | 17131.46 MB/s | 17142.16 MB/s |
|
||||
| input size | purego | asm |
|
||||
| ---------- | --------- | --------- |
|
||||
| 4 B | 1.3 GB/s | 1.2 GB/s |
|
||||
| 16 B | 2.9 GB/s | 3.5 GB/s |
|
||||
| 100 B | 6.9 GB/s | 8.1 GB/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
|
||||
the following commands under Go 1.11.2:
|
||||
These numbers were generated on Ubuntu 20.04 with an Intel Xeon Platinum 8252C
|
||||
CPU using the following commands under Go 1.19.2:
|
||||
|
||||
```
|
||||
$ go test -tags purego -benchtime 10s -bench '/xxhash,direct,bytes'
|
||||
$ go test -benchtime 10s -bench '/xxhash,direct,bytes'
|
||||
benchstat <(go test -tags purego -benchtime 500ms -count 15 -bench 'Sum64$')
|
||||
benchstat <(go test -benchtime 500ms -count 15 -bench 'Sum64$')
|
||||
```
|
||||
|
||||
## 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
|
||||
)
|
||||
|
||||
// NOTE(caleb): I'm using both consts and vars of the primes. Using consts where
|
||||
// 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
|
||||
// convenience in the Go code in a few places where we need to intentionally
|
||||
// avoid constant arithmetic (e.g., v1 := prime1 + prime2 fails because the
|
||||
// result overflows a uint64).
|
||||
var (
|
||||
prime1v = prime1
|
||||
prime2v = prime2
|
||||
prime3v = prime3
|
||||
prime4v = prime4
|
||||
prime5v = prime5
|
||||
)
|
||||
// Store the primes in an array as well.
|
||||
//
|
||||
// The consts are used when possible in Go code to avoid MOVs but we need a
|
||||
// contiguous array of the assembly code.
|
||||
var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5}
|
||||
|
||||
// Digest implements hash.Hash64.
|
||||
type Digest struct {
|
||||
@@ -50,10 +42,10 @@ func New() *Digest {
|
||||
|
||||
// Reset clears the Digest's state so that it can be reused.
|
||||
func (d *Digest) Reset() {
|
||||
d.v1 = prime1v + prime2
|
||||
d.v1 = primes[0] + prime2
|
||||
d.v2 = prime2
|
||||
d.v3 = 0
|
||||
d.v4 = -prime1v
|
||||
d.v4 = -primes[0]
|
||||
d.total = 0
|
||||
d.n = 0
|
||||
}
|
||||
@@ -69,21 +61,23 @@ func (d *Digest) Write(b []byte) (n int, err error) {
|
||||
n = len(b)
|
||||
d.total += uint64(n)
|
||||
|
||||
memleft := d.mem[d.n&(len(d.mem)-1):]
|
||||
|
||||
if d.n+n < 32 {
|
||||
// This new data doesn't even fill the current block.
|
||||
copy(d.mem[d.n:], b)
|
||||
copy(memleft, b)
|
||||
d.n += n
|
||||
return
|
||||
}
|
||||
|
||||
if d.n > 0 {
|
||||
// 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.v2 = round(d.v2, u64(d.mem[8:16]))
|
||||
d.v3 = round(d.v3, u64(d.mem[16:24]))
|
||||
d.v4 = round(d.v4, u64(d.mem[24:32]))
|
||||
b = b[32-d.n:]
|
||||
b = b[c:]
|
||||
d.n = 0
|
||||
}
|
||||
|
||||
@@ -133,21 +127,20 @@ func (d *Digest) Sum64() uint64 {
|
||||
|
||||
h += d.total
|
||||
|
||||
i, end := 0, d.n
|
||||
for ; i+8 <= end; i += 8 {
|
||||
k1 := round(0, u64(d.mem[i:i+8]))
|
||||
b := d.mem[:d.n&(len(d.mem)-1)]
|
||||
for ; len(b) >= 8; b = b[8:] {
|
||||
k1 := round(0, u64(b[:8]))
|
||||
h ^= k1
|
||||
h = rol27(h)*prime1 + prime4
|
||||
}
|
||||
if i+4 <= end {
|
||||
h ^= uint64(u32(d.mem[i:i+4])) * prime1
|
||||
if len(b) >= 4 {
|
||||
h ^= uint64(u32(b[:4])) * prime1
|
||||
h = rol23(h)*prime2 + prime3
|
||||
i += 4
|
||||
b = b[4:]
|
||||
}
|
||||
for i < end {
|
||||
h ^= uint64(d.mem[i]) * prime5
|
||||
for ; len(b) > 0; b = b[1:] {
|
||||
h ^= uint64(b[0]) * prime5
|
||||
h = rol11(h) * prime1
|
||||
i++
|
||||
}
|
||||
|
||||
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 gc
|
||||
// +build !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// Register allocation:
|
||||
// AX h
|
||||
// SI pointer to advance through b
|
||||
// DX n
|
||||
// BX loop end
|
||||
// R8 v1, k1
|
||||
// R9 v2
|
||||
// R10 v3
|
||||
// R11 v4
|
||||
// R12 tmp
|
||||
// R13 prime1v
|
||||
// R14 prime2v
|
||||
// DI prime4v
|
||||
// Registers:
|
||||
#define h AX
|
||||
#define d AX
|
||||
#define p SI // pointer to advance through b
|
||||
#define n DX
|
||||
#define end BX // loop end
|
||||
#define v1 R8
|
||||
#define v2 R9
|
||||
#define v3 R10
|
||||
#define v4 R11
|
||||
#define x R12
|
||||
#define prime1 R13
|
||||
#define prime2 R14
|
||||
#define prime4 DI
|
||||
|
||||
// round reads from and advances the buffer pointer in SI.
|
||||
// It assumes that R13 has prime1v and R14 has prime2v.
|
||||
#define round(r) \
|
||||
MOVQ (SI), R12 \
|
||||
ADDQ $8, SI \
|
||||
IMULQ R14, R12 \
|
||||
ADDQ R12, r \
|
||||
ROLQ $31, r \
|
||||
IMULQ R13, r
|
||||
#define round(acc, x) \
|
||||
IMULQ prime2, x \
|
||||
ADDQ x, acc \
|
||||
ROLQ $31, acc \
|
||||
IMULQ prime1, acc
|
||||
|
||||
// mergeRound applies a merge round on the two registers acc and val.
|
||||
// It assumes that R13 has prime1v, R14 has prime2v, and DI has prime4v.
|
||||
#define mergeRound(acc, val) \
|
||||
IMULQ R14, val \
|
||||
ROLQ $31, val \
|
||||
IMULQ R13, val \
|
||||
XORQ val, acc \
|
||||
IMULQ R13, acc \
|
||||
ADDQ DI, acc
|
||||
// round0 performs the operation x = round(0, x).
|
||||
#define round0(x) \
|
||||
IMULQ prime2, x \
|
||||
ROLQ $31, x \
|
||||
IMULQ prime1, x
|
||||
|
||||
// mergeRound applies a merge round on the two registers acc and x.
|
||||
// It assumes that prime1, prime2, and prime4 have been loaded.
|
||||
#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
|
||||
TEXT ·Sum64(SB), NOSPLIT, $0-32
|
||||
TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32
|
||||
// Load fixed primes.
|
||||
MOVQ ·prime1v(SB), R13
|
||||
MOVQ ·prime2v(SB), R14
|
||||
MOVQ ·prime4v(SB), DI
|
||||
MOVQ ·primes+0(SB), prime1
|
||||
MOVQ ·primes+8(SB), prime2
|
||||
MOVQ ·primes+24(SB), prime4
|
||||
|
||||
// Load slice.
|
||||
MOVQ b_base+0(FP), SI
|
||||
MOVQ b_len+8(FP), DX
|
||||
LEAQ (SI)(DX*1), BX
|
||||
MOVQ b_base+0(FP), p
|
||||
MOVQ b_len+8(FP), n
|
||||
LEAQ (p)(n*1), end
|
||||
|
||||
// The first loop limit will be len(b)-32.
|
||||
SUBQ $32, BX
|
||||
SUBQ $32, end
|
||||
|
||||
// Check whether we have at least one block.
|
||||
CMPQ DX, $32
|
||||
CMPQ n, $32
|
||||
JLT noBlocks
|
||||
|
||||
// Set up initial state (v1, v2, v3, v4).
|
||||
MOVQ R13, R8
|
||||
ADDQ R14, R8
|
||||
MOVQ R14, R9
|
||||
XORQ R10, R10
|
||||
XORQ R11, R11
|
||||
SUBQ R13, R11
|
||||
MOVQ prime1, v1
|
||||
ADDQ prime2, v1
|
||||
MOVQ prime2, v2
|
||||
XORQ v3, v3
|
||||
XORQ v4, v4
|
||||
SUBQ prime1, v4
|
||||
|
||||
// Loop until SI > BX.
|
||||
blockLoop:
|
||||
round(R8)
|
||||
round(R9)
|
||||
round(R10)
|
||||
round(R11)
|
||||
blockLoop()
|
||||
|
||||
CMPQ SI, BX
|
||||
JLE blockLoop
|
||||
MOVQ v1, h
|
||||
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
|
||||
ROLQ $1, AX
|
||||
MOVQ R9, R12
|
||||
ROLQ $7, R12
|
||||
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)
|
||||
mergeRound(h, v1)
|
||||
mergeRound(h, v2)
|
||||
mergeRound(h, v3)
|
||||
mergeRound(h, v4)
|
||||
|
||||
JMP afterBlocks
|
||||
|
||||
noBlocks:
|
||||
MOVQ ·prime5v(SB), AX
|
||||
MOVQ ·primes+32(SB), h
|
||||
|
||||
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, BX
|
||||
ADDQ $24, end
|
||||
CMPQ p, end
|
||||
JG try4
|
||||
|
||||
CMPQ SI, BX
|
||||
JG fourByte
|
||||
loop8:
|
||||
MOVQ (p), x
|
||||
ADDQ $8, p
|
||||
round0(x)
|
||||
XORQ x, h
|
||||
ROLQ $27, h
|
||||
IMULQ prime1, h
|
||||
ADDQ prime4, h
|
||||
|
||||
wordLoop:
|
||||
// Calculate k1.
|
||||
MOVQ (SI), R8
|
||||
ADDQ $8, SI
|
||||
IMULQ R14, R8
|
||||
ROLQ $31, R8
|
||||
IMULQ R13, R8
|
||||
CMPQ p, end
|
||||
JLE loop8
|
||||
|
||||
XORQ R8, AX
|
||||
ROLQ $27, AX
|
||||
IMULQ R13, AX
|
||||
ADDQ DI, AX
|
||||
try4:
|
||||
ADDQ $4, end
|
||||
CMPQ p, end
|
||||
JG try1
|
||||
|
||||
CMPQ SI, BX
|
||||
JLE wordLoop
|
||||
MOVL (p), x
|
||||
ADDQ $4, p
|
||||
IMULQ prime1, x
|
||||
XORQ x, h
|
||||
|
||||
fourByte:
|
||||
ADDQ $4, BX
|
||||
CMPQ SI, BX
|
||||
JG singles
|
||||
ROLQ $23, h
|
||||
IMULQ prime2, h
|
||||
ADDQ ·primes+16(SB), h
|
||||
|
||||
MOVL (SI), R8
|
||||
ADDQ $4, SI
|
||||
IMULQ R13, R8
|
||||
XORQ R8, AX
|
||||
|
||||
ROLQ $23, AX
|
||||
IMULQ R14, AX
|
||||
ADDQ ·prime3v(SB), AX
|
||||
|
||||
singles:
|
||||
ADDQ $4, BX
|
||||
CMPQ SI, BX
|
||||
try1:
|
||||
ADDQ $4, end
|
||||
CMPQ p, end
|
||||
JGE finalize
|
||||
|
||||
singlesLoop:
|
||||
MOVBQZX (SI), R12
|
||||
ADDQ $1, SI
|
||||
IMULQ ·prime5v(SB), R12
|
||||
XORQ R12, AX
|
||||
loop1:
|
||||
MOVBQZX (p), x
|
||||
ADDQ $1, p
|
||||
IMULQ ·primes+32(SB), x
|
||||
XORQ x, h
|
||||
ROLQ $11, h
|
||||
IMULQ prime1, h
|
||||
|
||||
ROLQ $11, AX
|
||||
IMULQ R13, AX
|
||||
|
||||
CMPQ SI, BX
|
||||
JL singlesLoop
|
||||
CMPQ p, end
|
||||
JL loop1
|
||||
|
||||
finalize:
|
||||
MOVQ AX, R12
|
||||
SHRQ $33, R12
|
||||
XORQ R12, AX
|
||||
IMULQ R14, AX
|
||||
MOVQ AX, R12
|
||||
SHRQ $29, R12
|
||||
XORQ R12, AX
|
||||
IMULQ ·prime3v(SB), AX
|
||||
MOVQ AX, R12
|
||||
SHRQ $32, R12
|
||||
XORQ R12, AX
|
||||
MOVQ h, x
|
||||
SHRQ $33, x
|
||||
XORQ x, h
|
||||
IMULQ prime2, h
|
||||
MOVQ h, x
|
||||
SHRQ $29, x
|
||||
XORQ x, h
|
||||
IMULQ ·primes+16(SB), h
|
||||
MOVQ h, x
|
||||
SHRQ $32, x
|
||||
XORQ x, h
|
||||
|
||||
MOVQ AX, ret+24(FP)
|
||||
MOVQ h, ret+24(FP)
|
||||
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
|
||||
TEXT ·writeBlocks(SB), NOSPLIT, $0-40
|
||||
TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40
|
||||
// Load fixed primes needed for round.
|
||||
MOVQ ·prime1v(SB), R13
|
||||
MOVQ ·prime2v(SB), R14
|
||||
MOVQ ·primes+0(SB), prime1
|
||||
MOVQ ·primes+8(SB), prime2
|
||||
|
||||
// Load slice.
|
||||
MOVQ b_base+8(FP), SI
|
||||
MOVQ b_len+16(FP), DX
|
||||
LEAQ (SI)(DX*1), BX
|
||||
SUBQ $32, BX
|
||||
MOVQ b_base+8(FP), p
|
||||
MOVQ b_len+16(FP), n
|
||||
LEAQ (p)(n*1), end
|
||||
SUBQ $32, end
|
||||
|
||||
// Load vN from d.
|
||||
MOVQ d+0(FP), AX
|
||||
MOVQ 0(AX), R8 // v1
|
||||
MOVQ 8(AX), R9 // v2
|
||||
MOVQ 16(AX), R10 // v3
|
||||
MOVQ 24(AX), R11 // v4
|
||||
MOVQ s+0(FP), d
|
||||
MOVQ 0(d), v1
|
||||
MOVQ 8(d), v2
|
||||
MOVQ 16(d), v3
|
||||
MOVQ 24(d), v4
|
||||
|
||||
// We don't need to check the loop condition here; this function is
|
||||
// always called with at least one block of data to process.
|
||||
blockLoop:
|
||||
round(R8)
|
||||
round(R9)
|
||||
round(R10)
|
||||
round(R11)
|
||||
|
||||
CMPQ SI, BX
|
||||
JLE blockLoop
|
||||
blockLoop()
|
||||
|
||||
// Copy vN back to d.
|
||||
MOVQ R8, 0(AX)
|
||||
MOVQ R9, 8(AX)
|
||||
MOVQ R10, 16(AX)
|
||||
MOVQ R11, 24(AX)
|
||||
MOVQ v1, 0(d)
|
||||
MOVQ v2, 8(d)
|
||||
MOVQ v3, 16(d)
|
||||
MOVQ v4, 24(d)
|
||||
|
||||
// The number of bytes written is SI minus the old base pointer.
|
||||
SUBQ b_base+8(FP), SI
|
||||
MOVQ SI, ret+32(FP)
|
||||
// The number of bytes written is p minus the old base pointer.
|
||||
SUBQ b_base+8(FP), p
|
||||
MOVQ p, ret+32(FP)
|
||||
|
||||
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 gc
|
||||
// +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
|
||||
|
||||
@@ -14,10 +15,10 @@ func Sum64(b []byte) uint64 {
|
||||
var h uint64
|
||||
|
||||
if n >= 32 {
|
||||
v1 := prime1v + prime2
|
||||
v1 := primes[0] + prime2
|
||||
v2 := prime2
|
||||
v3 := uint64(0)
|
||||
v4 := -prime1v
|
||||
v4 := -primes[0]
|
||||
for len(b) >= 32 {
|
||||
v1 = round(v1, u64(b[0:8:len(b)]))
|
||||
v2 = round(v2, u64(b[8:16:len(b)]))
|
||||
@@ -36,19 +37,18 @@ func Sum64(b []byte) uint64 {
|
||||
|
||||
h += uint64(n)
|
||||
|
||||
i, end := 0, len(b)
|
||||
for ; i+8 <= end; i += 8 {
|
||||
k1 := round(0, u64(b[i:i+8:len(b)]))
|
||||
for ; len(b) >= 8; b = b[8:] {
|
||||
k1 := round(0, u64(b[:8]))
|
||||
h ^= k1
|
||||
h = rol27(h)*prime1 + prime4
|
||||
}
|
||||
if i+4 <= end {
|
||||
h ^= uint64(u32(b[i:i+4:len(b)])) * prime1
|
||||
if len(b) >= 4 {
|
||||
h ^= uint64(u32(b[:4])) * prime1
|
||||
h = rol23(h)*prime2 + prime3
|
||||
i += 4
|
||||
b = b[4:]
|
||||
}
|
||||
for ; i < end; i++ {
|
||||
h ^= uint64(b[i]) * prime5
|
||||
for ; len(b) > 0; b = b[1:] {
|
||||
h ^= uint64(b[0]) * prime5
|
||||
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
|
||||
|
||||
// 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
|
||||
|
||||
// This file encapsulates usage of unsafe.
|
||||
@@ -11,7 +12,7 @@ import (
|
||||
|
||||
// In the future it's possible that compiler optimizations will make these
|
||||
// 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
|
||||
// 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/protoc-gen-gogo
|
||||
#
|
||||
# Note: as of 2020-11-01, it is not (yet) possible to use gogoproto
|
||||
# v1.3.x to generate .pb.go files so that they are compatible with
|
||||
# CockroachDB. This is because CockroachDB hardcodes gogoproto at
|
||||
# version v1.2, and thus does not provide v1.3's
|
||||
# proto.GoGoProtoPackageIsVersion3.
|
||||
# Note: as of 2021-04-13, we like to use a custom protoc-gen-gogo
|
||||
# with additional options, to stabilize the marshalled
|
||||
# encoding of objects (so that they are deterministic
|
||||
# across marshal/unmarshal cycles) and reduce the memory footprint
|
||||
# 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
|
||||
# adequate; instead:
|
||||
@@ -23,21 +26,33 @@
|
||||
#
|
||||
# 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)
|
||||
|
||||
SED = sed
|
||||
SED_INPLACE := $(shell $(SED) --version 2>&1 | grep -q GNU && echo -i || echo "-i ''")
|
||||
|
||||
all: $(PROTOS)
|
||||
protoc \
|
||||
-I$$GOPATH/src/ \
|
||||
-I$$GOPATH/src/github.com \
|
||||
-I$$GOPATH/src/github.com/gogo/protobuf \
|
||||
-I$$GOPATH/src/github.com/gogo/protobuf/protobuf \
|
||||
--gogoroach_out=Mgoogle/protobuf/any.proto=github.com/gogo/protobuf/types,plugins=grpc,import_prefix=:. \
|
||||
$(PROTOS:%=$(GOPATH)/src/github.com/cockroachdb/errors/%)
|
||||
mv -f github.com/cockroachdb/errors/errorspb/*.pb.go errorspb/
|
||||
set -e; for dir in $(sort $(dir $(PROTOS))); do \
|
||||
protoc \
|
||||
-I. \
|
||||
-I$$GOPATH/src/ \
|
||||
-I$$GOPATH/src/github.com \
|
||||
-I$$GOPATH/src/github.com/cockroachdb/errors \
|
||||
-I$$GOPATH/src/github.com/gogo/protobuf \
|
||||
-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 \
|
||||
-e '/import _ /d' \
|
||||
-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).
|
||||
|
||||
[](https://travis-ci.org/cockroachdb/errors)
|
||||

|
||||
[](https://pkg.go.dev/github.com/cockroachdb/errors)
|
||||
|
||||
Table of contents:
|
||||
|
||||
@@ -86,7 +87,7 @@ older version of the package.
|
||||
| detail, eg `WithDetail()` | 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 |
|
||||
| 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 |
|
||||
| secondary errors, eg. `WithSecondaryError()`, `CombineErrors()` | 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.
|
||||
|
||||
- `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.
|
||||
- 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.
|
||||
- **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.
|
||||
- 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).)
|
||||
|
||||
**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
|
||||
(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
|
||||
library that need a custom encoder:
|
||||
@@ -535,9 +536,13 @@ Example use:
|
||||
| `WrapWithDepthf` | `WithMessagef` + `WithStackDepth` |
|
||||
| `AssertionFailedWithDepthf` | `NewWithDepthf` + `WithAssertionFailure` |
|
||||
| `NewAssertionErrorWithWrappedErrf` | `HandledWithMessagef` (barrier) + `WrapWithDepthf` + `WithAssertionFailure` |
|
||||
|
||||
| `Join` | `JoinWithDepth` (see below) |
|
||||
| `JoinWithDepth` | multi-cause wrapper + `WithStackDepth` |
|
||||
## 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
|
||||
// Access causes.
|
||||
func UnwrapAll(err error) error
|
||||
@@ -569,10 +574,16 @@ func RegisterLeafDecoder(typeName TypeKey, decoder LeafDecoder)
|
||||
func RegisterLeafEncoder(typeName TypeKey, encoder LeafEncoder)
|
||||
func RegisterWrapperDecoder(typeName TypeKey, decoder WrapperDecoder)
|
||||
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 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 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 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.
|
||||
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"
|
||||
|
||||
"github.com/cockroachdb/errors/errbase"
|
||||
"github.com/cockroachdb/redact"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
)
|
||||
|
||||
@@ -36,7 +37,7 @@ func Handled(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return HandledWithMessage(err, err.Error())
|
||||
return HandledWithSafeMessage(err, redact.Sprint(err))
|
||||
}
|
||||
|
||||
// HandledWithMessage is like Handled except the message is overridden.
|
||||
@@ -46,7 +47,14 @@ func HandledWithMessage(err error, msg string) error {
|
||||
if err == 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
|
||||
@@ -55,47 +63,48 @@ func HandledWithMessagef(err error, format string, args ...interface{}) error {
|
||||
if err == 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
|
||||
// matching via Is() and the Cause()/Unwrap() recursions.
|
||||
type barrierError struct {
|
||||
type barrierErr struct {
|
||||
// Message for the barrier itself.
|
||||
// In the common case, the message from the masked error
|
||||
// is used as-is (see Handled() above) however it is
|
||||
// useful to cache it here since the masked error may
|
||||
// have a long chain of wrappers and its Error() call
|
||||
// may be expensive.
|
||||
msg string
|
||||
smsg redact.RedactableString
|
||||
// Masked error chain.
|
||||
maskedErr error
|
||||
}
|
||||
|
||||
var _ error = (*barrierError)(nil)
|
||||
var _ errbase.SafeDetailer = (*barrierError)(nil)
|
||||
var _ errbase.SafeFormatter = (*barrierError)(nil)
|
||||
var _ fmt.Formatter = (*barrierError)(nil)
|
||||
var _ error = (*barrierErr)(nil)
|
||||
var _ errbase.SafeDetailer = (*barrierErr)(nil)
|
||||
var _ errbase.SafeFormatter = (*barrierErr)(nil)
|
||||
var _ fmt.Formatter = (*barrierErr)(nil)
|
||||
|
||||
// barrierError is an error.
|
||||
func (e *barrierError) Error() string { return e.msg }
|
||||
// barrierErr is an error.
|
||||
func (e *barrierErr) Error() string { return e.smsg.StripMarkers() }
|
||||
|
||||
// SafeDetails reports the PII-free details from the masked error.
|
||||
func (e *barrierError) SafeDetails() []string {
|
||||
func (e *barrierErr) SafeDetails() []string {
|
||||
var details []string
|
||||
for err := e.maskedErr; err != nil; err = errbase.UnwrapOnce(err) {
|
||||
sd := errbase.GetSafeDetails(err)
|
||||
details = sd.Fill(details)
|
||||
}
|
||||
details = append(details, redact.Sprintf("masked error: %+v", e.maskedErr).Redact().StripMarkers())
|
||||
return 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) {
|
||||
p.Print(e.msg)
|
||||
func (e *barrierErr) SafeFormatError(p errbase.Printer) (next error) {
|
||||
p.Print(e.smsg)
|
||||
if p.Detail() {
|
||||
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(
|
||||
ctx context.Context, err error,
|
||||
) (msg string, details []string, payload proto.Message) {
|
||||
e := err.(*barrierError)
|
||||
e := err.(*barrierErr)
|
||||
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.
|
||||
func decodeBarrier(ctx context.Context, msg string, _ []string, payload proto.Message) error {
|
||||
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() {
|
||||
tn := errbase.GetTypeKey((*barrierError)(nil))
|
||||
errbase.RegisterLeafDecoder(errbase.GetTypeKey((*barrierError)(nil)), decodeBarrierPrev)
|
||||
tn := errbase.GetTypeKey((*barrierErr)(nil))
|
||||
errbase.RegisterLeafDecoder(tn, decodeBarrier)
|
||||
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
|
||||
// `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
|
||||
// preserved across the network. To avoid creating a stateful interface
|
||||
// (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
|
||||
// `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
|
||||
// preserved across the network. To avoid creating a stateful interface
|
||||
// (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)
|
||||
}
|
||||
|
||||
// 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
|
||||
// exactly because it includes a non-serializable stack trace
|
||||
// object. In order to work with it, we encode it by dumping
|
||||
@@ -183,6 +188,8 @@ func init() {
|
||||
baseErr := goErr.New("")
|
||||
RegisterLeafDecoder(GetTypeKey(baseErr), decodeErrorString)
|
||||
|
||||
RegisterLeafDecoder(GetTypeKey(context.DeadlineExceeded), decodeDeadlineExceeded)
|
||||
|
||||
pkgE := pkgErr.New("")
|
||||
RegisterLeafEncoder(GetTypeKey(pkgE), encodePkgFundamental)
|
||||
|
||||
@@ -191,9 +198,11 @@ func init() {
|
||||
ws := pkgErr.WithStack(baseErr)
|
||||
RegisterWrapperEncoder(GetTypeKey(ws), encodePkgWithStack)
|
||||
|
||||
registerOsPathErrorMigration() // Needed for Go 1.16.
|
||||
pKey := GetTypeKey(&os.PathError{})
|
||||
RegisterWrapperEncoder(pKey, encodePathError)
|
||||
RegisterWrapperDecoder(pKey, decodePathError)
|
||||
|
||||
pKey = GetTypeKey(&os.LinkError{})
|
||||
RegisterWrapperEncoder(pKey, encodeLinkError)
|
||||
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.
|
||||
//
|
||||
// Can only be called if the EncodedError is set (see IsSet()).
|
||||
func DecodeError(ctx context.Context, enc EncodedError) error {
|
||||
if w := enc.GetWrapper(); w != nil {
|
||||
return decodeWrapper(ctx, w)
|
||||
@@ -55,6 +57,15 @@ func decodeLeaf(ctx context.Context, enc *errorspb.EncodedErrorLeaf) error {
|
||||
return genErr
|
||||
}
|
||||
// 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 {
|
||||
// Shortcut for non-registered proto-encodable error types:
|
||||
// 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
|
||||
// make it ready to re-encode exactly (if the error leaves over the
|
||||
// network again).
|
||||
@@ -95,7 +119,7 @@ func decodeWrapper(ctx context.Context, enc *errorspb.EncodedWrapper) error {
|
||||
typeKey := TypeKey(enc.Details.ErrorTypeMark.FamilyName)
|
||||
if decoder, ok := decoders[typeKey]; ok {
|
||||
// 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 {
|
||||
// Decoding succeeded. Use this.
|
||||
return genErr
|
||||
@@ -105,9 +129,10 @@ func decodeWrapper(ctx context.Context, enc *errorspb.EncodedWrapper) error {
|
||||
|
||||
// Otherwise, preserve all details about the original object.
|
||||
return &opaqueWrapper{
|
||||
cause: cause,
|
||||
prefix: enc.MessagePrefix,
|
||||
details: enc.Details,
|
||||
cause: cause,
|
||||
prefix: enc.Message,
|
||||
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.
|
||||
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 {
|
||||
return encodeWrapper(ctx, err, cause)
|
||||
}
|
||||
// Not a causer.
|
||||
return encodeLeaf(ctx, err)
|
||||
return encodeLeaf(ctx, err, UnwrapMulti(err))
|
||||
}
|
||||
|
||||
// encodeLeaf encodes a leaf error.
|
||||
func encodeLeaf(ctx context.Context, err error) EncodedError {
|
||||
// encodeLeaf encodes a leaf error. This function accepts a `causes`
|
||||
// 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 details errorspb.EncodedErrorDetails
|
||||
|
||||
if e, ok := err.(*opaqueLeaf); ok {
|
||||
msg = e.msg
|
||||
details = e.details
|
||||
} else if e, ok := err.(*opaqueLeafCauses); ok {
|
||||
msg = e.msg
|
||||
details = e.details
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
|
||||
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{
|
||||
Error: &errorspb.EncodedError_Leaf{
|
||||
Leaf: &errorspb.EncodedErrorLeaf{
|
||||
Message: msg,
|
||||
Details: details,
|
||||
Message: msg,
|
||||
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 {
|
||||
var msg string
|
||||
var details errorspb.EncodedErrorDetails
|
||||
messageType := Prefix
|
||||
|
||||
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
|
||||
details = e.details
|
||||
messageType = e.messageType
|
||||
} else {
|
||||
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.
|
||||
typeKey := TypeKey(details.ErrorTypeMark.FamilyName)
|
||||
if enc, ok := encoders[typeKey]; ok {
|
||||
msg, details.ReportablePayload, payload = enc(ctx, err)
|
||||
msg, details.ReportablePayload, payload, messageType = enc(ctx, err)
|
||||
} else {
|
||||
// No encoder.
|
||||
// In that case, we'll try to compute a message prefix
|
||||
// manually.
|
||||
msg = extractPrefix(err, cause)
|
||||
msg, messageType = extractPrefix(err, cause)
|
||||
|
||||
// If there are known safe details, use them.
|
||||
if s, ok := err.(SafeDetailer); ok {
|
||||
@@ -148,9 +172,10 @@ func encodeWrapper(ctx context.Context, err, cause error) EncodedError {
|
||||
return EncodedError{
|
||||
Error: &errorspb.EncodedError_Wrapper{
|
||||
Wrapper: &errorspb.EncodedWrapper{
|
||||
Cause: EncodeError(ctx, cause),
|
||||
MessagePrefix: msg,
|
||||
Details: details,
|
||||
Cause: EncodeError(ctx, cause),
|
||||
Message: msg,
|
||||
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.
|
||||
// For example,
|
||||
// err := errors.New("bar")
|
||||
// err = errors.Wrap(err, "foo")
|
||||
// extractPrefix(err)
|
||||
//
|
||||
// err := errors.New("bar")
|
||||
// err = errors.Wrap(err, "foo")
|
||||
// extractPrefix(err)
|
||||
//
|
||||
// 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()
|
||||
errMsg := err.Error()
|
||||
|
||||
if strings.HasSuffix(errMsg, 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, ": ") {
|
||||
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(
|
||||
@@ -183,6 +223,8 @@ func getTypeDetails(
|
||||
switch t := err.(type) {
|
||||
case *opaqueLeaf:
|
||||
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:
|
||||
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.
|
||||
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
|
||||
// the library. Registered wrappers will be encoded using their own
|
||||
// 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
|
||||
// prior to RegisterWrapperEncoder().
|
||||
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 {
|
||||
delete(encoders, theType)
|
||||
} else {
|
||||
@@ -304,7 +397,42 @@ func RegisterWrapperEncoder(theType TypeKey, encoder WrapperEncoder) {
|
||||
|
||||
// WrapperEncoder is to be provided (via RegisterWrapperEncoder above)
|
||||
// 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.
|
||||
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
|
||||
// post-order traversal. Since we have a linked list, the best we
|
||||
// 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.
|
||||
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
|
||||
// recursion. So we have no choice but to peel the data
|
||||
// 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.finishDisplay(verb)
|
||||
|
||||
@@ -195,7 +207,19 @@ func (s *state) formatEntries(err error) {
|
||||
// Wraps: (N) <details>
|
||||
//
|
||||
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]
|
||||
s.printEntry(entry)
|
||||
}
|
||||
@@ -278,8 +302,9 @@ func (s *state) printEntry(entry formatEntry) {
|
||||
//
|
||||
// This function is used both when FormatError() is called indirectly
|
||||
// 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.
|
||||
//
|
||||
@@ -329,13 +354,35 @@ func (s *state) formatSingleLineOutput() {
|
||||
// s.finalBuf is untouched. The conversion of s.entries
|
||||
// to s.finalBuf is done by formatSingleLineOutput() and/or
|
||||
// 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)
|
||||
numChildren := 0
|
||||
if cause != nil {
|
||||
// Recurse first.
|
||||
s.formatRecursive(cause, false /*isOutermost*/, withDetail)
|
||||
// Recurse first, which populates entries list starting from innermost
|
||||
// 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.
|
||||
s.wantDetail = withDetail
|
||||
s.needSpace = false
|
||||
@@ -349,75 +396,94 @@ func (s *state) formatRecursive(err error, isOutermost, withDetail bool) {
|
||||
|
||||
bufIsRedactable := false
|
||||
|
||||
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.
|
||||
for i := range s.entries {
|
||||
s.entries[i].elideShort = true
|
||||
}
|
||||
}
|
||||
break
|
||||
switch v := err.(type) {
|
||||
case SafeFormatter:
|
||||
bufIsRedactable = true
|
||||
desiredShortening := v.SafeFormatError((*safePrinter)(s))
|
||||
if desiredShortening == nil {
|
||||
// The error wants to elide the short messages from inner causes.
|
||||
// Read backwards through list of entries up to the number of new
|
||||
// entries created "under" this one amount and mark `elideShort`
|
||||
// true.
|
||||
s.elideShortChildren(numChildren)
|
||||
}
|
||||
}
|
||||
if !printDone {
|
||||
switch v := err.(type) {
|
||||
case SafeFormatter:
|
||||
bufIsRedactable = true
|
||||
desiredShortening := v.SafeFormatError((*safePrinter)(s))
|
||||
if desiredShortening == nil {
|
||||
|
||||
case Formatter:
|
||||
desiredShortening := v.FormatError((*printer)(s))
|
||||
if desiredShortening == nil {
|
||||
// The error wants to elide the short messages from inner
|
||||
// causes. Do it.
|
||||
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
|
||||
// causes. Do it.
|
||||
for i := range s.entries {
|
||||
s.entries[i].elideShort = true
|
||||
}
|
||||
s.elideShortChildren(numChildren)
|
||||
}
|
||||
}
|
||||
|
||||
case Formatter:
|
||||
desiredShortening := v.FormatError((*printer)(s))
|
||||
if desiredShortening == nil {
|
||||
// The error wants to elide the short messages from inner
|
||||
// causes. Do it.
|
||||
for i := range s.entries {
|
||||
s.entries[i].elideShort = true
|
||||
default:
|
||||
// Handle the special case overrides for context.Canceled,
|
||||
// os.PathError, etc for which we know how to extract some safe
|
||||
// strings.
|
||||
//
|
||||
// We need to do this in the `default` branch, instead of doing
|
||||
// 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:
|
||||
// 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 !printDone {
|
||||
// If the error did not implement errors.Formatter nor
|
||||
// fmt.Formatter, but it is a wrapper, still attempt best effort:
|
||||
// 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.
|
||||
entry := s.collectEntry(err, bufIsRedactable)
|
||||
entry := s.collectEntry(err, bufIsRedactable, withDepth, depth)
|
||||
|
||||
// If there's an embedded stack trace, also collect it.
|
||||
// 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.
|
||||
s.entries = append(s.entries, entry)
|
||||
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}
|
||||
if s.wantDetail {
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -490,16 +573,23 @@ func RegisterSpecialCasePrinter(fn safeErrorPrinterFn) {
|
||||
// formatSimple performs a best effort at extracting the details at a
|
||||
// given level of wrapping when the error object does not implement
|
||||
// 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
|
||||
elideCauses := false
|
||||
if cause != nil {
|
||||
pref = extractPrefix(err, cause)
|
||||
var messageType MessageType
|
||||
pref, messageType = extractPrefix(err, cause)
|
||||
if messageType == FullMessage {
|
||||
elideCauses = true
|
||||
}
|
||||
} else {
|
||||
pref = err.Error()
|
||||
}
|
||||
if len(pref) > 0 {
|
||||
s.Write([]byte(pref))
|
||||
}
|
||||
return elideCauses
|
||||
}
|
||||
|
||||
// finishDisplay renders s.finalBuf into s.State.
|
||||
@@ -558,7 +648,7 @@ type state struct {
|
||||
|
||||
// entries collect the result of formatRecursive(). They are
|
||||
// consumed by formatSingleLineOutput() and formatEntries() to
|
||||
// procude the contents of finalBuf.
|
||||
// produce the contents of finalBuf.
|
||||
entries []formatEntry
|
||||
|
||||
// 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
|
||||
// display a truncation indicator during verbose rendering.
|
||||
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.
|
||||
@@ -701,6 +796,12 @@ func (s *state) Write(b []byte) (n int, err error) {
|
||||
|
||||
for i, c := range b {
|
||||
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.
|
||||
s.buf.Write(b[k:i])
|
||||
// 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
|
||||
}
|
||||
}
|
||||
//if s.needNewline > 0 {
|
||||
// for i := 0; i < s.needNewline-1; i++ {
|
||||
// s.buf.Write(detailSep[:len(sep)-1])
|
||||
// }
|
||||
//}
|
||||
s.buf.Write(b[k:])
|
||||
return len(b), nil
|
||||
}
|
||||
@@ -756,6 +862,9 @@ func (p *state) switchOver() {
|
||||
p.buf = bytes.Buffer{}
|
||||
p.notEmpty = false
|
||||
p.hasDetail = true
|
||||
|
||||
// One of the newlines is accounted for in the switch over.
|
||||
// p.needNewline -= 1
|
||||
}
|
||||
|
||||
func (s *printer) Detail() bool {
|
||||
@@ -819,7 +928,7 @@ func (s *safePrinter) enhanceArgs(args []interface{}) {
|
||||
args[i] = redact.Safe(thisStack)
|
||||
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
|
||||
// already helps us recursing into a safe print for
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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 _ SafeDetailer = (*opaqueLeaf)(nil)
|
||||
var _ fmt.Formatter = (*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.
|
||||
// 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.
|
||||
type opaqueWrapper struct {
|
||||
cause error
|
||||
prefix string
|
||||
details errorspb.EncodedErrorDetails
|
||||
cause error
|
||||
prefix string
|
||||
details errorspb.EncodedErrorDetails
|
||||
messageType MessageType
|
||||
}
|
||||
|
||||
var _ error = (*opaqueWrapper)(nil)
|
||||
@@ -53,6 +73,9 @@ var _ SafeFormatter = (*opaqueWrapper)(nil)
|
||||
func (e *opaqueLeaf) Error() string { return e.msg }
|
||||
|
||||
func (e *opaqueWrapper) Error() string {
|
||||
if e.messageType == FullMessage {
|
||||
return e.prefix
|
||||
}
|
||||
if e.prefix == "" {
|
||||
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 *opaqueWrapper) SafeDetails() []string { return e.details.ReportablePayload }
|
||||
|
||||
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 *opaqueLeaf) 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) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
if e.messageType == FullMessage {
|
||||
return nil
|
||||
}
|
||||
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");
|
||||
// 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
|
||||
// permissions and limitations under the License.
|
||||
|
||||
// +build go1.16
|
||||
|
||||
package errbase
|
||||
|
||||
import "io"
|
||||
import "io/fs"
|
||||
|
||||
// formatSimpleError is a helper used by FormatError() for the top
|
||||
// level error/wrapper argument, if it does not implement the
|
||||
// errors.Formatter interface.
|
||||
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
|
||||
func registerOsPathErrorMigration() {
|
||||
// The os.PathError type was migrated to io.fs.PathError in Go 1.16.
|
||||
RegisterTypeMigration("os", "*os.PathError", &fs.PathError{})
|
||||
}
|
||||
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
|
||||
// github.com/pkg/errors) and `Wrapper` (`Unwrap()` method, from the
|
||||
// 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) {
|
||||
switch e := err.(type) {
|
||||
case interface{ Cause() error }:
|
||||
@@ -39,6 +49,7 @@ func UnwrapOnce(err error) (cause error) {
|
||||
|
||||
// UnwrapAll accesses the root cause object of the error.
|
||||
// If the error has no cause (leaf error), it is returned directly.
|
||||
// UnwrapAll treats multi-errors as leaf nodes.
|
||||
func UnwrapAll(err error) error {
|
||||
for {
|
||||
if cause := UnwrapOnce(err); cause != nil {
|
||||
@@ -49,3 +60,12 @@ func UnwrapAll(err error) error {
|
||||
}
|
||||
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.
|
||||
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
|
||||
// the library. Registered wrappers will be decoded using their own
|
||||
// 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.
|
||||
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.
|
||||
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
|
||||
// reflect.TypeOf(err).String() on the original error object.
|
||||
// This is usually composed as follows:
|
||||
// [*]<shortpackage>.<errortype>
|
||||
//
|
||||
// [*]<shortpackage>.<errortype>
|
||||
//
|
||||
// For example, Go's standard error type has name "*errors.errorString".
|
||||
// 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
|
||||
// of an error leaf.
|
||||
// of an error leaf or a multi-cause wrapper from go 1.20+
|
||||
message EncodedErrorLeaf {
|
||||
// The main error message (mandatory), that can be printed to human
|
||||
// users and may contain PII. This contains the value of the leaf
|
||||
@@ -26,6 +26,12 @@ message EncodedErrorLeaf {
|
||||
|
||||
// The error details.
|
||||
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 {
|
||||
@@ -60,24 +66,42 @@ message EncodedErrorDetails {
|
||||
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
|
||||
// of an error wrapper.
|
||||
message EncodedWrapper {
|
||||
// The cause error. Mandatory.
|
||||
EncodedError cause = 1 [(gogoproto.nullable) = false];
|
||||
|
||||
// The wrapper message prefix (which may be empty). This
|
||||
// isbprinted before the cause's own message when
|
||||
// constructing a full message. This may contain PII.
|
||||
// The wrapper message. This could either be a full error message
|
||||
// that can be printed independently, or a (potentially empty) prefix
|
||||
// which is printed before the cause's own message to construct the
|
||||
// full message. This may contain PII.
|
||||
//
|
||||
// This is extracted automatically:
|
||||
//
|
||||
// - for wrappers that have a registered encoder,
|
||||
// - otherwise, when the wrapper's Error() has its cause's Error() as suffix.
|
||||
string message_prefix = 2;
|
||||
string message = 2;
|
||||
|
||||
// The error details.
|
||||
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
|
||||
|
||||
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.
|
||||
// source: github.com/cockroachdb/errors/errorspb/hintdetail.proto
|
||||
// source: errorspb/hintdetail.proto
|
||||
|
||||
package errorspb
|
||||
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import io "io"
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -18,7 +20,7 @@ var _ = math.Inf
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// 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 {
|
||||
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 (*StringPayload) ProtoMessage() {}
|
||||
func (*StringPayload) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_hintdetail_429641491537a364, []int{0}
|
||||
return fileDescriptor_d99b88ec9cc4bc22, []int{0}
|
||||
}
|
||||
func (m *StringPayload) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *StringPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalTo(b)
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
func (dst *StringPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_StringPayload.Merge(dst, src)
|
||||
func (m *StringPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_StringPayload.Merge(m, src)
|
||||
}
|
||||
func (m *StringPayload) XXX_Size() int {
|
||||
return m.Size()
|
||||
@@ -56,10 +58,26 @@ var xxx_messageInfo_StringPayload proto.InternalMessageInfo
|
||||
func init() {
|
||||
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) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -67,27 +85,35 @@ func (m *StringPayload) Marshal() (dAtA []byte, err 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
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Msg) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i -= len(m.Msg)
|
||||
copy(dAtA[i:], 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 {
|
||||
offset -= sovHintdetail(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return offset + 1
|
||||
return base
|
||||
}
|
||||
func (m *StringPayload) Size() (n int) {
|
||||
if m == nil {
|
||||
@@ -103,14 +129,7 @@ func (m *StringPayload) Size() (n int) {
|
||||
}
|
||||
|
||||
func sovHintdetail(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozHintdetail(x uint64) (n int) {
|
||||
return sovHintdetail(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
@@ -130,7 +149,7 @@ func (m *StringPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -158,7 +177,7 @@ func (m *StringPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -168,6 +187,9 @@ func (m *StringPayload) Unmarshal(dAtA []byte) error {
|
||||
return ErrInvalidLengthHintdetail
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthHintdetail
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
@@ -179,7 +201,7 @@ func (m *StringPayload) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthHintdetail
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -197,6 +219,7 @@ func (m *StringPayload) Unmarshal(dAtA []byte) error {
|
||||
func skipHintdetail(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -228,10 +251,8 @@ func skipHintdetail(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
return iNdEx, nil
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -248,72 +269,34 @@ func skipHintdetail(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
iNdEx += length
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthHintdetail
|
||||
}
|
||||
return iNdEx, nil
|
||||
iNdEx += length
|
||||
case 3:
|
||||
for {
|
||||
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
|
||||
depth++
|
||||
case 4:
|
||||
return iNdEx, nil
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupHintdetail
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
return iNdEx, nil
|
||||
default:
|
||||
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 (
|
||||
ErrInvalidLengthHintdetail = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowHintdetail = fmt.Errorf("proto: integer overflow")
|
||||
ErrInvalidLengthHintdetail = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
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.
|
||||
// source: github.com/cockroachdb/errors/errorspb/markers.proto
|
||||
// source: errorspb/markers.proto
|
||||
|
||||
package errorspb
|
||||
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import io "io"
|
||||
import (
|
||||
fmt "fmt"
|
||||
_ "github.com/gogo/protobuf/gogoproto"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -18,7 +21,7 @@ var _ = math.Inf
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// 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.
|
||||
// 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 (*MarkPayload) ProtoMessage() {}
|
||||
func (*MarkPayload) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_markers_2a35e57f2b6b5666, []int{0}
|
||||
return fileDescriptor_84c3fd24ba37816d, []int{0}
|
||||
}
|
||||
func (m *MarkPayload) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *MarkPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalTo(b)
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
func (dst *MarkPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_MarkPayload.Merge(dst, src)
|
||||
func (m *MarkPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_MarkPayload.Merge(m, src)
|
||||
}
|
||||
func (m *MarkPayload) XXX_Size() int {
|
||||
return m.Size()
|
||||
@@ -60,10 +63,30 @@ var xxx_messageInfo_MarkPayload proto.InternalMessageInfo
|
||||
func init() {
|
||||
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) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -71,39 +94,49 @@ func (m *MarkPayload) Marshal() (dAtA []byte, err 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
|
||||
var l int
|
||||
_ = 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 {
|
||||
for _, msg := range m.Types {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintMarkers(dAtA, i, uint64(msg.Size()))
|
||||
n, err := msg.MarshalTo(dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
for iNdEx := len(m.Types) - 1; iNdEx >= 0; iNdEx-- {
|
||||
{
|
||||
size, err := m.Types[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
||||
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 {
|
||||
offset -= sovMarkers(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return offset + 1
|
||||
return base
|
||||
}
|
||||
func (m *MarkPayload) Size() (n int) {
|
||||
if m == nil {
|
||||
@@ -125,14 +158,7 @@ func (m *MarkPayload) Size() (n int) {
|
||||
}
|
||||
|
||||
func sovMarkers(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozMarkers(x uint64) (n int) {
|
||||
return sovMarkers(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
@@ -152,7 +178,7 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -180,7 +206,7 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -190,6 +216,9 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
||||
return ErrInvalidLengthMarkers
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthMarkers
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
@@ -209,7 +238,7 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -218,6 +247,9 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
||||
return ErrInvalidLengthMarkers
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthMarkers
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
@@ -232,7 +264,7 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthMarkers
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -250,6 +282,7 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
||||
func skipMarkers(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -281,10 +314,8 @@ func skipMarkers(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
return iNdEx, nil
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -301,75 +332,34 @@ func skipMarkers(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
iNdEx += length
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthMarkers
|
||||
}
|
||||
return iNdEx, nil
|
||||
iNdEx += length
|
||||
case 3:
|
||||
for {
|
||||
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
|
||||
depth++
|
||||
case 4:
|
||||
return iNdEx, nil
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupMarkers
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
return iNdEx, nil
|
||||
default:
|
||||
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 (
|
||||
ErrInvalidLengthMarkers = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowMarkers = fmt.Errorf("proto: integer overflow")
|
||||
ErrInvalidLengthMarkers = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
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;
|
||||
option go_package = "errorspb";
|
||||
|
||||
import "github.com/cockroachdb/errors/errorspb/errors.proto";
|
||||
import "errorspb/errors.proto";
|
||||
import "gogoproto/gogo.proto";
|
||||
|
||||
// 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.
|
||||
// source: github.com/cockroachdb/errors/errorspb/tags.proto
|
||||
// source: errorspb/tags.proto
|
||||
|
||||
package errorspb
|
||||
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import io "io"
|
||||
import (
|
||||
fmt "fmt"
|
||||
_ "github.com/gogo/protobuf/gogoproto"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -18,7 +21,7 @@ var _ = math.Inf
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// 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
|
||||
// marker.
|
||||
@@ -32,21 +35,21 @@ func (m *TagsPayload) Reset() { *m = TagsPayload{} }
|
||||
func (m *TagsPayload) String() string { return proto.CompactTextString(m) }
|
||||
func (*TagsPayload) ProtoMessage() {}
|
||||
func (*TagsPayload) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_tags_c75c5e322ef6b31c, []int{0}
|
||||
return fileDescriptor_2f0d1dc5c54e9f63, []int{0}
|
||||
}
|
||||
func (m *TagsPayload) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *TagsPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalTo(b)
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
func (dst *TagsPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_TagsPayload.Merge(dst, src)
|
||||
func (m *TagsPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_TagsPayload.Merge(m, src)
|
||||
}
|
||||
func (m *TagsPayload) XXX_Size() int {
|
||||
return m.Size()
|
||||
@@ -66,21 +69,21 @@ func (m *TagPayload) Reset() { *m = TagPayload{} }
|
||||
func (m *TagPayload) String() string { return proto.CompactTextString(m) }
|
||||
func (*TagPayload) ProtoMessage() {}
|
||||
func (*TagPayload) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_tags_c75c5e322ef6b31c, []int{1}
|
||||
return fileDescriptor_2f0d1dc5c54e9f63, []int{1}
|
||||
}
|
||||
func (m *TagPayload) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *TagPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalTo(b)
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
func (dst *TagPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_TagPayload.Merge(dst, src)
|
||||
func (m *TagPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_TagPayload.Merge(m, src)
|
||||
}
|
||||
func (m *TagPayload) XXX_Size() int {
|
||||
return m.Size()
|
||||
@@ -95,10 +98,30 @@ func init() {
|
||||
proto.RegisterType((*TagsPayload)(nil), "cockroach.errorspb.TagsPayload")
|
||||
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) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -106,29 +129,36 @@ func (m *TagsPayload) Marshal() (dAtA []byte, err 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
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Tags) > 0 {
|
||||
for _, msg := range m.Tags {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintTags(dAtA, i, uint64(msg.Size()))
|
||||
n, err := msg.MarshalTo(dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
for iNdEx := len(m.Tags) - 1; iNdEx >= 0; iNdEx-- {
|
||||
{
|
||||
size, err := m.Tags[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
||||
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) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -136,33 +166,42 @@ func (m *TagPayload) Marshal() (dAtA []byte, err 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
|
||||
var l int
|
||||
_ = 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 {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i -= len(m.Value)
|
||||
copy(dAtA[i:], 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 {
|
||||
offset -= sovTags(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return offset + 1
|
||||
return base
|
||||
}
|
||||
func (m *TagsPayload) Size() (n int) {
|
||||
if m == nil {
|
||||
@@ -197,14 +236,7 @@ func (m *TagPayload) Size() (n int) {
|
||||
}
|
||||
|
||||
func sovTags(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozTags(x uint64) (n int) {
|
||||
return sovTags(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
@@ -224,7 +256,7 @@ func (m *TagsPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -252,7 +284,7 @@ func (m *TagsPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -261,6 +293,9 @@ func (m *TagsPayload) Unmarshal(dAtA []byte) error {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
@@ -275,7 +310,7 @@ func (m *TagsPayload) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -305,7 +340,7 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -333,7 +368,7 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -343,6 +378,9 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
@@ -362,7 +400,7 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -372,6 +410,9 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
@@ -383,7 +424,7 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -401,6 +442,7 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
||||
func skipTags(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -432,10 +474,8 @@ func skipTags(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
return iNdEx, nil
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -452,75 +492,34 @@ func skipTags(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
iNdEx += length
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthTags
|
||||
}
|
||||
return iNdEx, nil
|
||||
iNdEx += length
|
||||
case 3:
|
||||
for {
|
||||
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
|
||||
depth++
|
||||
case 4:
|
||||
return iNdEx, nil
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupTags
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
return iNdEx, nil
|
||||
default:
|
||||
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 (
|
||||
ErrInvalidLengthTags = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowTags = fmt.Errorf("proto: integer overflow")
|
||||
ErrInvalidLengthTags = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
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.
|
||||
// source: github.com/cockroachdb/errors/errorspb/testing.proto
|
||||
// source: errorspb/testing.proto
|
||||
|
||||
package errorspb
|
||||
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import io "io"
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -18,7 +20,7 @@ var _ = math.Inf
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// 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.
|
||||
type TestError struct {
|
||||
@@ -28,21 +30,21 @@ func (m *TestError) Reset() { *m = TestError{} }
|
||||
func (m *TestError) String() string { return proto.CompactTextString(m) }
|
||||
func (*TestError) ProtoMessage() {}
|
||||
func (*TestError) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_testing_d9751a0a5e86c096, []int{0}
|
||||
return fileDescriptor_0551f0d913d6118f, []int{0}
|
||||
}
|
||||
func (m *TestError) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *TestError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalTo(b)
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
func (dst *TestError) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_TestError.Merge(dst, src)
|
||||
func (m *TestError) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_TestError.Merge(m, src)
|
||||
}
|
||||
func (m *TestError) XXX_Size() int {
|
||||
return m.Size()
|
||||
@@ -56,10 +58,25 @@ var xxx_messageInfo_TestError proto.InternalMessageInfo
|
||||
func init() {
|
||||
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) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -67,21 +84,28 @@ func (m *TestError) Marshal() (dAtA []byte, err 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
|
||||
var l int
|
||||
_ = l
|
||||
return i, nil
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintTesting(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovTesting(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return offset + 1
|
||||
return base
|
||||
}
|
||||
func (m *TestError) Size() (n int) {
|
||||
if m == nil {
|
||||
@@ -93,14 +117,7 @@ func (m *TestError) Size() (n int) {
|
||||
}
|
||||
|
||||
func sovTesting(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozTesting(x uint64) (n int) {
|
||||
return sovTesting(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
@@ -120,7 +137,7 @@ func (m *TestError) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -140,7 +157,7 @@ func (m *TestError) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthTesting
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -158,6 +175,7 @@ func (m *TestError) Unmarshal(dAtA []byte) error {
|
||||
func skipTesting(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -189,10 +207,8 @@ func skipTesting(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
return iNdEx, nil
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -209,70 +225,34 @@ func skipTesting(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
iNdEx += length
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthTesting
|
||||
}
|
||||
return iNdEx, nil
|
||||
iNdEx += length
|
||||
case 3:
|
||||
for {
|
||||
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
|
||||
depth++
|
||||
case 4:
|
||||
return iNdEx, nil
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupTesting
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
return iNdEx, nil
|
||||
default:
|
||||
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 (
|
||||
ErrInvalidLengthTesting = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowTesting = fmt.Errorf("proto: integer overflow")
|
||||
ErrInvalidLengthTesting = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
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 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`.
|
||||
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
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/errors/join"
|
||||
"github.com/cockroachdb/errors/secondary"
|
||||
"github.com/cockroachdb/errors/withstack"
|
||||
"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)
|
||||
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().
|
||||
// - 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) }
|
||||
|
||||
// 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.
|
||||
// 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:
|
||||
// - when formatting with `%+v`.
|
||||
@@ -97,10 +98,12 @@ type ErrorHinter interface {
|
||||
|
||||
// WithDetail decorates an error with a textual detail.
|
||||
// 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:
|
||||
// - when formatting with `%+v`.
|
||||
// - with `GetAllHints()` / `FlattenHints()` below.
|
||||
// - with `GetAllDetails()` / `FlattenDetails()` below.
|
||||
//
|
||||
// Note: the detail does not appear in the main error message returned
|
||||
// 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.
|
||||
// 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:
|
||||
// - 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.
|
||||
// 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:
|
||||
// - when formatting with `%+v`.
|
||||
// - with `GetAllHints()` / `FlattenHints()` below.
|
||||
// - with `GetAllDetails()` / `FlattenDetails()` below.
|
||||
//
|
||||
// Note: the detail does not appear in the main error message returned
|
||||
// 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
|
||||
// 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
|
||||
// package location or a different type, ensure that
|
||||
// RegisterTypeMigration() was called prior to Is().
|
||||
func Is(err, reference error) bool {
|
||||
if reference == nil {
|
||||
return err == reference
|
||||
return err == nil
|
||||
}
|
||||
|
||||
isComparable := reflect.TypeOf(reference).Comparable()
|
||||
|
||||
// Direct reference comparison is the fastest, and most
|
||||
// likely to be true, so do this first.
|
||||
for c := err; c != nil; c = errbase.UnwrapOnce(c) {
|
||||
if c == reference {
|
||||
if isComparable && c == reference {
|
||||
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 {
|
||||
@@ -51,8 +75,8 @@ func Is(err, reference error) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Not directly equal. Try harder, using error marks. We don't this
|
||||
// during the loop above as it may be more expensive.
|
||||
// Not directly equal. Try harder, using error marks. We don't do
|
||||
// this during the loop above as it may be more expensive.
|
||||
//
|
||||
// Note: there is a more effective recursive algorithm that ensures
|
||||
// that any pair of string only gets compared once. Should the
|
||||
@@ -67,6 +91,13 @@ func Is(err, reference error) bool {
|
||||
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
|
||||
// matches that of referenceType.
|
||||
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
|
||||
// RegisterTypeMigration() was called prior to IsAny().
|
||||
func IsAny(err error, references ...error) bool {
|
||||
// First try using direct reference comparison.
|
||||
for c := err; ; c = errbase.UnwrapOnce(c) {
|
||||
if err == nil {
|
||||
for _, refErr := range references {
|
||||
if c == refErr {
|
||||
if refErr == nil {
|
||||
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 error is nil, so don't bother with computing the marks in
|
||||
// that case. This avoids the computational expense of computing
|
||||
@@ -141,6 +163,31 @@ func IsAny(err error, references ...error) bool {
|
||||
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.
|
||||
// Note: there is a more effective recursive algorithm that ensures
|
||||
// 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
|
||||
// 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
|
||||
// package location or a different type, ensure that
|
||||
// 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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -23,7 +22,7 @@ import (
|
||||
"github.com/cockroachdb/errors/errbase"
|
||||
"github.com/cockroachdb/errors/withstack"
|
||||
"github.com/cockroachdb/redact"
|
||||
"github.com/cockroachdb/sentry-go"
|
||||
sentry "github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
||||
// 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:
|
||||
//
|
||||
////////////////
|
||||
// //////////////
|
||||
// Title: (1) some prefix in bold (2) one line for a stack trace
|
||||
// (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)
|
||||
//
|
||||
// (5) a "message"
|
||||
//
|
||||
// (6) zero or more "exceptions", each composed of:
|
||||
// (7) a bold title
|
||||
// (8) some freeform text
|
||||
// (9) a stack trace
|
||||
//
|
||||
// (7) a bold title
|
||||
// (8) some freeform text
|
||||
// (9) a stack trace
|
||||
//
|
||||
// (10) metadata fields: environment, arch, etc
|
||||
//
|
||||
// (11) "Additional data" fields
|
||||
//
|
||||
// (12) SDK version
|
||||
/////////////////
|
||||
// ///////////////
|
||||
//
|
||||
// These visual items map to the Sentry Event object as follows:
|
||||
//
|
||||
// (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
|
||||
// (3) the Value field of the 1st Exception object, if any, unwrapped as a single line
|
||||
// (4) the Tags 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)
|
||||
// (9) the Stacktrace field (input to (2) on 1st exception)
|
||||
// (10) the other fields on the Event object
|
||||
@@ -76,13 +78,15 @@ import (
|
||||
// Given this mapping, an error object is decomposed as follows:
|
||||
//
|
||||
// (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
|
||||
// (5): detailed structure of the entire error object, with references to "additional data"
|
||||
// and additional "exception" objects
|
||||
// (5): detailed structure of the entire error object, with references to
|
||||
//
|
||||
// additional "exception" objects
|
||||
//
|
||||
// (9): generated from innermost stack trace
|
||||
// (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
|
||||
// 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
|
||||
// provided by the library, but could impact error types defined by
|
||||
// 3rd parties. This limitation may be lifted in a later version.
|
||||
//
|
||||
func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]interface{}) {
|
||||
if err == nil {
|
||||
// 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.
|
||||
var stacks []*withstack.ReportableStackTrace
|
||||
var details []errbase.SafeDetailPayload
|
||||
for c := err; c != nil; c = errbase.UnwrapOnce(c) {
|
||||
visitAllMulti(err, func(c error) {
|
||||
st := withstack.GetReportableStackTrace(c)
|
||||
stacks = append(stacks, st)
|
||||
|
||||
sd := errbase.GetSafeDetails(c)
|
||||
details = append(details, sd)
|
||||
}
|
||||
})
|
||||
module := string(domains.GetDomain(err))
|
||||
|
||||
// firstDetailLine is the first detail string encountered.
|
||||
@@ -119,17 +122,22 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
||||
var firstDetailLine string
|
||||
|
||||
// longMsgBuf will become the Message field, which contains the full
|
||||
// structure of the error with cross-references to "Exception" and
|
||||
// "Additional data" fields.
|
||||
// structure of the error.
|
||||
var longMsgBuf strings.Builder
|
||||
redactedErrStr := redact.Sprint(err).Redact()
|
||||
if redactedErrStr != redactedMarker {
|
||||
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)
|
||||
if f, l, _, ok := withstack.GetOneLineSource(err); ok {
|
||||
fmt.Fprintf(&longMsgBuf, "%s:%d: ", f, l)
|
||||
}
|
||||
// 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
|
||||
// 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
|
||||
// error from innermost to outermost layer in longMsgBuf and
|
||||
// typesBuf.
|
||||
longMsgBuf.WriteString("\n-- report composition:\n")
|
||||
for i := len(details) - 1; i >= 0; i-- {
|
||||
// Collect the type name for this layer of error wrapping, towards
|
||||
// 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
|
||||
// object.
|
||||
|
||||
// genExtra will remember whether we are adding an
|
||||
// additional payload or not.
|
||||
var genExtra bool
|
||||
|
||||
// Is there a stack trace?
|
||||
if st := stacks[i]; st != nil {
|
||||
var excType strings.Builder
|
||||
@@ -245,17 +250,21 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
||||
exceptions = append(exceptions, exc)
|
||||
} else {
|
||||
// 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
|
||||
// at that level. This is because stack trace annotations also
|
||||
// produce the stack trace as safe detail string.
|
||||
genExtra = len(details[i].SafeDetails) > 1
|
||||
if len(details[i].SafeDetails) > 0 {
|
||||
d := details[i].SafeDetails[0]
|
||||
if d != "" {
|
||||
genExtra = true
|
||||
}
|
||||
if j := strings.IndexByte(d, '\n'); j >= 0 {
|
||||
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 {
|
||||
@@ -299,7 +296,7 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
||||
event.Message = longMsgBuf.String()
|
||||
event.Exception = exceptions
|
||||
|
||||
// If there is no exception payload, synthetize one.
|
||||
// If there is no exception payload, synthesize one.
|
||||
if len(event.Exception) == 0 {
|
||||
// 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
|
||||
@@ -354,7 +351,7 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
||||
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
|
||||
// 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]
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
"fmt"
|
||||
|
||||
"github.com/cockroachdb/sentry-go"
|
||||
"github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
||||
// 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 (
|
||||
"github.com/cockroachdb/errors/report"
|
||||
"github.com/cockroachdb/sentry-go"
|
||||
"github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
||||
// 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
|
||||
// (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)
|
||||
//
|
||||
// (5) a "message"
|
||||
@@ -57,7 +57,7 @@ import (
|
||||
// (3) the Value field of the 1st Exception object, if any, unwrapped as a single line
|
||||
// (4) the Tags 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)
|
||||
// (9) the Stacktrace field (input to (2) on 1st exception)
|
||||
// (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"
|
||||
|
||||
"github.com/cockroachdb/errors/errbase"
|
||||
"github.com/cockroachdb/sentry-go"
|
||||
"github.com/getsentry/sentry-go"
|
||||
pkgErr "github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
.vscode/
|
||||
.idea/
|
||||
.DS_Store
|
||||
|
||||
coverage.txt
|
||||
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
@@ -14,12 +11,15 @@ coverage.txt
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Test example output
|
||||
example.logV2.hlog
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# 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
|
||||
}
|
||||
|
||||
// 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
|
||||
// earlier tag, that tag is overwritten.
|
||||
// The receiver can be nil.
|
||||
@@ -52,6 +66,25 @@ func (b *Buffer) Add(key string, value interface{}) *Buffer {
|
||||
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
|
||||
// the tags from <other>.
|
||||
//
|
||||
@@ -67,7 +100,7 @@ func (b *Buffer) Merge(other *Buffer) *Buffer {
|
||||
if b == nil || len(b.tags) == 0 {
|
||||
return other
|
||||
}
|
||||
if len(other.tags) == 0 {
|
||||
if other == nil || len(other.tags) == 0 {
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -121,12 +154,12 @@ func (b *Buffer) String() string {
|
||||
}
|
||||
|
||||
// FormatToString emits the k/v pairs to a strings.Builder.
|
||||
// - the k/v pairs are separated by commas (& no spaces).
|
||||
// - if there is no value, only the key is printed.
|
||||
// - if there is a value, and the key is just 1 character long,
|
||||
// the key and the value are concatenated.
|
||||
// This supports e.g. printing k="n", v=123 as "n123".
|
||||
// - otherwise, it prints "k=v".
|
||||
// - the k/v pairs are separated by commas (& no spaces).
|
||||
// - if there is no value, only the key is printed.
|
||||
// - if there is a value, and the key is just 1 character long,
|
||||
// the key and the value are concatenated.
|
||||
// This supports e.g. printing k="n", v=123 as "n123".
|
||||
// - otherwise, it prints "k=v".
|
||||
func (b *Buffer) FormatToString(buf *strings.Builder) {
|
||||
comma := ""
|
||||
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 {
|
||||
// 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).
|
||||
b.tags = b.prealloc[:length]
|
||||
} 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))
|
||||
}
|
||||
|
||||
// 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
|
||||
// set of tags. Tags are deduplicated (see Buffer.AddTags).
|
||||
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.
|
||||
cpu.*.prof
|
||||
heap.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 :=
|
||||
TAGS := invariants
|
||||
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
|
||||
all:
|
||||
@@ -15,18 +16,40 @@ all:
|
||||
@echo " make stressrace"
|
||||
@echo " make stressmeta"
|
||||
@echo " make crossversion-meta"
|
||||
@echo " make testcoverage"
|
||||
@echo " make mod-update"
|
||||
@echo " make generate"
|
||||
@echo " make generate-test-data"
|
||||
@echo " make clean"
|
||||
|
||||
override testflags :=
|
||||
.PHONY: 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
|
||||
testrace: testflags += -race -timeout 20m
|
||||
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
|
||||
stressrace: testflags += -race
|
||||
stress stressrace: testflags += -exec 'stress ${STRESSFLAGS}' -timeout 0 -test.v
|
||||
@@ -40,20 +63,34 @@ stressmeta: stress
|
||||
|
||||
.PHONY: crossversion-meta
|
||||
crossversion-meta:
|
||||
git checkout ${LATEST_RELEASE}; \
|
||||
${GO} test -c ./internal/metamorphic -o './internal/metamorphic/crossversion/${LATEST_RELEASE}.test'; \
|
||||
git checkout ${PREV_RELEASE}; \
|
||||
${GO} test -c ./internal/metamorphic -o './internal/metamorphic/crossversion/${PREV_RELEASE}.test'; \
|
||||
git checkout -; \
|
||||
${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
|
||||
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:
|
||||
${GO} get -u
|
||||
${GO} mod tidy
|
||||
${GO} mod vendor
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@@ -78,11 +115,11 @@ endif
|
||||
@${GO} mod tidy
|
||||
$(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
|
||||
format:
|
||||
for _file in $$(gofmt -s -l . | grep -vE '^vendor/'); do \
|
||||
gofmt -s -w $$_file ; \
|
||||
done
|
||||
go install github.com/cockroachdb/crlfmt@44a36ec7 && crlfmt -w -tab 2 .
|
||||
|
||||
.PHONY: format-check
|
||||
format-check:
|
||||
@@ -90,4 +127,5 @@ ifneq ($(git_dirty),)
|
||||
$(error format-check must be invoked on a clean repository)
|
||||
endif
|
||||
$(MAKE) format
|
||||
git diff
|
||||
$(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/)
|
||||
|
||||
@@ -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
|
||||
format.
|
||||
|
||||
Pebble intentionally does not aspire to include every feature in
|
||||
RocksDB and is specifically targetting the use case and feature set
|
||||
needed by CockroachDB:
|
||||
Pebble intentionally does not aspire to include every feature in RocksDB and
|
||||
specifically targets the use case and feature set needed by CockroachDB:
|
||||
|
||||
* Block-based tables
|
||||
* Checkpoints
|
||||
@@ -68,6 +67,17 @@ Pebble offers several improvements over RocksDB:
|
||||
* Faster commit pipeline that achieves better concurrency.
|
||||
* Seamless merged iteration of indexed batches. The mutations in the
|
||||
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.
|
||||
|
||||
See the [Pebble vs RocksDB: Implementation
|
||||
@@ -100,10 +110,68 @@ known incompatibilities.
|
||||
format.
|
||||
* Plain table format. Pebble does not support the plain table sstable
|
||||
format.
|
||||
* SSTable format version 3 and 4. Pebble does not currently support
|
||||
version 3 and version 4 format sstables. The sstable format version
|
||||
is controlled by the `BlockBasedTableOptions::format_version`
|
||||
option. See [#97](https://github.com/cockroachdb/pebble/issues/97).
|
||||
* SSTable format version 3 and 4. Pebble does not support version 3
|
||||
and version 4 format sstables. The sstable format version is
|
||||
controlled by the `BlockBasedTableOptions::format_version` option.
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
// created.
|
||||
//
|
||||
// c := pebble.NewCache(...)
|
||||
// defer c.Unref()
|
||||
// d, err := pebble.Open(pebble.Options{Cache: c})
|
||||
// c := pebble.NewCache(...)
|
||||
// defer c.Unref()
|
||||
// d, err := pebble.Open(pebble.Options{Cache: c})
|
||||
func NewCache(size int64) *cache.Cache {
|
||||
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
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/cockroachdb/errors/oserror"
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/record"
|
||||
"github.com/cockroachdb/pebble/vfs"
|
||||
"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
|
||||
// checkpointing.
|
||||
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`.
|
||||
@@ -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.
|
||||
// Those missing parents, as well as the closest existing ancestor, are synced.
|
||||
// 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
|
||||
// closest existing ancestor (included).
|
||||
var parentPaths []string
|
||||
foundExistingAncestor := false
|
||||
for parentPath := fs.PathDir(destDir); parentPath != "."; parentPath = fs.PathDir(parentPath) {
|
||||
for parentPath := fs.PathDir(destDir); ; parentPath = fs.PathDir(parentPath) {
|
||||
parentPaths = append(parentPaths, parentPath)
|
||||
if fs.PathDir(parentPath) == parentPath {
|
||||
break
|
||||
}
|
||||
_, err := fs.Stat(parentPath)
|
||||
if err == nil {
|
||||
// Exit loop at the closest existing ancestor.
|
||||
foundExistingAncestor = true
|
||||
break
|
||||
}
|
||||
if !oserror.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Handle empty filesystem edge case.
|
||||
if !foundExistingAncestor {
|
||||
parentPaths = append(parentPaths, "")
|
||||
}
|
||||
// Create destDir and any of its missing parents.
|
||||
if err := fs.MkdirAll(destDir, 0755); err != nil {
|
||||
return nil, err
|
||||
@@ -142,11 +181,14 @@ func (d *DB) Checkpoint(
|
||||
// file number.
|
||||
memQueue := d.mu.mem.queue
|
||||
current := d.mu.versions.currentVersion()
|
||||
formatVers := d.mu.formatVers.vers
|
||||
formatVers := d.FormatMajorVersion()
|
||||
manifestFileNum := d.mu.versions.manifestFileNum
|
||||
manifestSize := d.mu.versions.manifest.Size()
|
||||
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
|
||||
// the database.
|
||||
d.mu.versions.logUnlock()
|
||||
@@ -154,13 +196,10 @@ func (d *DB) Checkpoint(
|
||||
|
||||
// Wrap the normal filesystem with one which wraps newly created files with
|
||||
// vfs.NewSyncingFile.
|
||||
fs := syncingFS{
|
||||
FS: d.opts.FS,
|
||||
syncOpts: vfs.SyncingFileOptions{
|
||||
NoSyncOnClose: d.opts.NoSyncOnClose,
|
||||
BytesPerSync: d.opts.BytesPerSync,
|
||||
},
|
||||
}
|
||||
fs := vfs.NewSyncingFS(d.opts.FS, vfs.SyncingFileOptions{
|
||||
NoSyncOnClose: d.opts.NoSyncOnClose,
|
||||
BytesPerSync: d.opts.BytesPerSync,
|
||||
})
|
||||
|
||||
// Create the dir and its parents (if necessary), and sync them.
|
||||
var dir vfs.File
|
||||
@@ -170,11 +209,7 @@ func (d *DB) Checkpoint(
|
||||
}
|
||||
if ckErr != nil {
|
||||
// Attempt to cleanup on error.
|
||||
paths, _ := fs.List(destDir)
|
||||
for _, path := range paths {
|
||||
_ = fs.Remove(path)
|
||||
}
|
||||
_ = fs.Remove(destDir)
|
||||
_ = fs.RemoveAll(destDir)
|
||||
}
|
||||
}()
|
||||
dir, ckErr = mkdirAllAndSyncParents(fs, destDir)
|
||||
@@ -214,45 +249,34 @@ func (d *DB) Checkpoint(
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// 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.
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
var excludedFiles map[deletedFileEntry]*fileMetadata
|
||||
// Set of FileBacking.DiskFileNum which will be required by virtual sstables
|
||||
// in the checkpoint.
|
||||
requiredVirtualBackingFiles := make(map[base.DiskFileNum]struct{})
|
||||
// Link or copy the sstables.
|
||||
for l := range current.Levels {
|
||||
iter := current.Levels[l].Iter()
|
||||
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))
|
||||
ckErr = vfs.LinkOrCopy(fs, srcPath, destPath)
|
||||
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
|
||||
// will cause the WAL files to be reused which would invalidate the
|
||||
// checkpoint.
|
||||
@@ -269,7 +310,7 @@ func (d *DB) Checkpoint(
|
||||
if logNum == 0 {
|
||||
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))
|
||||
ckErr = vfs.Copy(fs, srcPath, destPath)
|
||||
if ckErr != nil {
|
||||
@@ -286,3 +327,99 @@ func (d *DB) Checkpoint(
|
||||
dir = nil
|
||||
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
|
||||
|
||||
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.
|
||||
type Cleaner = base.Cleaner
|
||||
@@ -14,3 +25,271 @@ type DeleteCleaner = base.DeleteCleaner
|
||||
|
||||
// ArchiveCleaner exports the base.ArchiveCleaner type.
|
||||
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"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
"time"
|
||||
|
||||
"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
|
||||
// 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
|
||||
// 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
|
||||
// atomically by the consumer and read atomically by the producer.
|
||||
slots [record.SyncConcurrency]unsafe.Pointer
|
||||
slots [record.SyncConcurrency]atomic.Pointer[Batch]
|
||||
}
|
||||
|
||||
const dequeueBits = 32
|
||||
@@ -57,17 +57,17 @@ func (q *commitQueue) pack(head, tail uint32) uint64 {
|
||||
}
|
||||
|
||||
func (q *commitQueue) enqueue(b *Batch) {
|
||||
ptrs := atomic.LoadUint64(&q.headTail)
|
||||
ptrs := q.headTail.Load()
|
||||
head, tail := q.unpack(ptrs)
|
||||
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.
|
||||
panic("pebble: not reached")
|
||||
}
|
||||
slot := &q.slots[head&uint32(len(q.slots)-1)]
|
||||
|
||||
// Check if the head slot has been released by dequeue.
|
||||
for atomic.LoadPointer(slot) != nil {
|
||||
// Check if the head slot has been released by dequeueApplied.
|
||||
for slot.Load() != nil {
|
||||
// Another goroutine is still cleaning up the tail, so the queue is
|
||||
// actually still full. We spin because this should resolve itself
|
||||
// momentarily.
|
||||
@@ -75,16 +75,20 @@ func (q *commitQueue) enqueue(b *Batch) {
|
||||
}
|
||||
|
||||
// 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.
|
||||
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 {
|
||||
ptrs := atomic.LoadUint64(&q.headTail)
|
||||
ptrs := q.headTail.Load()
|
||||
head, tail := q.unpack(ptrs)
|
||||
if tail == head {
|
||||
// Queue is empty.
|
||||
@@ -92,8 +96,8 @@ func (q *commitQueue) dequeue() *Batch {
|
||||
}
|
||||
|
||||
slot := &q.slots[tail&uint32(len(q.slots)-1)]
|
||||
b := (*Batch)(atomic.LoadPointer(slot))
|
||||
if b == nil || atomic.LoadUint32(&b.applied) == 0 {
|
||||
b := slot.Load()
|
||||
if b == nil || !b.applied.Load() {
|
||||
// The batch is not ready to be dequeued, or another goroutine has
|
||||
// already dequeued it.
|
||||
return nil
|
||||
@@ -102,13 +106,13 @@ func (q *commitQueue) dequeue() *Batch {
|
||||
// Confirm head and tail (for our speculative check above) and increment
|
||||
// tail. If this succeeds, then we own the slot at tail.
|
||||
ptrs2 := q.pack(head, tail+1)
|
||||
if atomic.CompareAndSwapUint64(&q.headTail, ptrs, ptrs2) {
|
||||
if q.headTail.CompareAndSwap(ptrs, ptrs2) {
|
||||
// We now own slot.
|
||||
//
|
||||
// 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
|
||||
// live longer than necessary.
|
||||
atomic.StorePointer(slot, nil)
|
||||
slot.Store(nil)
|
||||
// At this point enqueue owns the slot.
|
||||
return b
|
||||
}
|
||||
@@ -121,10 +125,10 @@ func (q *commitQueue) dequeue() *Batch {
|
||||
type commitEnv struct {
|
||||
// The next sequence number to give to a batch. Protected by
|
||||
// commitPipeline.mu.
|
||||
logSeqNum *uint64
|
||||
logSeqNum *atomic.Uint64
|
||||
// The visible sequence number at which reads should be performed. Ratcheted
|
||||
// upwards atomically as batches are applied to the memtable.
|
||||
visibleSeqNum *uint64
|
||||
visibleSeqNum *atomic.Uint64
|
||||
|
||||
// Apply the batch to the specified memtable. Called concurrently.
|
||||
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
|
||||
// conceptually:
|
||||
//
|
||||
// 1. Write the batch to the WAL and optionally sync the WAL
|
||||
// 2. Apply the mutations in the batch to the memtable
|
||||
// 1. Write the batch to the WAL and optionally sync the WAL
|
||||
// 2. Apply the mutations in the batch to the memtable
|
||||
//
|
||||
// These two simple steps are made complicated by the desire for high
|
||||
// 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, though it also needs to maintain two invariants:
|
||||
//
|
||||
// 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
|
||||
// invariant arises from the use of a single sequence number which
|
||||
// indicates which mutations are visible.
|
||||
// 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
|
||||
// invariant arises from the use of a single sequence number which
|
||||
// indicates which mutations are visible.
|
||||
//
|
||||
// Taking these invariants into account, let's revisit the work the commit
|
||||
// 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:
|
||||
//
|
||||
// with commitPipeline mutex locked:
|
||||
// assign batch sequence number
|
||||
// write batch to WAL
|
||||
// (optionally) add batch to WAL sync list
|
||||
// apply batch to memtable (concurrently)
|
||||
// wait for earlier batches to apply
|
||||
// ratchet read sequence number
|
||||
// (optionally) wait for the WAL to sync
|
||||
// with commitPipeline mutex locked:
|
||||
// assign batch sequence number
|
||||
// write batch to WAL
|
||||
// (optionally) add batch to WAL sync list
|
||||
// apply batch to memtable (concurrently)
|
||||
// wait for earlier batches to apply
|
||||
// ratchet read sequence number
|
||||
// (optionally) wait for the WAL to sync
|
||||
//
|
||||
// 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
|
||||
@@ -217,7 +221,28 @@ type commitPipeline struct {
|
||||
// Queue of pending batches to commit.
|
||||
pending commitQueue
|
||||
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
|
||||
// calls to commitEnv.write().
|
||||
mu sync.Mutex
|
||||
@@ -226,23 +251,60 @@ type commitPipeline struct {
|
||||
func newCommitPipeline(env commitEnv) *commitPipeline {
|
||||
p := &commitPipeline{
|
||||
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
|
||||
// have to allow one "slot" for a concurrent WAL rotation which will close
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
// WAL, and applying the batch to the memtable. Upon successful return the
|
||||
// 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() {
|
||||
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
|
||||
// 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
|
||||
// for reuse. See Batch.release().
|
||||
mem, err := p.prepare(b, syncWAL)
|
||||
mem, err := p.prepare(b, syncWAL, noSyncWait)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
// Apply the batch to the memtable.
|
||||
if err := p.env.apply(b, mem); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
// Publish the batch sequence number.
|
||||
p.publish(b)
|
||||
|
||||
<-p.sem
|
||||
<-p.commitQueueSem
|
||||
|
||||
if b.commitErr != nil {
|
||||
b.db = nil // prevent batch reuse on error
|
||||
if !noSyncWait {
|
||||
// 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
|
||||
@@ -280,7 +358,9 @@ func (p *commitPipeline) Commit(b *Batch, syncWAL bool) error {
|
||||
// sstable ingestion within the commit pipeline. The prepare callback is
|
||||
// invoked with commitPipeline.mu held, but note that DB.mu is not held and
|
||||
// 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
|
||||
// share additional code with those methods because Commit and prepare are
|
||||
// performance critical code paths.
|
||||
@@ -294,7 +374,7 @@ func (p *commitPipeline) AllocateSeqNum(count int, prepare func(), apply func(se
|
||||
b.setCount(uint32(count))
|
||||
b.commit.Add(1)
|
||||
|
||||
p.sem <- struct{}{}
|
||||
p.commitQueueSem <- struct{}{}
|
||||
|
||||
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
|
||||
// here to handle concurrent reads of logSeqNum. commitPipeline.mu provides
|
||||
// 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
|
||||
if seqNum == 0 {
|
||||
// We can't use the value 0 for the global seqnum during ingestion, because
|
||||
// 0 indicates no global seqnum. So allocate one more seqnum.
|
||||
atomic.AddUint64(p.env.logSeqNum, 1)
|
||||
p.env.logSeqNum.Add(1)
|
||||
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
|
||||
// unfortunate, but obviates the need for additional synchronization.
|
||||
for {
|
||||
visibleSeqNum := atomic.LoadUint64(p.env.visibleSeqNum)
|
||||
visibleSeqNum := p.env.visibleSeqNum.Load()
|
||||
if visibleSeqNum == logSeqNum {
|
||||
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
|
||||
// callback internally fails, the sequence number needs to be published in
|
||||
// order to allow the commit pipeline to proceed.
|
||||
prepare()
|
||||
prepare(b.SeqNum())
|
||||
|
||||
p.mu.Unlock()
|
||||
|
||||
@@ -341,26 +421,33 @@ func (p *commitPipeline) AllocateSeqNum(count int, prepare func(), apply func(se
|
||||
// Publish the sequence number.
|
||||
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())
|
||||
if n == invalidBatchCount {
|
||||
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 syncErr *error
|
||||
if syncWAL {
|
||||
syncWG, syncErr = &b.commit, &b.commitErr
|
||||
switch {
|
||||
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()
|
||||
@@ -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
|
||||
// here to handle concurrent reads of logSeqNum. commitPipeline.mu provides
|
||||
// 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.
|
||||
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) {
|
||||
// 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
|
||||
// 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
|
||||
// head of the queue then either we'll dequeue applied batches and reach our
|
||||
// it or someone else will dequeueApplied and publish it. If our batch is not the
|
||||
// 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 applies it will go through the same process and publish our batch
|
||||
// for us.
|
||||
for {
|
||||
t := p.pending.dequeue()
|
||||
t := p.pending.dequeueApplied()
|
||||
if t == nil {
|
||||
// Wait for another goroutine to publish us. We might also be waiting for
|
||||
// the WAL sync to finish.
|
||||
now := time.Now()
|
||||
b.commit.Wait()
|
||||
b.commitStats.CommitWaitDuration += time.Since(now)
|
||||
break
|
||||
}
|
||||
if atomic.LoadUint32(&t.applied) != 1 {
|
||||
if !t.applied.Load() {
|
||||
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
|
||||
// that the sequence number ratchets up.
|
||||
for {
|
||||
curSeqNum := atomic.LoadUint64(p.env.visibleSeqNum)
|
||||
curSeqNum := p.env.visibleSeqNum.Load()
|
||||
newSeqNum := t.SeqNum() + uint64(t.Count())
|
||||
if newSeqNum <= curSeqNum {
|
||||
// t's sequence number has already been published.
|
||||
break
|
||||
}
|
||||
if atomic.CompareAndSwapUint64(p.env.visibleSeqNum, curSeqNum, newSeqNum) {
|
||||
if p.env.visibleSeqNum.CompareAndSwap(curSeqNum, newSeqNum) {
|
||||
// We successfully published t's sequence number.
|
||||
break
|
||||
}
|
||||
@@ -426,18 +515,3 @@ func (p *commitPipeline) publish(b *Batch) {
|
||||
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.
|
||||
var _ internalIteratorWithStats = (*errorIter)(nil)
|
||||
var _ internalIterator = (*errorIter)(nil)
|
||||
|
||||
func newErrorIter(err error) *errorIter {
|
||||
return &errorIter{err: err}
|
||||
}
|
||||
|
||||
func (c *errorIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, []byte) {
|
||||
return nil, nil
|
||||
func (c *errorIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) SeekPrefixGE(
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
return nil, nil
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, []byte) {
|
||||
return nil, nil
|
||||
func (c *errorIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) First() (*InternalKey, []byte) {
|
||||
return nil, nil
|
||||
func (c *errorIter) First() (*InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) Last() (*InternalKey, []byte) {
|
||||
return nil, nil
|
||||
func (c *errorIter) Last() (*InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) Next() (*InternalKey, []byte) {
|
||||
return nil, nil
|
||||
func (c *errorIter) Next() (*InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) Prev() (*InternalKey, []byte) {
|
||||
return nil, nil
|
||||
func (c *errorIter) Prev() (*InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) NextPrefix([]byte) (*InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) Error() error {
|
||||
@@ -63,8 +67,6 @@ func (c *errorIter) String() string {
|
||||
}
|
||||
|
||||
func (c *errorIter) SetBounds(lower, upper []byte) {}
|
||||
func (c *errorIter) Stats() InternalIteratorStats { return InternalIteratorStats{} }
|
||||
func (c *errorIter) ResetStats() {}
|
||||
|
||||
type errorKeyspanIter struct {
|
||||
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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/cockroachdb/pebble/internal/humanize"
|
||||
"github.com/cockroachdb/pebble/internal/invariants"
|
||||
"github.com/cockroachdb/pebble/internal/manifest"
|
||||
"github.com/cockroachdb/pebble/vfs"
|
||||
"github.com/cockroachdb/redact"
|
||||
)
|
||||
|
||||
@@ -35,10 +39,11 @@ func formatFileNums(tables []TableInfo) string {
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// LevelInfo contains info pertaining to a partificular level.
|
||||
// LevelInfo contains info pertaining to a particular level.
|
||||
type LevelInfo struct {
|
||||
Level int
|
||||
Tables []TableInfo
|
||||
Score float64
|
||||
}
|
||||
|
||||
func (i LevelInfo) String() string {
|
||||
@@ -47,8 +52,11 @@ func (i LevelInfo) String() string {
|
||||
|
||||
// SafeFormat implements redact.SafeFormatter.
|
||||
func (i LevelInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
||||
w.Printf("L%d [%s] (%s)", redact.Safe(i.Level), redact.Safe(formatFileNums(i.Tables)),
|
||||
redact.Safe(humanize.Uint64(tablesTotalSize(i.Tables))))
|
||||
w.Printf("L%d [%s] (%s) Score=%.2f",
|
||||
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.
|
||||
@@ -71,6 +79,27 @@ type CompactionInfo struct {
|
||||
TotalDuration time.Duration
|
||||
Done bool
|
||||
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 {
|
||||
@@ -86,20 +115,25 @@ func (i CompactionInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
||||
}
|
||||
|
||||
if !i.Done {
|
||||
w.Printf("[JOB %d] compacting(%s) ", redact.Safe(i.JobID), redact.SafeString(i.Reason))
|
||||
w.Print(levelInfos(i.Input))
|
||||
w.Printf("[JOB %d] compacting(%s) ",
|
||||
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
|
||||
}
|
||||
outputSize := tablesTotalSize(i.Output.Tables)
|
||||
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.Printf(" -> L%d [%s] (%s), in %.1fs (%.1fs total), output rate %s/s",
|
||||
redact.Safe(i.Output.Level),
|
||||
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.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
|
||||
@@ -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
|
||||
// file.
|
||||
type DiskSlowInfo struct {
|
||||
// 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()))
|
||||
}
|
||||
type DiskSlowInfo = vfs.DiskSlowInfo
|
||||
|
||||
// FlushInfo contains the info for a flush event.
|
||||
type FlushInfo struct {
|
||||
@@ -140,6 +159,9 @@ type FlushInfo struct {
|
||||
Reason string
|
||||
// Input contains the count of input memtables that were flushed.
|
||||
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
|
||||
// is empty for the flush begin event.
|
||||
Output []TableInfo
|
||||
@@ -149,8 +171,14 @@ type FlushInfo struct {
|
||||
// TotalDuration is the total wall-time duration of the flush, including
|
||||
// applying the flush to the database. TotalDuration is always ≥ Duration.
|
||||
TotalDuration time.Duration
|
||||
Done bool
|
||||
Err error
|
||||
// Ingest is set to true if the flush is handling tables that were added to
|
||||
// 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 {
|
||||
@@ -169,20 +197,48 @@ func (i FlushInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
||||
plural = ""
|
||||
}
|
||||
if !i.Done {
|
||||
w.Printf("[JOB %d] flushing %d memtable", redact.Safe(i.JobID), redact.Safe(i.Input))
|
||||
w.SafeString(plural)
|
||||
w.Printf(" to L0")
|
||||
w.Printf("[JOB %d] ", redact.Safe(i.JobID))
|
||||
if !i.Ingest {
|
||||
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
|
||||
}
|
||||
|
||||
outputSize := tablesTotalSize(i.Output)
|
||||
w.Printf("[JOB %d] flushed %d memtable%s to L0 [%s] (%s), in %.1fs (%.1fs total), output rate %s/s",
|
||||
redact.Safe(i.JobID), redact.Safe(i.Input), plural,
|
||||
redact.Safe(formatFileNums(i.Output)),
|
||||
redact.Safe(humanize.Uint64(outputSize)),
|
||||
redact.Safe(i.Duration.Seconds()),
|
||||
redact.Safe(i.TotalDuration.Seconds()),
|
||||
redact.Safe(humanize.Uint64(uint64(float64(outputSize)/i.Duration.Seconds()))))
|
||||
if !i.Ingest {
|
||||
if invariants.Enabled && len(i.IngestLevels) > 0 {
|
||||
panic(errors.AssertionFailedf("pebble: expected len(IngestedLevels) == 0"))
|
||||
}
|
||||
w.Printf("[JOB %d] flushed %d memtable%s (%s) to L0 [%s] (%s), in %.1fs (%.1fs total), output rate %s/s",
|
||||
redact.Safe(i.JobID), redact.Safe(i.Input), plural,
|
||||
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.
|
||||
@@ -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)
|
||||
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.
|
||||
@@ -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)
|
||||
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.
|
||||
@@ -247,7 +303,7 @@ func (i TableCreateInfo) String() string {
|
||||
// SafeFormat implements redact.SafeFormatter.
|
||||
func (i TableCreateInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
||||
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.
|
||||
@@ -266,10 +322,10 @@ func (i TableDeleteInfo) String() string {
|
||||
func (i TableDeleteInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
||||
if i.Err != nil {
|
||||
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
|
||||
}
|
||||
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.
|
||||
@@ -283,7 +339,10 @@ type TableIngestInfo struct {
|
||||
// GlobalSeqNum is the sequence number that was assigned to all entries in
|
||||
// the ingested table.
|
||||
GlobalSeqNum uint64
|
||||
Err error
|
||||
// flushable indicates whether the ingested sstable was treated as a
|
||||
// flushable.
|
||||
flushable bool
|
||||
Err error
|
||||
}
|
||||
|
||||
func (i TableIngestInfo) String() string {
|
||||
@@ -297,14 +356,23 @@ func (i TableIngestInfo) SafeFormat(w redact.SafePrinter, _ rune) {
|
||||
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 {
|
||||
t := &i.Tables[j]
|
||||
if j > 0 {
|
||||
w.Printf(",")
|
||||
}
|
||||
w.Printf(" L%d:%s (%s)", redact.Safe(t.Level), redact.Safe(t.FileNum),
|
||||
redact.Safe(humanize.Uint64(t.Size)))
|
||||
levelStr := ""
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -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)
|
||||
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.
|
||||
@@ -427,9 +495,14 @@ type EventListener struct {
|
||||
// has been installed.
|
||||
CompactionEnd func(CompactionInfo)
|
||||
|
||||
// DiskSlow is invoked after a disk write operation on a file created
|
||||
// with a disk health checking vfs.FS (see vfs.DefaultWithDiskHealthChecks)
|
||||
// is observed to exceed the specified disk slowness threshold duration.
|
||||
// DiskSlow is invoked after a disk write operation on a file created with a
|
||||
// disk health checking vfs.FS (see vfs.DefaultWithDiskHealthChecks) is
|
||||
// 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)
|
||||
|
||||
// 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
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||
@@ -12,12 +16,59 @@ import (
|
||||
"github.com/cockroachdb/pebble/sstable"
|
||||
)
|
||||
|
||||
// NewExternalIter takes an input set of sstable files which may overlap
|
||||
// arbitrarily and returns an Iterator over the merged contents of the sstables.
|
||||
// ExternalIterOption provide an interface to specify open-time options to
|
||||
// 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 files slice must be sorted in reverse chronological ordering. A key in
|
||||
// a file at a lower index will shadow a key with an identical user key
|
||||
// contained within a file at a higher index.
|
||||
// input files slice must be sorted in reverse chronological ordering. A key in a
|
||||
// file at a lower index subarray will shadow a key with an identical user key
|
||||
// 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.
|
||||
//
|
||||
@@ -27,8 +78,20 @@ import (
|
||||
func NewExternalIter(
|
||||
o *Options,
|
||||
iterOpts *IterOptions,
|
||||
files []sstable.ReadableFile,
|
||||
extraReaderOpts ...sstable.ReaderOption,
|
||||
files [][]sstable.ReadableFile,
|
||||
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) {
|
||||
if iterOpts != 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.
|
||||
defer func() {
|
||||
if err != nil {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
@@ -54,11 +132,10 @@ func NewExternalIter(
|
||||
buf := iterAllocPool.Get().(*iterAlloc)
|
||||
dbi := &buf.dbi
|
||||
*dbi = Iterator{
|
||||
ctx: ctx,
|
||||
alloc: buf,
|
||||
cmp: o.Comparer.Compare,
|
||||
equal: o.equal(),
|
||||
merge: o.Merger.Merge,
|
||||
split: o.Comparer.Split,
|
||||
comparer: *o.Comparer,
|
||||
readState: nil,
|
||||
keyBuf: buf.keyBuf,
|
||||
prefixOrFullSeekKey: buf.prefixOrFullSeekKey,
|
||||
@@ -67,7 +144,9 @@ func NewExternalIter(
|
||||
// Add the readers to the Iterator so that Close closes them, and
|
||||
// SetOptions can re-construct iterators from them.
|
||||
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
|
||||
// `levelIters`. newIters should never be called. When we support
|
||||
// organizing multiple non-overlapping files into a single level
|
||||
@@ -80,9 +159,12 @@ func NewExternalIter(
|
||||
}
|
||||
if iterOpts != nil {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -102,84 +184,372 @@ func validateExternalIterOpts(iterOpts *IterOptions) error {
|
||||
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
|
||||
// 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
|
||||
// files in-memory. However, it would also require us to identify the bounds
|
||||
// of all the files upfront.
|
||||
|
||||
mlevels := it.alloc.mlevels[:0]
|
||||
if !it.opts.pointKeys() {
|
||||
it.pointIter = emptyIter
|
||||
} else if it.pointIter == nil {
|
||||
if len(it.externalReaders) > cap(mlevels) {
|
||||
mlevels = make([]mergingIterLevel, 0, len(it.externalReaders))
|
||||
}
|
||||
for _, r := range it.externalReaders {
|
||||
return emptyIter, nil
|
||||
} else if it.pointIter != nil {
|
||||
return it.pointIter, nil
|
||||
}
|
||||
mlevels := it.alloc.mlevels[:0]
|
||||
|
||||
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 (
|
||||
rangeDelIter keyspan.FragmentIterator
|
||||
pointIter internalIterator
|
||||
err error
|
||||
)
|
||||
pointIter, err = r.NewIter(it.opts.LowerBound, it.opts.UpperBound)
|
||||
if err == nil {
|
||||
rangeDelIter, err = r.NewRawRangeDelIter()
|
||||
}
|
||||
// We could set hideObsoletePoints=true, since we are reading at
|
||||
// InternalKeySeqNumMax, but we don't bother since these sstables should
|
||||
// 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 {
|
||||
pointIter = &errorIter{err: err}
|
||||
rangeDelIter = &errorKeyspanIter{err: err}
|
||||
return nil, 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{
|
||||
iter: base.WrapIterWithStats(pointIter),
|
||||
iter: pointIter,
|
||||
rangeDelIter: rangeDelIter,
|
||||
})
|
||||
}
|
||||
it.alloc.merging.init(&it.opts, it.cmp, it.split, mlevels...)
|
||||
it.alloc.merging.snapshot = base.InternalKeySeqNumMax
|
||||
it.alloc.merging.elideRangeTombstones = true
|
||||
it.pointIter = &it.alloc.merging
|
||||
if len(combinedIters) == 1 {
|
||||
mlevels = append(mlevels, mergingIterLevel{
|
||||
iter: combinedIters[0],
|
||||
})
|
||||
} 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
|
||||
|
||||
if it.opts.rangeKeys() {
|
||||
it.rangeKeyMasking.init(it, it.comparer.Compare, it.comparer.Split)
|
||||
var rangeKeyIters []keyspan.FragmentIterator
|
||||
if it.rangeKey == nil {
|
||||
it.rangeKey = iterRangeKeyStateAllocPool.Get().(*iteratorRangeKeyState)
|
||||
it.rangeKey.init(it.cmp, it.split, &it.opts)
|
||||
it.rangeKey.rangeKeyIter = it.rangeKey.iterConfig.Init(
|
||||
it.cmp,
|
||||
base.InternalKeySeqNumMax,
|
||||
)
|
||||
for _, r := range it.externalReaders {
|
||||
if rki, err := r.NewRawRangeKeyIter(); err != nil {
|
||||
it.rangeKey.iterConfig.AddLevel(&errorKeyspanIter{err: err})
|
||||
} else if rki != nil {
|
||||
it.rangeKey.iterConfig.AddLevel(rki)
|
||||
// We could take advantage of the lack of overlaps in range keys within
|
||||
// each slice in it.externalReaders, and generate keyspan.LevelIters
|
||||
// out of those. However, since range keys are expected to be sparse to
|
||||
// begin with, the performance gain might not be significant enough to
|
||||
// warrant it.
|
||||
//
|
||||
// TODO(bilal): Explore adding a simpleRangeKeyLevelIter that does not
|
||||
// operate on FileMetadatas (similar to simpleLevelIter), and implements
|
||||
// this optimization.
|
||||
for _, readers := range it.externalReaders {
|
||||
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,
|
||||
it.opts.LowerBound, it.opts.UpperBound)
|
||||
it.iter = &it.rangeKey.iiter
|
||||
if it.rangeKey != nil {
|
||||
it.rangeKey.iiter.Init(&it.comparer, it.iter, it.rangeKey.rangeKeyIter,
|
||||
keyspan.InterleavingIterOpts{
|
||||
Mask: &it.rangeKeyMasking,
|
||||
LowerBound: it.opts.LowerBound,
|
||||
UpperBound: it.opts.UpperBound,
|
||||
})
|
||||
it.iter = &it.rangeKey.iiter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openExternalTables(
|
||||
o *Options,
|
||||
files []sstable.ReadableFile,
|
||||
seqNumOffset int,
|
||||
readerOpts sstable.ReaderOptions,
|
||||
extraReaderOpts ...sstable.ReaderOption,
|
||||
) (readers []*sstable.Reader, err error) {
|
||||
readers = make([]*sstable.Reader, 0, len(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 {
|
||||
return readers, err
|
||||
}
|
||||
// Use the index of the file in files as the sequence number for all of
|
||||
// its keys.
|
||||
r.Properties.GlobalSeqNum = uint64(len(files) - i)
|
||||
r.Properties.GlobalSeqNum = uint64(len(files) - i + seqNumOffset)
|
||||
readers = append(readers, r)
|
||||
}
|
||||
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
|
||||
// use. Newer versions of Pebble running newer format major versions do
|
||||
// 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)
|
||||
oldFilename := base.MakeFilepath(fs, dirname, fileTypeTemp, fileNum)
|
||||
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