Do Vaadin apps scale?
Vaadin is used by thousands of companies to create applications, with sizes ranging from a few concurrent users to tens of thousands. Even then, every now and then there is the question ‘but how can a server-side model scale?’. To give concrete data, Vaadin investigated the performance of the framework and released a report on it. Everything in that report is reproducible with the Bakery App Starter available for download, and you can build similar tests for your own apps. The report concluded that the tested application scaled to 2000 concurrent users when served from a MacBook Pro; using proper server hardware and/or clustering would increase that significantly.
But what does it take for a framework to scale? Below, I will try to summarize the most important takeaways from the report in a shorter format. So, without further ado, let’s take a look on what server-side state actually means for performance, and what it means to use Vaadin instead of a client-server solution.
Memory consumption per user
Vaadin applications store the entire UI structure for each user in server-side memory. The memory is not freed between user requests, instead the state is kept and updated whenever the user performs their next action. What exactly is stored in the state, then?
- UI component list, such as the UI object, Layout objects, Button objects, etc
- Inner state for each UI component, such as Button captions, style names, event listener objects etc.
- Any application data that is visible on the screen, such as that from Containers or DataProviders. There is a big difference in behaviour if these are in-memory or lazy.
- Any data referenced inside your UI objects; for instance, if you have a reference to a User object in your UI class, then that object is also referenced from session data through the UI object.
- Depending on your backend, references to services might count here as well. This is not UI technology dependent though, as you would need the backend anyway.
During my many years as a Project Manager and Consultant building and maintaining large-scale Vaadin applications, I’ve very rarely seen Vaadin-specific objects be the cause of memory issues. The Vaadin component objects simply take up so little memory, that other data dwarfs it. A typical application has a user session size of a few hundred kilobytes, excluding application data. With application data, session sizes can be measured as a few megabytes; this varies wildly depending on how much data the app presents to the user and how it does it.
Of the five different cases above, there are only two that are usually an issue: data lists and references to application data in objects. The rest are typically handled automatically, and don’t cause issues. References to application data in components is pretty easy to handle: there usually isn’t any need to have them. Simply load the data from the back end when you need it, fill whatever components you use to display the data, and don’t store a reference to the actual data for later use; you can always load it again when needed.
Data lists are a bit more tricky. For any lists (DataProviders or Containers) bigger than 50–100 objects, I recommend you use a lazy provider. This means a bit more work in your backend (basically, you need to implement paging into your service methods) but it starts paying dividends really quickly. When you don’t use lazy providers all of the data for the listing is loaded and kept in memory, regardless if it is visible in the UI or not. For Vaadin 8 and later, you can use the built-in interfaces for lazy-loading DataProviders. For Vaadin 7 and 6 I wholeheartedly recommend the LazyQueryContainer from the Directory. These lazy data structures only load data from the backend when it is needed, regardless of the size of the list. Out of millions of rows, you only have some dozens loaded at any one time.
Sometimes the ease of Vaadin development makes it easy for developers to create something that isn’t strictly best practice. One of these issues is registering Views to the Navigator using the View object registration option instead of the View class option. Having the views initialized and ready to go might seem great, but the bigger the application, the more issues this causes, and we don’t recommend you use that strategy anymore. Remember that 90% of the time, the users only use 10% of your features.
As a summary, Vaadin itself is not the reason for high memory consumption in a majority of cases we’ve seen. The issues usually stem from bad backend programming practices and inadvertently storing references to data where they do not get cleaned up. Vaadin makes it very easy to build large applications with a lot of data, but you should still follow good practices when developing the backend.
CPU consumption per user
As far as CPU consumption goes, the situation is very similar to memory; Vaadin itself consumes very little CPU resources. Most of the CPU time is spent in application code doing backend queries. These are the tasks that a Vaadin request consists of:
- The Servlet Container (server) receiving, parsing and routing a request to the Vaadin Servlet (app)
- The Vaadin Servlet parsing the request, finding the UI components, updating any state, and calling the correct listeners
- The application code doing validations, calling the backend, and updating the UI and state data
- The Vaadin servlet serializing the new data and sending it to the client
The first task barely takes any time at all. The second usually doesn’t either, but this depends on how much data is sent from the client. For later versions of the framework (and earlier when using the immediate mode), data is sent in smaller pieces as soon as it is filled in; even huge forms are fast because the data is already on the server when the user presses the submit button.
The fourth bullet can be an issue in some cases where there is a lot of data to display. The framework tries to mitigate this situation as much as it can with server-to-client lazy loading in e.g. the Grid and the ComboBox, but in some cases there is nothing we can do. For instance, I’ve seen a case where an application added thousands upon thousands of data points to a Chart timeline. Every single data point given to the Chart is serialized and sent to the client, and that can take some time. For this case, I suggested the application pre-process the data so that they only have a hundred points visible at any time; this worked out well in this case.
And finally, the third point. As Vaadin is backend agnostic, it doesn’t force you into any specific way of managing your backend or the requests from e.g. your application to your database. With traditional JS + REST applications there is a clear indication on when and how many REST calls you do; it’s intuitive that they should be minimized and optimized. When you develop a Vaadin application your code already runs on the server, and you might be lulled into a false sense of security. But here too it is often very important to think about the backend communication. By far, most of the performance issues in Vaadin applications is because developers call the backend too often or with poorly optimized queries. This is of course not an issue specific to Vaadin, as it is a common issue for any back-end developer. We recommend industry standard best practices when dealing with backends.
So how does it scale
The Bakery demo application has performance tests as part of the package; you can run them yourself if you want to verify any of our claims. As a reminder, here is a link to the report I talked about earlier, but the main takeaway is this:
- The Bakery app scales to thousands of concurrent users when run on a MacBook Pro.
- Vaadin UIs overall scale very well with additional server hardware
- The main bottleneck for scaling is the application code, not the UI
Simply put, Vaadin scales perfectly well for any business applications you want to create, when the backend is built well. We have customers running applications with tens of thousands of concurrent users, using standard Java clustering techniques.
Vaadin was never built for massive public-facing web sites. If you need facebook-level scalability Vaadin isn’t the best tool for that. But for anything less, Vaadin demonstrably works, and works well.
Debugging CPU and memory consumption
Each Vaadin app behaves differently; there are differences in layouting, event handling, data handling, backend access, libraries, etc. How your app behaves is thusly impossible to predict. Luckily, there are tools that help you monitor what your app does, and to help you analyse the situation when there are issues. These tools are called profilers.
I recommend you familiarize yourself with a profiling tool, even if your app doesn’t have any issues. Profilers are invaluable when something actually does go wrong, and the better you know the tool the easier the problem becomes to solve. There are many alternatives you can choose from when it comes to profilers. There are free alternatives, such as VisualVM which is bundled with all JDK installations. Some products are commercial, such as JProfiler.
Most of these tools have the same set of basic features. With a profiler you can track memory consumption and allocated objects, find memory leaks origins, find CPU hot spots such as inefficient algorithms, track and analyse backend queries, and more. These tools eliminate the guesswork involved when trying to e.g. find out what a Spring Repository actually queries from the database.
As I’ve outlined above, most performance issues in Vaadin applications stems from the application logic itself. It is impossible to say how you should analyze and fix your application code, but here is a checklist I use when I get asked to perform an analysis. It should get you started in finding any issues:
- How much is the average session size?
- How high is transient memory usage (memory used during a request but freed almost immediately)?
- Are UI objects garbage collected correctly?
CPU usage; For each ‘slow’ operation in the app, check these:
- CPU Hot Spots: what class/method is the cause? is the algorithm slow? Is it run multiple times in error?
- Backend queries: how many queries are run and with what parameters?
In conclusion, yes, Vaadin applications do scale well. As with any framework, it’s possible to make mistakes. But by following standard Java best practices it’s easy to create applications with Vaadin that not only scale well, but look awesome and work great, making your users happy.
If you need any help with any performance or other issues when developing, Vaadin offers consultation and implementation help.