/* * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004-2008], Hyperic, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.tools.ant.dbupgrade; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.tools.ant.BuildException; import org.hyperic.hq.measurement.MeasurementConstants; import org.hyperic.hq.measurement.shared.MeasRange; import org.hyperic.hq.measurement.shared.MeasRangeObj; import org.hyperic.util.jdbc.DBUtil; public class SST_AvailRLEUpgrader extends SchemaSpecTask { public static final String logCtx = SST_AvailRLEUpgrader.class.getName(); public static final String SCHEMA_MOD_IN_PROGRESS = " *** UPGRADE TASK: migrating availability data to" + " Run Length Encoded Availability"; private static final List dataTables = new ArrayList(); private static final String TAB_MEAS = MeasurementConstants.TAB_MEAS; private static final String AVAILABILITY = MeasurementConstants.CAT_AVAILABILITY.toUpperCase(); private static final String TAB_MEAS_TEMPL = "EAM_MEASUREMENT_TEMPL"; private static final String TAB_AVAIL_RLE = "HQ_AVAIL_DATA_RLE"; private static final long ONE_HR = 1000*3600; private static final long SIX_HRS = 6*ONE_HR; private static final long ONE_DAY = 24*ONE_HR; private static final long MAX_TIMESTAMP = Long.MAX_VALUE; private static final long BATCH_SIZE = 1000; private static final String TAB_DATA_1H = MeasurementConstants.TAB_DATA_1H; private static final String TAB_DATA_6H = MeasurementConstants.TAB_DATA_6H; private static final String TAB_DATA_1D = MeasurementConstants.TAB_DATA_1D; public SST_AvailRLEUpgrader () {} private void initDataTables(Connection conn) throws SQLException { // these must be ordered from the latest table to the oldest List ranges = MeasRangeObj.getInstance().getRanges(); for (Iterator i=ranges.iterator(); i.hasNext(); ) { MeasRange range = (MeasRange)i.next(); dataTables.add(new TableObj(range.getTable(), ONE_HR, getMinTimestamp(range.getTable(), conn), getMaxTimestamp(range.getTable(), conn))); } // need to sort in descending order Collections.sort(dataTables); dataTables.add(new TableObj(TAB_DATA_1H, ONE_HR*10, getMinTimestamp(TAB_DATA_1H, conn), getMaxTimestamp(TAB_DATA_1H, conn))); dataTables.add(new TableObj(TAB_DATA_6H, SIX_HRS*10, getMinTimestamp(TAB_DATA_6H, conn), getMaxTimestamp(TAB_DATA_6H, conn))); dataTables.add(new TableObj(TAB_DATA_1D, ONE_DAY*10, getMinTimestamp(TAB_DATA_1D, conn), getMaxTimestamp(TAB_DATA_1D, conn))); } public void execute() throws BuildException { Map avails = new HashMap(); try { Connection conn = getConnection(); initDataTables(conn); log(SCHEMA_MOD_IN_PROGRESS); for (Iterator i=dataTables.iterator(); i.hasNext(); ) { TableObj table = (TableObj)i.next(); log("Migrating Table: "+table.getTable() + " min: "+table.getMinTimestamp() + " max: "+table.getMaxTimestamp()); setAvailData(avails, table, conn); } insertAvailData(avails, conn); } catch (SQLException e) { throw new BuildException(logCtx+": " + e.getMessage(), e); } } private void insertAvailData(Map avails, Connection conn) throws SQLException { String sql = "INSERT INTO " + TAB_AVAIL_RLE + " (measurement_id, " + "startime, endtime, availval) VALUES (?, ?, ?, ?)"; PreparedStatement pstmt = conn.prepareStatement(sql); List debugList = new ArrayList((int)BATCH_SIZE); try { for (Iterator i=avails.entrySet().iterator(); i.hasNext(); ) { Map.Entry entry = (Map.Entry)i.next(); int mid = ((Integer)entry.getKey()).intValue(); List list = (List)entry.getValue(); int ii=0; for (Iterator it=list.iterator(); it.hasNext(); ii++) { if (0 == (ii % BATCH_SIZE) && ii != 0) { int[] res = pstmt.executeBatch(); checkResult(res, debugList); debugList.clear(); pstmt.clearBatch(); } AvailData data = (AvailData)it.next(); //log("\tmid," + mid + // ":startime," + data.getStartTime() + // ":endtime," + data.getEndTime() + // ":availVal," + data.getAvailVal()); pstmt.clearParameters(); pstmt.setInt(1, mid); pstmt.setLong(2, data.getStartTime()); pstmt.setLong(3, data.getEndTime()); pstmt.setDouble(4, data.getAvailVal()); debugList.add(data); pstmt.addBatch(); } } int[] res = pstmt.executeBatch(); checkResult(res, debugList); } finally { DBUtil.closeStatement(logCtx, pstmt); } } private void checkResult(int[] res, List pts) { Iterator it = pts.iterator(); for (int i=0; i<res.length; i++) { AvailData pt = (AvailData)it.next(); if (res[i] == Statement.EXECUTE_FAILED) { log("ERROR inserting datapoint -> " + pt); } } } private long getMaxTimestamp(String table, Connection conn) throws SQLException { String sql = "SELECT max(timestamp) from " + table; ResultSet rs = null; Statement stmt = null; try { stmt = conn.createStatement(); rs = stmt.executeQuery(sql); if (rs.next()) { return rs.getLong(1); } return -1; } finally { DBUtil.closeJDBCObjects(logCtx, null, stmt, rs); } } private long getMinTimestamp(String table, Connection conn) throws SQLException { String sql = "SELECT min(timestamp) from " + table; ResultSet rs = null; Statement stmt = null; try { stmt = conn.createStatement(); rs = stmt.executeQuery(sql); if (rs.next()) { return rs.getLong(1); } return -1; } finally { DBUtil.closeJDBCObjects(logCtx, null, stmt, rs); } } private void setAvailData(Map avails, TableObj table, Connection conn) throws SQLException { long min = table.getMinTimestamp(); long max = table.getMaxTimestamp(); String sql = "SELECT timestamp, value, measurement_id" + " FROM " + table.getTable() + " d, " + TAB_MEAS + " m, " + TAB_MEAS_TEMPL + " t" + " WHERE d.timestamp between ? and ?" + " AND d.measurement_id = m.id" + " AND t.id = m.template_id AND upper(t.alias) = '" + AVAILABILITY + "'" + " ORDER BY d.timestamp desc, d.measurement_id"; PreparedStatement pstmt = conn.prepareStatement(sql); ResultSet rs = null; try { long interval = table.getInterval(); for (long i=max; i>min; i-=interval) { pstmt.setLong(1, (i-interval)); pstmt.setLong(2, i); rs = pstmt.executeQuery(); int timestamp_col = rs.findColumn("timestamp"); int value_col = rs.findColumn("value"); int measId_col = rs.findColumn("measurement_id"); while (rs.next()) { long timestamp = rs.getLong(timestamp_col); double value = rs.getDouble(value_col); int measId = rs.getInt(measId_col); setAvail(avails, timestamp, value, measId); } DBUtil.closeResultSet(logCtx, rs); } } finally { DBUtil.closeJDBCObjects(logCtx, null, pstmt, rs); } } private void setAvail(Map avails, long timestamp, double value, int id) { List list; Integer mid = new Integer(id); if (null == (list = (List)avails.get(mid))) { AvailData data = new AvailData(timestamp, MAX_TIMESTAMP, id, value); list = new ArrayList(); list.add(data); avails.put(mid, list); } else { AvailData last = (AvailData)list.get(list.size()-1); // the timestamps in the rollup tables may overlap the more recent // tables due to delays in deletion after compression. If we see // overlapping just ignore the datapoint if (timestamp >= last.getStartTime()) { return; } if (value != last.getAvailVal()) { AvailData data = new AvailData(timestamp, last.getStartTime(), id, value); list.add(data); } else { last.setStartTime(timestamp); } } } private class TableObj implements Comparable { private String _table; private long _interval; private long _minTime; private long _maxTime; public TableObj(String table, long interval, long minTime, long maxTime) { _minTime = minTime; _maxTime = maxTime; _table = table; _interval = interval; } public String getTable() { return _table; } public long getInterval() { return _interval; } public long getMinTimestamp() { return _minTime; } public long getMaxTimestamp() { return _maxTime; } public int compareTo(Object rhs) throws ClassCastException { return compareTo((TableObj)rhs); } public int compareTo(TableObj rhs) throws ClassCastException { Long min = new Long(_minTime); Long rmin = new Long(rhs._minTime); // want to sort in descending order return rmin.compareTo(min); } } private class AvailData { long _startTime; long _endtime; int _mid; double _availval; public AvailData(long starttime, long endtime, int metric_id, double availval) { super(); _startTime = starttime; _endtime = endtime; _mid = metric_id; _availval = availval; } public long getStartTime() { return _startTime; } public void setStartTime(long startTime) { _startTime = startTime; } public long getEndTime() { return _endtime; } public int getMetric() { return _mid; } public double getAvailVal() { return _availval; } public String toString() { return "mid:"+_mid+";startime:"+_startTime+";endtime:"+_endtime+";availval:"+_availval; } } }