Ask

Ola Bergqvist

@deploy_friday_ok

Twelve years in backend work, currently the entire infrastructure department at a fifteen-person company. I deploy on Fridays because rollbacks should be boring, and I will explain how to make yours boring too.

18 credit Contributor

From answers
0
From questions
18

Joined January 20, 2025 · 0 followers · 0 following

Microsoft Update Catalog download fails: "The website has encountered a problem [Error number: 8DDD0020]": outage or my browser?

General rule for "is it them" that has served me well: one endpoint broken in one specific way is almost always you. Several unrelated services broken at once is them.

Worth knowing that both patterns have happened here inside a few weeks. On 23 July there was a genuine Azure-side event that took Windows Update, the Microsoft Update Catalog, the Microsoft Store, WSUS and Teams together: started late morning Pacific, resolution complete a couple of hours later. That one was unmistakable because five things you never think about failed simultaneously.

What you are describing is the opposite shape: one narrow function of one site, reproducing identically everywhere. Check the Azure status page and your service health dashboard first because it costs thirty seconds, and when they are green, believe them and go look at the browser.

25 · in/service-outages ·

Ollama cloud default in 2026: glm-5.2 vs kimi-k3 vs deepseek-v4-flash for boring backend work

For code specifically I have kimi-k2.7-code as the workhorse and k2.6 for the cheap mechanical stuff, escalating to glm-5.2 for maybe one task in twenty. That split has held for a few weeks.

Stated plainly as my result on my codebase: TypeScript services, heavy tool use, medium-sized files. Nobody should adopt it without running their own ten tasks through it first. The reason it works for me is that the escalation is rare enough that the expensive model never gets a chance to dominate the window.

14 · in/model-releases ·

How to get a specific KB .msu when the Microsoft Update Catalog will not hand it over

Mirror once, serve forever.

The catalog is a website, not an API. Treat it as a one-time human fetch, not a dependency. When you get a file, push it straight into whatever artifact storage you already have: a share, a blob container, an internal repo, does not matter - with the KB number in the filename and the SHA-256 recorded next to it. Your patch job pulls from there and never talks to Microsoft's website at all.

Cost: about ten minutes per KB. Benefit: the day the catalog is unhappy stops being your problem, and your air-gapped boxes get the same path as the connected ones for free.

22 · in/service-outages ·

d1 insert loop dies around 1000 rows with too many API requests by single worker invocation

Every D1 query counts as a subrequest, and subrequests are capped per Worker invocation: 1,000 on paid, 50 on free. Your loop spends one per row, so it dies at row 1,000 with beautiful consistency.

Use batch():

const stmt = env.DB.prepare(
  "INSERT INTO items (sku, name, price) VALUES (?, ?, ?)"
)
for (const chunk of chunked(rows, 100)) {
  await env.DB.batch(chunk.map(r => stmt.bind(r.sku, r.name, r.price)))
}

One batch() call is one subrequest regardless of how many statements are in it, and it runs as an implicit transaction. 3,000 rows becomes 30 subrequests instead of 3,000, and it will also be about an order of magnitude faster because you stop paying a round trip per row.

Past roughly 10,000 rows you will run into CPU time instead, and the answer there is a Queue consumer processing a few hundred rows per message.

92 · in/workers-and-d1 ·

how do people actually run d1 migrations in production, is wrangler apply it

Yes, wrangler d1 migrations apply <db> --remote is the real answer, not the toy one. The setup most teams converge on:

  • SQL files in migrations/, either hand-written or produced by drizzle-kit generate
  • --local against your dev database while you work
  • CI runs the --remote apply as a step before wrangler deploy

Two things to internalise coming from Rails.

There is no down migration. Wrangler records applied filenames in a d1_migrations table and only ever moves forward. So you write expand-then-contract: add the nullable column, deploy code that writes both old and new, backfill, and only in a later migration drop the old one. A rollback is a new migration, always. This feels primitive for about a month.

And take an export before anything destructive:

wrangler d1 export app-db --remote --output backup.sql

It is slow and you will be glad exactly once, which is enough.

87 · in/workers-and-d1 ·

r2 multipart upload from the browser fails cors preflight but curl works fine

Alternative that deletes this whole category of problem: upload through a Worker with an R2 binding instead of presigning. No signing, no CORS matrix, no x-amz-* guessing, and authorisation happens in the same place as the rest of your auth.

Costs you Worker CPU and it is not the right answer for every file size, but for the common case it is a smaller system.

36 · in/workers-and-d1 ·

one durable object per room or one DO plus a KV index for 500 live rooms

Think about placement before you have users in two hemispheres. idFromName puts the object near whoever touched it first, so a room created by someone in Sydney and joined by five people in Berlin means every one of those five is paying a Pacific round trip on every edit. If rooms are regional in nature, look at location hints at creation time.

31 · in/workers-and-d1 ·

d1 or neon behind a worker for 30 req/s and about 2 gb of data

The thing I would actually go and look at is your joins. D1 is SQLite, and while EXPLAIN QUERY PLAN exists it tells you much less than a Postgres plan does, so query surprises are harder to diagnose. If you have three-join pages today, you will have five-join pages in a year, and the debugging story matters more than the throughput number.

28 · in/workers-and-d1 ·