HNHacker News
TopNewBestAskShowJobs

sa46

1,425 karma · joined December 2, 2015

Building AI for distributors, contractors, and manufacturers.

Paratrooper to programmer.

http://github.com/jschaf

hn@schafer.dev

submissionscomments
sa46··on I Fucking Hate Jira (2022)
I’ve been pretty happy with Slab. Straightforward shared wiki with a good editor, governance, and integrations. https://slab.com/

I tried using README files in the repo but there’s far too much friction to get most folks to bother. Google Docs tend to disappear content due to a lack of structure.

sa46··on What's new in the Postgres 16 query planner
Related discussion

Why PostgreSQL doesn't have query hints

https://news.ycombinator.com/item?id=2179433 (60 comments, 2011)

The official stance from the Postgres wiki https://wiki.postgresql.org/wiki/OptimizerHintsDiscussion:

> We are not interested in implementing hints in the exact ways they are commonly implemented on other databases.

> Problems with existing Hint systems: Poor application code maintainability, Interference with upgrades, Encouraging bad DBA habits, Does not scale with data size

I don't fault their stance, but it's frustrating when Postgres picks a stupid plan and can't be convinced to do something reasonable.

sa46··on Post-mortem for last week's incident at Kagi
Oh man, last week we had a small outage. Database queries took much longer than normal. I just so happened to be doing ad-hoc querying on the same table at the same time.

“Luckily”, the problem was unrelated to my querying but two coincidences are proper scary.

sa46··on Benchmarking 20 programming languages on N-queens and matrix multiplication
Reliably benchmarking optimizing VMs like Java and JavaScript is a research problem.

See “Virtual machine warmup blows hot and cold”. The authors run a variety of benchmarks (with a pristine methodology) and find 43% of them provide “bad inconsistent” results.

Paper summary: https://blog.acolyer.org/2017/11/07/virtual-machine-warmup-b...

Paper: https://dl.acm.org/doi/pdf/10.1145/3133876

sa46··on How does Base32 (or any Base2^n) work exactly?
Seems like a compiler should be able to convert division to shifts and subtractions.

> u8 divmod 58 can be reduced to a u8->u16 multiply, a right shift, and three conditional subtractions; that's not great, but on a modern CPU it's a afterthought compared to the quadratic loop over the input size.

Same topic from 2018: https://news.ycombinator.com/item?id=18409344

sa46··on Show HN: Octopus – a directed acyclic graph for app development
I worked with a framework like that inside Google in Java. Was okay. You’d write a bunch of methods and the framework would wire up annotated method parameters and return types. The main benefit was improved tail latency since the framework would traverse the graph and run the methods with optimal concurrency.

Same downsides as runtime dependency injection frameworks like Guice—the framework would explode if it couldn’t connect the graph.

Some other upsides: you could decorate methods with all sorts of distributed goodness like retries and hedging. The approach also enable reusable methods that you snap into your server.

sa46··on Cyberattack on Kansas Courts Leaves Lawyers Filing 'Everything by Fax'
Using iOS private relay can cause this as well.
sa46··on Code Search at Google: Han-Wen and Zoekt
Do you have any cases studies or success stories for non-Google repos? I miss code search but I’m not sure how close Kythe is to code-search-in-a-box.
sa46··on River: A fast, robust job queue for Go and Postgres
I wrote our own little Go and Postgres job queue similar in spirit. Some tricks we used:

- Use FOR NO KEY UPDATE instead of FOR UPDATE so you don't block inserts into tables with a foreign key relationship with the job table. [1]

- We parallelize worker by tenant_id but process a single tenant sequentially. I didn't see anything in the docs about that use case; might be worth some design time.

[1]: https://www.migops.com/blog/select-for-update-and-its-behavi...

sa46··on Google paid $8B to make its apps default on Samsung phones
That is the definition of a hypocrite. Reasonable hypocrisy remains hypocrisy.

From Oxford Languages:

> the practice of claiming to have moral standards or beliefs to which one's own behavior does not conform

sa46··on What if we replace guns and bullets with bows and arrows? (2022)
Fun article if a little silly. As the author notes, the primary reason muskets started to overtake bows is because you can train shooting in five minutes where archery takes months.

The transition is a classic example of a disruptive innovation—-initial tech is worse along every dimension (range, damage, reliability, firing rate) except one, training time.

Some of the comparisons are wildly biased. The effective range of an M4 is 550m, much better than the M9’s 50m. A standard rifleman’s combat load is 180 rounds which is surely lighter than 180 arrows.

sa46··on Show HN: Light implementation of Event Sourcing using PostgreSQL as event store
Table renames don’t break anything.

Changing columns must be done to both tables in a transaction. Unit tests verify that the table columns match.

sa46··on Show HN: Light implementation of Event Sourcing using PostgreSQL as event store
I love temporal tables. I built an implementation in Postgres [1] in a few hundred lines of PLPGSQL. We've run the implementation for several years across a few hundred tables for an ERP product.

The core ideas of the implementation:

- The current table is a typical Postgres table. The past table's primary key is (curr_tbl.pk, asr), where the asr column is the valid period of the row using a domain on tstzrange.

- Create statement-based triggers on the current table to copy the after-image of modified rows to the past table. Statement-based triggers amortize function call overhead compared to row-base triggers (I think depesz compared the performance and found about a 10x difference, but I can't find the link).

- Copying the row after-image on update and delete to the past table is advantageous because inserting a new row has no overhead. The disadvantage is that it's harder to figure out when a row was deleted (requires checking for gaps in the past table).

Some pointers if you dive into uni-temporal tables in Postgres:

- Separate the past table and current table into different schemas. Most database users shouldn't modify the past tables. It's easier to grant permission by schema and it makes autocomplete nicer. By convention, we use erp.invoice and erppast.invoice.

- Use a domain type instead of tstzrange to blunt the sharp edges of tstzrange. See the temporal.period domain type in [1].

- Resist the temptation to query against the past table. Temporal joins are fabulously complex without first-class database support (like Oracle's AS OF).

- Optimize for the typical case: querying the current table. Our first temporal implementation used table inheritance consisting of three tables: a parent table, the current table, and a past table. Theoretically, the parent table lets you query data transparently across the parent and child tables. In practice, we didn't use the parent query capability at all. Having a parent table made code review harder by requiring the reviewer to check that the code queried the current table and not the parent table. It's easy enough and rare enough to query all versions of a row by using:

    SELECT * FROM current_table
    UNION ALL
    SELECT * FROM past_table
- Track the current and past tables in a metadata table. Use tests to check that columns don't drift between the current and past tables. See misaligned_cols.sql in [1] for an example.

- Unfortunately, you can't use declarative partitioning because the primary key of the past table differs from the current table. The past table must include the valid period column to differentiate it from other versions of the same row.

[1]: https://gist.github.com/jschaf/cd1c1a3c2a5897282929ee5e16f94...

sa46··on Announcing SurrealDB 1.0
This line in particular is a head-scratcher:

> Advanced inter-document relations and analysis. No JOINs. No pain. […] queries allow for multi-table, multi-depth document retrieval, efficiently in the database, without the use of complicated JOINs

That sounds like they have a networked database, not a relational database. That might be fine, but as I understand it, relational databases won because they offer more flexible access patterns and it’s easier to write correct queries.

sa46··on Why you should probably be using SQLite
Yep, I used that trick as well. The evolution we went through:

1. Each test runs all migrations on a fresh database. Each test spends 2.1 seconds on db setup.

2. Each test suite runs all migrations. Each test copies the template database from the test suite. Each suite takes 2.1 seconds to run migrations, but cloning a template database takes 300 ms.

3. Bazel caches the data dir and rebuilds it once for all test suites. Reduces the initial test suite setup from 2.1 seconds to 160 ms (to copy the datadir).

4. Each test in a suite uses clonefile to copy the datadir. Reduces db setup overhead from 300 ms (to copy a template database) to 80 ms (to clonefile a datadir).

Currently, most of our testing overhead is clonefile and cleaning up the datadir. I'm interested in a single file sqlite FS because I could be clever and use LD_PRELOAD to replace Postgres's file system operations with sqlite, avoiding most syscalls altogether.

sa46··on Why you should probably be using SQLite
I've tried--macOS doesn't make it easy. It's not a clear performance win since tmpfs with Postgres fsync=off is mostly memory-backed storage via the page cache already.

Another problem is that parallel tests can exhaust memory.

The slowest part of our tests is syscall overhead. A mostly empty Postgres data dir for a medium-sized database with a few hundred tables consists of 3k files. On my M1 macOS, it takes 120 ms to delete the entire data dir. Copying is cheaper at 80 ms using clonefile.

sa46··on Why you should probably be using SQLite
> What I do think would be awesome would be an embeddable Postgres library/binary that could use a single state file on your local filesystem for development

I wired Postgres up for our local dev. I don't believe in mocking the database, so all our tests and local dev run against a real Postgres instance.

The main tricks:

- Store the Postgres installation in a known location.

- Set the dynamic loader path to the installation lib dir, e.g., LD_PRELOAD.

- Don't run CI as root (or patch out Postgres' check for root)

- Create and cache the data directory based on the source code migrations.

- Use clone file to duplicate the cached data directory to give to individual tests.

One thing I'd like to pursue is to store the Postgres data dir in SQLite [1]. Then, I can reset the "file system" using SQL after each test instead of copying the entire datadir.

[1]: https://github.com/guardianproject/libsqlfs

sa46··on Unified versus Split Diff
What the author wants (and I do too) is a normal, full fledged editor that highlights the code under review. Bonus points for easy commenting.

You can sort of do it with IntelliJ with git blame annotations but it’s clunky. I don’t really care about the author’s commits; I care about the entire set of changes or the changes since my last review.

You can also sort of do it with IntelliJ’s pull request mode but it’s also clunky since it’s not a “real” editor and you lose highlighting if you jump to the source code.

sa46··on Google opens Falcon, a reliable low-latency hardware transport, to the ecosystem
How do networks manage the larger number of IPv6 addresses?

My cursory digging indicates that the secret sauce is to grant large IPv6 prefixes and delegate routing to the prefix. An informative-looking Reddit comment says there are 100k IPv6 prefixes (as of Oct. 2020), and each active route takes 1 KiB. [1]

So, IPv6 differs significantly from MAC addresses because you only need to track prefixes.

[1]: https://old.reddit.com/r/networking/comments/j9twgq/how_much...

sa46··on Add extra stuff to a “standard” encoding? Sure, why not
The conformance suite tests encode and decode for single messages; you wouldn’t run it for a custom framing implementation; The gRPC interop tests are closer to what you’d want.

Framing appears higher up the stack, as an RPC transport, or structured storage like the recordio format referenced by the author. The article sounds like the client expected application/protobuf but the server sent application/custom-protobuf-framing.

sa46··on Maybe Rust isn’t a good tool for massively concurrent, userspace software
Go cannot catch data races at compile time (like Rust) but can catch a subset of data races at run time with the race detector. Go provides imperfect, opt-in protection.
sa46··on Maybe Rust isn’t a good tool for massively concurrent, userspace software
Rust protects against data races, not race conditions.

https://doc.rust-lang.org/nomicon/races.html

sa46··on PgCompute: New PostgreSQL Client-Side Extension for Database Functions
Vulnerable to SQL injection since it’s wrapping string args with single. https://github.com/dmagda/pg-compute-node/blob/main/compute/...

Personally, I’d prefer to create plv8 functions out of band (in a migration) and call them explicitly using a normal DB connection. Much less to go wrong.

sa46··on Keeping Figma Fast: perf-testing the WASM editor
6 bookings per second at $200 per booking is $103M processed per day.

Each booking likely represents dozens to hundreds of requests. Then, for every visit resulting in a booking there’s probably hundreds of non-booking visits.

sa46··on Designing a new concurrent data structure
Oh neat, that’s what that’s called. I did something similar in ignorance. I needed a histogram metric with multiple lock-free writers and a single reader.

- create two ring buffers (left/right; I called them hot/cold)

- store the hot ring buffer in an atomic pointer

- single reader swaps hot and cold and waits for writers to finish

sa46··on Show HN: Cashe - A money library written in Ada
I use a hand-rolled Money class that’s an ergonomic wrapper around big.js, an arbitrary precision decimal library.

I think using a separate language is massive overkill.

sa46··on Structured logging with slog
You can reproduce that example with slog verbatim by changing zap to slog.

For example: https://pkg.go.dev/golang.org/x/exp/slog#example-Group

sa46··on Structured logging with slog
There is. See slog.LogValuer

https://pkg.go.dev/golang.org/x/exp/slog#LogValuer

sa46··on Software Engineering at Google (2020)
Of all the Google dev tools, I miss Critique the most. GitHub is terrible at giving enough context to efficiently review a PR on a second or third pass.

I think coupling commits with review progress was a mistake.

sa46··on Most promoted and blocked domains among Kagi Search users
Does “at least 20 users” imply the list of blocked domains is all blocked domains with at least 20 users?

If so, I would have expected a much longer tail.

← PreviousPage 4 of 15Next →