[BUG] Performance Issues reading LocalDate,LocalDateTime, and LocalTime
See original GitHub issueDriver version
7.2.1.jre8
SQL Server version
Microsoft SQL Server 2014 (SP3) (KB4022619) - 12.0.6024.0 (X64) Sep 7 2018 01:37:51 Copyright © Microsoft Corporation Developer Edition (64-bit) on Windows NT 6.3 <X64> (Build 9600: )
Client Operating System
Ubuntu 16.04
JAVA/JVM version
openjdk version “12” 2019-03-19 OpenJDK Runtime Environment (build 12+33) OpenJDK 64-Bit Server VM (build 12+33, mixed mode, sharing)
Table schema
CREATE TABLE test.DatePerformance (
someDate DATE
)
-- Populate Table with Random Dates
INSERT INTO test.DatePerformance
SELECT DATEADD(DAY, -RAND(12313456) * 1000, GETDATE())
WHILE((SELECT COUNT(*) FROM test.DatePerformance) < 2500)
INSERT INTO test.DatePerformance
SELECT DATEADD(DAY, -RAND() * 1000, GETDATE())
Problem description
-
Expected behaviour:
- Reading Date objects from the database should have low overhead.
-
Actual behaviour:
- There is substantial overhead creating GregorianCalendars and converting them to the LocalDate, LocalDateTime, LocalTime classes
- Calling Calendar.getInstance(TimeZone.getTimeZone(“UTC”)) is 30% of the getObject call for LocalDate, LocalDateTime, LocalTime classes
-
Error message/stack trace:
-
Any other details that can be helpful:
- Result Set - Calendar.getInstance https://github.com/microsoft/mssql-jdbc/blob/v7.2.1/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java#L2384-L2398
- DDC.convertTemporalToObject - https://github.com/microsoft/mssql-jdbc/blob/v7.2.1/src/main/java/com/microsoft/sqlserver/jdbc/DDC.java#L755-L1061
Sample Profiling Snapshots
com.microsoft.sqlserver.jdbc.SQLServerResultSet.getObject () 15.094s
> com.microsoft.sqlserver.jdbc.DDC.convertTemporalToObject () 9.206s
>> java.util.GregorianCalendar.<init> () 6.01s
>> java.util.Calendar.getTimeInMillis () 3.005s
> java.util.Calendar.getInstance () 5.088s
com.microsoft.sqlserver.jdbc.SQLServerResultSet.getObject () 7.601s
> com.microsoft.sqlserver.jdbc.DDC.convertTemporalToObject () 3.906s
>> java.util.GregorianCalendar.<init> () 2.498s
>> java.util.Calendar.getTimeInMillis () 1.407s
> java.util.Calendar.getInstance () 3.302s
Reproduction code
/**
Using JMH (LocalDate)
Result: 338.352 ±(99.9%) 27.177 ops/s [Average]
Statistics: (min, avg, max) = (5.716, 338.352, 435.926), stdev = 115.069
Confidence interval (99.9%): [311.175, 365.529]
# Run complete. Total time: 00:08:10
Benchmark Mode Samples Score Score error Units
c.c.m.MyBenchmark.testMethod thrpt 200 338.352 27.177 ops/s
*/
@Benchmark
public void testMethod()
{
List<LocalDate> dates = new ArrayList<>();
try (
Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT someDate from test.DatePerformance");
ResultSet rs = ps.executeQuery()
)
{
while(rs.next())
dates.add(rs.getObject("someDate", LocalDate.class));
} catch (SQLException e)
{
e.printStackTrace();
}
}
Issue Analytics
- State:
- Created 4 years ago
- Reactions:3
- Comments:31 (24 by maintainers)
Top Results From Across the Web
Performance Issues Related to LocalDateTime and Instant ...
This article discusses some performance issues that an Alibaba engineered encountered during serialization operations which were related to ...
Read more >Java 8 LocalDateTime.now() only giving precision of ...
My problem is that I need to do logging using both Java and Javascript and they need ... LocalDate hpDate = LocalDate.now(microsecondClock);.
Read more >Still using java.util.Date? Don't! - Programming Hints
The LocalDateTime combines together LocaleDate and LocalTime and holds a date with time but without a time-zone in the ISO-8601 calendar system. ZonedDateTime ......
Read more >Migrating to the New Java 8 Date Time API - Baeldung
LocalDate – represents a date (year, month, day); LocalDateTime – same as LocalDate, but includes time with nanosecond precision; OffsetDateTime ...
Read more >99487: Mysql connector for java 8.0.20 shifts back LocalDate ...
But the driver can follow this change only when cacheDefaultTimezone=false. We have other issues with LocalDate/LocalTime/LocalDatetime reported ...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Hi @cogman, thanks for the input. I agree that we should use as little of GregorianCalendar as possible. I’d like to replace all instances of Calendar in the driver, but the JDBC specs state that the driver needs to provide public APIs that accept Calendar objects (mostly from getters/setters for temporal objects in prepared/callable statements).
Looking at how the driver actually uses the Calendar objects passed from the client, most of the time they’re used for the timezone information. I’ll look into extracting the necessary information from the Calendar object as soon as it’s passed from the public API, while replacing as many instances of Calendar parameters with TimeZone parameters in internal methods. I’ll also make sure that the driver never initializes any GregorianCalendar objects, either.
It’ll take a while, but I’ll update everyone once the initial PR/design has been made. Please let me know if you have any other input in the meantime.
@gordthompson Care should be taken in the fact that the Calendar object is not thread safe.
Ideally, the driver would switch away from using the calendar object and instead look at using ZonedDateTime or straight local dates. If my reading of the TDS standard is correct, than a SQL “Date” ends up on the wire as the integer “number of days since 1-1-1” That would be a trivial conversion to localDates because it would simply be “LocalDate.of(1,1,1).addDays(tdsIntValue);” (or, whatever that API is.
The new system is much faster and thread safe to boot. Converting to java.sql.Date is also pretty simple because it is just a “Date.from(ZonedDateTime.toInstant())” away.