From 8ce60159cf5cf9c13ae10a7a94266b9e17e85c8e Mon Sep 17 00:00:00 2001 From: Martin Tournoij Date: Sat, 4 Jul 2020 09:10:25 +0800 Subject: [PATCH] Fixes for SQLite concurrency issues In some busy workloads various SQLite operations would fail due to "locked tables"; most of this is related to the session code: - On every pageview a new session is either read (OK) or written (LOCK!) - At the same time, the cron may be writing data to the db (LOCK!) On smaller instances this isn't much of an issue since everything is fast enough to not lock for too long, but on longer instances this can be a problem. Setting SetMaxOpenConns(1) solves this by limiting the connections writing to the database. Also set the default journal mode to WAL, which should give better performance. Both of this is done in the zgo.at/zdb update. Add "goatcounter help db" to document database usage a bit better. --- cmd/goatcounter/create.go | 22 ++++---- cmd/goatcounter/help.go | 67 ++++++++++++++++++++++++- cmd/goatcounter/main.go | 37 ++++++++------ cmd/goatcounter/migrate.go | 10 ++-- cmd/goatcounter/monitor.go | 12 ++--- cmd/goatcounter/reindex.go | 30 +++++------ cmd/goatcounter/serve.go | 100 ++++++++++++++++++------------------- go.mod | 6 +-- go.sum | 18 ++++--- 9 files changed, 186 insertions(+), 116 deletions(-) diff --git a/cmd/goatcounter/create.go b/cmd/goatcounter/create.go index 836eca381..722ae81ca 100644 --- a/cmd/goatcounter/create.go +++ b/cmd/goatcounter/create.go @@ -26,25 +26,25 @@ Create a new site and user. Required flags: - -domain Domain to host e.g. "stats.example.com". The site will be - available on this domain only, so "stats.example.com" won't be - available on "localhost". + -domain Domain to host e.g. "stats.example.com". The site will be + available on this domain only, so "stats.example.com" won't be + available on "localhost". - -email Your email address. Will be required to login. + -email Your email address. Will be required to login. Other flags: - -password Password to log in; will be asked interactively if omitted. + -password Password to log in; will be asked interactively if omitted. - -parent Parent site; either as ID or domain. + -parent Parent site; either as ID or domain. - -db Database connection string. Use "sqlite://" for SQLite, - or "postgres://" for PostgreSQL - Default: sqlite://db/goatcounter.sqlite3 + -db Database connection: "sqlite://" or "postgres://" + See "goatcounter help db" for detailed documentation. Default: + sqlite://db/goatcounter.sqlite3?_busy_timeout=200&_journal_mode=wal&cache=shared - -createdb Create the database if it doesn't exist yet; only for SQLite. + -createdb Create the database if it doesn't exist yet; only for SQLite. - -debug Modules to debug, comma-separated or 'all' for all modules. + -debug Modules to debug, comma-separated or 'all' for all modules. ` func create() (int, error) { diff --git a/cmd/goatcounter/help.go b/cmd/goatcounter/help.go index b8c1c2f62..5a7a930cf 100644 --- a/cmd/goatcounter/help.go +++ b/cmd/goatcounter/help.go @@ -16,6 +16,66 @@ Show help; use "help commands" to dispay detailed help for a command, or "help all" to display everything. ` +const helpDatabase = ` +GoatCounter can use SQLite and PostgreSQL. All commands accept the -db flag to +customize the database connection string. + +You can select a database engine by using "sqlite://[..]" for SQLite, or +"postgresql://[..]" (or "postgres://[..]") for PostgreSQL. + +There are no plans to support other database engines such as MySQL/MariaDB. + +SQLite + + This is the default database engine as it has no dependencies, and for most + small to medium usage it should be more than fast enough. + + The SQLite connection string is usually just a filename, optionally prefixed + with "file:". Parameters can be added as a URL query string after a ?: + + -db 'sqlite://mydb.sqlite?param=value&other=value' + + See the go-sqlite3 documentation for a list of supported parameters: + https://github.com/mattn/go-sqlite3/#connection-string + + _journal_mode=wal is always added unless explicitly overridden. Generally + speaking using a Write-Ahead-Log is more suitable for GoatCounter than the + default DELETE journaling. + + The database is automatically created for the "serve" command, but you need + to add -createdb to any other commands to create the database. This is to + prevent accidentally operating on the wrong (new) database. + +PostgreSQL + + PostgreSQL provides better performance for large instances. If you have + millions of pageviews then PostgreSQL is probably a better choice. + + The PostgreSQL connection string can either be as "key=value" or as an URL; + the following are identical: + + -db 'postgresql://user=pqgotest dbname=pqgotest sslmode=verify-full' + -db 'postgresql://pqgotest:password@localhost/pqgotest?sslmode=verify-full' + + See the pq documentation for a list of supported parameters: + https://pkg.go.dev/github.com/lib/pq?tab=doc#hdr-Connection_String_Parameters + + You may want to consider lowering the "seq_page_cost" parameter; the query + planner tends to prefer seq scans instead of index scans for some operations + with the default of 4, which is much slower. + + I found that 0.5 is a fairly good setting, you can set it in your + postgresql.conf file, or just for one database with: + + alter database goatcounter set seq_page_cost=.5 + + The database isn't automatically created for PostgreSQL, you'll have to + manually create it first: + + createdb goatcounter + psql goatcounter < ./db/schema.pgsql +` + func help() (int, error) { if len(os.Args) == 2 { fmt.Fprint(stdout, usage[""]) @@ -24,7 +84,12 @@ func help() (int, error) { if os.Args[2] == "all" { fmt.Fprint(stdout, usage[""], "\n") - for _, h := range []string{"help", "version", "migrate", "serve", "create", "reindex", "monitor"} { + for _, h := range []string{ + "help", "version", + "migrate", "create", "serve", + "reindex", "monitor", + "database", + } { head := fmt.Sprintf("─── Help for %q ", h) fmt.Fprintf(stdout, "%s%s\n\n", head, strings.Repeat("─", 80-utf8.RuneCountInString(head))) fmt.Fprint(stdout, usage[h], "\n") diff --git a/cmd/goatcounter/main.go b/cmd/goatcounter/main.go index 584515da5..947f498f9 100644 --- a/cmd/goatcounter/main.go +++ b/cmd/goatcounter/main.go @@ -35,14 +35,16 @@ var ( ) var usage = map[string]string{ - "": usageTop, - "help": usageHelp, - "serve": usageServe, - "create": usageCreate, - "migrate": usageMigrate, - "saas": usageSaas, - "reindex": usageReindex, - "monitor": usageMonitor, + "": usageTop, + "help": usageHelp, + "serve": usageServe, + "create": usageCreate, + "migrate": usageMigrate, + "saas": usageSaas, + "reindex": usageReindex, + "monitor": usageMonitor, + "database": helpDatabase, + "db": helpDatabase, "version": ` Show version and build information. This is printed as key=value, separated by @@ -62,17 +64,20 @@ Usage: goatcounter [command] [flags] Commands: - help Show help; use "help " or "help all" for more details. - version Show version and build information and exit. - migrate Run database migrations. - create Create a new site and user. - serve Start HTTP server. + help Show help; use "help " or "help all" for more details. + version Show version and build information and exit. + migrate Run database migrations. + create Create a new site and user. + serve Start HTTP server. Advanced commands: - reindex Re-create the cached statistics (*_stats tables) from the hits. - This is generally rarely needed and mostly a development tool. - monitor Monitor for pageviews. + reindex Recreate the index tables (*_stats, *_count) from the hits. + monitor Monitor for pageviews. + +Extra help topics: + + db Documentation on the -db flag. See "help " for more details for the command. ` diff --git a/cmd/goatcounter/migrate.go b/cmd/goatcounter/migrate.go index b61747744..7887d8434 100644 --- a/cmd/goatcounter/migrate.go +++ b/cmd/goatcounter/migrate.go @@ -22,13 +22,13 @@ Run database migrations and exit. Flags: - -db Database connection string. Use "sqlite://" for SQLite, - or "postgres://" for PostgreSQL - Default: sqlite://db/goatcounter.sqlite3 + -db Database connection: "sqlite://" or "postgres://" + See "goatcounter help db" for detailed documentation. Default: + sqlite://db/goatcounter.sqlite3?_busy_timeout=200&_journal_mode=wal&cache=shared - -createdb Create the database if it doesn't exist yet; only for SQLite. + -createdb Create the database if it doesn't exist yet; only for SQLite. - -debug Modules to debug, comma-separated or 'all' for all modules. + -debug Modules to debug, comma-separated or 'all' for all modules. Positional arguments are names of database migrations, either as just the name ("2020-01-05-2-foo") or as the file path ("./db/migrate/sqlite/2020-01-05-2-foo.sql"). diff --git a/cmd/goatcounter/monitor.go b/cmd/goatcounter/monitor.go index 0196665fb..7c05f5cd9 100644 --- a/cmd/goatcounter/monitor.go +++ b/cmd/goatcounter/monitor.go @@ -18,15 +18,15 @@ log if it's 0. Flags: - -db Database connection string. Use "sqlite://" for SQLite, - or "postgres://" for PostgreSQL - Default: sqlite://db/goatcounter.sqlite3 + -db Database connection: "sqlite://" or "postgres://" + See "goatcounter help db" for detailed documentation. Default: + sqlite://db/goatcounter.sqlite3?_busy_timeout=200&_journal_mode=wal&cache=shared - -debug Modules to debug, comma-separated or 'all' for all modules. + -debug Modules to debug, comma-separated or 'all' for all modules. - -period Check every n seconds. Default: 120. + -period Check every n seconds. Default: 120. - -site Limit the check to just one site; makes the query faster. + -site Limit the check to just one site; makes the query faster. ` func monitor() (int, error) { diff --git a/cmd/goatcounter/reindex.go b/cmd/goatcounter/reindex.go index 3f655f96d..4da56708d 100644 --- a/cmd/goatcounter/reindex.go +++ b/cmd/goatcounter/reindex.go @@ -43,28 +43,28 @@ Avoiding race conditions Flags: - -db Database connection string. Use "sqlite://" for SQLite, - or "postgres://" for PostgreSQL - Default: sqlite://db/goatcounter.sqlite3 + -db Database connection: "sqlite://" or "postgres://" + See "goatcounter help db" for detailed documentation. Default: + sqlite://db/goatcounter.sqlite3?_busy_timeout=200&_journal_mode=wal&cache=shared - -debug Modules to debug, comma-separated or 'all' for all modules. + -debug Modules to debug, comma-separated or 'all' for all modules. - -pause Number of seconds to pause after each day, to give the server - some breathing room on large sites. Default: 0. + -pause Number of seconds to pause after each day, to give the server + some breathing room on large sites. Default: 0. - -since Reindex only statistics since this date instead of all of them; - as year-month-day in UTC. + -since Reindex only statistics since this date instead of all of them; + as year-month-day in UTC. - -to Reindex only statistics up to and including this day; as - year-month-day in UTC. The default is yesterday. + -to Reindex only statistics up to and including this day; as + year-month-day in UTC. The default is yesterday. - -table Which tables to reindex: hit_stats, hit_counts, browser_stats, - system_stats, location_stats, ref_counts, size_stats, or all - (default). + -table Which tables to reindex: hit_stats, hit_counts, browser_stats, + system_stats, location_stats, ref_counts, size_stats, or all + (default). - -site Only reindex this site ID. Default is to reindex all. + -site Only reindex this site ID. Default is to reindex all. - -quiet Don't print progress. + -quiet Don't print progress. ` func reindex() (int, error) { diff --git a/cmd/goatcounter/serve.go b/cmd/goatcounter/serve.go index 5394878ab..41884139f 100644 --- a/cmd/goatcounter/serve.go +++ b/cmd/goatcounter/serve.go @@ -33,85 +33,83 @@ with -dev. Flags: - -static Serve static files from a diffent domain, such as a CDN or - cookieless domain. Default: not set. + -static Serve static files from a diffent domain, such as a CDN or + cookieless domain. Default: not set. - -port Port your site is publicly accessible on. Only needed if it's - not 80 or 443. + -port Port your site is publicly accessible on. Only needed if it's + not 80 or 443. ` + serveAndSaasFlags const serveAndSaasFlags = ` - -db Database connection string. Use "sqlite://" for SQLite, - or "postgres://" for PostgreSQL - Default: sqlite://db/goatcounter.sqlite3 + -db Database connection: "sqlite://" or "postgres://" + See "goatcounter help db" for detailed documentation. Default: + sqlite://db/goatcounter.sqlite3?_busy_timeout=200&_journal_mode=wal&cache=shared - -listen Address to listen on. Default: localhost:8081 + -listen Address to listen on. Default: localhost:8081 - -dev Start in "dev mode". + -dev Start in "dev mode". - -tls Serve over tls. This is a comma-separated list with any of: + -tls Serve over tls. This is a comma-separated list with any of: - none Don't serve any TLS. + none Don't serve any TLS. - path/to/file.pem TLS certificate and keyfile, in one file. + path/to/file.pem TLS certificate and keyfile, in one file. - acme[:cache] Create TLS certificates with ACME, this can - optionally followed by a : and a cache - directory name (default: acme-secrets). + acme[:cache] Create TLS certificates with ACME, this can + optionally followed by a : and a cache + directory name (default: acme-secrets). - tls Accept TLS connections on -listen. + tls Accept TLS connections on -listen. - rdr Redirect port 80 to the -listen port. ACME - verification requires the server to be - available on port 80. + rdr Redirect port 80 to the -listen port. ACME + verification requires the server to be + available on port 80. - Examples: + Examples: - acme Create ACME certs but serve HTTP, - useful when serving behind proxy - which can use the certs. + acme Create ACME certs but serve HTTP, + useful when serving behind proxy + which can use the certs. - acme:/home/gc/.acme As above, but with custom cache dir. + acme:/home/gc/.acme As above, but with custom cache dir. - ./example.com.pem,tls,rdr Always use the certificate in the - file, serve over TLS, and redirect - port 80. + ./example.com.pem,tls,rdr Always use the certificate in the + file, serve over TLS, and redirect + port 80. - Default: "acme,tls,rdr", blank when -dev is given. + Default: "acme,tls,rdr", blank when -dev is given. - -smtp SMTP server, as URL (e.g. "smtp://user:pass@server"). + -smtp SMTP server, as URL (e.g. "smtp://user:pass@server"). - A special value of "stdout" means no emails will be sent and - emails will be printed to stdout only. This is the default. + A special value of "stdout" means no emails will be sent and + emails will be printed to stdout only. This is the default. - If this is blank emails will be sent without using a relay; - this should work fine, but deliverability will usually be worse - (i.e. it will be more likely to end up in the spam box). This - usually requires rDNS properly set up, and GoatCounter will - *not* retry on errors. Using stdout, a local smtp relay, or a - mailtrap.io box is probably better unless you really know what - you're doing. + If this is blank emails will be sent without using a relay; this + should work fine, but deliverability will usually be worse (i.e. + it will be more likely to end up in the spam box). This usually + requires rDNS properly set up, and GoatCounter will *not* retry + on errors. Using stdout, a local smtp relay, or a mailtrap.io box + is probably better unless you really know what you're doing. - -email-from From: address in emails. Default: @ + -email-from From: address in emails. Default: @ - -errors What to do with errors; they're always printed to stderr. + -errors What to do with errors; they're always printed to stderr. - mailto:to_addr[,from_addr] Email to this address; the - from_addr is optional and sets - the From: address. The default is - to use the same as the to_addr. + mailto:to_addr[,from_addr] Email to this address; the + from_addr is optional and sets the + From: address. The default is to + use the same as the to_addr. + Default: not set. - Default: not set. + -debug Modules to debug, comma-separated or 'all' for all modules. - -debug Modules to debug, comma-separated or 'all' for all modules. - - -automigrate Automatically run all pending migrations on startup. + -automigrate Automatically run all pending migrations on startup. Environment: - TMPDIR Directory for temporary files; only used to store CSV exports - at the moment. On Windows it will use the first non-empty value - of %TMP%, %TEMP%, and %USERPROFILE%. + TMPDIR Directory for temporary files; only used to store CSV exports at + the moment. On Windows it will use the first non-empty value of + %TMP%, %TEMP%, and %USERPROFILE%. ` func serve() (int, error) { diff --git a/go.mod b/go.mod index a34c73461..3c6d79e92 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/arp242/geoip2-golang v1.4.0 github.com/go-chi/chi v4.1.1+incompatible github.com/jmoiron/sqlx v1.2.0 - github.com/lib/pq v1.5.2 + github.com/lib/pq v1.7.0 github.com/mattn/go-sqlite3 v2.0.3+incompatible github.com/monoculum/formam v0.0.0-20200316225015-49f0baed3a1b github.com/teamwork/reload v1.3.2 @@ -21,11 +21,11 @@ require ( zgo.at/guru v1.1.0 zgo.at/isbot v0.0.0-20200518180519-d9e7a12daaea zgo.at/tz v0.0.0-20200520034804-aeba38d94d93 - zgo.at/zdb v0.0.0-20200529134049-932215d95f5b + zgo.at/zdb v0.0.0-20200704013256-53f5f3182386 zgo.at/zhttp v0.0.0-20200525055724-f2c83b7d82c1 zgo.at/zlog v0.0.0-20200519105857-4dc5e4ffe04c zgo.at/zpack v1.0.1 - zgo.at/zstd v0.0.0-20200528080824-83897c2259b4 + zgo.at/zstd v0.0.0-20200602040707-aaf6806a6bf5 zgo.at/zstripe v1.0.0 zgo.at/ztest v0.0.0-20200526130303-f59d60374e72 zgo.at/zvalidate v0.0.0-20200417055504-a940f5b1be33 diff --git a/go.sum b/go.sum index dc27a7a64..ed99411d9 100644 --- a/go.sum +++ b/go.sum @@ -26,11 +26,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= -github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.5.2 h1:yTSXVswvWUOQ3k1sd7vJfDrbSl8lKuscqFJRqjC0ifw= -github.com/lib/pq v1.5.2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= +github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/monoculum/formam v0.0.0-20200316225015-49f0baed3a1b h1:BvSZE/bUSz180cQzAEDVOh7seh57UNBlcGAte0CQ8l4= @@ -57,6 +56,8 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjut golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -67,6 +68,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4 h1:Hynbrlo6LbYI3H1IqXpkVDOcX/3HiPdhVEuyj5a59RM= golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4/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 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= @@ -97,8 +99,8 @@ zgo.at/isbot v0.0.0-20200518180519-d9e7a12daaea h1:Cl3Dnjfxvq1nOIffaDbhJOrnwb6aD zgo.at/isbot v0.0.0-20200518180519-d9e7a12daaea/go.mod h1:/w+LTWbHsV7G5fT41VnwM0kTdk1H/prynfMqaxcBWjA= zgo.at/tz v0.0.0-20200520034804-aeba38d94d93 h1:9jisiomiXkQ2K2yTJQOO5fL+Txg+rKxytHj8G8kRl2M= zgo.at/tz v0.0.0-20200520034804-aeba38d94d93/go.mod h1:A/XeaYjeMGoXptRB3EcR80tgir37tJnzCb6itDaHPxo= -zgo.at/zdb v0.0.0-20200529134049-932215d95f5b h1:ZLhAXHqTRUsR61eTcwFQ0gFDL585VzISU1307/jk2Ho= -zgo.at/zdb v0.0.0-20200529134049-932215d95f5b/go.mod h1:zu+rvWosTqQeaTUgzZQdkGrXN+X8gjePHUgbhxx6E6I= +zgo.at/zdb v0.0.0-20200704013256-53f5f3182386 h1:4uqJQOMBQbjBBAZYbONeXnaAdHP5mhOHf+mW/K1sC4U= +zgo.at/zdb v0.0.0-20200704013256-53f5f3182386/go.mod h1:qBj20DkzhioyoAgkqCNNrQYtU9dkaQXU6kko5B36zbg= zgo.at/zhttp v0.0.0-20200525055724-f2c83b7d82c1 h1:N2Bi3r6RB3THPU2wVajIUi3/AvJ0+5joRomVjByUz+c= zgo.at/zhttp v0.0.0-20200525055724-f2c83b7d82c1/go.mod h1:lnKdr+Z/u747AVwOkBKweLbFFe0yyvV2JY8NONI4XpQ= zgo.at/zlog v0.0.0-20200404052423-adffcc8acd57 h1:sIuh9IpccNlzMsmAvF7EPT87/Ab2XdUJHizsMxbQgM4= @@ -107,8 +109,8 @@ zgo.at/zlog v0.0.0-20200519105857-4dc5e4ffe04c h1:PQksY26D9QgCNbOvMmortUzbStTYwf zgo.at/zlog v0.0.0-20200519105857-4dc5e4ffe04c/go.mod h1:YkLAQZjLsp1RqWHn8SJokHzXyYU+6FZjM4GNY64IKME= zgo.at/zpack v1.0.1 h1:muDVwpdzfC/1+U2rsTsv0feI82zxMq0CZuHPZ9RTwJ8= zgo.at/zpack v1.0.1/go.mod h1:2laAAHqImmeiTlMAQ2PlMRBVhS6YKPkUoosMfdv7GXY= -zgo.at/zstd v0.0.0-20200528080824-83897c2259b4 h1:eOVo2ykKjVz8clrG2bvTGtyXNjq9I5X+INbvYiQ/UTQ= -zgo.at/zstd v0.0.0-20200528080824-83897c2259b4/go.mod h1:wSF/3tBbAJVo+RYk5CTMmSN2xxyy4C+qTb5fN4WP9iA= +zgo.at/zstd v0.0.0-20200602040707-aaf6806a6bf5 h1:pP0ZXWWkzwPZkWs6/L07XPB3AL4ny3w7CsMeqCDr+4s= +zgo.at/zstd v0.0.0-20200602040707-aaf6806a6bf5/go.mod h1:wSF/3tBbAJVo+RYk5CTMmSN2xxyy4C+qTb5fN4WP9iA= zgo.at/zstripe v1.0.0 h1:rfBrWEUpveYFZKNo4rdc3Fvhb9LEzbMIyL76EXAmXDk= zgo.at/zstripe v1.0.0/go.mod h1:EqblFpMvXAhzZAUUt0EVom06EnN5+D5ESBTSOkwSwTY= zgo.at/ztest v0.0.0-20200316134318-cfad86d80b41 h1:2vR+4hRs2YI2Lcm68AjHLSOHxQy/3XJ/b2nCV/dCrxs=