-
Notifications
You must be signed in to change notification settings - Fork 7
Storage Backends
Where PlayerData, PDSections and account data physically live is an admin decision, made in
plugins/EverNifeCore/storage.yml. Plugins advise a default; the admin has the final say. The storage
engine is EveryDatabase - EverNifeCore configures
and drives it.
The file is generated with a full set of commented, disabled example backends on first boot, so most
admins only flip enabled and fill in credentials.
storage-backends:
groupedfile: # the entry key is a FREE unique id (name it anything)
enabled: true
type: groupedfile
path: plugins/EverNifeCore/StorageData/groupedfile
format: yaml # yaml | json (file backends only)
mysql:
enabled: false
type: sql
url: "jdbc:mysql://localhost:3306/minecraft"
user: root
pass: ""
pool:
minIdle: 2
maxSize: 10
connectTimeoutSeconds: 5
idleTimeoutSeconds: 30
default-backend: groupedfile # used when nothing more specific is configuredThe entry key is a free unique id, not the type. Declaring several backends of the same type is how
you point different data at different servers - e.g. a mysql_economy and a mysql_points, both
type: sql, on two databases.
Every backend's runtime dependency (JDBC drivers, the Mongo driver) is downloaded up front at boot,
so switching type here never hits a missing dependency.
type: maps to one of these. Full capability matrix and per-backend setup live in the EveryDatabase
wiki - the short version:
type: |
Engine | Good for | EveryDatabase page |
|---|---|---|---|
groupedfile |
key-major files (one file per key, all its collections) | the per-player model; the factory default | Grouped Files |
localfile |
one file per entity | tiny deploys, human-readable | Local Files |
sql |
MySQL / MariaDB (HikariCP pool, JSON column) | production, shared DB | MySQL & MariaDB |
postgresql |
PostgreSQL (HikariCP pool, JSON column) | production, shared DB | PostgreSQL |
h2 |
H2 embedded / file / tcp | embedded / dev | H2 |
mongo |
MongoDB (native BSON documents) | document workloads | MongoDB |
memory |
in-memory only, ephemeral | tests / throwaway servers | In-Memory |
format: yaml | json applies to the file backends (groupedfile / localfile) only; yaml is the
default and json is always written pretty/indented. For choosing among them, see
Choosing a Backend.
⚠️ memorydata is lost on shutdown - the parser warns when you enable it. Use it only for tests.
Once backends are declared, three blocks route the actual data. Each may name a storage-backend-id;
naming a missing or disabled backend is a fail-fast error at boot with a message that points at the
offending key.
playerdata:
storage-backend-id: groupedfile # must be an enabled backend id
collection: evernifecore_playerdata
load-mode: ALL # ALL (default) | RECENT
recent-days: 60 # RECENT: how far back to eager-load
login-timeout-seconds: 15 # how long a login may wait on storage before being denied
slow-login-report-seconds: 3 # print a per-section breakdown of a login slower than this
default-idle-grace-seconds: 3600 # how long a section lingers after its player goes offline
orphan-reaper:
enabled: false # opt-in; sweeps section rows whose base is gone
interval-minutes: 360load-mode: ALL loads every player at startup; RECENT eager-loads only players seen in the last
recent-days and lazy-loads the rest on demand.
default-idle-grace-seconds (default 3600) is how long a player's section stays in memory after that
player goes offline - the server-wide value for every section whose developer did not ask for a
specific one. Lower it to reclaim memory sooner (0 releases as soon as the player leaves), raise it
so a reconnect does not have to read from the database again.
login-timeout-seconds (default 15) bounds the whole login resolution - the player row plus every
section that loads at login - not one read. When it is exceeded the login is denied rather than served
with data missing.
slow-login-report-seconds (default 3, 0 disables) is when the framework stops being silent about a
slow login and prints what it was waiting on: per section, the time, the plugin that declared it, that
plugin's author and the backend it lives on. Reach for it before blaming EverNifeCore for a slow join -
the breakdown usually names a specific plugin or a specific database. It prints on a timeout too, with
the sections still loading marked as pending.
pdsections:
myplugin: # your plugin's name, lowercase
jobs: # the section id declared at registration
storage-backend-id: mysql
# collection: pd_myplugin_jobs
# lifecycle: LAZY # LAZY | ONLINE | RESIDENT | PRELOADED
# idle-grace-seconds: 600 # overrides the developer AND the global default
# cache: { policy: TTL, ttlSeconds: 300 } # ALWAYS | TTLGenerated automatically the first time each PDSection registers, keyed by the section's id (not
its class name), so renaming the class never orphans the entry. The admin then edits
storage-backend-id, collection, lifecycle, idle-grace-seconds and cache freely - keys are
matched case-insensitively.
lifecycle overrides the plugin's declaration. It exists for one situation: the slow-login report
named a section you would rather not pay for at every join, and LAZY takes it off the login path -
the first read then happens whenever something actually asks for it. An unknown value fails the boot
with a message listing the valid ones, rather than silently falling back.
An admin cache: sets freshness only; when a cell enters and leaves memory is the developer's
lifecycle. NOCACHE is refused for a PDSection - the cached cell is the
instance the flush pipeline persists, so bypassing the cache would lose every write.
An entry no registered section claims is reported with a WARNING shortly after boot: either the plugin that owned it is gone, or its section id changed and the rows of the old collection are no longer reachable. Nothing is moved or deleted for you.
accountsections:
myplugin:
achievements:
collection: acs_myplugin_achievements
# cache: { policy: TTL, ttlSeconds: 300 }The same idea for account-wide sections: generated on first registration, keyed by the section id, matched case-insensitively, and an unclaimed entry gets the same orphan WARNING.
Two knobs only, and the difference from pdsections is deliberate. There is no per-section
backend: the whole account family lives on the backend set under multi-platform-accounts, which is
what lets a link absorb an account's rows without coordinating writes across backends. There is no
per-section lifecycle or idle-grace-seconds either - the family has one cycle, driven by whether
any member of the account is online.
cache.policy: TTL is the one worth knowing about. An account row is written by every instance of
the network, so on a backend with no change feed the login refresh would otherwise be the only moment
another server's write becomes visible here. A TTL bounds that staleness without needing Redis.
NOCACHE is refused, for the same reason as a PDSection.
multi-platform-accounts:
enabled: false
storage-backend-id: "" # empty = default-backend
# idle-grace-seconds: 3600 # absent = follow playerdata.default-idle-grace-secondsThe backend hosting the whole account family (registry + account-wide sections). On a real network it must be a database shared by every instance. See Accounts.
idle-grace-seconds is how long an account row stays in memory once no member of that account is
online here - after the last one quits, and equally for a row that was read for an account nobody was
playing on (an offline lookup, an aggregate). One value covers the whole family; what a single section
can override lives in accountsections.
When several instances share one database, each instance caches rows independently - a write on
instance A must invalidate the same entry on instance B. That is what multi-server-cache-sync does.
multi-server-cache-sync:
enabled: true # default; HARMLESS no-op on a single server
transport: auto # auto | redis | native
redis:
enabled: false
host: localhost
port: 6379Transport resolution:
-
auto(default) - Redis if theredisblock is enabled, else the backends' native change feed when every manager's backend has one, else a silent no-op. -
redis- force the Redis pub/sub transport. -
native- use only the backends' native feed.
Which backends have a native feed: only MongoDB and PostgreSQL. sql (MySQL/MariaDB) and the
file backends do not - for those, cross-instance coherence exists only through Redis.
📌 Current state of the Redis transport. The Redis transport lives in the optional
everydatabase-manager-jedismodule, which is not bundled with EverNifeCore on this version. If you configure aredisblock without that runtime on the classpath, EverNifeCore logs a warning at boot and cache-sync via Redis is a no-op. The native-feed path (Mongo/PostgreSQL) is wired and works today. On a single server the whole block is a harmless no-op regardless.
For the deeper model, see Cross-Process Cache Sync.
logging:
level: warn # warn (default) | info | debug | traceControls the EveryDatabase storage log verbosity (bind reports, conflict/flush lines).
A backend declared enabled: true that does not answer at boot stops the server. This is on by
default, and it is deliberate: booting without the database that holds player data would let every
player join with empty data, and the first save would overwrite the real rows with those empty ones.
Every unreachable backend is reported at once, not one per restart. The report names each one with
its type, the target it was pointed at (with the password redacted), which storage.yml keys
route data to it, and the root cause of the failure; the full stack traces are printed above the
banner.
# plugins/EverNifeCore/config.yml
Settings:
Storage:
STOP_SERVER_IF_STORAGE_IS_UNREACHABLE: true # the defaultTwo things worth knowing before you flip it:
- It only ever stops a BOOT. A failed reload never stops anything - the previously loaded storage is still live and serving, so the report says so and nothing is lost. Fix the config or the database and reload again.
-
falsedoes not make the server work without a database. EverNifeCore stays disabled either way, every plugin that depends on it fails, and whatever data does get written diverges from what the database holds. The only real ways out of a boot failure are starting the database, fixing the url/user/pass, or settingenabled: falseon a backend you no longer use.
| Subcommand | Permission node | What it does |
|---|---|---|
status |
evernifecore.command.storage.status |
Prints the routing (which backend/collection each entity persists on) plus health counters - quit-flush retry backlog, adopted conflicts, last failed write. |
transfer <plugin:section> <backend> |
evernifecore.command.storage.transfer |
Migrates one PDSection's collection to another backend at runtime (see below). |
You are not locked in: a single PDSection's collection can be migrated to another backend at runtime
without editing files by hand - see Legacy Data Migration for
/ecstorage transfer. The underlying builder is EveryDatabase's
Moving Data Between Backends.
- PlayerData & PDSections - what gets stored and how cache policies map on.
- Inline Backends for Plugins - a plugin routing its OWN data elsewhere.
- Accounts - the shared account backend.
- Legacy Data Migration - first-boot import and runtime transfer.
-
Command Framework - how
/ecstorageand other builtin commands are declared.
EverNifeCore · Home · made by Petrus Pradella
Getting Started
Commands & Text
Player Data & Storage
- PlayerData & PDSections
- Accounts
- Storage Backends
- Inline Backends for Plugins
- Legacy Data Migration
- Cooldowns
Config & Minecraft Systems
- Configuration
- Scheduler & Threading
- Items & NBT
- GUI Framework
- Integrations
- Economy
- Version Compatibility
Architecture & Reference