Well, first you can tune SQLite to get a 1000x speedup over 400w/s, without compromising safety too much. Second, the use case is small & local — I use it as an fprintf replacement.
Second, 400 concurrent users per second is a lot? If I had 400 users per second, with a 10 minute engagement time for 4 hours a day, I’d have ~10000 daily users. If they paid a monthly SAAS of 5$/user, I’d be making 600k$/year.
So… considering the implementation simplicity, that seems like a good trade off?
And to be explicit: It looks like InnoDB is ACID compliant and crash consistent, so indeed this criticism appears out of date. Thanks for correcting me.
Yep, and even before 5.5, InnoBD was present (since the early 2000's IIRC), and is what most people chose to use for any production level deployment (MyISAM never even supported transactions, so people had to use something different when they got serious), it just wasn't the default engine until that point so you had to specify it in your create table statement or use a config setting to change the default.
MySQL 3.x was mostly just okay for simple website that didn't do much. (MyISAM is good for either extremely read heavy with few writes or vice versa, was very performant in those cases for the time)
MySQL 4.x added subquery support and InnoDB as a standard component, even if not the default.
MySQL 5.0 added cursors, triggers, stored procedures, views.
I think from about this point MySQL was fairly usable in place of some enterprise DB's depending on use case, and most of the essential features were there, even if not always the default.
MySQL started as a super easy to use and accessible DB, and slowly accreted enough enterprise level features to be able to compete in that field, but it's been in use successfully there for quite a while.
In comparison, my (super limited and possibly incorrect) understanding of Postgres's history is that they focused on stability and enterprise level features first, and then later added some things for more convenience. I think they aren't super different in capabilities now, at least for the core functionality most expect.
When there are enough failings to generalize and the product is owned by a company that I feel is evil incarnate, I'm happy to dismiss them and then proceeded to not pay attention for a very long time, yes. In this case, it appears that this particular feeling may indeed have been fixed.
With no claims to suitability. Just back of envelope based on 400 write transactions per second.
-- Assume an employee filling out a form on a web site that takes on minute to complete.
-- Assume submits are randomly sent. (A bad assumption)
Per minute that's (1 * 60 * 400) 24,000 people submitting a form. Or over a million people submitting their form per hour. Nothing Google scale but enough for most any internal business application.
These are also likely just serialized commits per second. Parallelized non-blocking writes and/or using explicit micro-batching(SQLite supports batch inserts) and you'll be blowing most gov sites out of the water.
Shout out to the great people in the KDE Community.
I was in high-school and being enamored with the open-source concept ("You mean I can modify anything I want about it?" - hah) I wanted to make my mark in a simple little way. I remember fondly when lurking around in IRC and somehow decided I would change the login screen. Got pointed to the KDE greeter channel and that is how it all started for me. I kept annoying them, did not know how to build, compile, nothing. Thanks to d_ed for answering my annoying questions, that is how it all started for me.
Recently experimented a bit with Rust and I found the reverse to be true. You cannot compose types in Rust. You compose behaviors not types. Very important distinction as I found out the hard way.
In that example, both the bicycle and the car have the property `speed`. Imagine you have multiple types now that need to have the `speed` property. You would need to copy-paste the same code for each new type in order for you to be type safe.
* But if I want a single queue to be able to handle different tasks, then it's not clear how that could be done with monomorphization alone. That's why it's called "mono"morphization. It's all about taking abstract implementations and creating instances that do one thing. *
Which was exactly what I was experimenting with: A single queue worker that can handle different cases. Honestly, it made Rust almost not worth it for me. Sadly, I was too deep to turn back so I wound up doing the whole thing in Rust. I have tons of copy-paste code. It is ugly and it is bothering me.
... which does put both cars and bicycles in the same queue, but doesn't eliminate the copy-paste for each new type completely; 'car' and 'bicycle' still wind up with separate 'get_speed' impls, which are textually identical aside from the type names.
I've heard "monomorphisation" to refer to something a compiler does, but not something a programmer does. I think this is just "repetition"!
The need for something to solve the problems inheritance solves has been known in Rust for a long time. Mostly it's been motivated by the need to implement the HTML DOM, which is fundamentally an inheritance hierarchy, in Servo. There's a longstanding RFC about it:
For reference I have over a decade of JavaScript experience in industry and my async Rust rewrite of a large JS project was *more* concise then the heavily refactored and polished NodeJS version (a language I consider more concise then most). If you are having to copy and paste excessively in Rust that is an issue but it is not necessarily intrinsic to the language.
For what it's worth traits largely prevented copy and paste and where traits fail there are macros. The classic inheritance example you link to is a tiny percentage of my code and an orders of magnitude smaller time sink when compared to the code maintenance problems I faced in other languages.
Monomorphization is not a user action of copy pasting, it's something the compiler does with parametric code. If we're talking about Rust it means when you write:
The Rust compiler will generate a method of `id` that works for both String and usize, so there will be two copies of the function with a slight variation in your binary. That process is called monomorphization.
> Which was exactly what I was experimenting with: A single queue worker that can handle different cases. Honestly, it made Rust almost not worth it for me. Sadly, I was too deep to turn back so I wound up doing the whole thing in Rust. I have tons of copy-paste code. It is ugly and it is bothering me.
You can easily use generics, there is no reason to copy paste code like this
Would that copy-paste code ever amount to more than some getter methods, like the get_speed() example you provided? If the speed field had some special behaviour, couldn't you wrap it in its own Speed type, with its own methods, and use that from the enclosing types?
I think the solution you've proposed is probably how you'd do it - but isn't inheritance more elegant in this case (i.e. using a language which supports inheritance if this is important to you)
No, inheritance is not an elegant way to mix in shared data and behavior. It can seem like it is in a simple case where there is only one set of data and behavior you want to mix in, but it scales very poorly. Nothing is fundamentally a single kind of thing, and multiple inheritance is a mess. It is more elegant to mix the behavior in through delegation, because it scales to however many things you want to mix in. Carrying on the synthetic example in this thread, you can mix in Speed and Pedals into Bicycle but Speed and Cylinders into Car.
Maybe not on every situation, but I do agree that inheritance is more elegant in a lot of situations, in the sense that it gets some behaviour "out of the way".
Sorry, I'm not getting it -- could you please explain further?
> You cannot compose types in Rust. You compose behaviors not types.
But I thought in OOP behaviour is type? (Or, IOW, type includes behaviour.) To me, judging only from this, it feels like Rust isn't quite OO... Is it perhaps just not quite finished yet?
Another aspect: That whole "Rust has something called 'monomorphization', which leads to lots of copy-pasting" reinforces that impression. Is this the same problem that C++ tries to overcome by the "select which inherited implementation to use" operator, and other languages by allowing inly single inheritance?
Beyond some small changes I would make to use new-types all over the place, I personally like to rely on Deref impls to mimic inheritance (although not everyone agrees this is a good idea to do too often): https://play.rust-lang.org/?version=stable&mode=debug&editio...
As you can see I also used a macro by example there to remove some of the duplicated code that you would otherwise have.
Recently I started delving into gRPC vs RPC over RabbitMq using json as the mesage format. I saw that for small to medium sized messages, gRPC is actually slower. Of course, this was just a small scale experiment so I don't think nothing of it.
Does anyone have a sort of infrastructure/architecture guide at a bigger scale for gRPC?
My biggest questions range from: How do you actually load balance the servers? What happens if you have a sudden influx of requests but don't want to auto-scale? Do you still need a sort of queue-ing system in front of the gRPC server?
In my research I wasn't able to find some noteworthy articles about this and thus triggered my curiosity.
I suppose the problem with an easy go-to playbook is if that were easy to pick one solution for every problem, gRPC would have likely picked it by default (I can attest the team, and the external contributors, are top notch, having worked alongside them). Unfortunately some problems, at scale, need to be answered depending on architecture with the holistic system in mind, and are not just gRPC issues. Truth is large scale systems are hard to build and operate. Perhaps that is why you get help from experienced individuals and consultants.
I do agree some documentation/tooling is lacking and could be improved to guide folks through the process.
I found gRPC not particularly fast on a per-message basis unless you're using the streaming feature. If you have iterated calls, consider streaming instead.
Because it supports full duplex streaming, there's a risk of tunneling your own less than fully specified protocol on top of gRPC. In some circumstances that may be worth taking advantage of, because gRPC takes care of session management, reconnecting, authorization (i.e. it has ways for you to add authorization, like headers) etc.
If you need queuing I think you should use a queue instead.
Never tried with MsgPack, but I did try Protobuf. The reason I dislike Protobuf is that there is an extra code generation step I need to do. When using a compiled language, I guess you don't really mind the extra code generation step since you also get some safety from the compiler so you don't wind up miss-using the generated code. It is not the same when you are using an interpreted language that doesn't have really good strong typing. You will have to run a static code analyser and be very careful every time you do a change to the interface.
Since in my experiments I was calling a method in Php through RabbitMq from a Rust worker, so it just proved allot simpler to just use json. Also, I measured the time it took to:
1. Make a request to the Php API
2. Php sends the rpc message on the queue
3. Rust processes that message
4. Php catches the response from the worker
5. Php returns the response to the http client
It was <10ms running the cluster locally regardless if I used Protobuf or simply json.
One observation / warning I have WRT msgpack is that I have seen situations where data getting serialized directly to msgpack can't be rendered as proper json. Specifically JSON requires some proper encoding of characters while msgpack is perfectly happy to convey some binary characters.
The specific situation I saw was
1) antique perl application barfs a sql dump into msgpack
2) fluentd takes that, turns it into a thing like json but with a control character in it (think old time cyan blinky happy face or \0x03)
3) that thing gets dumped into kafka
4) logstash pulls that thing out of kafka and tries to feed it to elasticsearch
5) elasticsearch reports that it doesn't like the blinky happy face, generates an extensive log, and stores the event minus the key/value pair with the blinky happy face.
Certainly many of these steps have an implicit "don't do that!" or "Update to a newer thinger!" but the root cause (perl serializer into msgpack renders a thing that's valid msgpack but not valid json) is surprising in an unpleasant way.
You didn't define small or medium size and number of messages.
From my experience server stream while great for memory and giving the client time to process messages as they come can be slower than unary. One way to solve that is to have a batched message with a repeated field of the underline message you want to send, it's an order faster.
I read sometime ago about about server pushback, maybe you need to override/implement your own StreamObserver to notify the client to slow down as the server isn't ready yet or configure the amount of data the gRPC server can queue there are quite a few configurations (a lot of them obscured) you can make to the server/grpc service.
Seems so much work for something already implemented with messaging brokers, though.
In my opinion, clients themselves shouldn't worry about if the server is ready or not, only handle if the server does not respond in x seconds and then simply crash or error out. It is the same you would to with any other external service call.
I think the biggest change to gRPC is to the way I thought. Dumb clients was always something that I chose because it was an order of magnitude simpler to reason about. gRPC comes in and changes this by making the clients smart and the servers smarter, which brings allot of complexity to the table.
It's just a different framework for a different use case. The fact that it's http 2 and support bi-directional streams is nice for low latency applications.
I doesn't make RPC over message brokers deprecated and if that works for you under you conditions that's great.
You can say the same about message brokers. Is it LIFO, FIFO or something random? what about acknowledge, is it late or not? Can the queue hold responses (Rabbit advices against using it as a result store)?
In that regard message brokers are complicated, lets just do http calls and let the load balancer take care of routing, it's much simpler and the client is very dumb.
Where Conan doesn't really shine is crossplatform dependencies. For example, when a dependency requires to have been built with a specific toolchain. Thus, if your goal is write once and run everywhere you'll still have to build dependencies yourself.
When a dependency requires to be built with a certain toolchain, Conan can't do anything about it. At best it really only surfaces a problem that might be hidden if you were just be using pre-made builds.
Loved it! It gets crazy with the pawns, I was so surprised. The visuals of the game are so and so, but they are really cool nonetheless. Two thumbs up from me!
I didn't see it back when I was doing Qt, but writing a Qt app for every platform is hard. In theory you can have Qt apps on Android, iOs, Linux, etfc but in all fairness, it is just too much of a hassle. Big headache with toolchains, dependencies, it is just not worth it nowadays.
"Write once, run everywhere" was a nice motto for Qt and I even bought it for a while but yet again does not work in practice. Flutter, on the other hand, actually works. Sure it doesn't have all the bells and whistles of OS integrations, but it is getting there. I hate the language itself, but I can't deny that it works beautifully.
If you think about it, the Qt way of doing things was quite novel back in the days and had allot of promise, but I feel that the main thing that held it back was C++ (still no official package manager and no cool CLI tools in 2021!!). I am sad that I left Qt behind since it was an amazingly modern platform when I used it, but I am glad I don't do C++ anymore.
A while back we explored the use of Cassandra. We wanted to keep some event related data there and for it to be relatively fast read-wise in order for us to do all sorts of reporting based on it. So we wrote allot and wanted to read fast. Seemed like a perfect store for our timestamped events, especially since we wanted to not even use deletes and has in-build record deduplication via its primary key. Turns out, it is not that perfect.
Other than what the article described, I can also add:
1. It has a steep learning curve, but you do get to see the advantages while you learn it. But then, everything comes crumbling down.
2. The setup is a pain locally. Then it is a pain to set it up in prod and manage it. The tooling itself feels very unfinished and basic.
3. No querying outside primary index on AWS Keyspace if you want it managed. Also, any managed variants are EXPENSIVE. I mean, every database is fast if you only query by the primary index so why pay extra?
It is just not worth it. For example, we winded up using MongoDb and it turned out to be fast, scalable, had mature tooling and we can keep tons of event related metadata in it and it is easy to manage and doesn't cost a fortune.
I think Cassandra is a better fit for interactive use cases, not for reporting. Also, basically it's super heavy duty and it should start to shine when you're really serving entire Internet (on the scale of Reddit, Expedia etc.) and your Cassandra cluster is distributed across DCs across the world.
I haven't really worked in this space for a couple of years so I don't know if the cloud offerings have already completely matched Cassandra's features and robustness.
Of course, I have been totally unable to determine how they merge rows/partitions without cell timestamps. It's a black box.
I was just on a "Keyspaces" meeting where the sales dude basically described dynamo billing, dynamo provisioning, feature shortfalls obviously due to dynamo, but would not admit it was dynamo.
We initially looked at Cassandra. We liked its use case, we liked its scalability. However we also ran into maintenance and setup pains.
We ended up going with ScyllaDB, which is a drop in replacement for Cassandra. It’s written in C. Much easier in resource demands and we didn’t have to deal with Zookeeper directly.
Dumb statements all around, but clearly you've never been in a Cassandra environment vs Scylla. Scylla is far, far more reliable, easier to get up and running, and required a bit less supervision than Cassandra.
Hah! Brings back old memories. I actually did something similar for an RPG I was trying to create from scratch using Qt3D (back in the day when it first came out and was getting stable, QT 5.8 maybe).
Had the whole isometric world down and used qml to script monsters and abilities.
I think software takes longer to build now than it did before even with all our software advancements. Abstracting the market requirements completely, the new technologies are very slow to create an mvp out of.
For example, a week ago I started a Symfony (php framework) project and let it on default server side rendered setup. I bought a $20 css+html template. I created some entities and then just inserted a form for that entity into an html file using one line of code. It took me less than 1h to do this, with containerization and db setup and figuring out how things work in Symfony.
It blew my mind. I totally forgot how easy it was and I could focus on the things that mattered. To do the same thing in React/Angular would have taken me much longer since I simply had to build 2 things (a frontend and a backend). I think this is why today software takes longer to build: You simply have to build more behind the scenes so that the end customer sees a form/page/button.