HNHacker News
TopNewBestAskShowJobs

cakoose

1,103 karma · joined August 22, 2011

Work: https://anrok.com

[ my public key: https://keybase.io/cakoose; my proof: https://keybase.io/cakoose/sigs/gNz7dqjDBzs5anzP30jhCtr5bcogtlGjKLP2yghDN9I ]

submissionscomments
cakoose··on Website Fingerprinting on Early QUIC Traffic
This one seems pretty thorough: https://upcommons.upc.edu/bitstream/handle/2117/169283/TJM1d... (Chapter 5).

There's also a bunch from Google at various stages of QUIC development.

cakoose··on Website Fingerprinting on Early QUIC Traffic
The idea of certificates is built in to QUIC, but certificate authorities are the client software's responsibility. Client software can ignore certificate authorities entirely, just like with HTTPS.

How exactly does widespread adoption of QUIC destroy decentralization?

Is it any different from widespread adoption of HTTPS, e.g. with browsers becoming increasingly discouraging towards unencrypted HTTP?

cakoose··on An Unlikely Database Migration
> > Running an etcd cluster is much easier than running an HA PostgreSQL or MySQL config.

> What if you used one of the managed RDBMS services offered by the big cloud providers?

Yeah, AWS RDS "multi-AZ" does a good job of taking care of HA for you. (Google Cloud SQL's HA setup is extremely similar.) But you still get 1-2 minutes of full unavailability when hardware fails.

I haven't operated etcd in production myself, but I assume it does better because it's designed specifically for HA. You can't even run less than three nodes. (The etcd docs talk about election timeouts on the order of 1s, which is encouraging.)

For many use cases, 1-2 minutes of downtime is tolerable. But I can imagine situations where availability is paramount and you're willing to give up scale/performance/features to gain another 9.

cakoose··on PeerJS – Simple peer-to-peer with WebRTC
I don't think it's categorically wrong to say something is too simple. There are accepted terms for that: over-simplification, simplistic.

There's also the Einstein line: "Everything should be made as simple as possible, but no simpler."

I have no idea if PeerJS is actually too simple, but the "shape" of KaoruAoiShiho's statement seems fine.

cakoose··on Apple CPU tricks: memory reordering, JavaScript support, ref counting
Even if compiler-only ILP is doomed to fail, maybe VLIW optimists are just hoping something useful might come out of everything we've learned?

For example, compilers already model the CPU's pipeline and ILP capacity to inform instruction selection. VLIW designs are an attempt to make that cooperation more explicit, similar in spirit to branch/prefetch hints. Maybe there's still a way to do that well?

And re: microarchitectural changes in the next hardware rev: https://news.ycombinator.com/item?id=25237056

These ideas might ultimately fail, but they seem worth looking in to, at least.

cakoose··on Ok Google: please publish your DKIM secret keys
First, "innocent people can be framed" is not that simple.

Without non-repudiation, you don't automatically get to frame someone for whatever. You need to provide the usual (non-DKIM) evidence of whatever you're claiming.

And even with non-repudiation, you can still try and frame someone. Not having the DKIM signature might be suspicious in some circumstances, but it doesn't eliminate the possibility.

Second, "innocent" is not that simple.

I don't want my private communication to become public, or publicly verifiable. That doesn't mean I'm not "innocent". This is not a fringe concept: https://en.wikipedia.org/wiki/Nothing_to_hide_argument

"Give me six lines written by the most honest man in the world, and I will find enough in them to hang him." - Cardinal Richelieu

cakoose··on Ok Google: please publish your DKIM secret keys
> It simply offers an assurance that, at around the time of sending, a given email was mostly likely sent from the server that signed it. It can't prove _anything_ about who actually sent it, because it can't guarantee the ownership of the email account.

Not on it's own, but it's a critical step in this chain:

1. DKIM verifies that a message was sent by Gmail.

2. We assume Gmail is careful with its keys.

3. We assume Gmail doesn't forge addresses.

4. Find evidence that links me to that address.

Most people will readily grant #2 and #3. Now we just need #4, which can be easy.

No, it's not cryptographically verified end-to-end, but it's good enough to convince a court or to convince a respectable news organization to run a story.

cakoose··on Sum Types in Julia and Rust
What might be causing confusion is the difference between tagged unions and untagged unions.

A union type "A | B" means "a value of type A or a value of type B". Example:

    function f1(): String | Integer {
        if (rand()) {
            return "hello"
        } else {
            return 12
        }
    }

    function f2(x: String | Integer) {
        switch (typeof x) {
            case String: return "string: " + x
            case Integer: return "integer: " + x
        }
    }
The type "String | String" is exactly equivalent to "String".

A tagged union (aka sum type) "A + B" means "either a left value or a right value; if it's the left, it has type A, if it's the right it has type B".

    function g1(): String + Integer {
        if (rand()) {
            return Inl("hello")
        } else {
            return Inr("bye")
        }
    }

    function g2(x: String + String) {
        switch (x) {
            case Inl(s): return "left value: " + x
            case Inr(s): return "right value: " + x
        }
    }
The type "String + String" has one bit of additional information than just "String".
cakoose··on Solving Jepsen with OpenCensus Distributed Tracing
"Solving Jepsen" is kind of a misleading way to say "debugging issues found by Jepsen". The former made me expect some kind of general solution to distributed consistency.

> And ultimately, adding quality tests is the best you can do to improve your software.

Tests are great, but that's not "the best you can do". You can redesign things to be simpler, and there's even an example of that earlier in the article:

> By re-architecting the design, the code could be hugely simplified. So, I had rewritten the whole thing from scratch over the past few months.

> The rewritten, simplified codebase did fix a variety of Jepsen issues. The last major stronghold was the bank test.

A better design can make it easier to informally "prove" the absence of certain issues.

cakoose··on IBM Releases Fully Homomorphic Encryption Toolkit for macOS and iOS
Is it really that common? I often see HN posts calling for end-to-end encryption, but not homomorphic encryption.

(But even "just" end-to-end encryption is more difficult than many realize.)

cakoose··on Deno 1.0
When describing the input stream back-pressure problem:

> To mitigate this problem, a pause() method was added. This could solve the problem, but it required extra code; and since the flooding issue only presents itself when the process is very busy, many Node programs can be flooded with data. The result is a system with bad tail latency.

Are they saying that even with correct use of pause() there are still issues?

cakoose··on Deno 1.0
Is this different from how streams work in most other languages, e.g. Java, Go, Python?
cakoose··on Xor Filters: Faster and Smaller Than Bloom and Cuckoo Filters
Oh wow, I only read the paper's abstract and was fooled. Thanks for the correction.
cakoose··on Apple Faceshield
https://xkcd.com/1468/ "Worrying"
cakoose··on Xor Filters: Faster and Smaller Than Bloom and Cuckoo Filters
Xor filters require all the members of the set be provided up front.

Bloom filters allow adding members, but not removing them.

Cuckoo filters allow removing members: https://www.cs.cmu.edu/~dga/papers/cuckoo-conext2014.pdf

And just for completeness, storing everything losslessly in a btree, radix tree, or hash table would probably be under 300 kB. (80 kB just for the IDs plus, say, an additional ~2x overhead.)

cakoose··on Dashmap: Fast concurrent HashMap for Rust
I think this statement from the grandparent post might have been misleading:

> If you're looking for concurrent code that doesn't use `unsafe` you won't find any.

The Rust standard library provides a bunch of concurrency primitives. You can write 100% safe Rust code use these primitives to do things concurrently, and Rust guarantees that your resulting program is free of a specific category of bugs: memory errors, data races, etc. No other mainstream language gives you this combination of concurrency safety and efficiency.

The implementation of the concurrency primitives, however, often includes assembly, or C, or unsafe Rust code, which the Rust compiler can't provide guarantees for. We have to rely on humans for that. This is the norm for almost all programming languages.

Ideally, yes, it would be nice if the safe subset of Rust were powerful enough to implement efficient concurrency primitives, but that would add a TON of complexity to the type system.

People are generally ok with the Rust standard library using assembly, C, or unsafe Rust, because even though it can be tricky to write this kind of code correctly, the standard library is maintained by Rust experts who take every change seriously.

People tend to worry more about third party libraries because, on average, the authors are not Rust language experts and may not understand the subtleties.

cakoose··on Silly benchmarks on completely untuned server code
> That's not what the original blog post was about though, it was about how bad Gunicorn is.

Yes, the post starts out describing an issue with how Gunicorn listens for connections. Like you said, there are better libraries than Gunicorn so that's not a reason to jump to Ruby.

However, the article goes on to talk about other things, like the lack of real multithreading, import-time code execution, and overall efficiency, all of which also apply to Ruby.

I'm not saying Python or Ruby are a bad choice. It's just that MapleWalnut was soliciting an alternative that doesn't have the problems described in the original post, so Ruby doesn't qualify.

cakoose··on Why Not WireGuard
Sure, that's a fine argument to make. But the article says "I expect AES to outperform ChaCha20 in every single scenario", even when the very next sentence talks about a scenario where that's not true.
cakoose··on Silly benchmarks on completely untuned server code
The standard Ruby VM (MRI) is about the same speed as the standard Python VM (CPython), aka on the slow end. Twitter famously moved from Ruby to Java/Scala, largely for performance reasons.

Node.js is much faster because V8 is much faster, but it's still basically single threaded, so you need to run process-per-CPU, which is what the original blog post (to which this post is a follow-up) was complaining about.

cakoose··on We have to talk about this Python, Gunicorn, Gevent thing
It seems to be a complaint against doing process-per-CPU.

Let's say your server has 4 CPUs. The conservative option is to limit yourself to 4 requests at a time. But for most web applications, requests use tiny bursts of CPU in between longer spans of I/O, so your CPUs will be mostly idle.

Let's say we want to make better use of our CPUs and accept 40 requests at a time. Some environments (Java, Go, etc) allow any of the 40 requests to run on any of the CPUs. A request will have to wait only if 4+ of the 40 requests currently need to do CPU work.

Some environments (Node, Python, Ruby) allow a process to only use a single CPU at a time (roughly). You could run 40 processes, but that uses a lot of memory. The standard alternative is to do process-per-CPU; for this example we might run 4 processes and give each process 10 concurrent requests.

But now requests will have to wait if more than 1 of the 10 requests in its process needs to do CPU work. This has a higher probability of happening than "4+ out of 40". That's why this setup will result in higher latency.

And there's a bunch more to it. For example, it's slightly more expensive (for cache/NUMA reasons) for a request to switch from one CPU to another, so some high-performance frameworks intentionally pin requests to CPUs, e.g. Nginx, Seastar. A "work-stealing" scheduler tries to strike a balance: requests are pinned to CPUs, but if a CPU is idle it can "steal" a request from another CPU.

The starvation/timeout problem described in the post is strictly more likely to happen in process-per-CPU, sure. But for a ton of web app workloads, the odds of it happening are low, and there are things you can do to improve the situation.

The post also talks about Gunicorn accepting connections inefficiently and that should probably be fixed, but that space has very similar tradeoffs <https://blog.cloudflare.com/the-sad-state-of-linux-socket-ba....

cakoose··on We have to talk about this Python, Gunicorn, Gevent thing
Using async/await instead of gevent wouldn't fix the issue described in the blog post.
cakoose··on We have to talk about this Python, Gunicorn, Gevent thing
Even if you manage to preload everything you need, Python's reference counting mechanism will cause everything to be copied anyway.

Every time you access an object, its reference count is mutated, which will the memory page to be copied.

There are workarounds if you're willing to mess with the Python interpreter: https://instagram-engineering.com/copy-on-write-friendly-pyt...

cakoose··on Stepping Feet Illusion
I noticed that too. I think it's because the buses are 3 stripes long instead of 4.

After I changed the "--bus" CSS variable from 30px to 40px, I got the stepping effect.

cakoose··on Swift Crypto
Yeah, 2^48 is huge, but that gets you a collision probability of ~40%.

You'd want a probability more like 1-in-a-million (or 1-in-a-billion). That's 2^39 (or 2^34), which is 1000 messages a second for 17 years (or 7 months).

Again, probably still safe for many use cases. But you do have to think about it for a second. XChaCha20's 192-bit nonce capacity lets you totally ignore that factor, which is a nice property for generic crypto recommendations.

cakoose··on Swift Crypto
AES-GCM-SIV takes a nonce; if you reuse a nonce with two identical plaintexts, you reveal that the plaintexts were identical, but not much more.

With AES-GCM, if you reuse a nonce with two different plaintexts, you reveal the XOR of the two plaintexts!

cakoose··on Swift Crypto
The first example shows AES.GCM.seal(...) and then states:

> This code avoids some of the numerous pitfalls that you can encounter when constructing encryption schemes yourself. For example, it ensures that you use a randomly selected nonce, and that you authenticate your ciphertext.

The AES-GCM nonce is only 96 bits, which might be enough in many contexts, but is still a little short for comfort when selecting nonces randomly: https://www.imperialviolet.org/2017/05/14/aesgcmsiv.html

It's surprising that the blog post just declared success without bringing this up at all.

(It looks like AES.GCM.seal does let you specify the nonce, though, in cases where you can maintain a counter yourself.)

cakoose··on Slack’s new WYSIWYG input box is terrible
For me, if you type:

    when you do `foo()` it foos the bar.
It does the right thing for me. This was the case yesterday and it still is today.

It's possible that the author got a different release. It's also possible that the author was trying other very similar scenarios that behave differently, e.g. backquotes behaves differently at word boundaries and inside a span that has already been converted to the code-formatted rendering.

Overall it's pretty confusing, but the simple case that the original article points out works fine.

cakoose··on Ask HN: What is the most beautiful piece of code you've ever read?
> For Python, we don't need to supply the optional initializer 1.

Without the initializer, factorial(0) doesn't work.

cakoose··on Ask HN: What is the most beautiful piece of code you've ever read?
> In almost every other language this code would look messy or use some terrible recursion.

Nah, lots of other languages can do this.

Python:

    def factorial(i):
        return reduce(operator.mul, range(2, i+1), 1)
Ruby:

    def factorial(i)
        (2..i).reduce(1, :*)
    end
Haskell:

    factorial n = foldl (*) 1 [2..n]
cakoose··on Bedrock – A modular, WAN-replicated, blockchain-based database
Hmm... Git's commit chain might be a blockchain (in the minimal sense of the word), since I think it uses that hash chaining technique.

But if I were describing Git to others, I wouldn't say "Git is a distributed, blockchain-based VCS" because that would give the wrong idea.

← PreviousPage 4 of 8Next →