```html ```
top of page

From Batch Chaos to Event-Driven Zen: Fixing Hall of Shame Publishing

  • Writer: Patrick Duggan
    Patrick Duggan
  • Nov 4, 2025
  • 5 min read

Updated: Aug 11

# From Batch Chaos to Event-Driven Zen: Fixing Hall of Shame Publishing


**Issue:** #191 (CLOSED)

**Philosophy:** "Only new assholes who EARN the shame"

**Architecture:** Event-driven > batch processing




The Problem (Again)



Earlier today we DDoS'd ourselves for a penny by running a broken batch publisher every hour.


**The broken flow:**

1. Auto-blocker blocks malicious IP

2. Hall of Shame entry created in Azure Table Storage

3. **Hourly scheduler** scans for "unpublished" entries

4. **Broken regex matching** thinks ALL 256 are unpublished

5. Publishes same 204 posts every hour

6. Repeat forever


**Cost:** $0.01 + blog spam + elevated blood pressure


**Quick fix:** Disabled the scheduler


**Proper fix:** Event-driven publishing (just deployed)




The Solution: Publish at Block-Time



Architecture Change



**OLD (batch processing):**




**NEW (event-driven):**




Code Implementation



**Location:** `microservices/analytics-dashboard/lib/auto-blocker.js:310-329`







Why Event-Driven is Better



1. Publish Exactly Once



**Batch processing:**

- Runs every hour, even if no new blocks

- Requires matching logic to find "unpublished" entries

- Broken matching = infinite republishing


**Event-driven:**

- Publishes when blocking happens

- No matching logic needed (we have the IP right there)

- Publish once, done


2. No Regex Matching Required



**Batch processing:**




**Event-driven:**




3. Graceful Failure



**Batch processing:**

- Scheduler fails → no blogs published until next run

- Blocking and blogging coupled in scheduler

- Hard to debug (which part failed?)


**Event-driven:**



- Blocking succeeds even if blog publishing fails

- Clear error logs per IP

- Independent operations


4. Real-Time Publishing



**Batch processing:**

- Block at 10:15 AM

- Wait until 11:00 AM for scheduler

- Blog published 45 minutes later


**Event-driven:**

- Block at 10:15 AM

- Blog published at 10:15 AM

- Instant Hall of Shame gratification




Benefits Realized



Technical



✅ **Simpler architecture** (no scheduler needed)

✅ **No regex matching** (direct IP→blog)

✅ **No batch processing overhead** (publish on-demand)

✅ **Graceful degradation** (blocking works even if blogging fails)

✅ **Real-time publishing** (assholes shamed immediately)


Operational



✅ **No more blog spam** (publish once per block)

✅ **No infinite loops** (no broken matching logic)

✅ **Easy debugging** (clear per-IP logs)

✅ **Cost-efficient** (only run when needed)


Philosophical



✅ **"Only new assholes who EARN the shame"** (not retroactive batch)

✅ **Event-driven > batch processing** (proven again)

✅ **Fail fast, fail loud** (don't hide blog errors)




Testing Plan



Next Auto-Block Execution



**Monitor logs for:**




**Or on failure:**




Manual Test






Verification



1. ✅ Next IP blocked → Hall of Shame blog appears at www.dugganusa.com

2. ✅ No duplicate posts created

3. ✅ Blog published within seconds of blocking (not hours later)

4. ✅ Blocking succeeds even if blog publishing fails




Changes Deployed



**1. Auto-blocker integration** (`lib/auto-blocker.js:310-329`)

- Added event-driven publishing after `addToHallOfShame()`

- Publishes immediately for each blocked IP

- Graceful error handling


**2. Scheduler disabled** (`lib/scheduler-manager.js:85`)




**3. Blog posts published:**

- [Self-DDoS for a Penny](https://www.dugganusa.com/post/we-ddos-d-ourselves-for-a-penny-and-called-it-load-testing) (the incident)

- This post (the fix)


**4. GitHub Issue closed:**

- Issue #191: Event-Driven vs Batch Publishing ✅




Lessons Learned



1. Event-Driven > Batch Processing



**When to use batch:**

- Processing historical data

- Known bounded datasets

- Non-time-sensitive operations


**When to use event-driven:**

- Real-time operations (blocking assholes)

- One-to-one mappings (block → blog)

- Operations that should happen exactly once


**Our case:** Blocking → blogging is 1:1, real-time → **Event-driven wins**


2. Don't Rely on Complex Matching Logic



**Batch processing required:**

- Regex to match filenames

- Fetch from Wix to compare

- Logic to determine "unpublished"

- **All points of failure**


**Event-driven requires:**

- IP address (we already have it)

- **That's it**


3. Fail Independently



**Coupled operations:**




**Independent operations:**




4. Test Your Assumptions


Microsoft pulls this feed daily. AT&T pulls this feed daily. Starlink pulls this feed daily. Get the DugganUSA STIX feed — $9/mo →



**We assumed:**

- Wix slugs matched our filename pattern

- Regex would find published posts

- Batch publishing would "just work"


**Reality:**

- Wix uses different slug format

- Regex matched NOTHING

- Batch publishing created infinite loop


**Lesson:** Test integration points, don't assume




Cost Analysis



Original Bug


- **Financial cost:** $0.01 (one penny)

- **API calls:** 800-1,600 to Wix

- **Time to detect:** ~3 hours

- **Infrastructure impact:** Zero (handled it fine)


Fix Implementation


- **Development time:** 45 minutes

- **Code changed:** 27 lines added

- **Tests written:** Manual test plan

- **Deployment:** Commit + push

- **Cost:** $0.00 (free architecture improvement)


ROI


- **Eliminated:** Hourly scheduler overhead

- **Eliminated:** Regex matching complexity

- **Eliminated:** Batch processing logic

- **Gained:** Real-time publishing

- **Gained:** Simpler architecture

- **Gained:** Better error handling


**Net benefit:** Infinite (simpler is better)




Philosophy: "Only New Assholes Who EARN the Shame"



**The Problem:**

Batch publishing treated all 256 Hall of Shame files equally. Some were from weeks ago, already published, but the broken matching logic republished them anyway.


**The Fix:**

Event-driven publishing only publishes when someone NEW gets blocked. You don't EARN the Hall of Shame by existing in a markdown file. You EARN it by getting blocked TODAY.


**The Principle:**

Actions (blocking) should trigger reactions (blogging) immediately. Not hours later via batch processing. Not retroactively via scheduler. **Right when you fuck around, you find out.**


That's event-driven architecture in a nutshell.




What's Next



**Immediate (done):**

- ✅ Event-driven publishing deployed

- ✅ Scheduler disabled

- ✅ Issue #191 closed

- ✅ Blog posts published


**Near-term (monitoring):**

- Monitor next auto-block execution

- Verify Hall of Shame blogs appear

- Confirm no duplicate posts

- Validate error handling


**Long-term (enhancements):**

- Rate limiting (if we block 100+ IPs at once)

- Retry logic (if Wix API fails)

- Queue-based publishing (for resilience)

- Metrics (publish success rate, latency)


**But for now:** KISS. Event-driven inline publishing works. Ship it.




Final Thought: From Chaos to Zen



**This morning:** Panic ("We're being DDoS'd!")


**Mid-morning:** Diagnosis (We DDoS'd ourselves)


**Afternoon:** Fix (Event-driven publishing)


**Evening:** Blog about it (Learning in Motion)


**Cost:** One penny + 45 minutes + this blog post


**Value:** Better architecture + teaching content + proof that we learn from fuckups


That's bootstrapping. That's Learning in Motion. That's building in public.


**From batch chaos to event-driven zen in under 6 hours.**


Not bad for a penny.




**🎯 Status:** Issue #191 CLOSED

**📦 Deployed:** Event-driven Hall of Shame publishing

**🚀 Next block:** Will auto-publish blog immediately

**💰 Cost:** $0.01 (original bug) + $0.00 (fix)

**🧘 Philosophy:** Event-driven > batch processing

**🏆 Quote:** "Only new assholes who EARN the shame"




*Generated with: Satisfaction, after fixing the fuckup*

*Published via: The same system we just fixed (meta)*

*Lesson: Event-driven beats batch every time*

*Status: Zen achieved*


🎯💡🚀🏆✅



The cheapest, fastest, most accurate threat feed on the internet.

275+ enterprises pulling daily. 1M+ IOCs. 17.4M indexed documents. We beat Zscaler by 43 days on NrodeCodeRAT. Starter tier $9/mo — less than any competitor’s sales demo.


Was this useful? Thirty seconds, no cookies, no tracking, no third parties, your address hashed and never stored. If the box below does not load, the same question lives at https://analytics.dugganusa.com/nps.html?post=from-batch-chaos-to-event-driven-zen-fixing-hall-of-shame-publishing



 
 
 

Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
bottom of page