# run_chain_drive_simulation.ps1 # OneCharacterCode Chain Drive simulation - PowerShell 5.1 compatible. # Builds 300 website nodes arranged in a circular chain and runs 5 scenarios. # Outputs: # chain-drive-simulation.json (initial 300-node configuration) # chain-drive-results.json (per-scenario metrics) # # This is a SIMULATION, not a production deployment. No real network # traffic occurs. All values are produced by deterministic seeded logic. $ErrorActionPreference = 'Stop' $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $encUtf8NoBom = New-Object System.Text.UTF8Encoding $false # ---------------------------------------------------------------------- # CONFIGURATION # ---------------------------------------------------------------------- $NODE_COUNT = 300 $CHAIN_CYCLES = 4 # how many full loops to simulate per scenario $SEED = 11052026 # deterministic $rng = New-Object System.Random ($SEED) # Priority queues, in order of highest priority first $PRIORITY_ORDER = @( 'Accounts', 'SecurityAlerts', 'PaymentsSales', 'CustomerUpdates', 'InventoryUpdates', 'WebsiteContentUpdates', 'SEOIndexing', 'BackupTasks', 'ArchiveTasks', 'RestIdle' ) # Each priority has a baseline cost per task and a baseline byte size per packet $PRIORITY_META = @{ 'Accounts' = @{ cost = 8; bytes = 1800; symbolicBytes = 220 } 'SecurityAlerts' = @{ cost = 9; bytes = 1500; symbolicBytes = 160 } 'PaymentsSales' = @{ cost = 12; bytes = 2400; symbolicBytes = 300 } 'CustomerUpdates' = @{ cost = 6; bytes = 1200; symbolicBytes = 200 } 'InventoryUpdates' = @{ cost = 7; bytes = 1900; symbolicBytes = 240 } 'WebsiteContentUpdates' = @{ cost = 10; bytes = 5200; symbolicBytes = 420 } 'SEOIndexing' = @{ cost = 5; bytes = 3100; symbolicBytes = 360 } 'BackupTasks' = @{ cost = 4; bytes = 7400; symbolicBytes = 540 } 'ArchiveTasks' = @{ cost = 3; bytes = 9000; symbolicBytes = 620 } 'RestIdle' = @{ cost = 0; bytes = 0; symbolicBytes = 0 } } # Domain name shape pool for synthetic ecosystem $DOMAIN_PARTS_LEFT = @('blue','rapid','golden','prime','open','swift','solid','clear','crisp','noble','sharp','core','nexus','peak','axis','vortex','helix','crystal','signal','origin','prime','arc','beacon','focus','vivid','quartz','spark','glide','meridian','summit') $DOMAIN_PARTS_RIGHT = @('store','market','works','labs','studio','partners','agency','services','collective','press','media','co','company','group','network','exchange','direct','depot','factory','plus') $TLD_POOL = @('.com','.com','.com','.net','.io','.app','.shop','.co','.studio','.ai') # ---------------------------------------------------------------------- # Build 300 nodes # ---------------------------------------------------------------------- function New-Node($id) { $left = $DOMAIN_PARTS_LEFT[$rng.Next($DOMAIN_PARTS_LEFT.Length)] $right = $DOMAIN_PARTS_RIGHT[$rng.Next($DOMAIN_PARTS_RIGHT.Length)] $tld = $TLD_POOL[$rng.Next($TLD_POOL.Length)] $domain = "$left-$right-$id$tld" $queueType = $PRIORITY_ORDER[$rng.Next($PRIORITY_ORDER.Length - 1)] # rarely pure RestIdle initially # Dependencies: each node may depend on 0-3 earlier nodes for sales-after-data $depCount = $rng.Next(0, 4) $deps = @() for ($d = 0; $d -lt $depCount; $d++) { $depId = $rng.Next(1, $id + 1) if ($depId -ne $id -and -not ($deps -contains $depId)) { $deps += $depId } } # Shared customers: cluster of 2-6 customer-ids that overlap with neighbors $sharedCount = $rng.Next(2, 7) $shared = @() for ($s = 0; $s -lt $sharedCount; $s++) { $shared += "cust-$((($id * 7 + $s * 13) % 9999).ToString('0000'))" } # Initial load: random 5..95 percent capacity used $cap = 100 $load = $rng.Next(5, 96) return [pscustomobject]@{ SiteID = $id DomainName = $domain PriorityLevel = $queueType CurrentLoad = $load AvailableCapacity = ($cap - $load) QueueType = $queueType Dependencies = $deps SharedCustomers = $shared Status = 'healthy' LastProcessed = '2026-05-11T00:00:00Z' WorkCompleted = 0 SupportGiven = 0 SupportReceived = 0 } } Write-Host ('Building ' + $NODE_COUNT + ' chain nodes (seed=' + $SEED + ')...') $nodes = @() for ($i = 1; $i -le $NODE_COUNT; $i++) { $nodes += (New-Node $i) } # Snapshot the initial configuration as JSON $initialConfig = [pscustomobject]@{ schema_version = 'chain-drive-sim-v1' generated_at = (Get-Date).ToString('o') seed = $SEED node_count = $NODE_COUNT priority_order = $PRIORITY_ORDER priority_meta = $PRIORITY_META nodes = $nodes } [System.IO.File]::WriteAllText((Join-Path $scriptDir 'chain-drive-simulation.json'), ($initialConfig | ConvertTo-Json -Depth 8), $encUtf8NoBom) Write-Host ' wrote chain-drive-simulation.json' # ---------------------------------------------------------------------- # Scenario engine # ---------------------------------------------------------------------- function Clone-Nodes($srcNodes) { # Deep enough clone for simulation purposes (counters reset each scenario) $out = @() foreach ($n in $srcNodes) { $out += [pscustomobject]@{ SiteID = $n.SiteID DomainName = $n.DomainName PriorityLevel = $n.PriorityLevel CurrentLoad = $n.CurrentLoad AvailableCapacity = $n.AvailableCapacity QueueType = $n.QueueType Dependencies = @($n.Dependencies) SharedCustomers = @($n.SharedCustomers) Status = $n.Status LastProcessed = $n.LastProcessed WorkCompleted = 0 SupportGiven = 0 SupportReceived = 0 } } return $out } # Returns @{ metrics; finalNodes } for a scenario function Run-Scenario { param( [string]$name, [object[]]$nodes, [hashtable]$mods, # @{ spike=@{nodeIds=...; amount=...}; fail=@{nodeIds=...}; fastIndex=@{...}; salesWait=$true } [int]$cycles = $CHAIN_CYCLES ) Write-Host ('Scenario ' + $name + ' running (' + $cycles + ' cycles)...') # Apply modifications if ($mods.ContainsKey('spike')) { foreach ($id in $mods.spike.nodeIds) { $n = $nodes | Where-Object { $_.SiteID -eq $id } | Select-Object -First 1 if ($n) { $extra = [int]($mods.spike.amount * 80 / 100.0) $n.CurrentLoad = [Math]::Min(180, $n.CurrentLoad + $extra) $n.AvailableCapacity = 100 - $n.CurrentLoad # may go negative => overloaded $n.Status = 'overloaded-by-event' } } } if ($mods.ContainsKey('fail')) { foreach ($id in $mods.fail.nodeIds) { $n = $nodes | Where-Object { $_.SiteID -eq $id } | Select-Object -First 1 if ($n) { $n.Status = 'failed'; $n.CurrentLoad = 0; $n.AvailableCapacity = 0 } } } $overloadedBefore = @($nodes | Where-Object { $_.CurrentLoad -gt 100 }).Count $totalTasksProcessed = 0 $supportTransfers = 0 $idleCapacityReused = 0 $symbolicBytesTotal = 0 $normalBytesTotal = 0 $failsRecovered = 0 $cyclesCompleted = 0 $dependencyWaits = 0 # Pre-index by id for fast lookup $byId = @{} foreach ($n in $nodes) { $byId[$n.SiteID] = $n } for ($cycle = 1; $cycle -le $cycles; $cycle++) { # CIRCULAR PROCESSING: walk every node in turn, each does work in # priority order, idle nodes help overloaded ones. foreach ($n in $nodes) { if ($n.Status -eq 'failed') { # Self-repair: neighbors pick up its workload $left = if ($n.SiteID -gt 1) { $byId[$n.SiteID - 1] } else { $byId[$NODE_COUNT] } $right = if ($n.SiteID -lt $NODE_COUNT) { $byId[$n.SiteID + 1] } else { $byId[1] } $absorbed = 0 foreach ($neighbor in @($left, $right)) { if ($neighbor.Status -ne 'failed' -and $neighbor.AvailableCapacity -gt 10) { $neighbor.SupportGiven += 1 $neighbor.Status = 'self-repair' $supportTransfers++ $absorbed++ } } if ($absorbed -gt 0) { $failsRecovered++ } continue } # Check dependencies for sales-wait pattern $blocked = $false if ($n.QueueType -eq 'PaymentsSales' -and $mods.ContainsKey('salesWait') -and $mods.salesWait) { foreach ($depId in $n.Dependencies) { $depNode = $byId[$depId] if ($depNode -and $depNode.WorkCompleted -lt $cycle) { $blocked = $true; break } } if ($blocked) { $dependencyWaits++; continue } } # Do work based on queue type $meta = $PRIORITY_META[$n.QueueType] $cost = $meta.cost if ($n.QueueType -eq 'RestIdle') { $n.Status = 'resting' continue } if ($n.CurrentLoad -gt 100) { # Overloaded - try to redirect to idle nodes $n.Status = 'overloaded' $excess = $n.CurrentLoad - 100 # Find an idle/healthy node with spare capacity to support this one $supporter = $nodes | Where-Object { $_.Status -ne 'failed' -and $_.SiteID -ne $n.SiteID -and $_.AvailableCapacity -gt ($excess + 10) -and ($_.QueueType -eq 'RestIdle' -or $_.QueueType -in @('BackupTasks','ArchiveTasks','SEOIndexing')) } | Select-Object -First 1 if ($supporter) { $supporter.SupportGiven += 1 $supporter.Status = 'supporting' $supporter.CurrentLoad += [Math]::Min(20, $excess) $supporter.AvailableCapacity = 100 - $supporter.CurrentLoad $n.SupportReceived += 1 $n.CurrentLoad = [Math]::Max(80, $n.CurrentLoad - 25) $n.AvailableCapacity = 100 - $n.CurrentLoad $supportTransfers++ $idleCapacityReused += 20 } } else { $n.Status = 'busy' if ($n.CurrentLoad -lt 60) { $n.Status = 'healthy' } } # Tick work $n.WorkCompleted += 1 $n.LastProcessed = '2026-05-11T00:' + ('{0:D2}' -f $cycle) + ':00Z' $totalTasksProcessed += 1 $normalBytesTotal += $meta.bytes $symbolicBytesTotal += $meta.symbolicBytes } # Fast-index propagation (Scenario C): inventory at origin propagates to dependents in one pass if ($mods.ContainsKey('fastIndex')) { foreach ($id in $mods.fastIndex.originIds) { $origin = $byId[$id] if (-not $origin) { continue } # Each shared-customer link costs one symbolic packet vs a full content push foreach ($s in $origin.SharedCustomers) { $normalBytesTotal += $PRIORITY_META['InventoryUpdates'].bytes $symbolicBytesTotal += $PRIORITY_META['InventoryUpdates'].symbolicBytes $totalTasksProcessed += 1 } } } $cyclesCompleted++ } $overloadedAfter = @($nodes | Where-Object { $_.CurrentLoad -gt 100 }).Count $avgLoad = 0 if ($nodes.Count -gt 0) { $sum = 0 foreach ($n in $nodes) { $sum += $n.CurrentLoad } $avgLoad = [math]::Round($sum / [double]$nodes.Count, 2) } $pctSaved = 0 if ($normalBytesTotal -gt 0) { $pctSaved = [math]::Round((1.0 - ($symbolicBytesTotal / [double]$normalBytesTotal)) * 100.0, 2) } $metrics = [pscustomobject]@{ scenario = $name total_tasks_processed = $totalTasksProcessed average_load_per_node = $avgLoad overloaded_before_balancing = $overloadedBefore overloaded_after_balancing = $overloadedAfter idle_capacity_reused_units = $idleCapacityReused support_transfers_performed = $supportTransfers symbolic_packet_bytes = $symbolicBytesTotal normal_transfer_bytes = $normalBytesTotal percent_transfer_saved = $pctSaved failed_links_recovered = $failsRecovered total_chain_cycles_completed = $cyclesCompleted dependency_waits = $dependencyWaits } Write-Host (' tasks=' + $totalTasksProcessed + ' supports=' + $supportTransfers + ' overloaded ' + $overloadedBefore + '->' + $overloadedAfter + ' saved=' + $pctSaved + '%') return @{ metrics = $metrics; finalNodes = $nodes } } # ---------------------------------------------------------------------- # Run scenarios # ---------------------------------------------------------------------- $scenarios = @() # A: Normal 300-site chain $nodesA = Clone-Nodes $nodes $rA = Run-Scenario -name 'A_normal_300_chain' -nodes $nodesA -mods @{} $scenarios += $rA.metrics # B: One website event spike $spikeId = 47 # arbitrary mid-chain id $nodesB = Clone-Nodes $nodes # bump the chosen node's queue to PaymentsSales to make it high-priority $nodesB | Where-Object { $_.SiteID -eq $spikeId } | ForEach-Object { $_.QueueType = 'PaymentsSales'; $_.PriorityLevel = 'PaymentsSales' } $rB = Run-Scenario -name 'B_event_spike_one_site' -nodes $nodesB -mods @{ spike = @{ nodeIds = @($spikeId); amount = 100 } } $scenarios += $rB.metrics # C: New inventory must be fast-indexed across related sites $originIds = @(15, 88, 201) $nodesC = Clone-Nodes $nodes $nodesC | Where-Object { $_.SiteID -in $originIds } | ForEach-Object { $_.QueueType = 'InventoryUpdates'; $_.PriorityLevel = 'InventoryUpdates' } $rC = Run-Scenario -name 'C_fast_inventory_index' -nodes $nodesC -mods @{ fastIndex = @{ originIds = $originIds } } $scenarios += $rC.metrics # D: Important site fails - chain self-repairs $failIds = @(133, 220, 270) $nodesD = Clone-Nodes $nodes $rD = Run-Scenario -name 'D_self_repair_failed_link' -nodes $nodesD -mods @{ fail = @{ nodeIds = $failIds } } $scenarios += $rD.metrics # E: Sales-processing waits for upstream $nodesE = Clone-Nodes $nodes # Promote a handful of nodes to PaymentsSales with dependencies on lower-id upstream foreach ($n in $nodesE) { $sid = [int]$n.SiteID if (@(70, 130, 175, 230) -contains $sid) { $n.QueueType = 'PaymentsSales'; $n.PriorityLevel = 'PaymentsSales' $deps = New-Object 'System.Collections.Generic.List[int]' if (($sid - 5) -gt 0) { [void]$deps.Add($sid - 5) } if (($sid - 12) -gt 0) { [void]$deps.Add($sid - 12) } $n.Dependencies = $deps.ToArray() } } $rE = Run-Scenario -name 'E_sales_waits_for_upstream' -nodes $nodesE -mods @{ salesWait = $true } $scenarios += $rE.metrics # ---------------------------------------------------------------------- # Aggregate + write # ---------------------------------------------------------------------- $totalSymbolic = ($scenarios | Measure-Object -Property symbolic_packet_bytes -Sum).Sum $totalNormal = ($scenarios | Measure-Object -Property normal_transfer_bytes -Sum).Sum $pctSavedAll = if ($totalNormal -gt 0) { [math]::Round((1.0 - ($totalSymbolic / [double]$totalNormal)) * 100.0, 2) } else { 0 } $resultsPkg = [pscustomobject]@{ schema_version = 'chain-drive-results-v1' generated_at = (Get-Date).ToString('o') node_count = $NODE_COUNT chain_cycles = $CHAIN_CYCLES scenarios = $scenarios aggregate_normal_bytes = $totalNormal aggregate_symbolic_bytes = $totalSymbolic aggregate_percent_saved = $pctSavedAll disclosure = 'This is a simulation demo, not a production deployment. It models Bret Fencl''s Chain Drive architecture for distributed workload balancing across a multi-site ecosystem.' } [System.IO.File]::WriteAllText((Join-Path $scriptDir 'chain-drive-results.json'), ($resultsPkg | ConvertTo-Json -Depth 8), $encUtf8NoBom) Write-Host ' wrote chain-drive-results.json' Write-Host '' Write-Host '===========================================' Write-Host 'AGGREGATE: normal=' $totalNormal ' symbolic=' $totalSymbolic ' saved=' $pctSavedAll '%' foreach ($s in $scenarios) { Write-Host (' - ' + $s.scenario + ': tasks=' + $s.total_tasks_processed + ' supports=' + $s.support_transfers_performed + ' overloaded ' + $s.overloaded_before_balancing + '->' + $s.overloaded_after_balancing + ' saved=' + $s.percent_transfer_saved + '%') } Write-Host '' Write-Host 'Done.'