use java.time for time

This commit is contained in:
2016-12-10 08:16:55 +01:00
parent 256b278428
commit a409c4c5d0
23 changed files with 466 additions and 196 deletions

View File

@@ -3,5 +3,6 @@ dependencies {
//compile 'com.fasterxml.jackson.core:jackson-databind:2.8.5' //compile 'com.fasterxml.jackson.core:jackson-databind:2.8.5'
compile 'org.lucares:ludb:1.0.20161002111352' compile 'org.lucares:ludb:1.0.20161002111352'
//compile 'commons-io:commons-io:2.5' //compile 'commons-io:commons-io:2.5'
//compile 'joda-time:joda-time:2.9.6'
} }

View File

@@ -0,0 +1,8 @@
package org.lucares.performance.db;
import java.util.Optional;
public interface BlockingIterator<E> {
public Optional<E> next() throws InterruptedException;
}

View File

@@ -0,0 +1,24 @@
package org.lucares.performance.db;
import java.util.Iterator;
import java.util.Optional;
final class BlockingIteratorIterator<E> implements BlockingIterator<E> {
private final Iterator<E> iterator;
public BlockingIteratorIterator(final Iterator<E> iterator) {
this.iterator = iterator;
}
@Override
public Optional<E> next() throws InterruptedException {
if (iterator.hasNext()) {
final E next = iterator.next();
return Optional.of(next);
} else {
return Optional.empty();
}
}
}

View File

@@ -0,0 +1,35 @@
package org.lucares.performance.db;
import java.util.Optional;
import java.util.concurrent.BlockingQueue;
final class BlockingQueueIterator<E> implements BlockingIterator<E> {
private final BlockingQueue<E> queue;
private boolean closed = false;
private final E poison;
public BlockingQueueIterator(final BlockingQueue<E> queue, final E poison) {
this.queue = queue;
this.poison = poison;
}
@Override
public Optional<E> next() throws InterruptedException {
if (closed) {
return Optional.empty();
}
final E next = queue.take();
if (next == poison) {
closed = true;
return Optional.empty();
}
return Optional.of(next);
}
}

View File

@@ -1,6 +1,9 @@
package org.lucares.performance.db; package org.lucares.performance.db;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.TimeZone; import java.util.TimeZone;
@@ -8,70 +11,26 @@ import java.util.TimeZone;
public class DateUtils { public class DateUtils {
private static final TimeZone TIME_ZONE_UTC = TimeZone.getTimeZone("UTC"); private static final TimeZone TIME_ZONE_UTC = TimeZone.getTimeZone("UTC");
private static final SimpleDateFormat YEAR = new SimpleDateFormat("yyyy");
private static final SimpleDateFormat MONTH = new SimpleDateFormat("MM");
private static final SimpleDateFormat DAY = new SimpleDateFormat("dd");
public static final synchronized Day getDay(final Date date) { public static long getDateOffset(final OffsetDateTime date) {
final String yearString = YEAR.format(date); return date.truncatedTo(ChronoUnit.DAYS).toInstant().toEpochMilli();
final String monthString = MONTH.format(date);
final String dayString = DAY.format(date);
final int year = Integer.parseInt(yearString, 10);
final int month = Integer.parseInt(monthString, 10);
final int day = Integer.parseInt(dayString, 10);
return new Day(year, month, day);
}
public static long getDateOffset(final Date date) {
return getMidnightSameDay(date).getTime();
} }
public static Calendar getCalendar() { public static Calendar getCalendar() {
return Calendar.getInstance(TIME_ZONE_UTC); return Calendar.getInstance(TIME_ZONE_UTC);
} }
public static Date getMidnightSameDay(final Date date) { public static OffsetDateTime getLastInstantOfDay(final OffsetDateTime date) {
final Calendar exactTime = getCalendar(); return date.truncatedTo(ChronoUnit.DAYS).plusDays(1).minusNanos(1);
exactTime.setTime(date);
final Calendar midnight = getCalendar();
final int year = exactTime.get(Calendar.YEAR);
final int month = exactTime.get(Calendar.MONTH);
final int day = exactTime.get(Calendar.DATE);
midnight.set(year, month, day);
midnight.set(Calendar.HOUR, 0);
midnight.set(Calendar.MINUTE, 0);
midnight.set(Calendar.SECOND, 0);
midnight.set(Calendar.MILLISECOND, 0);
return midnight.getTime();
} }
public static Date getMidnightNextDay(final Date date) { public static OffsetDateTime getDate(final int year, final int month, final int day, final int hour,
final Calendar exactTime = Calendar.getInstance(TIME_ZONE_UTC); final int minute, final int second) {
exactTime.setTime(date);
exactTime.add(Calendar.DATE, 1); final OffsetDateTime result = OffsetDateTime.of(year, month, day, hour, minute, second, 0, ZoneOffset.UTC);
exactTime.add(Calendar.MILLISECOND, -1); return result;
return exactTime.getTime();
}
public static Date getDate(final int year, final int month, final int day, final int hour, final int minute,
final int second) {
final Calendar calendar = getCalendar();
calendar.set(year, month - 1, day);
calendar.set(Calendar.HOUR, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, second);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
} }
public static String format(final Date date) { public static String format(final Date date) {
@@ -80,4 +39,8 @@ public class DateUtils {
return dateFormat.format(date); return dateFormat.format(date);
} }
public static OffsetDateTime nowInUtc() {
return OffsetDateTime.now(ZoneOffset.UTC);
}
} }

View File

@@ -1,27 +1,44 @@
package org.lucares.performance.db; package org.lucares.performance.db;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;
class Day { class Day {
private final int year;
private final int month; private final OffsetDateTime date;
private final int day;
public Day(final int year, final int month, final int day) { public Day(final int year, final int month, final int day) {
super(); date = OffsetDateTime.of(year, month, day, 0, 0, 0, 0, ZoneOffset.UTC);
this.year = year; }
this.month = month;
this.day = day; public Day(final OffsetDateTime date) {
this.date = date.truncatedTo(ChronoUnit.DAYS);
}
public Day() {
date = OffsetDateTime.now(ZoneOffset.UTC);
} }
public int getYear() { public int getYear() {
return year; return date.getYear();
} }
public int getMonth() { public int getMonth() {
return month; return date.getMonthValue();
} }
public int getDay() { public int getDay() {
return day; return date.getDayOfMonth();
}
public long getOffsetInEpochMilli() {
return date.toInstant().toEpochMilli();
} }
@Override @Override
@@ -30,7 +47,67 @@ class Day {
} }
public String format(final String separator) { public String format(final String separator) {
return String.format("%04d%s%02d%s%02d", year, separator, month, separator, day);
final String pattern = createPattern(separator);
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
return date.format(formatter);
}
private static String createPattern(final String separator) {
return String.format("yyyy%1$sMM%1$sdd", separator);
}
public String serialize() {
return format("-");
}
public static Day fromString(final String dateOffset) {
final String pattern = createPattern("-");
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
final TemporalQuery<OffsetDateTime> query = new TemporalQuery<OffsetDateTime>() {
@Override
public OffsetDateTime queryFrom(final TemporalAccessor temporal) {
final LocalDate localDate = LocalDate.from(temporal);
final LocalTime localTime = LocalTime.MIDNIGHT;
return OffsetDateTime.of(localDate, localTime, ZoneOffset.UTC);
}
};
final OffsetDateTime date = formatter.parse(dateOffset, query);
return new Day(date);
}
public TimeRange toTimeRange() {
final OffsetDateTime from = date;
final OffsetDateTime to = DateUtils.getLastInstantOfDay(from);
return new TimeRange(from, to);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((date == null) ? 0 : date.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Day other = (Day) obj;
if (date == null) {
if (other.date != null)
return false;
} else if (!date.equals(other.date))
return false;
return true;
} }
} }

View File

@@ -1,36 +1,54 @@
package org.lucares.performance.db; package org.lucares.performance.db;
import java.util.Date; import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
public class Entry { public class Entry {
private final Date date; public static final Comparator<Entry> BY_DATE = new EntryByDateComparator();
private final long epochMilli;
private final long value; private final long value;
public Entry(final Date date, final long value) { public Entry(final OffsetDateTime date, final long value) {
super(); super();
this.date = date; this.epochMilli = date.toInstant().toEpochMilli();
this.value = value; this.value = value;
} }
public Date getDate() { Entry(final long epochMilli, final long value) {
return date; super();
this.epochMilli = epochMilli;
this.value = value;
}
public OffsetDateTime getDate() {
final Instant instant = Instant.ofEpochMilli(epochMilli);
return OffsetDateTime.ofInstant(instant, ZoneOffset.UTC);
} }
public long getValue() { public long getValue() {
return value; return value;
} }
long getEpochMilli() {
return epochMilli;
}
@Override @Override
public String toString() { public String toString() {
return DateUtils.format(date) + " = " + value; final OffsetDateTime date = getDate();
return date.format(DateTimeFormatter.ISO_ZONED_DATE_TIME) + " = " + value;
} }
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + ((date == null) ? 0 : date.hashCode()); result = prime * result + (int) (epochMilli ^ (epochMilli >>> 32));
result = prime * result + (int) (value ^ (value >>> 32)); result = prime * result + (int) (value ^ (value >>> 32));
return result; return result;
} }
@@ -44,13 +62,11 @@ public class Entry {
if (getClass() != obj.getClass()) if (getClass() != obj.getClass())
return false; return false;
final Entry other = (Entry) obj; final Entry other = (Entry) obj;
if (date == null) { if (epochMilli != other.epochMilli)
if (other.date != null)
return false;
} else if (!date.equals(other.date))
return false; return false;
if (value != other.value) if (value != other.value)
return false; return false;
return true; return true;
} }
} }

View File

@@ -0,0 +1,19 @@
package org.lucares.performance.db;
import java.util.Comparator;
public class EntryByDateComparator implements Comparator<Entry> {
@Override
public int compare(final Entry o1, final Entry o2) {
long result = o1.getEpochMilli() - o2.getEpochMilli();
if (result == 0) {
result = o1.getValue() - o2.getValue();
}
return result < 0 ? -1 : (result == 0 ? 0 : 1);
}
}

View File

@@ -1,35 +1,23 @@
package org.lucares.performance.db; package org.lucares.performance.db;
import java.io.File; import java.io.File;
import java.util.Date;
class PdbFile { class PdbFile {
private final Tags tags; private final Tags tags;
private final long dateOffset; private final Day day;
private final File file; private final File file;
public PdbFile(final long dateOffset, final File file, final Tags tags) { public PdbFile(final Day day, final File file, final Tags tags) {
checkOffset(dateOffset); this.day = day;
this.dateOffset = dateOffset;
this.file = file; this.file = file;
this.tags = tags; this.tags = tags;
} }
public static PdbFile today(final File file, final Tags tags) { public static PdbFile today(final File file, final Tags tags) {
final long dateOffset = DateUtils.getDateOffset(new Date()); final Day day = new Day();
return new PdbFile(dateOffset, file, tags); return new PdbFile(day, file, tags);
}
private void checkOffset(final long dateOffset) {
final Date date = new Date(dateOffset);
final long expectedDateOffset = DateUtils.getDateOffset(date);
if (dateOffset != expectedDateOffset) {
throw new IllegalArgumentException("dateOffset must be at exactly midnight UTC: " + dateOffset + " != "
+ expectedDateOffset + "(" + date + " != " + new Date(expectedDateOffset) + ")");
}
} }
public Tags getTags() { public Tags getTags() {
@@ -40,14 +28,17 @@ class PdbFile {
return file; return file;
} }
public long getDateOffset() { public Day getDay() {
return dateOffset; return day;
} }
public TimeRange getTimeRange() { public TimeRange getTimeRange() {
final Date from = new Date(dateOffset);
final Date to = DateUtils.getMidnightNextDay(from); return day.toTimeRange();
return new TimeRange(from, to); }
public long getOffsetInEpochMilli() {
return getTimeRange().getFrom().toInstant().toEpochMilli();
} }
@Override @Override
@@ -59,7 +50,7 @@ class PdbFile {
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + (int) (dateOffset ^ (dateOffset >>> 32)); result = prime * result + ((day == null) ? 0 : day.hashCode());
result = prime * result + ((file == null) ? 0 : file.hashCode()); result = prime * result + ((file == null) ? 0 : file.hashCode());
result = prime * result + ((tags == null) ? 0 : tags.hashCode()); result = prime * result + ((tags == null) ? 0 : tags.hashCode());
return result; return result;
@@ -74,7 +65,10 @@ class PdbFile {
if (getClass() != obj.getClass()) if (getClass() != obj.getClass())
return false; return false;
final PdbFile other = (PdbFile) obj; final PdbFile other = (PdbFile) obj;
if (dateOffset != other.dateOffset) if (day == null) {
if (other.day != null)
return false;
} else if (!day.equals(other.day))
return false; return false;
if (file == null) { if (file == null) {
if (other.file != null) if (other.file != null)
@@ -88,4 +82,5 @@ class PdbFile {
return false; return false;
return true; return true;
} }
} }

View File

@@ -1,5 +1,6 @@
package org.lucares.performance.db; package org.lucares.performance.db;
import java.time.OffsetDateTime;
import java.util.Comparator; import java.util.Comparator;
public class PdbFileByTimeAsc implements Comparator<PdbFile> { public class PdbFileByTimeAsc implements Comparator<PdbFile> {
@@ -7,8 +8,9 @@ public class PdbFileByTimeAsc implements Comparator<PdbFile> {
@Override @Override
public int compare(final PdbFile o1, final PdbFile o2) { public int compare(final PdbFile o1, final PdbFile o2) {
final long difference = o1.getDateOffset() - o2.getDateOffset(); final OffsetDateTime o1From = o1.getTimeRange().getFrom();
return difference < 0 ? -1 : (difference == 0 ? 0 : 1); final OffsetDateTime o2From = o2.getTimeRange().getFrom();
return o1From.compareTo(o2From);
} }
} }

View File

@@ -3,7 +3,9 @@ package org.lucares.performance.db;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.util.Date; import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Optional; import java.util.Optional;
class PdbReader implements AutoCloseable { class PdbReader implements AutoCloseable {
@@ -36,18 +38,26 @@ class PdbReader implements AutoCloseable {
/** /**
* Reads the next date value. * Reads the next date value.
* *
* @return the date, or {@code null} if end of stream has been reached * @return the date, or {@code -1} if end of stream has been reached
* @throws IOException * @throws IOException
*/ */
public Date readDate() { public long readEpochMilli() {
assertPositionIsADatePosition(); assertPositionIsADatePosition();
final long value = read(); final long value = read();
if (value < 0) { if (value < 0) {
return -1;
}
return pdbFile.getDay().getOffsetInEpochMilli() + value;
}
public OffsetDateTime readDate() {
final long epochMilli = readEpochMilli();
if (epochMilli < 0) {
return null; return null;
} }
return new Date(pdbFile.getDateOffset() + value); return Instant.ofEpochMilli(epochMilli).atOffset(ZoneOffset.UTC);
} }
// visible for test // visible for test
@@ -174,8 +184,8 @@ class PdbReader implements AutoCloseable {
} }
public Optional<Entry> readEntry() throws ReadRuntimeException { public Optional<Entry> readEntry() throws ReadRuntimeException {
final Date date = readDate(); final long epochMilli = readEpochMilli();
if (date == null) { if (epochMilli < 0) {
return Optional.empty(); return Optional.empty();
} }
final long value = readValue(); final long value = readValue();
@@ -183,7 +193,7 @@ class PdbReader implements AutoCloseable {
if (value < 0) { if (value < 0) {
return Optional.empty(); return Optional.empty();
} }
return Optional.of(new Entry(date, value)); return Optional.of(new Entry(epochMilli, value));
} }
} }

View File

@@ -5,7 +5,6 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Date;
class PdbWriter implements AutoCloseable { class PdbWriter implements AutoCloseable {
@@ -24,12 +23,12 @@ class PdbWriter implements AutoCloseable {
} }
public void write(final Entry entry) throws WriteException { public void write(final Entry entry) throws WriteException {
write(entry.getDate(), entry.getValue()); write(entry.getEpochMilli(), entry.getValue());
} }
void write(final Date time, final long value) throws WriteException { void write(final long epochMilli, final long value) throws WriteException {
final long timeValue = time.getTime(); final long offsetEpochMill = pdbFile.getOffsetInEpochMilli();
final long adjustedValue = timeValue - pdbFile.getDateOffset(); final long adjustedValue = epochMilli - offsetEpochMill;
assertValueInRange(adjustedValue); assertValueInRange(adjustedValue);
assertValueInRange(value); assertValueInRange(value);

View File

@@ -2,12 +2,14 @@ package org.lucares.performance.db;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.OffsetDateTime;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Spliterator; import java.util.Spliterator;
import java.util.Spliterators; import java.util.Spliterators;
import java.util.concurrent.BlockingQueue;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -24,7 +26,7 @@ public class PerformanceDb implements AutoCloseable {
tagsToFile = new TagsToFile(dataDirectory); tagsToFile = new TagsToFile(dataDirectory);
} }
public void put(final Date date, final long value, final Tags tags) throws WriteException { public void put(final OffsetDateTime date, final long value, final Tags tags) throws WriteException {
put(new Entry(date, value), tags); put(new Entry(date, value), tags);
} }
@@ -33,31 +35,53 @@ public class PerformanceDb implements AutoCloseable {
} }
public void put(final Iterable<Entry> entries, final Tags tags) throws WriteException { public void put(final Iterable<Entry> entries, final Tags tags) throws WriteException {
put(entries.iterator(), tags);
}
public void put(final BlockingQueue<Entry> entries, final Entry poisonObject, final Tags tags)
throws WriteException {
final BlockingQueueIterator<Entry> iterator = new BlockingQueueIterator<>(entries, poisonObject);
put(iterator, tags);
}
public void put(final Iterator<Entry> entries, final Tags tags) throws WriteException {
final BlockingIteratorIterator<Entry> iterator = new BlockingIteratorIterator<>(entries);
put(iterator, tags);
}
public void put(final BlockingIterator<Entry> entries, final Tags tags) throws WriteException {
final long start = System.nanoTime(); final long start = System.nanoTime();
double timeSpendInWrite = 0.0; final double timeSpendInWrite = 0.0;
long count = 0; long count = 0;
PdbWriter writer = null; PdbWriter writer = null;
PdbFile pdbFile = null; PdbFile pdbFile = null;
try { try {
for (final Entry entry : entries) { while (true) {
final Date date = entry.getDate(); final Optional<Entry> entryOptional = entries.next();
if (!entryOptional.isPresent()) {
break;
}
final Entry entry = entryOptional.get();
final long epochMilli = entry.getEpochMilli();
final long value = entry.getValue(); final long value = entry.getValue();
if (pdbFile == null // if (pdbFile == null //
|| !pdbFile.getTimeRange().inRange(date)) // TODO @ahr || !pdbFile.getTimeRange().inRange(epochMilli)) // TODO
// correct // @ahr
// would be // correct
// to check // would be
// if the // to check
// date is // if the
// in the // date is
// available // in the
// range // available
// range
{ {
final long startWrite = System.nanoTime(); final OffsetDateTime date = entry.getDate();
pdbFile = tagsToFile.getFile(date, tags); pdbFile = tagsToFile.getFile(date, tags);
timeSpendInWrite += (System.nanoTime() - startWrite) / 1_000_000.0;
} }
if (writer == null || !writer.getFile().equals(pdbFile)) { if (writer == null || !writer.getFile().equals(pdbFile)) {
@@ -67,10 +91,13 @@ public class PerformanceDb implements AutoCloseable {
writer = new PdbWriter(pdbFile); writer = new PdbWriter(pdbFile);
} }
writer.write(date, value); writer.write(epochMilli, value);
count++; count++;
} }
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
LOGGER.info("Thread was interrupted. Aborting exectution.");
} catch (final IOException e) { } catch (final IOException e) {
throw new WriteException(e); throw new WriteException(e);
} finally { } finally {

View File

@@ -5,9 +5,9 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.OffsetDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@@ -31,7 +31,7 @@ public class TagsToFile implements AutoCloseable, CollectionUtils {
try { try {
db = new H2DB(new File(dataDirectory.toFile(), "lu.db")); db = new H2DB(new File(dataDirectory.toFile(), "lu.db"));
try { try {
db.createField(Fields.DATE_OFFSET, FieldType.BIGINT); db.createField(Fields.DATE_OFFSET, FieldType.STRING);
} catch (final FieldExistsException e) { } catch (final FieldExistsException e) {
// TODO @ahr ludb needs a hasField method, or a // TODO @ahr ludb needs a hasField method, or a
// createIfNotExists // createIfNotExists
@@ -69,9 +69,10 @@ public class TagsToFile implements AutoCloseable, CollectionUtils {
private PdbFile toPdbFile(final Document document) { private PdbFile toPdbFile(final Document document) {
final File file = document.getFile(); final File file = document.getFile();
final long dateOffset = document.getPropertyLong(Fields.DATE_OFFSET); final String dateOffset = document.getPropertyString(Fields.DATE_OFFSET);
final Day day = Day.fromString(dateOffset);
final Tags tagsOfFile = toTags(document); final Tags tagsOfFile = toTags(document);
final PdbFile pdbFile = new PdbFile(dateOffset, file, tagsOfFile); final PdbFile pdbFile = new PdbFile(day, file, tagsOfFile);
return pdbFile; return pdbFile;
} }
@@ -88,7 +89,7 @@ public class TagsToFile implements AutoCloseable, CollectionUtils {
return tagsOfFile; return tagsOfFile;
} }
public PdbFile getFile(final Date date, final Tags tags) throws FileNotFoundException, IOException { public PdbFile getFile(final OffsetDateTime date, final Tags tags) throws FileNotFoundException, IOException {
final List<PdbFile> pdbFiles = getFilesMatchingTagsExactly(tags); final List<PdbFile> pdbFiles = getFilesMatchingTagsExactly(tags);
final List<PdbFile> preResult = new ArrayList<>(); final List<PdbFile> preResult = new ArrayList<>();
@@ -123,21 +124,20 @@ public class TagsToFile implements AutoCloseable, CollectionUtils {
try (PdbReader reader = new PdbReader(pdbFile)) { try (PdbReader reader = new PdbReader(pdbFile)) {
if (reader.canSeekTail(2)) { if (reader.canSeekTail(2)) {
reader.seekTail(2); reader.seekTail(2);
final Date lastDate = reader.readDate(); final OffsetDateTime lastWrittenDate = reader.readDate();
return new TimeRange(lastDate, pdbFile.getTimeRange().getTo()); return new TimeRange(lastWrittenDate, pdbFile.getTimeRange().getTo());
} else { } else {
return pdbFile.getTimeRange(); return pdbFile.getTimeRange();
} }
} }
} }
private PdbFile createNewPdbFile(final Date date, final Tags tags) { private PdbFile createNewPdbFile(final OffsetDateTime date, final Tags tags) {
final File file; final File file;
final long dateOffset;
PdbFile result; PdbFile result;
file = createNewFile(date, tags); file = createNewFile(date, tags);
dateOffset = DateUtils.getDateOffset(date); final Day day = new Day(date);
db.addDocument(file); db.addDocument(file);
@@ -147,9 +147,9 @@ public class TagsToFile implements AutoCloseable, CollectionUtils {
db.setProperty(file, Fields.prefixedKey(key), value); db.setProperty(file, Fields.prefixedKey(key), value);
}); });
db.setProperty(file, Fields.DATE_OFFSET, dateOffset); db.setProperty(file, Fields.DATE_OFFSET, day.serialize());
result = new PdbFile(dateOffset, file, tags); result = new PdbFile(day, file, tags);
return result; return result;
} }
@@ -168,8 +168,8 @@ public class TagsToFile implements AutoCloseable, CollectionUtils {
}); });
} }
private File createNewFile(final Date date, final Tags tags) { private File createNewFile(final OffsetDateTime date, final Tags tags) {
final Day day = DateUtils.getDay(date); final Day day = new Day(date);
final String name = tags.abbreviatedRepresentation() + UUID.randomUUID().toString(); final String name = tags.abbreviatedRepresentation() + UUID.randomUUID().toString();
final File result = StorageUtils.createStorageFile(dataDirectory, day, name); final File result = StorageUtils.createStorageFile(dataDirectory, day, name);

View File

@@ -1,14 +1,17 @@
package org.lucares.performance.db; package org.lucares.performance.db;
import java.util.Date; import java.time.Duration;
import java.util.concurrent.TimeUnit; import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
public class TimeRange { public class TimeRange {
private final Date from; private final OffsetDateTime from;
private final Date to; private final OffsetDateTime to;
public TimeRange(final Date from, final Date to) { public TimeRange(final OffsetDateTime from, final OffsetDateTime to) {
if (!from.before(to)) { if (from.isAfter(to)) {
throw new IllegalArgumentException("from date must be before to date. from: " + from + " to: " + to); throw new IllegalArgumentException("from date must be before to date. from: " + from + " to: " + to);
} }
@@ -16,20 +19,27 @@ public class TimeRange {
this.to = to; this.to = to;
} }
public Date getFrom() { public OffsetDateTime getFrom() {
return from; return from;
} }
public Date getTo() { public OffsetDateTime getTo() {
return to; return to;
} }
public long length(final TimeUnit timeUnit) { public Duration duration() {
final long duration = to.getTime() - from.getTime(); return Duration.between(from, to);
return timeUnit.convert(duration, TimeUnit.MILLISECONDS);
} }
public boolean inRange(final Date date) { public boolean inRange(final long epochMilli) {
// TODO @ahr cache the epoch millis
final long fromEpochMilli = from.toInstant().toEpochMilli();
final long toEpochMilli = to.toInstant().toEpochMilli();
return fromEpochMilli <= epochMilli && epochMilli <= toEpochMilli;
}
public boolean inRange(final OffsetDateTime date) {
return from.compareTo(date) <= 0 && to.compareTo(date) >= 0; return from.compareTo(date) <= 0 && to.compareTo(date) >= 0;
} }
@@ -42,14 +52,17 @@ public class TimeRange {
@Override @Override
public String toString() { public String toString() {
return "[" + from + ":" + to + "]";
final DateTimeFormatter formatter = DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(ZoneOffset.UTC);
final String fromUtc = from.format(formatter);
final String totc = from.format(formatter);
return "[" + fromUtc + ":" + totc + "]";
} }
public static TimeRange today() { public static TimeRange ofDay(final OffsetDateTime day) {
final OffsetDateTime from = day.truncatedTo(ChronoUnit.DAYS);
final Date now = new Date(); final OffsetDateTime to = from.plusDays(1).minusNanos(1);
final Date from = DateUtils.getMidnightSameDay(now);
final Date to = DateUtils.getMidnightNextDay(now);
return new TimeRange(from, to); return new TimeRange(from, to);
} }

View File

@@ -4,7 +4,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Date; import java.time.OffsetDateTime;
import org.testng.Assert; import org.testng.Assert;
import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterMethod;
@@ -41,7 +41,8 @@ public class PdbReaderWriterTest {
final File file = Files.createTempFile(dataDirectory, "pdb", ".db").toFile(); final File file = Files.createTempFile(dataDirectory, "pdb", ".db").toFile();
final PdbFile pdbFile = PdbFile.today(file, new Tags()); final PdbFile pdbFile = PdbFile.today(file, new Tags());
final Date now = new Date(); // TODO @ahr might fail at midnight final OffsetDateTime now = OffsetDateTime.now(); // TODO @ahr might fail
// at midnight
final Entry entry = new Entry(now, value); final Entry entry = new Entry(now, value);
try (PdbWriter writer = new PdbWriter(pdbFile)) { try (PdbWriter writer = new PdbWriter(pdbFile)) {

View File

@@ -4,14 +4,15 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils;
import org.testng.Assert; import org.testng.Assert;
import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeMethod;
@@ -35,7 +36,7 @@ public class PerformanceDbTest {
public void testInsertRead() throws Exception { public void testInsertRead() throws Exception {
try (PerformanceDb performanceDb = new PerformanceDb(dataDirectory)) { try (PerformanceDb performanceDb = new PerformanceDb(dataDirectory)) {
final Date date = new Date(); final OffsetDateTime date = DateUtils.nowInUtc();
final long value = 1; final long value = 1;
final Tags tags = new Tags("myKey", "myValue"); final Tags tags = new Tags("myKey", "myValue");
performanceDb.put(date, value, tags); performanceDb.put(date, value, tags);
@@ -51,8 +52,8 @@ public class PerformanceDbTest {
public void testInsertIntoMultipleFilesRead() throws Exception { public void testInsertIntoMultipleFilesRead() throws Exception {
try (PerformanceDb performanceDb = new PerformanceDb(dataDirectory)) { try (PerformanceDb performanceDb = new PerformanceDb(dataDirectory)) {
final Date dayOne = DateUtils.getDate(2016, 11, 1, 10, 0, 0); final OffsetDateTime dayOne = DateUtils.getDate(2016, 11, 1, 10, 0, 0);
final Date dayTwo = DateUtils.getDate(2016, 11, 2, 12, 34, 56); final OffsetDateTime dayTwo = DateUtils.getDate(2016, 11, 2, 12, 34, 56);
final long valueOne = 1; final long valueOne = 1;
final long valueTwo = 2; final long valueTwo = 2;
final Tags tags = new Tags("myKey", "myValue"); final Tags tags = new Tags("myKey", "myValue");
@@ -69,15 +70,14 @@ public class PerformanceDbTest {
} }
} }
private List<Entry> generateEntries(final TimeRange timeRange, final long n) { private List<Entry> generateEntries(final TimeRange timeRange, final long n, final int addToDate) {
final List<Entry> result = new ArrayList<>(); final List<Entry> result = new ArrayList<>();
final long differenceInMs = timeRange.length(TimeUnit.MILLISECONDS) / n; final long differenceInMs = timeRange.duration().toMillis() / n;
long currentTime = timeRange.getFrom().getTime(); long currentTime = timeRange.getFrom().toInstant().toEpochMilli();
for (long i = 0; i < n; i++) { for (long i = 0; i < n; i++) {
final Date date = new Date(currentTime);
final long value = ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE); final long value = ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE);
result.add(new Entry(date, value)); result.add(new Entry(currentTime + addToDate, value));
currentTime += differenceInMs; currentTime += differenceInMs;
} }
@@ -88,11 +88,11 @@ public class PerformanceDbTest {
try (PerformanceDb performanceDb = new PerformanceDb(dataDirectory)) { try (PerformanceDb performanceDb = new PerformanceDb(dataDirectory)) {
final TimeRange timeRange = TimeRange.today(); final TimeRange timeRange = TimeRange.ofDay(OffsetDateTime.now(ZoneOffset.UTC));
final long numberOfEntries = 2; final long numberOfEntries = 2;
final Tags tags = new Tags("myKey", "one"); final Tags tags = new Tags("myKey", "one");
final List<Entry> entries = generateEntries(timeRange, numberOfEntries); final List<Entry> entries = generateEntries(timeRange, numberOfEntries, 0);
printEntries(entries, ""); printEntries(entries, "");
@@ -103,8 +103,8 @@ public class PerformanceDbTest {
final List<Entry> actualEntries = performanceDb.getAsList(tags); final List<Entry> actualEntries = performanceDb.getAsList(tags);
Assert.assertEquals(actualEntries, entries); Assert.assertEquals(actualEntries, entries);
final File storageFileForToday = StorageUtils.createStorageFile(dataDirectory, final File storageFileForToday = StorageUtils.createStorageFile(dataDirectory, new Day(timeRange.getFrom()),
DateUtils.getDay(timeRange.getFrom()), "name doesn't matter"); "name doesn't matter");
final File storageFolderForToday = storageFileForToday.getParentFile(); final File storageFolderForToday = storageFileForToday.getParentFile();
final File[] filesInStorage = storageFolderForToday.listFiles(); final File[] filesInStorage = storageFolderForToday.listFiles();
Assert.assertEquals(filesInStorage.length, 1, Assert.assertEquals(filesInStorage.length, 1,
@@ -115,27 +115,25 @@ public class PerformanceDbTest {
public void testInsertIntoMultipleFilesWithDifferentTags() throws Exception { public void testInsertIntoMultipleFilesWithDifferentTags() throws Exception {
try (PerformanceDb performanceDb = new PerformanceDb(dataDirectory)) { try (PerformanceDb performanceDb = new PerformanceDb(dataDirectory)) {
final Date from = DateUtils.getDate(2016, 1, 1, 00, 00, 00); final OffsetDateTime from = DateUtils.getDate(2016, 1, 1, 00, 00, 00);
final Date to = DateUtils.getDate(2016, 12, 31, 23, 59, 59); final OffsetDateTime to = DateUtils.getDate(2016, 1, 1, 23, 59, 50);
final TimeRange timeRange = new TimeRange(from, to); final TimeRange timeRange = new TimeRange(from, to);
final long numberOfEntries = timeRange.length(TimeUnit.DAYS) * 2; // two final long numberOfEntries = timeRange.duration().toHours();
// entries
// per
// day
final Tags tagsCommon = new Tags("commonKey", "commonValue");
final Tags tagsOne = new Tags("myKey", "one", "commonKey", "commonValue"); final Tags tagsOne = new Tags("myKey", "one", "commonKey", "commonValue");
final List<Entry> entriesOne = generateEntries(timeRange, numberOfEntries); final List<Entry> entriesOne = generateEntries(timeRange, numberOfEntries, 1);
printEntries(entriesOne, "one"); printEntries(entriesOne, "one");
performanceDb.put(entriesOne, tagsOne); performanceDb.put(entriesOne, tagsOne);
final Tags tagsTwo = new Tags("myKey", "two", "commonKey", "commonValue"); final Tags tagsTwo = new Tags("myKey", "two", "commonKey", "commonValue");
final List<Entry> entriesTwo = generateEntries(timeRange, numberOfEntries); final List<Entry> entriesTwo = generateEntries(timeRange, numberOfEntries, 2);
printEntries(entriesTwo, "two"); printEntries(entriesTwo, "two");
performanceDb.put(entriesTwo, tagsTwo); performanceDb.put(entriesTwo, tagsTwo);
final Tags tagsThree = new Tags("myKey", "three", "commonKey", "commonValue"); final Tags tagsThree = new Tags("myKey", "three", "commonKey", "commonValue");
final List<Entry> entriesThree = generateEntries(timeRange, numberOfEntries); final List<Entry> entriesThree = generateEntries(timeRange, numberOfEntries, 3);
printEntries(entriesThree, "three"); printEntries(entriesThree, "three");
performanceDb.put(entriesThree, tagsThree); performanceDb.put(entriesThree, tagsThree);
@@ -148,6 +146,13 @@ public class PerformanceDbTest {
final List<Entry> actualEntriesThree = performanceDb.getAsList(tagsThree); final List<Entry> actualEntriesThree = performanceDb.getAsList(tagsThree);
Assert.assertEquals(actualEntriesThree, entriesThree); Assert.assertEquals(actualEntriesThree, entriesThree);
final List<Entry> actualEntriesAll = performanceDb.getAsList(tagsCommon);
final List<Entry> expectedAll = CollectionUtils.collate(entriesOne,
CollectionUtils.collate(entriesTwo, entriesThree, Entry.BY_DATE), Entry.BY_DATE);
actualEntriesAll.sort(Entry.BY_DATE);
Assert.assertEquals(actualEntriesAll, expectedAll);
} }
} }
@@ -155,8 +160,12 @@ public class PerformanceDbTest {
int index = 0; int index = 0;
for (final Entry entry : entriesOne) { for (final Entry entry : entriesOne) {
System.out.printf("%4d %s %d (%s)\n", index, DateUtils.format(entry.getDate()), entry.getValue(), label); System.out.printf("%4d %s %d (%s)\n", index, entry.getDate(), entry.getValue(), label);
index++; index++;
} }
} }
public void testBlockingIteratorInput() throws Exception {
}
} }

View File

@@ -3,7 +3,8 @@ package org.lucares.performance.db;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Date; import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import org.testng.Assert; import org.testng.Assert;
import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterMethod;
@@ -28,7 +29,7 @@ public class TagsToFilesTest {
public void test() throws Exception { public void test() throws Exception {
try (final TagsToFile tagsToFile = new TagsToFile(dataDirectory)) { try (final TagsToFile tagsToFile = new TagsToFile(dataDirectory)) {
final Date date = new Date(); final OffsetDateTime date = OffsetDateTime.now(ZoneOffset.UTC);
final Tags tags = new Tags("myKey", "myValue"); final Tags tags = new Tags("myKey", "myValue");
final PdbFile newFileForTags = tagsToFile.getFile(date, tags); final PdbFile newFileForTags = tagsToFile.getFile(date, tags);

View File

@@ -1,7 +1,9 @@
package org.lucares.performance.db; package org.lucares.performance.db;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import org.testng.Assert; import org.testng.Assert;
@@ -15,10 +17,10 @@ public class TimeRangeTest {
Object[][] providerIntersect() { Object[][] providerIntersect() {
final List<Object[]> result = new ArrayList<>(); final List<Object[]> result = new ArrayList<>();
final Date a = new Date(1000); final OffsetDateTime a = Instant.ofEpochMilli(1000).atOffset(ZoneOffset.UTC);
final Date b = new Date(2000); final OffsetDateTime b = Instant.ofEpochMilli(2000).atOffset(ZoneOffset.UTC);
final Date c = new Date(3000); final OffsetDateTime c = Instant.ofEpochMilli(3000).atOffset(ZoneOffset.UTC);
final Date d = new Date(4000); final OffsetDateTime d = Instant.ofEpochMilli(4000).atOffset(ZoneOffset.UTC);
result.add(new Object[] { new TimeRange(a, b), new TimeRange(c, d), false }); result.add(new Object[] { new TimeRange(a, b), new TimeRange(c, d), false });
result.add(new Object[] { new TimeRange(a, c), new TimeRange(b, d), true }); result.add(new Object[] { new TimeRange(a, c), new TimeRange(b, d), true });
@@ -34,4 +36,5 @@ public class TimeRangeTest {
Assert.assertEquals(a.intersect(b), expected, a + " intersects " + b); Assert.assertEquals(a.intersect(b), expected, a + " intersects " + b);
Assert.assertEquals(b.intersect(a), expected, a + " intersects " + b); Assert.assertEquals(b.intersect(a), expected, a + " intersects " + b);
} }
} }

5
recommind-logs/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
/bin/
/test-output/
/.settings/
/.classpath
/.project

View File

@@ -0,0 +1,6 @@
dependencies {
compile project(':performanceDb')
compile "io.thekraken:grok:0.1.5"
}

View File

@@ -0,0 +1,37 @@
package org.lucares.recommind.logs;
import java.util.Iterator;
import org.lucares.performance.db.Entry;
import io.thekraken.grok.api.Grok;
import io.thekraken.grok.api.Match;
public class LogReader implements Iterable<Entry> {
private final Grok grok;
public LogReader(final Grok grok) {
super();
this.grok = grok;
}
@Override
public Iterator<Entry> iterator() {
// Grok grok = Grok.create("patterns/patterns");
/** Grok pattern to compile, here httpd logs */
// grok.compile("%{COMBINEDAPACHELOG}");
/** Line of log to match */
final String log = "112.169.19.192 - - [06/Mar/2013:01:36:30 +0900] \"GET / HTTP/1.1\" 200 44346 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.152 Safari/537.22\"";
final Match gm = grok.match(log);
gm.captures();
gm.toMap();
return null;
}
}

View File

@@ -0,0 +1,19 @@
package org.lucares.recommind.logs;
import java.io.File;
import java.util.concurrent.ArrayBlockingQueue;
import org.lucares.performance.db.Entry;
import org.lucares.performance.db.PerformanceDb;
import org.lucares.performance.db.Tags;
public class PerformanceLogs {
public void ingest(final PerformanceDb db, final File performanceLog, final Tags tags) {
final ArrayBlockingQueue<Entry> queue = new ArrayBlockingQueue<>(10);
db.put(queue, tags);
}
}