Each MOAD now has a synthetic defective specimen and fixed specimen proven from first principles across three test tiers: Unit (tests/unit/Moad000X*.java): - Correctness: defective and fixed produce identical functional output - Defect behavior: defective specimen exhibits the defect (measurable) - Fix behavior: fixed specimen eliminates the defect Integration (tests/integration/AllMoadsIntegrationTest.java): - All 9 MOADs proven at medium scale (N=500-2000) - MOAD-0001: O(N^2) vs O(N) list scan at N=1000 - MOAD-0002: 500 sessions trample each other (defective) vs coexist (fixed) - MOAD-0003: 250 anonymous requests leak auth identity (defective) vs zero (fixed) - MOAD-0004: 3000 credential exposures across 1000 requests (defective) vs zero (fixed) - MOAD-0005: 500 computes for 500 concurrent misses vs exactly 1 - MOAD-0006: all 500 passwords extractable from DB (defective) vs unextractable (fixed) - MOAD-0007: N=2000 spatial objects, defective visits all 2000 vs O(log N + k) - MOAD-0009: 990 wasted firings for 1000 ticks / 10 events vs zero waste - MOAD-0011: 10240 NFA steps vs 13 steps on N=12 adversarial input (788x) Functional (tests/functional/AllMoadsFunctionalTest.java): - MOAD-0005: real-thread contention proves herd (defective >1 compute, fixed exactly 1) - MOAD-0007: N=50000 spatial objects, 50M defective probes vs 516K fixed (97x speedup) - MOAD-0009: 10000 ticks / 10 events, 9990 wasted firings vs zero (1000x ratio) - MOAD-0011: N=16 adversarial, 163840 defective steps vs 17 fixed (9638x ratio) Support algorithms (tests/support/Moad000X*.java): - Moad0002Algorithm: shared mutable global state (DefectiveAudioSystem / FixedAudioSystem + Context) - Moad0003Algorithm: ThreadLocal not cleared (handleDefective / handleFixed with finally) - Moad0004Algorithm: HTTP headers logged verbatim (logDefective / logFixed with CREDENTIAL_HEADERS denylist) - Moad0005Algorithm: get+null+compute+put (DefectiveCache HashMap / FixedCache ConcurrentHashMap.computeIfAbsent) - Moad0006Algorithm: Base64 password storage (DefectiveCredentialStore / FixedCredentialStore SHA-256+salt) - Moad0007Algorithm: linear spatial scan (queryDefective list / queryFixed sorted array + binary search) - Moad0009Algorithm: timer-driven polling (runDefectiveScheduler / runFixedEventDriven) - Moad0011Algorithm: PCRE nested quantifiers (matchDefective backtracking NFA / matchFixed linear NFA) Makefile: added unit-moad-0002 through unit-moad-0011 targets, integration-all-moads, functional-all-moads. integration and functional targets now depend on all-MOADs variants.
119 lines
4.9 KiB
Java
119 lines
4.9 KiB
Java
package support;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* MOAD-0009: A Metered Heart.
|
|
*
|
|
* Defect: a scheduled job fires on a fixed timer interval regardless of whether
|
|
* any event warrants the execution. Two forms:
|
|
* (a) State-repair: patches broken state during a visible glitch window
|
|
* instead of preventing the broken state at its source.
|
|
* (b) Report-generation: recomputes everything from scratch on each tick
|
|
* instead of responding to real data-change events.
|
|
*
|
|
* Both substitute time-delayed blind repetition for correct event-driven design.
|
|
* A Metered Heart cannot tell the truth about its own state — it only knows
|
|
* the clock. Between ticks, the world is wrong and nobody knows.
|
|
*
|
|
* Fix: event-driven listener fires exactly once per event, immediately, with
|
|
* no polling overhead and no stale-state window.
|
|
*
|
|
* Scanner detects: Timer.scheduleAtFixedRate, ScheduledExecutorService.scheduleAtFixedRate,
|
|
* crontab entries, @Scheduled annotations — where the job body reads state and
|
|
* conditionally acts (the polling tell), rather than receiving an event payload.
|
|
*/
|
|
public class Moad0009Algorithm {
|
|
|
|
/** An event with a name and a simulated arrival time (tick). */
|
|
public static final class Event {
|
|
public final String name;
|
|
public final int arrivalTick; // which timer tick this event arrives on
|
|
|
|
public Event(String name, int arrivalTick) {
|
|
this.name = name;
|
|
this.arrivalTick = arrivalTick;
|
|
}
|
|
}
|
|
|
|
/** Result: how many times the job fired and how many events it processed. */
|
|
public static final class Result {
|
|
public final int firings; // how many times the job ran
|
|
public final int eventsProcessed; // how many events were handled
|
|
public final int totalEvents; // total events that existed
|
|
public final List<String> processedNames;
|
|
|
|
public Result(int firings, int eventsProcessed, int totalEvents,
|
|
List<String> processedNames) {
|
|
this.firings = firings;
|
|
this.eventsProcessed = eventsProcessed;
|
|
this.totalEvents = totalEvents;
|
|
this.processedNames = processedNames;
|
|
}
|
|
}
|
|
|
|
// ── Defective: polling scheduler ─────────────────────────────────────────
|
|
|
|
/**
|
|
* Simulates a timer-driven job that fires every tick for totalTicks ticks.
|
|
* DEFECT: the job fires even when there are no events to process — wasting
|
|
* CPU, database I/O, and thread time on empty polls.
|
|
*
|
|
* @param totalTicks number of timer firings to simulate
|
|
* @param events events to be processed (each has an arrivalTick)
|
|
* @return result showing wasted firings vs useful work
|
|
*/
|
|
public static Result runDefectiveScheduler(int totalTicks, List<Event> events) {
|
|
int firings = 0;
|
|
int eventsProcessed = 0;
|
|
List<String> processedNames = new ArrayList<>();
|
|
|
|
for (int tick = 0; tick < totalTicks; tick++) {
|
|
firings++; // DEFECT: fires every tick, useful or not
|
|
|
|
// Scan for pending events (simulates a DB poll or queue drain)
|
|
for (Event e : events) {
|
|
if (e.arrivalTick == tick) {
|
|
eventsProcessed++;
|
|
processedNames.add(e.name);
|
|
}
|
|
}
|
|
}
|
|
|
|
return new Result(firings, eventsProcessed, events.size(), processedNames);
|
|
}
|
|
|
|
// ── Fixed: event-driven listener ─────────────────────────────────────────
|
|
|
|
/**
|
|
* Simulates an event-driven listener that fires exactly once per event.
|
|
* FIX: no timer, no polling — the job runs only when an event arrives.
|
|
* No CPU wasted on empty ticks. No stale-state window between ticks.
|
|
*
|
|
* @param events events to process
|
|
* @return result showing one firing per event, zero wasted firings
|
|
*/
|
|
public static Result runFixedEventDriven(List<Event> events) {
|
|
int firings = 0;
|
|
List<String> processedNames = new ArrayList<>();
|
|
|
|
// FIX: each event triggers exactly one handler invocation
|
|
for (Event e : events) {
|
|
firings++; // fired because an event arrived
|
|
processedNames.add(e.name);
|
|
}
|
|
|
|
return new Result(firings, firings, events.size(), processedNames);
|
|
}
|
|
|
|
/** Build a list of M events spread across [0, maxTick). */
|
|
public static List<Event> buildEvents(int count, int maxTick) {
|
|
List<Event> events = new ArrayList<>(count);
|
|
for (int i = 0; i < count; i++) {
|
|
int tick = (count == 1) ? 0 : (maxTick - 1) * i / (count - 1);
|
|
events.add(new Event("event-" + i, tick));
|
|
}
|
|
return events;
|
|
}
|
|
}
|