HNHacker News
TopNewBestAskShowJobs

Diggsey

2,823 karma · joined October 6, 2014

submissionscomments
Diggsey··on A PostgreSQL planner semi-join gotcha with CTE, LIMIT, and RETURNING
FWIW, I tested this locally (using `DELETE FROM example WHERE id = RANDOM(1,100)`) and it did delete multiple rows in some cases, even without a subquery.
Diggsey··on A PostgreSQL planner semi-join gotcha with CTE, LIMIT, and RETURNING
Why would it only be run once? The WHERE condition of the outer query is run multiple times: once for each row, so of course it can return TRUE multiple times.

For example:

    DELETE FROM example WHERE id = (SELECT RANDOM(0, 100) FROM other_table LIMIT 1)
This could delete multiple rows in principle, since there may be multiple rows where the `=` expression is true.
Diggsey··on A PostgreSQL planner semi-join gotcha with CTE, LIMIT, and RETURNING
I think your logic is flawed (even if PostgreSQL may behave this way in practice).

First, let's make sure we're talking about the same two examples.

A:

    DELETE FROM task_queue
    WHERE id = (
      SELECT id FROM task_queue
      WHERE queue_group_id = 15
      LIMIT 1
      FOR UPDATE SKIP LOCKED
    )
B:

    DELETE FROM task_queue
    WHERE id IN (
      SELECT id FROM task_queue
      WHERE queue_group_id = 15
      LIMIT 1
      FOR UPDATE SKIP LOCKED
    )
You seem to be saying that B may exhibit the problem whilst A does not. (ie. there is a different between using `=` vs `IN`). I would like to see some documentation justifying that.

Here's my logic:

- In both cases, the outer query is run once.

- In both cases the outer WHERE clause is evaluated for each row in `task_queue`.

- In both cases, it is up to the optimizer whether the result of the subquery is materialized or not.

- In both cases, if the subquery is not materialized, multiple rows in the outer query may match the condition.

In practice, it may be that the optimizer always materializes uncorrelated subqueries on the RHS of an `=` expression. My contention is whether that is a formal guarantee.

Diggsey··on A PostgreSQL planner semi-join gotcha with CTE, LIMIT, and RETURNING
Based on my understanding, your comment is the most accurate in this thread.

However, I think it's telling that almost every single comment here has a different understanding of how PostgreSQL works, and nobody can link to any documentation that conclusively addresses this case. I think that indicates that this is a failing if not of implementation, then of documentation.

For example: another comment says that `=` forces PostgreSQL to evaluate the subquery exactly once. I couldn't find any justification for that statement. Is it guaranteed? I don't know.

Another comment says "if only the subquery was ordered then the result would be consistent", but AFAIK, this is not true in the presence of "SKIP LOCKED".

I think the problem stems from the fact that the SQL standard is written from an academic point of view: the ordering of execution of subqueries is not defined because it is irrelevant within the relational algebra, so optimizations which change the execution order are just that: optimizations, with no observable side effects.

Real database systems do not actually have the nice algebraic properties of the relational algebra... The same subquery may give different results over multiple executions, etc. Given this, it's important that the database system give sufficient and sufficiently clear guarantees such that they can still be used correctly, and so that the optimizations implemented by the system can be justified.

Diggsey··on We fell out of love with Next.js and back in love with Ruby on Rails
> Every webapp built with something other than GraphQL ends up with an ad hoc, informally-specified, bug-ridden, slow implementation of half of GraphQL.

Not remotely true. There are plenty of web apps that work just fine with a standard fixed set of API endpoints with minimal if any customization of responses. Not to mention the web apps that don't have any client-side logic at all...

GraphQL solves a problem that doesn't exist for most people, and creates a ton of new problems in its place.

The value of GraphQL is also its downfall. The flexibility it offers to the client greatly complicates the backend, and makes it next to impossible to protect against DoS attacks effectively, or even to understand app performanmce. Every major implementation of GraphQL I've seen has pretty serious flaws deriving from this complexity, to the point that GraphQL APIs are more buggy than simpler fixed APIs.

With most web apps having their front-end and back-end developed in concert, there's simply no need for this flexibility. Just have the backend provide the APIs that the front-end actually needs. If those needs change, also change the backend. When that kind of change is too hard or expensive to do, it's an organisational failing, not a technical one.

Sure, some use-cases might warrant the flexibility that GraphQL uses. A book tracking app does not.

Diggsey··on Zig's new LinkedList API (it's time to learn fieldParentPtr)
If you just mean using array indexes instead of pointers, that can work in some cases, but can run into concurrency issues when the container needs to be resized. With pointers to nodes, new nodes can be allocated and old nodes freed on a separate thread without interfering with the performance sensitive thread, whereas resizing an array requires all access to stop while the backing memory is re-allocated.

I don't think you mean this, but elaborating on why a non-intrusive linked list is not very useful, that's because it takes linear time to find an object in a non-intrusive linked list, but constant time in an intrusive linked list.

For example:

    struct Node {
        Node* prev_by_last_update;
        Node* next_by_last_update;
        Node* prev_with_owner;
        Node* next_with_owner;
        Payload data;
    }
This intrusive structure allows reassigning and updating nodes, whilst being able to efficiently find eg. the next most recently updated node, or to iterate over nodes with the same owner.
Diggsey··on Zig's new LinkedList API (it's time to learn fieldParentPtr)
They are definitely more error-prone usage patterns, and coming from Rust the amount of hidden unsafety is quite unsettling (in addition to the obvious potential for user errors, depending on the aliasing model of your language/compiler, which I suspect Zig hasn't figured out yet, this parent pointer trick could even lead to miscompilations...)

Having said that, intrusive linked lists are quite an important data structure in code which is required to be allocation-free. Kernel code, signal handlers and code which requires consistent performance (eg. audio processing) need to be able to move objects between containers or have objects in multiple containers without allocating.

Diggsey··on I want a good parallel computer
> I worked on the Cell processor and I can tell you it was a nightmare. It demanded an unrealistic amount of micromanagement and gave developers rope to hang themselves with.

So the designers of the Cell processor made some mistakes and therefore the entire concept is bunk? Because you've seen a concept done badly, you can't imagine it done well?

To be clear, I'm not criticising those designers, they probably did a great job with what they had, but technology has moved on a long way from then... The theoretical foundations for memory models, etc. are much more advanced. We've figured out how to design languages to be memory safe without significantly compromising on performance or usability. We have decades of tooling for running and debugging programs on GPUs and we've figured out how to securely isolate "users" of the same GPU from each other. Programmers are as abstracted from the hardware as they've ever been with emulation of different architectures so fast that it's practical on most consumer hardware.

None of the things you mentioned are inherently at odds with more parallel computation. Whether something is a good idea can change. At one point in time electric cars were a bad idea. Decades of incremental improvements to battery and motor technology means they're now pretty practical. At one point landing and reusing a rocket was a bad idea. Then we had improvements to materials science, control systems, etc. that collectively changed the equation. You can't just apply the same old equation and come to the same conclusion.

Diggsey··on Sign in as anyone: Bypassing SAML SSO authentication with parser differentials
I recently had to implement SAML and this headline does not surprise me in the slightest.

The SAML spec itself is fairly reasonable, but is built upon XML signatures (and in turn, XML canonicalization) which are truly insane standards, if they can even be called such.

Only a committee could produce such a twisted and depraved specification, no single mind would be capable of holding and combining such contradictory ideas.

It would be so simple to just transmit signatures out-of-band and SAML would be a pleasure to implement.

Diggsey··on Ask HN: How did the internet discover my subdomain?
This is the worst take...

People consistently misuse the Swiss cheese security metaphor to justify putting multiple ineffective security barriers in place.

The holes in the cheese are supposed to represent unknown or very difficult to exploit flaws in your security layers, and that's why you ideally want multiple layers.

You can't just stack up multiple known to be broken layers and call something secure. The extra layers are inconvenient to users and readily bypassed by attackers by simply tackling them one at a time.

Security by obscurity is one such layer.

Diggsey··on The power of interning: making a time series database smaller
I have a Rust crate (ijson) which implements string interning and a generally much more efficient in-memory JSON representation. It probably doesn't compete with the author's specialized implementation, but if you just want a plug and play solution it's pretty good.
Diggsey··on Intel delays $28B Ohio chip fabs to 2030
> Now imagine that corporate tax rate was 40% instead. It completely changes the decision-making process.

Seems more like a question of degree. Dividends are also taxed as income so ~36% is already paid in tax depoending on the income of the shareholder. Increasing the corporate tax rate to 40% brings the effective tax rate to ~52%.

In my experience there's a more fundamental problem with large companies. In a small company, the best way to succeed as an individual (whatever position you have) is for the company as a whole to succeed. At a very large company, the best way to succeed is to be promoted up the ladder, whatever the cost. This effect is the worst at the levels just below the top: you have everything to lose and nothing to gain by the company being successful. It's far more effective to sabotage your peers and elevate yourself rather than work hard and increase the value of the company by a couple of percentage points.

The thing is, the people that have been there since the beginning still have the mindset of helping the company as a whole succeed, but after enough time and enough people have been rotated out, you're left with people at the top who only care about the politics. To them the company is simply a fixture - it existed before them and will continue to exist regardless of what they do.

Diggsey··on Torvalds: You can avoid Rust as a C maintainer, but you can't interfere with it
I don't think that's accurate. It was adding Rust DMA code that was to be shared between Rust drivers that was the spark. The C code was unchanged AFAIK.
Diggsey··on Announcing Rust 1.85.0 and Rust 2024
Huge! Async function traits resolve one of the biggest pain points for async rust.
Diggsey··on Caddy – The Ultimate Server with Automatic HTTPS
I haven't used Caddy but it's config format can't be worse than nginx's. Some crazy gotchas with the way nginx config works...
Diggsey··on We were wrong about GPUs
I don't agree, I think you're just describing two sides of the same coin.

As a software developer I want strong abstractions without bloat.

LLMs are so successful in part because they are a really strong abstraction. You feed in text and you get back text. Depending on the model and other parameters your results may be better or worse, but changing from eg. Claude to ChatGPT is as simple as swapping out one request with another.

If what I want is to run AI tasks, then GPUs are a poor abstraction. It's very complicated (as Fly have discovered) to share them securely. The amount of GPU you need could vary dramatically. You need to worry about drivers. You need to worry about all kinds of things. There is very little bloat to the ChatGPT-style abstraction, because the network overhead is a negligable part of the overall cost.

If I say I don't want magic, what I really mean is that I don't trust the strength of the abstraction that is being offered. For example, when a distributed SQL database claims to be PostgreSQL compatible, it might just mean it's wire compatible, so none of my existing queries will actually work. It might have all the same functions but be missing support for stored procedures. The transaction isolation might be a lie. It's not that these databases are bad, it's that "PostgreSQL as a whole" cannot serve as a strong abstraction boundary - the API surface is simply too large and complex, and too many implementation details are exposed.

It's the same reason people like containers: running your application on an existing system is a very poor abstraction. The API surface of a modern linux distro is huge, and includes everything from what libraries come pre-installed to the file-system layout. On the other hand the kernel API is (in comparison) small and stable, and so you can swap out either side without too much fear.

K8S can be a very good abstraction if you deploy a lot of services to multiple VMs and need a lot of control over how they are scaled up and down. If you're deploying a single container to a VM, it's massively bloated.

TLDR: Abstractions can be good and bad, both inherently, and depending on your use-case. Make the right choice based on your needs. Fly are probably correct that their GPU offering is a bad abstraction for many of their customer's needs.

Diggsey··on We are the "thin blue line" that is trying to keep the code high quality
Noone's asking the maintainers to make the project happen.

It is the maintainers job to determine whether a project should be pursued at all.

If a maintainer says "yes I'd be happy to accept feature X", and then simply rejects all PRs implementing X without giving feedback then they're a bad maintainer!

That's essentially what's happening here due to the fact that the kernel maintainers have not come to a coherent decision regarding R4L.

Diggsey··on We are the "thin blue line" that is trying to keep the code high quality
He's not wrong, but he hasn't addressed the problem:

Some maintainers are rejecting changes as a way to block a project they disagree with - ie. there is no path forward for the contributor. I wouldn't assume this normally, but they're not hiding this fact, it's self proclaimed.

On the other hand, Linus has been largely in favour of the R4L project, and gave the green light for it to go ahead.

The Linux kernel maintainers need to figure out among themselves whether they want to allow Rust to be introduced or not, and if so under what constraints. If they can't come to an agreement, they're wasting everyone's time.

Once that happens, either the project is canned, or people can stop arguing over if these changes should be upstreamed, and start arguing over how instead, and that's a lot more productive.

Diggsey··on Durable execution should be lightweight
The example is overly simplified. It glosses over many of the subtle-but-important aspects of durable execution.

For example:

- Steps should be small but fallible operations - eg. sending a request to an external service. You generally want to tailor the retry logic on steps to the specific task they are doing. Doing too much in a step can increase failure rates or cause other problems due to the at-least-once behaviour of steps.

- The article makes a big deal of "Hello" being printed 5 times in the event of a crash, but durable execution doesn't guarantee this! You can never have exactly-once guarantees of side-effectful functions like this without cooperation from the other side. For example, if the external service supports idempotency via request IDs then you can generate an ID in a separate step and then use that in your request to get exactly-once behaviour. However, most services don't offer this. Crashes during a step will cause the step to re-run, so durable execution only gives you at least once behaviour.

- Triggering the workflow itself is a point of failure. In the example, the workflow decorator generates an ID for the workflow internally, but for triggering a workflow exactly once the workflow ID needs to be externally generated.

- The solution is light-weight in terms of infrastructure, but not at all lightweight in terms of performance.

Diggsey··on Cheating Is All You Need
Using AI is like trying to build a product with only extremely junior devs. You can get something working fairly quickly but sooner rather than later it's going to collapse in on itself and there won't be anyone around capable of fixing it. Moving past that requires someone with more experience to figure out what they did and eventually fix it, which ends up being far more expensive than correcting things along the way.

The error rate needs to come down significantly from where it's currently at, and we'd also need AIs that cover each others weaknesses - at the moment you can't just run another AI to fix the 20% of errors the first one generated because they tend to have overlapping blind spots.

Having said all that, I think AI is hugely useful as a programming tool. Here are the places I've got most value:

- Better IDE auto-complete.

- Repetetive re-factors that are beyond what's possible to do with a regex. For example, replaing one function with another whose usage is subtly different. LLMs can crack out those changes across a codebase and it's easy to review.

- Summarizing and extracting intent from dense technical content. I'm often implementing some part of a web standard, and being able to quickly ask an AI questions, and have it give references back to specific parts of the standard is super useful.

- Bootstrapping tests. The hardest part of writing tests in my experience is writing the first one - there's always a bit of boilerplate that's annoying to write. The AI is pretty good at generating a smoke test and then you can use that a basis to do proper test coverage.

I'm sure there are more. The point is that it's currently incredibly valuable as a tool, but it's not yet capable of being an agent.

Diggsey··on Pushing the whole company into the past on purpose
You seem to be very confused. Even in countries like the UK with weaker freedom-of-speech laws, there are specific exceptions to slander law for insults.
Diggsey··on Pushing the whole company into the past on purpose
The "right to not be insulted" is literally censorship.
Diggsey··on More telcos confirm Salt Typhoon breaches as White House weighs in
Companies already pay for cyber insurance because they don't want to take on this risk themselves.

In principle the insurance company then dictates security requirements back to the company in order to keep the premiums manageable.

However, in practice the insurance company has no deep understanding of the company and so the security requirements are blunt and ineffective at preventing breaches. They are very effective at covering the asses of the decision makers though... "we tried: look we implemented all these policies and bought this security software and installed it on our machines! Nobody could possibly have prevented such an advanced attack that bypassed all these precautions!"

Another problem is that often the IT at large enterprises is functionally incompetent. Even when the individual people are smart and incentivised (which is no guarantee) the entire department is steeped in legacy ways of doing things and caught between petty power struggles of executives. You can't fix that with financial incentives because most of these companies would go bankrupt before figuring out how to change.

I don't see things improving unless someone spoon-feeds these companies solutions to these problems in a low risk (ie. nobody's going to get fired over implementing them) way.

Diggsey··on Safe relational database queries using the Rust type system
In this case you just set the column value to the existing value...
Diggsey··on Safe relational database queries using the Rust type system
That has not been my experience. SQL is a fully fledged programming language so there's very little that you can't express this way.

What are you doing that requires dynamically building a query?

Diggsey··on Safe relational database queries using the Rust type system
FWIW, you very rarely need to make the query itself dynamic. After all, SQL alone is powerful enough to do conditionals.

For example:

    SELECT * FROM example
    WHERE ($1 IS NULL OR field1 = $1)
    AND ($2 IS NULL OR field2 = $2)
    ...

When you supply the parameters to this prepared statement, the constant conditions should be optimized away by the query planner if you're using a decent database.
Diggsey··on Safe relational database queries using the Rust type system
SQL, while not great, does have some advantages that are hard to beat:

- Everyone knows at least some basic SQL, even non-technical users have often encountered it in some form.

- The documentation for eg. PostgreSQL is for SQL, so if you write queries in anything else you have to mentally translate back and forth, and so you need to know SQL anyway.

- Any external tools you use to interact with the database will use SQL.

- Changing the queries doesn't require an expensive compilation step - `sqlx` gets the best of both worlds in this respect, in that it is able to type-check the parameters and rely on the database itself to validate the query, so you don't end up with a ton of type-system shenanigans that increase compilation times.

Maybe for a brand new database, a better query language could win out, but having used sqlx I can't imagine going back to a query-builder style interface for existing SQL databases.

Diggsey··on Charset="WTF-8"
I thought this was https://simonsapin.github.io/wtf-8/
Diggsey··on My negative views on Rust (2023)
> Maybe not yet, but it is heading in that direction;

It's definitely getting more complex, but C++ has a huge lead haha. C++ is like a fractal in that you can look at almost any feature closer and closer to reveal more and more complexity, and there are a lot of features... Here's a page on just one dark corner of the language: https://isocpp.org/wiki/faq/pointers-to-members and it interacts in interesting ways with all the other corners (like virtual vs non-virtual inheritance) in fun and exciting ways...

Also, there are far more ways to cause UB in C++. Rust has a big lead on formalizing what constitutes UB, and even those rules you only need to learn if you are using "unsafe", whilst in C++ you don't have that luxury.

Diggsey··on QUIC is not quick enough over fast internet
Historically there have been too many constraints on the Linux syscall interface:

- Performance

- Stability

- Convenience

- Security

This differs from eg. Windows because on Windows the stable interface to the OS is in user-space, not tied to the syscall boundary. This has resulted in unfortunate compromises in the design of various pieces of OS functionality.

Thankfully things like futex and io-uring have dropped the "convenience" constraint from the syscall itself and moved it into user-space. Convenience is still important, but it doesn't need to be a constraint at the lowest level, and shouldn't compromise the other ideals.

← PreviousPage 2 of 16Next →