Files
scadalink-design/AkkaDotNet/21-Configuration.md
Joseph Doherty de636b908b Add Akka.NET reference documentation
Notes and documentation covering actors, remoting, clustering, persistence,
streams, serialization, hosting, testing, and best practices for the Akka.NET
framework used throughout the ScadaLink system.
2026-03-16 09:08:17 -04:00

7.8 KiB

21 — HOCON Configuration

Overview

HOCON (Human-Optimized Config Object Notation) is Akka.NET's native configuration format. It supports hierarchical keys, includes, substitutions, fallback chains, and default values. While Akka.Hosting provides code-first configuration for most modules, HOCON remains necessary for fine-grained settings that Hosting APIs don't yet cover, and for understanding default behaviors.

In the SCADA system, the recommended approach is Akka.Hosting as the primary configuration mechanism, with HOCON used only for settings not exposed by Hosting's fluent API. Site-specific values (hostnames, ports, device lists) come from appsettings.json via Microsoft.Extensions.Configuration.

When to Use

  • Fine-grained settings not yet covered by Akka.Hosting APIs (e.g., specific dispatcher tuning, transport failure detector thresholds, serialization bindings)
  • Understanding and overriding Akka.NET default configurations
  • Legacy configuration that predates Akka.Hosting adoption

When Not to Use

  • As the primary configuration mechanism in new .NET 10 projects — use Akka.Hosting instead
  • For site-specific deployment values (hostnames, connection strings) — use appsettings.json and IConfiguration
  • Do not duplicate settings in both HOCON and Akka.Hosting — pick one source of truth per setting

Design Decisions for the SCADA System

Configuration Layering

The SCADA system uses a three-layer configuration approach:

  1. Akka.Hosting (code-first): Remoting, Cluster, Singleton, Persistence, Distributed Data — all major module configuration
  2. HOCON (embedded in code): Fine-grained tuning that Hosting APIs don't expose — SBR details, dispatcher settings, serialization bindings
  3. appsettings.json: Site-specific values — node hostname, seed node addresses, device configuration path, database connection strings
akkaBuilder
    // Layer 1: Hosting APIs
    .WithRemoting(options => { ... })
    .WithClustering(new ClusterOptions { ... })

    // Layer 2: HOCON for fine-grained settings
    .AddHocon(ConfigurationFactory.ParseString(@"
        akka.cluster.split-brain-resolver {
            active-strategy = keep-oldest
            keep-oldest.down-if-alone = on
            stable-after = 15s
        }
        akka.actor.serialization-bindings {
            ""ScadaSystem.Messages.IClusterMessage, ScadaSystem"" = hyperion
        }
    "), HoconAddMode.Prepend);

HOCON Precedence

When combining HOCON with Hosting, use HoconAddMode.Prepend to ensure your HOCON overrides defaults, or HoconAddMode.Append to provide fallback values. Hosting API settings are applied after HOCON, so they take highest precedence.

Effective precedence (highest to lowest):

  1. Akka.Hosting API calls
  2. HOCON with Prepend
  3. Akka.NET internal defaults (reference.conf)

Avoiding HOCON Files on Disk

Do not deploy akka.hocon or akka.conf files alongside the SCADA application. All HOCON should be embedded in code (via ConfigurationFactory.ParseString) or generated from appsettings.json. This avoids configuration drift between nodes — both nodes use the same compiled code with identical embedded HOCON.

Site-specific differences (hostname, seed nodes) come from appsettings.json, which is deployed per-node.

Common Patterns

Reading HOCON Defaults for Reference

Akka.NET modules ship with reference.conf files that contain all default values. These are embedded in the NuGet packages. To view defaults for any module:

var defaults = ConfigurationFactory.Default();
var remoteDefaults = defaults.GetConfig("akka.remote");
Console.WriteLine(remoteDefaults.ToString());

This is useful when tuning specific settings — you can see the default value before overriding it.

HOCON Substitutions

HOCON supports variable substitution, useful for sharing values:

scada {
  cluster-role = "scada-node"
}

akka.cluster {
  roles = [${scada.cluster-role}]
}

akka.cluster.singleton {
  role = ${scada.cluster-role}
}

Environment Variable Overrides

HOCON can read environment variables, useful for container deployments:

akka.remote.dot-netty.tcp {
  hostname = ${?SCADA_HOSTNAME}  # Override from env var if set
  port = 4053
}

The ? prefix makes the substitution optional — if the env var isn't set, the setting is omitted (and the default applies).

Complete HOCON Reference for the SCADA System

This is the full HOCON block for settings that Hosting APIs don't cover:

akka {
  # SBR fine-tuning
  cluster {
    split-brain-resolver {
      active-strategy = keep-oldest
      keep-oldest.down-if-alone = on
      stable-after = 15s
    }

    failure-detector {
      heartbeat-interval = 1s
      threshold = 8.0
      acceptable-heartbeat-pause = 10s
    }
  }

  # Serialization bindings
  actor {
    serializers {
      hyperion = "Akka.Serialization.HyperionSerializer, Akka.Serialization.Hyperion"
    }
    serialization-bindings {
      "ScadaSystem.Messages.IClusterMessage, ScadaSystem" = hyperion
    }
  }

  # Remote transport tuning
  remote {
    dot-netty.tcp {
      maximum-frame-size = 256000b
    }
    transport-failure-detector {
      heartbeat-interval = 4s
      acceptable-heartbeat-pause = 20s
    }
  }

  # Distributed Data tuning
  cluster.distributed-data {
    gossip-interval = 2s
    notify-subscribers-interval = 500ms
    durable {
      keys = ["pending-commands"]
      lmdb {
        dir = "C:\\ProgramData\\SCADA\\ddata"
        map-size = 104857600
      }
    }
  }

  # IO tuning for custom protocol connections
  io.tcp {
    max-channels = 1024
  }
}

Anti-Patterns

HOCON Files with Different Content Per Node

If akka.conf files are deployed separately to each node and they drift out of sync, cluster behavior becomes unpredictable. Both nodes must have identical Akka configuration except for node-specific values (hostname). Use embedded HOCON + appsettings.json to enforce this.

Overriding Hosting Settings with HOCON

If you configure Remoting via WithRemoting() and also set akka.remote.dot-netty.tcp.port in HOCON, the effective value depends on precedence order. This is confusing and error-prone. Pick one mechanism per setting.

Deep HOCON Nesting Without Comments

HOCON is powerful but can become unreadable. Always comment non-obvious settings, especially timeouts and thresholds that affect failover behavior.

Ignoring reference.conf Defaults

Akka.NET's defaults are generally sensible. Do not override every setting "just to be explicit." Override only settings where the default doesn't match your SCADA requirements (e.g., SBR strategy, failure detector tuning).

Configuration Guidance

appsettings.json Structure

{
  "ScadaSite": {
    "NodeHostname": "nodeA.scada.local",
    "SeedNodes": [
      "akka.tcp://scada-system@nodeA.scada.local:4053",
      "akka.tcp://scada-system@nodeB.scada.local:4053"
    ],
    "RemotePort": 4053,
    "ManagementPort": 8558,
    "DeviceConfigPath": "C:\\ProgramData\\SCADA\\devices.json",
    "PersistenceDbPath": "C:\\ProgramData\\SCADA\\persistence.db",
    "DistributedDataPath": "C:\\ProgramData\\SCADA\\ddata"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Akka": "Warning"
    }
  }
}

File Paths

All SCADA data files (persistence DB, distributed data, device config) should live under a dedicated directory (C:\ProgramData\SCADA\) with appropriate ACLs. Both nodes should use the same path structure for consistency, even though the files are node-local.

References