I am not satisfied with the performance and especially the time my piece of code takes to complete.
The surrounding environment for better understanding is the following:
A statistic for Downtime has to be created. Downtime means that some machines are not working and standing still. These have to be captured and my part is to collect this data and display it on request.
For this I created a structure of a POJO called Downtime which holds the basic information like when it started and when it finished. And a POJO called DowntimeSpecification which holds more detailed information as where in the machine the problem happened and such things.
The connection between these is that 1 Downtime can have N Specifications and 1 Specification can have only 1 Downtime it is associated to.
The data I get looks like this:
"data": [
[
"2016-05-01T14:35:01+02:00", // Downtime
1,
0,
900000,
"2016-05-01T14:50:01+02:00",
0,
"2016-05-01T14:35:01+02:00", // specification starts here
6,
56,
0,
1,
40,
0,
0,
0,
0
],
[
"2016-05-01T14:35:01+02:00",
1,
0,
900000,
"2016-05-01T14:50:01+02:00",
0,
"2016-05-01T14:35:01+02:00",
5,
93,
0,
1,
31,
0,
15,
16029,
1
],
[
"2016-05-01T15:55:01+02:00",
1,
0,
900000,
"2016-05-01T16:10:01+02:00",
0,
"2016-05-01T15:55:01+02:00",
6,
95,
0,
1,
40,
0,
0,
0,
0
],
[
"2016-05-01T15:55:01+02:00",
1,
0,
900000,
"2016-05-01T16:10:01+02:00",
0,
"2016-05-01T15:55:01+02:00",
6,
103,
0,
1,
111,
0,
15,
16030,
1
],
[
"2016-05-01T23:45:01+02:00",
1,
0,
900000,
"2016-05-02T00:00:01+02:00",
0,
"2016-05-01T23:45:01+02:00",
6,
95,
0,
1,
40,
0,
0,
0,
0
],
[
"2016-05-01T23:45:01+02:00",
1,
0,
900000,
"2016-05-02T00:00:01+02:00",
0,
"2016-05-01T23:45:01+02:00",
6,
79,
0,
1,
111,
0,
15,
16031,
1
]
]
The data in this JSON is redundant as the first bit of data in each json part is the Downtime Pojo and the other data is for the specifications.
Here is the piece of code that takes too long and is generally not satisfying. What I would like it to look like:
private List<Downtime> createDowntimes( Department department, TQLResult queryTQL ) throws CustomException
{
List<Downtime> result = new ArrayList<>( queryTQL.getData().size() );
Downtime savedDowntime = null;
Downtime savedSummary = null;
for ( Object[] row : queryTQL.getData() ) // The above JSON is in this Object[]
{
OffsetDateTime time = OffsetDateTime.parse( (String) row[COLINDEX_TIME_FROM] ); // this is the first data in the JSON ( 2016-05-01T14:35:01+02:00 )
ZonedDateTime downtimeFrom = time.atZoneSimilarLocal( ZoneId.systemDefault() );
Downtime downtime = new Downtime( row, department );
downtime.setSummary( false );
DowntimeSpecification specification = new DowntimeSpecification( row, department, false );
downtime.addSpecifications( specification );
if ( previousDateTime != null ) // first run in loop?
{
if ( savedDowntime != null ) // did I find a Downtime already?
{
if ( downtimeFrom.equals( previousDateTime ) ) // is the first bit the same? Only the specification is interesting then
{
log.trace( "found specification for the same downtime {0}", previousDateTime );
savedDowntime.addSpecifications( new DowntimeSpecification( row, department, false ) );
}
else
{
if ( savedSummary != null && savedSummary.getDowntimeTo().getDayOfMonth() <= savedDowntime.getDowntimeFrom().getDayOfMonth()
&& savedDowntime.getDowntimeFrom().getHour() >= 6 ) // it is a new day after 6AM so I have to add the summary
{
log.trace( "Adding summary {0} to the result", savedSummary );
result.add( savedSummary );
savedSummary = null;
}
log.trace( "adding finished downtime {0} to the result", savedDowntime );
result.add( savedDowntime );
ZonedDateTime tempPrevious = previousDateTime.truncatedTo( ChronoUnit.DAYS ).plusHours( 6 );
ZonedDateTime tempFrom = downtimeFrom.truncatedTo( ChronoUnit.DAYS ).plusHours( 6 );
if ( tempFrom.isAfter( tempPrevious ) ) // day change? create summary
{
log.trace( "Day change detected. Building Summary" );
if ( savedSummary != null )
{
result.add( savedSummary );
}
savedSummary = createSummary( department );
}
previousDateTime = downtimeFrom;
savedDowntime = downtime;
}
}
else
{
savedDowntime = downtime;
}
}
else
{
previousDateTime = downtimeFrom;
savedDowntime = downtime;
}
}
if ( !result.contains( savedDowntime ) && savedDowntime != null ) // to not swallow the last downtime
{
result.add( savedDowntime );
}
if ( savedSummary != null ) // to create a summary even if the last downtimes don't change the day for completion reasons
{
result.add( savedSummary );
}
log.debug( "setting the states for the downtimes" );
setStates( result ); // only takes >1 sec. So its ok
return result;
}
private Downtime createSummary( Deparment department ) throws CustomException
{
ZonedDateTime dayStart = previousDateTime.truncatedTo( ChronoUnit.DAYS );
dayStart = dayStart.plusHours( 6 );
ZonedDateTime dayEnd = previousDateTime.truncatedTo( ChronoUnit.DAYS );
dayEnd = dayEnd.plusDays( 1 );
dayEnd = dayEnd.plusHours( 6 );
Percent availability = downtimeCalculationService.getAvailability( department, dayStart, dayEnd ); // DB query
BigDecimal downtimeSummary = downtimeCalculationService.getDowntimeMinutes( department, dayStart, dayEnd ); // DB query
Downtime summaryDowntime = new Downtime( availability, downtimeSummary, dayStart, dayEnd );
summaryDowntime.setSummary( true );
return summaryDowntime;
}
What I do in this code is the following:
I iterate over the result I get from some other system. Each Object[]
therefore is a downtime with a specification included. So I need to recognize if the data is for the same downtime and I only have to add the bit of data for the specification. Also I need to know if a day change happened to build the summary.
I know that the function createSummary
is part of the culprit since this has two calls that make db queries. Meaning I query the db for each day change I detect. However I can't change that for now, since this is done by someone else and I have to wait until they give me the ability to do that once for a span of days.
This piece of code takes around 3-6 seconds for data that represents about 130 downtimes.
I would like to reduce this time and also, if possible, find a better solution for the code in the for loop
. Less if-else would be really appreciated since it would make it more readable.