Hacker Newsnew | past | comments | ask | show | jobs | submit | mpyne's commentslogin

Are you saying that most data loss happens because your data center gets blown up in a shooting war? Like, AWS is the first digital service provider to lose data in decades?

I'm saying that if you have eliminated more mundane failures like dying harddrives, cosmic rays and so on from your systems and your calculation ends up with 11 nines then actually those "force majeure" events are probable enough that they dominate whatever other residuals are supposedly hiding in those last 0.0000000001%.

The region has seen a bunch of wars in the last 100 years, so the annual war-rate is > 1%. Even if we generously add the assumption that only 1 in 100 wars affects a datacenter you can see that wars become a major source of correlated hardware failures that they need to solve to actually deliver that kind of reliability.


You don’t want to blend probabilities like this, because the tactics you use as a consumer vary between the two. If you consider 11 9s like “object AFR”, you might build systems that are resilient to very occasional single object loss. And it’s useful to know at what rate that might occur.

Whereas with these force majeure events you’d want a complete DR setup, and it’s typically an async recovery. Here it is useful to understand the fault domain (single server or single building or multi-building) so you can plan.

Blending the two numbers doesn’t help you build better against the systems. And the force majeure events are rare enough that they won’t happen … until they do. I’m not sure that knowing the precise probability that Iran would attack a gulf nation would change the fact that if they do, you need to have a DR story.


Seems like begging the question to me. You can't blend the numbers because amazon didn't blend the numbers. If they did and miraculously still arrived at 11 9s then that would also cover things such as wars and natural catastrophes, e.g. because they do offsite backups internally.

I’m not saying you can’t blend the numbers, I’m saying you shouldn’t blend the numbers. Because one number doesn’t communicate what you actually need to know to build.

You want to know how reliable the service is in steady state. For example it’s useful to know that S3 is effectively lossless in steady state whereas EBS volumes have an AFR of about 0.1%. You build your apps very differently between S3 and EBS knowing this. You can build highly resilient applications on each, but you code them differently, informed by these design goals.

You separately want to understand the failure modes that will require you to fully recover from backup. For example knowing that cloud storage is resilient to everything but region failure would inform you that your backups should be out of the region, not just a bucket in the same region. You don’t get that perspective from just a 9s number.


Cosmic rays and dying hard drives are not force majeure though.

Well yes, that's what UB means. It's a singularity, you run into it and there's no longer a specified requirement for the behavior that will follow.

Rust also has UB, btw, https://doc.rust-lang.org/reference/behavior-considered-unde..., so I don't know where it is that people have imagined this is something the C and C++ language designers went out of their way to foist upon you.

If you want to write code for a VAX, then you can use the K&R C compiler where it had defined outcomes for everything. If you want to write portable C code for modern CPUs then it's fair to ask what the C language standard is supposed to define for each of those CPUs and OSes and ABIs.

And a bunch of people were nice enough to do that for you and I, but because they are not deities, there are things that they had to leave out to make the language useful, so they did.


> Rust also has UB, btw

Not at all the same. C and C++ are full of hazards and I get the impression it’s genuinely difficult to avoid entirely in normal code bases, and typically impossible to avoid statically. Whereas in Rust it’s all gated behind the unsafe keyword, and if you don’t use it (and most code bases never need to use it), you cannot encounter UB; and that scoping makes it far easier to control and handle correctly.


I think this is a bit exaggerated. I mostly find it not difficult to avoid UB in C. There are mainly five areas where you can have problems: type safety issues, signed integer overflow, out-of-bounds accesses, use-after-free, and race conditions. Type safety is generally not a problem if you avoid unsafe casts (and casts are easy to screen for just like "unsafe"), signed overflow one can protect against via sanitizers or one can rule it out statically, and out-of-bounds accesses you can avoid by using safe buffer and string abstractions and never doing open-coded pointer arithmetic.

Use-after-free and race conditions are the areas where Rust has a clear advantage. Here one needs to have a clear strategy and enforce it manually (or using tools, but we lack good open-source tools for this). Valgrind and similar tools also help.


As someone who coded C++ (15+ years) and later Rust (about 4 years now) for my dayjob: there are more than those you listed I have seen commonly. Unaligned accesses is a perrenial favourite, as is ODR violations and reliance on the undefined order of static constructors between translation units. In C++ I didn't see much of unsafe casts, except related to enums (always specify an underlying type to mitigate this).

Rust protects against all of these, but if you think Rust is only about memory safety, I don't believe you have seriously tried it. It does a lot of things in std API design as well to steer you away from bugs. Some examples:

- The pervasive use of Result and Option makes it impossible to forget to handle (or forward) the error case.

- Because of usage of RAII (C++ has this too, but not as pervasively, C doesn't except using some very new GCC extension) it is very hard to forget to free resources such as files, sockets, database connections, mutexes, etc.

- Enums can carry payload in their variants (C devs: think tagged unions, but safe, C++ devs: think std::variant but with match/case rather than bulky visitor pattern), which means you can make API designs that cannot represent invalid states.

- The typestate pattern is a bit hard to explain briefly, but it allows a state machine with types at compile time, to make sure you dont misuse an API. For example it can be used to prevent forgetting setting required fields in a builder before building. Or in embedded microcontrollers to make sure you can't hand out the same GPIO pin to different parts of the code base.

I often find that my code in Rust works first try, while that almost never happen in C++ for non-trivial code. It is what all those Haskell devs were talking about all these years, but in a systems language (no GC is critical to my day job in hard realtime control systems) and without the incomprehensible abstract math lingo.


I was only speaking for C not C++ (I fled C++ a long time ago). My code usually works first try in C, but my experience also working with students is that you need to learn to use safe patterns and strategies first. I can imagine that Rust enforces those.

You can do a lot more in C too: You can design safe interfaces based around incomplete structure types. This also should allows what you call typestate pattern (if I understand it correctly). You can build a decent option type / result type. You can have a bounds safe vector type. You can have safe string types. One can have type-safe dynamic casts. One can annotate return values so that they can't be ignored. One can use many different tools for safety. People coming from C++ often think that one can not do this in C because "it lacks abstractions", but this is not really true.


It has been a long time since I coded C. Last I did I remember hating C strings and the standard functions for dealing with them. So easy to get buffer overflows. I'd rather have the language design be so that I dont constantly have to think about not tripping over various things, instead I want to focus on the hard and interesting domain specific problems.

EDIT: Also, errno is an awful design. Forgetting to check for errors, or screw up which error you report is so easy in C. Exceptions in C++ are also bad, it is very easy to have no idea what exceptions are possible 5 layers down and end up with unhandled exceptions.


True, if you do pointer arithmetic on C strings or similar low-level buffer operations without introducing safe abstractions, there is basically no way to do this safely.

And I think that illustrates my point well. Yes there is MISRA C++ and CERT for C when you write safety critical code. But that is a lot of extra rules to follow and remember (and have linting tools check where possible). It is basically a entirely separate dialect of the parent languages. And if you aren't doing safety critical you won't be dealing with these but have to come up with your own (company specific or individual) rules. (You dont want to write MISRA C++ unless you have to, large parts of it are quite miserable).

In Rust I get good defaults, and a language that guides me in the right direction. The rules for safety critical rules are somewhat still under development but so far they look a lot shorter (you still need the "don't allocate in hard realtime tasks except at startup" and similar rules for example). And if you aren't doing safety critical you can safely use all of the language as long as you stay away from unsafe.

And for most code you dont need unsafe, and even when you do someone else has likely done the hard work for you already, providing safe abstractions on top. (The exception is FFI to other languages, it is impossible to avoid unsafe when calling code in another language that the compiler can't reason about, you should build a safe Rust API on top of the raw bindings. For popular libraries this often already exists.)


And my point is that "do not do low-level pointer arithmetic" is not really much harder to follow in practice than do not use "unsafe". In safety critical systems you may also care about panics, memory leaks, etc. I am not sure this is so simply in Rust as well.

That you do not get safe libraries out-of-the-box in C is a major problem. But I also see the supply chain situation in the Rust world as highly problematic.


Two counterpoints: Searching for "unsafe" is a lot easier when you want to audit the code (there is even a lint you can enable to forbid all unsafe in a crate (library)). And there is a pervasive culture to avoid unsafe where possible and document all the unsafe you do have explaining why it is in fact ok.

There are also experiments in formalizing the safety comments with attributes. To me the current prototypes look halfway towards formal verification, with unsafe functions specifying named requirements that must be upheld when calling them and the callsites needing to "discharge" them by name. Time will tell if this is a good idea for general code and it it catches on.


I agree that searching for "unsafe" is easier, we should have this in C too. I have some local patches to GCC that emit diagnostics for some things which are unsafe in C and were GCC does not already have a warning. I think the culture is an important point, the question is how this scales when the community becomes larger and then the share of well-paid enthusiastic Rust early adopers make way to tired, less interested developers that inherit a lot of code and have to get some things to work with limited time...

Sure Rust is better on it, my point is that it is there, despite that community making it a Big Freakin' Deal to have all the memory safe they could design into the compiler and language.

If even they had to add escape hatches despite the presences of powerful language primitives like types, traits, borrow-checking, maybe the people charged with making it all work with 80s compiler technology weren't the literal Antichrist for also having UB as Rust does.

For what it's worth, C and C++ are much different in terms of hazard, so when you bucket them together it makes me wonder how familiar you are with the actual risk of UB in practice.

Nowadays it generally stems from doing weird things.... but no one is making you do weird things, any more than people are making you use Rust's unsafe keyword.


Almost but not quite. You are promised that safe Rust doesn't have Undefined Behaviour, but that's cultural, not technological. The technology is just enabling Rust's culture to deliver what they promised, but it would be very easy to purposefully (indeed there are known bugs where it does happen, look for "Rust soundness bug" if you want examples) inject UB which happens in your safe Rust.

The extent to which this is about culture should not be underestimated, to me that's the most hilarious part of Bjarne Stroustrup's big rant on memory safety a few years ago. The C word appears exactly once in his slides, in a quote from somebody else about what needs fixing. But Bjarne never addresses this once, even though it's the actual problem.


> I don't know where it is that people have imagined this is something the C and C++ language designers went out of their way to foist upon you.

They did. Most other languages, the vast majority of which are also memory safe languages, go out of their way to do the opposite, and give meaning even to erroneous programs. Some things slip through the cracks, and generally language designers and implementers work hard to get rid of UB.

C/C++ is the only ecosystem that has fully embraced UB as a way of life. They are the only compilers that make full use of "UB is bad and cannot ever happen" as a core tenet in how optimizations are designed. Rust UB is at least a little different. Rust UB can only be the result of unsafe code and is meant to be limited in blast radius, and is absolutely not meant as a loophole for compilers to just do whatever to make the code faster.

C/C++ have a surprisingly large set of UB, too. Thankfully, the rest of the software world is rising up and the committees are starting to make things like gasp signed arithmetic overflow into defined behavior.

But don't hold your breath.


For, C we have already removed at lot of UB from the working draft for C2y. My hope is that only the UB is left that is difficult to remove without requiring extensive changes to code or compilers, and that this can then be addressed by a technical specification that defines a safe subset of C.

But note that UB also does not necessarily mean your program has no meaning. A good compiler can do something reasonable by defining the UB. There is no mandate in the specification that a compiler has to break things. It is also the user's responsibility to put pressure on compiler developers to do something reasonable.


C++26 and C++29 are in the process of turning a lot of that UB into erroneous behaviour, basically what safer languages have been doing for ages.

However, how many years it will take until those versions become widely deployed across major compilers, and used by developers?


> Rust UB […] is meant to be limited in blast radius, and is absolutely not meant as a loophole for compilers to just do whatever to make the code faster.

This isn't true. Rust UB is meant to only be possible to trigger via `unsafe` code, but if you do trigger it, the compiler is free to do whatever it wants, and in practice it will make full use of that freedom. Rustc uses LLVM, it shares most of its optimizations with Clang!


We run it at my org and it's never been a noticeable resource hog. It's actually the best performer between it, our AI observability stack and the front end.

It may not be a huge resource hog, but it adds a ton of latency.

https://www.getmaxim.ai/bifrost/resources/benchmarks

Having ran both LiteLLM and Bifrost for months, I can largely confirm the numbers from those benchmarks for myself.


It may, but the latency it contributes to the end-to-end AI processing has been not noticeable in practice for our users.

That's not to say Bifrost wouldn't have been better, but the choice to use LiteLLM was arrived at after a fair bit of internal discussion (most of which predated my addition to the team), and so far we've seen nothing from LiteLLM that has been contradictory to the pros/cons they thought would be the case when LiteLLM was adopted.

Or in other words, the org will be happy indeed when they have solved so many of the rest of the problems we've had in AI uptake that the difference in latency between one AI gateway or the other becomes a problem to be solved.


And it doesn't foreclose the possibility of LLM achieving human-like writing ability. If I stole the writing of a human author much better than me it would still be plagiarism.

Of course, with real books we do have the problem of ghostwriting, where an author willingly writes for a book that will be published under someone else's authorship. That may be where things end up here, with requirements to acknowledge sources, whether ghostwritten for you or written by others.


> Root cause here is that writing is how humans communicate ideas.

Writing is a way humans communicate ideas. It's not the only way humans communicate ideas. And now, it's not only humans who communicate ideas, we just saw with the OpenAI HuggingFace hack how AI agents were able to communicate amongst themselves by using various hacked websites to opportunistically write notes for later agents to use.

All that "X is a thing only humans do" type of circular definitions will buy you, is to expand the definition of what humanity is. And I doubt that's really what you think.


I think you're missing the context, which isn't musing on what it is to be human and can machines think, but the purpose of Joe Smith's LinkedIn account expressing that he has ideas about X is to convey the message that Joe Smith is interested enough in X to venture an opinion on it, and Joe Smith has actually just set up an automated process to generate content without thinking about X, that perverts the purpose of communicating that Joe Smith is interested enough in X to propose the following ideas he been thinking about...

Similarly if Joe's contribution to his long form "idea" is a couple of bullet points, a program trained on flowery phrasing and a weighted average of everyone else's ideas isn't communicating Joe's thoughts on the topic, it's just adding words.

The debate on whether Claude actually thinks or not is orthogonal to the fact that outsourcing your "thought leadership" to it is avoiding thinking or leading. If I want to know how Wikipedia or Claude summarise wider human thought about the topic, I can find their websites thanks


I get the context, but then the comment I'd replied to would have said that humans get better at communicating their ideas by writing their ideas on their own, rather than that writing is some kind of human-only mode of exposition. It's not.

And nor is an LLM generating text just "copy/pasting a Wikipedia article", you'd think people on HN would be smarter than that at least.

If all Joe Smith is going to do is cat $(which claude) to his LinkedIn, then he'll deserve the poor results he gets from it, but we shouldn't mistakenly say that this will be because LLMs simply cannot write. It would be just as dumb for Joe Smith to do with with a professional human ghostwriter.


It's pretty deeply tied into C++'s object lifetime mechanics, which rely on storage being available and reserved for the use of that object's lifetime. If multiple objects with valid lifetimes had a situation where one lifetime needs to end, what should happen to the other objects' lifetimes?

C++ actually did end up evolving the ability to define a zero-sized class without a unique memory address, but mostly to allow optimizations like the empty base optimization to apply in other situations where it could make sense, especially with templated or constexpr code.


> C++ actually did end up evolving the ability to define a zero-sized class without a unique memory address

Did it? Are you talking about the no_unique_address attribute (I had to go look that up because WG21 apparently doesn't care about consistently using or not using separators in attribute names) ? That attribute lets you do the same trick as empty base class but without the ceremony, however it doesn't let you make ZSTs.


Yes, that's what I'm talking about. It does expand on the empty base object optimization, though it is still not fully generic.

But you can now have multiple zero-sized types as siblings in a struct or class that can be zero-sized, the restriction is that they do have to be different types.


But that's just not ZSTs. All C++ is doing is, as with EBC you can overlap a thing which doesn't need any representation with any number of other such things and with the no_unique_address attribute C++ will say their total size is 1.

C++ is bad at type arithmetic, that's nothing new. Rust has unit types like () which have size zero, and it has empty types like ! [aka never] which do not have a size because no values of these types exist. C++ struggles with this, if you attempt a unit type you get a type with a single byte that's all padding, thus size 1, and you can't write an empty type at all.


Everything. The issue is that the compiler won't even bother with polymorphism through a vtable for a polymorphic type (one with a vtable), unless the object is accessed through a pointer or reference.

If you have a value of the type itself (not a pointer or reference), then polymorphism doesn't even enter the equation in C++, even if you initialize from a derived type.

E.g. in this code:

    Base b(m_catalog.makeDerived());
    b.call_virt_func();
Even if `call_virt_func` is declared virtual, it will be `Base::call_virt_func()` that is called here, guaranteed. From a language perspective, we already know that `b` is a `Base`, you literally declared and defined it that way.

Runtime polymorphism is therefore only a game for pointers or references; it is the process of resolving the indirection that even allows for polymorphism to become a thing in C++. But this means that the compiler cannot know the actual type at compile-time for a polymorphic type, unless it can perform devirtualization as an optimization pass.

So although C++ will certainly allow you to define a class method that returns a virtual type by value (and not by pointer or reference), even for complex types, it is almost certainly a bug to do this unless you know for sure what the type will be statically, at compile time. Because the object you create as the return value will be forced to be the return type declared at compile time, "forgetting" the fact that it was created from a type deeper in the inheritance chain. This is the 'slicing problem' that was mentioned in the earlier comment.


Ah. Dyno (https://github.com/ldionne/dyno) is good at this stuff. If you have types Base, Child1, Child2, you can just return a Dyno object that can be any of these, expressed as a tagged union and not a Box-equivalent, and then do regular vtable-based or otherwise polymorphic dispatch into the object. You can also arrange it so that if you have a Child3 that can't fit in the (Base, Child1, Child2) union, the Child3 can be heap-allocated and invoked transparently as well. It's open-world type erasure.

C++ is so freakishly powerful is that it can not only solve this problem, but it can solve it via a regular library and not a language extension.


Dyno is really freakish and I can see a couple of pain points if someone tries to use it productively. A IMO glaring one is that dyno::poly is a boxing type that will happily take your value type and shove it on the heap while nobody's looking. This is a defect similar to the silent heap allocations for captured parameters in std::function.

On the other hand, it's really impressive that one can push dynamic function pointer lookup tables in C++ to a point where it looks almost, but maybe not quite like the real thing.


It is the real thing. Not "almost". The actual thing. If you want to forestall accidental heap policy, make a storage policy that prohibits it.


The perl $, @, %, etc. being compared to declensions is amusing.

I feel like you need to know something about perl and latin (both considered outdated) to get this joke.

For those who don't: in perl, the same text identifier can have a different meaning depending on these prefixes, i.e. $foo is scalar, @foo is array, %foo is a hash (dictionary). Likewise latin (like most old indo European languages) has a bunch of suffixes to indicate what is happening with a noun or adjective: is it a subject, direct object, possessive, used as a tool or location, etc.


> If you have to ask "Why?", then the answer probably won't make any sense to you either.

Saved to shortcuts


Came here to say this :)

RIAA doesn't like it, but my introduction to a video game series I've since spent hundreds of dollars on over the years, came from running across one of its soundtrack pieces on Youtube.

There's a reason companies talk about "customer acquistion cost", as you generally need to pay to market your products to potential customers of them.

So free marketing can be a real cost reduction. It may not be enough to be a benefit compared to the cost of piracy, but it's hardly a made-up defense.


> FOSS started as a counter movement against Microsoft in order to escape from surveillance and corporate control.

??? FOSS was around before that, famously so.

The rise of Microsoft made FOSS more important, but FOSS wasn't a reaction to surveillance and control, when it started even multi-user networks were practically unprotected, and it was quite easy indeed to see what other users were up to.

The phone phreaks were the ones who were worried about corporate control of comms, the FOSS people just couldn't figure out why people were paying for software with the hood welded shut when there was better software available to those willing to help contribute to it.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: