package com.actiontech.dble.statistic.trace;

import com.actiontech.dble.backend.mysql.nio.handler.query.DMLResponseHandler;
import com.actiontech.dble.backend.mysql.nio.handler.query.impl.BaseSelectHandler;
import com.actiontech.dble.backend.mysql.nio.handler.query.impl.OutputHandler;
import com.actiontech.dble.plan.util.ComplexQueryPlanUtil;
import com.actiontech.dble.plan.util.ReferenceHandlerInfo;
import com.actiontech.dble.route.RouteResultsetNode;
import com.actiontech.dble.util.CollectionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class SelectTraceResult {
    private static final Logger LOGGER = LoggerFactory.getLogger(SelectTraceResult.class);
    final TraceResult tr;

    public SelectTraceResult(TraceResult traceResult) {
        this.tr = traceResult;
    }

    // show @@connection.sql.status where FRONT_ID=?
    public List<String[]> genRunningSQLStage() {
        if (!tr.isDetailTrace) return null;
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("start genRunningSQLStage");
        }
        List<String[]> lst = new ArrayList<>();
        if (tr.requestStart != 0) {
            if (genTraceRecord(lst, "Read_SQL", tr.requestStart, tr.parseStart))
                return lst;
            if (genTraceRecord(lst, "Parse_SQL", tr.parseStart, tr.routeStart, tr.requestEnd))
                return lst;
            if (genTraceRecord(lst, "Route_Calculation", tr.routeStart, tr.preExecuteStart))
                return lst;
            if (genTraceRecord(lst, "Prepare_to_Push/Optimize", tr.preExecuteStart, tr.preExecuteEnd))
                return lst;
            if (tr.simpleHandler != null) {
                genRunningSimpleResults(lst);
                return lst;
            } else if (tr.builder != null) {
                genRunningComplexQueryResults(lst);
                return lst;
            } else if (tr.subQuery) {
                lst.add(genTraceRecord("Doing_SubQuery", tr.preExecuteEnd));
                return lst;
            } else if (tr.shardingNodes == null || (tr.type == TraceResult.SqlTraceType.COMPLEX_QUERY)) {
                lst.add(genTraceRecord("Generate_Query_Explain", tr.preExecuteEnd));
                return lst;
            } else {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("not support trace this query or unfinished");
                }
            }
        }
        return lst;
    }

    private void genRunningSimpleResults(List<String[]> lst) {
        List<TraceResult.BackendRoute> bRs = tr.findByHandler(tr.simpleHandler);
        List<String[]> executeList = new ArrayList<>(bRs.size());
        List<String[]> fetchList = new ArrayList<>(bRs.size());

        Set<String> receivedNode = new HashSet<>();
        long minFetchStart = Long.MAX_VALUE;
        long maxFetchEnd = 0;
        if (!CollectionUtil.isEmpty(bRs)) {
            for (TraceResult.BackendRoute ar : bRs) {
                receivedNode.add(ar.getShardingNode());
                minFetchStart = Math.min(minFetchStart, ar.getFirstRevTime());
                executeList.add(genTraceRecord("Execute_SQL", tr.preExecuteEnd, ar.getFirstRevTime(), ar.getShardingNode(), ar.getSql()));
                if (ar.getFinished() == 0) {
                    fetchList.add(genTraceRecord("Fetch_result", ar.getFirstRevTime(), ar.getShardingNode(), ar.getSql()));
                } else {
                    fetchList.add(genTraceRecord("Fetch_result", ar.getFirstRevTime(), ar.getFinished(), ar.getShardingNode(), ar.getSql()));
                    maxFetchEnd = Math.max(maxFetchEnd, ar.getFinished());
                }
            }
            lst.addAll(executeList);
            if (receivedNode.size() != tr.shardingNodes.length) {
                for (RouteResultsetNode shardingNode : tr.shardingNodes) {
                    if (!receivedNode.contains(shardingNode.getName())) {
                        lst.add(genTraceRecord("Execute_SQL", tr.preExecuteEnd, shardingNode.getName(), shardingNode.getStatement()));
                        fetchList.add(genTraceRecord("Fetch_result", shardingNode.getName(), shardingNode.getStatement()));
                    }
                }
            }
            lst.addAll(fetchList);
        } else {
            for (RouteResultsetNode shardingNode : tr.shardingNodes) {
                lst.add(genTraceRecord("Execute_SQL", tr.preExecuteEnd, shardingNode.getName(), shardingNode.getStatement()));
                lst.add(genTraceRecord("Fetch_result", shardingNode.getName(), shardingNode.getStatement()));
            }
        }
        if (tr.adtCommitBegin != 0) {
            lst.add(genTraceRecord("Distributed_Transaction_Prepare", maxFetchEnd, tr.adtCommitBegin));
            lst.add(genTraceRecord("Distributed_Transaction_Commit", tr.adtCommitBegin, tr.adtCommitEnd));
        }
        if (minFetchStart == Long.MAX_VALUE) {
            lst.add(genTraceRecord("Write_to_Client"));
        } else if (tr.requestEnd == 0) {
            lst.add(genTraceRecord("Write_to_Client", minFetchStart));
        } else {
            lst.add(genTraceRecord("Write_to_Client", minFetchStart, tr.requestEnd));
        }
    }

    private void genRunningComplexQueryResults(List<String[]> lst) {
        List<ReferenceHandlerInfo> results = ComplexQueryPlanUtil.getComplexQueryResult(tr.builder);
        long lastChildFinished = tr.preExecuteEnd;
        for (ReferenceHandlerInfo result : results) {
            DMLResponseHandler handler = result.getHandler();
            if (handler instanceof BaseSelectHandler) {
                List<TraceResult.BackendRoute> bRs = tr.findByHandler(handler);
                if (CollectionUtil.isEmpty(bRs)) {
                    if (!result.isNestLoopQuery()) {
                        lst.add(genTraceRecord("Execute_SQL", lastChildFinished, result.getName(), result.getRefOrSQL())); // lastChildFinished may is Long.MAX_VALUE
                    } else {
                        lst.add(genTraceRecord("Generate_New_Query", lastChildFinished)); // lastChildFinished may is Long.MAX_VALUE
                    }
                    lst.add(genTraceRecord("Fetch_result", result.getName(), result.getRefOrSQL()));
                } else {
                    TraceResult.BackendRoute ar = bRs.iterator().next();
                    if (!result.isNestLoopQuery()) {
                        lst.add(genTraceRecord("Execute_SQL", lastChildFinished, ar.getFirstRevTime(), result.getName(), result.getRefOrSQL()));
                    } else {
                        TraceResult.ComplexHandler complexHandler = tr.findByComplexHandler(handler);
                        if (complexHandler == null) {
                            lst.add(genTraceRecord("Generate_New_Query", lastChildFinished)); // lastChildFinished may is Long.MAX_VALUE
                        } else if (complexHandler.getEndTime() == 0) {
                            lst.add(genTraceRecord("Generate_New_Query", lastChildFinished, complexHandler.getStartTime()));
                            lst.add(genTraceRecord("Execute_SQL", complexHandler.getStartTime(), result.getName(), result.getRefOrSQL()));
                        } else {
                            lst.add(genTraceRecord("Generate_New_Query", lastChildFinished, complexHandler.getStartTime()));
                            lst.add(genTraceRecord("Execute_SQL", complexHandler.getStartTime(), complexHandler.getEndTime(), result.getName(), result.getRefOrSQL()));
                        }
                    }
                    if (ar.getFinished() == 0) {
                        lst.add(genTraceRecord("Fetch_result", ar.getFirstRevTime(), result.getName(), result.getRefOrSQL()));
                    } else {
                        lst.add(genTraceRecord("Fetch_result", ar.getFirstRevTime(), ar.getFinished(), result.getName(), result.getRefOrSQL()));
                    }
                }
            } else if (handler instanceof OutputHandler) {
                TraceResult.ComplexHandler complexHandler = tr.findByComplexHandler(handler);
                if (complexHandler == null) {
                    lst.add(genTraceRecord("Write_to_Client"));
                } else if (tr.requestEnd == 0) {
                    lst.add(genTraceRecord("Write_to_Client", complexHandler.getStartTime()));
                } else {
                    lst.add(genTraceRecord("Write_to_Client", complexHandler.getStartTime(), tr.requestEnd));
                }
            } else {
                TraceResult.ComplexHandler complexHandler = tr.findByComplexHandler(handler);
                if (complexHandler == null) {
                    lst.add(genTraceRecord(result.getType()));
                } else if (complexHandler.getEndTime() == 0) {
                    lst.add(genTraceRecord(result.getType(), complexHandler.getStartTime(), result.getName(), result.getRefOrSQL()));
                } else {
                    lst.add(genTraceRecord(result.getType(), complexHandler.getStartTime(), complexHandler.getEndTime(), result.getName(), result.getRefOrSQL()));
                }

                if (handler.getNextHandler() == null) {
                    if (complexHandler.getEndTime() != 0) {
                        lastChildFinished = Math.max(lastChildFinished, complexHandler.getEndTime());
                    } else {
                        lastChildFinished = Long.MAX_VALUE;
                    }
                }
            }
        }
    }

    // show trace
    protected List<String[]> genTraceResult() {
        if (!tr.isDetailTrace) return null;
        List<String[]> lst = new ArrayList<>();
        if (tr.isCompletedV2()) {
            lst.add(genTraceRecord("Read_SQL", tr.requestStart, tr.parseStart));
            lst.add(genTraceRecord("Parse_SQL", tr.parseStart, tr.routeStart));
            if (tr.simpleHandler != null) {
                if (genSimpleResults(lst)) return null;
            } else if (tr.builder != null) {
                if (genComplexQueryResults(lst)) return null;
            } else {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("not support trace this query");
                }
                return null;
            }
        } else {
            if (tr.isCompletedV1() && tr.isNonBusinessSql()) {
                genTraceRecord(lst, "Read_SQL", tr.requestStart, tr.parseStart);
                genTraceRecord(lst, "Parse_SQL", tr.parseStart, tr.routeStart, tr.requestEnd);
            } else {
                if (LOGGER.isDebugEnabled()) {
                    long requestCount = tr.backendRouteList.size();
                    long firstRevCount = tr.backendRouteList.stream().filter(f -> f.getFirstRevTime() != 0).count();
                    long finishedCount = tr.backendRouteList.stream().filter(f -> f.getFinished() != 0).count();
                    long recordStartCount = tr.complexHandlerMap.size();
                    long recordEndCount = tr.complexHandlerMap.values().stream().filter(f -> f.endTime != 0).count();
                    LOGGER.debug("collect info not in pairs; requestEnd:{}, requestCount:{}, firstRevCount:{}, finishedCount:{}, recordStartCount:{}, recordEndCount:{}",
                            tr.requestEnd, requestCount, firstRevCount, finishedCount, recordStartCount, recordEndCount);
                }
                return null;
            }
        }
        if (lst.size() > 0) {
            lst.add(genTraceRecord("Over_All", tr.requestStart, tr.requestEnd));
        }
        return lst;
    }

    private boolean genComplexQueryResults(List<String[]> lst) {
        lst.add(genTraceRecord("Try_Route_Calculation", tr.routeStart, tr.preExecuteStart));
        lst.add(genTraceRecord("Try_to_Optimize", tr.preExecuteStart, tr.preExecuteEnd));
        List<ReferenceHandlerInfo> results = ComplexQueryPlanUtil.getComplexQueryResult(tr.builder);
        long lastChildFinished = tr.preExecuteEnd;
        for (ReferenceHandlerInfo result : results) {
            DMLResponseHandler handler = result.getHandler();
            if (handler instanceof BaseSelectHandler) {
                List<TraceResult.BackendRoute> bRs = tr.findByHandler(handler);
                long firstRevCount = bRs.stream().filter(f -> f.getFirstRevTime() != 0).count();
                long finishedCount = bRs.stream().filter(f -> f.getFinished() != 0).count();
                if (CollectionUtil.isEmpty(bRs) || firstRevCount != 1 || finishedCount != 1) {
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("collect info not in pairs for connection; firstRevCount is {}, finishedCount is {}", firstRevCount, finishedCount);
                    }
                    return true;
                }
                TraceResult.BackendRoute ar = bRs.iterator().next();
                if (!result.isNestLoopQuery()) {
                    lst.add(genTraceRecord("Execute_SQL", lastChildFinished, ar.getFirstRevTime(), result.getName(), result.getRefOrSQL()));
                } else {
                    TraceResult.ComplexHandler complexHandler = tr.findByComplexHandler(handler);
                    if (complexHandler == null || complexHandler.getEndTime() == 0) {
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug("collect info not in pairs for handler" + handler);
                        }
                        return true;
                    }
                    lst.add(genTraceRecord("Generate_New_Query", lastChildFinished, complexHandler.getStartTime()));
                    lst.add(genTraceRecord("Execute_SQL", complexHandler.getStartTime(), complexHandler.getEndTime(), result.getName(), result.getRefOrSQL()));
                }
                lst.add(genTraceRecord("Fetch_result", ar.getFirstRevTime(), ar.getFinished(), result.getName(), result.getRefOrSQL()));
            } else if (handler instanceof OutputHandler) {
                TraceResult.ComplexHandler complexHandler = tr.findByComplexHandler(handler);
                if (complexHandler == null) {
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("collect info not in pairs for OutputHandler");
                    }
                    return true;
                }
                lst.add(genTraceRecord("Write_to_Client", complexHandler.getStartTime(), tr.requestEnd));
            } else {
                TraceResult.ComplexHandler complexHandler = tr.findByComplexHandler(handler);
                if (complexHandler == null || complexHandler.getEndTime() == 0) {
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("collect info not in pairs for handler" + handler);
                    }
                    return true;
                }
                lst.add(genTraceRecord(result.getType(), complexHandler.getStartTime(), complexHandler.getEndTime(), result.getName(), result.getRefOrSQL()));
                if (handler.getNextHandler() == null) {
                    lastChildFinished = Math.max(lastChildFinished, complexHandler.getEndTime());
                }
            }
        }
        return false;
    }

    private boolean genSimpleResults(List<String[]> lst) {
        lst.add(genTraceRecord("Route_Calculation", tr.routeStart, tr.preExecuteStart));
        lst.add(genTraceRecord("Prepare_to_Push", tr.preExecuteStart, tr.preExecuteEnd));
        List<TraceResult.BackendRoute> bRs = tr.findByHandler(tr.simpleHandler);
        List<String[]> executeList = new ArrayList<>(bRs.size());
        List<String[]> fetchList = new ArrayList<>(bRs.size());
        long minFetchStart = Long.MAX_VALUE;
        long maxFetchEnd = 0;
        for (TraceResult.BackendRoute ar : bRs) {
            minFetchStart = Math.min(minFetchStart, ar.getFirstRevTime());
            executeList.add(genTraceRecord("Execute_SQL", tr.preExecuteEnd, ar.getFirstRevTime(), ar.getShardingNode(), ar.getSql()));
            if (ar.getFinished() == 0) {
                LOGGER.debug("connection fetchEndRecord is null ");
                return true;
            }
            fetchList.add(genTraceRecord("Fetch_result", ar.getFirstRevTime(), ar.getFinished(), ar.getShardingNode(), ar.getSql()));
            maxFetchEnd = Math.max(maxFetchEnd, ar.getFinished());
        }
        lst.addAll(executeList);
        lst.addAll(fetchList);
        if (tr.adtCommitBegin != 0) {
            lst.add(genTraceRecord("Distributed_Transaction_Prepare", maxFetchEnd, tr.adtCommitBegin));
            lst.add(genTraceRecord("Distributed_Transaction_Commit", tr.adtCommitBegin, tr.adtCommitEnd));
        }
        lst.add(genTraceRecord("Write_to_Client", minFetchStart, tr.requestEnd));
        return false;
    }

    // slow log
    public List<String[]> genLogResult() {
        if (!tr.isDetailTrace) return null;
        List<String[]> lst = new ArrayList<>();
        if (tr.isCompletedV2()) {
            lst.add(genLogRecord("Read_SQL", tr.requestStart, tr.parseStart));
            lst.add(genLogRecord("Prepare_Push", tr.parseStart, tr.preExecuteEnd));
            if (tr.simpleHandler != null) {
                if (genSimpleLogs(lst)) return null;
            } else if (tr.builder != null) {
                if (genComplexQueryLogs(lst)) return null;
            } else {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("not support trace this query");
                }
                return null;
            }
        } else {
            if (tr.isCompletedV1() && tr.isNonBusinessSql()) {
                lst.add(genLogRecord("Read_SQL", tr.requestStart, tr.parseStart));
                lst.add(genLogRecord("Inner_Execute", tr.parseStart, tr.requestEnd));
                lst.add(genLogRecord("Write_Client", tr.requestEnd, tr.requestEnd));
            } else {
                if (LOGGER.isDebugEnabled()) {
                    long requestCount = tr.backendRouteList.size();
                    long firstRevCount = tr.backendRouteList.stream().filter(f -> f.getFirstRevTime() != 0).count();
                    long finishedCount = tr.backendRouteList.stream().filter(f -> f.getFinished() != 0).count();
                    long recordStartCount = tr.complexHandlerMap.size();
                    long recordEndCount = tr.complexHandlerMap.values().stream().filter(f -> f.endTime != 0).count();
                    LOGGER.debug("collect info not in pairs; requestEnd:{}, requestCount:{}, firstRevCount:{}, finishedCount:{}, recordStartCount:{}, recordEndCount:{}",
                            tr.requestEnd, requestCount, firstRevCount, finishedCount, recordStartCount, recordEndCount);
                }
                return null;
            }
        }
        return lst;
    }

    private boolean genComplexQueryLogs(List<String[]> lst) {
        List<ReferenceHandlerInfo> results = ComplexQueryPlanUtil.getComplexQueryResult(tr.builder);
        long lastChildFinished = tr.preExecuteEnd;
        for (ReferenceHandlerInfo result : results) {
            DMLResponseHandler handler = result.getHandler();
            if (handler instanceof BaseSelectHandler) {
                List<TraceResult.BackendRoute> bRs = tr.findByHandler(handler);
                long firstRevCount = bRs.stream().filter(f -> f.getFirstRevTime() != 0).count();
                long finishedCount = bRs.stream().filter(f -> f.getFinished() != 0).count();
                if (CollectionUtil.isEmpty(bRs) || firstRevCount != 1 || finishedCount != 1) {
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("collect info not in pairs for connection; firstRevCount is {}, finishedCount is {}", firstRevCount, finishedCount);
                    }
                    return true;
                }
                TraceResult.BackendRoute ar = bRs.iterator().next();
                if (!result.isNestLoopQuery()) {
                    lst.add(genLogRecord(result.getName() + "_First_Result_Fetch", lastChildFinished, ar.getFirstRevTime()));
                } else {
                    TraceResult.ComplexHandler complexHandler = tr.findByComplexHandler(handler);
                    if (complexHandler == null || complexHandler.getEndTime() == 0) {
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug("collect info not in pairs for handler: {}", handler);
                        }
                        return true;
                    }
                    lst.add(genLogRecord("Generate_New_Query", lastChildFinished, complexHandler.getStartTime()));
                    lst.add(genLogRecord(result.getName() + "_First_Result_Fetch", complexHandler.getStartTime(), complexHandler.getEndTime()));
                }
                lst.add(genLogRecord(result.getName() + "_Last_Result_Fetch", ar.getFirstRevTime(), ar.getFinished()));
            } else if (handler instanceof OutputHandler) {
                TraceResult.ComplexHandler complexHandler = tr.findByComplexHandler(handler);
                if (complexHandler == null) {
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("collect info not in pairs for OutputHandler");
                    }
                    return true;
                }
                lst.add(genLogRecord("Write_Client", complexHandler.getStartTime(), tr.requestEnd));
            } else {
                TraceResult.ComplexHandler complexHandler = tr.findByComplexHandler(handler);
                if (complexHandler == null || complexHandler.getEndTime() == 0) {
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("collect info not in pairs for handler" + handler);
                    }
                    return true;
                }
                if (handler.getNextHandler() == null) {
                    lastChildFinished = Math.max(lastChildFinished, complexHandler.getEndTime());
                }
            }
        }
        return false;
    }

    private boolean genSimpleLogs(List<String[]> lst) {
        List<TraceResult.BackendRoute> bRs = tr.findByHandler(tr.simpleHandler);
        List<String[]> executeList = new ArrayList<>(bRs.size());
        List<String[]> fetchList = new ArrayList<>(bRs.size());
        long minFetchStart = Long.MAX_VALUE;
        long maxFetchEnd = 0;
        for (TraceResult.BackendRoute ar : bRs) {
            minFetchStart = Math.min(minFetchStart, ar.getFirstRevTime());
            executeList.add(genLogRecord(ar.getShardingNode() + "_First_Result_Fetch", tr.preExecuteEnd, ar.getFirstRevTime()));
            if (ar.getFinished() == 0) {
                LOGGER.debug("connection fetchEndRecord is null ");
                return true;
            }
            fetchList.add(genLogRecord(ar.getShardingNode() + "_Last_Result_Fetch", ar.getFirstRevTime(), ar.getFinished()));
            maxFetchEnd = Math.max(maxFetchEnd, ar.getFinished());
        }
        lst.addAll(executeList);
        lst.addAll(fetchList);
        lst.add(genLogRecord("Write_Client", minFetchStart, tr.requestEnd));
        return false;
    }

    // calculate
    private boolean genTraceRecord(List<String[]> lst, String operation, long start0, long end0) {
        return genTraceRecord(lst, operation, start0, end0, 0);
    }

    private boolean genTraceRecord(List<String[]> lst, String operation, long start0, long end0, long finalEnd) {
        if (end0 == 0) {
            if (finalEnd != 0) {
                lst.add(genTraceRecord(operation, start0, finalEnd));
                lst.add(genTraceRecord("Write_to_Client", finalEnd, finalEnd));
            } else {
                lst.add(genTraceRecord(operation, start0));
            }
            return true;
        } else {
            lst.add(genTraceRecord(operation, start0, end0));
            return false;
        }
    }

    private String[] genTraceRecord(String operation, long start) {
        return genTraceRecord(operation, start, "-", "-");

    }

    private String[] genTraceRecord(String operation, long start, String shardingNode, String ref) {
        if (start == Long.MAX_VALUE) {
            return genTraceRecord(operation, shardingNode, ref);
        }
        String[] readQuery = new String[6];
        readQuery[0] = operation;
        readQuery[1] = nanoToMilliSecond(start - tr.requestStart);
        readQuery[2] = "unfinished";
        readQuery[3] = "unknown";
        readQuery[4] = shardingNode;
        readQuery[5] = ref.replaceAll("[\\t\\n\\r]", " ");
        return readQuery;
    }

    private String[] genTraceRecord(String operation, String shardingNode, String ref) {
        String[] readQuery = new String[6];
        readQuery[0] = operation;
        readQuery[1] = "not started";
        readQuery[2] = "unfinished";
        readQuery[3] = "unknown";
        readQuery[4] = shardingNode;
        readQuery[5] = ref.replaceAll("[\\t\\n\\r]", " ");
        return readQuery;
    }

    private String[] genTraceRecord(String operation) {
        return genTraceRecord(operation, "-", "-");
    }

    private String[] genTraceRecord(String operation, long start, long end) {
        return genTraceRecord(operation, start, end, "-", "-");
    }

    private String[] genTraceRecord(String operation, long start, long end, String shardingNode, String ref) {
        String[] readQuery = new String[6];
        readQuery[0] = operation;
        readQuery[1] = nanoToMilliSecond(start - tr.requestStart);
        readQuery[2] = nanoToMilliSecond(end - tr.requestStart);
        readQuery[3] = nanoToMilliSecond(end - start);
        readQuery[4] = shardingNode;
        readQuery[5] = ref.replaceAll("[\\t\\n\\r]", " ");
        return readQuery;
    }

    private String[] genLogRecord(String operation, long start, long end) {
        String[] readQuery = new String[2];
        readQuery[0] = operation;
        readQuery[1] = nanoToSecond(end - start);
        return readQuery;
    }

    private String nanoToMilliSecond(long nano) {
        double milliSecond = (double) nano / 1000000;
        return String.valueOf(milliSecond);
    }

    private String nanoToSecond(long nano) {
        double milliSecond = (double) nano / 1000000000;
        return String.format("%.6f", milliSecond);
    }

}
