unicorn-binance-depth-cache-cluster Change Log¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
Discussions about unicorn-binance-depth-cache-cluster releases!
How to upgrade to the latest version!
0.9.1.dev (development stage/unreleased/unstable)¶
Added¶
Helm chart: optional N DCN pods per node — the optimal distribution is one DCN per CPU core. The default is unchanged (a
DaemonSet, exactly 1 DCN pod per node). Settingdcn.coresPerNode > 1switches the DCN to aDeploymentthat auto-detects the count of schedulable, non-control-plane nodes via a clusterlookupand rendersreplicas = coresPerNode × nodeCount, spread evenly across nodes withtopologySpreadConstraints(maxSkew: 1).Helm values
dcn.coresPerNode,dcn.nodeCount(override auto-detection),dcn.replicas(force Deployment mode + override the whole calculation),dcn.nodeSelector(pin DCN to a node pool) anddcn.resources(set requests == limits for Guaranteed QoS / per-core CPU pinning).Helm values
restapi.perNode,restapi.nodeCount,restapi.replicas,restapi.nodeSelectorandrestapi.resources— symmetric to thedcn.*block, for tuning the RESTAPI topology.
Changed¶
Helm chart: RESTAPI is now a
DaemonSetby default (exactly 1 RESTAPI pod per node, auto-scales with the cluster) instead of a hard-codedStatefulSetwithreplicas: 3+ per-hostpodAntiAffinity. The old layout broke on clusters with < 3 nodes (pods stuckPending) and wasted capacity on clusters with > 3 nodes. Settingrestapi.perNode > 1(or an explicitrestapi.replicas) switches RESTAPI to aDeploymentwithreplicas = perNode × nodeCount, spread evenly viatopologySpreadConstraints. ThereplicaCount.restapivalue is removed.admin/k8s/ubdcc-restapi.yamlupdated to match (plainDaemonSet).
0.9.1¶
Fixed¶
get_k8s_runtime_information()inpackages/ubdcc-shared-modules/ubdcc_shared_modules/App.py: the in-cluster Kubernetes API client lost its Bearer token whenUBDCC_K8S_VERIFY_SSL=falsewas set, causing every API request to hit the API server assystem:anonymousand fall back to dev mode (random pod identity +localhost:42080mgmt routing). The previous flow loaded the in-cluster config, then created a copy viaConfiguration.get_default_copy(), mutatedverify_ssl=Falseon the copy, and pushed it back viaConfiguration.set_default(); in the pinned kubernetes-client version this swap dropped theapi_key['authorization']Bearer header. New flow: pre-create akubernetes.client.Configuration(), setverify_ssl=Falseon it before callingkubernetes.config.load_incluster_config(client_configuration=cfg)so the lib writes the token directly into the already-modified Configuration, then instantiateCoreV1Api/CustomObjectsApiwith an explicitApiClient(configuration=cfg)— no more global default-swap. Verified out-of-band with a raw urllib request from inside the pod (HTTP 200, real pod JSON) on a Vultr Kubernetes cluster where the previous code path returned 403.
0.9.0¶
Added¶
UBDCC_K8S_VERIFY_SSLenvironment variable (default"true") for the three services (ubdcc-mgmt,ubdcc-restapi,ubdcc-dcn). Set to"false"to disable TLS verification for the in-cluster Kubernetes API calls made byget_k8s_runtime_information()/get_k8s_nodes()inpackages/ubdcc-shared-modules/ubdcc_shared_modules/App.py. Intended as an opt-out for clusters that ship Kubernetes API server certificates rejected by strict X.509 verification (e.g. missing AuthorityKeyIdentifier,keyCertSignset on a non-CA cert — observed on some managed clusters). The flag is wired through the Helm chart (dev/helm/ubdcc/values.yaml:k8s.verifySsl) and the raw Kubernetes manifests inadmin/k8s/. When set tofalse, aWARNINGis logged on every service start and urllib3’sInsecureRequestWarningis silenced.
0.8.1¶
Hotfix release — only the ubdcc CLI meta-package was bumped; the
service packages (ubdcc-mgmt, ubdcc-restapi, ubdcc-dcn,
ubdcc-shared-modules) stay at 0.8.0 because they are unchanged. The
CLI pins them with ==0.8.0.
Fixed¶
ubdcc starton Windows:packages/ubdcc/ubdcc/cli.pynow embedscwdandlog_levelviarepr()({cwd!r}) into thepython -ccommand used to spawn mgmt / restapi / dcn services. The previous'{cwd}'f-string interpolation emitted raw backslashes into the child Python source, so a working directory likec:\ubdcc-testwas parsed with\uas a Unicode escape (\ubdcc-> U+BDCC), pointing the child at a non-existent path and crashingos.chdir()withFileNotFoundError [WinError 2]. Linux/macOS were unaffected because POSIX paths have no backslashes.
0.8.0¶
Changed¶
Default service log level lowered from
DEBUGtoERRORinpackages/ubdcc-shared-modules/ubdcc_shared_modules/App.py. Cuts disk usage in production drastically —~/.unicorn-binance-suite/logs/ubdcc-*.logno longer fills up with per-tick DEBUG noise. Affects every service (mgmt, restapi, dcn) regardless of how it’s launched (ubdcc start, K8s, manual).
Added¶
packages/ubdcc: shipsubdcc-dashboard >= 0.2.0as a runtime dependency (setup.py,requirements.txt,pyproject.toml).pip install ubdccnow also installs the browser-based UBDCC Dashboard — launch it withubdcc-dashboard start. README updated accordingly.ubdcc start --log-level DEBUG|INFO|WARNING|ERROR|CRITICALto pick a log level for the spawned mgmt / restapi / dcn services without having to edit code. Propagated as alog_levelconstructor kwarg throughMgmt,RestApi,DepthCacheNode→ServiceBase→App.
0.7.0¶
Changed¶
Breaking: dropped the redundant
ubdcc_prefix from the three user-facing credential endpoints to align with the rest of the REST API (/create_depthcache,/get_asks, …):/ubdcc_add_credentials→/add_credentials/ubdcc_remove_credentials→/remove_credentials/ubdcc_get_credentials_list→/get_credentials_listRenamed on both the public restapi and the internal mgmt service. Internal cluster endpoints (/ubdcc_assign_credentials,/ubdcc_node_*,/ubdcc_update_depthcache_distribution, …) keep their prefix — it signals “not a public API”. The matching UBLDC cluster client methods were renamed too (requires UBLDC ≥ 2.14.0): usecluster.add_credentials()/cluster.remove_credentials()/cluster.get_credentials_list(). Theubdcc credentials add/list/removeCLI continues to work unchanged (it now hits the new endpoints internally).
packages/ubdcc-dcn: bumpedunicorn-binance-local-depth-cacheminimum to>=2.14.0acrosssetup.py,requirements.txtandpyproject.toml— the renamed cluster client methods live there.
Fixed¶
AccountGroups.py:binance.com-margin-testnetandbinance.com-isolated_margin-testnetare now mapped to thebinance.com-testnetaccount group. Without this, credential assignment (get_account_group()) returnedNonefor testnet-margin DCNs, so no API keys could be dispatched to them. Spot-testnet and margin-testnet share the sametestnet.binance.visioncredentials, so they belong in the same account group.
0.6.0¶
Added¶
CLI
status: new “DC restarts” section lists every depth cache whose WebSocket stream has been restarted at least once, sorted by restart count descending, with a human-readable “last restart” timestamp (e.g.45s ago,2h ago). Hidden when no DC has restarted.CLI
--help: the top-level help now lists thecredentialssubcommands (add,remove,list) in the epilog alongside the interactive shell commands, so users no longer need to runubdcc credentials --helpto discover them.
Changed¶
Credential assignment is now event-driven and self-healing. Previously, a DCN pulled its API key exactly once (on the first depth cache it created for a given exchange) and cached the result — including a sticky
Noneafter any transient failure, leavingassigned_dcns: []forever across cluster restarts. Now:Database.rebalance_account_group()redistributes every active DCN round-robin across the credentials available for that account group. It runs onadd_credentials()/delete_credentials()and as part ofrevise()when the DCN population changes.DCNs cache only the
credential_idper account group as a comparison reference. On every main-loop tick they ask mgmt for their current assignment and, when the id differs, hot-swap the UBRA in every affectedBinanceLocalDepthCacheManagervia the newset_credentials()public API (requires UBLDC ≥ 2.13.0). WebSocket streams keep running; new credentials take effect from the next REST call (snapshot / resync).
packages/ubdcc-dcn: bumpedunicorn-binance-local-depth-cacheminimum to>=2.13.0acrosssetup.py,requirements.txtandpyproject.tomlfor theset_credentials()method.
Fixed¶
mgmt
/ubdcc_update_depthcache_distribution:last_restart_timewas parsed from the query string but never forwarded toDatabase.update_depthcache_distribution()— so theRESTARTScounter per DC distribution entry stayed at 0 forever, no matter how often a UBLDC stream restarted. Also: the query param arrived as a string but the DB expected a float, so the subsequent!=compare against the stored value would have been string-vs-float. Both are fixed now: cast tofloatwith an explicit error response (#1024) on malformed input, and pass the value through to the DB call.
0.5.0¶
Added¶
CLI
status: show UBLDC version in parentheses next to each DCN’s versionCLI
status: DepthCache redundancy summary — replica breakdown (running/starting) and redundancy categories (fully redundant, degraded, no redundancy)AccountGroups:
binance.com-vanilla-options→binance.com,binance.com-vanilla-options-testnet→binance.com-futures-testnet
0.4.1¶
Fixed¶
Compiled file ‘ubdcc_shared_modules.AccountGroups’ was missing in the wheels
Pinning ubdcc versions in ubdcc cli requirements
0.4.0¶
Added¶
Binance API credentials support. Optional: add one or more
(api_key, api_secret)pairs per account group (binance.com,binance.com-testnet,binance.com-futures-testnet,binance.us,binance.tr) via new REST endpoints/ubdcc_add_credentials(POST/GET),/ubdcc_remove_credentials,/ubdcc_get_credentials_listand matchingubdcc credentials add/list/removeCLI subcommands. Mgmt load-balances credentials across DCN pods (fewest-assigned-first); DCNs fetch their assigned key via the internal/ubdcc_assign_credentialsendpoint and pass it to UBLDC as aBinanceRestApiManager. Lets the cluster use authenticated rate limits without mandating credentials — public-only operation remains unchanged. Keys are part of the self-healing DB sync (full cleartext cluster-internal); public responses only show masked previews and the list of assigned DCN UIDs per key. See README → API Credentials for the security trade-offs.get_asks/get_bids: when failover recovers from one or more unreachable DCNs, the successful response now includes anerror_id(#5001) and anerrormessage listing the pods that failed before success. Gives monitoring a visible signal that failover occurred without changing the OK-with-data semantics. Closes #3.DepthCache distribution tracking: new DB fields
LAST_DISTRIBUTION_CHANGE+DISTRIBUTION_CHANGES(top-level, counts how often a DC has been re-assigned across pods) andRESTARTSper distribution entry (counts stream restarts on the currently assigned pod). Visible inget_depthcache_info/get_depthcache_list— useful for diagnosing upstream stability and cluster rebalancing history. Closes #1.
Removed¶
External
ubdcc restart <name>CLI subcommand — it didn’’’t actually work because the spawn functions live in the interactive shell closure. Restart is still available inside the interactiveubdcc>shell.
Fixed¶
DCN startup crash:
self._restart_queuewas initialized aftersuper().__init__(), butServiceBase.__init__()blocks onself.app.start()and runsmain()synchronously — so the assignment was never reached. Every DCN crashed seconds after registering withAttributeError: '_restart_queue', leaving only mgmt and restapi alive. Queue init now runs beforesuper().__init__().
0.3.3¶
Added¶
New
ubdccmeta-package and cluster manager (pip install ubdcc)ubdccCLI command with interactive console:ubdcc start --dcn 4— starts mgmt, restapi and DCN processesubdcc status— shows all pods with role, name, port, statusubdcc stop— graceful shutdown via RESTubdcc restart <name>— full restart (shutdown + respawn) for mgmt, restapi or DCNadd-dcn [count]— spawn new DCN process(es) from the interactive shellremove-dcn <count|name>— stop DCN(s) by count or by nameInteractive
ubdcc>prompt duringstartfor live managementpython -m ubdccentry point for running from source during development
/shutdownREST endpoint on all pods (dev-mode only) for graceful process shutdown/create_depthcachesnow supports both GET (comma-separated markets) and POST (JSON body)mgmt_portparameter on all services — allows custom port configuration via CLI.ubdccstate file for automatic port detection across CLI commandsdebug=truenow includespost_bodyin response for POST requestsPort retry on bind failure — fixes race condition when multiple DCNs start simultaneously
Mermaid architecture diagram in README (replaces external Lucidchart image)
Comprehensive README rewrite: What is UBDCC, Why, Architecture, Local Setup, REST API reference with all endpoints, debugging guide
Python 3.9-3.14 support on Linux, macOS and Windows (all 5 packages)
Build workflows updated to UBWA pattern: 3 OS matrix, cibuildwheel v3.4.1, sdist, GitHub Release
New build workflow for
ubdccpackage (pure Python)
Changed¶
Replaced
jsonwithorjsonfor faster JSON parsing (closes #5)/create_depthcachesswitched from GET to POST as primary method (closes #10)Build target: Python 3.14 only → Python 3.9-3.14 on all platforms
Aligned
project_urlsacross all packages with UBS standard
Fixed¶
Remove remaining LUCIT references in issue templates, PR template, container LICENSE files, container READMEs, dev scripts
Fix issue templates: correct project name, add Python 3.13/3.14 to dropdown
Fix README: wrong port (42080 → 42081), old project name, typos
Fix double JSON serialization in UBLDC cluster POST requests
/shutdownendpoint now force-exits viaos._exit(0)0.5s after the response — previously processes could hang if the main loop was stuck inasyncio.sleepis_port_free()now setsSO_REUSEADDR— previously the TIME_WAIT state after a restart caused the new mgmt to land on the next port, breaking restapi/DCN connections
Removed¶
License purchase and commercial support sections from all package READMEs
External Lucidchart dependency (replaced with inline Mermaid diagram)
0.2.0¶
Added¶
AGENTS.md and TASKS.md
Changed¶
Rebranded from LUCIT to open source MIT
Renamed packages:
lucit-ubdcc-*→ubdcc-*, namespaces:lucit_ubdcc_*→ubdcc_*Kubernetes namespace:
lucit-ubdcc→ubdccLICENSE: LSOSL → MIT
Author: LUCIT Systems and Development → Oliver Zehentleitner
All URLs updated from lucit.tech/shop.lucit.services to GitHub
Sphinx docs theme:
python_docs_theme_lucit→alabaster
Fixed¶
create_depth_cache()→create_depthcache()inDepthCacheNode.py(deprecated method call)Per-package LICENSE files replaced with correct MIT license
Remaining LUCIT references in file headers, pyproject.toml descriptions, and per-package CHANGELOGs
UBLDC dependency pin:
unicorn_binance_local_depth_cache==2.6.0→unicorn-binance-local-depth-cache>=2.8.1
Removed¶
LUCIT licensing system (LicensingManager, LicensingExceptions, submit_license endpoints)
lucit-licensing-pythondependency from all packagesLicense params (
lucit_api_secret,lucit_license_token) from UBLDC instantiation in DCN
0.1.4¶
Fixed¶
https://github.com/oliver-zehentleitner/unicorn-binance-depth-cache-cluster/issues/8 - We don’t overwrite the None anymore.
0.1.3¶
MVP release!

