changeset 1593:0133da742cc7

Make vm-memory tab query less data GUI side for PR2006 Reviewed-by: omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2014-December/011998.html
author Jie Kang <jkang@redhat.com>
date Thu, 04 Dec 2014 14:13:40 -0500
parents 3c55a3f203b8
children 5c308934c4fc
files client/core/src/main/java/com/redhat/thermostat/client/core/experimental/SingleValueStat.java client/core/src/main/java/com/redhat/thermostat/client/core/experimental/SingleValueSupplier.java client/core/src/main/java/com/redhat/thermostat/client/core/experimental/TimeRangeController.java common/core/pom.xml vm-classstat/client-core/src/main/java/com/redhat/thermostat/vm/classstat/client/core/internal/VmClassStatController.java vm-cpu/client-core/src/main/java/com/redhat/thermostat/vm/cpu/client/core/internal/VmCpuController.java vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsController.java vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsControllerTest.java vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/VmMemoryStatDAO.java vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOImpl.java vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOImplStatementDescriptorRegistration.java vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOImplStatementDescriptorRegistrationTest.java vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOTest.java
diffstat 13 files changed, 201 insertions(+), 272 deletions(-) [+]
line wrap: on
line diff
--- a/client/core/src/main/java/com/redhat/thermostat/client/core/experimental/SingleValueStat.java	Thu Dec 04 10:40:57 2014 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/*
- * 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.client.core.experimental;
-
-import com.redhat.thermostat.storage.model.TimeStampedPojo;
-
-public interface SingleValueStat<T> extends TimeStampedPojo {
-
-    public T getValue();
-
-}
--- a/client/core/src/main/java/com/redhat/thermostat/client/core/experimental/SingleValueSupplier.java	Thu Dec 04 10:40:57 2014 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-/*
- * 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.client.core.experimental;
-
-import com.redhat.thermostat.storage.core.VmRef;
-
-import java.util.List;
-
-public interface SingleValueSupplier<T> {
-
-    public abstract List<T> getStats(VmRef ref, long since, long to);
-}
--- a/client/core/src/main/java/com/redhat/thermostat/client/core/experimental/TimeRangeController.java	Thu Dec 04 10:40:57 2014 -0500
+++ b/client/core/src/main/java/com/redhat/thermostat/client/core/experimental/TimeRangeController.java	Thu Dec 04 14:13:40 2014 -0500
@@ -38,18 +38,23 @@
 
 import com.redhat.thermostat.common.model.Range;
 import com.redhat.thermostat.storage.core.VmRef;
-import com.redhat.thermostat.storage.model.DiscreteTimeData;
 
-import java.util.ArrayList;
 import java.util.List;
 
-public class TimeRangeController {
+public class TimeRangeController <T> {
+
+    public interface SingleArgRunnable <T> {
+        public void run(T arg);
+    }
+
+    public interface StatsSupplier<T> {
+        public abstract List<T> getStats(VmRef ref, long since, long to);
+    }
 
     private Range<Long> availableRange = new Range<>(Long.MAX_VALUE, Long.MIN_VALUE);
     private Range<Long> displayedRange = new Range<>(Long.MAX_VALUE, Long.MIN_VALUE);
-    private List<DiscreteTimeData<Number>> allToDisplay;
 
-    public void update(Duration userDesiredDuration,Range<Long> newAvailableRange, SingleValueSupplier dao, VmRef ref) {
+    public void update(Duration userDesiredDuration, Range<Long> newAvailableRange, StatsSupplier<T> dao, VmRef ref, SingleArgRunnable<T> updater) {
         long now = System.currentTimeMillis();
         long userVisibleTimeDelta = (userDesiredDuration.unit.toMillis(userDesiredDuration.value));
         Range<Long> desiredRange = new Range<>(now - userVisibleTimeDelta, now);
@@ -65,10 +70,11 @@
         long displayedMin = Math.min(displayedRange.getMin(), Long.MAX_VALUE);
         long displayedMax = Math.max(displayedRange.getMax(), Long.MIN_VALUE);
 
-        allToDisplay = new ArrayList<>();
         for (Range<Long> interval : additionalIntervals) {
-            List<SingleValueStat> stats = dao.getStats(ref, interval.getMin(), interval.getMax());
-            allToDisplay.addAll(getDiscreteTimeData(stats));
+            List<T> stats = dao.getStats(ref, interval.getMin(), interval.getMax());
+            for (T stat : stats) {
+                updater.run(stat);
+            }
 
             displayedMin = Math.min(displayedMin, interval.getMin());
             displayedMax = Math.max(displayedMax, interval.getMax());
@@ -80,21 +86,6 @@
         displayedRange = new Range<>(displayedMin, displayedMax);
     }
 
-    public List<DiscreteTimeData<Number>> getDataToDisplay() {
-        return new ArrayList<>(allToDisplay);
-    }
-
-    private List<DiscreteTimeData<Number>> getDiscreteTimeData(List<SingleValueStat> stats) {
-        List<DiscreteTimeData<Number>> toDisplay = new ArrayList<>(stats.size());
-
-        for (SingleValueStat stat : stats) {
-            DiscreteTimeData<Number> data =
-                    new DiscreteTimeData<>(stat.getTimeStamp(),(Number) stat.getValue());
-            toDisplay.add(data);
-        }
-        return toDisplay;
-    }
-
     public Range<Long> getAvailableRange() {
         return availableRange;
     }
--- a/common/core/pom.xml	Thu Dec 04 10:40:57 2014 -0500
+++ b/common/core/pom.xml	Thu Dec 04 14:13:40 2014 -0500
@@ -86,6 +86,7 @@
               com.redhat.thermostat.common.utils,
               com.redhat.thermostat.common.ssl,
               com.redhat.thermostat.common.model,
+              com.redhat.thermostat.common.experimental,
             </Export-Package>
             <Private-Package>
               com.redhat.thermostat.test,
--- a/vm-classstat/client-core/src/main/java/com/redhat/thermostat/vm/classstat/client/core/internal/VmClassStatController.java	Thu Dec 04 10:40:57 2014 -0500
+++ b/vm-classstat/client-core/src/main/java/com/redhat/thermostat/vm/classstat/client/core/internal/VmClassStatController.java	Thu Dec 04 14:13:40 2014 -0500
@@ -55,8 +55,7 @@
 import com.redhat.thermostat.shared.locale.LocalizedString;
 import com.redhat.thermostat.shared.locale.Translate;
 import com.redhat.thermostat.storage.core.VmRef;
-import com.redhat.thermostat.client.core.experimental.SingleValueSupplier;
-import com.redhat.thermostat.client.core.experimental.SingleValueStat;
+import com.redhat.thermostat.storage.model.DiscreteTimeData;
 import com.redhat.thermostat.vm.classstat.client.core.VmClassStatView;
 import com.redhat.thermostat.vm.classstat.client.core.VmClassStatViewProvider;
 import com.redhat.thermostat.vm.classstat.client.locale.LocaleResources;
@@ -67,46 +66,37 @@
 
     private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
 
-    private TimeRangeController timeRangeController;
+    private TimeRangeController<VmClassStat> timeRangeController;
 
     private class UpdateChartData implements Runnable {
         @Override
         public void run() {
+            final List<DiscreteTimeData<Long>> data = new ArrayList<>();
 
             VmClassStat oldest = dao.getOldest(ref);
             VmClassStat latest = dao.getLatest(ref);
 
             Range<Long> newAvailableRange = new Range<>(oldest.getTimeStamp(), latest.getTimeStamp());
 
-            SingleValueSupplier singleValueSupplier = new SingleValueSupplier() {
+            TimeRangeController.StatsSupplier<VmClassStat> singleValueSupplier = new TimeRangeController.StatsSupplier() {
                 @Override
-                public List getStats(final VmRef ref, final long since, final long to) {
-                    List<VmClassStat> stats = dao.getClassStats(ref, since, to);
-                    List<SingleValueStat<Long>> singleValueStats = new ArrayList<>();
-                    for (final VmClassStat stat : stats ) {
-                        singleValueStats.add(new SingleValueStat<Long>() {
-                            @Override
-                            public Long getValue() {
-                                return stat.getLoadedClasses();
-                            }
-
-                            @Override
-                            public long getTimeStamp() {
-                                return stat.getTimeStamp();
-                            }
-                        });
-                    }
-                    return singleValueStats;
+                public List<VmClassStat> getStats(final VmRef ref, final long since, final long to) {
+                    return dao.getClassStats(ref, since, to);
                 }
             };
 
-            timeRangeController.update(userDesiredDuration, newAvailableRange, singleValueSupplier, ref);
+            TimeRangeController.SingleArgRunnable<VmClassStat> runnable = new TimeRangeController.SingleArgRunnable<VmClassStat>() {
+                @Override
+                public void run(VmClassStat arg) {
+                    data.add(new DiscreteTimeData<>(arg.getTimeStamp(), arg.getLoadedClasses()));
+                }
+            };
+
+            timeRangeController.update(userDesiredDuration, newAvailableRange, singleValueSupplier, ref, runnable);
             classesView.setAvailableDataRange(timeRangeController.getAvailableRange());
 
-            List classCount = timeRangeController.getDataToDisplay();
-            classesView.addClassCount(classCount);
+            classesView.addClassCount(data);
         }
-
     }
 
     private final VmClassStatView classesView;
@@ -163,8 +153,7 @@
 
         userDesiredDuration = classesView.getUserDesiredDuration();
 
-        timeRangeController = new TimeRangeController();
-
+        timeRangeController = new TimeRangeController<>();
     }
 
     private void start() {
--- a/vm-cpu/client-core/src/main/java/com/redhat/thermostat/vm/cpu/client/core/internal/VmCpuController.java	Thu Dec 04 10:40:57 2014 -0500
+++ b/vm-cpu/client-core/src/main/java/com/redhat/thermostat/vm/cpu/client/core/internal/VmCpuController.java	Thu Dec 04 14:13:40 2014 -0500
@@ -55,8 +55,7 @@
 import com.redhat.thermostat.shared.locale.LocalizedString;
 import com.redhat.thermostat.shared.locale.Translate;
 import com.redhat.thermostat.storage.core.VmRef;
-import com.redhat.thermostat.client.core.experimental.SingleValueSupplier;
-import com.redhat.thermostat.client.core.experimental.SingleValueStat;
+import com.redhat.thermostat.storage.model.DiscreteTimeData;
 import com.redhat.thermostat.vm.cpu.client.core.VmCpuView;
 import com.redhat.thermostat.vm.cpu.client.core.VmCpuViewProvider;
 import com.redhat.thermostat.vm.cpu.client.core.VmCpuView.UserAction;
@@ -76,7 +75,7 @@
 
     private Duration userDesiredDuration;
 
-    private TimeRangeController timeRangeController;
+    private TimeRangeController<VmCpuStat> timeRangeController;
 
     public VmCpuController(ApplicationService appSvc, VmCpuStatDAO vmCpuStatDao, VmRef ref, VmCpuViewProvider provider) {
         this.ref = ref;
@@ -130,7 +129,7 @@
 
         userDesiredDuration = view.getUserDesiredDuration();
 
-        timeRangeController = new TimeRangeController();
+        timeRangeController = new TimeRangeController<>();
     }
 
     private void start() {
@@ -138,37 +137,30 @@
     }
 
     private void updateData() {
+        final List<DiscreteTimeData<? extends Number>> data = new ArrayList<>();
+
         VmCpuStat oldest = dao.getOldest(ref);
         VmCpuStat latest = dao.getLatest(ref);
 
         Range<Long> newAvailableRange = new Range<>(oldest.getTimeStamp(), latest.getTimeStamp());
 
-        SingleValueSupplier singleValueSupplier = new SingleValueSupplier() {
+        TimeRangeController.StatsSupplier<VmCpuStat> singleValueSupplier = new TimeRangeController.StatsSupplier<VmCpuStat>() {
             @Override
-            public List getStats(final VmRef ref, final long since, final long to) {
-                List<VmCpuStat> stats = dao.getVmCpuStats(ref, since, to);
-                List<SingleValueStat<Double>> singleValueStats = new ArrayList<>();
-                for (final VmCpuStat stat : stats ) {
-                    singleValueStats.add(new SingleValueStat<Double>() {
-                        @Override
-                        public Double getValue() {
-                            return stat.getCpuLoad();
-                        }
+            public List<VmCpuStat> getStats(final VmRef ref, final long since, final long to) {
+                return dao.getVmCpuStats(ref, since, to);
 
-                        @Override
-                        public long getTimeStamp() {
-                            return stat.getTimeStamp();
-                        }
-                    });
-                }
-                return singleValueStats;
            }
         };
 
-        timeRangeController.update(userDesiredDuration, newAvailableRange, singleValueSupplier, ref);
+        TimeRangeController.SingleArgRunnable<VmCpuStat> runnable = new TimeRangeController.SingleArgRunnable<VmCpuStat>() {
+            @Override
+            public void run(VmCpuStat arg) {
+                data.add(new DiscreteTimeData<>(arg.getTimeStamp(), arg.getCpuLoad()));
+            }
+        };
+
+        timeRangeController.update(userDesiredDuration, newAvailableRange, singleValueSupplier, ref, runnable);
         view.setAvailableDataRange(timeRangeController.getAvailableRange());
-
-        List data = timeRangeController.getDataToDisplay();
         view.addData(data);
     }
 
--- a/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsController.java	Thu Dec 04 10:40:57 2014 -0500
+++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsController.java	Thu Dec 04 14:13:40 2014 -0500
@@ -42,6 +42,8 @@
 import java.util.concurrent.TimeUnit;
 
 import com.redhat.thermostat.client.core.controllers.InformationServiceController;
+import com.redhat.thermostat.client.core.experimental.Duration;
+import com.redhat.thermostat.client.core.experimental.TimeRangeController;
 import com.redhat.thermostat.client.core.views.BasicView.Action;
 import com.redhat.thermostat.client.core.views.UIComponent;
 import com.redhat.thermostat.common.ActionEvent;
@@ -55,6 +57,7 @@
 import com.redhat.thermostat.common.command.RequestResponseListener;
 import com.redhat.thermostat.common.command.Response;
 import com.redhat.thermostat.common.command.Response.ResponseType;
+import com.redhat.thermostat.common.model.Range;
 import com.redhat.thermostat.gc.remote.common.GCRequest;
 import com.redhat.thermostat.gc.remote.common.command.GCAction;
 import com.redhat.thermostat.shared.locale.LocalizedString;
@@ -86,6 +89,10 @@
     private final Map<String, Payload> regions;
     
     private VMCollector collector;
+
+    private Duration userDesiredDuration = new Duration(defaultDuration.value, defaultDuration.unit);
+
+    private TimeRangeController<VmMemoryStat> timeRangeController;
     
     class VMCollector implements Runnable {
 
@@ -93,10 +100,28 @@
 
         @Override
         public void run() {
-            List<VmMemoryStat> vmInfo = vmDao.getLatestVmMemoryStats(ref, desiredUpdateTimeStamp);
-            for (VmMemoryStat memoryStats: vmInfo) {
-                update(memoryStats);
-            }
+            VmMemoryStat oldest = vmDao.getOldestMemoryStat(ref);
+            VmMemoryStat latest = vmDao.getLatestMemoryStat(ref);
+
+            Range<Long> newAvailableRange = new Range<>(oldest.getTimeStamp(), latest.getTimeStamp());
+
+            timeRangeController = new TimeRangeController<>();
+
+            TimeRangeController.StatsSupplier<VmMemoryStat> statsSupplier = new TimeRangeController.StatsSupplier<VmMemoryStat>() {
+                @Override
+                public List<VmMemoryStat> getStats(VmRef ref, long since, long to) {
+                    return vmDao.getVmMemoryStats(ref, since, to);
+                }
+            };
+
+            TimeRangeController.SingleArgRunnable<VmMemoryStat> runnable = new TimeRangeController.SingleArgRunnable<VmMemoryStat>() {
+                @Override
+                public void run(VmMemoryStat arg) {
+                    update(arg);
+                }
+            };
+
+            timeRangeController.update(userDesiredDuration, newAvailableRange, statsSupplier, ref, runnable);
         }
 
         private void update(VmMemoryStat memoryStats) {
--- a/vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsControllerTest.java	Thu Dec 04 10:40:57 2014 -0500
+++ b/vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsControllerTest.java	Thu Dec 04 14:13:40 2014 -0500
@@ -52,7 +52,6 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.TimeUnit;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -82,24 +81,26 @@
 public class MemoryStatsControllerTest {
 
     private Generation[] generations = new Generation[2];
-    
+
     private VmInfoDAO infoDao;
     private VmMemoryStatDAO memoryStatDao;
     private MemoryStatsView view;
     private Timer timer;
-    
+
     private ActionListener<MemoryStatsView.Action> viewListener;
     private ActionListener<GCAction> gcActionListener;
 
     private MemoryStatsController controller;
-    
+
     private Space canary;
 
     private AgentInfoDAO agentDAO;
     private GCRequest gcRequest;
-    
+
     private VmRef ref;
-    
+
+    private Long[] timestamps = new Long[5];
+
     @SuppressWarnings({ "unchecked", "rawtypes" })
     @Before
     public void setUp() {
@@ -112,23 +113,23 @@
         timer = mock(Timer.class);
         ArgumentCaptor<Runnable> actionCaptor = ArgumentCaptor.forClass(Runnable.class);
         doNothing().when(timer).setAction(actionCaptor.capture());
-        
+
         TimerFactory timerFactory = mock(TimerFactory.class);
         when(timerFactory.createTimer()).thenReturn(timer);
         ApplicationService appSvc = mock(ApplicationService.class);
         when(appSvc.getTimerFactory()).thenReturn(timerFactory);
-        
+
         VmInfo vmOverallInformation = mock(VmInfo.class);
         when(vmOverallInformation.isAlive()).thenReturn(vmIsAlive);
         infoDao = mock(VmInfoDAO.class);
         when(infoDao.getVmInfo(any(VmRef.class))).thenReturn(vmOverallInformation);
 
         List<VmMemoryStat> vmInfo = new ArrayList<>();
-        
+
         for (int i = 0; i < 2; i++) {
             Generation generation = new Generation();
             generation.setName("fluff" + i);
-            VmMemoryStat.Space[] spaces = new VmMemoryStat.Space[2 + (1 - i)]; 
+            VmMemoryStat.Space[] spaces = new VmMemoryStat.Space[2 + (1 - i)];
             for (int j = 0; j < 2; j++) {
                 Space space = new Space();
                 space.setName("fluffer" + i + j);
@@ -149,21 +150,27 @@
             generation.setSpaces(spaces);
             generations[i] = generation;
         }
-        
-        long timestamp = 1;
+
+        long timestamp = System.currentTimeMillis();
         String vmID = "vmId";
         for (int i = 0; i < 5; i++) {
-            VmMemoryStat vmMemory = new VmMemoryStat("foo-agent", timestamp++, vmID, generations);
+            timestamps[i] = timestamp;
+            VmMemoryStat vmMemory = new VmMemoryStat("foo-agent", timestamp, vmID, generations);
             vmInfo.add(vmMemory);
+            timestamp++;
         }
-        
+
         memoryStatDao = mock(VmMemoryStatDAO.class);
-        when(memoryStatDao.getLatestVmMemoryStats(any(VmRef.class), anyLong())).thenReturn(vmInfo);
-        
+
+        when(memoryStatDao.getVmMemoryStats(any(VmRef.class), anyLong(), anyLong())).thenReturn(vmInfo);
+
+        when(memoryStatDao.getOldestMemoryStat(any(VmRef.class))).thenReturn(vmInfo.get(0));
+        when(memoryStatDao.getLatestMemoryStat(any(VmRef.class))).thenReturn(vmInfo.get(4));
+
         view = mock(MemoryStatsView.class);
         MemoryStatsViewProvider viewProvider = mock(MemoryStatsViewProvider.class);
         when(viewProvider.createView()).thenReturn(view);
-        
+
         ArgumentCaptor<ActionListener> viewArgumentCaptor =
                 ArgumentCaptor.forClass(ActionListener.class);
         doNothing().when(view).addActionListener(viewArgumentCaptor.capture());
@@ -171,18 +178,18 @@
         ArgumentCaptor<ActionListener> gcArgumentCaptor =
                 ArgumentCaptor.forClass(ActionListener.class);
         doNothing().when(view).addGCActionListener(gcArgumentCaptor.capture());
-        
+
         ref = mock(VmRef.class);
-        
+
         agentDAO = mock(AgentInfoDAO.class);
         gcRequest = mock(GCRequest.class);
-        
+
         controller = new MemoryStatsController(appSvc, infoDao, memoryStatDao, ref, viewProvider, agentDAO, gcRequest);
-        
+
         viewListener = viewArgumentCaptor.getValue();
         gcActionListener = gcArgumentCaptor.getValue();
     }
-    
+
     @Test
     public void testStartStopTimer() {
         viewListener.actionPerformed(new ActionEvent<>(view, MemoryStatsView.Action.VISIBLE));
@@ -207,28 +214,28 @@
         gcActionListener.actionPerformed(new ActionEvent<>(view, GCAction.REQUEST_GC));
         verify(gcRequest).sendGCRequestToAgent(eq(ref), eq(agentDAO), isA(RequestResponseListener.class));
     }
-    
+
     @Test
     public void testPayloadContainSpaces() {
-        MemoryStatsController.VMCollector collettor = controller.getCollector();
-        collettor.run();
-        
+        Runnable collector = controller.getCollector();
+        collector.run();
+
         Map<String, Payload> regions = controller.getRegions();
         assertEquals(5, regions.size());
-        
+
         assertTrue(regions.containsKey("fluffer00"));
         assertTrue(regions.containsKey("fluffer01"));
         assertTrue(regions.containsKey("fluffer10"));
         assertTrue(regions.containsKey("fluffer11"));
-        
+
         assertTrue(regions.containsKey("canary"));
     }
-    
+
     @Test
     public void testValues() {
-        MemoryStatsController.VMCollector collettor = controller.getCollector();
-        collettor.run();
-        
+        Runnable collector = controller.getCollector();
+        collector.run();
+
         Map<String, Payload> regions = controller.getRegions();
 
         Payload payload = regions.get("fluffer00");
@@ -236,77 +243,61 @@
         assertEquals(10000, payload.getMaxCapacity(), 0);
         assertEquals(1000, payload.getCapacity(), 0);
         assertEquals(100, payload.getUsed(), 0);
-        
+
         payload = regions.get("canary");
         assertEquals("canary", payload.getName());
         assertEquals(3, payload.getMaxCapacity(), 0);
         assertEquals(2, payload.getCapacity(), 0);
         assertEquals(1, payload.getUsed(), 0);
-        
+
         // the value above all ensure the same scale is used
         String tooltip = payload.getName() + ": used: " + String.format("%.2f", payload.getUsed()) + " " +
                          payload.getUsedUnit() + ", capacity: " +
                          String.format("%.2f", payload.getCapacity()) + " " + payload.getUsedUnit() +
                          ", max capacity: " + String.format("%.2f", payload.getMaxCapacity()) + " " +
                          payload.getUsedUnit();
-        
+
         assertEquals(tooltip, payload.getTooltip());
     }
-    
+
 
     @Test
-    public void testTimerFetchesMemoryDataDeltaOnly() {
+    public void testTimerFetchesMemoryDataDeltaOnly() throws InterruptedException {
         ArgumentCaptor<Long> timeStampCaptor = ArgumentCaptor.forClass(Long.class);
 
-        final long DATA_TIMESTAMP = System.currentTimeMillis() + 1000000000;
+        Runnable timerAction = controller.getCollector();
+        timerAction.run();
+
         Space space = new Space();
         space.setCapacity(10);
         space.setMaxCapacity(20);
         space.setUsed(5);
         Generation gen = new Generation();
         gen.setName("foobar");
-        gen.setSpaces(new Space[] { space });
+        gen.setSpaces(new Space[]{space });
         VmMemoryStat stat = new VmMemoryStat();
+        final long DATA_TIMESTAMP = System.currentTimeMillis() + 100;
         stat.setTimeStamp(DATA_TIMESTAMP);
         stat.setGenerations(new Generation[] { gen });
 
-        when(memoryStatDao.getLatestVmMemoryStats(isA(VmRef.class), anyLong())).thenReturn(Arrays.asList(stat));
+        when(memoryStatDao.getVmMemoryStats(any(VmRef.class), anyLong(), anyLong())).thenReturn(Arrays.asList(stat));
+        when(memoryStatDao.getLatestMemoryStat(any(VmRef.class))).thenReturn(stat);
 
-        Runnable timerAction = controller.getCollector();
-
-        timerAction.run();
+        Thread.sleep(100l);
         timerAction.run();
 
-        verify(memoryStatDao, times(2)).getLatestVmMemoryStats(isA(VmRef.class), timeStampCaptor.capture());
+        verify(memoryStatDao, times(2)).getVmMemoryStats(isA(VmRef.class), timeStampCaptor.capture(), timeStampCaptor.capture());
 
         long timeStamp1 = timeStampCaptor.getAllValues().get(0);
-        assertTimeStampIsAround(System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(10), timeStamp1);
+        assertTimeStampIsAround(timestamps[0], timeStamp1);
 
-        long timeStamp2 = timeStampCaptor.getAllValues().get(1);
+        long timeStamp2 = timeStampCaptor.getAllValues().get(3);
         assertTimeStampIsAround(DATA_TIMESTAMP, timeStamp2);
     }
 
-    @Test
-    public void testTimerFetchesMemoryDataDeltaOnlyEvenWithNoData() {
-        ArgumentCaptor<Long> timeStampCaptor = ArgumentCaptor.forClass(Long.class);
-
-        Runnable timerAction = controller.getCollector();
-
-        timerAction.run();
-        timerAction.run();
-
-        verify(memoryStatDao, times(2)).getLatestVmMemoryStats(isA(VmRef.class), timeStampCaptor.capture());
-
-        long timeStamp1 = timeStampCaptor.getAllValues().get(0);
-        assertTimeStampIsAround(System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(10), timeStamp1);
-
-        long timeStamp2 = timeStampCaptor.getAllValues().get(1);
-        assertTimeStampIsAround(System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(10), timeStamp2);
-    }
 
     private void assertTimeStampIsAround(long expected, long actual) {
-        assertTrue(actual <= expected + 1000);
-        assertTrue(actual >= expected - 1000);
+        assertTrue(actual <= expected + 500);
+        assertTrue(actual >= expected - 500);
     }
 }
-
--- a/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/VmMemoryStatDAO.java	Thu Dec 04 10:40:57 2014 -0500
+++ b/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/VmMemoryStatDAO.java	Thu Dec 04 14:13:40 2014 -0500
@@ -57,8 +57,12 @@
 
     public VmMemoryStat getLatestMemoryStat(VmRef ref);
 
+    public VmMemoryStat getOldestMemoryStat(VmRef ref);
+
     public List<VmMemoryStat> getLatestVmMemoryStats(VmRef vm, long since);
 
+    public List<VmMemoryStat> getVmMemoryStats(VmRef vm, long since, long to);
+
     public void putVmMemoryStat(VmMemoryStat stat);
 
 }
--- a/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOImpl.java	Thu Dec 04 10:40:57 2014 -0500
+++ b/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOImpl.java	Thu Dec 04 14:13:40 2014 -0500
@@ -50,17 +50,32 @@
 import com.redhat.thermostat.storage.core.Storage;
 import com.redhat.thermostat.storage.core.VmLatestPojoListGetter;
 import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.core.VmTimeIntervalPojoListGetter;
 import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 import com.redhat.thermostat.vm.memory.common.model.VmMemoryStat;
 
 class VmMemoryStatDAOImpl implements VmMemoryStatDAO {
 
     private static final Logger logger = LoggingUtils.getLogger(VmMemoryStatDAOImpl.class);
-    static final String QUERY_LATEST = "QUERY "
-            + vmMemoryStatsCategory.getName() + " WHERE '"
-            + Key.AGENT_ID.getName() + "' = ?s AND '" 
-            + Key.VM_ID.getName() + "' = ?s SORT '" 
-            + Key.TIMESTAMP.getName() + "' DSC LIMIT 1";
+    // QUERY vm-cpu-stats WHERE 'agentId' = ?s AND \
+    //                        'vmId' = ?s \
+    //                        SORT 'timeStamp' ASC  \
+    //                        LIMIT 1
+    static final String DESC_OLDEST_VM_MEMORY_STAT = "QUERY " + vmMemoryStatsCategory.getName() + " " +
+            "WHERE '" + Key.AGENT_ID.getName() + "' = ?s " +
+            "AND '" + Key.VM_ID.getName() + "' = ?s " +
+            "SORT '" + Key.TIMESTAMP.getName() + "' ASC " +
+            "LIMIT 1";
+
+    // QUERY vm-cpu-stats WHERE 'agentId' = ?s AND \
+    //                        'vmId' = ?s \
+    //                        SORT 'timeStamp' DSC  \
+    //                        LIMIT 1
+    static final String DESC_LATEST_VM_MEMORY_STAT = "QUERY " + vmMemoryStatsCategory.getName() + " " +
+            "WHERE '" + Key.AGENT_ID.getName() + "' = ?s " +
+            "AND '" + Key.VM_ID.getName() + "' = ?s " +
+            "SORT '" + Key.TIMESTAMP.getName() + "' DSC " +
+            "LIMIT 1";
     // ADD vm-memory-stats SET 'agentId' = ?s , \
     //                         'vmId' = ?s , \
     //                         'timeStamp' = ?s , \
@@ -73,38 +88,42 @@
     
     private final Storage storage;
     private final VmLatestPojoListGetter<VmMemoryStat> getter;
+    private final VmTimeIntervalPojoListGetter<VmMemoryStat> otherGetter;
 
     VmMemoryStatDAOImpl(Storage storage) {
         this.storage = storage;
         storage.registerCategory(vmMemoryStatsCategory);
         getter = new VmLatestPojoListGetter<>(storage, vmMemoryStatsCategory);
+        otherGetter = new VmTimeIntervalPojoListGetter<>(storage, vmMemoryStatsCategory);
     }
 
     @Override
     public VmMemoryStat getLatestMemoryStat(VmRef ref) {
-        StatementDescriptor<VmMemoryStat> desc = new StatementDescriptor<>(vmMemoryStatsCategory, QUERY_LATEST);
-        PreparedStatement<VmMemoryStat> stmt;
-        Cursor<VmMemoryStat> cursor;
+        return runAgentAndVmIdQuery(ref, DESC_LATEST_VM_MEMORY_STAT);
+    }
+
+    @Override
+    public VmMemoryStat getOldestMemoryStat(VmRef ref) {
+        return runAgentAndVmIdQuery(ref, DESC_OLDEST_VM_MEMORY_STAT);
+    }
+
+    private VmMemoryStat runAgentAndVmIdQuery(VmRef ref, String descriptor) {
+        StatementDescriptor<VmMemoryStat> desc = new StatementDescriptor<>(vmMemoryStatsCategory, descriptor);
+        PreparedStatement<VmMemoryStat> prepared;
         try {
-            stmt = storage.prepareStatement(desc);
-            stmt.setString(0, ref.getHostRef().getAgentId());
-            stmt.setString(1, ref.getVmId());
-            cursor = stmt.executeQuery();
+            prepared = storage.prepareStatement(desc);
+            prepared.setString(0, ref.getHostRef().getAgentId());
+            prepared.setString(1, ref.getVmId());
+            Cursor<VmMemoryStat> cursor = prepared.executeQuery();
+            if (cursor.hasNext()) {
+                return cursor.next();
+            }
         } catch (DescriptorParsingException e) {
-            // should not happen, but if it *does* happen, at least log it
-            logger.log(Level.SEVERE, "Preparing query '" + desc + "' failed!", e);
-            return null;
+            logger.log(Level.SEVERE, "Preparing stmt '" + desc + "' failed!", e);
         } catch (StatementExecutionException e) {
-            // should not happen, but if it *does* happen, at least log it
-            logger.log(Level.SEVERE, "Executing query '" + desc + "' failed!", e);
-            return null;
+            logger.log(Level.SEVERE, "Executing stmt '" + desc + "' failed!", e);
         }
-        
-        VmMemoryStat result = null;
-        if (cursor.hasNext()) {
-            result = cursor.next();
-        }
-        return result;
+        return null;
     }
 
     @Override
@@ -129,5 +148,10 @@
     public List<VmMemoryStat> getLatestVmMemoryStats(VmRef ref, long since) {
         return getter.getLatest(ref, since);
     }
+
+    @Override
+    public List<VmMemoryStat> getVmMemoryStats(VmRef ref, long since, long to) {
+        return otherGetter.getLatest(ref, since, to);
+    }
 }
 
--- a/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOImplStatementDescriptorRegistration.java	Thu Dec 04 10:40:57 2014 -0500
+++ b/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOImplStatementDescriptorRegistration.java	Thu Dec 04 14:13:40 2014 -0500
@@ -41,6 +41,7 @@
 
 import com.redhat.thermostat.storage.core.PreparedParameter;
 import com.redhat.thermostat.storage.core.VmLatestPojoListGetter;
+import com.redhat.thermostat.storage.core.VmTimeIntervalPojoListGetter;
 import com.redhat.thermostat.storage.core.auth.DescriptorMetadata;
 import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration;
 import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
@@ -53,19 +54,21 @@
 public class VmMemoryStatDAOImplStatementDescriptorRegistration implements
         StatementDescriptorRegistration {
     
-    private final Set<String> descs;
+    static final String latestDescriptor = String.format(VmLatestPojoListGetter.VM_LATEST_QUERY_FORMAT,
+            VmMemoryStatDAO.vmMemoryStatsCategory.getName());
+    static final String rangeDescriptor = String.format(VmTimeIntervalPojoListGetter.VM_INTERVAL_QUERY_FORMAT,
+            VmMemoryStatDAO.vmMemoryStatsCategory.getName());
     
-    public VmMemoryStatDAOImplStatementDescriptorRegistration() {
-        descs = new HashSet<>(2);
-        String descriptor = String.format(VmLatestPojoListGetter.VM_LATEST_QUERY_FORMAT, 
-                VmMemoryStatDAO.vmMemoryStatsCategory.getName());
-        descs.add(descriptor);
-        descs.add(VmMemoryStatDAOImpl.QUERY_LATEST);
-        descs.add(VmMemoryStatDAOImpl.DESC_ADD_VM_MEMORY_STAT);
-    }
-
     @Override
     public Set<String> getStatementDescriptors() {
+        Set<String> descs = new HashSet<>(5);
+        descs.add(VmMemoryStatDAOImpl.DESC_LATEST_VM_MEMORY_STAT);
+        descs.add(VmMemoryStatDAOImpl.DESC_OLDEST_VM_MEMORY_STAT);
+        descs.add(VmMemoryStatDAOImpl.DESC_ADD_VM_MEMORY_STAT);
+
+        descs.add(latestDescriptor);
+        descs.add(rangeDescriptor);
+
         return descs;
     }
 
--- a/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOImplStatementDescriptorRegistrationTest.java	Thu Dec 04 10:40:57 2014 -0500
+++ b/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOImplStatementDescriptorRegistrationTest.java	Thu Dec 04 14:13:40 2014 -0500
@@ -66,7 +66,7 @@
     public void registersAllDescriptors() {
         VmMemoryStatDAOImplStatementDescriptorRegistration reg = new VmMemoryStatDAOImplStatementDescriptorRegistration();
         Set<String> descriptors = reg.getStatementDescriptors();
-        assertEquals(3, descriptors.size());
+        assertEquals(5, descriptors.size());
         assertFalse("null descriptor not allowed", descriptors.contains(null));
     }
     
@@ -94,7 +94,7 @@
         // storage-core + this module
         assertEquals(2, registrations.size());
         assertNotNull(vmMemoryDaoReg);
-        assertEquals(3, vmMemoryDaoReg.getStatementDescriptors().size());
+        assertEquals(5, vmMemoryDaoReg.getStatementDescriptors().size());
     }
 
 }
--- a/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOTest.java	Thu Dec 04 10:40:57 2014 -0500
+++ b/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOTest.java	Thu Dec 04 14:13:40 2014 -0500
@@ -115,7 +115,7 @@
     @Test
     public void preparedQueryDescriptorsAreSane() {
         String expectedQueryThreadCaps = "QUERY vm-memory-stats WHERE 'agentId' = ?s AND 'vmId' = ?s SORT 'timeStamp' DSC LIMIT 1";
-        assertEquals(expectedQueryThreadCaps, VmMemoryStatDAOImpl.QUERY_LATEST);
+        assertEquals(expectedQueryThreadCaps, VmMemoryStatDAOImpl.DESC_LATEST_VM_MEMORY_STAT);
         String addVmMemoryStat = "ADD vm-memory-stats SET 'agentId' = ?s , " +
                                         "'vmId' = ?s , " +
                                         "'timeStamp' = ?l , " +