vaadin / flow

Vaadin Flow is a Java framework binding Vaadin web components to Java. This is part of Vaadin 10+.
Apache License 2.0
609 stars 167 forks source link

Speed up vaadin:prepare-frontend and vaadin:build-frontend goals in the Maven plugin #19596

Open AB-xdev opened 3 months ago

AB-xdev commented 3 months ago

Describe your motivation

I noticed that vaadin:prepare-frontend and vaadin:build-frontend are relatively slow - a rerunning production build takes around 15s in one of our projects that I recently updated to 24.4 - even when they have to do nearly nothing, as everything was already generated before.

I had a look at the log messages and the code an could narrow down the following places where performance can be optimized: Problem Possible fix
In 24.4 a new goal configure is invoked when running prepare-frontend or build-frontend, this goal is only required when you use hilla (for example we do not) Make the goal optional or at least provide a parameter to control if it should be invoked
It's always checked if Node/NPM versions are correct - this takes around 2-3s on my machine Add a parameter which makes it possible to disable this
In prepare-frontend or build-frontend all classpathElements are scanned for classes (see getClassFinder). This also includes libraries which have nothing to do with Vaadin at all, e.g. Hibernate, Spring Boot, etc Provide an option to ignore these libraries/jar from scanning
The scan is done multiple times (counted at least 3 for build-frontend) Cache the scan for the mojo
Inherited lookup is nearly always performed: When e.g. looking up classes that use @NpmPackage also all classes that extend from classes which have the annotation are looked up (→ ReflectionsClassFinder) Do not search for extending classes in all lookups

Describe alternatives you've considered

For now we forked the plugin inside our repository and applied some fixes manually.

For example:

// Workaround to ignore Node/NPM checks
System.setProperty(FrontendUtils.PARAM_IGNORE_VERSION_CHECKS, "true");
// Workaround to invoke ClassFinder only once per goal
protected ClassFinder cached;

@Override
public ClassFinder getClassFinder()
{
    if(this.cached == null)
    {
        this.cached = ... // createClassFinder
    }
    return this.cached;
}
// Filtering only relevant classpathElements
final List<String> classpathElements = FlowModeAbstractMojo.getClasspathElements(project);
final List<String> filtered = classpathElements.stream()
    .filter(s -> {
        if(!s.endsWith(".jar") // The current workdir is not a JAR
            || s.contains("target") // If a jar is inside a target folder, it's likely a sibling project (multi-module)
            || s.contains("vaadin") // Vaadin projects are always needed
        )
        {
            return true;
        }
        ... // Additional user configure conditions go here
        return false;
    })
    .toList();
return BuildFrontendUtil.getClassFinder(filtered);

The above changes improve the build speed (in our case) from ~15s down to 3s when rerunning the production build.

mstahv commented 3 months ago

Well done @AB-xdev! I didn't see that bad regressions, but sure noticed "something". Just pushed our a tutorial of my solution, but gotta check yours tomorrow as well!

https://vaadin.com/blog/streamlined-vaadin-flow-setup-for-v24.4

mstahv commented 3 months ago

@AB-xdev Does your fork happen to be in GitHub? I'm working/continuing on a custom plugin today. On a different topic (trying to make a PoC for #17737), but your sources might be helpful for me though. I'm not very experienced with Maven plugins, more on the happy user side on the Maven front.

AB-xdev commented 3 months ago

@AB-xdev Does your fork happen to be in GitHub? I'm working/continuing on a custom plugin today. On a different topic (trying to make a PoC for #17737), but your sources might be helpful for me though. I'm not very experienced with Maven plugins, more on the happy user side on the Maven front.

Sadly not, however I extracted the 4 files that required modification into a GitHub Gist and you can find them here :) The classes are pretty much just an override of https://github.com/vaadin/hilla/tree/main/packages/java/maven-plugin 😉

mshabarov commented 3 months ago

@AB-xdev thank you for the comprehensive analysis! Indeed, with Vaadin 24.4 the start-up time of Flow applications suffer a bit, because we now have one more framework by default and new product - Copilot, which makes extra work on startup comparing to previous versions.

The immediate solution is to exclude Hilla and Copilot, as @mstahv described in his comment above and how it's documented in the migration guide.

Nevertheless, let me add this into Flow's team backlog, it's worth it to continue investigating it.

mstahv commented 3 months ago

Thanks @mshabarov for taking this on the plate. I'm considering this slowdown in V24.4 a nasty regression.

While working on a PoC related to #17737, I think I found a workaround for that "configure" goal that is obsolete for most Flow users. Was tryign to find workaround from the sources and found out that it is brought together somehow in "platform build". Also notifed that flow-maven-plugin is pushed to the central and quite functional if not using Hilla. The nasty thing using it directly is that you probably need to keep following its version separatly (flow project version != vaadin version).

Also the initial build is now slow in my PoC as the front end build is created from the scratch...