changeset 1560:54538c884737

Make VmTimeIntervalPojoListGetter query with a partially closed interval instead of an open interval Reviewed-by: jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2014-November/011657.html
author Jie Kang <jkang@redhat.com>
date Wed, 19 Nov 2014 14:08:44 -0500
parents 5c6937a60c17
children 7b5d6d7edb9b
files storage/core/src/main/java/com/redhat/thermostat/storage/core/VmTimeIntervalPojoListGetter.java storage/core/src/test/java/com/redhat/thermostat/storage/core/VmTimeIntervalPojoListGetterTest.java
diffstat 2 files changed, 199 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/VmTimeIntervalPojoListGetter.java	Wed Nov 19 13:35:05 2014 -0500
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/VmTimeIntervalPojoListGetter.java	Wed Nov 19 14:08:44 2014 -0500
@@ -51,10 +51,14 @@
  */
 public class VmTimeIntervalPojoListGetter <T extends TimeStampedPojo> extends AbstractGetter<T> {
 
+    // The query for VmTimeIntervalPojoListGetter should query for since <= timestamp < to
+    // in order not to miss data for multiple consecutive queries of the form [a, b), [b, c), ...
+    // If the query were since < timestamp < to then queries of (a, b), (b, c), ... would
+    // result in missed data at the endpoints (b, ...)
     public static final String VM_INTERVAL_QUERY_FORMAT = "QUERY %s WHERE '"
             + Key.AGENT_ID.getName() + "' = ?s AND '"
             + Key.VM_ID.getName() + "' = ?s AND '" 
-            + Key.TIMESTAMP.getName() + "' > ?l AND '"
+            + Key.TIMESTAMP.getName() + "' >= ?l AND '"
             + Key.TIMESTAMP.getName() + "' < ?l SORT '"
             + Key.TIMESTAMP.getName() + "' DSC";
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/core/VmTimeIntervalPojoListGetterTest.java	Wed Nov 19 14:08:44 2014 -0500
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.storage.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.model.TimeStampedPojo;
+
+public class VmTimeIntervalPojoListGetterTest {
+    private static final String AGENT_ID = "agentid";
+    private static final String HOSTNAME = "host.example.com";
+    private static final String VM_ID = "vmId";
+    private static final int VM_PID = 123;
+    private static final String MAIN_CLASS = "Foo.class";
+    private static final String CATEGORY_NAME = "vm-timeinterval-category";
+    // Make this one static so we don't get IllegalStateException from trying
+    // to make category of same name while running tests in same classloader.
+    private static final Category<TestPojo> cat =  new Category<>(CATEGORY_NAME, TestPojo.class);
+
+    private static long t1 = 1;
+    private static long t2 = 5;
+    private static long t3 = 10;
+
+    private static long lc1 = 10;
+    private static long lc2 = 20;
+    private static long lc3 = 30;
+
+    private HostRef hostRef;
+    private VmRef vmRef;
+    private TestPojo result1, result2, result3;
+
+    @Before
+    public void setUp() {
+        hostRef = new HostRef(AGENT_ID, HOSTNAME);
+        vmRef = new VmRef(hostRef, VM_ID, VM_PID, MAIN_CLASS);
+        result1 = mock(TestPojo.class);
+        when(result1.getTimeStamp()).thenReturn(t1);
+        when(result1.getData()).thenReturn(lc1);
+        result2 = mock(TestPojo.class);
+        when(result2.getTimeStamp()).thenReturn(t2);
+        when(result2.getData()).thenReturn(lc2);
+        result3 = mock(TestPojo.class);
+        when(result3.getTimeStamp()).thenReturn(t3);
+        when(result3.getData()).thenReturn(lc3);
+    }
+
+    @Test
+    public void verifyQueryDescriptorFormat() {
+        String expected = "QUERY %s WHERE 'agentId' = ?s AND " +
+                "'vmId' = ?s AND 'timeStamp' >= ?l AND 'timeStamp' < ?l SORT 'timeStamp' DSC";
+        assertEquals(expected, VmTimeIntervalPojoListGetter.VM_INTERVAL_QUERY_FORMAT);
+    }
+
+    @Test
+    public void verifyQueryDescriptorIsSane() {
+        Storage storage = mock(Storage.class);
+        VmTimeIntervalPojoListGetter<TestPojo> getter = new VmTimeIntervalPojoListGetter<>(storage, cat);
+        String actualDesc = getter.getQueryLatestDesc();
+        String expected = "QUERY " + CATEGORY_NAME + " WHERE 'agentId' = ?s AND " +
+                "'vmId' = ?s AND 'timeStamp' >= ?l AND 'timeStamp' < ?l SORT 'timeStamp' DSC";
+        assertEquals(expected, actualDesc);
+    }
+
+    @Test
+    public void testBuildQuery() throws DescriptorParsingException {
+        Storage storage = mock(Storage.class);
+        @SuppressWarnings("unchecked")
+        PreparedStatement<TestPojo> query = (PreparedStatement<TestPojo>) mock(PreparedStatement.class);
+        when(storage.prepareStatement(anyDescriptor())).thenReturn(query);
+
+        VmTimeIntervalPojoListGetter<TestPojo> getter = new VmTimeIntervalPojoListGetter<>(storage, cat);
+        query = getter.buildQuery(vmRef, 0l, 10l);
+
+        assertNotNull(query);
+        verify(storage).prepareStatement(anyDescriptor());
+        verify(query).setString(0, AGENT_ID);
+        verify(query).setString(1, VM_ID);
+        verify(query).setLong(2, 0l);
+        verify(query).setLong(3, 10l);
+        verifyNoMoreInteractions(query);
+    }
+
+    @SuppressWarnings("unchecked")
+    private StatementDescriptor<TestPojo> anyDescriptor() {
+        return (StatementDescriptor<TestPojo>) any(StatementDescriptor.class);
+    }
+
+    @Test
+    public void testBuildQueryPopulatesUpdateTimes() throws DescriptorParsingException {
+        Storage storage = mock(Storage.class);
+        @SuppressWarnings("unchecked")
+        PreparedStatement<TestPojo> ignored = (PreparedStatement<TestPojo>) mock(PreparedStatement.class);
+        @SuppressWarnings("unchecked")
+        PreparedStatement<TestPojo> query = (PreparedStatement<TestPojo>) mock(PreparedStatement.class);
+        when(storage.prepareStatement(anyDescriptor())).thenReturn(ignored).thenReturn(query);
+
+        VmTimeIntervalPojoListGetter<TestPojo> getter = new VmTimeIntervalPojoListGetter<>(storage, cat);
+        getter.buildQuery(vmRef, Long.MIN_VALUE, 100l); // Ignore first return value.
+        query = getter.buildQuery(vmRef, Long.MIN_VALUE, 100l);
+
+        assertNotNull(query);
+        verify(storage, times(2)).prepareStatement(anyDescriptor());
+        verify(query).setString(0, AGENT_ID);
+        verify(query).setString(1, VM_ID);
+        verify(query).setLong(2, Long.MIN_VALUE);
+        verify(query).setLong(3, 100l);
+        verifyNoMoreInteractions(query);
+    }
+
+    @Test
+    public void testGetInterval() throws DescriptorParsingException, StatementExecutionException {
+        @SuppressWarnings("unchecked")
+        Cursor<TestPojo> cursor = mock(Cursor.class);
+        when(cursor.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false);
+        when(cursor.next()).thenReturn(result1).thenReturn(result2).thenReturn(null);
+
+        Storage storage = mock(Storage.class);
+        @SuppressWarnings("unchecked")
+        PreparedStatement<TestPojo> query = (PreparedStatement<TestPojo>) mock(PreparedStatement.class);
+        when(storage.prepareStatement(anyDescriptor())).thenReturn(query);
+        when(query.executeQuery()).thenReturn(cursor);
+
+        VmTimeIntervalPojoListGetter<TestPojo> getter = new VmTimeIntervalPojoListGetter<>(storage, cat);
+
+        List<TestPojo> stats = getter.getLatest(vmRef, t1, t2);
+
+        verify(storage).prepareStatement(anyDescriptor());
+        verify(query).setString(0, AGENT_ID);
+        verify(query).setString(1, VM_ID);
+        verify(query).setLong(2, t1);
+        verify(query).setLong(3, t2);
+        verify(query).executeQuery();
+        verifyNoMoreInteractions(query);
+
+        assertNotNull(stats);
+        assertEquals(2, stats.size());
+        TestPojo stat1 = stats.get(0);
+        assertEquals(t1, stat1.getTimeStamp());
+        assertEquals(lc1, stat1.getData());
+        TestPojo stat2 = stats.get(1);
+        assertEquals(t2, stat2.getTimeStamp());
+        assertEquals(lc2, stat2.getData());
+    }
+
+    private static interface TestPojo extends TimeStampedPojo {
+        long getData();
+    }
+}