1. The Core Paradigm Shift: Node.js vs TruLang
In Node.js, developers learn early on that "Never block the Event Loop" is the cardinal rule. In Node.js, all JavaScript runs on a single main thread, while a generational Garbage Collector (V8) periodically pauses execution (Stop-the-World) to scan heaps and compact live references.
π’ Node.js Architecture
- Concurrency: Single-Threaded Event Loop with libuv thread pool.
- Memory Model: V8 Generational GC (Scavenger + Mark-Sweep-Compact).
- GC Overhead: 15 β 45 ms Stop-the-World GC pauses under heavy load.
- Memory Footprint: 40 β 80 MB RSS base overhead per process.
- Failure Mode: An unhandled exception or memory leak crashes the entire Node process.
- Blocking Risk: Heavy JSON serialization or crypto starves incoming socket events.
π· TruLang Architecture
- Concurrency: Zero-Lock Service Actors with lock-free bounded mailboxes.
- Memory Model: Dual-Region (Region A bump arena + Region B acyclic heap).
- GC Overhead: 0.0 ms Garbage Collection pauses (reclaimed in $O(1)$).
- Memory Footprint: 2.5 MB RSS (~6,400 microservices per 16GB node).
- Failure Mode: Per-request memory budget isolation; overflows fail fast without crashing actors.
- Blocking Risk: Foreign I/O (SQL, HTTP) automatically offloaded to worker pool.
2. The Dual-Region Memory Model in Depth
Traditional languages force an agonizing trade-off: either accept unpredictable GC pauses (Java, Go, Node.js) or endure complex borrow checker rules and manual memory lifetimes (Rust, C++). TruLang eliminates this trade-off by categorizing memory into two distinct operational regions:
Developers never manually annotate complex lifetimes. TruLang enforces a deterministic 3-tier allocation decision rule:
detect_value_cycles). Guarantees zero circular reference leaks in persistent actor storage.cache.insert(k, v)) are automatically detected by the compiler, which inserts Opcode::Persist.3. Step-by-Step Lifecycle of a TruLang Request
Here is exactly what happens under the hood when a client invokes an endpoint on a TruLang Service:
Incoming requests land in the Service Actor's bounded mailbox queue. If the queue is at capacity (e.g. during a sudden downstream outage), TruLang rejects the request immediately with ActorError::MailboxFull (HTTP 429), protecting the process from runaway queue bloat and out-of-memory crashes.
Before handling the message, the runtime ensures the actor's Region A arena offset is reset to zero (offset = 0). This instant reset takes < 1 nanosecond and guarantees pristine, unfragmented memory for the request.
The actor executes the request handler sequentially. All allocations (JSON parsing, query string decodes, intermediate maps, response objects) bump the Region A pointer. Because only one request executes on this actor at a time, there is zero mutex lock contention and zero data race risk.
If the handler queries a PostgreSQL database or issues an HTTP GET, the call is dispatched to the background WorkerPool. The actor mailbox remains responsive, and results stream directly back into Region A bump memory upon completion.
If any cached entity is stored in service state, it is promoted to Region B. The response is written to the client socket, and the actor executes FreeRegionA. All memory used by the request vanishes instantlyβwithout running a Garbage Collection sweep.
4. Zero-Lock Service Actors & Mailbox Mechanics
In traditional multi-threaded languages (Go, Java, C#), shared mutable state requires sync.Mutex, rwlock, or synchronized blocks. Under high concurrency, threads spend up to 70% of CPU time waiting on lock acquisitions and cache line invalidations.
Why TruLang Uses the Zero-Lock Actor Model
In TruLang, every service declaration defines an independent Actor with its own private memory domain and mailbox queue:
service AccountService {
memory_budget request = 16 MB max 64 MB;
memory_budget persistent = 256 MB;
let mut balances: Map<string, i64> = Map::new();
fn transfer(from: string, to: string, amount: i64) -> bool {
// Executed sequentially inside the Actor!
// NO mutex.lock() needed! NO data race possible!
let cur_from = balances.get(from);
if cur_from >= amount {
balances.insert(from, cur_from - amount);
balances.insert(to, balances.get(to) + amount);
return true;
}
return false;
}
}
5. The Hybrid Thread-Pooled FFI Bridge (`crates/ffi`)
A common pitfall with pure actor architectures is Mailbox Starvation: if an actor executes a synchronous blocking foreign call (such as a slow SQL query or network socket read), its mailbox freezes and cannot process incoming messages.
How TruLang Solves Mailbox Starvation
TruLang's crates/ffi introduces a Hybrid Thread Pool:
- Worker Pool Offloading: Foreign blocking functions (
db.query,http.get) are automatically dispatched to dedicated OS background worker threads. - Deterministic RAII Destructors: Opaque foreign resources (e.g. connection pools) are held via
ExternalHandle<T>. When the handle goes out of scope or the actor terminates, the native C/Rust destructor runs automatically with zero leaks. - Region A Marshaling: Results returned by native libraries stream directly into the caller's Region A bump arena, enforcing memory budget ceilings so external drivers cannot bypass quotas.
6. Dual-Mode Execution: Dev VM vs Production Native AOT
To maximize both developer productivity and production throughput, TruLang provides a seamless Dual-Mode Execution Strategy:
| Execution Mode | CLI Command | Cold-Start Time | Throughput | Primary Use Case |
|---|---|---|---|---|
| Development Mode | trulang run file.tr |
< 3 ms | ~780,000 req/s | Local development, testing, live debugging, CLI scripting. |
| Native JIT Mode | trulang run --jit file.tr |
~300 ms (JIT compile) | 1,150,000+ req/s | On-the-fly native benchmark evaluation and test validation. |
| Production AOT Mode | trulang build --release file.tr |
Instant (0 ms) | 1,200,000+ req/s | Production container images, Kubernetes pods, standalone binaries. |
7. Out-of-Process Atomic Seqlock Telemetry (`crates/supervisor`)
In high-performance services, monitoring metrics (Prometheus scraping, memory sampling) often introduces latency jitter and CPU overhead on worker threads. TruLang decouples monitoring completely into an independent out-of-process supervisor:
// Worker Process writes telemetry without taking locks:
sequence.store(seq + 1, Release);
region_a_used.store(bytes, Relaxed);
burst_count.store(count, Relaxed);
sequence.store(seq + 2, Release);
// External Monitor reads atomically without pausing the worker:
do {
s1 = sequence.load(Acquire);
read_telemetry_snapshot(&data);
s2 = sequence.load(Acquire);
} while (s1 != s2 || (s1 & 1) != 0);
8. Developer Mental Model: Best Practices for Building TruLang Apps
π‘ Rule 1: Think in Request Scopes
Do not worry about freeing intermediate strings, arrays, or objects created while handling a request. Allocate freelyβthe entire request arena is reclaimed automatically in $O(1)$ constant time as soon as the function returns.
π‘ Rule 2: Keep Persistent State Truly Shared
Only promote data to Region B if it needs to be accessed across multiple requests (such as an in-memory session cache or database connection pool). All transient request payloads belong in Region A.
π‘ Rule 3: Size Budgets with Headroom
Use elastic budgets (e.g. memory_budget request = 16 MB max 64 MB;). The base budget (16 MB) keeps memory footprint tiny during normal traffic, while the max ceiling (64 MB) gracefully accommodates occasional large payloads without crashing.