Skip to content

Commit

Permalink
Keep backend running when exceptions occur (#143)
Browse files Browse the repository at this point in the history
  • Loading branch information
tracyhenry authored Mar 5, 2020
1 parent a13bc86 commit dbf8068
Show file tree
Hide file tree
Showing 12 changed files with 392 additions and 299 deletions.
37 changes: 26 additions & 11 deletions back-end/src/main/java/index/AutoDDInMemoryIndexer.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;
import main.Config;
import main.DbConnector;
import main.Main;
Expand Down Expand Up @@ -105,8 +107,8 @@ public int compare(RTreeData o1, RTreeData o2) {
int zColId = autoDD.getColumnNames().indexOf(zCol);
String zOrder = autoDD.getzOrder();

float v1 = Float.valueOf(rawRows.get(o1.rowId).get(zColId));
float v2 = Float.valueOf(rawRows.get(o2.rowId).get(zColId));
float v1 = parseFloat(rawRows.get(o1.rowId).get(zColId));
float v2 = parseFloat(rawRows.get(o2.rowId).get(zColId));
if (v1 == v2) return 0;
if (zOrder.equals("asc")) return v1 < v2 ? -1 : 1;
else return v1 < v2 ? 1 : -1;
Expand Down Expand Up @@ -188,6 +190,9 @@ private void setCommonVariables() throws SQLException, ClassNotFoundException {

// store raw query results into memory
rawRows = DbConnector.getQueryResult(autoDD.getDb(), autoDD.getQuery());
for (int i = 0; i < rawRows.size(); i++)
for (int j = 0; j < numRawColumns; j++)
if (rawRows.get(i).get(j) == null) rawRows.get(i).set(j, "");

// add row number as a BGRP
Main.getProject().addBGRP("roughN", String.valueOf(rawRows.size()));
Expand Down Expand Up @@ -233,12 +238,12 @@ private void computeClusterAggs() throws SQLException, ClassNotFoundException {
double cx =
autoDD.getCanvasCoordinate(
i - 1,
Double.valueOf(rawRows.get(rd.rowId).get(autoDD.getXColId())),
parseFloat(rawRows.get(rd.rowId).get(autoDD.getXColId())),
true);
double cy =
autoDD.getCanvasCoordinate(
i - 1,
Double.valueOf(rawRows.get(rd.rowId).get(autoDD.getYColId())),
parseFloat(rawRows.get(rd.rowId).get(autoDD.getYColId())),
false);
double minx = cx - autoDD.getBboxW() * overlappingThreshold / 2;
double miny = cy - autoDD.getBboxH() * overlappingThreshold / 2;
Expand All @@ -255,13 +260,13 @@ private void computeClusterAggs() throws SQLException, ClassNotFoundException {
double curCx =
autoDD.getCanvasCoordinate(
i - 1,
Double.valueOf(
parseFloat(
rawRows.get(neighborRd.rowId).get(autoDD.getXColId())),
true);
double curCy =
autoDD.getCanvasCoordinate(
i - 1,
Double.valueOf(
parseFloat(
rawRows.get(neighborRd.rowId).get(autoDD.getYColId())),
false);
double curDistance =
Expand Down Expand Up @@ -452,10 +457,11 @@ private void setInitialClusterAgg(RTreeData rd) {
// convexHull
double cx =
autoDD.getCanvasCoordinate(
autoDD.getNumLevels(), Double.valueOf(row.get(autoDD.getXColId())), true);
autoDD.getNumLevels(), parseFloat(row.get(autoDD.getXColId())), true);
double cy =
autoDD.getCanvasCoordinate(
autoDD.getNumLevels(), Double.valueOf(row.get(autoDD.getYColId())), false);
autoDD.getNumLevels(), parseFloat(row.get(autoDD.getYColId())), false);

float minx = (float) (cx - autoDD.getBboxW() / 2.0),
maxx = (float) (cx + autoDD.getBboxW() / 2.0);
float miny = (float) (cy - autoDD.getBboxH() / 2.0),
Expand Down Expand Up @@ -491,7 +497,7 @@ private void setInitialClusterAgg(RTreeData rd) {
float curValue =
(curMeasureFunction.equals("count")
? 1.0f
: Float.valueOf(
: parseFloat(
row.get(autoDD.getColumnNames().indexOf(curMeasureField))));

if (curMeasureFunction.equals("sqrsum"))
Expand Down Expand Up @@ -537,8 +543,8 @@ else if (childIter >= childTopk.length)
else {
int parentHead = parentTopk[parentIter];
int childHead = childTopk[childIter];
double parentValue = Double.valueOf(rawRows.get(parentHead).get(zColId));
double childValue = Double.valueOf(rawRows.get(childHead).get(zColId));
double parentValue = parseFloat(rawRows.get(parentHead).get(zColId));
double childValue = parseFloat(rawRows.get(childHead).get(zColId));
if ((parentValue <= childValue && zOrder.equals("asc"))
|| (parentValue >= childValue && zOrder.equals("desc"))) {
mergedTopk[mergedIter++] = parentHead;
Expand Down Expand Up @@ -622,4 +628,13 @@ private float[][] getCoordsListOfGeometry(Geometry geometry) {
}
return coordsList;
}

private float parseFloat(String s) {
NumberFormat nf = NumberFormat.getInstance(Locale.US);
try {
return nf.parse(s).floatValue();
} catch (Exception e) {
return 0;
}
}
}
7 changes: 7 additions & 0 deletions back-end/src/main/java/main/DbConnector.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,11 @@ public static void closeConnection(String dbName) throws SQLException {
connections.remove(dbName + "_batch");
}
}

public static void closeAllConnections() throws SQLException {
for (String connName : connections.keySet()) {
connections.get(connName).close();
}
connections.clear();
}
}
3 changes: 2 additions & 1 deletion back-end/src/main/java/main/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ public static Project getProject() {

public static void setProject(Project newProject) {

System.out.println("Current project set to: " + newProject.getName());
System.out.println(
"Current project set to: " + (newProject != null ? newProject.getName() : "null"));
project = newProject;

// clear cache whenever there is a project switch
Expand Down
15 changes: 12 additions & 3 deletions back-end/src/main/java/project/AutoDD.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class AutoDD {
private int numLevels, topLevelWidth, topLevelHeight;
private double overlap;
private double zoomFactor;
private int xColId = -1, yColId = -1;
private int xColId = -1, yColId = -1, zColId = -1;
private double loX = Double.NaN, loY, hiX, hiY;
private String mergeClusterAggs,
getCitusSpatialHashKeyBody,
Expand Down Expand Up @@ -78,7 +78,7 @@ public int getXColId() {

if (xColId < 0) {
ArrayList<String> colNames = getColumnNames();
for (int i = 0; i < colNames.size(); i++) if (colNames.get(i).equals(xCol)) xColId = i;
xColId = colNames.indexOf(xCol);
}
return xColId;
}
Expand All @@ -87,11 +87,20 @@ public int getYColId() {

if (yColId < 0) {
ArrayList<String> colNames = getColumnNames();
for (int i = 0; i < colNames.size(); i++) if (colNames.get(i).equals(yCol)) yColId = i;
yColId = colNames.indexOf(yCol);
}
return yColId;
}

public int getZColId() {

if (zColId < 0) {
ArrayList<String> colNames = getColumnNames();
zColId = colNames.indexOf(zCol);
}
return zColId;
}

public ArrayList<String> getColumnNames() {

// if it is specified already, return
Expand Down
171 changes: 87 additions & 84 deletions back-end/src/main/java/server/BoxRequestHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,96 +34,99 @@ public void handle(HttpExchange httpExchange) throws IOException {
// TODO: this method should be thread safe, allowing concurrent requests
System.out.println("\nServing /dynamic Box");

// get data of the current request
// variable definitions
String response;
String canvasId, viewId;
double minx, miny;
BoxandData data = null;

// check if this is a POST request
if (!httpExchange.getRequestMethod().equalsIgnoreCase("GET")) {
Server.sendResponse(httpExchange, HttpsURLConnection.HTTP_BAD_METHOD, "");
return;
}

// get data of the current request
String query = httpExchange.getRequestURI().getQuery();
Map<String, String> queryMap = Server.queryToMap(query);
// print
for (String s : queryMap.keySet()) System.out.println(s + " : " + queryMap.get(s));

// check parameters, if not pass, send a bad request response
response = checkParameters(queryMap);
if (response.length() > 0) {
Server.sendResponse(httpExchange, HttpsURLConnection.HTTP_BAD_REQUEST, response);
return;
}
// get parameters
canvasId = queryMap.get("id");
viewId = queryMap.get("viewId");
minx = Double.valueOf(queryMap.get("x"));
miny = Double.valueOf(queryMap.get("y"));
Canvas c = null;
try {
// get data of the current request
// variable definitions
String response;
String canvasId, viewId;
double minx, miny;
BoxandData data = null;

// check if this is a POST request
if (!httpExchange.getRequestMethod().equalsIgnoreCase("GET")) {
Server.sendResponse(httpExchange, HttpsURLConnection.HTTP_BAD_METHOD, "");
return;
}

// get data of the current request
String query = httpExchange.getRequestURI().getQuery();
Map<String, String> queryMap = Server.queryToMap(query);
// print
for (String s : queryMap.keySet()) System.out.println(s + " : " + queryMap.get(s));

// check parameters, if not pass, send a bad request response
response = checkParameters(queryMap);
if (response.length() > 0) {
Server.sendResponse(httpExchange, HttpsURLConnection.HTTP_BAD_REQUEST, response);
return;
}
// get parameters
canvasId = queryMap.get("id");
viewId = queryMap.get("viewId");
minx = Double.valueOf(queryMap.get("x"));
miny = Double.valueOf(queryMap.get("y"));
Canvas c = null;
c = Main.getProject().getCanvas(canvasId).deepCopy();
} catch (Exception e) {
e.printStackTrace();
}
View v = Main.getProject().getView(viewId);
if (queryMap.containsKey("canvasw")) c.setW(Integer.valueOf(queryMap.get("canvasw")));
if (queryMap.containsKey("canvash")) c.setH(Integer.valueOf(queryMap.get("canvash")));
ArrayList<String> predicates = new ArrayList<>();
for (int i = 0; i < c.getLayers().size(); i++)
predicates.add(queryMap.get("predicate" + i));
double oMinX = Double.valueOf(queryMap.get("oboxx"));
double oMinY = Double.valueOf(queryMap.get("oboxy"));
double oMaxX = oMinX + Double.valueOf(queryMap.get("oboxw"));
double oMaxY = oMinY + Double.valueOf(queryMap.get("oboxh"));
Box oldBox = new Box(oMinX, oMinY, oMaxX, oMaxY);
Boolean isJumping = Boolean.valueOf(queryMap.get("isJumping"));

// get box data
long st = System.currentTimeMillis();
try {
View v = Main.getProject().getView(viewId);
if (queryMap.containsKey("canvasw")) c.setW(Integer.valueOf(queryMap.get("canvasw")));
if (queryMap.containsKey("canvash")) c.setH(Integer.valueOf(queryMap.get("canvash")));
ArrayList<String> predicates = new ArrayList<>();
for (int i = 0; i < c.getLayers().size(); i++)
predicates.add(queryMap.get("predicate" + i));
double oMinX = Double.valueOf(queryMap.get("oboxx"));
double oMinY = Double.valueOf(queryMap.get("oboxy"));
double oMaxX = oMinX + Double.valueOf(queryMap.get("oboxw"));
double oMaxY = oMinY + Double.valueOf(queryMap.get("oboxh"));
Box oldBox = new Box(oMinX, oMinY, oMaxX, oMaxY);
Boolean isJumping = Boolean.valueOf(queryMap.get("isJumping"));

// get box data
long st = System.currentTimeMillis();
data = boxGetter.getBox(c, v, minx, miny, oldBox, predicates);
double fetchTime = System.currentTimeMillis() - st;
int intersectingRows = 0;
for (int i = 0; i < data.data.size(); i++) {
intersectingRows += data.data.get(i).size();
}
System.out.println("Fetch data time: " + fetchTime + "ms.");
System.out.println("number of intersecting rows in result: " + intersectingRows);

// TODO: improve this by not sending insert query every time there is a user
// interaction,
// instead, store in prepare statement (idk if that would work?)
// or in-memory data structure and flush to db in batches
if (isJumping) {
Server.sendStats(
Main.getProject().getName(),
c.getId(),
"jump",
fetchTime,
intersectingRows);
} else {
Server.sendStats(
Main.getProject().getName(), c.getId(), "pan", fetchTime, intersectingRows);
}

// send data and box back
Map<String, Object> respMap = new HashMap<>();
respMap.put("renderData", BoxandData.getDictionaryFromData(data.data, c));
respMap.put("minx", data.box.getMinx());
respMap.put("miny", data.box.getMiny());
respMap.put("boxH", data.box.getHeight());
respMap.put("boxW", data.box.getWidth());
respMap.put("canvasId", canvasId);
response = gson.toJson(respMap);

// send back response
st = System.currentTimeMillis();
Server.sendResponse(httpExchange, HttpsURLConnection.HTTP_OK, response);
System.out.println("Send response time: " + (System.currentTimeMillis() - st) + "ms.");
System.out.println();
} catch (Exception e) {
e.printStackTrace();
System.out.println("\n\n" + e.getMessage() + "\n");
Server.printServingErrorMessage();
}
double fetchTime = System.currentTimeMillis() - st;
int intersectingRows = 0;
for (int i = 0; i < data.data.size(); i++) {
intersectingRows += data.data.get(i).size();
}
System.out.println("Fetch data time: " + fetchTime + "ms.");
System.out.println("number of intersecting rows in result: " + intersectingRows);

// TODO: improve this by not sending insert query every time there is a user interaction,
// instead, store in prepare statement (idk if that would work?)
// or in-memory data structure and flush to db in batches
if (isJumping) {
Server.sendStats(
Main.getProject().getName(), c.getId(), "jump", fetchTime, intersectingRows);
} else {
Server.sendStats(
Main.getProject().getName(), c.getId(), "pan", fetchTime, intersectingRows);
}

// send data and box back
Map<String, Object> respMap = new HashMap<>();
respMap.put("renderData", BoxandData.getDictionaryFromData(data.data, c));
respMap.put("minx", data.box.getMinx());
respMap.put("miny", data.box.getMiny());
respMap.put("boxH", data.box.getHeight());
respMap.put("boxW", data.box.getWidth());
respMap.put("canvasId", canvasId);
response = gson.toJson(respMap);

// send back response
st = System.currentTimeMillis();
Server.sendResponse(httpExchange, HttpsURLConnection.HTTP_OK, response);
System.out.println("Send response time: " + (System.currentTimeMillis() - st) + "ms.");
System.out.println();
}

private String checkParameters(Map<String, String> queryMap) {
Expand Down
Loading

0 comments on commit dbf8068

Please sign in to comment.