Commit ab86ea2f ab86ea2f66fa71520c736d129d3b74a6ecf092b0 by Christian Gerdes

First version with support for the "Oracle Bug"

1 parent a578de84
......@@ -66,3 +66,13 @@ inte i alla metoder i OraMon.java klassen). Anger man en PDB i OraMon.jsp så bl
Lyckades köra JavaMonWeb i Debug läge med Tomcat 7 local server. Skapade en sanityCheck() metod med break points så jag kan debugga när vissa räknare blir negativa.
Dock svårt att hitta rätt LongDelta objekt, eftersom det finns över 1000 i listan i random ordning. Behöver spara undan det nånstans eller breaka inne i en LongDelta metod istället.
Checkar in detta i alla fall så länge.
2019-01-23
Oracle support var ingen väg framåt. Planen är att bygga en work around.
Men det blir inte så lätt. Koden just nu har "underrun protection" påslaget default, men det fungerar inte eftersom flera räknare "inte" är cumulativa utan ska kunna ha minskande värden.
Därmed blir det svårt att skydda sig mot detta under en update.
Istället måste detta detekteras när man läser av ett delta i någon av delta metoderna, varvid det dock är för sent.
Innan trodde jag att problemet alltid inleds med ett negativt delta, sedan en spik, sedan normalt. Men så är inte fallet, jag har sett idag tvärtom, det inleds med en spik och sedan
en rättning (vilket känns konstigt) men så blev det på tex file io wait time. Så jag tror tyvärr att enda rätta lösningen på problemet är att spara minst 3 mätningar innan ett delta rapporteras,
samt att man då kan upptäcka ett negativt delta, och i så fall ignorera värdet innan.
......
package se.lil.om;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
class Collector {
ArrayList<LongDelta> list = new ArrayList<LongDelta>();
String name = "";
Boolean debugLog = false;
public Collector(String name) {
this.name = name;
}
public void update(ResultSet rset) throws Throwable {
String timeStamp = null;
BufferedWriter writer = null;
if(debugLog) {
timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(Calendar.getInstance().getTime());
writer = new BufferedWriter(new FileWriter("Collector_" + name + "_" + timeStamp + ".txt"));
}
while(rset.next()) {
// Add try catch for issue #5
Timestamp ts = rset.getTimestamp(1);
......@@ -21,27 +38,29 @@ class Collector {
value = BigDecimal.ZERO;
}
updateValue(ts, name, value);
if(writer != null) {
writer.write(ts + ":" + name + ":" + value);
writer.newLine();
}
}
if(writer != null) {
writer.close();
}
rset.close();
}
public void updateValue(Timestamp ts, String name, BigDecimal value) {
public void updateValue(Timestamp timestamp, String name, BigDecimal value) {
LongDelta myDelta = null;
for(int x = 0; x<list.size() && myDelta == null; x++) {
if(name.equals(list.get(x).name)) {
if(list.get(x).matches(name)) {
myDelta = list.get(x);
}
}
if(myDelta != null) {
myDelta.lastFetch = myDelta.curFetch;
myDelta.curFetch = ts;
myDelta.lastValue = myDelta.curValue;
myDelta.curValue = value;
myDelta.update(value, timestamp);
} else {
myDelta = new LongDelta();
myDelta.curFetch = ts;
myDelta.curValue = value;
myDelta.name = name;
myDelta = new LongDelta(name);
myDelta.update(value, timestamp);
list.add(myDelta);
}
}
......@@ -49,7 +68,7 @@ class Collector {
public long getCurrentValue(String name) throws Throwable {
LongDelta myDelta = null;
for(int x = 0; x<list.size() && myDelta == null; x++) {
if(name.equals(list.get(x).name)) {
if(list.get(x).matches(name)) {
myDelta = list.get(x);
}
}
......@@ -59,23 +78,23 @@ class Collector {
return 0;
}
public long getDelta(String name) throws Throwable {
LongDelta myDelta = null;
for(int x = 0; x<list.size() && myDelta == null; x++) {
if(name.equals(list.get(x).name)) {
myDelta = list.get(x);
}
}
if(myDelta != null)
return myDelta.getDelta();
else
return 0;
}
// public long getDelta(String name) throws Throwable {
// LongDelta myDelta = null;
// for(int x = 0; x<list.size() && myDelta == null; x++) {
// if(list.get(x).matches(name)) {
// myDelta = list.get(x);
// }
// }
// if(myDelta != null)
// return myDelta.getDelta();
// else
// return 0;
// }
public double getPerSecValue(String name) throws Throwable {
LongDelta myDelta = null;
for(int x = 0; x<list.size() && myDelta == null; x++) {
if(name.equals(list.get(x).name)) {
if(list.get(x).matches(name)) {
myDelta = list.get(x);
}
}
......@@ -88,7 +107,7 @@ class Collector {
public double getSeconds(String name) throws Throwable {
LongDelta myDelta = null;
for(int x = 0; x<list.size() && myDelta == null; x++) {
if(name.equals(list.get(x).name)) {
if(list.get(x).matches(name)) {
myDelta = list.get(x);
}
}
......
......@@ -5,17 +5,25 @@ import java.sql.ResultSet;
import java.sql.Timestamp;
class LongDelta {
public String name = null;
public BigDecimal curValue = null;
public BigDecimal lastValue = null;
public Timestamp lastFetch = null;
public Timestamp curFetch = null;
boolean urProt = false; // Under-run protection
boolean lastUpdCausedUR = false;
public static BigDecimal bd1000 = new BigDecimal(1000);
public LongDelta() {};
public LongDelta(boolean UnderrunProtection) { this.urProt = UnderrunProtection; }
private String name = null;
private BigDecimal curValue = null;
private BigDecimal lastValue = null;
private BigDecimal last2Value = null;
private Timestamp curFetch = null;
private Timestamp lastFetch = null;
private Timestamp last2Fetch = null;
private static BigDecimal bd1000 = new BigDecimal(1000);
private double numMilliSeconds;
private double delta;
private boolean haveGoodDelta = false;
public LongDelta(String name) {
this.name = name;
}
public void update(ResultSet rset) throws Throwable{
update(rset, 2, false, true);
......@@ -32,63 +40,80 @@ class LongDelta {
if(close) rset.close();
}
public boolean matches(String name) {
if(this.name.equals(name)) return true;
else return false;
}
public void update(BigDecimal value, Timestamp timestamp) {
update(value, timestamp, false);
}
public void update(BigDecimal value, Timestamp timestamp, boolean convert) {
if(convert) value = value.divide(bd1000, BigDecimal.ROUND_HALF_UP);
if(urProt) {
if(this.curValue != null && this.curValue.compareTo(value) > 0) {
//This would be a negative delta. Ignore this data
//System.out.println("Underrunprotection on " + name + " New Val: " + newval + " Old Val: " + this.curValue);
lastUpdCausedUR = true;
return;
}
}
this.last2Fetch = this.lastFetch;
this.lastFetch = this.curFetch;
this.curFetch = timestamp;
this.last2Value = this.lastValue;
this.lastValue = this.curValue;
this.curValue = value;
lastUpdCausedUR = false;
this.haveGoodDelta = false;
}
public boolean didLastUnderRun() {
return lastUpdCausedUR;
private void updateDelta() {
if(this.curValue != null && this.lastValue != null && this.last2Value != null
&& this.lastFetch != null && this.last2Fetch != null && this.curFetch != null) {
// We have values, calculate the number of gets per second
// Handle the Oracle Bug (ref needed)
if(curValue.compareTo(lastValue) >= 0 && lastValue.compareTo(last2Value) >= 0) {
// All seems fine, no negative deltas, return the delta for last and last2
numMilliSeconds = this.lastFetch.getTime() - this.last2Fetch.getTime();
delta = this.lastValue.subtract(this.last2Value).doubleValue();
} else {
// negative delta between current and last, or last and last2, return current and last2 instead
numMilliSeconds = this.curFetch.getTime() - this.last2Fetch.getTime();
delta = this.curValue.subtract(this.last2Value).doubleValue();
}
haveGoodDelta = true;
}
}
public double getPerSecondValue() throws Throwable {
if(this.curValue != null && this.lastValue != null && this.lastFetch != null && this.curFetch != null) {
// We have values, calculate the number of gets per second
double numMilliSeconds = this.curFetch.getTime() - this.lastFetch.getTime();
double delta = this.curValue.subtract(this.lastValue).doubleValue();
if(haveGoodDelta == false) updateDelta();
if(haveGoodDelta) {
double perMilli = delta/numMilliSeconds;
return perMilli * 1000;
} else {
return 0;
}
}
public long getCurrentValue() throws Throwable {
if(this.curValue != null) return this.curValue.longValue();
else return 0;
}
public double getSeconds() throws Throwable {
if(this.lastFetch != null && this.curFetch != null) {
double numSeconds = this.curFetch.getTime() - this.lastFetch.getTime();
return numSeconds/1000;
}
return 0;
}
public long getMilliSeconds() throws Throwable {
if(this.lastFetch != null && this.curFetch != null) {
return this.curFetch.getTime() - this.lastFetch.getTime();
}
if(haveGoodDelta == false) updateDelta();
if(haveGoodDelta) {
return numMilliSeconds/1000;
} else {
return 0;
}
public long getDelta() throws Throwable {
if(this.lastValue != null && this.curValue != null) {
return this.curValue.subtract(this.lastValue).longValue();
}
return 0;
}
// public long getMilliSeconds() throws Throwable {
// if(this.lastFetch != null && this.curFetch != null) {
// return this.curFetch.getTime() - this.lastFetch.getTime();
// }
// return 0;
// }
// public long getDelta() throws Throwable {
// if(this.lastValue != null && this.curValue != null) {
// return this.curValue.subtract(this.lastValue).longValue();
// }
// return 0;
// }
}
\ No newline at end of file
......
......@@ -5,12 +5,12 @@ import java.util.HashSet;
import java.util.Properties;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
//import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class OraMon {
private final static Logger LOGGER = Logger.getLogger(OraMon.class.getName());
//private final static Logger LOGGER = Logger.getLogger(OraMon.class.getName());
Connection conn = null;
String conString = "jdbc:oracle:thin:@//hostname:1521/SID";
String conUser = "system";
......@@ -21,8 +21,8 @@ public class OraMon {
public static final int SYSSTAT = 1;
public static final int OSSTAT = 2;
Collector colSysStat = new Collector();
Collector colOsStat = new Collector();
Collector colSysStat = new Collector("colSysStat");
Collector colOsStat = new Collector("colOsStat");
int getDataCalls = 0;
int getDataSucess = 0;
......@@ -95,8 +95,8 @@ public class OraMon {
}
//DB CPU
LongDelta cpuTime = new LongDelta(true);
LongDelta niwTime = new LongDelta(true);
LongDelta cpuTime = new LongDelta(this.getFriendlyName()+" cpuTime");
LongDelta niwTime = new LongDelta(this.getFriendlyName()+" niwTime");
public double getCPUPercent(boolean excludeNonIdleWaitTime, String pdbName) throws Throwable {
double cpuPerSec;
if(pdbName == null) cpuPerSec = cpuTime.getPerSecondValue();
......@@ -146,15 +146,15 @@ public class OraMon {
return getCPURawValue();
}
public long getCPURawDiff() throws Throwable {
return cpuTime.getDelta();
}
public long getCPURawDiff(String pdbName) throws Throwable {
if(pdbName != null)
return colPdbCPU.getDelta(pdbName + strCpuTime);
else
return getCPURawDiff();
}
// public long getCPURawDiff() throws Throwable {
// return cpuTime.getDelta();
// }
// public long getCPURawDiff(String pdbName) throws Throwable {
// if(pdbName != null)
// return colPdbCPU.getDelta(pdbName + strCpuTime);
// else
// return getCPURawDiff();
// }
public double getCPUTimePerSecond() throws Throwable {
return cpuTime.getPerSecondValue();
......@@ -408,7 +408,7 @@ public class OraMon {
//Get PDB specific data if getPDB is set to true
if(getPdbs) {
//CPU
if(colPdbCPU == null) colPdbCPU = new Collector();
if(colPdbCPU == null) colPdbCPU = new Collector("colPdbCPU");
rset = stmt.executeQuery("select systimestamp, c.NAME, s.value as NIWT, t.value as DBCPU from V$CON_SYSSTAT s, V$CON_SYS_TIME_MODEL t, V$CONTAINERS c where s.CON_ID=t.CON_ID and s.CON_ID=c.CON_ID and s.name='non-idle wait time' and t.STAT_NAME='DB CPU'");
while(rset.next()) {
Timestamp ts = rset.getTimestamp(1);
......@@ -421,14 +421,12 @@ public class OraMon {
rset.close();
//SYSSTATS
if(colPdbSysStat == null) colPdbSysStat = new Collector();
if(colPdbSysStat == null) colPdbSysStat = new Collector("colPdbSysStat");
colPdbSysStat.update(stmt.executeQuery("select systimestamp, c.NAME || '" + strSeparator + "' || s.name, s.value from V$CON_SYSSTAT s, V$CONTAINERS c where s.CON_ID=c.CON_ID"));
}
stmt.close();
sanityCheck(pdbName);
getDataSucess++;
lastFetchTSns = System.nanoTime();
lastRTns = lastFetchTSns - startTSns;
......@@ -452,30 +450,6 @@ public class OraMon {
// End thread safe
}
public void sanityCheck(String pdbName) throws Throwable {
String prefix = getFriendlyName(pdbName);
if(getBufferCacheHitRatioPercent(pdbName) < 0)
LOGGER.warning(prefix + " getBufferCacheHitRatioPercent" + " has negative delta");
if(getLogicalReadsPerSecond(pdbName) < 0)
LOGGER.warning(prefix + " getLogicalReadsPerSecond" + " has negative delta");
if(getCPUTimePerSecond(pdbName) < 0)
LOGGER.warning(prefix + " getCPUTimePerSecond" + " has negative delta");
if(getCacheHitRatioPercent(pdbName) < 0)
LOGGER.warning(prefix + " getCacheHitRatioPercent" + " has negative delta");
if(getCPUTimePerSecond(pdbName) < 0)
LOGGER.warning(prefix + " getCPUTimePerSecond" + " has negative delta");
if(getPerSecondValue("execute count",pdbName) < 0)
LOGGER.warning(prefix + " getPerSecondValue(execute count)" + " has negative delta");
if(getPerSecondValue("consistent gets",pdbName) < 0)
LOGGER.warning(prefix + " getPerSecondValue(consistent gets)" + " has negative delta");
if(getPerSecondValue("physical reads",pdbName) < 0)
LOGGER.warning(prefix + " getPerSecondValue(physical reads)" + " has negative delta");
if(getPerSecondValue("non-idle wait time",pdbName) < 0)
LOGGER.warning(prefix + " getPerSecondValue(non-idle wait time)" + " has negative delta");
if(getPerSecondValue("user commits",pdbName) < 0)
LOGGER.warning(prefix + " getPerSecondValue(user commits)" + " has negative delta");
}
public Throwable getLastEx() {
return lastEx;
}
......