Give Codeship a try

Want to learn more?

Elixir and Go have both grown significantly in popularity over the past few years, and both are often reached for by developers looking for high concurrency solutions. The two languages follow many similar principles, but both have made some core tradeoffs that affect their potential use cases.

Let’s compare the two by taking a look at their backgrounds, their programming styles, and how they deal with concurrency.

Foundations

Go/Golang has been developed by Google since 2009 and runs as a compiled native binary for the architecture where it’s deployed. It began as an experiment to create a new programming language that addressed the main criticisms of other programming languages while maintaining their strengths.

Go does an excellent job of achieving the balance of development speed, concurrency, performance, stability, portability, and maintainability that it set out to reach. As a result, Docker and InfluxDB are built with Go while many major companies including Google, Netflix, Uber, Dropbox, SendGrid and SoundCloud are using it for assortments of tools.

Elixir has been developed by Jose Valim at Plataformatec since 2011 and runs on the BEAM virtual machine, also known as the Erlang VM.

Erlang has been developed by Ericsson since 1986 for use in highly available, distributed phone systems. It has since expanded into numerous other areas, such as web servers, and has achieved nine 9s of availability (31 milliseconds/year of downtime).

Elixir was designed to enable higher extensibility and productivity in the Erlang VM while maintaining compatibility with the Erlang ecosystem. It achieves that goal by allowing use of Erlang libraries in Elixir code and vice versa.

For purposes of this article and to avoid repetition, we’ll refer to Elixir/Erlang/BEAM as “Elixir.”

A number of companies are using Elixir in production, including Discord, Bleacher Report, Teachers Pay Teachers, Puppet Labs, Seneca Systems, and FarmBot. Many other projects are built with Erlang, including WhatsApp, Facebook’s chat service, Amazon’s CloudFront CDN, Incapsula, Heroku’s routing and logging layers, CouchDB, Riak, RabbitMQ, and about half of the world’s phone systems.

Programming Style

The core principles of each run-time need to be understood to make a solid comparison of Elixir and Go, as these building blocks are where everything else is derived.

Go is a language that will be more familiar to people coming from a traditional C-style programming background, even though the language makes some decisions that favor a functional programming style. You’ll see static typing, pointers, and structs that will feel very familiar.

Functions can be created and attached to struct types, but the style is more composable to facilitate the growth of projects over time. Rather than embedding the functions within an Object that must be expanded, the function can be created anywhere and attached to the type.

If a method needs to be called with more than one type of struct, an interface can be defined for the method to provide greater flexibility. Unlike interfaces from typical object-oriented programming languages where an object must be defined initially to implement a specific interface, interfaces in Go are automatically applied to anything that matches them. Here is a good example of Go interface code.

Elixir favors a more functional style but blends some principles from object-oriented languages that make the transition seem less foreign.

Variables are immutable, and message passing is used so no pointers are passed around, meaning that function calls are very literal in their operation. Arguments are passed in, a result is returned with no side effects. This simplifies a number of aspects of development, including testing and code readability.

Due to immutable data, common operations such as for-loops aren’t available because incrementing a counter isn’t an option. Instead, recursion is used for these operations, although the Enum library provides common iterative patterns in a manner that is comfortable.

Because recursion is so heavily used, Elixir also utilizes tail call optimization, which prevents the call stack from growing if a function’s last call is a call to itself, avoiding stack overflow errors.

Elixir utilizes pattern matching everywhere, which is very similar to the way that Go leverages interfaces. With Elixir, a function can be defined as:

Using a map pattern as an argument, that function will only be called if a map with a key of data is passed in that has a nested map which contains a nifty key that has a value of "bob" and an other_thing key. The variable other would be set to its value.

Pattern matching is used everywhere, from function arguments to variable assignments and especially recursion. Here are a few good examples of pattern matching to make sense of it. Structs can be defined as types and then utilized in pattern matching as well.

These approaches are both very similar in principle. Both separate data structures from data operations. Both use matching to define function calls, Go via interfaces and Elixir via pattern matching.

Even though Go allows functions to be called by a specific type, g.area(), it’s essentially the same thing as calling area(g). The only difference in the two is that with Elixir, area() would have to return a result while Go could potentially manipulate a reference in memory.

Both languages are very composable because of this approach, meaning that large inheritance trees don’t have to be manipulated, extended, injected, or rebuilt over the life-cycle of a project. This is a significant boon for large projects over time.

The biggest difference here is that with Go, the patterns are defined outside of a function for reuse but can result in creating a lot of duplicate interfaces if they aren’t well organized. Elixir can’t reuse the patterns as easily, but the pattern is always defined in the exact place where it is used.

Elixir utilizes “strong” typing rather than static typing, and much of it is inferred. Within Elixir, there is no operator overloading, which can seem confusing at first if you want to use a + to concatenate two strings. In Elixir you would use <> instead.

This will seem tedious if you don’t understand the reason behind it. The compiler is able to use the explicit operators to infer that whatever is on either side of that plus sign must be a number. Likewise, either side of the <> must be a string.

Strong typing essentially means dynamic typing where the compiler (via dialyzer) can catch virtually every type, with the exception of ambiguous arguments in a pattern match (using an _ operator to indicate a variable or argument that isn’t used to avoid allocated memory for it, just as with Go). Code comments can be used to define types in these exception cases. The benefit of this is that you gain most of the benefits of static typing without losing the flexibility and meta programming perks that come from dynamic typing.

Elixir files can use a .ex extension for compiled code or a .exs extension for scripts that are compiled at run-time, such as shell scripts and so on. Go is always compiled, however the Go compiler is so fast that even with huge code bases it can feel almost instantaneous.

Concurrency

Concurrency is where the meat of the comparison comes from, and now that you have a basic overview of the language styles, the rest of this will make more sense.

Traditionally, concurrency has involved threads, which were more heavyweight. More recently, languages have taken to using “light threads” or “green threads,” which essentially use a scheduler inside of a single thread to let different logic take turns.

This type of concurrency pattern is much more memory efficient but relies on the run-time to dictate its flow. JavaScript has utilized this style for years in the browser. As a simple example, when you hear the term “non-blocking I/O” with JavaScript, it means that code executing in the thread relinquishes control back to the scheduler to do something else when an I/O operation begins.

Cooperative versus preemptive scheduling

Both Elixir and Go utilize a scheduler to achieve their concurrency models, although both languages naturally spread across multiple processors, while JavaScript does not.

Elixir and Go both implement scheduling differently. Go utilizes cooperative scheduling, which means that running code must relinquish control back to the scheduler for another operation to have a turn. Elixir utilizes preemptive scheduling, in which each operation has a preset execution window that will be enforced no matter what.

Cooperative scheduling is more efficient in terms of benchmarking, as preemptive scheduling creates additional execution overhead to enforce. Preemptive scheduling is more consistent, meaning that millions of small operations can’t be delayed by a single large operation that doesn’t relinquish control.

Go programmers have the ability to insert runtime.Gosched() in their code to force more check-ins with the scheduler as a precautionary measure for potential problem code. Run-time enforcement allows more trust of third-party libraries and real-time systems.

Goroutines versus processes

In order to execute a concurrent operation in Go, we use a goroutine, which is as simple as typing go before your method…any method. So we can go from:

hello("Bob")
// To...
go hello("Bob")

Elixir is very similar in this regard. Instead of goroutines, you spawn processes (not OS processes, for the sake of clarity). Also note that functions must be inside modules in Elixir.

The main difference between what’s happening here is that the go operation returns nothing while the spawn operation returns an id for the process.

Both systems utilize a similar communication style with these routines via messages queues. Go calls them channels, while Elixir has process mailboxes.

With Go, a channel can be defined so that anything can pass messages to if it has the channel reference. With Elixir, messages are sent to a process either via the process id or a process name. Channels in Go are defined with types for the messages, while process mailboxes in Elixir utilize pattern matching.

Sending messages to an Elixir process is equivalent to sending a message to a Go channel monitored by a goroutine. Here’s a simple example:

Both have the ability to set timeouts when listening for messages as well. Because Go has shared memory, goroutines can also directly transform an in-memory reference, although a mutex lock must be used to avoid contention. Ideally, a single goroutine should listen on a channel for updates to shared memory to avoid the need for a mutex lock.

Beyond this functionality is where things start to expand.

Erlang defines a set of patterns for best practices when utilizing concurrency and distribution logic all bundled under “OTP”. In most cases with Elixir code, you’ll never touch the raw spawn and send/receive functions, deferring to the abstractions for this functionality.

Wrappers include Task for simple async/await style calls; Agent for concurrent processes which maintain and update a shared state; GenServer for more complex custom logic.

In order to constrain maximum concurrency to a particular queue, Go channels implement buffers which receive a defined number of messages (blocking the sender of at the limit). By default, channels block until something is ready to receive the messages, unless that buffer has been set.

Elixir process mailboxes default to unlimited messages but can utilize Task.async_stream to define max concurrency for an operation and blocking senders in the same way as limited buffers on a channel.

Routines in both languages are inexpensive: goroutines are 2KB each, while Elixir processes are 0.5KB each. Elixir processes have their own isolated heap spaces which are individually reclaimed when the process finishes, while goroutines utilize shared memory and an application-wide garbage collector to reclaim resources.

Error handling

This is probably the single biggest difference in the languages. Go is very explicit about error handling at all levels, from function calls to panics. In Elixir, error handling is considered “code smell.” I’ll take a second to let you read that again.

So how does this all work? Remember earlier when we talked about Elixir’s spawn call returning a process ID? That’s used for more than just sending messages. It can also be used to monitor the process and check whether it’s still alive.

Because processes are so inexpensive, the standard mode of operation in Elixir is to create two. One to run the process and another to supervise the process.

This approach is called the supervisor pattern, and Elixir applications tend to operate within a supervision tree. The supervisor spawns the process with a different function called spawn_link under the hood that will crash the spawning process if the spawned process itself crashes. Supervisors handle these and instantly restart the process.

Here is a simple example using a supervised process to do division. Dividing by zero crashes the process, which the supervisor immediately restarts, allowing future operations to continue. It’s not that error handling doesn’t exist, it’s just implemented by supervisors transparently.

By contrast, Go has no way of tracking the execution of individual goroutines. Error handling is very explicit at every level, leading to a lot of code that looks like this:

The expectation here is that error handling will exist at the point where an error can occur, whether in a goroutine or not.

Goroutines can pass error cases to channels in the same way. However, if panics occur, each goroutine is responsible for having its own recovery condition met, or it will crash the entire application. Panics are not equivalent to exceptions in other languages as they are intended to be system level “stop everything” events.

This condition is perfectly encapsulated by an Out of Memory error. If a goroutine triggers an Out of Memory error, the entire Go application will crash even with proper error handling because of the shared memory state of the run-time.

With Elixir, because each process has its own heap space, a max heap size can be set per process that would crash the process if reached but would then be independently garbage-collected and restarted without affecting anything else.

That’s not to say that Elixir is bullet proof. The VM itself can still run out of memory via other means. But it’s a containable problem within processes.

This isn’t intended to be a knock against Go either. This is a problem faced by virtually every language with shared memory, meaning most every language you’ve ever heard about. It’s specifically a strength of the way that Erlang/Elixir was designed.

Go’s approach forces developers to handle errors directly at the point that they would occur, which requires explicit design thought and can lead to very well-thought-out applications.

The main point of the Elixir model is an application that can be expected to run forever, as Joe Armstrong put it. Just as you can manually call the scheduler with Go, you can also manually implement a version of supervisors in Go via the suture library.

Note: Within handlers that you’ll implement with Go for most servers, panics are already addressed. Therefore, a crash within a web request that wasn’t critical enough to kill the entire application won’t. You’ll still have to address it on your own goroutines though. Don’t let this explanation imply that Go is fragile, because it is not.

Mutable versus Immutable

Understanding the trade-offs that come from mutable versus immutable data is important in a comparison between Elixir and Go.

Go uses the same style of memory management that most programmers are experienced with via shared memory, pointers, and data structures that can be changed or reassigned. For dealing with transformations of large data structures, this can be much more efficient.

Immutable data within Elixir utilizes copy-on-write. That means that within the same heap space it really is just passing a pointer to the data, but as soon as you want to do something to it, a new copy is created.

A list of values, for example, would pass a list of pointers to those immutable values in memory, while sorting would return a list of pointers in a different order since the values in memory themselves can be relied on to go unchanged. Changing a value in the list would return a new list of pointers, including a pointer to the different value. If I want to pass that list to another process however, the entire list including values would be copied to the new heap space.

Clustering

The other trade-off that comes from mutable versus immutable data comes from clustering. With Go, you have the ability to make remote procedure calls very seamlessly if you want to implement them, but because of pointers and shared memory, if you call a method on another box with an argument that references to something on your machine, it can’t be expected to function the same way.

With Elixir, because everything operates with message passing, the entire application stack can be clustered across any number of machines. Data is passed into a function that returns a response. There is no in-memory transformation that occurs as the result of any function call, which allows Elixir to call functions on different heap spaces, different machines, or different data centers entirely in the exact same way as any other function in its local heap space.

Many applications don’t require clustering, but there are a number of applications that benefit from it significantly, such as communications systems like chats where users are connected from different machines or horizontally distributed databases. Both have common solutions available via the Phoenix framework’s channels and Erlang’s Mnesia database respectively. Clustering is critical to any application’s ability to horizontally scale without depending on bottlenecked central relay points.

Libraries

Go has an extensive standard library which will allow most developers to do virtually anything without needing to reach for third-party packages.

Both Elixir and Go have plenty of third-party packages available. Go uses a direct go get command to import packages remotely, while Elixir uses Mix, a build tool that invokes the Hex package manager in a manner that will be familiar for users of most languages.

Go is still working to standardize a full package-management solution across the language. Between that and the extensive standard library, most people in the Go community seem to favor sticking with the standard lib where possible. There are several package-management tools already available.

Deployment

Go deployments are straightforward. A Go application is compiled into a single binary including all of its dependencies that can then be run natively, cross platform, wherever it’s going. The Go compiler can compile binaries for just about any destination architecture, regardless of the type of machine that you’re running on. This is one of Go’s greatest strengths.

Elixir actually comes with a lot of deployment options, but the primary method is via the excellent distillery package. This wraps up your Elixir application into a binary with all of its dependencies that can be deployed to its destination.

The biggest difference between the two languages is that compilation for the destination architecture has to be done on that same architecture with Elixir. The documents include several workarounds for this scenario, but the simplest method is to build your release within a Docker container that has the destination architecture.

With both of those approaches, you just stop the currently running code, replace the binary, and restart it as you would with most blue-green style deployments today.

Hot reloading

Elixir also comes with another deployment option that the BEAM makes possible. It’s a little bit more complex, but certain types of applications can greatly benefit from it. It’s called a “hot reload” or “hot upgrade.”

Distillery goes out of its way to make this easy by just letting you tac on the --upgrade flag to your release build command, but that still doesn’t mean you should always use it.

Before talking about when you’d use it, we need to understand what it does.

Erlang was developed to power phone systems (OTP stands for Open Telecom Platform) and currently powers about half of them on the planet. It was designed to never go down, which is a complicated problem for deployments when you have active phone calls running through the system.

How do you deploy without disconnecting everybody on the system? Do you stop new traffic from coming to that server and then politely wait for every single call to finish?

The answer is no, and that’s where hot reloads come in.

Because of the heap isolation between processes, a release upgrade can be deployed without interrupting existing processes. The processes that aren’t actively running can be replaced, new processes can be deployed side by side with currently running processes while absorbing the new traffic, and the running processes can keep on trucking until they finish their individual jobs.

That allows you do deploy a system upgrade with millions of calls and let the existing calls finish on their own time without interruption. Imagine replacing a bunch of bubbles in the sky with new bubbles…that’s basically how hot reloading works; the old bubbles hang around until they pop.

Understanding that, we can potentially see some scenarios where it might come in handy:

A chat system utilizing websockets where users are connected to specific machines

A job server where you may need to deploy an update without interrupting jobs in progress

A CDN with huge transfers in progress on a slow connection next to small web requests

For websockets specifically, this allows deployments to a machine that might have millions of active connections without immediately bombarding the server with millions of reconnection attempts, losing any in progress messages. That’s why WhatsApp is built on Erlang, by the way. Hot reloading has been used to deploy updates to flight computers while planes were in the air.

The drawback is that hot reloading is more complicated if you need to roll back. You probably shouldn’t use it unless you have a scenario where you really do need it. Knowing you have the option is nice.

Clustering is the same way; you don’t always need it, but it’s indispensable when you do. Clustering and hot reloads go hand in hand with a distributed system.

Conclusions

This article has been a long journey, but hopefully it provides a solid overview of the differences between Elixir and Go. The most helpful way to think about these two languages that I’ve found is to think of Elixir as an operating system and Go as a specialized program.

Go works extremely well for engineering exceptionally fast and very focused solutions. Elixir creates an environment where many different programs can coexist, operate, and talk to each other without interfering with each other even during deployments. You’d utilize Go to build individual microservices. You’d build multiple microservices within a single Elixir umbrella.

Go is more focused and simpler to pick up. Elixir is very straightforward once you get the hang of it, but the world of OTP and expanse of Erlang can be intimidating if your goal is to learn it all before you use it.

Both are excellent languages that are at the top of my list of recommendations to do virtually anything in programming.

Subscribe via Email

Over 60,000 people from companies like Netflix, Apple, Spotify and O'Reilly are reading our articles. Subscribe to receive a weekly newsletter with articles around Continuous Integration, Docker, and software development best practices.

We promise that we won't spam you. You can unsubscribe any time.

Join the Discussion

Leave us some comments on what you think about this topic or if you like to add something.

liveweird

Good post. One, small correction though – Elixir’s package manager is named Hex. Mix is the build tool.

Really enjoyed this write-up. Two minor corrections: OTP stands for Open Telecom Platform (not protocol) and most refer to what you call “hot code reloading” as either “hot code swapping” or “hot code loading” as opposed to “reloading”. Just nits :)

evilnode

Very nice post. I’ve never looked at Elixir before, and this article makes me want to give it a look. You noted that both languages require you to build for your target architecture from the same architecture. That’s actually not the case with Go. You can build for any architecture from any architecture (with some minor exceptions for ARM). For example, to build for Windows, you simply add `GOOS=windows` before your `go build` command.

Well, Elixir (and Erlang) are VM based, with bytecode. It will run on any system & architecture without recomplilation, as long as there’s BEAM VM for it, and you don’t use native code parts, known as NIF (similar to Java – platform independent .class files as long as you don’t use JNI)

Qqwy

Great article!

Here are a couple of minor details about Elixir that you might want to correct:

– Elixir does support operator overloading, but most libraries do not use it as it tends to break the principle of least surprise (For instance, it is hard to write functions that work inside the so-called guard clauses that are part of pattern matching, but most people expect that operators always are.) Also, in mathematics it is a lot more common to use “ or `.` instead of `+` to describe concatenation. Elixir is by far not the only language that uses “ to do this.

– Elixir’s ‘let it crash’ does not imply ‘let it burn’. The idea is that you only spend time on handling exceptional cases that you expect (i.e. that are not that exceptional at all), but you also write your application in such a way that íf an unexpected thing happens, then this problem remains as self-contained as possible, instead of having to worry that your whole system might come crashing down.

– When doing hot code reloading, old processes do not stay around ‘until they pop’, but rather their code is upgraded one-by-one by calling a special function called `code_change`, which transforms the process’ current state structure to the new one. This is possible because every process is self-contained and the internal data is immutable.

—- Great to see these systems side-by-side, especially since I am completely new to Go :-).

“With Go, a channel can be defined so that anything can pass messages to if it has the channel name.”

Misleading. You can’t refer to channels by a name. You have to specifically hold a reference to the channel object. Meaning you can know a name for it in some random part of the system and globally interact with the channel like and id value.

“Go channels implement buffers which can either receive a a certain number of messages (blocking the sender if at the limit) or unlimited messages which could have a memory constraint”

Go does not have unlimited buffered channels. Channels are by default unbuffered so that their behaviour is to provide synchronisation between a sender and receiver. You can choose to bugger them up to an amount defined when the channel is created, which allows sends to become non-blocking until the buffer limit has been reached.

Thanks for the comparison. Until now I only tried Go so far and I love it (except for the error handling, which desperately needs some syntactic sugar). I find the go get command a little understated in the comparison, since it can use any hosted git repository which is very handy.

Elixir/Erlang is one of my top candidates for my next language. Don’t understand how someone can replace such a beautiful language with something as crude as C++. What C++ sells as object orientation is just s***. If you want to know what object orientation is about try Smalltalk.

Thx for the Go blog link, I didn’t know about it before. On the one hand this might help a bit, but also feels a bit cheap. In reality we do not call always the same function, and therefore the signatures differ.

I mean the whole idea of an error buffer is great and could be useful if golang would allow us, to define any function as a functions parameter ‘which has an error as first return value’ and would simply return all other values no matter what type they are (which would be quite simple in bash for example :-D no, I didn’t compare Go to Bash ;-).

But afaik this is not possible (maybe with the slow reflect package, have to look into that) and therefore, you would have to define a lot of signatures for the error buffer before being able to use it.

Nevertheless, its not your fault and thanks again for the read, it helped to guide my thoughts into a more constructive way about this topic of error handling in Go.

” Go utilizes cooperative scheduling, which means that running code must relinquish control back to the scheduler for another operation to have a turn.”

True-ish, Go previously only yileded control back to the scheduler on sys calls, nowadays its on function calls too. so even if it is not the real deal, most code do call functions, making the Go way alot closer to the BEAM reductions than say C# async await or similar.

After using Elixir for about a year and Go for several years, what I find most interesting is how they have very different approaches to reach the same goals.

For example, both Erlang and Go have an emphasis on reliability. Elixir has immutable values, super visor trees, and hot code reloading. But Elixir is a dynamic language with a virtual machine whereas Go looks to static typing and compiled binaries to help with reliability.

Their concurrency models are quite different to work with, but both have green threading that scales to multiple cores, making them both good choices for web services and web applications.

The remaining edits should be done at some point today. I was unavailable most of Friday when this post was really getting traction or they would have been corrected sooner. Thanks for everybody’s feedback.

Hey, I’d like to ask for your permission to translate this article into Korean – I think it’s a great introductory article for Elixir and Go. If you do give me permission to do so, then let me know if there’s any personal and/or company policy for translation. In the absence of any particular policy, at the top of the post I put a link to the original article and state that the Korean version is a translation.

Alex Lattaro

Hy Barry,

Great Article! Congratulations!

My name is Alex Lattaro and I am Community Manager and Content Leader in iMasters. IMasters is one of the largest Brazilian portals for developers (imasters.com.br).

The reason for my contact is to request your permission for us to translate some of articles on our website.

I readed this article and find it very interesting

So I logged into codeship site and found many other good articles. Would you authorize us? It would be of great value to the Brazilian public.

All your content that is published on iMasters will leave your name and you will have an international iMasters author profile. To illustrate how this process works, follow an example link:

Very nice comparison. I really liked that you went so deep on the two language. One nit I might make is error handling being a code smell in Elixir – I think you mean unexpected error handling is a code smell. There are still of course many benefits to gracefully handling expected errors (e.g. non-successful API requests)

Mehdi Shojaei

Great article. You compared features of both side by side nicely. Thanks a lot.

Terry J Leach

Great article Barry! Article like this is why I subscribe to the Codeship newsletter. Most technology comparison post involve alot of subject and not objective comparisons. Thanks!

Really well written, and seems to come from the perspective of someone who’s actually written apps in both languages.

Talis

Once vested time goes into comparing late model languages, performance needs to rank alongside these abundant features , something always ignored by abstraction coders.. who get excited quoting relatively useless “discovery” as this Quik Left post i say useless, as it does not load a server up with a million active sessions ..(needing fancy scheduler management) Entire S/W landscape is void of decent benchmark, for eg: WHY defer to slower abstractions ??? WHY not explain to satisfy the mind, find unanswered questions leaving technicals way behind.. Same with manual writers, they dont know any better, caught up in features ..

with Elixir code, you’ll never touch the raw spawn and send/receive functions, deferring to the abstractions for this functionality. WHY ? If i had to service 1mil sessions, dont think it could be done in Elixir, unless a 10min latency is acceptable.

created 3 models with migrations, but ultimately failed to implement the controller actions for them correctly, so I scrapped it and just used mock data. holy Batman, making those models and migrations was dead-simple because of the generators.

Speed

If I were 1) using the database and 2) serving from a real server (not localhost), I’m guessing this would be slower. . Here’s the server log with response times:

[info] GET /api/shops … [info] Sent 200 in 756µs [info] GET /api/shops … [info] Sent 200 in 774µs [info] GET /api/shops … [info] Sent 200 in 752µs [info] GET /api/shops/1 … [info] Sent 200 in 920µs

Behold, MICROSECONDS. Have you ever seen that µ symbol in a server log before? My team mate had to Google it. U dont say ,, Really ?

WHEN are coders going to realize serving one page in a loop, only shows stupidity? While i have incomplete reference to million open session http// server tests, where may rock solid code be found to run on a FreeBSD small cluster (that separates the database from a failsafe pair of servers dealing 1mil PF_ring sessions.. Why is the most basic function of serving (defined packets , not animated web ) so hard to find.. Any reader gets excited about ConnectX3 VPI with Infiniband RDMA intercluster comms .. with PF_ring packet process and thinks Elixir can cut the mustard, the hardware is at Santa Clara.. From a database priority, Elixir would waste too much time spawning pointers / copying data even for read only DB tables..? When data is sent to a client request (is there a copy op taking place ?? this is the part from article

“within the same heap space it really is just passing a pointer to the data, but as soon as you want to do something to it, a new copy is created. ”

can u see how unsatisfying such wording is ? all data sits on server RAM (1TB) each request finds a best match (how does pattern matching perform when one million clients are sending a singe packet update each second, is Elixir able to deal with this loading // 64 byte chunk) from the above 752uS spec i doubt it