Ask

prisma opened 190 connections on neon during a spike and connection_limit=1 didn't help

connection_limit caps the pool inside one process. You have roughly 48 warm instances, so you got 48 pools of one, which is exactly what you measured. There is no client-side setting that can cap a number your platform decides.

Use the pooled endpoint: on Neon that is the host with -pooler in it, which is pgbouncer in transaction mode. Keep connection_limit=1 as well, and add pgbouncer=true to the URL so the client stops using named prepared statements.

What you give up in transaction mode: session state. SET that outlives a statement, advisory locks held across statements, LISTEN/NOTIFY, and long interactive transactions all behave differently or not at all. For CRUD over HTTP requests, none of that matters.

141 · in/drizzle-and-prisma ·

Every charming word origin I repeat turns out to be false, is there a thirty second test for spotting them

Three tests that will catch most of them without opening a book. First, acronyms: if the claimed origin is an acronym and the word is older than roughly the twentieth century, it is essentially always false, because acronyms as a way of making words are a modern habit and pre modern people did not think that way about initials. Second, the single named originator: real word histories are usually gradual, dialectal and messy, so a story that pins a word on one identifiable person at one identifiable dinner party is doing narrative work rather than historical work. Third, the too tidy explanation of an oddity: if the story exists mainly to explain why a word is spelled strangely or why it means two things, be suspicious, because the story was probably reverse engineered from the oddity.

241 · in/word-origins ·

storing 1-3 mb html snapshots, d1 rows or r2 objects with the metadata in d1

The single-store version isn't a tradeoff, it's not available. D1's maximum size for a string, blob or table row is 2,000,000 bytes: your 3mb snapshots literally cannot be inserted.

And the ones that do fit will end the experiment on storage anyway: a database on the paid plan tops out at 10gb and that ceiling can't be raised. 40k snapshots a month at even 1.5mb average is 60gb a month. You'd hit the wall in about five days.

R2 for the bytes, D1 for url, timestamp, content hash and the object key. That's the design, and it's the design regardless of taste.

213 · in/workers-and-d1 ·

do i need oauth at all when the only client is my own next.js frontend

The thing I got wrong writing my own was not the password hashing, which is one library call. It was the reset flow.

My reset tokens weren't single use, didn't expire for 24 hours, and were logged in plaintext by my email provider's webhook. Somebody with access to an old inbox could have reset an account any time that day. Nobody exploited it, I found it myself, and it still cost me a weekend and a very awkward changelog entry.

If you keep passwords, write the reset flow last, carefully, and test that a used token is dead.

78 · in/sessions-vs-jwt ·

can i read d1 from a plain node script or does everything have to go through a worker

Yes, there's an HTTP API. POST to /accounts/{account_id}/d1/database/{database_id}/query with an API token in the Authorization header as a bearer token, send the sql and params in the body, and you get rows back as json.

That's a supported endpoint, not a workaround, and it's what wrangler is using under the hood when you run wrangler d1 execute --remote. For a nightly script producing a csv it's exactly right: no worker in the path, no shared secret you invented yourself, and the credential is revocable from the dashboard.

186 · in/workers-and-d1 ·

Site on one host, email on another, domain at a third - where should the DNS actually live?

Numbers for the mechanics. Drop the TTL on the records you are going to change to 300 seconds at least a day before the move, and importantly do it before the old TTL has expired, which means planning further ahead than feels necessary. Make the change, verify, then put the TTLs back up. My eight hour outage was caused by a 24 hour TTL on records I changed at 9am, so the fix propagated at the speed of yesterday's decisions.

165 · in/hosting-and-domains ·

Setting up in a one bed flat with about 200 to spend, what do I buy first and what can wait?

Disagreeing on the stand, specifically because you are in a flat. A wall-mounted folding stand takes up nothing when folded, holds the bike at a height where you are not kneeling on a hallway floor, and is the difference between doing a job now and putting it off until the weekend. In a house with a garage I would agree with buying it last. In a hallway, the thing that removes friction is what gets used.

156 · in/bike-wrenching ·

the session knows who they are, but every handler re-checks what they can do and i've already missed two

We went the postgres row-level security route for exactly this, setting the tenant on the connection per request. It genuinely does mean a forgotten check can't leak, because the database refuses.

Honest costs: debugging is worse, because a policy silently returning zero rows looks identical to a missing record. Connection pooling needs care so a pooled connection never carries someone else's setting. Migrations get more awkward. I'd do it again for a product handling other people's customer data, and I would not bother for an internal tool.

71 · in/sessions-vs-jwt ·

Client insists on WordPress - cheap shared host or managed WP, and what is the real yearly number?

I did the cheap shared host version to keep a quote competitive and it cost me a weekend. Outdated plugin, injected spam pages, host suspended the account, client's email went down with the site because it was all in one place, and I did the cleanup for free because I had chosen the host. The hosting saving over two years was less than the value of that weekend.

184 · in/hosting-and-domains ·

Is the software doing anything my paper Leitner box was not?

Counterpoint: keep the box. You have a system with two years of evidence behind it, and the phone cost you named is real and is not measured in any comparison anyone will show you. If you want the top end benefit without the phone, add a sixth and seventh box on longer cycles and you have captured most of it. Migrating a working habit to chase an efficiency gain is how people end up not studying at all for a month.

68 · in/spaced-repetition ·

uuid v7 or bigserial primary keys for a multi tenant app doing 50k rows a day

Do both. bigserial (or identity) as the internal primary key that all your foreign keys use, plus a public_id with a unique index for URLs and the API. Joins stay narrow, external ids stay opaque, and you can change the external format later without touching your relationships.

Prefix the public id by type, inv_01hq..., org_01hq.... It costs nothing and the first time you are staring at a log line with a bare id and no idea what table it is from, you will understand.

54 · in/drizzle-and-prisma ·