FerretDB: default to polling-only on all platforms; OpLog is opt-in (#6503).

FerretDB v1 can tail an OpLog, but on the SQLite backend Meteor's tailable+awaitData OpLog tail keeps FerretDB CPU pinned (reporters saw ~190-390% even idle; a 2-core box maxed out) and a struggling tail also surfaces as 'oplog catching up took too long', stalling board and login-page loading. Despite the FerretDB-side mitigations (16 MiB capped oplog, 500ms awaitData poll, SQLite pragmas, CPU self-regulation) the peg persisted on real deployments, and the polling-only path reliably drops CPU to ~10%. So make POLLING the default on every launcher (snap wekan-control, bundle start-wekan.sh, docker wekan-entrypoint.sh + docker-compose.yml, windows start-wekan.bat, sandstorm start.js): WEKAN_FERRETDB_OPLOG now defaults false, docker-compose reactivity is 'polling' with MONGO_OPLOG_URL commented out. The OpLog machinery still exists and is opt-in via WEKAN_FERRETDB_OPLOG=true. The snap exposes it as a setting: 'snap set wekan wekan-ferretdb-oplog=true|false' (registered in snap-src/bin/config, default false). The calmer polling throttle (#6467/#6468) and SQLite pragma tuning keep poll-and-diff cheap.

Tests: tests/ferretdbOplog.test.cjs updated to assert the polling-only default everywhere, the commented-out docker-compose MONGO_OPLOG_URL, the opt-in OpLog machinery still present, and the new wekan-ferretdb-oplog snap setting (default false).

Thanks to xet7 !
This commit is contained in:
Lauri Ojansivu committed 2026-07-22 10:35:52 +03:00
1 parent 2cde7bf25c
commit b15d89dd0c
8 files changed
+107 -81

No files matched your search

+17 -12
View File
@@ -142,13 +142,15 @@ services:
# https://github.com/meteor/meteor/blob/release-3.5/v3-docs/docs/performance/change-streams-observer-driver.md#choosing-the-reactivity-driver-order
# Keep oplog/polling fallback available for deployments that cannot use Change Streams:
# https://github.com/wekan/wekan/issues/6307#issuecomment-4299349231
# wekan/wekan#6480/#6481: FerretDB v1 now ships an OpLog (the ferretdb service
# above runs with --repl-set-name=rs0), so prefer OpLog tailing and fall back
# to polling — this is the main fix for FerretDB's high CPU on busy boards.
# FerretDB has no MongoDB change streams. To force polling only, set both to
# `polling` and comment out MONGO_OPLOG_URL below.
- DEFAULT_METEOR_REACTIVITY_ORDER=oplog,polling
- METEOR_REACTIVITY_ORDER=oplog,polling
# wekan/wekan#6503/#6480/#6481: FerretDB v1 CAN tail an OpLog (the ferretdb
# service above runs with --repl-set-name=rs0), but on the SQLite backend the
# tailable+awaitData OpLog tail keeps FerretDB CPU pinned (~190–390% even idle)
# and a struggling tail shows as "oplog catching up took too long" and stalls
# loading, so the DEFAULT here is now POLLING ONLY. FerretDB has no MongoDB
# change streams. To opt INTO OpLog tailing, set both to `oplog,polling` and
# uncomment MONGO_OPLOG_URL in the wekan service below.
- DEFAULT_METEOR_REACTIVITY_ORDER=polling
- METEOR_REACTIVITY_ORDER=polling
# uws does not work at s390x. sockjs works.
#- DDP_TRANSPORT=uws
- DDP_TRANSPORT=sockjs
@@ -215,11 +217,14 @@ services:
# - Latency from 50ms to 2000ms
# Must configure MongoDB replica set first
# See: https://blog.meteor.com/tuning-meteor-mongo-livedata-for-scalability-13fe9deb8908
# FerretDB v1 emulates the OpLog via --repl-set-name=rs0 (set on the ferretdb
# service above). This enables real-time DDP updates and stops the poll-and-diff
# CPU burn (wekan/wekan#6480/#6481). To force polling only, comment this out and
# set METEOR_REACTIVITY_ORDER=polling above.
- MONGO_OPLOG_URL=mongodb://ferretdb:27017/local?replicaSet=rs0
# FerretDB v1 CAN emulate the OpLog via --repl-set-name=rs0 (set on the
# ferretdb service above), but on the SQLite backend the tail keeps FerretDB
# CPU pinned and stalls loading (wekan/wekan#6503), so it is DISABLED by
# default here. Merely SETTING MONGO_OPLOG_URL makes Meteor start an OpLog
# tail regardless of the reactivity order (#6498), so it is commented out.
# To opt INTO OpLog tailing, uncomment the next line AND set
# METEOR_REACTIVITY_ORDER=oplog,polling above.
#- MONGO_OPLOG_URL=mongodb://ferretdb:27017/local?replicaSet=rs0
#---------------------------------------------------------------
# ==== OPTIONAL: KADIRA PERFORMANCE MONITORING FOR METEOR ====
# https://github.com/edemaine/kadira-compose
+5 -5
View File
@@ -23,11 +23,11 @@ set "FERRETDB_SQLITE_DIR=%FILES%\db"
if not defined PORT set "PORT=8080"
if not defined ROOT_URL set "ROOT_URL=http://localhost:%PORT%"
if not defined MONGO_URL set "MONGO_URL=mongodb://127.0.0.1:27017/wekan"
REM #6480/#6481: FerretDB v1 now ships an OpLog (started below with
REM --repl-set-name), so by default WeKan's Meteor TAILS the OpLog instead of
REM poll-and-diff (fixes high FerretDB CPU on busy boards). Set
REM WEKAN_FERRETDB_OPLOG=false to force the old polling-only behaviour.
if not defined WEKAN_FERRETDB_OPLOG set "WEKAN_FERRETDB_OPLOG=true"
REM #6503/#6480/#6481: FerretDB v1 CAN tail an OpLog (started below with
REM --repl-set-name), but on the SQLite backend the tail keeps FerretDB CPU
REM pinned (~190-390% even idle) and stalls loading, so the DEFAULT is now
REM POLLING ONLY. Opt into OpLog tailing with WEKAN_FERRETDB_OPLOG=true.
if not defined WEKAN_FERRETDB_OPLOG set "WEKAN_FERRETDB_OPLOG=false"
if not defined WEKAN_FERRETDB_REPL_SET set "WEKAN_FERRETDB_REPL_SET=rs0"
set "FERRET_REPL_ARG="
if /I "%WEKAN_FERRETDB_OPLOG%"=="true" (
+10 -10
View File
@@ -69,16 +69,16 @@ FERRET_PID=""
stop_ferret() { [ -n "$FERRET_PID" ] && kill "$FERRET_PID" 2>/dev/null || true; }
trap 'stop_ferret; exit 0' INT TERM
# #6480/#6481: FerretDB v1 now ships an OpLog (auto-created capped local.oplog.rs
# + replica-set hello handshake — the ferretdb below is launched with
# --repl-set-name). By default WeKan's Meteor TAILS the OpLog instead of
# poll-and-diff, the main fix for FerretDB's high CPU on busy boards. Set
# WEKAN_FERRETDB_OPLOG=false to force the old polling-only behaviour.
# The polling settings remain the FALLBACK for queries Meteor cannot observe via
# the OpLog. #6467/#6468: Meteor's defaults (re-poll 50 ms after any write, at
# least every 10 s) hammer the database; calmer defaults re-poll at most every
# 2 s / 30 s. Overridable by exporting different values before running this script.
WEKAN_FERRETDB_OPLOG="${WEKAN_FERRETDB_OPLOG:-true}"
# #6503/#6480/#6481: FerretDB v1 CAN tail an OpLog (auto-created capped
# local.oplog.rs + replica-set hello handshake), but on the SQLite backend the
# tailable+awaitData OpLog tail keeps FerretDB CPU pinned (reporters saw ~190–390%
# even idle) and a struggling tail shows as "oplog catching up took too long" and
# stalls loading, so the DEFAULT is now POLLING ONLY. Opt into OpLog tailing with
# WEKAN_FERRETDB_OPLOG=true if you specifically want it.
# The polling settings apply either way. #6467/#6468: Meteor's defaults (re-poll
# 50 ms after any write, at least every 10 s) hammer the database; calmer defaults
# re-poll at most every 2 s / 30 s. Overridable by exporting values before running.
WEKAN_FERRETDB_OPLOG="${WEKAN_FERRETDB_OPLOG:-false}"
WEKAN_FERRETDB_REPL_SET="${WEKAN_FERRETDB_REPL_SET:-rs0}"
FERRET_REPL_ARG=""
if [ "$want_ferret" = true ]; then
+7 -6
View File
@@ -47,12 +47,13 @@ if [ "$want_ferret" = true ]; then
fi
export MONGO_URL="${MONGO_URL:-mongodb://$FERRETDB_LISTEN_ADDR/wekan}"
mkdir -p "$FERRETDB_SQLITE_DIR"
# #6480/#6481: FerretDB v1 now ships an OpLog (auto-created capped
# local.oplog.rs + replica-set hello handshake), launched below with
# --repl-set-name. By default WeKan's Meteor TAILS the OpLog instead of
# poll-and-diff — the main fix for FerretDB's high CPU on busy boards. Set
# WEKAN_FERRETDB_OPLOG=false to force the old polling-only behaviour.
WEKAN_FERRETDB_OPLOG="${WEKAN_FERRETDB_OPLOG:-true}"
# #6503/#6480/#6481: FerretDB v1 CAN tail an OpLog (auto-created capped
# local.oplog.rs + replica-set hello handshake), but on the SQLite backend the
# tailable+awaitData tail keeps FerretDB CPU pinned (~190–390% even idle) and a
# struggling tail shows as "oplog catching up took too long" and stalls loading,
# so the DEFAULT is now POLLING ONLY. Opt into OpLog tailing with
# WEKAN_FERRETDB_OPLOG=true if you specifically want it.
WEKAN_FERRETDB_OPLOG="${WEKAN_FERRETDB_OPLOG:-false}"
REPL_SET_NAME="${WEKAN_FERRETDB_REPL_SET:-rs0}"
if [ "$WEKAN_FERRETDB_OPLOG" = "true" ]; then
export MONGO_OPLOG_URL="${MONGO_OPLOG_URL:-mongodb://$FERRETDB_LISTEN_ADDR/local?replicaSet=$REPL_SET_NAME}"
+8 -7
View File
@@ -54,13 +54,14 @@ const DB_PORT = '4001'; // FerretDB stead
const SRC_PORT = '4003'; // mongod 3.0 (migration source)
const NISCU_PORT = '4004'; // niscud (niscu→3.0 source)
// #6480/#6481: FerretDB v1 ships an OpLog (auto-created capped local.oplog.rs +
// replica-set hello handshake). Run the STEADY-STATE FerretDB with a replica-set
// name and point WeKan at it so Meteor tails the OpLog instead of poll-and-diff.
// Polling stays the fallback (METEOR_REACTIVITY_ORDER=oplog,polling → OpLog only
// when tailing works). WEKAN_FERRETDB_OPLOG=false forces polling only. The
// transient migration-target FerretDB above stays OpLog-free (no --repl-set-name).
const FERRET_OPLOG = process.env.WEKAN_FERRETDB_OPLOG !== 'false';
// #6503/#6480/#6481: FerretDB v1 CAN tail an OpLog (auto-created capped
// local.oplog.rs + replica-set hello handshake), but on the SQLite backend the
// tailable+awaitData tail keeps FerretDB CPU pinned (~190–390% even idle) and a
// struggling tail shows as "oplog catching up took too long" and stalls loading,
// so the DEFAULT is now POLLING ONLY. Opt into OpLog tailing with
// WEKAN_FERRETDB_OPLOG=true. The transient migration-target FerretDB above stays
// OpLog-free (no --repl-set-name) regardless.
const FERRET_OPLOG = process.env.WEKAN_FERRETDB_OPLOG === 'true';
const REPL_SET_NAME = process.env.WEKAN_FERRETDB_REPL_SET || 'rs0';
function ensureDirs() {
+5 -1
View File
@@ -3,7 +3,7 @@
# All supported keys are defined here together with descriptions and default values
# list of supported keys
keys="DEBUG DATABASE DDP_TRANSPORT METEOR_REACTIVITY_ORDER MONGO_LOG_DESTINATION MONGO_URL MONGO_OPLOG_URL MONGODB_BIND_UNIX_SOCKET MONGODB_BIND_IP MONGODB_PORT MAIL_URL MAIL_FROM MAIL_SERVICE MAIL_SERVICE_USER MAIL_SERVICE_PASSWORD ROOT_URL PORT DISABLE_MONGODB CADDY_ENABLED CADDY_BIND_PORT WITH_API RICHER_CARD_COMMENT_EDITOR CARDS_LOADING CARD_OPENED_WEBHOOK_ENABLED ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM ATTACHMENTS_UPLOAD_MIME_TYPES ATTACHMENTS_UPLOAD_MAX_SIZE AVATARS_UPLOAD_EXTERNAL_PROGRAM AVATARS_UPLOAD_MIME_TYPES AVATARS_UPLOAD_MAX_SIZE MAX_IMAGE_PIXEL IMAGE_COMPRESS_RATIO BIGEVENTS_PATTERN NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE NOTIFY_DUE_DAYS_BEFORE_AND_AFTER NOTIFY_DUE_AT_HOUR_OF_DAY DEFAULT_BOARD_ID EMAIL_NOTIFICATION_TIMEOUT CORS CORS_ALLOW_HEADERS CORS_EXPOSE_HEADERS MATOMO_ADDRESS MATOMO_SITE_ID MATOMO_DO_NOT_TRACK MATOMO_WITH_USERNAME METRICS_ALLOWED_IP_ADDRESSES BROWSER_POLICY_ENABLED TRUSTED_URL WEBHOOKS_ATTRIBUTES OAUTH2_ENABLED OIDC_REDIRECTION_ENABLED OAUTH2_CA_CERT OAUTH2_LOGIN_STYLE OAUTH2_CLIENT_ID OAUTH2_SECRET OAUTH2_SERVER_URL OAUTH2_AUTH_ENDPOINT OAUTH2_USERINFO_ENDPOINT OAUTH2_TOKEN_ENDPOINT OAUTH2_LOGOUT_ENDPOINT OAUTH2_ID_MAP OAUTH2_USERNAME_MAP OAUTH2_FULLNAME_MAP OAUTH2_ID_TOKEN_WHITELIST_FIELDS OAUTH2_EMAIL_MAP OAUTH2_REQUEST_PERMISSIONS OAUTH2_ADFS_ENABLED OAUTH2_B2C_ENABLED OAUTH2_MERGE_EXISTING_USERS LDAP_ENABLE LDAP_PORT LDAP_HOST LDAP_AD_SIMPLE_AUTH LDAP_BASEDN LDAP_LOGIN_FALLBACK LDAP_RECONNECT LDAP_TIMEOUT LDAP_IDLE_TIMEOUT LDAP_CONNECT_TIMEOUT LDAP_AUTHENTIFICATION LDAP_AUTHENTIFICATION_USERDN LDAP_AUTHENTIFICATION_PASSWORD LDAP_LOG_ENABLED LDAP_BACKGROUND_SYNC LDAP_BACKGROUND_SYNC_INTERVAL LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS LDAP_ENCRYPTION LDAP_CA_CERT LDAP_REJECT_UNAUTHORIZED LDAP_USER_AUTHENTICATION LDAP_USER_AUTHENTICATION_FIELD LDAP_USER_SEARCH_FILTER LDAP_USER_SEARCH_SCOPE LDAP_USER_SEARCH_FIELD LDAP_SEARCH_PAGE_SIZE LDAP_SEARCH_SIZE_LIMIT LDAP_GROUP_FILTER_ENABLE LDAP_GROUP_FILTER_OBJECTCLASS LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT LDAP_GROUP_FILTER_GROUP_NAME LDAP_UNIQUE_IDENTIFIER_FIELD LDAP_UTF8_NAMES_SLUGIFY LDAP_USERNAME_FIELD LDAP_FULLNAME_FIELD LDAP_MERGE_EXISTING_USERS LDAP_SYNC_USER_DATA LDAP_SYNC_USER_DATA_FIELDMAP LDAP_SYNC_GROUP_ROLES LDAP_DEFAULT_DOMAIN LDAP_EMAIL_MATCH_ENABLE LDAP_EMAIL_MATCH_REQUIRE LDAP_EMAIL_MATCH_VERIFIED LDAP_EMAIL_FIELD LDAP_SYNC_ADMIN_STATUS LDAP_SYNC_ADMIN_GROUPS HEADER_LOGIN_ID HEADER_LOGIN_FIRSTNAME HEADER_LOGIN_LASTNAME HEADER_LOGIN_EMAIL HEADER_LOGIN_TRUSTED_IPS HEADER_LOGIN_TRUSTED_PROXIES LOGOUT_WITH_TIMER LOGOUT_IN LOGOUT_ON_HOURS LOGOUT_ON_MINUTES DEFAULT_AUTHENTICATION_METHOD PASSWORD_LOGIN_ENABLED CAS_ENABLED CAS_BASE_URL CAS_LOGIN_URL CAS_VALIDATE_URL SAML_ENABLED SAML_PROVIDER SAML_ENTRYPOINT SAML_ISSUER SAML_CERT SAML_IDPSLO_REDIRECTURL SAML_PRIVATE_KEYFILE SAML_PUBLIC_CERTFILE SAML_IDENTIFIER_FORMAT SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE SAML_ATTRIBUTES ORACLE_OIM_ENABLED RESULTS_PER_PAGE WAIT_SPINNER NODE_OPTIONS LDAP_AUTHENTIFICATION_PASSWORD_FILE OAUTH2_SECRET_FILE MAIL_SERVICE_PASSWORD_FILE MONGO_PASSWORD_FILE S3 S3_SECRET_FILE OAUTH2_AUTO_REGISTRATION OAUTH2_ADMIN_GROUPS NOTIFY_ON_ASSIGN LDAP_BACKGROUND_SYNC_DISABLE_NONEXISTANT_USERS LDAP_SYNC_ORGANIZATIONS LDAP_SYNC_ORGANIZATIONS_GROUPS LDAP_SYNC_TEAMS LDAP_SYNC_TEAMS_GROUPS"
keys="DEBUG DATABASE WEKAN_FERRETDB_OPLOG DDP_TRANSPORT METEOR_REACTIVITY_ORDER MONGO_LOG_DESTINATION MONGO_URL MONGO_OPLOG_URL MONGODB_BIND_UNIX_SOCKET MONGODB_BIND_IP MONGODB_PORT MAIL_URL MAIL_FROM MAIL_SERVICE MAIL_SERVICE_USER MAIL_SERVICE_PASSWORD ROOT_URL PORT DISABLE_MONGODB CADDY_ENABLED CADDY_BIND_PORT WITH_API RICHER_CARD_COMMENT_EDITOR CARDS_LOADING CARD_OPENED_WEBHOOK_ENABLED ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM ATTACHMENTS_UPLOAD_MIME_TYPES ATTACHMENTS_UPLOAD_MAX_SIZE AVATARS_UPLOAD_EXTERNAL_PROGRAM AVATARS_UPLOAD_MIME_TYPES AVATARS_UPLOAD_MAX_SIZE MAX_IMAGE_PIXEL IMAGE_COMPRESS_RATIO BIGEVENTS_PATTERN NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE NOTIFY_DUE_DAYS_BEFORE_AND_AFTER NOTIFY_DUE_AT_HOUR_OF_DAY DEFAULT_BOARD_ID EMAIL_NOTIFICATION_TIMEOUT CORS CORS_ALLOW_HEADERS CORS_EXPOSE_HEADERS MATOMO_ADDRESS MATOMO_SITE_ID MATOMO_DO_NOT_TRACK MATOMO_WITH_USERNAME METRICS_ALLOWED_IP_ADDRESSES BROWSER_POLICY_ENABLED TRUSTED_URL WEBHOOKS_ATTRIBUTES OAUTH2_ENABLED OIDC_REDIRECTION_ENABLED OAUTH2_CA_CERT OAUTH2_LOGIN_STYLE OAUTH2_CLIENT_ID OAUTH2_SECRET OAUTH2_SERVER_URL OAUTH2_AUTH_ENDPOINT OAUTH2_USERINFO_ENDPOINT OAUTH2_TOKEN_ENDPOINT OAUTH2_LOGOUT_ENDPOINT OAUTH2_ID_MAP OAUTH2_USERNAME_MAP OAUTH2_FULLNAME_MAP OAUTH2_ID_TOKEN_WHITELIST_FIELDS OAUTH2_EMAIL_MAP OAUTH2_REQUEST_PERMISSIONS OAUTH2_ADFS_ENABLED OAUTH2_B2C_ENABLED OAUTH2_MERGE_EXISTING_USERS LDAP_ENABLE LDAP_PORT LDAP_HOST LDAP_AD_SIMPLE_AUTH LDAP_BASEDN LDAP_LOGIN_FALLBACK LDAP_RECONNECT LDAP_TIMEOUT LDAP_IDLE_TIMEOUT LDAP_CONNECT_TIMEOUT LDAP_AUTHENTIFICATION LDAP_AUTHENTIFICATION_USERDN LDAP_AUTHENTIFICATION_PASSWORD LDAP_LOG_ENABLED LDAP_BACKGROUND_SYNC LDAP_BACKGROUND_SYNC_INTERVAL LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS LDAP_ENCRYPTION LDAP_CA_CERT LDAP_REJECT_UNAUTHORIZED LDAP_USER_AUTHENTICATION LDAP_USER_AUTHENTICATION_FIELD LDAP_USER_SEARCH_FILTER LDAP_USER_SEARCH_SCOPE LDAP_USER_SEARCH_FIELD LDAP_SEARCH_PAGE_SIZE LDAP_SEARCH_SIZE_LIMIT LDAP_GROUP_FILTER_ENABLE LDAP_GROUP_FILTER_OBJECTCLASS LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT LDAP_GROUP_FILTER_GROUP_NAME LDAP_UNIQUE_IDENTIFIER_FIELD LDAP_UTF8_NAMES_SLUGIFY LDAP_USERNAME_FIELD LDAP_FULLNAME_FIELD LDAP_MERGE_EXISTING_USERS LDAP_SYNC_USER_DATA LDAP_SYNC_USER_DATA_FIELDMAP LDAP_SYNC_GROUP_ROLES LDAP_DEFAULT_DOMAIN LDAP_EMAIL_MATCH_ENABLE LDAP_EMAIL_MATCH_REQUIRE LDAP_EMAIL_MATCH_VERIFIED LDAP_EMAIL_FIELD LDAP_SYNC_ADMIN_STATUS LDAP_SYNC_ADMIN_GROUPS HEADER_LOGIN_ID HEADER_LOGIN_FIRSTNAME HEADER_LOGIN_LASTNAME HEADER_LOGIN_EMAIL HEADER_LOGIN_TRUSTED_IPS HEADER_LOGIN_TRUSTED_PROXIES LOGOUT_WITH_TIMER LOGOUT_IN LOGOUT_ON_HOURS LOGOUT_ON_MINUTES DEFAULT_AUTHENTICATION_METHOD PASSWORD_LOGIN_ENABLED CAS_ENABLED CAS_BASE_URL CAS_LOGIN_URL CAS_VALIDATE_URL SAML_ENABLED SAML_PROVIDER SAML_ENTRYPOINT SAML_ISSUER SAML_CERT SAML_IDPSLO_REDIRECTURL SAML_PRIVATE_KEYFILE SAML_PUBLIC_CERTFILE SAML_IDENTIFIER_FORMAT SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE SAML_ATTRIBUTES ORACLE_OIM_ENABLED RESULTS_PER_PAGE WAIT_SPINNER NODE_OPTIONS LDAP_AUTHENTIFICATION_PASSWORD_FILE OAUTH2_SECRET_FILE MAIL_SERVICE_PASSWORD_FILE MONGO_PASSWORD_FILE S3 S3_SECRET_FILE OAUTH2_AUTO_REGISTRATION OAUTH2_ADMIN_GROUPS NOTIFY_ON_ASSIGN LDAP_BACKGROUND_SYNC_DISABLE_NONEXISTANT_USERS LDAP_SYNC_ORGANIZATIONS LDAP_SYNC_ORGANIZATIONS_GROUPS LDAP_SYNC_TEAMS LDAP_SYNC_TEAMS_GROUPS"
#-------------------- CHANGE STREAMS --------------------
# https://forums.meteor.com/t/meteor-3-5-beta-change-streams-performance-improvements/64461#change-streams-setup-3
@@ -114,6 +114,10 @@ DESCRIPTION_DATABASE="Database backend: 'mongodb' (default) or 'ferretdb' (Ferre
DEFAULT_DATABASE="mongodb"
KEY_DATABASE="database"
DESCRIPTION_WEKAN_FERRETDB_OPLOG="FerretDB reactivity mode. 'false' (default) = polling only, which keeps FerretDB CPU low; 'true' = tail the FerretDB OpLog (real-time), but on the SQLite backend the tail can pin FerretDB CPU and stall loading (wekan/wekan#6503). Only applies when database=ferretdb. Force polling: snap set ${SNAP_NAME} wekan-ferretdb-oplog=false ; opt into OpLog: snap set ${SNAP_NAME} wekan-ferretdb-oplog=true"
DEFAULT_WEKAN_FERRETDB_OPLOG="false"
KEY_WEKAN_FERRETDB_OPLOG="wekan-ferretdb-oplog"
DESCRIPTION_CADDY_ENABLED="Enable caddy service (caddy - Every Site on HTTPS) personal license for non-commercial use only, see https://caddyserver.com/products/licenses . Set to 'true' to enable caddy\n\t\tcaddy settings are handled through $SNAP_COMMON/Caddyfile"
DEFAULT_CADDY_ENABLED="false"
KEY_CADDY_ENABLED="caddy-enabled"
+14 -12
View File
@@ -86,20 +86,22 @@ else
fi
fi
# #6480/#6481: FerretDB v1 now ships an OpLog (auto-created capped local.oplog.rs
# + replica-set hello handshake — see bin/ferretdb-control, which launches with
# --repl-set-name). So by default WeKan's Meteor TAILS the OpLog instead of
# poll-and-diff, which is the main fix for FerretDB's high CPU on busy boards.
# Enabled by default; set WEKAN_FERRETDB_OPLOG=false (snap set) to force the old
# polling-only behaviour if OpLog tailing ever misbehaves.
# #6503/#6480/#6481: FerretDB v1 CAN tail an OpLog (auto-created capped
# local.oplog.rs + replica-set hello handshake — see bin/ferretdb-control), but
# on the SQLite backend Meteor's tailable+awaitData OpLog tail keeps FerretDB CPU
# pinned (reporters saw ~190–390% even idle; deleting local.sqlite* dropped it to
# ~10%), and a struggling tail also shows up as "oplog catching up took too long"
# and stalls board/login loading. So the DEFAULT is now POLLING ONLY. With the
# calmer polling defaults (#6467/#6468) and the SQLite pragma tuning, poll-and-diff
# is cheap. Opt into OpLog tailing with WEKAN_FERRETDB_OPLOG=true (snap set) if you
# specifically want it and have verified it stays calm on your data.
#
# The polling settings below still apply as the FALLBACK for the queries Meteor
# cannot observe through the OpLog (and when OpLog is disabled). #6467/#6468: the
# Meteor defaults (re-poll 50 ms after any write, at least every 10 s) hammered
# FerretDB; calmer defaults re-poll at most every 2 s / 30 s. Own writes still
# show instantly (local cache); only OTHER users' changes wait up to ~2 s.
# The polling settings below apply either way (they are also the OpLog fallback).
# #6467/#6468: the Meteor defaults (re-poll 50 ms after any write, at least every
# 10 s) hammered FerretDB; calmer defaults re-poll at most every 2 s / 30 s. Own
# writes still show instantly (local cache); only OTHER users' changes wait ~2 s.
if [ "ferretdb" = "${DATABASE}" ]; then
WEKAN_FERRETDB_OPLOG="${WEKAN_FERRETDB_OPLOG:-true}"
WEKAN_FERRETDB_OPLOG="${WEKAN_FERRETDB_OPLOG:-false}"
if [ "true" = "${WEKAN_FERRETDB_OPLOG}" ]; then
if [ -z "${MONGO_OPLOG_URL}" ]; then
REPL_SET_NAME="${WEKAN_FERRETDB_REPL_SET:-rs0}"
+41 -28
View File
@@ -1,15 +1,17 @@
'use strict';
// Guards for the FerretDB OpLog + data-reduction changes (#6480/#6481).
// Guards for the FerretDB OpLog + data-reduction changes (#6503/#6480/#6481).
//
// With FerretDB there is no MongoDB oplog, so Meteor observed every live query by
// poll-and-diff, pinning FerretDB CPU at 100-390% on busy boards. FerretDB v1 now
// ships an OpLog (auto-created capped local.oplog.rs + replica-set hello
// handshake), so every FerretDB launch path must (a) start ferretdb with
// --repl-set-name so the OpLog exists and the server advertises the replica set,
// and (b) point WeKan at it via MONGO_OPLOG_URL so Meteor TAILS the OpLog instead
// of polling — with a WEKAN_FERRETDB_OPLOG kill-switch back to polling. Also
// asserts the oversized activity page was trimmed.
// FerretDB v1 CAN tail an OpLog (auto-created capped local.oplog.rs + replica-set
// hello handshake), but on the SQLite backend the tailable+awaitData tail pins
// FerretDB CPU (~190-390% even idle) and a struggling tail stalls loading
// ("oplog catching up took too long"), so #6503 makes POLLING the DEFAULT on every
// platform. The OpLog machinery still EXISTS and is opt-in via
// WEKAN_FERRETDB_OPLOG=true. These guards assert: (a) the default is polling only
// (WEKAN_FERRETDB_OPLOG defaults false; docker-compose reactivity is `polling` and
// MONGO_OPLOG_URL is commented out); (b) when opted in, ferretdb still launches
// with --repl-set-name and WeKan is pointed at MONGO_OPLOG_URL with polling as the
// fallback; and (c) the oversized activity page was trimmed.
//
// Run: node tests/ferretdbOplog.test.cjs
@@ -58,33 +60,42 @@ test('windows start-wekan.bat passes a repl-set arg to ferretdb.exe', () => {
assert.ok(/ferretdb\.exe.*%FERRET_REPL_ARG%/.test(src), 'passes it on the ferretdb.exe line');
});
// ── WeKan points at the OpLog by default (with a kill-switch) ────────────────
test('snap wekan-control sets MONGO_OPLOG_URL for FerretDB, gated by WEKAN_FERRETDB_OPLOG', () => {
// ── #6503: FerretDB is POLLING ONLY by default; OpLog is opt-in ──────────────
test('snap wekan-control: polling-only default for FerretDB, OpLog opt-in', () => {
const src = read('snap-src/bin/wekan-control');
assert.ok(/WEKAN_FERRETDB_OPLOG:-true/.test(src), 'OpLog on by default');
assert.ok(/export MONGO_OPLOG_URL="mongodb:\/\/.*\/local\?replicaSet=/.test(src), 'sets oplog url to local?replicaSet');
assert.ok(/"true" = "\$\{WEKAN_FERRETDB_OPLOG\}"/.test(src), 'kill-switch check present');
assert.ok(/WEKAN_FERRETDB_OPLOG:-false/.test(src), 'OpLog OFF by default (polling only)');
assert.ok(/export MONGO_OPLOG_URL="mongodb:\/\/.*\/local\?replicaSet=/.test(src), 'still sets oplog url when opted in');
assert.ok(/"true" = "\$\{WEKAN_FERRETDB_OPLOG\}"/.test(src), 'opt-in check present');
});
test('bundle start-wekan.sh sets MONGO_OPLOG_URL by default, kill-switch WEKAN_FERRETDB_OPLOG', () => {
test('snap config registers wekan-ferretdb-oplog (default false, snap set toggle)', () => {
const src = read('snap-src/bin/config');
assert.ok(/\bWEKAN_FERRETDB_OPLOG\b/.test(src.match(/keys="[^"]*"/)[0]), 'listed in the settings keys');
assert.ok(/DEFAULT_WEKAN_FERRETDB_OPLOG="false"/.test(src), 'defaults to false (polling)');
assert.ok(/KEY_WEKAN_FERRETDB_OPLOG="wekan-ferretdb-oplog"/.test(src), 'snap set key is wekan-ferretdb-oplog');
});
test('bundle start-wekan.sh: polling-only default, OpLog opt-in via WEKAN_FERRETDB_OPLOG=true', () => {
const src = read('releases/ferretdb/start-wekan.sh');
assert.ok(/WEKAN_FERRETDB_OPLOG="\$\{WEKAN_FERRETDB_OPLOG:-true\}"/.test(src), 'default true');
assert.ok(/WEKAN_FERRETDB_OPLOG="\$\{WEKAN_FERRETDB_OPLOG:-false\}"/.test(src), 'default false');
assert.ok(/export MONGO_OPLOG_URL="\$\{MONGO_OPLOG_URL:-mongodb:\/\/\$FERRETDB_LISTEN_ADDR\/local\?replicaSet=/.test(src));
});
test('docker wekan-entrypoint.sh sets MONGO_OPLOG_URL, kill-switch present', () => {
test('docker wekan-entrypoint.sh: polling-only default, OpLog opt-in', () => {
const src = read('releases/ferretdb/wekan-entrypoint.sh');
assert.ok(/WEKAN_FERRETDB_OPLOG="\$\{WEKAN_FERRETDB_OPLOG:-true\}"/.test(src), 'default true');
assert.ok(/WEKAN_FERRETDB_OPLOG="\$\{WEKAN_FERRETDB_OPLOG:-false\}"/.test(src), 'default false');
assert.ok(/export MONGO_OPLOG_URL="\$\{MONGO_OPLOG_URL:-mongodb:\/\/\$FERRETDB_LISTEN_ADDR\/local\?replicaSet=/.test(src));
});
test('docker-compose prefers oplog reactivity + sets MONGO_OPLOG_URL (uncommented)', () => {
test('docker-compose: polling-only reactivity by default + MONGO_OPLOG_URL commented out', () => {
const src = read('docker-compose.yml');
assert.ok(/METEOR_REACTIVITY_ORDER=oplog,polling/.test(src), 'reactivity prefers oplog then polling');
// the MONGO_OPLOG_URL line must be active (not commented out)
assert.ok(/\n\s*- MONGO_OPLOG_URL=mongodb:\/\/ferretdb:27017\/local\?replicaSet=rs0/.test(src),
'MONGO_OPLOG_URL must be an active env line, not commented');
assert.ok(/METEOR_REACTIVITY_ORDER=polling/.test(src), 'reactivity is polling by default');
// the MONGO_OPLOG_URL line must NOT be active (commented out for polling-only)
assert.ok(!/\n\s*- MONGO_OPLOG_URL=mongodb:\/\/ferretdb:27017/.test(src),
'MONGO_OPLOG_URL must NOT be an active env line');
assert.ok(/#\s*-\s*MONGO_OPLOG_URL=mongodb:\/\/ferretdb:27017\/local\?replicaSet=rs0/.test(src),
'the opt-in MONGO_OPLOG_URL line is present but commented');
});
test('windows start-wekan.bat sets MONGO_OPLOG_URL under the kill-switch', () => {
test('windows start-wekan.bat: polling-only default, OpLog opt-in', () => {
const src = read('releases/ferretdb/start-wekan.bat');
assert.ok(/WEKAN_FERRETDB_OPLOG%"=="true"/.test(src), 'gated on the kill-switch');
assert.ok(/set "WEKAN_FERRETDB_OPLOG=false"/.test(src), 'default false');
assert.ok(/WEKAN_FERRETDB_OPLOG%"=="true"/.test(src), 'opt-in branch gated on =true');
assert.ok(/MONGO_OPLOG_URL=mongodb:\/\/127\.0\.0\.1:27017\/local\?replicaSet=/.test(src));
});
@@ -107,7 +118,7 @@ test('#6498: polling mode UNSETS MONGO_OPLOG_URL so Meteor never tails the OpLog
// OpLog only when tailing actually works, else polling — so a broken/absent
// OpLog never stops WeKan starting. Every FerretDB launch path must keep polling
// last (like the MongoDB "changeStreams,oplog,polling" default).
test('every FerretDB launch path keeps polling as the reactivity fallback (oplog,polling)', () => {
test('every FerretDB launch path always has polling (as the default or the OpLog fallback)', () => {
for (const f of [
'snap-src/bin/wekan-control',
'releases/ferretdb/start-wekan.sh',
@@ -117,8 +128,10 @@ test('every FerretDB launch path keeps polling as the reactivity fallback (oplog
'sandstorm-src/start.js',
]) {
const src = read(f);
assert.ok(/METEOR_REACTIVITY_ORDER[^\n]*oplog,polling/.test(src),
`${f} must set METEOR_REACTIVITY_ORDER to oplog,polling (polling fallback)`);
// Default is now polling-only (METEOR_REACTIVITY_ORDER=polling); the opt-in
// OpLog branch keeps polling last (oplog,polling). Both contain "polling".
assert.ok(/METEOR_REACTIVITY_ORDER[^\n]*polling/.test(src),
`${f} must keep polling available in METEOR_REACTIVITY_ORDER`);
}
});