3 ms·
`async: false` is the worst
- jbm 3y ago> async: false is the worst I suppose the reason is because it takes more time? (The article doesn't mention, and just takes it as a given)
- scythmic_waves 3y ago> I suppose the reason is because it takes more time? I assume so. I didn't even notice that the article didn't motivate that `async: false` is bad. I always avoid it if I can since you might as well perform independent tests concurrently. From the docs [1]: * `:async` - configures tests in this module to run concurrently with tests in other modules. Tests in the same module never run concurrently. It should be enabled only if tests do not change any global state. Defaults to `false`. [1]: https://hexdocs.pm/ex_unit/main/ExUnit.Case.html#module-tags https://hexdocs.pm/ex_unit/main/ExUnit.Case.html#module-tags
- jcparkyn 3y agoOne feature I've never seen in test frameworks that I think would be good: Instead of concurrency being a boolean on/off, tests should be able to declare a list of shared dependencies (e.g. using arbitrary string identifiers). Then the test runner could make sure no two tests with the same dependency run at the same time, but still use as much concurrency as possible. You could extend this further by marking certain dependencies as read-only, so the read-only tests can still run in parallel with each other.
- kevingadd 3y agoI wouldn't have expected this much dependence on global state in erlang software. I thought erlang was all about encapsulation, but I guess some problems are basically universal to production software no matter what language you use, and global state is one of them
- jerf 3y agoYou have to be careful about your definition of "global state". We kind of have this definition based on C programs from the 1970s, but it doesn't always trivially translate into more modern architectures. BEAM programs (Erlang/Elixir) are basically microservice architectures by default. They lack global state in the sense that there is no way for process (in the BEAM sense, not the OS sense) A to reach into process B and directly manipulate the values. That is the form of isolation that BEAM and its accompanying languages offer. But for any network architecture to function, there's always going to be unique identifiers for the actors within the network, and that is intrinsically at a higher level than a single process. It doesn't have to be "global", in that two processes may have a different idea what it reaches if it asks for "leader", but it's certainly bigger than the single process. And yeah, there's not a lot that can be done about the fact that's going to be somewhat global, and if the identified network targets are not in the local OS process, intrinsically mutable in the sense that the state of those services can at the very least go down without notice. In the modern world there are many useful definitions of "global state", some of which as I suggested may not even be "global" but may be in some scope that lives between "global" and "local", and depending on which definition you choose, various languages may have different "globalnesses" in their state. (Another common example is, "does the language have global variables?" Many languages have the concept of "packages", that you can import once into the system, and everyone will see the same "print" package. That package may have variables, like for example maybe "print.default_format". Is that a global variable? Well, in the old C sense, maybe not. "default_format" can not be reached simply with "default_format" from all locations in the program, such that is globally defined. It can't be reached at all from places that did not import "print". But if what you're concerned about is whether changes made by one module are witnessed by all, they are, so from that sense it is a modern global variable, even if more nicely organized than traditional ones. There isn't a right or wrong answer here, it depends on the definition you choose which answer you get.) In general, immutability at the value level does not automatically make things encapsulated. It more easily affords it, but it doesn't just happen.
- Quekid5 3y agoI think a slightly more succinct way of thinking about mutability in actor systems is this: (I think it's from Erik Meijer, but not quite sure.) Ultimately, any actor system has global mutable state via message queues. You can perfectly emulate a shared mutable variable by just sending it back and forth between message queues. (At least it's only atomically modifiable, I suppose.) It's very different from abient authority to modify a mutable variable, but still...
- loeg 3y ago(Elixir)
- benmmurphy 3y agothis sounds suspicious. bits of code are going to be walking the whole process tree looking for something that is not going to be there when the code is running in production. i guess the operation is relatively inexpensive so maybe it is ok.
- jbsteadman 3y agoHey, I wrote the blog post under discussion. It's good to bring suspicions! But yeah, the rarest situations aside, there's not going to be any relevant performance impact. For one thing, in production, for a given key, the process tree will be "climbed" only once by any calling process. Thereafter, ProcessTree will cache, in the process dictionary of the calling process, either the value it "finds" in the tree, or a provided default value. For any later requests for the data, the calling process will find the cached value in its own process dictionary with a single, local lookup. Along with persistent terms, a process looking in its own dictionary is as fast as data access gets on the BEAM. Faster, for example, than reading from the global application environment. In situations that warrant micro-optimization, the process dictionary is something to be leaned into, not away from. In considering the initial "climb", we're talking about accessing the dictionaries of maybe 10-ish parent processes. In typical situations like mounting a LiveView, time spent climbing the tree will be dwarfed by other factors. The main theoretical performance issue of climbing the tree is copying the full contents of other processes' dictionaries into the space of the calling process. Unless there's drastically large amounts of data in the parent dictionaries, it's not going to matter. Even then, OTP 26.2 introduced a new API for accessing specific keys in the dictionaries of other processes. Once an upcoming Elixir release supports 26.2, ProcessTree will integrate the new API, and the theoretical impact of copying full dictionaries will no longer be an issue.