$ cargo run
Compiling todo-list v0.1.0 (file:///Users/yannsimon/projects/rust/rust-playground/todo-list)src/main.rs:27:38: 27:50 error: the type of this value must be known in this context
src/main.rs:27 with_todo_id(todos, todo_id, |todo| todo.deleted =true); ^~~~~~~~~~~~
if needed, update the sbt version in project/build.properties:
update the build.sbt:
I also had to replace playVersion.value with play.core.PlayVersion.current.
The goal of this first step is to be able to load the application with sbt, even if the application itself does not compile. At this point, Intellij can load the application with a play 2.4 version, and can provide auto-completion.
Disclaimer: I am continually learning, and this post reflects my current understanding of the topic. I may be wrong. Do not believe directly what I write. Test what you need. If you want to provide some precisions or corrections, please contact me on twitter, and I’ll fix this post.
He explains the details very well. If you have not read that post, I recommend it highly.
The main point if that Scala Future adds a context switching for each map or flatMap.
With Scalaz Task, we have to describe which tasks need a new thread, the other ones are called on the same thread as the previous computation, avoiding these context switchings.
With Scala Futures, if you want to multiply the result of a Future[Int] by 2, you need an ExecutionContext (EC):
To compute the i => i * 2, the ExecutionContext may use a different thread than the one having the result of the futureCountOfUsers. We observe a context switching between the future and the callback in the map. And the thread executing i => i * 2 can run on a different CPU/core than the one having the result of futureCount, meaning that the CPU cache is missed.
This overhead is not problematic for simple computations. But if we do 100 or 1000 of them, then it can have a significant impact on performances.
And in my opinion, Scala Futures have other downsides.
My first impression with Scalaz Tasks is that they have a better design than the Scala Futures.
But I have not used Scalaz Tasks extensively and cannot say if they have other problems.
But all in all, Scala Futures are here to stay. They are part of the standard API and are used everywhere.
I’m still wondering why the Scala Futures were designed that way.
I can only guess, but I think this avoids some common pitfalls:
“easy” for new-comers: simply import the default execution context and that’s all
safe by default: If a callback takes a long time (blocking IO and expensive computation), this callback will not block other ones. The execution context will be able to use a different thread for other computations.
and a design like Scala Tasks works well if all parts of the system are non-blocking and using one thread pool. The reality is more complex. Each driver/http client can use its own thread pool. For example, an asynchronous http client may have its own thread pool because some parts of the networking API in Java is blocking like the standard ns lookup InetAddress.getByName(). Running a computation directly on the thread without forking it will run the computation of the thread pool of the http client. And that can lead to an exhaustion of the thread pool of the http client, and the http client cannot function anymore.
Introducing the trampoline execution context
This performance problem with the standard execution context is not new. The play framework team had this problem, especially with Iteratees that compute everything with a Future and uses callbacks extensively on stream of data.
To solve this problem, James Roper introduced the trampoline Execution Context.
This trampoline execution context is a great piece of software:
it makes sure the callbacks are run on the same thread than the future to avoid context switchings.
it does not overflow with recursive callbacks.
To show the benefit of the trampoline execution context, let’s call this function that does not make any sense, but simply calls Future.map n times:
On the other hand, servlet containers traditionally use a thread per request. When doing IO (database access, external web request), the thread waits for the IO completion (blocking IO). That’s why servlet containers need a lot of working threads to handle requests in parallel (default 200 threads max for tomcat)
Asynchronous 3.0 servlet
Servlet 3.0 containers introduced the possibility to “suspend” a request.
For example, if an application makes an HTTP request to another web service using the WS client, the play2 war plugin is able to suspend the servlet request until the external web service answers. It means that with the same number of servlet threads, a servlet 3.0 container can support more requests in parallel than a servlet 2.x container. It does not need that a thread waits for an HTTP request, this thread can be used for other requests.
Limitations of asynchronous 3.0 servlet
When uploading or downloading a big file, the servlet container is able to stream the data. But between each chunks, the servlet thread is blocking, waiting for the next one.
It is not possible to consume one chunk, and later, when we are ready to consume another one, tell the container: “now I am ready, you can send me the next chunk as soon as you receive one from the browser”.
If a browser needs an hour to upload a file with a slow Internet connection, the container needs a thread during an hour, even if the application does not do anything, just waiting for the upload completion.
Play applications deployed as war are limited by the servlet container
A Play application deployed in a war container is limited by the technical possibilites of the servlet API. With a servlet 2.x or 3.0, a Play application does not scale as well as when run natively.
This feature should be used especially if the application is using big download/upload. The servlet 3.1 will help to scale much better.
During my tests, I could upload and download files from several GB in parallel. The container and the JVM garbage collection could support that without any problems. The memory usage was very low.
Also please test this new version and report issues!
How to build the application to scale better?
To scale as much as possible, the application should not block. It should always use asynchronous IO API like in the WS client.
But in the Java World a lot a librairies are still designed for a one-thread-per-request model and do not provide asynchronous API. It is for example the case for a JDBC driver. In that case, a separate dispatcher should be configured to handle blocking IO. More Information for this can be found in the Play Framework documentation.
I had to find a good way to implement the glue between two very different APIs. The servlet 3.1 API is quite imperative and use EventListener and methods with side effects. The Iteratee API is functional and I needed some time to feel at ease with it.
The servlet 3.1 specification was recently finalized as I began. The first implementations in some containers contained bugs. Reporting the problems, explaining, convincing other developers took a lot of time and energy.
The servlet 3.1 specification is not always explicit. The implementations in the different servlet containers are also different. Finding an implementation that covers all these subtle differences was challenging. The testing infrastructure of the play2-war plugin provides a lot of integration tests with different containers and helped a lot for this.
My firm Leanovate gave me some time to work on that. Having worked two days full time on it helped me a lot. Thanks Leanovate for this!
All in all it was a great experience to contribute to the WAR Plugin, and I hope my work will be useful for others.
At the beginning at the year, I had the chance to present how to organize a play application with the Cake pattern at Ping Conf.
I showed how this pattern enable designing the application as components, how to reduce visibility of specific gateway’s model only to components that need it. One side-effect of the cake pattern is that it allows a dependency injection resolved at compilation time.
In one of my last slides, I warned against abusing a dependency injection mechanism to write too much unit tests.
To stay within the time slot, I have not developed so much my concerns about that point.
During the talk, I implemented an application to demonstrate my points. This application consumes two external web services to render HTML pages. It is quite a typical application we can see in an infrastructure build with micro-services.
Let’s dig into the details how this new version differs from the ones build around the Cake pattern.
Traditional view of unit tests
When building an application, we usually structure the code into layers to separate responsibilities, thus enabling re-use of logic, and avoiding repetition.
In the demo I used for the talk, the application is for example layered into views, controllers, services and gateways. All these layers have access to a common model.
A traditional approach of unit test is to consider one class or function of one layer as a unit to test. The other dependent layers are mocked.
For example, to test the service layers, we use mocks for the gateways, simulating responses from external web services.
This approach works well, but has several downsides:
the unit tests prove that one layer is working as expected, but they said nothing about all the layers used together.
the unit tests use the internal details of the implementation. Re-factoring the code implies then to change a lot of tests.
By using dependency injection and mocks, it is nowadays very easy to write unit tests. The effect if some applications are tested almost only with unit tests:
Traditional view of component tests
To complement the unit tests, a team can decide to implement some component tests.
For the sample used in the talk, the component is the font end application we are currently implementing.
The most common way to run component tests is to start the tested application. The application is configured not to use the real external web services, but some local mock implementations. The local mock implementations are started as http servers as well, running on different ports.
When the application and all the mocks are started, we can test the application by sending some http requests and by analyzing the responses.
Setting up the test environment with this approach is quite complex. For each external web service, a mock must be implemented as a real local http server. We must start all mock implementations, and then the new configured application. At the end of the tests, we must shutdown all services, even in case of exceptions.
But the main drawback with this approach in my opinion is that running the tests take a lot of time, too much to be integrated in a normal development flow (write code -> compile -> test)
An alternative approach between component and unit tests
To strictly adhere to the definition of component tests, we should treat the tested application as a black box, and simulate all external web services. We saw that this approach is somewhat heavy to use: each external web service must be mock with a real http server.
Starting and running the tests in that configuration take time. Debugging an error can be difficult.
The strategy I used in new version of the demo application (TBA_07) is a little bit different.
The idea is still to use a request / response to test the application, but without having to run the application and any external web services.
Implementing that is actually quite easy: each layer declared as dependency an HTTP client (a WSClient in play framework 2.3)
The http client is a dependency at the top (controllers' layer):
The application is constructed as if it was depending from the http client and the current play application.
These tests are not strictly component tests, as we are not testing the real implementation of the http client.
The application is not entirely treated as a black box. But most of the code is tested like in production.
Drawbacks of this approach:
writing a test is more complicated than testing a little unit of code
writing unit test can help avoiding code mixing responsibilities. We do not profit from that.
when a test suddenly fails, it is more difficult to find out why.
we do not test the complete application stack. For example, the play filters and the real http client is not tested.
Advantages of this approach:
a developer must understand how the application works in general to be able to write good tests
the application can be re-factored without modifying the tests
the user functions are better tested
the integration of all layers together is tested
we do not need any running server to check all the tests. The tests run very fast.
With one team, we are currently testing this approach. The results are quite encouraging: more than 80 % of the statements are covered by tests. We have more than 200 tests running in 10 seconds on my machine.
And I could deeply change the code with almost no impact on the tests. ;)