Cache startup queries
This commit is contained in:
parent
b4a824aa38
commit
113900d3eb
9 changed files with 81 additions and 34 deletions
|
@ -40,6 +40,7 @@ var flagsServe = append(
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "firebase-key-file", Aliases: []string{"firebase_key_file", "F"}, EnvVars: []string{"NTFY_FIREBASE_KEY_FILE"}, Usage: "Firebase credentials file; if set additionally publish to FCM topic"}),
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "firebase-key-file", Aliases: []string{"firebase_key_file", "F"}, EnvVars: []string{"NTFY_FIREBASE_KEY_FILE"}, Usage: "Firebase credentials file; if set additionally publish to FCM topic"}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "cache-file", Aliases: []string{"cache_file", "C"}, EnvVars: []string{"NTFY_CACHE_FILE"}, Usage: "cache file used for message caching"}),
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "cache-file", Aliases: []string{"cache_file", "C"}, EnvVars: []string{"NTFY_CACHE_FILE"}, Usage: "cache file used for message caching"}),
|
||||||
altsrc.NewDurationFlag(&cli.DurationFlag{Name: "cache-duration", Aliases: []string{"cache_duration", "b"}, EnvVars: []string{"NTFY_CACHE_DURATION"}, Value: server.DefaultCacheDuration, Usage: "buffer messages for this time to allow `since` requests"}),
|
altsrc.NewDurationFlag(&cli.DurationFlag{Name: "cache-duration", Aliases: []string{"cache_duration", "b"}, EnvVars: []string{"NTFY_CACHE_DURATION"}, Value: server.DefaultCacheDuration, Usage: "buffer messages for this time to allow `since` requests"}),
|
||||||
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "cache-startup-queries", Aliases: []string{"cache_startup_queries"}, EnvVars: []string{"NTFY_CACHE_STARTUP_QUERIES"}, Usage: "queries run when the cache database is initialized"}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "auth-file", Aliases: []string{"auth_file", "H"}, EnvVars: []string{"NTFY_AUTH_FILE"}, Usage: "auth database file used for access control"}),
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "auth-file", Aliases: []string{"auth_file", "H"}, EnvVars: []string{"NTFY_AUTH_FILE"}, Usage: "auth database file used for access control"}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "auth-default-access", Aliases: []string{"auth_default_access", "p"}, EnvVars: []string{"NTFY_AUTH_DEFAULT_ACCESS"}, Value: "read-write", Usage: "default permissions if no matching entries in the auth database are found"}),
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "auth-default-access", Aliases: []string{"auth_default_access", "p"}, EnvVars: []string{"NTFY_AUTH_DEFAULT_ACCESS"}, Value: "read-write", Usage: "default permissions if no matching entries in the auth database are found"}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "attachment-cache-dir", Aliases: []string{"attachment_cache_dir"}, EnvVars: []string{"NTFY_ATTACHMENT_CACHE_DIR"}, Usage: "cache directory for attached files"}),
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "attachment-cache-dir", Aliases: []string{"attachment_cache_dir"}, EnvVars: []string{"NTFY_ATTACHMENT_CACHE_DIR"}, Usage: "cache directory for attached files"}),
|
||||||
|
@ -103,6 +104,7 @@ func execServe(c *cli.Context) error {
|
||||||
firebaseKeyFile := c.String("firebase-key-file")
|
firebaseKeyFile := c.String("firebase-key-file")
|
||||||
cacheFile := c.String("cache-file")
|
cacheFile := c.String("cache-file")
|
||||||
cacheDuration := c.Duration("cache-duration")
|
cacheDuration := c.Duration("cache-duration")
|
||||||
|
cacheStartupQueries := c.String("cache-startup-queries")
|
||||||
authFile := c.String("auth-file")
|
authFile := c.String("auth-file")
|
||||||
authDefaultAccess := c.String("auth-default-access")
|
authDefaultAccess := c.String("auth-default-access")
|
||||||
attachmentCacheDir := c.String("attachment-cache-dir")
|
attachmentCacheDir := c.String("attachment-cache-dir")
|
||||||
|
@ -222,6 +224,7 @@ func execServe(c *cli.Context) error {
|
||||||
conf.FirebaseKeyFile = firebaseKeyFile
|
conf.FirebaseKeyFile = firebaseKeyFile
|
||||||
conf.CacheFile = cacheFile
|
conf.CacheFile = cacheFile
|
||||||
conf.CacheDuration = cacheDuration
|
conf.CacheDuration = cacheDuration
|
||||||
|
conf.CacheStartupQueries = cacheStartupQueries
|
||||||
conf.AuthFile = authFile
|
conf.AuthFile = authFile
|
||||||
conf.AuthDefaultRead = authDefaultRead
|
conf.AuthDefaultRead = authDefaultRead
|
||||||
conf.AuthDefaultWrite = authDefaultWrite
|
conf.AuthDefaultWrite = authDefaultWrite
|
||||||
|
|
|
@ -733,6 +733,21 @@ out [this discussion on Reddit](https://www.reddit.com/r/golang/comments/r9u4ee/
|
||||||
|
|
||||||
Depending on *how you run it*, here are a few limits that are relevant:
|
Depending on *how you run it*, here are a few limits that are relevant:
|
||||||
|
|
||||||
|
### WAL for message cache
|
||||||
|
By default, the [message cache](#message-cache) (defined by `cache-file`) uses the SQLite default settings, which means it
|
||||||
|
syncs to disk on every write. For personal servers, this is perfectly adequate. For larger installations, such as ntfy.sh,
|
||||||
|
the [write-ahead log (WAL)](https://sqlite.org/wal.html) should be enabled, and the sync mode should be adjusted.
|
||||||
|
See [this article](https://phiresky.github.io/blog/2020/sqlite-performance-tuning/) for details.
|
||||||
|
|
||||||
|
Here's how ntfy.sh has been tuned in the `server.yml` file:
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
cache-startup-queries: |
|
||||||
|
pragma journal_mode = WAL;
|
||||||
|
pragma synchronous = normal;
|
||||||
|
pragma temp_store = memory;
|
||||||
|
```
|
||||||
|
|
||||||
### For systemd services
|
### For systemd services
|
||||||
If you're running ntfy in a systemd service (e.g. for .deb/.rpm packages), the main limiting factor is the
|
If you're running ntfy in a systemd service (e.g. for .deb/.rpm packages), the main limiting factor is the
|
||||||
`LimitNOFILE` setting in the systemd unit. The default open files limit for `ntfy.service` is 10,000. You can override it
|
`LimitNOFILE` setting in the systemd unit. The default open files limit for `ntfy.service` is 10,000. You can override it
|
||||||
|
@ -865,6 +880,7 @@ variable before running the `ntfy` command (e.g. `export NTFY_LISTEN_HTTP=:80`).
|
||||||
| `firebase-key-file` | `NTFY_FIREBASE_KEY_FILE` | *filename* | - | If set, also publish messages to a Firebase Cloud Messaging (FCM) topic for your app. This is optional and only required to save battery when using the Android app. See [Firebase (FCM](#firebase-fcm). |
|
| `firebase-key-file` | `NTFY_FIREBASE_KEY_FILE` | *filename* | - | If set, also publish messages to a Firebase Cloud Messaging (FCM) topic for your app. This is optional and only required to save battery when using the Android app. See [Firebase (FCM](#firebase-fcm). |
|
||||||
| `cache-file` | `NTFY_CACHE_FILE` | *filename* | - | If set, messages are cached in a local SQLite database instead of only in-memory. This allows for service restarts without losing messages in support of the since= parameter. See [message cache](#message-cache). |
|
| `cache-file` | `NTFY_CACHE_FILE` | *filename* | - | If set, messages are cached in a local SQLite database instead of only in-memory. This allows for service restarts without losing messages in support of the since= parameter. See [message cache](#message-cache). |
|
||||||
| `cache-duration` | `NTFY_CACHE_DURATION` | *duration* | 12h | Duration for which messages will be buffered before they are deleted. This is required to support the `since=...` and `poll=1` parameter. Set this to `0` to disable the cache entirely. |
|
| `cache-duration` | `NTFY_CACHE_DURATION` | *duration* | 12h | Duration for which messages will be buffered before they are deleted. This is required to support the `since=...` and `poll=1` parameter. Set this to `0` to disable the cache entirely. |
|
||||||
|
| `cache-startup-queries` | `NTFY_CACHE_STARTUP_QUERIES` | *string (SQL queries)* | - | SQL queries to run during database startup; this is useful for tuning and [enabling WAL mode](#wal-for-message-cache) |
|
||||||
| `auth-file` | `NTFY_AUTH_FILE` | *filename* | - | Auth database file used for access control. If set, enables authentication and access control. See [access control](#access-control). |
|
| `auth-file` | `NTFY_AUTH_FILE` | *filename* | - | Auth database file used for access control. If set, enables authentication and access control. See [access control](#access-control). |
|
||||||
| `auth-default-access` | `NTFY_AUTH_DEFAULT_ACCESS` | `read-write`, `read-only`, `write-only`, `deny-all` | `read-write` | Default permissions if no matching entries in the auth database are found. Default is `read-write`. |
|
| `auth-default-access` | `NTFY_AUTH_DEFAULT_ACCESS` | `read-write`, `read-only`, `write-only`, `deny-all` | `read-write` | Default permissions if no matching entries in the auth database are found. Default is `read-write`. |
|
||||||
| `behind-proxy` | `NTFY_BEHIND_PROXY` | *bool* | false | If set, the X-Forwarded-For header is used to determine the visitor IP address instead of the remote address of the connection. |
|
| `behind-proxy` | `NTFY_BEHIND_PROXY` | *bool* | false | If set, the X-Forwarded-For header is used to determine the visitor IP address instead of the remote address of the connection. |
|
||||||
|
@ -929,6 +945,7 @@ OPTIONS:
|
||||||
--behind-proxy, --behind_proxy, -P if set, use X-Forwarded-For header to determine visitor IP address (for rate limiting) (default: false) [$NTFY_BEHIND_PROXY]
|
--behind-proxy, --behind_proxy, -P if set, use X-Forwarded-For header to determine visitor IP address (for rate limiting) (default: false) [$NTFY_BEHIND_PROXY]
|
||||||
--cache-duration since, --cache_duration since, -b since buffer messages for this time to allow since requests (default: 12h0m0s) [$NTFY_CACHE_DURATION]
|
--cache-duration since, --cache_duration since, -b since buffer messages for this time to allow since requests (default: 12h0m0s) [$NTFY_CACHE_DURATION]
|
||||||
--cache-file value, --cache_file value, -C value cache file used for message caching [$NTFY_CACHE_FILE]
|
--cache-file value, --cache_file value, -C value cache file used for message caching [$NTFY_CACHE_FILE]
|
||||||
|
--cache-startup-queries value, --cache_startup_queries value queries run when the cache database is initialized [$NTFY_CACHE_STARTUP_QUERIES]
|
||||||
--cert-file value, --cert_file value, -E value certificate file, if listen-https is set [$NTFY_CERT_FILE]
|
--cert-file value, --cert_file value, -E value certificate file, if listen-https is set [$NTFY_CERT_FILE]
|
||||||
--config value, -c value config file (default: /etc/ntfy/server.yml) [$NTFY_CONFIG_FILE]
|
--config value, -c value config file (default: /etc/ntfy/server.yml) [$NTFY_CONFIG_FILE]
|
||||||
--debug, -d enable debug logging (default: false) [$NTFY_DEBUG]
|
--debug, -d enable debug logging (default: false) [$NTFY_DEBUG]
|
||||||
|
|
|
@ -6,14 +6,9 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
|
||||||
|
|
||||||
## ntfy server v1.27.0 (UNRELEASED)
|
## ntfy server v1.27.0 (UNRELEASED)
|
||||||
|
|
||||||
!!! info
|
|
||||||
The message cache database (typically `cache.db`) now enables the write-ahead log (WAL) in SQLite.
|
|
||||||
WAL mode will create two additional files (`cache.db-wal` and `cache.db-shm`). This is perfectly normal.
|
|
||||||
Do not delete or modify these files, as that can lead to database corruption.
|
|
||||||
|
|
||||||
**Features:**
|
**Features:**
|
||||||
|
|
||||||
* Greatly improve SQLite performance for the message cache by enabling WAL mode (no ticket)
|
* Add `cache-startup-queries` option to allow custom SQLite performance tuning (no ticket)
|
||||||
* ntfy CLI can now [wait for a command or PID](https://ntfy.sh/docs/subscribe/cli/#wait-for-pidcommand) before publishing ([#263](https://github.com/binwiederhier/ntfy/issues/263), thanks to the [original ntfy](https://github.com/dschep/ntfy) for the idea)
|
* ntfy CLI can now [wait for a command or PID](https://ntfy.sh/docs/subscribe/cli/#wait-for-pidcommand) before publishing ([#263](https://github.com/binwiederhier/ntfy/issues/263), thanks to the [original ntfy](https://github.com/dschep/ntfy) for the idea)
|
||||||
* Trace: Log entire HTTP request to simplify debugging (no ticket)
|
* Trace: Log entire HTTP request to simplify debugging (no ticket)
|
||||||
* Allow setting user password via `NTFY_PASSWORD` env variable ([#327](https://github.com/binwiederhier/ntfy/pull/327), thanks to [@Kenix3](https://github.com/Kenix3))
|
* Allow setting user password via `NTFY_PASSWORD` env variable ([#327](https://github.com/binwiederhier/ntfy/pull/327), thanks to [@Kenix3](https://github.com/Kenix3))
|
||||||
|
|
|
@ -57,6 +57,7 @@ type Config struct {
|
||||||
FirebaseKeyFile string
|
FirebaseKeyFile string
|
||||||
CacheFile string
|
CacheFile string
|
||||||
CacheDuration time.Duration
|
CacheDuration time.Duration
|
||||||
|
CacheStartupQueries string
|
||||||
AuthFile string
|
AuthFile string
|
||||||
AuthDefaultRead bool
|
AuthDefaultRead bool
|
||||||
AuthDefaultWrite bool
|
AuthDefaultWrite bool
|
||||||
|
|
|
@ -88,18 +88,6 @@ const (
|
||||||
selectAttachmentsExpiredQuery = `SELECT mid FROM messages WHERE attachment_expires > 0 AND attachment_expires < ?`
|
selectAttachmentsExpiredQuery = `SELECT mid FROM messages WHERE attachment_expires > 0 AND attachment_expires < ?`
|
||||||
)
|
)
|
||||||
|
|
||||||
// Performance & setup queries (see https://phiresky.github.io/blog/2020/sqlite-performance-tuning/)
|
|
||||||
// - Write-ahead log (speeds up reads)
|
|
||||||
// - Only sync on WAL checkpoint
|
|
||||||
// - Temporary indices in memory
|
|
||||||
const (
|
|
||||||
setupQueries = `
|
|
||||||
pragma journal_mode = WAL;
|
|
||||||
pragma synchronous = normal;
|
|
||||||
pragma temp_store = memory;
|
|
||||||
`
|
|
||||||
)
|
|
||||||
|
|
||||||
// Schema management queries
|
// Schema management queries
|
||||||
const (
|
const (
|
||||||
currentSchemaVersion = 7
|
currentSchemaVersion = 7
|
||||||
|
@ -198,12 +186,12 @@ type messageCache struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newSqliteCache creates a SQLite file-backed cache
|
// newSqliteCache creates a SQLite file-backed cache
|
||||||
func newSqliteCache(filename string, nop bool) (*messageCache, error) {
|
func newSqliteCache(filename, startupQueries string, nop bool) (*messageCache, error) {
|
||||||
db, err := sql.Open("sqlite3", filename)
|
db, err := sql.Open("sqlite3", filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := setupCacheDB(db); err != nil {
|
if err := setupCacheDB(db, startupQueries); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &messageCache{
|
return &messageCache{
|
||||||
|
@ -214,13 +202,13 @@ func newSqliteCache(filename string, nop bool) (*messageCache, error) {
|
||||||
|
|
||||||
// newMemCache creates an in-memory cache
|
// newMemCache creates an in-memory cache
|
||||||
func newMemCache() (*messageCache, error) {
|
func newMemCache() (*messageCache, error) {
|
||||||
return newSqliteCache(createMemoryFilename(), false)
|
return newSqliteCache(createMemoryFilename(), "", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newNopCache creates an in-memory cache that discards all messages;
|
// newNopCache creates an in-memory cache that discards all messages;
|
||||||
// it is always empty and can be used if caching is entirely disabled
|
// it is always empty and can be used if caching is entirely disabled
|
||||||
func newNopCache() (*messageCache, error) {
|
func newNopCache() (*messageCache, error) {
|
||||||
return newSqliteCache(createMemoryFilename(), true)
|
return newSqliteCache(createMemoryFilename(), "", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createMemoryFilename creates a unique memory filename to use for the SQLite backend.
|
// createMemoryFilename creates a unique memory filename to use for the SQLite backend.
|
||||||
|
@ -511,10 +499,12 @@ func readMessages(rows *sql.Rows) ([]*message, error) {
|
||||||
return messages, nil
|
return messages, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupCacheDB(db *sql.DB) error {
|
func setupCacheDB(db *sql.DB, startupQueries string) error {
|
||||||
// Performance: WAL mode, only sync on WAL checkpoints
|
// Run startup queries
|
||||||
if _, err := db.Exec(setupQueries); err != nil {
|
if startupQueries != "" {
|
||||||
return err
|
if _, err := db.Exec(startupQueries); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If 'messages' table does not exist, this must be a new database
|
// If 'messages' table does not exist, this must be a new database
|
||||||
|
|
|
@ -378,7 +378,7 @@ func TestSqliteCache_Migration_From0(t *testing.T) {
|
||||||
require.Nil(t, db.Close())
|
require.Nil(t, db.Close())
|
||||||
|
|
||||||
// Create cache to trigger migration
|
// Create cache to trigger migration
|
||||||
c := newSqliteTestCacheFromFile(t, filename)
|
c := newSqliteTestCacheFromFile(t, filename, "")
|
||||||
checkSchemaVersion(t, c.db)
|
checkSchemaVersion(t, c.db)
|
||||||
|
|
||||||
messages, err := c.Messages("mytopic", sinceAllMessages, false)
|
messages, err := c.Messages("mytopic", sinceAllMessages, false)
|
||||||
|
@ -424,7 +424,7 @@ func TestSqliteCache_Migration_From1(t *testing.T) {
|
||||||
require.Nil(t, db.Close())
|
require.Nil(t, db.Close())
|
||||||
|
|
||||||
// Create cache to trigger migration
|
// Create cache to trigger migration
|
||||||
c := newSqliteTestCacheFromFile(t, filename)
|
c := newSqliteTestCacheFromFile(t, filename, "")
|
||||||
checkSchemaVersion(t, c.db)
|
checkSchemaVersion(t, c.db)
|
||||||
|
|
||||||
// Add delayed message
|
// Add delayed message
|
||||||
|
@ -443,6 +443,37 @@ func TestSqliteCache_Migration_From1(t *testing.T) {
|
||||||
require.Equal(t, 11, len(messages))
|
require.Equal(t, 11, len(messages))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSqliteCache_StartupQueries_WAL(t *testing.T) {
|
||||||
|
filename := newSqliteTestCacheFile(t)
|
||||||
|
startupQueries := `pragma journal_mode = WAL;
|
||||||
|
pragma synchronous = normal;
|
||||||
|
pragma temp_store = memory;`
|
||||||
|
db, err := newSqliteCache(filename, startupQueries, false)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Nil(t, db.AddMessage(newDefaultMessage("mytopic", "some message")))
|
||||||
|
require.FileExists(t, filename)
|
||||||
|
require.FileExists(t, filename+"-wal")
|
||||||
|
require.FileExists(t, filename+"-shm")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSqliteCache_StartupQueries_None(t *testing.T) {
|
||||||
|
filename := newSqliteTestCacheFile(t)
|
||||||
|
startupQueries := ""
|
||||||
|
db, err := newSqliteCache(filename, startupQueries, false)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Nil(t, db.AddMessage(newDefaultMessage("mytopic", "some message")))
|
||||||
|
require.FileExists(t, filename)
|
||||||
|
require.NoFileExists(t, filename+"-wal")
|
||||||
|
require.NoFileExists(t, filename+"-shm")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSqliteCache_StartupQueries_Fail(t *testing.T) {
|
||||||
|
filename := newSqliteTestCacheFile(t)
|
||||||
|
startupQueries := `xx error`
|
||||||
|
_, err := newSqliteCache(filename, startupQueries, false)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func checkSchemaVersion(t *testing.T, db *sql.DB) {
|
func checkSchemaVersion(t *testing.T, db *sql.DB) {
|
||||||
rows, err := db.Query(`SELECT version FROM schemaVersion`)
|
rows, err := db.Query(`SELECT version FROM schemaVersion`)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
@ -468,7 +499,7 @@ func TestMemCache_NopCache(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSqliteTestCache(t *testing.T) *messageCache {
|
func newSqliteTestCache(t *testing.T) *messageCache {
|
||||||
c, err := newSqliteCache(newSqliteTestCacheFile(t), false)
|
c, err := newSqliteCache(newSqliteTestCacheFile(t), "", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -479,8 +510,8 @@ func newSqliteTestCacheFile(t *testing.T) string {
|
||||||
return filepath.Join(t.TempDir(), "cache.db")
|
return filepath.Join(t.TempDir(), "cache.db")
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSqliteTestCacheFromFile(t *testing.T, filename string) *messageCache {
|
func newSqliteTestCacheFromFile(t *testing.T, filename, startupQueries string) *messageCache {
|
||||||
c, err := newSqliteCache(filename, false)
|
c, err := newSqliteCache(filename, startupQueries, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,7 +158,7 @@ func createMessageCache(conf *Config) (*messageCache, error) {
|
||||||
if conf.CacheDuration == 0 {
|
if conf.CacheDuration == 0 {
|
||||||
return newNopCache()
|
return newNopCache()
|
||||||
} else if conf.CacheFile != "" {
|
} else if conf.CacheFile != "" {
|
||||||
return newSqliteCache(conf.CacheFile, false)
|
return newSqliteCache(conf.CacheFile, conf.CacheStartupQueries, false)
|
||||||
}
|
}
|
||||||
return newMemCache()
|
return newMemCache()
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,14 +37,22 @@
|
||||||
#
|
#
|
||||||
# firebase-key-file: <filename>
|
# firebase-key-file: <filename>
|
||||||
|
|
||||||
# If set, messages are cached in a local SQLite database instead of only in-memory. This
|
# If "cache-file" is set, messages are cached in a local SQLite database instead of only in-memory.
|
||||||
# allows for service restarts without losing messages in support of the since= parameter.
|
# This allows for service restarts without losing messages in support of the since= parameter.
|
||||||
#
|
#
|
||||||
# The "cache-duration" parameter defines the duration for which messages will be buffered
|
# The "cache-duration" parameter defines the duration for which messages will be buffered
|
||||||
# before they are deleted. This is required to support the "since=..." and "poll=1" parameter.
|
# before they are deleted. This is required to support the "since=..." and "poll=1" parameter.
|
||||||
# To disable the cache entirely (on-disk/in-memory), set "cache-duration" to 0.
|
# To disable the cache entirely (on-disk/in-memory), set "cache-duration" to 0.
|
||||||
# The cache file is created automatically, provided that the correct permissions are set.
|
# The cache file is created automatically, provided that the correct permissions are set.
|
||||||
#
|
#
|
||||||
|
# The "cache-startup-queries" parameter allows you to run commands when the database is initialized,
|
||||||
|
# e.g. to enable WAL mode (see https://phiresky.github.io/blog/2020/sqlite-performance-tuning/)).
|
||||||
|
# Example:
|
||||||
|
# cache-startup-queries: |
|
||||||
|
# pragma journal_mode = WAL;
|
||||||
|
# pragma synchronous = normal;
|
||||||
|
# pragma temp_store = memory;
|
||||||
|
#
|
||||||
# Debian/RPM package users:
|
# Debian/RPM package users:
|
||||||
# Use /var/cache/ntfy/cache.db as cache file to avoid permission issues. The package
|
# Use /var/cache/ntfy/cache.db as cache file to avoid permission issues. The package
|
||||||
# creates this folder for you.
|
# creates this folder for you.
|
||||||
|
@ -55,6 +63,7 @@
|
||||||
#
|
#
|
||||||
# cache-file: <filename>
|
# cache-file: <filename>
|
||||||
# cache-duration: "12h"
|
# cache-duration: "12h"
|
||||||
|
# cache-startup-queries:
|
||||||
|
|
||||||
# If set, access to the ntfy server and API can be controlled on a granular level using
|
# If set, access to the ntfy server and API can be controlled on a granular level using
|
||||||
# the 'ntfy user' and 'ntfy access' commands. See the --help pages for details, or check the docs.
|
# the 'ntfy user' and 'ntfy access' commands. See the --help pages for details, or check the docs.
|
||||||
|
|
|
@ -1415,6 +1415,7 @@ func TestServer_PublishWhileUpdatingStatsWithLotsOfMessages(t *testing.T) {
|
||||||
count := 50000
|
count := 50000
|
||||||
c := newTestConfig(t)
|
c := newTestConfig(t)
|
||||||
c.TotalTopicLimit = 50001
|
c.TotalTopicLimit = 50001
|
||||||
|
c.CacheStartupQueries = "pragma journal_mode = WAL; pragma synchronous = normal; pragma temp_store = memory;"
|
||||||
s := newTestServer(t, c)
|
s := newTestServer(t, c)
|
||||||
|
|
||||||
// Add lots of messages
|
// Add lots of messages
|
||||||
|
|
Loading…
Reference in a new issue