Utformade ett funktionsbaserat dataskikt i PostgreSQL — 1 275 lagrade funktioner i 34 scheman — så att varje läsning och skrivning går via en funktion som databasen kan ge behörighet till, i stället för via en tabell.
Arbetet utfört: 2025
1,275 lagrade funktioner som vaktar varje läsning och skrivning
Situation. Affärslogik har en tendens att läcka. Lite hamnar i API:et, lite i någon SQL som en hanterare kör direkt i koden, och snart är samma regel skriven på två eller tre lite olika sätt och det finns ingenstans att peka och säga ”det här är vad systemet gör med sina data”. Det är så buggar och säkerhetshål kommer in.
Uppgift. Målet var ett enda hem för alltihop: varje läsning och skrivning skulle gå genom databasen, API:et skulle vara en tunn adapter som inte känner till affärsreglerna, och det hela skulle gå att låsa ordentligt.
Handling. Datalagret är byggt med funktionerna först. Schemat är uppdelat per domän — identity, organization, review, message, notification och 29 till, 34 totalt — och varje operation som appen kan utföra är en av 1 275 PostgreSQL‑funktioner som den anropar; det finns ingen direkt tabellåtkomst från Go över huvud taget. Sedan ser databasen till att det efterlevs. Rollen som API:et loggar in som, mariner, har EXECUTE på appens funktioner och USAGE på schemana och inget annat — ingen SELECT, ingen INSERT, inget sätt att röra en tabell direkt — vilket blir ungefär 4 000 explicita behörigheter i stället för en generell. Funktionerna körs som SECURITY DEFINER och ägs av en separat roll utan inloggning, function_owner, med en fast search_path, och superanvändarkontot är reserverat för migreringar och cron, långt från den körande appen.
Resultat. Logiken bor på ett ställe som faktiskt går att granska, API:et förblir tunt och tråkigt på det bra sättet, och åtkomstgränsen upprätthålls av själva Postgres i stället för av att alla kommer ihåg reglerna. Om API:et på något sätt komprometterades skulle det ändå inte kunna göra något som funktionerna inte tillåter. Med 1 275 funktioner kostar disciplinen på riktigt — ett nytt fält är en migrering och en funktionsändring, inte en rad i en SQL‑fråga — och den friktionen är priset för att gränsen håller.
Läs vidare: Implementerade en katalogstyrd djupsammanslagning av lagrade JSON‑inställningar som förhindrar krascher på grund av saknade nycklar när schemat utvecklas. Eller ta en titt på hela portföljen.
En del av en längre historik — och den finns i sin helhet här.
Varje post är skriven på samma sätt: situationen, uppgiften, vad vi gjorde och vad som förändrades.