> I do wish the guide was expanded a bit with lots of specific examples and details... a lot of it is hand-wavy blurbs.
I appreciate the feedback; I'm usually someone who tends to go into way too much detail, so this was difficult to write - I tried to focus on the "mental model" of understanding Postgres rather than very nuanced specifics. I tried to link out to my favorite articles on a number of subjects, and the Postgres manual is quite good.
> in addition to minimizing locked records, make sure your locks are ordered deterministically across all queries (eg by id asc, always) or you’ll deadlock (but postgres has a really good deadlock detector so you’ll more likely just error out if you’re lucky)
This is really good advice, I should put this somewhere in the guide. To add to this, not only can you deadlock by not having a consistent `ORDER BY` when you're locking sets of rows, but you should also be careful of locking rows on tables in different orders. For example, even if you lock each row in a table with an ORDER BY and FOR UPDATE, if one tx locks `table_a` and then `table_b`, and the other locks `table_b` and then `table_a`, you'll deadlock. This is obvious in theory but exponentially harder to debug in practice, because you need to be globally aware of every table that a write touches - something that's bitten us in particular with certain extensions.
> learn about GIN (and GIST) indexes
We're just testing GIN for fast key-value lookups for JSONB columns, and the performance improvements have been really massive. Interestingly there was a large performance skew between AND vs OR on these key-value queries.
OP here, I appreciate the feedback! I tried to focus on things which could take down your database, so things like particularly slow reads and writes, autovacuum settings, reducing lock contention, particularly focused on cases that I've seen. There are lots of things that I left out which would belong in a general user guide.
We're heavy users of stored functions because we're (perhaps overly) reliant on Postgres triggers, which can improve performance by reducing network round-trips but are fairly risky because they're difficult to monitor and observe.
Hatchet | Full-Stack Engineer | NYC or SF or REMOTE (US and EU) | https://hatchet.run
Hey HN! I'm Alexander, one of the founders of Hatchet. Hatchet is an open-source platform for running background jobs at scale.
We're hiring engineers who are excited to build the next class of engineering primitives, starting with queues, background tasks and durable execution. We started in early 2024 after launching our distributed task queue (https://news.ycombinator.com/item?id=39643136).
Hatchet is currently used by thousands of engineers for all kinds of workloads: log ingestion pipelines, code review agents, video encoding, GPU scheduling, etc. Our target customer is fast-growing startups who have a strong need for background jobs system. These days, that tends to be AI companies, though we're general-purpose and not exclusively targeted for AI workloads.
Applying: email me at [email protected] and tell me about something impressive you've built, along with your CV and why you're interested in Hatchet.
Note that we're fully open-source, which you can check out here: https://github.com/hatchet-dev/hatchet (and if you have thoughts / opinions / questions about the codebase, please include those in your note!)
> Our analysis revealed the fork-to-star ratio as the strongest simple heuristic for identifying potential manipulation. The logic is straightforward: a star costs nothing and conveys no commitment. A fork means someone downloaded the code to use or modify it.
This is just clearly...incorrect? You can both modify code without forking it and most software is distributed via a registry or binary download, which also wouldn't be represented in forks. For most projects, the number of forks is a lossy signal for how busy the contributor ecosystem is, nothing else.
Candidly we're still trying to figure that out: all of the plumbing is there in the open source, but the actual implementation of writes to S3 are only on the cloud version. This is partially because we're loath to introduce additional dependencies, and partially because this job requires a decent amount of CPU and memory and would have to run separate from the Hatchet engine, which adds complexity to self-hosted setups. That said, we're aware of multi-TB self-hosted instances, and this would be really useful for them - so it's important that we can get this into the open source.
The payloads are time-partitioned (in either case) so we do drop them after the user-defined retention period.
Author here - I'm also generally skeptical of coding agents, but with the right problem domain and approach they can produce quality output when paired with humans. There was a point in time in the chess world where computer + human was stronger than computer or human alone. I think we're in that era for a handful of applications. Not for things like kernels, browsers, or databases.
> Besides, who is going to maintain that code?
I maintain the code. If Claude gets sunset tomorrow, I'll still be able to maintain and write it - I've already rewritten parts of it.
You could make the same argument for a team member leading a project that you've worked on. Is that code forever required to be maintained by one team member?
Previously the overhead of ensuring code quality when the development process was driven by Claude Code was greater than just writing the code myself. But that was different for this project.
Hi, thanks! To be clear, the demo there is merely a WASM-based Ghostty build which is rendering the TUI on a web page, just so people could try it out without needing to install anything. The actual TUI runs in your terminal. I'm guessing it's the WASM side of things causing the fans to spin, which you wouldn't see locally.
Hi everyone, I enjoyed building this TUI for myself and wanted to write down how I did it. I appreciate all the thoughts and feedback! The web app is our main investment, but I think there's a slice of developers who really like to interact with TUIs, so I'm going to keep working on it.
For the demo at https://tui.hatchet.run, to answer some messages asking about it: I built this with the fantastic ghostty-web project (https://github.com/coder/ghostty-web). It's been a while since I've used WASM for anything and this made it really easy. I deployed the demo across six Fly.io regions (hooray stateless apps) to try to minimize the impact of keystroke latency, but I imagine it's still felt by quite a few people.
You're right - I'll remove that now until we can get it more performant or drop it altogether. This wasn't something we caught during testing. I appreciate the feedback!
Drawing the boundary at high throughput, huge fan-out and ultra-low-latency is correct - I'd also add that MQs are often used for pub/sub and signaling.
MQs are heavily optimized for reducing E2E latency between publishers and consumers in a way that DE engines are not, since DE engines usually rely on an ACID compliant database. Under load I've seen an order of magnitude difference in enqueue times (low single-digit milliseconds for the MQ p95 vs 10ms p95 for Postgres commit times). And AMQP has a number of routing features built-in (i.e. different exchange types) that you won't see in DE engines.
Another way to think about it is that message queues usually provide an optional message durability layer alongside signaling and pub/sub. So if you need a very simple queue with retries _and_ you need pub/sub, I'd be eyeing an MQ (or a DE execution engine that supports basic pub/sub, like Hatchet).
Not harmful for now - "fork from checkpoint" would be perfectly fine for me at the moment. The main issue (as flagged in the post) is that setting up additional tooling can take a while!
In the longer term, docker is nice from a reproducibility + CI perspective, and a docker build is already something can easily work with and track in my system.
One thing I've heard but not verified with other sandboxed execution providers is that startup times for custom images can be quite slow, so it could be a potential differentiator given Fly's existing infra.
This is seriously cool - it's exactly the DX and API I've been waiting for from sandboxed execution providers.
I'd love to be able to configure the base image/VM in a way that doesn't bundle coding tools or anything else I don't need, and comes with some other binaries installed (I'm more interested in using this as an API for a sandbox use-case I have). Is there a way to do this at the moment / is this on the roadmap?
Another option would be configuring the sprite via checkpoint and then cloning the checkpoint from a base sprite, but I don't see this option anywhere either.
Oh no, haven't gotten reports about the website being slow, thanks for flagging! Which browser are you using?
Regarding Temporal, our goal is to create the best developer experience possible, and we started Hatchet because we felt that Temporal misses the mark (I used Temporal for years before starting Hatchet).
The primary difference is that we're not solely focused on durable workflows, we're a general-purpose background jobs platform which offers durable workflows as a feature. In our view there are a set of equally important primitives: tasks, events, streaming/pubsub, concurrency, priority, rate limiting, scheduling, and yes, durable workflows.
Tasks being the entrypoint to the platform, rather than immediately dealing with the overhead of durable workflows, generally makes Hatchet easier to adopt for engineering teams. I wrote a little more about how task queues relate to durable execution here: https://hatchet.run/blog/durable-execution
We've also invested quite heavily in platform features like logging, observability, alerting, and our UI which either aren't offered or are underdeveloped in Temporal.
But ultimately I'd encourage people to give both a try - we're both MIT licensed and can easily be run locally.
Hatchet | Founding Engineer | NYC or REMOTE (US and EU) | https://hatchet.run
Hey HN! I'm Alexander, one of the founders of Hatchet. Hatchet is an open-source platform for running background jobs at scale.
We're hiring engineers who are excited to build the next class of engineering primitives, starting with queues, background tasks and durable execution. We started in early 2024 after launching our distributed task queue (https://news.ycombinator.com/item?id=39643136).
Hatchet is currently used by thousands of engineers for all kinds of workloads: log ingestion pipelines, code review agents, video encoding, GPU scheduling, etc. Our target customer is fast-growing startups who have a strong need for background jobs system. These days, that tends to be AI companies, though we're general-purpose and not exclusively targeted for AI workloads.
(plus an interesting discussion in the comments of that post on how the query planner chose a certain row estimate in the specific case that Laurenz shared!)
The other thing I'll add is that we still haven't figured out:
1. An optimal ANALYZE schedule here on parent partitions; we're opting to over-analyze than under-analyze at the moment, because it seems like our query distribution might change quite often.
2. Whether double-partitioned tables (we have some tables partitioned by time series first, and an enum value second) need analyze on the intermediate tables, or whether the top-level parent and bottom-level child tables are enough. So far just the top-level and leaf tables seem good enough.
FWIW, I think the memory snapshotting idea isn't going to work for most stacks for a few different reasons, but to speak more broadly on API design for durable execution systems, I agree completely. One of the issues with Temporal and Hatchet in its current state is that it currently abstracts concepts that are essential for the developer to understand, like what it means for a workflow to be durable, while the developer is building the system. So you end up discovering a bunch of weird behaviors like "non-determinism error" when starting to test these systems without a good grasp of the fundamentals.
We're investing heavily in separating out some of these primitives that are separately useful and come together in a DE system: tasks, idempotency keys and workflow state (i.e. event history). I'm not sure exactly what this API will look like in its end state, but idempotency keys, durable tasks and event-based histories are independently useful. This is only true of the durable execution side of the Hatchet platform, though; I think our other primitives (task queues, streaming, concurrency, rate limiting, retries) are more widely used than our `durableTasks` feature because of this very problem you're describing.