Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Refactoring the persistence layer to be able to persist any Java Object #407

Merged
merged 6 commits into from
Sep 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.gson.JsonElement;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jooq.Record;
import org.jooq.Result;
import org.jooq.exception.DataAccessException;

public interface Persistable {
/**
Expand All @@ -48,6 +52,21 @@ public interface Persistable {
*/
JsonElement read(String rca);

/**
* This API reads the latest row from the table corresponding to the Object.
* @param clz The Class whose Object is desired.
* @param <T> The generic type of the class
* @return An instantiated Object of the class with the fields populated with the data from the latest row in the table and other
* referenced tables or null if the table does not exist yet.
* @throws NoSuchMethodException If the expected setter does not exist.
* @throws IllegalAccessException If the setter is not Public
* @throws InvocationTargetException If invoking the setter by reflection threw an exception.
* @throws InstantiationException Creating an Object of the class failed for some reason.
* @throws DataAccessException Thrown by the DB layer.
*/
<T> @Nullable T read(Class<T> clz)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, DataAccessException;

/**
* Write data to the database.
*
Expand All @@ -56,6 +75,36 @@ public interface Persistable {
*/
<T extends ResourceFlowUnit> void write(Node<?> node, T flowUnit) throws SQLException, IOException;

/**
* This API helps us write any Java Object into the Database and it does not need to be a Graph node anymore.
* This is required because:
* - in future we would like to persist 'remediation Actions' and they are not necessarily
* FlowUnits or Graph nodes.
* - This removes de-coupling of the persistence layer from the RCA runtime.
* Restrictions on the Object
* --------------------------
* The Object that can be persisted has certain restrictions:
* - The Fields of the class that are to be persisted should be annotated with '@ValueColumn' or '@RefColumn'.
* A Field should be annotated as @ValueColumn if the field type is a Java primitive type or String type.
* The accepted types are boolean, byte, char, short, int, long, float, and double and their Boxed counterparts.
* A Column can be marked as @RefColumn if the class Field is an user defined Java Class or a collection of the same.
* - The annotated Fields should have a 'getter' and a 'setter'. getters are expected to be named as 'get' or 'is'
* prepended to the _capitalized_ fieldName. And the setters are expected to be named 'set' and the capitalized
* field name. The type of the field should match the type of the getter's return type or the setter's argument
* type. Remember, int and Integer are two different types. Therefore, if the field is of type 'int' and your
* 'getter' returns a value of type Integer, you will still get NoSuchMethodException.
* - The annotated fields and the getter/setters should be declared in the Object and not in a Super class of it.
* @param object The Object that needs to be persisted.
* @param <T> The type of the Object.
* @throws SQLException This is thrown if the underlying DB throws an error.
* @throws IOException This is thrown in case the DB file rotation fails.
* @throws IllegalAccessException If the getters and setters of the fields to be persisted are not Public.
* @throws NoSuchMethodException If getter or setters don't exist with the expected naming convention.
* @throws InvocationTargetException Invoking the getter or setter throws an exception.
*/
<T> void write(@NonNull T object) throws SQLException, IOException, IllegalAccessException, NoSuchMethodException,
InvocationTargetException;

void close() throws SQLException;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.google.gson.JsonElement;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
Expand All @@ -34,6 +35,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.LogManager;
Expand Down Expand Up @@ -188,6 +190,30 @@ public synchronized <T extends ResourceFlowUnit> void write(Node<?> node, T flow
}
}

public synchronized <T> void write(T obj)
throws SQLException, IOException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Objects.requireNonNull(obj);
rotateRegisterGarbageThenCreateNewDB(RotationType.TRY_ROTATE);
try {
writeImpl(obj);
} catch (IllegalStateException | IllegalArgumentException | IllegalAccessException | InvocationTargetException | NoSuchMethodException
illegalEx) {
throw illegalEx;
} catch (SQLException e) {
LOG.info("RCA: Fail to write.", e);
rotateRegisterGarbageThenCreateNewDB(RotationType.FORCE_ROTATE);
try {
writeImpl(obj);
} catch (SQLException ex) {
LOG.error("Failed to write multiple times. Giving up.");
// We rethrow this exception so that framework can take appropriate action.
throw e;
}
}
}

abstract <T> void writeImpl(T obj) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, SQLException;

private synchronized void rotateRegisterGarbageThenCreateNewDB(RotationType type) throws IOException, SQLException {
Path rotatedFile = null;
long currTime = System.currentTimeMillis();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.amazon.opendistro.elasticsearch.performanceanalyzer.rca.persistence;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface RefColumn {
}
Loading