HN user

ekimekim

2,337 karma

Programmer, gamer, chaser of random interests. github: https://github.com/ekimekim

Posts8
Comments571
View on HN

Suddenly turning off services when the billing cap is reached is a big reliability risk for customers.

And yet this is exactly what happens if you exceed a quota limit, which aren't always well signposted and sometimes require days to get the limit raised. I've experienced multiple hours-long outages due to hitting GCP quotas we didn't know existed.

To provide a concrete example, this bit me in a typescript codebase:

    type Option<T> = T | undefined

    function f<T>(value: T): Option<T> { ... }

    let thing: string | undefined = undefined;
    let result = f(thing);
Now imagine the definition of Option is in some library or other file and you don't realize how it works. You are thinking of the Option as its own structure and expect f to return Option<string | undefined>. But Option<string | undefined> = string | undefined | undefined = string | undefined = Option<string>.

The mistake here is in how Option is defined, but it's a footgun you need to be aware of.

Twitch puts the ads directly in the HLS stream, but as seperate segments from the content (a HLS stream is made of many small video files, on twitch they're about 2s long). They're trivial to recognize and filter out (they're actually explicitly tagged as ad segments) but it still won't serve you the actual stream you were trying to watch - the ad segments override it. The best you can do is just block until the first non-ad segment arrives.

In a weird way it kinda reminds me of `exec` in sh (which replaces the current process instead of creating a child process). Practically, there's little difference between these two scripts:

    #!/bin/sh
    foo
    bar
vs
    #!/bin/sh
    foo
    exec bar
And you could perhaps imagine a shell that does "tail process elimination" to automatically perform the latter when you write the former.

But the distinction can be important due to a variety of side effects and if you could only achieve it through carefully following a pattern that the shell might or might not recognize, that would be very limiting.

Sure, but the assumption here is that primary and backup (edit: probably, ie. they're not coordinating this) aren't going to the bathroom at the same time. It's also based on the idea that alerts are extremely rare to begin with. If you're expecting at least one page every rotation, that's way, way too often. Step one is to get alerts under control, step two is a sane on-call rotation.

When I'm in charge of an on-call rotation I always try to make it very clear that this is not the expectation.

In my preferred model of on-call, you have a primary, then after 5min an escalation to secondary, then after 5min an escalation to something drastic (sometimes "everyone", sometimes a manager).

The expectation is that most of the time you should be able to respond within 5 minutes, but if you can't then that's what the secondary role is for - to catch you. This means it's perfectly acceptable to go for a run, go to a movie, etc.

You relax the responsibility on the individual and let a sensible amount of redundancy solve the problem instead. Everyone is less stressed, and sure you get the occasional 5min delay in response but I'm willing to bet that the overall MTTR is lower since people are well rested and happier to be on call to begin with.

I wonder why that C file which maps a more abstract Rust-friendly C-API on top of the existing API can't live inside the Rust directory and build structure

This is more or less what the RfL folks are asking for - they have a Rust API to be used by other Rust code, which uses the existing C API, and are promising to maintain that API themselves. It lives in the Rust "directory".

The C maintainer is rejecting this, seemingly because his goal isn't to find a compromise that works but to completely block the project.

ownership moves to a new not-for-profit entity based somewhere in Europe, with the exact location still to be finalized. The organization is currently headquartered in Germany, where it was a nonprofit until its charitable status was stripped last year.

So it sounds like Mastodon was run by a non-profit, but the non-profit ran afoul of some legal issues, and they're now creating a fixed version? This seems to be administrative details, not news.

Curl-Impersonate 2 years ago

In most cases this is just based on user agent. It's widespread enough that I just habitually tell requests not to set a User Agent at all (these aren't blocked, but if the UA contains "python" it is).

I disagree that strategic voting as a downside outweighs the downsides of RCV or FPTP - especially when FPTP itself is susceptible to strategic voting, too.

To clarify, I never intended that as a defence of FPTP. It's awful and I'll take any of the systems being discussed here over it. It was a statement specifically towards IRV over score/approval.

Being vulnerable to strategic voting is a huge downside that outweighs other considerations.

As the article mentions, in the real world score voting would just be approval voting where you put a max score on some choices and 0 on others.

And in approval voting you need to think about how others will vote and pick your cutoff point based on who you think has a chance - do you vote "yes" for the center-right party to avoid the hard right party getting in? Or do you vote "no" to help the center-left party beat the center-right party? (swap those directions to personal preference)

RCV isn't perfect, but in all but the smallest elections there's really no practical strategic voting considerations. You just state your true preference order.

Of course, I'll take any of them over FPTP.

That's interesting. To me stack traces + default pass up the stack are the distinguishing features of exceptions.

Suppose we had a version of the ? operator that automatically appended a call stack to the error value returned. Are you saying that that's not "an exception" because I still need to write ? after each falliable function? Or because it's still part of the return type? Or is it specifically only an exception if it works via stack unwinding?

Result is for expected domain failures. Panics are for programmer errors and unrecoverable constraint violations.

The problem is that "unrecoverable constraint violations" happen a lot in practice when you're dealing with filesystems, networking...anything that isn't pure computation.

Suppose I have a function that calls other functions that themselves make 3 database queries, two HTTP requests, and reads/writes from a cache directory. It considers all of them (except perhaps the caching) unrecoverable in the context of that function. What should it do?

I see three reasonable options:

(1). return a simple error type saying "Networking failure", "IO Error", etc if any of those fail

(2). return a complex error type that exposes the internal details of all the different things it's doing and which one failed and why

(3). panic if any of them fail

I would argue that (1) is unfit for purpose as you have no idea what's actually going wrong.

And (3) is currently very heavily discouraged, though I think if I'm understanding your argument right it probably makes the most sense. However it leaves your top-level function in the awkward position of needing to make that panic part of its API contract, without the type system to help. It's also highly limiting because the caller now can't distinugish between programmer errors and possibly-transient environmental conditions like a service outage.

(2) is what I'd expect to see in practice right now, and that's what leads to these automatic stack traces, etc. But none of these feel like good options. Ideally I'd want something that is:

- Debuggable (like (2) and (3))

- Part of the type system (like (1) and (2))

- Still allows introspection by the caller (like (1) and (2))

- Doesn't require a ton of boilerplate at each level (like (3), and possibly (1))

(edited for formatting)

Ok, so the original idea of Result<T, Error> was that you have to consider and handle the error at each place.

But then people realised that 99% of the time you just want to handle the error by passing it upwards, and so ? was invented.

But then people realised that this loses context of where the error occured, so now we're inventing call stacks.

So it seems that what people actually want is errors that by default get transferred to their caller and by default show the call stack where they occured. And we have a name for that...exceptions.

It seems that what we're converging towards is really not all that different from checked exceptions, just where the error type is an enum of possible errors (which can be non-exhaustive) instead of a list of possible exception types (which IIUC was the main problem with java's checked exceptions).

I've noticed this effect pretty often, as one high-profile article leads people to go looking at the author's other work and then submit it.

There are two important differences between corporations (even non-for-profit ones) and the government.

The first is that citizens have a say in who governs them. If the government does something they don't like, they can (at least in theory) vote for someone who will change it.

The second is that in most legal systems, there are differences in what governments are allowed to do and what corporations are allowed to do. For example, a government may be required to provide services to all citizens, whereas a corporation has the right to not serve certain people (such as "people who don't own a smartphone").

These differences aren't insurmountable, but it's something to keep in mind.

There are two concepts which are being used interchangably here.

The first is JSON as a data encoding, ie. the particular syntax involving braces and quotes and commas and string escapes.

The second is JSON as a data type, ie. a value which may be a string, number, bool, null, array of such values, or map from string to such values. The JSON data type is the set of values which can be represented by the JSON data encoding.

The article describes an optimized storage format for storing values which have the JSON data type. It is not related to JSON the data encoding, except in that it allows input and output using that encoding.

This is the same thing as postgres' JSONB type, which is also an optimized storage format for values of the JSON data type (internally it uses a binary representation).

Yes! But your lookup table will need 2^N bits for a function with N inputs. In this way you can easily enumerate all possible functions from N bits to 1 bit.

As a fun exercise, you can do this for all 2-bit -> 1-bit functions. There's only 16 of them, and most of them have very well known names like "and" (LUT 1000) or "xor" (LUT 0110). Some of them don't depend on some of the inputs (eg. LUT 1100 / 1010 which is "return A" and "return B" respectively) or even any of them (eg. LUT 0000 which always returns 0).

The other problem with not setting limits is that it's very easy to use more than your requests routinely, and you won't know that you're misconfigured until the one day you have a noisy neighbor and you only get what you asked for.

Monitoring helps, but requires some nuance. For example, your average CPU might look fine at 50%, but in truth you're using 200% for 500ms followed by 0% for 500ms, and when CPU is scarce your latency unexpectedly doubles.

While it doesn't eliminate it entirely (as you rightly point out), enforcing limits even when there's excess CPU available will mostly ensure that your performance doesn't suddenly change due to outside factors, which IMO is more valuable than having higher performance most-but-not-all of the time.