Skip to content

Commit 8ce6015

Browse files
committed
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.
1 parent 8e2c83b commit 8ce6015

File tree

9 files changed

+186
-116
lines changed

9 files changed

+186
-116
lines changed

cmd/goatcounter/create.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,25 @@ Create a new site and user.
2626
2727
Required flags:
2828
29-
-domain Domain to host e.g. "stats.example.com". The site will be
30-
available on this domain only, so "stats.example.com" won't be
31-
available on "localhost".
29+
-domain Domain to host e.g. "stats.example.com". The site will be
30+
available on this domain only, so "stats.example.com" won't be
31+
available on "localhost".
3232
33-
-email Your email address. Will be required to login.
33+
-email Your email address. Will be required to login.
3434
3535
Other flags:
3636
37-
-password Password to log in; will be asked interactively if omitted.
37+
-password Password to log in; will be asked interactively if omitted.
3838
39-
-parent Parent site; either as ID or domain.
39+
-parent Parent site; either as ID or domain.
4040
41-
-db Database connection string. Use "sqlite://<dbfile>" for SQLite,
42-
or "postgres://<connect string>" for PostgreSQL
43-
Default: sqlite://db/goatcounter.sqlite3
41+
-db Database connection: "sqlite://<file>" or "postgres://<connect>"
42+
See "goatcounter help db" for detailed documentation. Default:
43+
sqlite://db/goatcounter.sqlite3?_busy_timeout=200&_journal_mode=wal&cache=shared
4444
45-
-createdb Create the database if it doesn't exist yet; only for SQLite.
45+
-createdb Create the database if it doesn't exist yet; only for SQLite.
4646
47-
-debug Modules to debug, comma-separated or 'all' for all modules.
47+
-debug Modules to debug, comma-separated or 'all' for all modules.
4848
`
4949

5050
func create() (int, error) {

cmd/goatcounter/help.go

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,66 @@ Show help; use "help commands" to dispay detailed help for a command, or "help
1616
all" to display everything.
1717
`
1818

19+
const helpDatabase = `
20+
GoatCounter can use SQLite and PostgreSQL. All commands accept the -db flag to
21+
customize the database connection string.
22+
23+
You can select a database engine by using "sqlite://[..]" for SQLite, or
24+
"postgresql://[..]" (or "postgres://[..]") for PostgreSQL.
25+
26+
There are no plans to support other database engines such as MySQL/MariaDB.
27+
28+
SQLite
29+
30+
This is the default database engine as it has no dependencies, and for most
31+
small to medium usage it should be more than fast enough.
32+
33+
The SQLite connection string is usually just a filename, optionally prefixed
34+
with "file:". Parameters can be added as a URL query string after a ?:
35+
36+
-db 'sqlite://mydb.sqlite?param=value&other=value'
37+
38+
See the go-sqlite3 documentation for a list of supported parameters:
39+
https://github.com/mattn/go-sqlite3/#connection-string
40+
41+
_journal_mode=wal is always added unless explicitly overridden. Generally
42+
speaking using a Write-Ahead-Log is more suitable for GoatCounter than the
43+
default DELETE journaling.
44+
45+
The database is automatically created for the "serve" command, but you need
46+
to add -createdb to any other commands to create the database. This is to
47+
prevent accidentally operating on the wrong (new) database.
48+
49+
PostgreSQL
50+
51+
PostgreSQL provides better performance for large instances. If you have
52+
millions of pageviews then PostgreSQL is probably a better choice.
53+
54+
The PostgreSQL connection string can either be as "key=value" or as an URL;
55+
the following are identical:
56+
57+
-db 'postgresql://user=pqgotest dbname=pqgotest sslmode=verify-full'
58+
-db 'postgresql://pqgotest:password@localhost/pqgotest?sslmode=verify-full'
59+
60+
See the pq documentation for a list of supported parameters:
61+
https://pkg.go.dev/github.com/lib/pq?tab=doc#hdr-Connection_String_Parameters
62+
63+
You may want to consider lowering the "seq_page_cost" parameter; the query
64+
planner tends to prefer seq scans instead of index scans for some operations
65+
with the default of 4, which is much slower.
66+
67+
I found that 0.5 is a fairly good setting, you can set it in your
68+
postgresql.conf file, or just for one database with:
69+
70+
alter database goatcounter set seq_page_cost=.5
71+
72+
The database isn't automatically created for PostgreSQL, you'll have to
73+
manually create it first:
74+
75+
createdb goatcounter
76+
psql goatcounter < ./db/schema.pgsql
77+
`
78+
1979
func help() (int, error) {
2080
if len(os.Args) == 2 {
2181
fmt.Fprint(stdout, usage[""])
@@ -24,7 +84,12 @@ func help() (int, error) {
2484

2585
if os.Args[2] == "all" {
2686
fmt.Fprint(stdout, usage[""], "\n")
27-
for _, h := range []string{"help", "version", "migrate", "serve", "create", "reindex", "monitor"} {
87+
for _, h := range []string{
88+
"help", "version",
89+
"migrate", "create", "serve",
90+
"reindex", "monitor",
91+
"database",
92+
} {
2893
head := fmt.Sprintf("─── Help for %q ", h)
2994
fmt.Fprintf(stdout, "%s%s\n\n", head, strings.Repeat("─", 80-utf8.RuneCountInString(head)))
3095
fmt.Fprint(stdout, usage[h], "\n")

cmd/goatcounter/main.go

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,16 @@ var (
3535
)
3636

3737
var usage = map[string]string{
38-
"": usageTop,
39-
"help": usageHelp,
40-
"serve": usageServe,
41-
"create": usageCreate,
42-
"migrate": usageMigrate,
43-
"saas": usageSaas,
44-
"reindex": usageReindex,
45-
"monitor": usageMonitor,
38+
"": usageTop,
39+
"help": usageHelp,
40+
"serve": usageServe,
41+
"create": usageCreate,
42+
"migrate": usageMigrate,
43+
"saas": usageSaas,
44+
"reindex": usageReindex,
45+
"monitor": usageMonitor,
46+
"database": helpDatabase,
47+
"db": helpDatabase,
4648

4749
"version": `
4850
Show version and build information. This is printed as key=value, separated by
@@ -62,17 +64,20 @@ Usage: goatcounter [command] [flags]
6264
6365
Commands:
6466
65-
help Show help; use "help <topic>" or "help all" for more details.
66-
version Show version and build information and exit.
67-
migrate Run database migrations.
68-
create Create a new site and user.
69-
serve Start HTTP server.
67+
help Show help; use "help <topic>" or "help all" for more details.
68+
version Show version and build information and exit.
69+
migrate Run database migrations.
70+
create Create a new site and user.
71+
serve Start HTTP server.
7072
7173
Advanced commands:
7274
73-
reindex Re-create the cached statistics (*_stats tables) from the hits.
74-
This is generally rarely needed and mostly a development tool.
75-
monitor Monitor for pageviews.
75+
reindex Recreate the index tables (*_stats, *_count) from the hits.
76+
monitor Monitor for pageviews.
77+
78+
Extra help topics:
79+
80+
db Documentation on the -db flag.
7681
7782
See "help <topic>" for more details for the command.
7883
`

cmd/goatcounter/migrate.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ Run database migrations and exit.
2222
2323
Flags:
2424
25-
-db Database connection string. Use "sqlite://<dbfile>" for SQLite,
26-
or "postgres://<connect string>" for PostgreSQL
27-
Default: sqlite://db/goatcounter.sqlite3
25+
-db Database connection: "sqlite://<file>" or "postgres://<connect>"
26+
See "goatcounter help db" for detailed documentation. Default:
27+
sqlite://db/goatcounter.sqlite3?_busy_timeout=200&_journal_mode=wal&cache=shared
2828
29-
-createdb Create the database if it doesn't exist yet; only for SQLite.
29+
-createdb Create the database if it doesn't exist yet; only for SQLite.
3030
31-
-debug Modules to debug, comma-separated or 'all' for all modules.
31+
-debug Modules to debug, comma-separated or 'all' for all modules.
3232
3333
Positional arguments are names of database migrations, either as just the name
3434
("2020-01-05-2-foo") or as the file path ("./db/migrate/sqlite/2020-01-05-2-foo.sql").

cmd/goatcounter/monitor.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ log if it's 0.
1818
1919
Flags:
2020
21-
-db Database connection string. Use "sqlite://<dbfile>" for SQLite,
22-
or "postgres://<connect string>" for PostgreSQL
23-
Default: sqlite://db/goatcounter.sqlite3
21+
-db Database connection: "sqlite://<file>" or "postgres://<connect>"
22+
See "goatcounter help db" for detailed documentation. Default:
23+
sqlite://db/goatcounter.sqlite3?_busy_timeout=200&_journal_mode=wal&cache=shared
2424
25-
-debug Modules to debug, comma-separated or 'all' for all modules.
25+
-debug Modules to debug, comma-separated or 'all' for all modules.
2626
27-
-period Check every n seconds. Default: 120.
27+
-period Check every n seconds. Default: 120.
2828
29-
-site Limit the check to just one site; makes the query faster.
29+
-site Limit the check to just one site; makes the query faster.
3030
`
3131

3232
func monitor() (int, error) {

cmd/goatcounter/reindex.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,28 +43,28 @@ Avoiding race conditions
4343
4444
Flags:
4545
46-
-db Database connection string. Use "sqlite://<dbfile>" for SQLite,
47-
or "postgres://<connect string>" for PostgreSQL
48-
Default: sqlite://db/goatcounter.sqlite3
46+
-db Database connection: "sqlite://<file>" or "postgres://<connect>"
47+
See "goatcounter help db" for detailed documentation. Default:
48+
sqlite://db/goatcounter.sqlite3?_busy_timeout=200&_journal_mode=wal&cache=shared
4949
50-
-debug Modules to debug, comma-separated or 'all' for all modules.
50+
-debug Modules to debug, comma-separated or 'all' for all modules.
5151
52-
-pause Number of seconds to pause after each day, to give the server
53-
some breathing room on large sites. Default: 0.
52+
-pause Number of seconds to pause after each day, to give the server
53+
some breathing room on large sites. Default: 0.
5454
55-
-since Reindex only statistics since this date instead of all of them;
56-
as year-month-day in UTC.
55+
-since Reindex only statistics since this date instead of all of them;
56+
as year-month-day in UTC.
5757
58-
-to Reindex only statistics up to and including this day; as
59-
year-month-day in UTC. The default is yesterday.
58+
-to Reindex only statistics up to and including this day; as
59+
year-month-day in UTC. The default is yesterday.
6060
61-
-table Which tables to reindex: hit_stats, hit_counts, browser_stats,
62-
system_stats, location_stats, ref_counts, size_stats, or all
63-
(default).
61+
-table Which tables to reindex: hit_stats, hit_counts, browser_stats,
62+
system_stats, location_stats, ref_counts, size_stats, or all
63+
(default).
6464
65-
-site Only reindex this site ID. Default is to reindex all.
65+
-site Only reindex this site ID. Default is to reindex all.
6666
67-
-quiet Don't print progress.
67+
-quiet Don't print progress.
6868
`
6969

7070
func reindex() (int, error) {

cmd/goatcounter/serve.go

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -33,85 +33,83 @@ with -dev.
3333
3434
Flags:
3535
36-
-static Serve static files from a diffent domain, such as a CDN or
37-
cookieless domain. Default: not set.
36+
-static Serve static files from a diffent domain, such as a CDN or
37+
cookieless domain. Default: not set.
3838
39-
-port Port your site is publicly accessible on. Only needed if it's
40-
not 80 or 443.
39+
-port Port your site is publicly accessible on. Only needed if it's
40+
not 80 or 443.
4141
` + serveAndSaasFlags
4242

4343
const serveAndSaasFlags = `
44-
-db Database connection string. Use "sqlite://<dbfile>" for SQLite,
45-
or "postgres://<connect string>" for PostgreSQL
46-
Default: sqlite://db/goatcounter.sqlite3
44+
-db Database connection: "sqlite://<file>" or "postgres://<connect>"
45+
See "goatcounter help db" for detailed documentation. Default:
46+
sqlite://db/goatcounter.sqlite3?_busy_timeout=200&_journal_mode=wal&cache=shared
4747
48-
-listen Address to listen on. Default: localhost:8081
48+
-listen Address to listen on. Default: localhost:8081
4949
50-
-dev Start in "dev mode".
50+
-dev Start in "dev mode".
5151
52-
-tls Serve over tls. This is a comma-separated list with any of:
52+
-tls Serve over tls. This is a comma-separated list with any of:
5353
54-
none Don't serve any TLS.
54+
none Don't serve any TLS.
5555
56-
path/to/file.pem TLS certificate and keyfile, in one file.
56+
path/to/file.pem TLS certificate and keyfile, in one file.
5757
58-
acme[:cache] Create TLS certificates with ACME, this can
59-
optionally followed by a : and a cache
60-
directory name (default: acme-secrets).
58+
acme[:cache] Create TLS certificates with ACME, this can
59+
optionally followed by a : and a cache
60+
directory name (default: acme-secrets).
6161
62-
tls Accept TLS connections on -listen.
62+
tls Accept TLS connections on -listen.
6363
64-
rdr Redirect port 80 to the -listen port. ACME
65-
verification requires the server to be
66-
available on port 80.
64+
rdr Redirect port 80 to the -listen port. ACME
65+
verification requires the server to be
66+
available on port 80.
6767
68-
Examples:
68+
Examples:
6969
70-
acme Create ACME certs but serve HTTP,
71-
useful when serving behind proxy
72-
which can use the certs.
70+
acme Create ACME certs but serve HTTP,
71+
useful when serving behind proxy
72+
which can use the certs.
7373
74-
acme:/home/gc/.acme As above, but with custom cache dir.
74+
acme:/home/gc/.acme As above, but with custom cache dir.
7575
76-
./example.com.pem,tls,rdr Always use the certificate in the
77-
file, serve over TLS, and redirect
78-
port 80.
76+
./example.com.pem,tls,rdr Always use the certificate in the
77+
file, serve over TLS, and redirect
78+
port 80.
7979
80-
Default: "acme,tls,rdr", blank when -dev is given.
80+
Default: "acme,tls,rdr", blank when -dev is given.
8181
82-
-smtp SMTP server, as URL (e.g. "smtp://user:pass@server").
82+
-smtp SMTP server, as URL (e.g. "smtp://user:pass@server").
8383
84-
A special value of "stdout" means no emails will be sent and
85-
emails will be printed to stdout only. This is the default.
84+
A special value of "stdout" means no emails will be sent and
85+
emails will be printed to stdout only. This is the default.
8686
87-
If this is blank emails will be sent without using a relay;
88-
this should work fine, but deliverability will usually be worse
89-
(i.e. it will be more likely to end up in the spam box). This
90-
usually requires rDNS properly set up, and GoatCounter will
91-
*not* retry on errors. Using stdout, a local smtp relay, or a
92-
mailtrap.io box is probably better unless you really know what
93-
you're doing.
87+
If this is blank emails will be sent without using a relay; this
88+
should work fine, but deliverability will usually be worse (i.e.
89+
it will be more likely to end up in the spam box). This usually
90+
requires rDNS properly set up, and GoatCounter will *not* retry
91+
on errors. Using stdout, a local smtp relay, or a mailtrap.io box
92+
is probably better unless you really know what you're doing.
9493
95-
-email-from From: address in emails. Default: <user>@<hostname>
94+
-email-from From: address in emails. Default: <user>@<hostname>
9695
97-
-errors What to do with errors; they're always printed to stderr.
96+
-errors What to do with errors; they're always printed to stderr.
9897
99-
mailto:to_addr[,from_addr] Email to this address; the
100-
from_addr is optional and sets
101-
the From: address. The default is
102-
to use the same as the to_addr.
98+
mailto:to_addr[,from_addr] Email to this address; the
99+
from_addr is optional and sets the
100+
From: address. The default is to
101+
use the same as the to_addr.
102+
Default: not set.
103103
104-
Default: not set.
104+
-debug Modules to debug, comma-separated or 'all' for all modules.
105105
106-
-debug Modules to debug, comma-separated or 'all' for all modules.
107-
108-
-automigrate Automatically run all pending migrations on startup.
106+
-automigrate Automatically run all pending migrations on startup.
109107
110108
Environment:
111109
112-
TMPDIR Directory for temporary files; only used to store CSV exports
113-
at the moment. On Windows it will use the first non-empty value
114-
of %TMP%, %TEMP%, and %USERPROFILE%.
110+
TMPDIR Directory for temporary files; only used to store CSV exports at
111+
the moment. On Windows it will use the first non-empty value of
112+
%TMP%, %TEMP%, and %USERPROFILE%.
115113
`
116114

117115
func serve() (int, error) {

0 commit comments

Comments
 (0)