It’s a common fallacy that the borrow checker is just a substitute for a GC, when it’s much more than that: it can give you safety guarantees (absence of mutable aliasing and data races) that e.g. Java or Go cannot.
This is true, but at least for me these additional guarantees aren’t preventing many bugs. In particular, my code is rarely subject to data races–usually any shared state is a file or network resource that is accessible by multiple processes on multiple hosts and Rust’s borrow checker doesn’t help in these regards, but I still have to pay for it with respect to productivity (yes, I know that productivity improves with experience, but returns diminish quickly and the apparent consensus seems to be that a wide productivity gap remains between GC and BC).
I’m not taking shots at Rust; I’m glad it exists, it’s impressive and ambitious and etc. It just isn’t going to eat Go’s lunch unless there’s some (ergonomic) way to opt out of borrow checking.
Because for a language with unrestricted semantics around mutability, like Go, this would be equivalent to the halting problem.
Languages like Rust or similar use linear/affine/unique types to restrict semantics in a way to make such type of analysis possible. That’s the whole point of linear types.
You can do it at runtime though (see Go’s race detector), and while that’s immensely useful, it’s not the same thing.
They can, to a small extent. C compilers too, or Python or whatever. But if you don’t have Rust’s move semantics and Rust’s borrow system, then the compiler doesn’t have a lot of information to do the analysis with.
Compiler has to work in concert with the language – Rust isn’t just C++ with more rules on top, lifetimes and aliasing are a visible part of type system the programmer interacts with. The user spends more time explaining in more details what’s going on to the compiler: in Java you have
T, in Rust it’s your task to pick between
&'a T or
&'a mut T. In exchange for this extra annotation burden, compiler can reason precisely about aliasing and rule out things like data races or iterator invalidation.
In other words: Rust is referentially transparent. If you would like a referentially transparent language—or a language which allows you to control and manage mutation—without spurious copies I suggest haskell or ocaml.
I don’t use async Rust, but I’ve seen complaints that the lang-level async support prematurely standardized on the “readiness-based” async model (e.g. epoll/kqueue), while the rest of the world seems to be moving toward the “completion-based” async model (e.g. IOCP/io_uring). What do actual Rust async users (say who might want to use underlying OS support in IOCP/io_uring) think of this?
What do you mean by “ideal message delivery”? Uniform atomic broadcast? If so, is every system built around Paxos or Raft “negligent”?
To be frank, no project that uses the GPL post-GPL2 will ever be the ‘boring’ variant. Not because it can’t, but because you need to convince lawyers that it’s boring.
And GCC has done little to create a situation where it might be. clang/LLVM breaks the compiler in two, with a frontend and a backend that can evolve independently. Can you even do that with gcc? And I mean, in a practical sense. I know that the frontend and backend of gcc can technically be decoupled, but technically != plausibly.
What does a compiler’s choice of license have to do with its approach to undefined behaviour? Maybe just being dense, but I don’t understand what point you’re making here.
Your information is outdated. You can use GCC backend without GCC frontend, and it is an option supported by upstream. Since GCC 5. See https://gcc.gnu.org/wiki/JIT.
Since the compiler’s license has no effect on the license of your code, nor does GPL3 change anything much vs GPL2 (in reality, I understand there is a lot of spin to the contrary), this seems like an axe to grind more than a contribution
We couldn’t use gcc post-GPL3 when I was at Amazon (or recent versions of emacs for that matter). Do GOOG/MSFT/FB treat gcc differently?
There were many engineers using emacs, but the official line was that you weren’t allowed to install any GPL3/AGPL software on a work machine for any purpose, and that explicitly included recent versions of emacs (and also recent versions of gcc, which meant the build system was stuck with obsolete versions of gcc). I suspect everyone just ignored the emacs restriction, though. I’m sure a lot has changed since I left in 2014 (I bet the build system has moved to clang), and I don’t know the current policy on GPL software.
At Microsoft, the policy is surprisingly sane: You can use any open source program. The only problems happen when you need to either:
There are approvals processes for these. There’s no blanket ban on any license (that I’m aware of) but there are automatic approvals for some licenses (e.g. MIT), at least from the lawyers - the security folks might have different opinions if upstream has no coordinated disclosure mechanism or even a mechanism for reporting CVEs.
That sound unsustainable. Do you not already need new builds of GCC to build Linux? Surely if not, then you will eventually. And I can’t see Amazon ditching Linux any time soon
Keep in mind my information is 7 years out of date (I left in 2014, when Amazon was just starting to hire Linux kernel developers).
The third mode, with both strict ordering and guaranteed delivery is obviously the safest. It’s also, in my experience, the most common.
In my experience, it’s very rare that you actually need strict ordering in your message queue. For instance, the delivery notifications example in the article is not strictly ordered end-to-end. Once your notifications are sent to the user, there is no guarantee that they will arrive in the same order (or on time, or even at all!). In that case, why maintain a strict ordering and the scaling limitations that come with it?
A global total order on messages (and guaranteed delivery in that order) can simplify reasoning about a system. For example, it can be useful to know that if you’ve seen a given message, you’ve already seen all previous messages (WRT the total order). Replication can also be easier with this guarantee: you can quantify how “far behind” one replica is WRT another, and if 2 replicas have the same “last seen message ID”, you know they’re identical.
“The distributed systems community is split between the two.”
“The distributed systems community” certainly does not describe the academic distributed systems community. Apparently it refers to some sort of pop-culture clique of programmers. For academics, Raft is just another Multi-Paxos variant among dozens.
“The distributed systems community” certainly does not describe the academic distributed systems community. Apparently it refers to some sort of pop-culture clique of programmers. For academics, Raft is just another Multi-Paxos variant among dozens.
I guess that, without further qualification, it’s an umbrella term that includes both academia and industry. I’m sure that Raft is not particularly interesting to academia, but it’s focus on (for lack of a better term) “implementability” has made it categorically different than ~all other Paxos variants for industry practitioners. That is, all evidence suggests that, for a competent engineer, it’s practically feasible to implement Raft, and difficult or infeasible to implement Paxos. With that said, I’m super excited to watch Heidi’s work play out in industry, I’m optimistic it will bridge this gap.
I disagree. The main contribution of the Raft paper was that it made explicit several implementation decisions (e.g. leader election) that are left as an “exercise for the reader” in generic descriptions of Multi-Paxos like “Paxos Made Simple”. But there are already many descriptions of Multi-Paxos, designed for implementors, that also specify these implementation decisions. I don’t perceive that these explicit descriptions of Multi-Paxos are any more difficult or confusing to implement than the Raft paper or thesis. Some examples (by no means an exhaustive list):
“Paxos for System Builders” http://www.dsn.jhu.edu/Paxos-SB.html
“Paxos Made Moderately Complex” https://www.cs.cornell.edu/courses/cs7412/2011sp/paxos.pdf
“Paxos Made Practical” https://www.scs.stanford.edu/~dm/home/papers/paxos.pdf
The main contribution of the Raft paper was that it made explicit several implementation decisions (e.g. leader election) that are left as an “exercise for the reader” in generic descriptions of Multi-Paxos like “Paxos Made Simple”.
That was indeed one of a few important properties of Raft which made it “implementable” in industry! I’d add that the overall tone or phrasing of the paper, speaking in large part to an audience of implementors rather than peer reviewers, was also super important.
But there are already many descriptions of Multi-Paxos, designed for implementors, that also specify these implementation decisions. I don’t perceive that these explicit descriptions of Multi-Paxos are any more difficult or confusing to implement than the Raft paper or thesis.
Appreciated, and I don’t necessarily disagree, but our subjective perception of the difficulty of a given protocol isn’t nearly as good a signal as the actual number of implementations in common use. And I think it’s pretty undeniable that the number of Raft implementations in the wild handily outnumber the Multi-Paxoses! That says a lot.
I wish the article had distinguished between Multi-Paxos and the Synod (single-instance) protocol. The Synod protocol is wonderfully simple yet subtle, and is surprisingly useful even though the conventional wisdom is that only Multi-Paxos is relevant to practice.
Anyone see any benchmarks for Hotwire? Mention of performance is conspicuously absent from DHH’s article and the Hotwire home page. This other article idealizes performance by implying updates take only 18ms, but that’s not under realistic traffic conditions and doesn’t include the DOM update itself, only the HTTP overhead.
Generally speaking, the SPAs will perform better for anything but mostly static content, especially under heavy traffic. Hotwire sends requests to the server for every DOM update and wait for the server to render HTML. SPAs send requests to the server only as needed and perform DOM updates in the browser itself.
LiveView takes a similar approach. I hear it performs OK except for memory scaling issues. But being built on Erlang gives it the benefit of being designed from the ground up to manage stateful connections concurrently. I suspect Hotwire performs more like Blazor (i.e. not very well). It seems like it might actually perform worse under heavy traffic since Hotwire doesn’t compile application logic to wasm so it can be run client-side like Blazor does.
I think a big part of the answer is that although in theory a carefully written SPA could out-perform HTML over the wire, other constraints take things very far away from optimal. Compare, for example, Jira vs Github issues (or a blast from the past like Trac, which is still around, for example Django’s bug tracker https://code.djangoproject.com/query). The latter two both feel much lighter and you spend far less time waiting, despite both being server rendered HTML.
Some of the reasons are:
Interesting. I didn’t realize Github used HTML over the wire. What’s their implementation? Hotwire? Something custom? I’m digging through their blog, but the only article I’ve found that’s remotely related is their transition from jQuery to the Web Components API, which only relates to relatively small interactions in widgets.
I’m working on a .NET project now that uses async partials in a similar manner, but user interactions are noticeably slower than a comparable app I’d previously written in Vue. The more dynamic content in the partial, the longer it takes the server to render it. There may be some performance optimizations I’m missing and I admit to being a relative novice to C#. But, in general, SPA bashing is rarely supported by evidence.
Let’s take your Jira example. I’m looking at a Lighthouse report and a performance flamegraph of the main issue page. To chalk their performance problems up to “client side join” doesn’t tell the whole story. It takes half a second just for the host page to load, never mind the content in it. They also made the unfortunate choice of implementing a 2.2 MB Markdown WYSIWYG editor in addition to another editor for their proprietary LWML. Github sensibly has only one LWML (GFM) and you have to click on a preview tab to see how it will be rendered. I think it’s fair to say that If you rewrote all of Jira’s features, it’d be a pig no matter how you did it.
Meant to reply to this earlier, then the weekend happened!
GitHub is basically a classic Ruby on Rail app - see https://github.blog/2019-09-09-running-github-on-rails-6-0/ and https://github.blog/2020-12-15-encapsulating-ruby-on-rails-views/ - using Web Components where they need it for enhanced UI. Open up web tools and you’ll see on most pages, the bulk of the page arrives as HTML from the server, and a few parts then load afterwards, also as HTML chunks. I’m guessing they have a custom JS implementation of this, it’s not that hard to do.
I completely agree that the comparison I made is far from the whole story, but part of my point is that other decisions and factors often dominate. Also, once you’ve gone down the SPA route, slapping in another chunk of JS for extra functionality is the path of least resistance, and justified on the basis of “they’ll only need to download this once”. While if you have HTML over the wire, where every page load has to stand on its own, I think you are more cautious about what you add.
I read that LiveView postmortem, and found it odd that none of the “lessons learned” included load testing (which AFAICT would have completely prevented the outage). Also, unbounded queues with no natural backpressure (aka Erlang mailboxes) are land mines waiting to explode.
Looks like you skimmed the excellent post mortem there, liveview is used at scale already elsewhere. this is more of a pubsub design issue rather than an erlang vm or liveview issue per se. If you’re streaming updates faster than they can be consumed then you have problems anyway in any system. You need to find an alternative approach, which they did.
Used at scale where? I’d genuinely like to see some data.
The article I linked to was the first really in-depth one I’ve seen. I didn’t skim it, but it’s a fair point: If you want to provide live updates without refreshing, you’re going to have scaling challenges regardless.
https://smartlogic.io/podcast/elixir-wizards/s7e2-jose/ 12:00 on - but you can’t take this as a cargo cult example of success. They are using Liveview “at scale” but with what problems? ~19:00 I don’t know the exact details of where the friction happened.
Stress test already showed millions (2015) on a single server. https://fly.io/blog/how-we-got-to-liveview/ But I think this is micro/in-theory. I tweeted at Angel, I’m curious too.
I do admit it’s fun watching English start to magically grow “they” into a third-person-singular gender-neutral pronoun. It’s not even a terrible solution, for once!
The wikipedia page for it (citing the Oxford English Dictionary) mentions it appearing as early as the 14th century. So to be fair, I’m not so sure we’re seeing it “start”!
The ambiguity about cardinality is a bit annoying, but it’s no worse in that respect than “you”, so I think we can live with it.
I find it hard to believe that all their services are completely portable between their Linux servers and their MacOS dev machines. But maybe I’m just too accustomed to C++ and programming to the syscall interface.
I’m accustomed to C++ and programming to the syscall interface and it’s quite rare for me to have problems porting code between macOS and Linux. Between macOS / Linux / *BSD and Windows is another matter, but between two POSIX platforms you have to be doing something pretty unusual for it to matter. For example, the sandboxing frameworks and hypervisor interfaces are different and they spell
futex differently so I often need a small platform layer, but it’s a tiny fraction of the code.
Yeah, depends on what you’re doing for sure. In my work I directly depend on Linux-only APIs like epoll (though I’d prefer kqueue), memfd sealing, io_uring, etc., so portability isn’t something I either try for or could easily achieve at this point. POSIX compatibility simply isn’t worth it, since our product is only expected to ever run on Linux.
kqueue isn’t as big a deal as it used to be. There’s a
libkqueue for Linux and a
libepoll for everything else that implement one interface in terms of the other.
Memfd sealing is a mechanism created to introduce security vulnerabilities because it depends entirely on getting the error handling (which is tested only when under attack) correct. We proposed an alternative mechanism when it was introduced where you could request a snapshot mapping and explicitly pull updates. If everyone followed the rules, this is nice and fast (no CoW faults) but if someone doesn’t then it falls back to copying. No need for applications to handle the error condition because the attack is not observable.
io_uring is starting to look very nice. Hopefully other kernels will pick it up soon, since it seems to be stabilising. It may be a bit late though. For anything high-performance, things like DPDK / SPDK seem to be the future and the intersection of ‘need very high performance’ and ‘are happy to have the kernel in the fast path’ is shrinking.
I’m curious what the product is.
I don’t do networking, but my impression from reading Cloudflare blog posts etc. was that eBPF has largely removed the need for userspace network stacks in Linux.
Not sure I can talk about the product yet; it’s in closed beta but should be public beta relatively soon.
Avoiding external dependencies when possible and reasonable doesn’t preclude modularity in your own code. “External dependencies for everything XOR spaghetti-code monolith” is a false choice.
Yeah, 100% agree. I hope my article didn’t make it sound like I think that’s an
XOR choice, because I don’t.
I think that those are two ends of a spectrum. Every project needs to decide where to fall on that spectrum, which could be at one extreme or the other but is more often somewhere in the middle.
This “huge VM” approach works great and I am using it extensively in production (though not with a dynamic array interface). For 32-bit systems or other contexts in which allocating large virtual address ranges is not possible, another alternative that preserves pointer stability (but does not guarantee contiguity of elements in virtual memory) is the “segment array”: a small fixed-size array stores pointers to “segments” of exponentially increasing size (except the initial size is repeated once). The total memory doubles each time a segment is added to the array: 8, 8 + 8 = 16, 8 + 8 + 16 = 32, etc.
I first encountered the “segment array” idea in “The Design and Implementation of Dynamic Hashing for Sets and Tables in Icon” but have seen it in many other places since. The “huge VM” paradigm shows up in many, many places as well; one of the most interesting is the “virtual span” idea in scalloc. I use a similar approach to nearly eliminate the need for compaction due to virtual memory fragmentation in a compacting GC for an MVCC database (compaction is generally required only for physical memory fragmentation, i.e., sparse allocations within a single page).
It was actually just after implementing a segment array that I thought of doing this. I’d been using the virtual memory ring buffer trick at work a few weeks before so it was fresh in my head, and I thought “wait, the page table could just put all my segments beside each other…”
You can exploit demand paging in so many applications. Another “huge VM” trick I found was to adapt linear hashing to a predetermined layout of page-size buckets in a single VM segment, so buckets can be split in any order (rather than strictly in order per standard linear hashing). It doesn’t matter how sparse allocated pages are, as long as 1) allocations within the pages themselves are dense, and 2) you can spare enough virtual memory (which you nearly always can).
As does pretty much every database which actually guarantees the durability of committed operations.
This seems really naive. It is not trivial to implement low-latency and high-throughput write-ahead logging and checkpointing from scratch. The systems which have managed to do so efficiently are commonly known as “databases”.
Databases also try to do so while processing several write requests concurrently and remaining scalable. Indeed, this is far from trivial, and traditional RDBMSs have been battle tested and refined over several decades to achieve this.
If your application’s input rate is low enough that you can accept limited horizontal scalability and lack of concurrency - then write-ahead logging and checkpointing really do become trivial.
Going to the symphony! Beethoven’s seventh.
Also taking a stab at implementing the Boyer-Myrvold planar embedding algorithm (PDF). I still don’t really know anything about graph theory, but after lots of reading this is pretty much the only paper that I can actually follow. And once I can calculate planar embeddings, I will have the final piece I need to finish implementing The Best Procedural Level Generation Algorithm I Have Ever Seen. (The canonical implementation just uses Boost for the graph theory stuff but like that’s no fun.)
Beethoven’s 7th is his greatest middle-period symphony IMO (probably most critics would rate the 3rd and 5th as more influential though). An almost equally great work that clearly (IMO) owes a huge debt to the 7th is Schubert’s 9th symphony.
Nobody knows how to correctly install and package Python apps.
That’s a relief. I thought I was the only one.
After switching to poetry, I’ve never really had any issues.
pip3 install --user poetry git clone... cd project poetry install poetry run python -m project
You can pull the whole install sequence in a Docker container, push it in your CI/CD to ECR/Gitlab or whatever repo you use, and just include both the manual and the docker command in your readme. Everyone on your team can use it. If you find an issue, you can add that gotcha do the docs.
Python is fine for system programming so long as you write some useful unittests and force pycodestyle. You loose the type-safety of Go and Rust, yes, but I’ve found they’re way faster to write. Of course if you need something that’s super high performance, Go or Rust should be what you look towards (or JVM–Kotlin/Java/Scala if you don’t care about startup time or memory footprints). And of course, it depends on what talent pools you can hire from. Use the right tool for the right job.
I’ve switched to poetry over the last several months. It’s the sanest installing python dependencies has felt in quite a few years. So far I prefer to export it to
requirements.txt for deployment. But it feels like about 95% of the right answer.
It does seem that without some diligence, I could be signing up for some npm-style “let’s just lock in all of our vulnerabilities several versions ago” and that gives me a little bit of heartburn. From that vantage point, it would be better, IMO, to use distro packages that would at least organically get patched. I feel like the answer is to “just” write something to update my poetry packages the same way I have a process to keep my distro packages patched, but it’s a little rotten to have one more thing to do.
Of course, “poetry and pyoxidize having a baby” would not save any of this. That form of packaging and static linking might even make it harder to audit for the failure mode I’m worrying about here.
I’d make an exception to this point: “…unless you’re already a Python shop.” I did this at $job and it’s going okay because it’s just in the monorepo where everyone has a Python toolchain set up. No installation required (thank god).
I think the same goes for running Python web apps. I had a conversation with somebody here… and we both agreed it took us YEARS to really figure out how to run a Python web app. Compared to PHP where there is a good division of labor between hosting and app authoring.
The first app I wrote was CGI in Python on shared hosting, and that actually worked. So that’s why I like Unix – because it’s simple and works. But it is limited because I wasn’t using any libraries, etc. And SSL at that time was a problem.
Then I moved from shared hosting to a VPS. I think I started using mod_python, which is the equivalent of mod_php – a shared library within Apache.
Then I used a CherryPy server and WSGI. (mod_python was before WSGI existed) I think it was behind Apache.
Then I moved to gunicorn behind nginx, and I still use that now.
But at the beginning of this year, I made another small Python web app with Flask. I managed to configure it on shared hosting with FastCGI, so Python is just like PHP now!!! (Although I wouldn’t do this for big apps, just personal apps).
So I went full circle … while all the time I think PHP stayed roughly the same :) I just wanted to run a simple app and not mess with this stuff.
There were a lot of genuine improvements, like gunicorn is better than CherryPy, nginx is easier to config than Apache, and FastCGI is better than CGI and mod_python … but it was a lot of catching up with PHP IMO. Also FastCGI is still barely supported.
nginx, uWSGI, supervisord. Pretty simple to setup for Flask or Django. A good shared hosting provider for Python is OpalStack, made by the people who created Webfaction (which, unfortunately, got gobbled up by GoDaddy).
You’re right that there are a lot of options for running a Python web app. But nginx, uWSGI, supervisord is a solid option that is easy to configure, high performance, open source, UNIXy, and rock solid. For dependency management in Python 3.x you can stick with pip and venv, remotely configured on your server via SSH.
My companies have been using this stack in production at the scale of hundreds of thousands of requests per second and billions of requests per month – spanning SaaS web apps and HTTP API services – for years now. It just works.
I’m curious, now that systemd is available in almost all Linux distributions by default, why are you still using supervisord? To me it feels like it is redundant. I’m very interested.
I think systemd can probably handle the supervisord use cases. The main benefit of supervisord is that it runs as whatever $USER you want without esoteric configuration, and it’s super clear it’s not for configuring system services (since that’s systemd’s job). So when you run supervisorctl and list on a given node, you know you are listing “my custom apps (like uwsgi or tornado services)”, not all the system-wide services as well as my custom app’s ones. Also this distinction used to matter more when systemd was less standard across distros.
Hm thanks for the OpalStack recommendation, I will look into it. I like shared hosting / managed hosting but the Python support tends to be low.
I don’t doubt that combination is solid, but I think my point is more about having something in the core vs. outside.
PHP always had hosting support in the core. And also database support. I recall a talk from PHP creator Ramsus saying how in the early days he spent a ton of time inside Apache, and committed to Apache. He also made some kind of data limiting support to SQL databases to make them stable. So he really did create “LAMP”, whereas Python had a much different history (which is obviously good and amazing in its own way, and why it’s my preferred language).
Similar to package management being outside the core and evolving lots of 3rd party solutions, web hosting was always outside the core in Python. Experts knew how to do it, but the experience for hobbyists was rough. (Also I 100% agree about not developing on Windows. I was using Python on Windows to make web apps from ~2003-2010 and that was a mistake …)
It obviously can be made to work, I mean YouTube was developed in Python in 2006, etc. I just wanted to run a Python web app without learning about mod_python and such :) Similarly I wish I didn’t know so much about PYTHONPATH!
I agree with all that. This is actually part of the reason I started playing with and working on the
piku open source project earlier this year. It gives Python web apps (and any other Python-like web app programming environments) a simple git-push-based deploy workflow that is as easy as PHP/Apache used to be, but also a bit fancier, too. Built atop ssh and a Linux node bootstrapped with nginx, uWSGI, anacrond, and acme.sh. See my documentation on this here:
Very cool, I hadn’t seen piku! I like that it’s even simpler than dokku. (I mentioned dokku on my blog as an example of something that started from a shell script!)
I agree containers are too complex and slow. Though I think that’s not fundamental, and is mostly Docker … In the past few days, I’ve been experimenting with bubblewrap to run containers without Docker, and different tools for buliding containers without Docker. (podman is better, but it seems like it’s only starting to get packaged on Debian/Ubuntu, and I ran into packaging bugs.)
I used containers many years ago pre-Docker, but avoided them since then. But now I’m seeing where things are at after the ecosystem has settled down a bit.
I’m a little scared of new Python packaging tools. I’ve never used pyenv or pipx; I use virtualenv when I need it, but often I just manually control PYTHONPATH with shell scripts :-/ Although my main language is Python, I also want something polyglot, so I can reuse components in other languages.
That said I think piku and Flask could be a very nice setup for many apps and I may give it a spin!
It’s still a very new and small project, but that’s part of what I love about it. This talk on YouTube gives a really nice overview from one of its committers.
In addition to @jstoja’s question about systemd vs supervisord, I’d be very curious to hear what’s behind your preference for nginx and uWSGI as opposed to caddy and, say, gunicorn. I kind of want caddy to be the right answer because, IME, it makes certificates much harder to screw up than nginx does.
Have you chosen nginx over caddy because of some gotcha I’m going to soon learn about very unhappily?
Simple answer: age/stability. nginx and uWSGI have been running fine for a decade+ and keep getting incrementally better. We handle HTTPS with acme.sh or certbot, which integrate fine with nginx.
That’s a super-good point. I’m going to need to finish the legwork to see whether I’m willing to bet on caddy/gunicorn being as reliable as nginx/uWSGI. I really love how terse the Caddy config is for the happy path. Here’s all it is for a service that manages its own certs using LetsEncrypt, serves up static files with compression, and reverse proxies two backend things. The “hard to get wrong” aspect of this is appealing. Unless, of course, that’s hiding something that’s going to wake me at 3AM :)
Why is Python’s packaging story so much worse than Ruby’s? Is it just that dependencies aren’t specified declaratively in Python, but in code (i.e. setup.py), so you need to run code to determine them?
I dunno; if it were me I’d treat Ruby exactly the same as Python. (Source: worked at Heroku for several years and having the
heroku CLI written in Ruby was a big headache once the company expanded to hosting more than just Rails apps.)
I agree. I give perl the same handling, too. While python might be able to claim a couple of hellish inovations in this area, it’s far from alone here. It might simply be more attractive to people looking to bang out a nice command line interface quickly.
I think a lot of it is mutable global variables like PYTHONPATH which is sys.path. The OS, the package managers, and the package authors often fight over that, which leads to unexpected consequences.
It’s basically a lack of coordination… it kinda has to be solved in the core, or everybody else is left patching up their local problems, without thinking about the big picture.
Some other reasons off the top of my head:
importmechanism is very dynamic, and also inefficient. So the language design kind of works against the grain of easy distribution, although it’s a tradeoff.
I don’t know, it’s been a long time since I’ve written any Ruby. All I know is that we’re migrating the Alloy website from Jekyll to Hugo because nobody could get Jekyll working locally, and a lot of those issues were dependency related.
Gemfile and gemspec are both just ruby DSLs and can contain arbitrary code, so that’s not much different.
One thing is that pypi routinely distributes binary blobs that can be built in arbitrarily complex ways called “wheels” whereas rubygems always builds from source.
Not true. Ruby has always been able to package and distribute precompiled native extensions, it’s just that it wasn’t the norm in a lot of popular gems, including nokogiri. Which by the way, ships precompiled binaries now, taking couple of seconds where it used to take 15m, and now there’s an actual tool chain for targeting multi arch packaging, and the community is catching up.
Hmm, that’s very unfortunate. I haven’t run into any problems with gems yet, but if this grows in popularity the situation could easily get as bad as pypi.
Thanks for the explanation, so what is the fundamental unfixable issue behind Python’s packaging woes?
I could be wrong but AFAICT it doesn’t seem to be the case that the Ruby crowd has solved deployment and packaging once and for all.
Related xkcd: https://imgs.xkcd.com/comics/python_environment.png
I just run
pkg install some-python-package-here using my OS’s package manager. ;-P
It’s usually pretty straightforward to add Python projects to our ports/package repos.
Speaking from experience, that works great up until it doesn’t. I have “fond” memories of an ex-coworker who developed purely on Mac (while the rest of the company at the time was a Linux shop), aggressively using docker and virtualenv to handle dependencies. It always worked great on his computer! Sigh. Lovely guy, but his code still wastes my time to this day.
I guess I’m too spoiled by BSD where everything’s interconnected and unified. The ports tree (and the package repo that is built off of it) is a beauty to work with.
I’m as happy to be smug as the next BSD user but it isn’t justified in this case. Installing Python packages works for Python programs installed from packages but:
pipto install some things you may end up with conflicts.
In my experience, there’s a good chance that a Python program will run on the computer of the author. There’s a moderately large chance that it will run on the same OS and version as the author. Beyond that, who knows.
I mean, we used Ubuntu, which is pretty interconnected and unified. (At the time; they’re working on destroying that with snap.) It just often didn’t have quiiiiiite what we, or at least some of us, wanted and so people reached for pip.
Yeah. With the ports tree and the base OS, we have full control over every single aspect of the system. With most Linux distros, you’re at the whim of the distro. With BSD, I have full reign. :-)
But it could still be the case that application X requires Python 3.1 when application Y requires Python 3.9, right? Or X requires version 1.3 of library Z which is not backwards compatible with Z 1.0, required by Y?
The Debian/Ubuntu packaging system handles multiple versions without any hassle. That’s one thing I like about it.
The ports tree handles multiple versions of Python fine. In fact, on my laptop, here’s the output of:
pkg info | grep python:
py37-asn1crypto-1.4.0 ASN.1 library with a focus on performance and a pythonic API py37-py-1.9.0 Library with cross-python path, ini-parsing, io, code, log facilities py37-python-docs-theme-2018.2 Sphinx theme for the CPython docs and related projects py37-python-mimeparse-1.6.0 Basic functions for handling mime-types in Python py37-requests-toolbelt-0.9.1 Utility belt for advanced users of python-requests py38-dnspython-1.16.0 DNS toolkit for Python python27-2.7.18_1 Interpreted object-oriented programming language python35-3.5.10 Interpreted object-oriented programming language python36-3.6.15_1 Interpreted object-oriented programming language python37-3.7.12_1 Interpreted object-oriented programming language python38-3.8.12_1 Interpreted object-oriented programming language
Fwiw, I’ve had good luck using Pyinstaller to create standalone binaries. Even been able to build them for Mac in Circleci.
It can feel a bit like overkill at times, but I’ve had good luck with https://www.pantsbuild.org/ to manage python projects.
I’m not sure I agree on the ‘Don’t Design for Multiple Cloud Providers’ point. It sounds as if his experience is designing for multiple cloud providers but deploying in only one. This means that your second-provider implementation is never tested and you’re always optimising for the first one. AWS probably isn’t going away, but a particular service in AWS that you depend on might. If AWS’s market position gets more entrenched, they can easily put up prices on you. This is what a lot of companies are deploying a mix of AWS and Azure: it prevents either company from putting up prices in a way that would seriously impact your costs. If Azure gets more expensive, shift the majority over to AWS. If AWS gets more expensive, shift the majority to Azure.
Advice that boils down to ‘don’t have a second source for your critical infrastructure’ feels very bad.
AWS knows this, and they’ve structured their pricing to make deploying to multiple clouds prohibitively expensive by making internal AWS bandwidth cheap/free, but AWS<->rest of the world super expensive. There’s no point hedging your bets against AWS. Either you’re all-in, or avoid them entirely.
I’ve worked for a company that had a policy of developing everything for AWS+Rackspace running in parallel. When AWS had an outage we could boast that we remained up, but it wasn’t even a big win, since most of our customers had other dependencies on AWS and were down anyway.
I agree with the author. I was going about kubernetes implementation and just had a realization that I was so focused on not tying my knowledge to one provider that I was wasting a lot of time. I specialized in Unix systems and other things in my career, and honestly AWS is so large that just being an AWS specialist is a skill in itself. Thankfully, the large cloud providers basically all clone themselves, and if I use terraform and such eventual porting is likely to not be impossible, but proving it constantly just isn’t worth my time. There are options to cost control AWS, but they already win on being the lowest cost of the top tier clouds when I last compared prices. If I ever need to shift, I’m sure I will have some months to do so and the cost of those months will likely be lower than the investment in always testing on two clouds. I do not like this reality, but I think it makes the most sense.
I agree that “being an AWS specialist is a skill in itself”.
But so is being an Oracle DBA, or a Solaris sysadmin, or a Microsoft ActiveDirectory admin… and I feel strongly that tying my employment skills/identity to a corporation that I don’t even work for has to be a mistake.
It doesn’t stop lots of people from being paid to do it. It’s just wrong for me. My whole life in computing has been about fundamentals lasting while market leaders come and go; that may be the effect of luck, though: IP, routing, switching, DNS, NTP, SMTP, SNMP, UNIX-like operating systems: I think all of these things will still be recognizable and valuable in 2050.
I started early enough that I was able to learn the fundamentals and experience things I think younger people are going to miss out on. It will affect their ability to troubleshoot at lower levels for sure. I am sad that I no longer get to work with routing and switching hardware outside of my home. I’ve always had the perspective that I should learn what I’m getting paid to use–and often just use that at home as well. I had Sun workstations at home when that was my life. I run my personal stuff on AWS to practice now, but still pay through the nose for a home switch with SNMP. AWS’ DNS and NTP are just going to be better than mine. I don’t use their SMTP due to cost, but I would love to never touch SMTP again. I run personal servers on DO and Alicloud also due to geo and pricing and honestly the terraform and other processes are not significantly different. If I’m being paid to make the best choices for someone, then I have to be open to everything AWS offers if they are on AWS. And, I’m still doing all of this because I enjoy that there’s always something new to learn. I would never make AWS my only skill.
The entire list seemed right, except for that point.
Yes, it’s hard to design for multiple clouds. Technically it’s very hard. However strategically, the chance of a single provider shutting you down or killing your favorite service or charging too much for you to survive… these are all more likely than the cloud provider going away.
Technically hard. But doing so successfully can make all the difference in a bad spot.
Usually this is only worth it if multi-cloud is actually part of your business proposition. If you’re not Hashicorp or Pulumi…probably not.
well the whole article is AWS specific (to the point I get the weird urge to run away), so I wouldn’t wonder
modern AWS depending customers are more a cult than anything else
I agree. While they might not go away, all big cloud providers have proven that they are not magically immune to large scale failure (and certainly not small scale), so as with everything in IT it makes sense to invest in a backdrop strategy.
It also can be good to keep things portable, because business decisions (internal and external) might require migrations.
For newer projects it’s also easier to at least aim for being portable in that regard. Using Hashicorp tools, using minio or seaweedfs or anything S3 compatible as well as Kubernetes (or nomad if you want to self manage) significantly reduce the amount of work required compared to only a few years ago
Yes, it’s not zero effort, but this being possible when you have big stakeholders that have huge interest to lock you in isn’t granted. Given this things became relatively simple.
I actually not so long ago had a client that ordered me to make an AWS setup work on GCP (partly technical, partly business reasons). They were not migrating, but required it to run on multiple clouds.
How quick that went surprised both them and me, but it certainly also was a well made setup in first place, so the list of things that had to be changed was short and easy to figure out.
I am sure it would have been a lot more complex only a few years before that, so maybe that recommendation is only true for older setups?
Everything comes with effort, but to anyone thinking about that I’d recommend actually looking into it a bit to see if it’s really as much effort as you initially might think.
A limited alternative to Optional for languages that don’t support it is to always use empty strings and empty collections in preference to null. Any code that iterates over strings or collections will also be cleaner, because it doesn’t need to special-case null.
The issue is when an empty string is a valid return value that carries distinct information from null.
a.k.a. the semi predicate problem
Python treating 0 as falsey causes so many problems… Treating empty string as null… No way that could go wrong.
Translating between SQL dialects is difficult but at least seems somewhat tractable. But trying to emulate (presumably bug-for-bug) the behavior of decades-old software with millions of LOC seems like a fool’s errand.
PS See also CompilerWorks: https://www.compilerworks.com (looks like they were acquired by Google Cloud after I interviewed with them).
The reason I’m interested is because I just want fake servers for integration tests. Right now I’m working through integration tests against SQL Server in docker and I’ll be doing the same thing for Oracle soon.
I’d much rather have a fake server speaking the wire protocol and returning hardcoded data a la https://github.com/jackc/pgmock because it’s just so much faster not to run Oracle or SQL Server in tests.
Ultimately I haven’t found anything like pgmock for Oracle or SQL Server yet.