Showing posts with label performance. Show all posts
Showing posts with label performance. Show all posts

Tuesday, March 05, 2024

subscribeOn or publishOn? may be both.

neither
[ctor-http-nio-4] ServiceClient : getting
[ctor-http-nio-4] Controller : getting
[ctor-http-nio-4] ServiceClient : got
[ctor-http-nio-4] Controller : got


subscribeOn in service
[ctor-http-nio-4] Controller : getting
[ scheduler-1] ServiceClient : getting
[ctor-http-nio-6] ServiceClient : got
[ctor-http-nio-6] Controller : got


publishOn in service client
[ctor-http-nio-4] ServiceClient : getting
[ctor-http-nio-4] Controller : getting
[ scheduler-1] ServiceClient : got
[ scheduler-1] Controller : got


subscribeOn in service and publishOn in service client
[ctor-http-nio-4] Controller : getting
[ scheduler-1] ServiceClient : getting
[ scheduler-2] ServiceClient : got
[ scheduler-2] Controller : got

Saturday, July 01, 2023

revisit timeout settings after 9 years

After almost 9 year, this is part 2, or an update to Are you setting Connect Timeout correctly? I'm going to give the best practice of setting connect timeout and read timeout for org.springframework.boot.web.client.RestTemplateBuilder as of Spring Boot 2.7.13.

Let's say it takes up to 0.1 sec to setup connection and 1 sec to receive response, then it's easy to understand you'll get org.apache.http.conn.ConnectTimeoutException if

return restTemplateBuilder
.rootUri(rootUri)
.basicAuthentication(username, password)
.setConnectTimeout(ofMillis(10))
.setReadTimeout(ofMillis(1000))
.build();

and java.net.SocketTimeoutException: Read timed out if

return restTemplateBuilder
.rootUri(rootUri)
.basicAuthentication(username, password)
.setConnectTimeout(ofMillis(100))
.setReadTimeout(ofMillis(100))
.build();

You can either set both timeouts

return restTemplateBuilder
.rootUri(rootUri)
.basicAuthentication(username, password)
.setConnectTimeout(ofMillis(100))
.setReadTimeout(ofMillis(1000))
.build();

or omit connect timeout and only set read timeout

return restTemplateBuilder
.rootUri(rootUri)
.basicAuthentication(username, password)
//.setConnectTimeout(ofMillis(100))
.setReadTimeout(ofMillis(1000))
.build();

however by setting connect timeout only and omitting read timeout, you'll get java.net.SocketTimeoutException: Read timed out

return restTemplateBuilder
.rootUri(rootUri)
.basicAuthentication(username, password)
.setConnectTimeout(ofMillis(100))
//.setReadTimeout(ofMillis(1000))
.build();

Let me rephrase what I said 9 years ago, don't set both timeout to be the same, which shows you don't know how network communication works.


Thursday, August 07, 2014

Are you setting Connect Timeout correctly?

Application development nowadays is becoming more and more like mashing up. Unless you provide file storage service, or data repository service, there's no way you can avoid consuming 3rd party APIs / (micro-) services. In Java battlefield, most of the time, internal or external APIs are provided via RESTful interface. If this is the case, chances are to consume a web service you'll be using one of
No matter which one you end up use, it's always a good practice to set timeout for HTTP clients, and this can be done only by org.apache.http.client.config.RequestConfig. Now can you please check the value you pass into RequestConfig.Builder#setConnectTimeout()? If it's significantly larger than 5(ms, not a typo here), you're not setting it correctly.

Connect timeout is used to provide QoS of the creation of TCP connection between client and server, not the whole lifecycle of the connection, or in pooled environment, from borrow to return. If this process can't finish in few milliseconds, org.apache.http.conn.ConnectTimeoutException should be thrown to speed up the exception handling. It's not the end of the world however, if you misuse it. It's just better to fail fast in case something goes wrong for better user experience, rather than wasting the x seconds you set.

Then how about the RequestConfig.Builder#setSocketTimeout()? Well, I may have another post on it. Until then, it's more like the meaning you think setConnectionTimeout has.

Never take anything for granted.

Thursday, May 17, 2012

One-to-one relationship using EclipseLink

Best Practice in JPA series:
Part 1 – JPA Caching in EclipseLink
Part 2 – One-to-one relationship using EclipseLink

Here are some tips on how to implement high performance one to one relationship using EclipseLink 2.3.2.

Identify owning side (side with foreign key) and inverse side (side with mappedBy attribute in @OneToOne annotation). I will use Owning entity and Inverse entity as examples in following tips.

If you define private Inverse inverse; in Owning entity and you're satisfied with inverse_id as column name for foreign key, you don't need to specify @JoinColumn(name = "inverse_id") on it. However, you need to specify @OneToOne(cascade = CascadeType.ALL) on it, so that any operations on Inverse entity can be performed from Owning entity. You also need to specify @OneToOne(mappedBy = "inverse") on private Owning owning; in Inverse entity.

To avoid N + 1 select problem, specify @BatchFetch(BatchFetchType.EXISTS) on owning property in Inverse entity. You can also use @BatchFetch(BatchFetchType.IN) or @BatchFetch(BatchFetchType.JOIN). Following SQL statements will be used respectively for better performance.

SELECT t0.* FROM INVERSE t0 WHERE EXISTS (SELECT t1.ID FROM OWNING t1 WHERE (t0.ID = t1.INVERSE_ID))

SELECT * FROM INVERSE WHERE (ID IN (?,?))

SELECT t0.* FROM INVERSE t0, OWNING t1 WHERE (t0.ID = t1.INVERSE_ID)

If both Owning entity loads Inverse entity eagerly and Inverse entity loads Owning entity eagerly, following SQL statement will still be executed N times. Otherwise N + 1 problem is solved.

SELECT * FROM OWNING WHERE (INVERSE_ID = ?)

fetch = FetchType.LAZY can be set in @OneToOne annotation on owning side and / or inverse side. But it's better to use it on inverse side, because when owning object loads inverse object eagerly (FetchType.EAGER is default in one-to-one relation) by INVERSE_ID, the result can be used to populate inverse entity cache.

If you want lazy fetch take effect outside of a Java EE 5/6 application server, VM argument -javaagent:/home/jerry/.m2/repository/org/eclipse/persistence/eclipselink/2.3.2/eclipselink-2.3.2.jar needs to be set. Note that full absolute path is used here. See Using EclipseLink JPA Weaving for more details.

Sunday, March 04, 2012

Everybody Loves FizzBuzz

One of my career objectives is implementing business requirements in a way that makes
  • customer / employer happy
  • developers happy, and
  • infrastructure happy
at the same time. I'd like to take FizzBuzz puzzle as an example to illustrate how attention to detail helps me achieve this goal in Java.
Write a program that prints the numbers from 1 to 100. But for multiples of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers which are multiples of both three and five print "FizzBuzz".

public class FizzBuzz {

    public static void main(String[] args) {
        boolean fizzOrBuzz;

        for (int i = 1; i <= 100; i++) {
            fizzOrBuzz = false;

            if (i % 3 == 0) {
                fizzOrBuzz = true;
                System.out.print("Fizz");
            }

            if (i % 5 == 0) {
                fizzOrBuzz = true;
                System.out.print("Buzz");
            }

            if (!fizzOrBuzz) {
                System.out.print(i);
            }

            System.out.println();
        }
    }
}

Update (06/12/2014): https://github.com/codingsince1985/FizzBuzz

Friday, February 10, 2012

JPA Caching in EclipseLink 2.3.2


Best Practice in JPA series:
Part 1 – JPA Caching in EclipseLink
Part 2 – One-to-one relationship using EclipseLink


Java Persistence API 2.0 defines Level 1 (L1) Cache (Entity Cache), Level 2 (L2) Cache (Shared Entity Cache) and Query (Result) Cache. Now I can take full advantage of JPA cache, just like what I did 5, 6 years ago using Hibernate cache. Although Hibernate also has its JPA implementation, I found EclipseLink has better default settings and also easier to enable advanced features. Here are some random tips.

The shared attribute of @Cache annotation has deprecated and is replaced by isolation=CacheIsolationType.SHARED, which means sharing entity cache between EntityManager objects and allowing query cache (if enabled) to use entity cache. Even you don't set @Cache annotation on a domain object, it's enabled by default.

CacheIsolationType.PROTECTED means sharing entity cache between EntityManager objects but disallowing query cache to use entity cache, even when query cache is enabled.

CacheIsolationType.ISOLATED means not sharing entity cache between EntityManager objects and disallow query cache to use entity cache, even when query cache is enabled.

The default coordinationType=CacheCoordinationType.SEND_OBJECT_CHANGES in @Cache means any entity update to database also updates entity in cache, which is great in performance.

Set hints = { @QueryHint(name = QueryHints.QUERY_RESULTS_CACHE, value = HintValues.TRUE) } in @NamedQuery if you want to enable query cache. But unless you have a fixed domain objects, don't enable its query cache. Note that "eclipselink.query-results-cache" is not a standard JPA hint, you cannot set it to "True" for a javax.persistence.Query object.

Unless you have every domain object in entity cache, don't use hints = { @QueryHint(name = QueryHints.CACHE_USAGE, value = CacheUsage.CheckCacheOnly) } in @NamedQuery.

Set <property name="eclipselink.logging.level" value="FINE" /> in META-INFO/persistence.xml to show SQL statements.

Unless you want to change the default cache retrieve / store mode, don't need to set following properties to EntityManager object.
em.setProperty(QueryHints.CACHE_RETRIEVE_MODE, CacheRetrieveMode.USE);
em.setProperty(QueryHints.CACHE_STORE_MODE, CacheStoreMode.USE);

Even you don't set any hint in @NamedQuery, query result will be used to populate entity cache.

Only entityManager.find(entityClass, id) can use entity cache. Get entity, or get entities, by any field(s) other than id don't use entity cache.

The default sizes of entity cache and query cache are 100 each.

If you get
Exception in thread "main" java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Syntax error parsing the query [from Entity], line 1, column 0: unexpected token [from].
Internal Exception: NoViableAltException(33@[])
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1477)
change your JPA query to entityManager.createQuery("select entity from Entity entity").getResultList();

Please feel free to let me know if you have any questions regarding EclipseLink cache.

Friday, June 17, 2011

Performance of Virtual Machine

I gave up dual boot when I upgraded to Ubuntu 11.04 and moved Windows into virtual machine. The performance of Windows guest was good, 35 seconds from power on to desktop (very good as my HDD is 5400 rpm), until I began using it seriously.

I've done a benchmark of compiling one of my GWT project in Eclipse four times. I still remember Turbo C can compile 10 lines of code per second, while Turbo Pascal can compile 100 lines per second when I was in university. Compilation is quite suitable for benchmark because it involves both CPU and I/O tasks.

1st2nd3rd4th
native in Ubuntu82818079
in VirtualBox143195196265
in Vmware Player112107108108

The Windows I'm using in virtual machines is Windows 7 Professional 32-bit. IDE is Eclipse for Java EE 3.6.2 32-bit for Linux and Windows. Virtual machine software are Oracle VirtualBox 4.0.8 and VMware Player 3.1.4, for Linux of course.

This is by no means a scientific benchmark, but what I got is if you plan to use a Windows guest heavily in a Linux host, VMware Player is the one to go with at the moment.

Thursday, April 07, 2011

Performance Tuning

It's a common sense in the industry that performance tuning should be done after feature complete. But very likely there is only a small time frame between feature complete and code freeze, and during this short time period we need to do so many more important things. There is another common sense after product release, if it ain't broken, don't fix it. Performance issues, if any, are far from being broken.

Sounds weird, but such is life. Thanks to modern programming languages, revolutionary methodologies, cutting edge hardware, we are in a time that you don't need to know how many registers are there in a CPU to development software. And more importantly, nobody actually knows what performance can be achieved for a certain system on a specific platform. I have some real life performance tuning examples and the amazing results here.


From 5 Minutes to 90 Seconds

When I worked in a product called Public Content Management 8 years back, there was a home-made caching system in this product. It worked beautifully after system is started but it took 5 minutes for the system to start. It became so frustrating that my manager gave me a week to figure it out if we could do something. By the end of the 4th day, the product was able to start in 1.5 minutes. After this, I began to pay more attention on what can be done to:
  • finish a time consuming task in (much) less time; and
  • leave more CPU cycles to customers.
And from that time, I put "Performance Tuning" as a speciality in my profile.

From 7 Seconds to 70 Milliseconds

Last year I participated in a system called Marin Safety. From my development environment, the loading of Waterway Management page took 7 seconds. I knew that this was mainly because I was using a remote database instance, which was not the case in a production environment. An amplified performance issue, ignore it or not? Several hours later, I shortened the loading time to 70 ms.

From 13 Seconds to 182 Milliseconds

Last week, I needed to parse some returning strings of WMS calls from a GeoServer. I could select format from text/html and text/plain. I should have one more choice, text/xml, according to the protocol. XML format was obviously the choice because it's self-explained and way easy to get parsed, but GeoServer doesn't even support it. HTML format is reasonably my next choice, it's at lease easier to parse than plain text. But for a sample request, it took GeoServer 13 seconds to return an HTML format result while only 182 ms to return a plain text result. I'm sure you know my answer at this point of time. Do the hard work myself and save user 10+ seconds per call.

From the above examples I just want to give you an idea about performance tuning. It can be done and should be done any time, especially when the environment is not ideal. The fact is, ideal environment (usually production environment) can only cover the performance issues, it won't solve them. It's too late to start performance tuning when you start thinking upgrade your sever or losing your impatient customers.

Monday, November 16, 2009

Message Digest algorithms in Java Cryptography Architecture

This is the 2nd part of Message Digest algorithms test. I did performance tests on MD5, SHA-1, SHA-256, SHA-384 and SHA-512 in Java Cryptography Architecture (JCA) of Java SE6 against the same file as in 1st part.


Alg. Name Real Time User Time System Time
MD5 0m15.004s 0m9.109s 0m1.344s
SHA-1 0m38.654s 0m35.318s 0m2.036s
SHA-256 0m59.053s 0m56.032s 0m1.784s
SHA-384 1m56.362s 1m53.663s 0m1.364s
SHA-512 1m58.385s 1m53.727s 0m1.484s

The results are quite interesting if compared with the results of md5sum, sha1sum, sha224sum, sha256sum, sha384sum and sha512sum in Ubuntu in 1st part. Basically
  • MD5 is comparable in terms of time but with a higher percentage of CPU usage (2X);
  • SHA-1 and SHA-256 are much slower (2X);
  • SHA-384 and SHA-512 are a bit faster than native implementations.

Tuesday, November 03, 2009

Message Digest algorithms

I'm doing a home-made backup product that needs to compute checksum of tens of thousands of file. According to Wikipedia, there are quite a number of checksum algorithms.

This product will first base on Linux and some of the checksum utilities are already provided by Ubuntu 9.10. They are md5sum, sha1sum, sha224sum, sha256sum, sha384sum and sha512sum.

In case one day I will port this product to Windows, I also checked MessageDigest of Java Cryptography Architecture (JCA) in Java SE6. It supports MD2, MD5, SHA-1, SHA-256, SHA-384 and SHA-512.

Doing checksum is both CPU and IO intensive. I did a simple performance test today on how much CPU time is needed to compute different checksums (commonly supported by Linux and MessageDigest) of a 723,488,768-byte file.


Alg. Name Real Time User Time System Time
md5sum 0m14.655s 0m4.380s 0m1.300s
sha1sum 0m15.187s 0m12.641s 0m1.764s
sha256sum 0m25.859s 0m23.341s 0m1.656s
sha384sum 2m25.417s 2m23.841s 0m1.432s
sha512sum 2m25.733s 2m23.373s 0m2.116s

Now I have a clear idea of work load of these algorithms. I will do a Java implementation test later to see if I can get similar results.