Table of Contents

Class SceneServerPlacementPolicy

Namespace
FishMMO.Server.Implementation.World.SceneServer
Assembly
FishMMO.Server.dll

Decides how much new scene-load work a scene server takes on, given what it is already hosting.

public static class SceneServerPlacementPolicy
Inheritance
SceneServerPlacementPolicy
Inherited Members

Remarks

Why this exists. Scene placement is a PULL: the world server enqueues a pending scene row and every scene server races to claim it through ISceneService.DequeueAsync, which is a FOR UPDATE SKIP LOCKED take-the-oldest. Nothing in that path considers load, so whichever server's pulse timer happens to fire first wins — and it wins every race in that window, including the ones it should lose. A server that came up first, or pulses slightly out of phase with its peers, accumulates scenes while an idle peer takes none.

How this fixes it without cluster coordination. Each server scales its own per-pulse dequeue budget down as its load rises. A lightly loaded server keeps claiming several scenes per pulse; a heavily loaded one claims one; a full one claims none and leaves the row for somebody with room. No server needs to see its peers, so there is no extra query on the pulse path and no way for a stale view of the cluster to make the decision wrong.

What it deliberately is not. This is statistical balancing, not exact least-loaded placement. Exact placement needs every server to see every peer's live load — a per-pulse cluster query, and a decision made on data that is already out of date by the time the race is run. At a handful of scene servers hosting scenes that live for minutes to hours, the difference does not survive first contact with the stale-scene reaper, which retires idle scenes and lets placement re-level on its own.

Pure and static so the whole decision is unit tested without a database, a pulse, or a cluster — the same shape as ObserverStreamingPolicy.

Properties

HardCapCharacters

Characters hosted at or above which a server claims no new scenes.

public static int HardCapCharacters { get; set; }

Property Value

int

Remarks

Scene count alone is a poor proxy for load: one busy town and one empty dungeon are both "a scene". Population is what actually costs CPU and bandwidth, so both are measured and the more loaded of the two decides.

HardCapScenes

Scenes hosted at or above which a server claims nothing and leaves rows for its peers.

public static int HardCapScenes { get; set; }

Property Value

int

Remarks

A real capacity limit, not a balancing hint. Refusing here is correct: the row stays queued and the next server with room takes it. It cannot starve the cluster unless every server is genuinely full, which is a provisioning problem rather than a placement one — and the alternative (taking it anyway) turns that into an overloaded server instead of a visible queue.

SoftCapCharacters

Characters hosted at or below which a server keeps its full per-pulse dequeue budget.

public static int SoftCapCharacters { get; set; }

Property Value

int

SoftCapScenes

Scenes hosted at or below which a server keeps its full per-pulse dequeue budget.

public static int SoftCapScenes { get; set; }

Property Value

int

Methods

ApplySetting(string, int)

Applies a named configuration value. Unknown keys are ignored.

public static bool ApplySetting(string key, int value)

Parameters

key string

Configuration key.

value int

Value to apply.

Returns

bool

True when the key was recognised and applied.

Remarks

Mirrors ObserverStreamingPolicy.ApplySetting so the scene server can push its configuration in without this type knowing what a configuration file is.

ResolveDequeueBudget(int, int, int)

How many pending scenes this server should try to claim on this pulse.

public static int ResolveDequeueBudget(int loadedScenes, int characterCount, int maxScenesPerPulse)

Parameters

loadedScenes int

Scenes this server currently hosts.

characterCount int

Characters this server currently hosts.

maxScenesPerPulse int

The configured ceiling, used when the server is idle.

Returns

int

A budget in [0, maxScenesPerPulse].