From 4e2c1cd369a368d293e8b17662daa30d4c51c7e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=EC=9E=AC=ED=98=B8=28JaeHo=20Noh=29/Data=20Infras?= =?UTF-8?q?tructure=ED=8C=80/SKP?= Date: Thu, 2 Jun 2016 09:32:10 +0900 Subject: [PATCH 1/5] =?UTF-8?q?daemon=20=EC=8A=A4=ED=81=AC=EB=A6=BD?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/start-checkmate.sh | 37 +++++++++++++++++++++++++++++++++++++ bin/stop-checkmate.sh | 15 +++++++++++++++ 2 files changed, 52 insertions(+) create mode 100755 bin/start-checkmate.sh create mode 100755 bin/stop-checkmate.sh diff --git a/bin/start-checkmate.sh b/bin/start-checkmate.sh new file mode 100755 index 0000000..2b34afe --- /dev/null +++ b/bin/start-checkmate.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +bin=`dirname "$0"` +bin=`cd "${bin}/.."; pwd` + +export CM_HOME="${bin}" + +CM_LOG_DIR=${CM_HOME}/logs +CM_CONF_DIR=${CM_HOME}/conf +CM_PID_DIR=${CM_HOME}/pid + +if [ ! -d ${CM_LOG_DIR} ]; then + mkdir ${CM_LOG_DIR} + if [ "$?" != "0" ]; then + echo "ERROR mkdir ${CM_LOG_DIR}" + exit 1 + fi +fi +if [ ! -d ${CM_PID_DIR} ]; then + mkdir ${CM_PID_DIR} + if [ "$?" != "0" ]; then + echo "ERROR mkdir ${CM_PID_DIR}" + exit 1 + fi +fi +START_TIME=`date "+%Y%m%d_%H%M%S"` + +CLASSPATH=${CM_CONF_DIR}:${CM_HOME}/lib/*:${CLASSPATH} + +if [ "${JVM_ARGS}" == "" ]; then + JVM_ARGS="-enableassertions -enablesystemassertions -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly -Xms256m -Xmx512m" +fi +JVM_ARGS="${JVM_ARGS} -DCM_HOME=${CM_HOME}" +DEBUG_ARGS="-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -verbose:gc -Xloggc:${CM_LOG_DIR}/checkmate_${START_TIME}.gc" + +nohup java $JVM_ARGS -cp "$CLASSPATH" com.skplanet.checkmate.CheckMateServer "$@" > ${CM_LOG_DIR}/checkmate_${START_TIME}.out 2>&1 & +echo $! > ${CM_PID_DIR}/checkmate.pid diff --git a/bin/stop-checkmate.sh b/bin/stop-checkmate.sh new file mode 100755 index 0000000..b5db44a --- /dev/null +++ b/bin/stop-checkmate.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +bin=`dirname "$0"` +bin=`cd "${bin}/.."; pwd` + +export CM_HOME="${bin}" + +CM_PID_DIR=${CM_HOME}/pid + +if [ -f ${CM_PID_DIR}/checkmate.pid ]; then + pid=`cat ${CM_PID_DIR}/checkmate.pid` + echo "kill -9 ${pid}" + kill -9 "${pid}" + rm ${CM_PID_DIR}/checkmate.pid +fi From 6ae6c3a72e5dc04ce3c3f7bfdf0e75e3b5eccc67 Mon Sep 17 00:00:00 2001 From: 1002571 Date: Wed, 27 Jul 2016 12:05:46 +0900 Subject: [PATCH 2/5] =?UTF-8?q?[CM-10]=20checkmate=20=EB=A9=94=EB=AA=A8?= =?UTF-8?q?=EB=A6=AC=EB=A6=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CheckMate 서버의 메모리릭 수정하고 js 에서 과도한 loop 발생하는 부분 수정함 --- .gitignore | 2 + conf/.gitignore | 2 + conf/checkmate.ini.sample | 9 +- conf/log4j.properties | 34 +- conf/querycache.ini.sample | 3 +- pom.xml | 15 - src/main/java/com/beans/ClustersBean.java | 12 +- .../skplanet/checkmate/CheckMateServer.java | 307 +++------ .../com/skplanet/checkmate/CheckMateWeb.java | 129 ++++ .../querycache/QCClientWebSocket.java | 136 ---- .../checkmate/querycache/QCCluster.java | 531 ++++++-------- .../querycache/QCClusterManager.java | 215 ++---- .../querycache/QCClusterOptions.java | 41 ++ .../checkmate/querycache/QCQuery.java | 407 +++++++---- .../checkmate/querycache/QCQueryMail.java | 47 +- .../checkmate/querycache/QCServer.java | 648 ++++++++---------- .../querycache/QCWebSocketClient.java | 224 ++++++ .../data/ClusterServerPoolInfo.java | 37 + .../data/ClusterServerSysStats.java | 42 ++ .../querycache/data/QCQueryEvent.java | 54 ++ .../querycache/data/QCQueryImport.java | 98 +++ .../querycache/data/ServerConDesc.java | 27 + .../querycache/data/ServerObjectPool.java | 41 ++ .../querycache/data/ServerQueries.java | 22 + .../querycache/data/ServerRuntimeInfo.java | 33 + .../querycache/data/ServerSystemInfo.java | 47 ++ .../querycache/data/ServerSystemStats.java | 27 + .../querycache/data/ServerThreadInfo.java | 41 ++ .../checkmate/servlet/CMApiServletQC.java | 115 ++-- .../servlet/CMQCServerWebSocket.java | 51 +- .../servlet/CMWebSocketServletQC.java | 8 +- .../skplanet/checkmate/utils/HttpUtil.java | 90 ++- .../skplanet/checkmate/utils/MailSender.java | 355 +++++----- .../checkmate/yarn/YarnResourceManager.java | 2 +- www/css/querycache.css | 18 +- www/qc/queries.funcs.html | 29 +- 36 files changed, 2175 insertions(+), 1724 deletions(-) create mode 100644 conf/.gitignore create mode 100644 src/main/java/com/skplanet/checkmate/CheckMateWeb.java delete mode 100644 src/main/java/com/skplanet/checkmate/querycache/QCClientWebSocket.java create mode 100644 src/main/java/com/skplanet/checkmate/querycache/QCClusterOptions.java create mode 100644 src/main/java/com/skplanet/checkmate/querycache/QCWebSocketClient.java create mode 100644 src/main/java/com/skplanet/checkmate/querycache/data/ClusterServerPoolInfo.java create mode 100644 src/main/java/com/skplanet/checkmate/querycache/data/ClusterServerSysStats.java create mode 100644 src/main/java/com/skplanet/checkmate/querycache/data/QCQueryEvent.java create mode 100644 src/main/java/com/skplanet/checkmate/querycache/data/QCQueryImport.java create mode 100644 src/main/java/com/skplanet/checkmate/querycache/data/ServerConDesc.java create mode 100644 src/main/java/com/skplanet/checkmate/querycache/data/ServerObjectPool.java create mode 100644 src/main/java/com/skplanet/checkmate/querycache/data/ServerQueries.java create mode 100644 src/main/java/com/skplanet/checkmate/querycache/data/ServerRuntimeInfo.java create mode 100644 src/main/java/com/skplanet/checkmate/querycache/data/ServerSystemInfo.java create mode 100644 src/main/java/com/skplanet/checkmate/querycache/data/ServerSystemStats.java create mode 100644 src/main/java/com/skplanet/checkmate/querycache/data/ServerThreadInfo.java diff --git a/.gitignore b/.gitignore index e81ea6b..9460bc7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ tmp/* .idea *.iml checkmate-dist +/target/ +/logs/ diff --git a/conf/.gitignore b/conf/.gitignore new file mode 100644 index 0000000..df63422 --- /dev/null +++ b/conf/.gitignore @@ -0,0 +1,2 @@ +/checkmate.ini +/querycache.ini diff --git a/conf/checkmate.ini.sample b/conf/checkmate.ini.sample index c91cfa3..c2e9f8d 100644 --- a/conf/checkmate.ini.sample +++ b/conf/checkmate.ini.sample @@ -1 +1,8 @@ -WebServicePort = 60080 +[global] +WebPort = 60080 + +[email] +SmtpServer = +SmtpFrom = CheckMate +SmtpPort = 25 +MailRecipients = diff --git a/conf/log4j.properties b/conf/log4j.properties index 6762096..b95eca3 100644 --- a/conf/log4j.properties +++ b/conf/log4j.properties @@ -1,4 +1,4 @@ -checkmate.root.logger=INFO,DRFA,RFA,console +checkmate.root.logger=INFO,DRFA,console checkmate.log.dir=${CM_HOME}/logs checkmate.log.file=checkmate.log checkmate.warn.log.file=warn.checkmate.log @@ -15,38 +15,10 @@ log4j.appender.DRFA=org.apache.log4j.DailyRollingFileAppender log4j.appender.DRFA.File=${checkmate.log.dir}/${checkmate.log.file} log4j.appender.DRFA.DatePattern=.yyyy-MM-dd log4j.appender.DRFA.layout=org.apache.log4j.PatternLayout -log4j.appender.DRFA.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n - -# WARN -log4j.appender.RFA=org.apache.log4j.DailyRollingFileAppender -log4j.appender.RFA.File=${checkmate.log.dir}/${checkmate.warn.log.file} -log4j.appender.RFA.DatePattern=.yyyy-MM-dd -log4j.appender.RFA.layout=org.apache.log4j.PatternLayout -log4j.appender.RFA.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n -log4j.appender.RFA.Threshold=WARN +log4j.appender.DRFA.layout.ConversionPattern=%d{ISO8601} %-5p %c{2}(%L): %m%n # Console log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.target=System.err log4j.appender.console.layout=org.apache.log4j.PatternLayout -log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{2}: %m%n - -# querycache log -checkmate.querycache.log.file=querycache.log -log4j.logger.querycache=DEBUG,DRFAquerycache -log4j.appender.DRFAquerycache=org.apache.log4j.DailyRollingFileAppender -log4j.appender.DRFAquerycache.File=${checkmate.log.dir}/${checkmate.querycache.log.file} -log4j.appender.DRFAquerycache.DatePattern=.yyyy-MM-dd -log4j.appender.DRFAquerycache.layout=org.apache.log4j.PatternLayout -log4j.appender.DRFAquerycache.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n - - -# querycache log -checkmate.api.log.file=api.log -log4j.logger.api=DEBUG,DRFAapi -log4j.appender.DRFAapi=org.apache.log4j.DailyRollingFileAppender -log4j.appender.DRFAapi.File=${checkmate.log.dir}/${checkmate.api.log.file} -log4j.appender.DRFAapi.DatePattern=.yyyy-MM-dd -log4j.appender.DRFAapi.layout=org.apache.log4j.PatternLayout -log4j.appender.DRFAapi.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n - +log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %-5p %c{2}(%L): %m%n diff --git a/conf/querycache.ini.sample b/conf/querycache.ini.sample index 9ebdba7..d494eac 100644 --- a/conf/querycache.ini.sample +++ b/conf/querycache.ini.sample @@ -1,7 +1,8 @@ -PartialUpdateInterval = 3000 +[global] FullUpdateInterval = 10000 Clusters = "myCluster" [myCluster] WebPort = 8080 Servers = localhost +MaxCompleteQueries = 100 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 43ba042..9c8a2bb 100644 --- a/pom.xml +++ b/pom.xml @@ -168,21 +168,6 @@ javax.mail 1.5.4 - - - - - - - - - - - - - - - io.dropwizard.metrics metrics-core diff --git a/src/main/java/com/beans/ClustersBean.java b/src/main/java/com/beans/ClustersBean.java index 026bf66..6365596 100644 --- a/src/main/java/com/beans/ClustersBean.java +++ b/src/main/java/com/beans/ClustersBean.java @@ -1,18 +1,22 @@ package com.beans; -import com.skplanet.checkmate.querycache.QCClusterManager; - import java.util.ArrayList; import java.util.Collection; +import com.skplanet.checkmate.CheckMateServer; + /** * Created by nazgul33 on 15. 2. 6. */ public class ClustersBean implements java.io.Serializable { - Collection qcClusters = new ArrayList<>(); + + + private static final long serialVersionUID = 2387849279567737354L; + + private Collection qcClusters = new ArrayList<>(); public ClustersBean() { - qcClusters.addAll( QCClusterManager.getInstance().getClusterList() ); + qcClusters.addAll( CheckMateServer.getClusterManager().getClusterNameList() ); } public Collection getQcClusters() { diff --git a/src/main/java/com/skplanet/checkmate/CheckMateServer.java b/src/main/java/com/skplanet/checkmate/CheckMateServer.java index d5d0a37..3800245 100644 --- a/src/main/java/com/skplanet/checkmate/CheckMateServer.java +++ b/src/main/java/com/skplanet/checkmate/CheckMateServer.java @@ -1,270 +1,127 @@ package com.skplanet.checkmate; -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.servlets.MetricsServlet; -import com.skplanet.checkmate.querycache.QCClusterManager; -import com.skplanet.checkmate.servlet.CMApiServletQC; -import com.skplanet.checkmate.servlet.CMWebSocketServletQC; -import com.skplanet.checkmate.utils.MailSender; -import com.skplanet.checkmate.yarn.YarnMonitor; +import java.io.File; + import org.apache.commons.configuration.HierarchicalINIConfiguration; import org.apache.commons.configuration.SubnodeConfiguration; -import org.apache.tomcat.InstanceManager; -import org.apache.tomcat.SimpleInstanceManager; -import org.eclipse.jetty.annotations.ServletContainerInitializersStarter; -import org.eclipse.jetty.apache.jsp.JettyJasperInitializer; -import org.eclipse.jetty.jsp.JettyJspServlet; -import org.eclipse.jetty.plus.annotation.ContainerInitializer; -import org.eclipse.jetty.server.Handler; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.ContextHandlerCollection; -import org.eclipse.jetty.server.handler.HandlerList; -import org.eclipse.jetty.servlet.DefaultServlet; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.webapp.WebAppContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.*; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import com.codahale.metrics.MetricRegistry; +import com.skplanet.checkmate.querycache.QCClusterManager; +import com.skplanet.checkmate.utils.MailSender; /** * Hello world! * */ -public class CheckMateServer -{ - private static final Logger LOG = LoggerFactory.getLogger("main"); +public class CheckMateServer { + + private static final Logger LOG = LoggerFactory.getLogger(CheckMateServer.class); + private static final MetricRegistry metrics = new MetricRegistry(); - public static MetricRegistry getMetrics() { - return metrics; - } + private File confDir; + + private final HierarchicalINIConfiguration ini; - private void StartClusterManagers() { - LOG.info("Starting QueryCache Cluster Thread"); - QCClusterManager.getInstance().startThread(); + private QCClusterManager clusterManager; + private CheckMateWeb webServer; -// LOG.info("Starting Yarn Monitor"); -// YarnMonitor.getInstance(); - } - private void ShutdownClusterManagers() { - LOG.info("Shutting down QueryCache Cluster Thread"); - QCClusterManager.getInstance().finishThread(); - } - - private Server webServer = null; - private int webServicePort; - private String home = null; - private String wwwroot = null; - private static final String WEBROOT_INDEX = "/www/"; + private static CheckMateServer _this = null; + + private CheckMateServer(File homeDir) throws Exception { - private URI getWebRootResourceUri() throws FileNotFoundException, URISyntaxException, MalformedURLException - { - // try local resource first - wwwroot = home + "/www"; - URL indexUri = new File(wwwroot).toURI().toURL(); - if (indexUri == null) - { - // fallback to resources in jar - wwwroot = WEBROOT_INDEX; - indexUri = this.getClass().getResource(wwwroot); - throw new FileNotFoundException("Unable to find resource " + wwwroot); - } - // Points to wherever /webroot/ (the resource) is - return indexUri.toURI(); - } + confDir = new File(homeDir, "conf"); - private File getScratchDir() throws IOException - { - File scratchDir = new File(home, "/tmp/jetty-jsp"); + File iniFile = new File(confDir,"checkmate.ini"); + LOG.info("reading checkmate configuration from " + iniFile); - if (!scratchDir.exists()) + ini = new HierarchicalINIConfiguration(iniFile); + + SubnodeConfiguration globalSection = ini.getSection("global"); + globalSection.setThrowExceptionOnMissing(true); + + // MailSender { - LOG.info("making scratch directory " + scratchDir.toString()); - if (!scratchDir.mkdirs()) - { - LOG.error("making scratch directory failed."); - throw new IOException("Unable to create scratch directory: " + scratchDir); + SubnodeConfiguration email = ini.getSection("email"); + email.setThrowExceptionOnMissing(true); + + String server = email.getString("SmtpServer", null); + String from = email.getString("SmtpFrom", "CheckMate"); + int port = email.getInt("SmtpPort", 25); + String tos = email.getString("MailRecipients", null); + + if (server != null && !server.isEmpty() && tos != null && !tos.isEmpty()) { + MailSender.initialize(server, port, from, tos.split(",")); + } else { + LOG.info("mail sender not initialized"); } } - return scratchDir; + + // ClusterManager + clusterManager = new QCClusterManager(confDir); + + // WebServer + { + int port = globalSection.getInt("WebPort", 8080); + webServer = new CheckMateWeb(port, homeDir, metrics); + } } - - private List jspInitializers() - { - JettyJasperInitializer sci = new JettyJasperInitializer(); - ContainerInitializer initializer = new ContainerInitializer(sci, null); - List initializers = new ArrayList<>(); - initializers.add(initializer); - return initializers; + + public static CheckMateServer getInstance() { + return _this; } - - private ClassLoader getUrlClassLoader() - { - ClassLoader jspClassLoader = new URLClassLoader(new URL[0], this.getClass().getClassLoader()); - return jspClassLoader; + + public static QCClusterManager getClusterManager() { + return _this.clusterManager; } - - private ServletHolder jspServletHolder() - { - ServletHolder holderJsp = new ServletHolder("jsp", JettyJspServlet.class); - holderJsp.setInitOrder(0); - holderJsp.setInitParameter("logVerbosityLevel", "DEBUG"); - holderJsp.setInitParameter("fork", "false"); - holderJsp.setInitParameter("xpoweredBy", "false"); - holderJsp.setInitParameter("compilerTargetVM", "1.7"); - holderJsp.setInitParameter("compilerSourceVM", "1.7"); - holderJsp.setInitParameter("keepgenerated", "true"); - return holderJsp; + + public static MetricRegistry getMetrics() { + return metrics; } - private ServletHolder defaultServletHolder(URI baseUri) - { - ServletHolder holderDefault = new ServletHolder("default", DefaultServlet.class); - LOG.info("Base URI: " + baseUri); - holderDefault.setInitParameter("resourceBase", baseUri.toASCIIString()); - holderDefault.setInitParameter("dirAllowed", "false"); - holderDefault.setInitParameter("redirectWelcome", "true"); - holderDefault.setInitParameter("welcomeServlets", "false"); + private void startClusterManagers() { + + LOG.info("Starting QueryCache Cluster Thread"); + clusterManager.start(); - return holderDefault; +// LOG.info("Starting Yarn Monitor"); +// YarnMonitor.getInstance(); } - - private WebAppContext getWebAppContext(URI baseUri, File scratchDir) - { - WebAppContext context = new WebAppContext(); - context.setContextPath("/"); - context.setAttribute("javax.servlet.context.tempdir", scratchDir); - context.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", - ".*/[^/]*servlet-api-[^/]*\\.jar$|.*/javax.servlet.jsp.jstl-.*\\.jar$|.*/.*taglibs.*\\.jar$"); - context.setResourceBase(baseUri.toASCIIString()); - context.setAttribute("org.eclipse.jetty.containerInitializers", jspInitializers()); - context.setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager()); - context.addBean(new ServletContainerInitializersStarter(context), true); - context.setClassLoader(getUrlClassLoader()); - - context.addServlet(jspServletHolder(), "*.jsp"); - // Add Application Servlets - // context.addServlet(BeanMapper.class, "*.cm"); - context.addServlet(defaultServletHolder(baseUri), "/"); - return context; + + private void startWebServer() throws Exception { + webServer.runWebServer(); } - - public Server runWebInterface() throws Exception { - // create web service - LOG.info("Starting web interface..."); - webServer = new Server(webServicePort); - - URI baseUri = getWebRootResourceUri(); - - System.setProperty("org.apache.jasper.compiler.disablejsr199","false"); - -// ServletContextHandler apiImpala = new ServletContextHandler(ServletContextHandler.SESSIONS); -// apiImpala.setContextPath("/api/impala"); -// apiImpala.addServlet(new ServletHolder(new QCWebApiServlet()), "/*"); - - ServletContextHandler apiQc = new ServletContextHandler(ServletContextHandler.SESSIONS); - apiQc.setContextPath("/api/qc"); - apiQc.addServlet(new ServletHolder(new CMWebSocketServletQC()), "/websocket/*"); - apiQc.addServlet(new ServletHolder(new CMApiServletQC()), "/*"); - - ServletContextHandler metrics = new ServletContextHandler(ServletContextHandler.SESSIONS); - metrics.setContextPath("/api/metrics"); - metrics.addServlet(new ServletHolder(new MetricsServlet(getMetrics())), "/metrics"); - - File scratchDir = getScratchDir(); - ServletContextHandler webappHandler = getWebAppContext(baseUri, scratchDir); - - ContextHandlerCollection contextCollection = new ContextHandlerCollection(); - contextCollection.setHandlers( new Handler[] { apiQc, metrics, webappHandler } ); - - HandlerList handlers = new HandlerList(); - handlers.setHandlers(new Handler[]{contextCollection}); - webServer.setHandler(handlers); - webServer.start(); - LOG.info("Started web interface..."); - - return webServer; + + private void stopClusterManagers() { + LOG.info("Shutting down QueryCache Cluster Thread"); + clusterManager.stop(); } - private final HierarchicalINIConfiguration clusterConfigFile; - private MailSender mailSender = null; - private List mailRecipients = null; + public static void main( String[] args ) { - public CheckMateServer() throws Exception { - home = System.getenv("CM_HOME"); - if (home == null) { - home = "./"; + String cmHome = System.getenv("CM_HOME"); + if (cmHome == null) { + throw new RuntimeException("$CM_HOME undefined"); } - - String conf = home + "/conf/checkmate.ini"; - LOG.info("reading checkmate configuration from " + conf); - - clusterConfigFile = new HierarchicalINIConfiguration(conf); - SubnodeConfiguration globalSection = clusterConfigFile.getSection(null); - globalSection.setThrowExceptionOnMissing(true); - webServicePort = globalSection.getInt("WebServicePort", 8080); - - // init mail sender - String smtpServer = globalSection.getString("SmtpServer", null); - String smtpFrom = globalSection.getString("SmtpFrom", "CheckMate"); - int smtpPort = globalSection.getInt("SmtpPort", 25); - String recipients = globalSection.getString("MailRecipients", null); - - if (smtpServer != null && recipients != null) { - mailRecipients = Arrays.asList( recipients.split(",") ); - mailSender = new MailSender(smtpServer, smtpFrom, smtpPort, mailRecipients); - new Thread(mailSender).start(); - } - } - - public HierarchicalINIConfiguration getClusterConfigFile() { - return clusterConfigFile; - } - - public List getMailRecipients() { - return mailRecipients; - } - - public MailSender getMailSender() { - return mailSender; - } - - private static CheckMateServer server = null; - public static CheckMateServer getInstance() { - return server; - } - - public static void main( String[] args ) - { - try { - server = new CheckMateServer(); - server.StartClusterManagers(); + + File homeDir = new File(cmHome); + try { + _this = new CheckMateServer(homeDir); + _this.startClusterManagers(); } catch (Exception e) { LOG.error("error initializing CheckMateServer", e); System.exit(1); } try { - server.runWebInterface(); + _this.startWebServer(); } catch (Exception e) { - LOG.error("exception while starting web interface"); + LOG.error("exception while starting web interface", e); System.exit(1); } - - // run forever until interrupted. - try { - server.webServer.join(); - } catch (Exception e) { - LOG.error("Checkmate Interrupted." + e.toString()); - } - server.ShutdownClusterManagers(); + + _this.stopClusterManagers(); } } diff --git a/src/main/java/com/skplanet/checkmate/CheckMateWeb.java b/src/main/java/com/skplanet/checkmate/CheckMateWeb.java new file mode 100644 index 0000000..e325de3 --- /dev/null +++ b/src/main/java/com/skplanet/checkmate/CheckMateWeb.java @@ -0,0 +1,129 @@ +package com.skplanet.checkmate; + +import java.io.File; +import java.io.FileNotFoundException; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; + +import org.apache.tomcat.InstanceManager; +import org.apache.tomcat.SimpleInstanceManager; +import org.eclipse.jetty.annotations.ServletContainerInitializersStarter; +import org.eclipse.jetty.apache.jsp.JettyJasperInitializer; +import org.eclipse.jetty.jsp.JettyJspServlet; +import org.eclipse.jetty.plus.annotation.ContainerInitializer; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.handler.HandlerList; +import org.eclipse.jetty.servlet.DefaultServlet; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.webapp.WebAppContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.servlets.MetricsServlet; +import com.skplanet.checkmate.servlet.CMApiServletQC; +import com.skplanet.checkmate.servlet.CMWebSocketServletQC; + +public class CheckMateWeb { + + private static final Logger LOG = LoggerFactory.getLogger(CheckMateWeb.class); + + private Server webServer = null; + + public CheckMateWeb(int port, File rootDir, MetricRegistry metric) throws Exception { + LOG.info("Starting web interface..."); + webServer = new Server(port); + + File webRoot = new File(rootDir,"www"); + if (!webRoot.exists()) { + throw new FileNotFoundException("Unable to find resource " + webRoot); + } + // Points to wherever /www/ (the resource) is + URI baseUri = webRoot.toURI(); + + System.setProperty("org.apache.jasper.compiler.disablejsr199","false"); + + // ServletContextHandler apiImpala = new ServletContextHandler(ServletContextHandler.SESSIONS); + // apiImpala.setContextPath("/api/impala"); + // apiImpala.addServlet(new ServletHolder(new QCWebApiServlet()), "/*"); + + ServletContextHandler apiQc = new ServletContextHandler(ServletContextHandler.SESSIONS); + apiQc.setContextPath("/api/qc"); + apiQc.addServlet(new ServletHolder(new CMWebSocketServletQC()), "/websocket/*"); + apiQc.addServlet(new ServletHolder(new CMApiServletQC()), "/*"); + + ServletContextHandler metrics = new ServletContextHandler(ServletContextHandler.SESSIONS); + metrics.setContextPath("/api/metrics"); + metrics.addServlet(new ServletHolder(new MetricsServlet(metric)), "/metrics"); + + // File scratchDir = getScratchDir(); + WebAppContext context = new WebAppContext(); + context.setContextPath("/"); + // context.setAttribute("javax.servlet.context.tempdir", scratchDir); + context.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", + ".*/[^/]*servlet-api-[^/]*\\.jar$|.*/javax.servlet.jsp.jstl-.*\\.jar$|.*/.*taglibs.*\\.jar$"); + context.setResourceBase(webRoot.toString()); + context.setAttribute("org.eclipse.jetty.containerInitializers", getJspInitializers()); + context.setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager()); + context.addBean(new ServletContainerInitializersStarter(context), true); + context.setClassLoader(getUrlClassLoader()); + context.addServlet(getJspServletHolder(), "*.jsp"); + context.addServlet(getDefaultServletHolder(baseUri), "/"); + // ServletContextHandler webappHandler = getWebAppContext(baseUri, scratchDir); + + ContextHandlerCollection contextCollection = new ContextHandlerCollection(); + contextCollection.setHandlers( new Handler[] { apiQc, metrics, context } ); + + HandlerList handlers = new HandlerList(); + handlers.setHandlers(new Handler[]{contextCollection}); + webServer.setHandler(handlers); + } + + private List getJspInitializers() { + JettyJasperInitializer sci = new JettyJasperInitializer(); + ContainerInitializer initializer = new ContainerInitializer(sci, null); + List initializers = new ArrayList<>(); + initializers.add(initializer); + return initializers; + } + + private ClassLoader getUrlClassLoader() { + ClassLoader jspClassLoader = new URLClassLoader(new URL[0], this.getClass().getClassLoader()); + return jspClassLoader; + } + + private ServletHolder getJspServletHolder() { + ServletHolder holderJsp = new ServletHolder("jsp", JettyJspServlet.class); + holderJsp.setInitOrder(0); + holderJsp.setInitParameter("logVerbosityLevel", "DEBUG"); + holderJsp.setInitParameter("fork", "false"); + holderJsp.setInitParameter("xpoweredBy", "false"); + holderJsp.setInitParameter("compilerTargetVM", "1.7"); + holderJsp.setInitParameter("compilerSourceVM", "1.7"); + holderJsp.setInitParameter("keepgenerated", "true"); + return holderJsp; + } + + private ServletHolder getDefaultServletHolder(URI baseUri) { + ServletHolder holderDefault = new ServletHolder("default", DefaultServlet.class); + LOG.info("Base URI: " + baseUri); + holderDefault.setInitParameter("resourceBase", baseUri.toASCIIString()); + holderDefault.setInitParameter("dirAllowed", "false"); + holderDefault.setInitParameter("redirectWelcome", "true"); + holderDefault.setInitParameter("welcomeServlets", "false"); + + return holderDefault; + } + + public void runWebServer() throws Exception { + + webServer.start(); + webServer.join(); + } +} diff --git a/src/main/java/com/skplanet/checkmate/querycache/QCClientWebSocket.java b/src/main/java/com/skplanet/checkmate/querycache/QCClientWebSocket.java deleted file mode 100644 index 2ca8086..0000000 --- a/src/main/java/com/skplanet/checkmate/querycache/QCClientWebSocket.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.skplanet.checkmate.querycache; - -import com.google.gson.Gson; -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.WebSocketListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sun.misc.Request; - -/** - * Created by nazgul33 on 15. 2. 23. - */ -public class QCClientWebSocket implements WebSocketListener { - private static final boolean DEBUG = false; - private static final Logger LOG = LoggerFactory.getLogger("websocket-client-qc"); - - private Session outbound = null; - private QCServer server = null; - - private static class RequestMsg { - public String request; - public String channel; - public String data; - } - - public QCClientWebSocket(QCServer server) { - this.server = server; - } - - @Override - public void onWebSocketBinary(byte[] payload, int offset, int len) - { - /* not interested */ - } - - @Override - public void onWebSocketClose(int statusCode, String reason) - { - LOG.info("WebSocket to " + server.name + " closed"); - this.outbound = null; - server.removeRtWebSocket(); - } - - @Override - public void onWebSocketConnect(Session session) { - this.outbound = session; - LOG.info("WebSocket to " + server.name + " established"); - - RequestMsg req = new RequestMsg(); - req.request = "subscribe"; - req.channel = "runningQueries"; - - Gson gson = new Gson(); - String msg = gson.toJson(req); - - sendMessage(msg); - } - - @Override - public void onWebSocketError(Throwable cause) { - LOG.error("websocket error", cause); - if (this.outbound == null) { - // connection error. server's websocket should be cleared. - server.removeRtWebSocket(); - } - } - - public static final String evtStrQueryAdded = "runningQueryAdded"; - public static final String evtStrQueryUpdated = "runningQueryUpdated"; - public static final String evtStrQueryRemoved = "runningQueryRemoved"; - public static final String evtStrResult = "result"; - public static final String evtStrPong = "pong"; - private static class RunningQueryEvent { - public String msgType = null; - public String result = null; - public QCQuery.QueryImport query = null; - public String queryId = null; - } - - @Override - public void onWebSocketText(String message) { - Gson gson = new Gson(); - RunningQueryEvent evt = null; - try { - evt = gson.fromJson(message, RunningQueryEvent.class); - } catch (Exception e) { - LOG.error("Gson exception :", message); - } - - if (evt == null || evt.msgType == null) { - LOG.error("invalid event object received.", message); - return; - } - - QCQuery q; - switch (evt.msgType) { - case evtStrQueryAdded: - case evtStrQueryUpdated: - if (evt.query != null) { - LOG.debug("new RT query"); - q = new QCQuery(server, evt.query); - server.processAddQueryEvent(q); - } - break; - case evtStrQueryRemoved: - if (evt.query != null) { - LOG.debug("removing RT query"); - q = new QCQuery(server, evt.query); - server.processRemoveQueryEvent(q); - } - break; - case evtStrResult: - LOG.info("result " + evt.result); - break; - case evtStrPong: - LOG.debug("pong!!"); - break; - default: - LOG.warn("ignoring message", message); - break; - } - } - - public void sendMessage(String message) { - if (outbound == null || !outbound.isOpen()) - return; - outbound.getRemote().sendString(message, null); - } - - public void ping() { - Gson gson = new Gson(); - RequestMsg msg = new RequestMsg(); - msg.request = "ping"; - sendMessage(gson.toJson(msg)); - } -} diff --git a/src/main/java/com/skplanet/checkmate/querycache/QCCluster.java b/src/main/java/com/skplanet/checkmate/querycache/QCCluster.java index 8caaebb..5b38267 100644 --- a/src/main/java/com/skplanet/checkmate/querycache/QCCluster.java +++ b/src/main/java/com/skplanet/checkmate/querycache/QCCluster.java @@ -1,370 +1,273 @@ package com.skplanet.checkmate.querycache; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.codahale.metrics.Histogram; import com.codahale.metrics.SlidingTimeWindowReservoir; import com.google.gson.Gson; import com.skplanet.checkmate.CheckMateServer; +import com.skplanet.checkmate.querycache.data.ClusterServerPoolInfo; +import com.skplanet.checkmate.querycache.data.ClusterServerSysStats; +import com.skplanet.checkmate.querycache.data.QCQueryEvent; +import com.skplanet.checkmate.querycache.data.ServerObjectPool; import com.skplanet.checkmate.servlet.CMQCServerWebSocket; -import com.skplanet.checkmate.utils.MailSender; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.*; -import java.util.*; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; /** * Created by nazgul33 on 15. 1. 27. */ public class QCCluster { - private static final boolean DEBUG = false; - private static final Logger LOG = LoggerFactory.getLogger("querycache"); - private static final int MAX_COMPLETE_QUERIES = 100; - private static final String RQE_ADDED = "runningQueryAdded"; - private static final String RQE_UPDATED = "runningQueryUpdated"; - private static final String RQE_REMOVED = "runningQueryRemoved"; + + private static final Logger LOG = LoggerFactory.getLogger(QCCluster.class); + + private String name; + private QCClusterOptions opt; + + private Map serverMap = new HashMap<>(); + + private Timer notifyTimer = new Timer(true); + private Histogram histogram; + + private long fullUpdateInterval; + private Timer updateTimer = new Timer(true); + +// private static final long longRunningLimit = 900L * 1000L; // 900 sec - static public class Options { - public int webPort = 8080; - public int partialUpdateInterval = 3000; - public int fullUpdateInterval = 10000; - public String[] servers; - } - - public final String name; - private Map servers; - private Options opt; - - private Timer notifyTimer = new Timer(); - Histogram histogram; + private final static long retentionTime = 200; + private final LinkedList evtQueueList = new LinkedList<>(); + + private Map realtimeMessageReceiverMap = new HashMap<>(); + private AtomicInteger realtimeMessageReceiversId = new AtomicInteger(0); - public QCCluster(String name, Options opt) { + public QCCluster(String name, QCClusterOptions opt, long fullUpdateInterval) throws URISyntaxException { + this.name = name; this.opt = opt; - - servers = new HashMap<>(); - for (String serverName : opt.servers) { - if (servers.containsKey(serverName)) { - System.console().printf("duplicate server name %s\n", serverName); - System.exit(1); + this.fullUpdateInterval = fullUpdateInterval; + + for (String serverName : opt.getServers()) { + if (serverMap.containsKey(serverName)) { + LOG.warn("duplicate server name {}", serverName); + } else { + serverMap.put(serverName, new QCServer(this, serverName)); } - servers.put(serverName, new QCServer(serverName, this)); } - - TimerTask wsNotifyTask = new TimerTask() { - @Override - public void run() { - notifySubscribers(); - } - }; - notifyTimer.scheduleAtFixedRate(wsNotifyTask, 0, 50); - notifyTimer.scheduleAtFixedRate(queryAnomalyDetectorTask, 5000L, 5000L); - histogram = new Histogram( new SlidingTimeWindowReservoir(5L, TimeUnit.MINUTES) ); + CheckMateServer.getMetrics().register("queries."+name, histogram); } - public void updateCounters() { - int count = 0; - for (Map.Entry entry : servers.entrySet()) { - count += entry.getValue().updateQueryCount(); - } - histogram.update(count); - } - public Map getServers() { - return servers; - } + public String getName() { + return name; + } public QCServer getServer(String name) { - return servers.get(name); - } - - public void Update() { - // update all servers - Date start = new Date(); - for (Map.Entry entry : servers.entrySet()) { - QCServer server = entry.getValue(); - server.Update(); - - // update sysinfo - QCServer.SysInfoCollection sysInfo = server.getSysInfoCollection(); - if (sysInfo != null) { - SystemInfo mySysInfo = sysInfoMap.get(server.name); - if ( mySysInfo == null) { - mySysInfo = new SystemInfo(); - mySysInfo.server = server.name; - sysInfoMap.put(server.name, mySysInfo); - } - mySysInfo.jvm = sysInfo.jvm; - mySysInfo.system = sysInfo.system; - mySysInfo.threads = sysInfo.threads; - } - - QCServer.ObjectPool objPoolInfo = server.getObjectPool(); - Collection connPoolInfo = server.getConnections(); - if (objPoolInfo != null && connPoolInfo != null) { - PoolInfo myPoolInfo = poolInfoMap.get(server.name); - if ( myPoolInfo == null) { - myPoolInfo = new PoolInfo(); - myPoolInfo.server = server.name; - poolInfoMap.put(server.name, myPoolInfo); - } - myPoolInfo.objPool = objPoolInfo; - myPoolInfo.connPoolList = connPoolInfo; - } - } - Date end = new Date(); - if (DEBUG) { - LOG.debug("Updating cluster " + name + " took " + (end.getTime() - start.getTime()) + " ms"); - } - - refreshExportedCompleteQueries(); + return serverMap.get(name); } - - public Options getOpt() { + + public QCClusterOptions getOptions() { return opt; } - - MailSender mailSender = CheckMateServer.getInstance().getMailSender(); - private class EventAnomalyDetector { - private static final long longRunningLimit = 900L * 1000L; // 900 sec - boolean longRunningQuery = false; - QCQuery.QueryExport query = null; - - void detect() { - long now = System.currentTimeMillis(); - if ( query == null ) - return; - - if (!longRunningQuery) { - if (query.startTime + longRunningLimit < now && - ("EXEC".equals(query.state) || "INIT".equals(query.state))) { - longRunningQuery = true; - - LOG.info("Slow query {}:{}", query.backend, query.id); - if (mailSender != null) mailSender.addMail( - new QCQueryMail(query, "Slow Query by " + query.user, "Slow query detected by CheckMate.") - ); - } - } - } + + public void start() { + + for (QCServer server:serverMap.values()) { + server.start(); + } + + notifyTimer.schedule(new TimerTask() { + // CheckMate 의 websocket 에 연결된 client 에게 실행중인 쿼리를 발송하는 타이머 + @Override + public void run() { + notifySubscribers(); + } + }, 0, 50); + +// notifyTimer.schedule(new TimerTask() { +// @Override +// public void run() { +// List list = new ArrayList<>(); +// synchronized (runningQueriesMap) { +// for (QCQuery query:runningQueriesMap.values()) { +// if (isAnomalQuery(query)) { +// list.add(query); +// } +// } +// } +// processAnomalQuery(list); +// } +// }, 5000L, 5000L); + + updateTimer.schedule(new TimerTask() { + @Override + public void run() { + updateApi(); + } + }, 0, fullUpdateInterval); + + updateTimer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + updateCounters(); + } + }, 60*1000, 60*1000); } - - private final HashMap detectorMap = new HashMap<>(); - - TimerTask queryAnomalyDetectorTask = new TimerTask() { - @Override - public void run() { - Map dMap; - synchronized (detectorMap) { - dMap = new HashMap<>(detectorMap); - } - for ( Map.Entry entry : dMap.entrySet() ) { - entry.getValue().detect(); - } - } - }; - - private static class RunningQueryEvent { - public long timestamp = System.currentTimeMillis(); - public String msgType = null; - public QCQuery.QueryExport query = null; - public String cuqId = null; - } - - private final Map exportedRunningQueriesMap = new HashMap<>(); - public QCQuery.QueryExport getExportedRunningQueryByCuqId(String cuqId) { - QCQuery.QueryExport q; - synchronized (exportedRunningQueriesMap) { - q = exportedRunningQueriesMap.get(cuqId); - } - return q; + + public void close() { + updateTimer.cancel(); + + for (QCServer server:serverMap.values()) { + server.stop(); + } } - - public void addExportedRunningQuery(QCQuery q) { - QCQuery.QueryExport eq = q.export(); - boolean queryUpdated; - synchronized (exportedRunningQueriesMap) { - queryUpdated = (exportedRunningQueriesMap.put(eq.cuqId, eq) != null); - } - synchronized (detectorMap) { - EventAnomalyDetector det = detectorMap.get(eq.cuqId); - if (det != null) - det.query = eq; - else { - det = new EventAnomalyDetector(); - det.query = eq; - detectorMap.put(eq.cuqId, det); - } + + private void updateCounters() { + int count = 0; + for (QCServer server:serverMap.values()) { + count += server.getAndResetQueryCount(); } - - RunningQueryEvent evt = new RunningQueryEvent(); - evt.msgType = queryUpdated? RQE_UPDATED:RQE_ADDED; - evt.query = eq; - evt.cuqId = eq.cuqId; - addRunningQueryEvent(evt); + histogram.update(count); } - - public void removeExportedRunningQuery(QCQuery q) { - synchronized (exportedRunningQueriesMap) { - exportedRunningQueriesMap.remove(q.cuqId); - } - synchronized (detectorMap) { - detectorMap.remove(q.cuqId); + + private void updateApi() { + // update all servers + for (QCServer server:serverMap.values()) { + server.updateApi(); } - QCQuery.QueryExport eq = q.export(); - RunningQueryEvent evt = new RunningQueryEvent(); - evt.msgType = RQE_REMOVED; - evt.query = eq; - evt.cuqId = eq.cuqId; - addRunningQueryEvent(evt); } - - public Collection getExportedRunningQueries() { - List ql = new ArrayList<>(); - synchronized (exportedRunningQueriesMap) { - ql.addAll(exportedRunningQueriesMap.values()); - } - Collections.sort(ql, new Comparator() { + + public List getServerInfo() { + List list = new ArrayList<>(serverMap.size()); + for (QCServer server:serverMap.values()) { + list.add(new ClusterServerSysStats(server.getName(), server.getSystemStats())); + } + Collections.sort(list, new Comparator() { @Override - public int compare(QCQuery.QueryExport o1, QCQuery.QueryExport o2) { - return ((o1.startTime - o2.startTime) < 0) ? -1 : (o1.startTime == o2.startTime) ? 0 : 1; + public int compare(ClusterServerSysStats o1, ClusterServerSysStats o2) { + return o1.getServer().compareTo(o2.getServer()); } }); - return ql; + return list; } - // complete query update accelerators - private final List tmpCompleteQueries = new ArrayList<>(); - void queueCompleteQuery(QCQuery q) { - synchronized (tmpCompleteQueries) { - tmpCompleteQueries.add(q); - } - } - - private final LinkedList exportedCompleteQueries = new LinkedList<>(); - private void refreshExportedCompleteQueries() { - synchronized (tmpCompleteQueries) { - Collections.sort(tmpCompleteQueries, new Comparator() { - @Override - public int compare(QCQuery o1, QCQuery o2) { - return (o2.endTime > o1.endTime) ? 1 : (o2.endTime == o1.endTime) ? 0 : -1; - } - }); - - synchronized (exportedCompleteQueries) { - for (QCQuery q: tmpCompleteQueries) { - // addFirst() to make most recent query comes first. - exportedCompleteQueries.addFirst(q.export()); - } - - // maintain size of exportedCompleteQueries - int toRemove = exportedCompleteQueries.size() - MAX_COMPLETE_QUERIES; - if (toRemove > 0) { - for (int i=0; i getPoolInfo() { + List list = new ArrayList<>(serverMap.size()); + for (QCServer server:serverMap.values()) { + list.add(new ClusterServerPoolInfo(server.getName(), + server.getObjectPool(), server.getConnDescList())); + } + Collections.sort(list, new Comparator() { + @Override + public int compare(ClusterServerPoolInfo o1, ClusterServerPoolInfo o2) { + return o1.getServer().compareTo(o2.getServer()); } - tmpCompleteQueries.clear(); - } + }); + return list; } - // TODO: getting subset of complete queries - public Collection getExportedCompleteQueries() { - List ql = new ArrayList<>(); - synchronized (exportedCompleteQueries) { - ql.addAll(exportedCompleteQueries); + // Web UI + public QCQuery getRunningQueryByCuqId(String cuqId) { + for (QCServer server:serverMap.values()) { + return server.getRunningQueryByCuqId(cuqId); } - return ql; - } - - static public class SystemInfo { - public String server; - public QCServer.RuntimeInfo jvm; - public QCServer.SystemInfo system; - public QCServer.ThreadInfo threads; + return null; } - private final Map sysInfoMap = new HashMap<>(); - public Collection getSystemInfo() { - List l = new ArrayList<>(); - synchronized (sysInfoMap) { - l.addAll(sysInfoMap.values()); + + // Web UI + public List getRunningQueries() { + List list = new ArrayList<>(); + for (QCServer server:serverMap.values()) { + list.addAll(server.getRunningQueryList()); } - - Collections.sort(l, new Comparator() { + Collections.sort(list, new Comparator() { @Override - public int compare(SystemInfo o1, SystemInfo o2) { - return o1.server.compareTo(o2.server); + public int compare(QCQuery o1, QCQuery o2) { + return Long.compare(o2.getStartTime(), o1.getStartTime()); } }); - return l; + return list; } - - static public class PoolInfo { - public String server; - public QCServer.ObjectPool objPool; - public Collection connPoolList; - } - - private final Map poolInfoMap = new HashMap<>(); - public Collection getPoolInfo() { - List l = new ArrayList<>(); - synchronized (poolInfoMap) { - l.addAll(poolInfoMap.values()); + + public List getCompleteQueries() { + List list = new ArrayList<>(); + for (QCServer server:serverMap.values()) { + list.addAll(server.getCompleteQueryList()); } - Collections.sort(l, new Comparator() { + Collections.sort(list, new Comparator() { @Override - public int compare(PoolInfo o1, PoolInfo o2) { - return o1.server.compareTo(o2.server); + public int compare(QCQuery o1, QCQuery o2) { + return Long.compare(o2.getStartTime(), o1.getStartTime()); } }); - return l; - } - - private Map realtimeMessageReceivers = new HashMap<>(); - AtomicInteger realtimeMessageReceiversId = new AtomicInteger(0); - - public int subscribe(CMQCServerWebSocket ws) { - int id = realtimeMessageReceiversId.getAndAdd(1); - realtimeMessageReceivers.put(id, ws); - return id; + return list; } - - public void unSubscribe(int id) { - realtimeMessageReceivers.remove(id); - } - - private final static long retentionTime = 200; - private final LinkedList evtQueue = new LinkedList<>(); - private void addRunningQueryEvent(RunningQueryEvent evt) { - synchronized (evtQueue) { - if (RQE_REMOVED.equals(evt.msgType)) { - Iterator it = evtQueue.iterator(); + + public void addQueryEvent(String type, QCQuery query) { + + QCQueryEvent evt = new QCQueryEvent(type, query); + + synchronized (evtQueueList) { + if (QCQueryEvent.RUN_QUERY_REMOVED.equals(evt.getMsgType())) { + Iterator it = evtQueueList.iterator(); while (it.hasNext()) { - RunningQueryEvent rqe = it.next(); - String cuqId = (rqe.cuqId != null)? rqe.cuqId:rqe.query.cuqId; - if (evt.cuqId.equals(cuqId)) { + QCQueryEvent rqe = it.next(); + if (evt.getCuqId().equals(rqe.getCuqId())) { it.remove(); } } } - - evtQueue.addLast(evt); + evtQueueList.addLast(evt); } } - public void notifySubscribers() { + + /** + * CheckMate 의 websocket 에 연결된 client 등록 + * @param ws + * @return + */ + public int subscribe(CMQCServerWebSocket ws) { + int id = realtimeMessageReceiversId.getAndAdd(1); + realtimeMessageReceiverMap.put(id, ws); + return id; + } + + /** + * CheckMate 의 websocket 연결이 끊기면 client 삭제 + * @param id + */ + public void unsubscribe(int id) { + realtimeMessageReceiverMap.remove(id); + } + + /** + * CheckMate 의 websocket 에 연결한 client running query event 전송 + */ + private void notifySubscribers() { + long retainAfter = System.currentTimeMillis() - retentionTime; // send events older than 200ms - LinkedList el = new LinkedList<>(); - synchronized (evtQueue) { - Iterator it = evtQueue.iterator(); + List el = new ArrayList<>(); + synchronized (evtQueueList) { + Iterator it = evtQueueList.iterator(); while (it.hasNext()) { - RunningQueryEvent evt = it.next(); - if (evt.timestamp < retainAfter) { - el.addLast(evt); + QCQueryEvent evt = it.next(); + if (evt.getTimestamp() < retainAfter) { + el.add(evt); it.remove(); } } @@ -372,13 +275,27 @@ public void notifySubscribers() { if (el.size()>0) { Gson gson = new Gson(); - for (RunningQueryEvent evt: el) { + for (QCQueryEvent evt: el) { String msg = gson.toJson(evt); - for (CMQCServerWebSocket ws : realtimeMessageReceivers.values()) { + for (CMQCServerWebSocket ws : realtimeMessageReceiverMap.values()) { ws.sendMessage(msg); } } - el.clear(); } } + +// private boolean isAnomalQuery(QCQuery query) { +// return query.isAnormal() == false && +// (query.getStartTime() + longRunningLimit) < System.currentTimeMillis() && +// ("EXEC".equals(query.getState()) || "INIT".equals(query.getState()) ); +// } +// +// private void processAnomalQuery(List list) { +// for (QCQuery query:list) { +// query.setAnormal(true); +// LOG.info("Slow query {}:{}", query.getBackend(), query.getId()); +// MailSender.send( +// new QCQueryMail(query, "[QC] Slow Query by "+ query.getUser(), "Slow query detected by CheckMate.")); +// } +// } } diff --git a/src/main/java/com/skplanet/checkmate/querycache/QCClusterManager.java b/src/main/java/com/skplanet/checkmate/querycache/QCClusterManager.java index 777bdd0..7d8a7e6 100644 --- a/src/main/java/com/skplanet/checkmate/querycache/QCClusterManager.java +++ b/src/main/java/com/skplanet/checkmate/querycache/QCClusterManager.java @@ -1,193 +1,88 @@ package com.skplanet.checkmate.querycache; +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.HierarchicalINIConfiguration; import org.apache.commons.configuration.SubnodeConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; - /** * Created by nazgul33 on 15. 1. 27. */ public class QCClusterManager { - private static final Logger LOG = LoggerFactory.getLogger("querycache"); - private static QCClusterManager qcMgr = null; - - private Map clusterConfigMap = new HashMap<>(); - - private int fullUpdateInterval = 10 * 1000; - private int pingWebSocketsInterval = 5 * 1000; - - private void readConfiguration() { - String home = System.getenv("CM_HOME"); - if (home == null) { - home = "./"; - } - String conf = home + "/conf/querycache.ini"; - HierarchicalINIConfiguration clusterConfigFile = null; - String[] clusterNames; - QCCluster.Options options[]; - + + private static final Logger LOG = LoggerFactory.getLogger(QCClusterManager.class); + + private Map clusterMap = new HashMap<>(); + + public QCClusterManager(File confDir) throws Exception { + + File iniFile = new File(confDir, "querycache.ini"); + HierarchicalINIConfiguration ini = null; + + Map optionMap = new HashMap<>(); + int fullUpdateInterval; try { - LOG.info("reading checkmate::querycache configuration from " + conf); - clusterConfigFile = new HierarchicalINIConfiguration(conf); - SubnodeConfiguration globalSection = clusterConfigFile.getSection(null); + LOG.info("reading checkmate::querycache configuration from " + iniFile); + ini = new HierarchicalINIConfiguration(iniFile); + + SubnodeConfiguration globalSection = ini.getSection("global"); globalSection.setThrowExceptionOnMissing(true); - clusterNames = globalSection.getStringArray("Clusters"); - fullUpdateInterval = globalSection.getInt("FullUpdateInterval"); - - for (String cn: clusterNames) { - SubnodeConfiguration sc = clusterConfigFile.getSection(cn); - QCCluster.Options opt = new QCCluster.Options(); - opt.webPort = sc.getInt("WebPort"); - opt.servers = sc.getStringArray("Servers"); - - if (clusterConfigFile.containsKey(cn)) { + + String[] cnames = globalSection.getStringArray("Clusters"); + for (String cname: cnames) { + + SubnodeConfiguration sc = ini.getSection(cname); + QCClusterOptions opt = new QCClusterOptions(); + opt.setWebPort(sc.getInt("WebPort")); + opt.setServers(sc.getStringArray("Servers")); + opt.setServerMaxCompleteQueries(sc.getInt("MaxCompleteQueries", 100)); + + if (ini.containsKey(cname)) { throw new ConfigurationException("duplicate cluster definition"); } - clusterConfigMap.put(cn, opt); + optionMap.put(cname, opt); } + fullUpdateInterval = globalSection.getInt("FullUpdateInterval", 10 * 1000); } catch (Exception e) { - LOG.error("error loading configuration.", e); - System.exit(1); - } - } - - public class QCClusterThread implements Runnable { - private class QCClusterFullUpdateTask extends TimerTask { - @Override - public void run() { - if ( !QCClusterThread.this.fullUpdateTaskRunning.compareAndSet(false, true) ) { - // update task is running. skip this time.... - LOG.warn("a full update task is running. skipping full update task"); - return; - } - - QCClusterManager.qcMgr.UpdateClusters(); - lastFullUpdate = new Date(); - QCClusterThread.this.fullUpdateTaskRunning.set(false); - } + throw new Exception("error loading configuration. "+iniFile, e); } - private class QCClusterWebSocketPingTask extends TimerTask { - @Override - public void run() { - QCClusterManager.qcMgr.pingWebSockets(); - } - } - - private class QCCounterUpdateTask extends TimerTask { - @Override - public void run() { - for (QCCluster cluster: clusters) { - cluster.updateCounters(); - } - } - } - TimerTask qcTimerFull = new QCClusterFullUpdateTask(); - TimerTask qcTimerPingWebSockets = new QCClusterWebSocketPingTask(); - TimerTask qcCounterUpdateTask = new QCCounterUpdateTask(); - - AtomicBoolean fullUpdateTaskRunning = new AtomicBoolean(false); - Date lastFullUpdate = new Date(); - - @Override - public void run() { - Timer timerFull = new Timer(true); - Timer timerPingWebSockets = new Timer(true); - Timer timerCounterUpdateTask = new Timer(true); - - timerFull.scheduleAtFixedRate(qcTimerFull, 0, fullUpdateInterval); - timerPingWebSockets.scheduleAtFixedRate(qcTimerPingWebSockets, 0, pingWebSocketsInterval); - timerCounterUpdateTask.scheduleAtFixedRate(qcCounterUpdateTask, 60*1000, 60*1000); - while (!QCClusterManager.this.quitThread) { - try { - Thread.sleep(100); - } catch (Exception e) { - } - } - - timerFull.cancel(); - timerPingWebSockets.cancel(); - } - } - - public static QCClusterManager getInstance() { - if (qcMgr == null) { - qcMgr = new QCClusterManager(); - } - - return qcMgr; - } - - private List clusters = new ArrayList<>(); - private Thread qcClusterThread = new Thread(this.new QCClusterThread()); - private boolean quitThread = false; - - protected QCClusterManager() { - readConfiguration(); - // initialize clusters; - for (Map.Entry entry: clusterConfigMap.entrySet()) { - QCCluster cluster = new QCCluster(entry.getKey(), entry.getValue()); - clusters.add(cluster); + for (String cname:optionMap.keySet()) { + QCCluster cluster = new QCCluster(cname, optionMap.get(cname), fullUpdateInterval); + clusterMap.put(cname, cluster); } - - // initialize thread; - qcClusterThread = new Thread(this.new QCClusterThread()); } - public void startThread() { - qcClusterThread.start(); + public void start() { + for (QCCluster cluster:clusterMap.values()) { + cluster.start(); + } } // stop - public void finishThread() { - quitThread = true; - try { - qcClusterThread.join(); - } catch (Exception e) { - LOG.error("exception while waiting for thread " + e.toString()); - } + public void stop() { + for (QCCluster cluster:clusterMap.values()) { + cluster.close(); + } } - public void UpdateClusters() { - for (QCCluster cluster: clusters) { - cluster.Update(); - } - } - - public QCCluster getCluster(String clusterName) { - for (QCCluster c: clusters) { - if (c.name.equals(clusterName)) { - return c; - } - } - return null; - } - - public Collection getClusterList() { - List cl = new ArrayList<>(); - for (QCCluster c: clusters) { - cl.add(c.name); - } - return cl; + + public QCCluster getCluster(String name) { + return clusterMap.get(name); } - private void pingWebSockets() { - for (QCCluster cluster: clusters) { - for (QCServer server: cluster.getServers().values()) { - QCClientWebSocket ws = server.getRtWebSocket(); - if (ws == null) { - server.connectRealtimeWebSocket(); - } - else { - ws.ping(); - } - } - } + public List getClusterNameList() { + List list = new ArrayList<>(clusterMap.keySet()); + Collections.sort(list); + return list; } } diff --git a/src/main/java/com/skplanet/checkmate/querycache/QCClusterOptions.java b/src/main/java/com/skplanet/checkmate/querycache/QCClusterOptions.java new file mode 100644 index 0000000..3d4ebb1 --- /dev/null +++ b/src/main/java/com/skplanet/checkmate/querycache/QCClusterOptions.java @@ -0,0 +1,41 @@ +package com.skplanet.checkmate.querycache; + +public class QCClusterOptions { + private int webPort = 8080; + private int partialUpdateInterval = 3000; + private int fullUpdateInterval = 10000; + private int serverMaxCompleteQueries = 100; + + private String[] servers; + + public int getWebPort() { + return webPort; + } + public void setWebPort(int webPort) { + this.webPort = webPort; + } + public int getPartialUpdateInterval() { + return partialUpdateInterval; + } + public void setPartialUpdateInterval(int partialUpdateInterval) { + this.partialUpdateInterval = partialUpdateInterval; + } + public int getFullUpdateInterval() { + return fullUpdateInterval; + } + public void setFullUpdateInterval(int fullUpdateInterval) { + this.fullUpdateInterval = fullUpdateInterval; + } + public String[] getServers() { + return servers; + } + public void setServers(String[] servers) { + this.servers = servers; + } + public int getServerMaxCompleteQueries() { + return serverMaxCompleteQueries; + } + public void setServerMaxCompleteQueries(int serverMaxCompleteQueries) { + this.serverMaxCompleteQueries = serverMaxCompleteQueries; + } +} \ No newline at end of file diff --git a/src/main/java/com/skplanet/checkmate/querycache/QCQuery.java b/src/main/java/com/skplanet/checkmate/querycache/QCQuery.java index 3c9aa95..8cf2a58 100644 --- a/src/main/java/com/skplanet/checkmate/querycache/QCQuery.java +++ b/src/main/java/com/skplanet/checkmate/querycache/QCQuery.java @@ -1,169 +1,302 @@ package com.skplanet.checkmate.querycache; +import java.text.SimpleDateFormat; +import java.util.Date; + import org.apache.commons.codec.binary.Base64; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.util.Arrays; -import java.util.Date; +import com.skplanet.checkmate.querycache.data.QCQueryImport; /** * Created by nazgul33 on 15. 1. 27. */ public class QCQuery { - private static final boolean DEBUG = false; - public static class QueryImport { - public String queryId; - public String connType; - public String user; - public String queryType; - public String queryStr; - public String clientIp; - public String stmtState; - public long rowCnt; - public long startTime; - public long endTime; - // 0:exec/1:getmeta/2:fetch/3:stmtclose or - // 1:getschemas/2:fetch - public long[] timeHistogram = {0,0,0,0}; - public long[] execProfile = null; - public long[] fetchProfile = {0,0,0,-1,-1,-1,-1,0,0,-1,-1}; - } - public static class QueryExport { - public String cuqId; // cluster unique query id - public String server; - public String id; - public String backend; - public String user; - public String type; - public String statement; - public String client; - public String state; - public long rowCnt = -1; - public long startTime; - public long endTime = 0; - // 0:exec/1:getmeta/2:fetch/3:stmtclose or - // 1:getschemas/2:fetch - public long[] timeHistogram; - public long[] execProfile; - public long[] fetchProfile; - public String cancelUrl; - } - - private QCServer server; - - public final String cuqId; - public final String id; - public final String backend; - public final String user; - public String type; - public final String statement; - public final String client; - public String state; - public long rowCnt = -1; - public final long startTime; - public long endTime = 0; + private static final Logger LOG = LoggerFactory.getLogger(QCQuery.class); + + private String cluster; + private String server; + private String id; + private String backend; + private String user; + private String type; + private String statement; + private String client; + private String state; + private long rowCnt = -1; + private long startTime; + private long endTime = 0; // 0:exec/1:getmeta/2:fetch/3:stmtclose or // 1:getschemas/2:fetch - public long[] timeHistogram; - public long[] execProfile; - public long[] fetchProfile; - - public long updateTime = 0; - - public QCQuery(QCServer server, QueryImport qObj) { - this.server = server; - this.id = qObj.queryId; - this.backend = qObj.connType; - this.user = qObj.user; - this.type = qObj.queryType; - this.statement = qObj.queryStr; - this.client = qObj.clientIp; - this.state = qObj.stmtState; - this.rowCnt = qObj.rowCnt; - this.startTime = qObj.startTime; - this.endTime = qObj.endTime; - this.timeHistogram = qObj.timeHistogram; - this.execProfile = qObj.execProfile; - this.fetchProfile = qObj.fetchProfile; - - this.updateTime = new Date().getTime(); - - this.cuqId = getCuqId(server, qObj); - } + private long[] timeHistogram; + private long[] execProfile; + private long[] fetchProfile; + + private String cuqId; + private String cancelUrl; + + private long updateTime; + private long updateCount; + private boolean anormal = false; + + public QCQuery(String cluster, String server, QCQueryImport qObj) { + + this.cluster = cluster; + this.server = server; + this.id = qObj.getQueryId(); + this.backend = qObj.getConnType(); + this.user = qObj.getUser(); + this.type = qObj.getQueryType(); + this.statement = qObj.getQueryStr(); + this.client = qObj.getClientIp(); + this.state = qObj.getStmtState(); + this.rowCnt = qObj.getRowCnt(); + this.startTime = qObj.getStartTime(); + this.endTime = qObj.getEndTime(); + this.timeHistogram = qObj.getTimeHistogram(); + this.execProfile = qObj.getExecProfile(); + this.fetchProfile = qObj.getFetchProfile(); + + this.cuqId = getCuqId(cluster, server, qObj); + this.cancelUrl = "/api/qc/cancelQuery?cluster="+cluster+"&cuqId="+cuqId; + + this.updateCount = 0; + this.updateTime = System.currentTimeMillis(); +} + + public String getCluster() { + return cluster; + } + + public void setCluster(String cluster) { + this.cluster = cluster; + } + + public String getServer() { + return server; + } + + public void setServer(String server) { + this.server = server; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getBackend() { + return backend; + } + + public void setBackend(String backend) { + this.backend = backend; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getStatement() { + return statement; + } + + public void setStatement(String statement) { + this.statement = statement; + } + + public String getClient() { + return client; + } + + public void setClient(String client) { + this.client = client; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } - public boolean Update(QCQuery qObj) { + public long getRowCnt() { + return rowCnt; + } + + public void setRowCnt(long rowCnt) { + this.rowCnt = rowCnt; + } + + public long getStartTime() { + return startTime; + } + + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + public long getEndTime() { + return endTime; + } + + public void setEndTime(long endTime) { + this.endTime = endTime; + } + + public long[] getTimeHistogram() { + return timeHistogram; + } + + public void setTimeHistogram(long[] timeHistogram) { + this.timeHistogram = timeHistogram; + } + + public long[] getExecProfile() { + return execProfile; + } + + public void setExecProfile(long[] execProfile) { + this.execProfile = execProfile; + } + + public long[] getFetchProfile() { + return fetchProfile; + } + + public void setFetchProfile(long[] fetchProfile) { + this.fetchProfile = fetchProfile; + } + + public long getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(long updateTime) { + this.updateTime = updateTime; + } + + public long getUpdateCount() { + return updateCount; + } + + public void setUpdateCount(long updateCount) { + this.updateCount = updateCount; + } + + public String getCuqId() { + return cuqId; + } + + public void setCuqId(String cuqId) { + this.cuqId = cuqId; + } + + public String getCancelUrl() { + return cancelUrl; + } + + public void setCancelUrl(String cancelUrl) { + this.cancelUrl = cancelUrl; + } + + public boolean isAnormal() { + return anormal; + } + + public void setAnormal(boolean anormal) { + this.anormal = anormal; + } + + public static boolean update(QCQuery oq, QCQuery nq) { + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); boolean updated = false; - if ( !this.type.equals(qObj.type) ) { - if (DEBUG) System.console().printf("query %s update type %s -> %s\n", id, type, qObj.type); - this.type = qObj.type; + if (!oq.getType().equals(nq.getType())) { + LOG.debug("query {} update type {} -> {}", oq.getId(), oq.getType(), nq.getType()); + oq.setType(nq.getType()); updated = true; } - if ( !this.state.equals(qObj.state) ) { - if (DEBUG) System.console().printf("query %s update state %s -> %s\n", id, state, qObj.state); - this.state = qObj.state; + if (!oq.getState().equals(nq.getState())) { + LOG.debug("query {} update state {} -> {}", oq.getId(), oq.getState(), nq.getState()); + oq.setState(nq.getState()); updated = true; } - if ( this.rowCnt != qObj.rowCnt ) { - if (DEBUG) System.console().printf("query %s update rowCnt %s -> %s\n", id, rowCnt, qObj.rowCnt); - this.rowCnt = qObj.rowCnt; + if (oq.getRowCnt() != nq.getRowCnt() ) { + LOG.debug("query {} update rowCnt {} -> {}", oq.getId(), oq.getRowCnt(), nq.getRowCnt()); + oq.setRowCnt(nq.getRowCnt()); updated = true; } - if ( this.endTime != qObj.endTime ) { - if (DEBUG) System.console().printf("query %s update rowCnt %s -> %s\n", id, endTime, qObj.endTime); - this.endTime = qObj.endTime; + if (oq.getEndTime() != nq.getEndTime()) { + LOG.debug("query {} update endTime {} -> %s", oq.getId(), + sdf.format(new Date(oq.getEndTime())), + sdf.format(new Date(nq.getEndTime()))); + oq.setEndTime(nq.endTime); updated = true; } -// if ( !Arrays.equals(this.timeHistogram, qObj.timeHistogram) ) { -// this.timeHistogram = qObj.timeHistogram; -// updated = true; -// } -// if ( !Arrays.equals(this.execProfile, qObj.execProfile) ) { -// this.execProfile = qObj.execProfile; -// updated = true; -// } -// if ( !Arrays.equals(this.fetchProfile, qObj.fetchProfile) ) { -// this.fetchProfile = qObj.fetchProfile; -// updated = true; -// } - if (updated) { - this.updateTime = new Date().getTime(); + oq.setUpdateTime(System.currentTimeMillis()); + oq.setUpdateCount(oq.getUpdateCount()+1); } return updated; } - public static String getCuqId(QCServer server, QCQuery.QueryImport qi) { - String cuqid = new String(Base64.encodeBase64((server.name+qi.connType+qi.queryId).trim().toUpperCase().getBytes())); + public static String getCuqId(String cluster, String server, QCQueryImport qi) { + String key = server+qi.getConnType()+qi.getQueryId(); + String cuqid = Base64.encodeBase64String(key.trim().getBytes()); return cuqid.replace('+',':').replace('/','.').replace('=','-'); } - public QueryExport export() { - QueryExport eq = new QueryExport(); - eq.cuqId = cuqId; - // remove characters not supported by html4 standard for id field. - - eq.server = server.name; - eq.id = id; - eq.backend = backend; - eq.user = user; - eq.type = type; - eq.statement = statement; - eq.client = client; - eq.state = state; - eq.rowCnt = rowCnt; - eq.startTime = startTime; - eq.endTime = endTime; - eq.timeHistogram = new long[timeHistogram.length]; - System.arraycopy(eq.timeHistogram, 0, timeHistogram, 0, timeHistogram.length); - if (execProfile != null) { - eq.execProfile = new long[execProfile.length]; - System.arraycopy(eq.execProfile, 0, execProfile, 0, execProfile.length); - } - eq.fetchProfile = new long[fetchProfile.length]; - System.arraycopy(eq.fetchProfile, 0, fetchProfile, 0, fetchProfile.length); - // eq.cancelUrl = "http://" + server.name + ":" + server.cluster.getOpt().webPort + "/api/cancelQuery?id="+id+"&driver="+backend; - eq.cancelUrl = "/api/qc/cancelQuery?cluster="+server.cluster.name+"&cuqId="+eq.cuqId; - return eq; - } + public boolean equals(Object o) { + return o instanceof QCQuery && cuqId.equals(((QCQuery)o).getCuqId()); + } + +// public QueryExport export() { +// +// QueryExport qe = new QueryExport(); +// qe.setCuqId(cuqId); +// qe.setServer(server); +// qe.setId(id); +// qe.setBackend(backend); +// qe.setUser(user); +// qe.setType(type); +// qe.setStatement(statement); +// qe.setClient(client); +// qe.setState(state); +// qe.setRowCnt(rowCnt); +// qe.setStartTime(startTime); +// qe.setEndTime(endTime); +// long[] cloneHistogram = new long[timeHistogram.length]; +// System.arraycopy(timeHistogram, 0, cloneHistogram, 0, timeHistogram.length); +// qe.setTimeHistogram(cloneHistogram); +// if (execProfile != null) { +// long[] cloneExec = new long[execProfile.length]; +// System.arraycopy(execProfile, 0, cloneExec, 0, execProfile.length); +// qe.setExecProfile(cloneExec); +// } +// long [] cloneFetch = new long[fetchProfile.length]; +// System.arraycopy(fetchProfile, 0, cloneFetch, 0, fetchProfile.length); +// qe.setFetchProfile(cloneFetch); +// qe.setCancelUrl("/api/qc/cancelQuery?cluster="+cluster+"&cuqId="+cuqId); +// return qe; +// } } diff --git a/src/main/java/com/skplanet/checkmate/querycache/QCQueryMail.java b/src/main/java/com/skplanet/checkmate/querycache/QCQueryMail.java index 6beae7f..cafeb30 100644 --- a/src/main/java/com/skplanet/checkmate/querycache/QCQueryMail.java +++ b/src/main/java/com/skplanet/checkmate/querycache/QCQueryMail.java @@ -1,22 +1,25 @@ package com.skplanet.checkmate.querycache; -import com.skplanet.checkmate.utils.MailSender; -import org.apache.commons.lang.time.DateUtils; - +import java.text.SimpleDateFormat; import java.util.Date; +import com.skplanet.checkmate.utils.MailSender; + /** * Created by nazgul33 on 4/8/16. */ public class QCQueryMail extends MailSender.Mail { - public QCQueryMail(QCQuery.QueryExport query, String subject, String content) { - super(subject, content); - setContent( content + "\n\n" + queryToMailContent(query) ); - } + + public QCQueryMail(QCQuery query, String subject, String content) { + super(subject); + setContent(content + "\n\n" + queryToMailContent(query) ); + } + + public static String queryToMailContent(QCQuery query) { - static String queryToMailContent(QCQuery.QueryExport query) { - StringBuilder sb = new StringBuilder(); - /* + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + StringBuilder sb = new StringBuilder(); + /* public String cuqId; // cluster unique query id public String server; public String id; @@ -35,18 +38,18 @@ static String queryToMailContent(QCQuery.QueryExport query) { public long[] execProfile; public long[] fetchProfile; public String cancelUrl; - */ - sb.append("Server : ").append(query.server).append('\n'); - sb.append("QueryId : ").append(query.id).append('\n'); - sb.append("Backend : ").append(query.backend).append('\n'); - sb.append("User : ").append(query.user).append('\n'); - sb.append("State : ").append(query.state).append('\n'); - sb.append("Fetched Rows : ").append(query.rowCnt).append('\n'); - sb.append("StartTime : ").append( new Date(query.startTime) ).append('\n'); - sb.append("Now : ").append( new Date() ).append('\n'); + */ + sb.append("Server : ").append(query.getServer()).append('\n'); + sb.append("QueryId : ").append(query.getId()).append('\n'); + sb.append("Backend : ").append(query.getBackend()).append('\n'); + sb.append("User : ").append(query.getUser()).append('\n'); + sb.append("State : ").append(query.getState()).append('\n'); + sb.append("Fetched Rows : ").append(query.getRowCnt()).append('\n'); + sb.append("StartTime : ").append( sdf.format(new Date(query.getStartTime())) ).append('\n'); + sb.append("Now : ").append( sdf.format(new Date()) ).append('\n'); - sb.append("SQL : ").append(query.statement).append('\n'); + sb.append("SQL : ").append(query.getStatement()).append('\n'); - return sb.toString(); - } + return sb.toString(); + } } diff --git a/src/main/java/com/skplanet/checkmate/querycache/QCServer.java b/src/main/java/com/skplanet/checkmate/querycache/QCServer.java index 9136a28..8a29110 100644 --- a/src/main/java/com/skplanet/checkmate/querycache/QCServer.java +++ b/src/main/java/com/skplanet/checkmate/querycache/QCServer.java @@ -1,436 +1,356 @@ package com.skplanet.checkmate.querycache; -import com.codahale.metrics.Histogram; -import com.codahale.metrics.Meter; -import com.codahale.metrics.SlidingTimeWindowReservoir; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; -import com.skplanet.checkmate.CheckMateServer; -import com.skplanet.checkmate.utils.HttpUtil; +import static java.lang.String.format; import java.net.ConnectException; -import java.net.URI; import java.net.URISyntaxException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; -import org.eclipse.jetty.websocket.client.WebSocketClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.codahale.metrics.Histogram; +import com.codahale.metrics.SlidingTimeWindowReservoir; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.skplanet.checkmate.CheckMateServer; +import com.skplanet.checkmate.querycache.data.QCQueryEvent; +import com.skplanet.checkmate.querycache.data.QCQueryImport; +import com.skplanet.checkmate.querycache.data.ServerConDesc; +import com.skplanet.checkmate.querycache.data.ServerObjectPool; +import com.skplanet.checkmate.querycache.data.ServerQueries; +import com.skplanet.checkmate.querycache.data.ServerSystemStats; +import com.skplanet.checkmate.utils.HttpUtil; + /** * Created by nazgul33 on 15. 1. 27. */ public class QCServer { - private static final boolean DEBUG = false; - private static final Logger LOG = LoggerFactory.getLogger("querycache"); - private static final int MAX_COMPLETE_QUERIES = 100; - private final Histogram histogram; - - // inner-classes below are for gson conversion from json object. - // NOTICE! - // sync between QCWebApiServlet - public static class ConDesc { - public String driver; - public int free; - public int using; - } - - public static class RuntimeInfo { - public int nProcessors; - public long memFree; - public long memTotal; - public long memMax; - } - - public static class ThreadInfo { - public int webServerThreads; - public int webServerThreadsIdle; - public int handlerThreads; - public int handlerThreadsIdle; - public int totalThreads; - } - - public static class SystemInfo { - public double loadSystem; - public double loadProcess; - public long memPhysFree; - public long memPhysTotal; - public long swapFree; - public long swapTotal; - } - - public static class ObjectPool { - // configuration - public long sReloadCycle = 0; - public int sMaxPoolSize = 0; - public float sReloadingThreshold = 0; - public int sCellCoeff = 0; - public int[] poolSize = {0, 0, 0, 0}; - } - - public static class Queries { - public List runningQueries = new ArrayList<>(); - public List completeQueries = new ArrayList<>(); - } + + private static final Logger LOG = LoggerFactory.getLogger(QCServer.class); + + private int MAX_COMPLETE_QUERIES = 100; + + private Histogram histogram; - public static class SysInfoCollection { - public RuntimeInfo jvm; - public SystemInfo system; - public ThreadInfo threads; - } - - public final String name; - private Map queriesRunning; - private Map queriesComplete; - public final QCCluster cluster; + private String name; + private Map runningMap; + private Map completeMap; + private QCCluster cluster; + private QCClusterOptions clusterOpt; - private List connections = null; - private ObjectPool objectPool = null; - private SysInfoCollection sysInfoCollection = null; + private String wsUrl; + private List conDescList; + private ServerObjectPool objectPool; + private ServerSystemStats systemStats; - private long lastQueryUpdate = 0; + private long lastExceptionTime; - private boolean online; - private String lastException; + private QCWebSocketClient wsClient; private AtomicInteger queryCount = new AtomicInteger(0); - QCServer(String name, QCCluster cluster) { - this.name = name; - queriesRunning = new HashMap<>(); - queriesComplete = new LinkedHashMap() { - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > MAX_COMPLETE_QUERIES; - } - }; - online = false; + private String queriesUrl; + private String systemUrl; + private String connectionUrl; + private String objectpoolUrl; + + public QCServer(QCCluster cluster, String name) throws URISyntaxException { + this.cluster = cluster; + this.clusterOpt = cluster.getOptions(); + this.name = name; - connectRealtimeWebSocket(); - - StringBuffer sb = new StringBuffer(); - sb.append("queries.").append(cluster.name).append('.').append(this.name); + this.MAX_COMPLETE_QUERIES = cluster.getOptions().getServerMaxCompleteQueries(); + + this.runningMap = new HashMap<>(); + this.completeMap = new LimitHashMap<>(MAX_COMPLETE_QUERIES); + + this.wsUrl = format("ws://%s:%s%s", name, clusterOpt.getWebPort(), "/api/websocket"); + this.wsClient = new QCWebSocketClient(this, wsUrl); + + int webPort = cluster.getOptions().getWebPort(); + this.queriesUrl = format("http://%s:%s/api/queries", name, webPort); + this.systemUrl = format("http://%s:%s/api/system", name, webPort); + this.connectionUrl = format("http://%s:%s/api/connections", name, webPort); + this.objectpoolUrl = format("http://%s:%s/api/objectpool", name, webPort); + + String metricName = format("queries.%s.%s",cluster.getName(),name); histogram = new Histogram( new SlidingTimeWindowReservoir(5L, TimeUnit.MINUTES) ); - CheckMateServer.getMetrics().register(sb.toString(), histogram); + CheckMateServer.getMetrics().register(metricName, histogram); } - - public int updateQueryCount() { - int count = queryCount.getAndSet(0); - histogram.update(count); - return count; + + public String getName() { + return name; } - - public boolean isOnline() { - return online; + + public QCCluster getCluster() { + return cluster; } - public void setOnline(boolean online) { - this.online = online; + public void start() { + wsClient.start(); } - - public String getLastException() { - return lastException; + + public void stop() { + wsClient.stop(); } - - public void setLastException(Exception e) { - this.lastException = e.getMessage(); - } - - public Collection getRunningQueries() { - Collection ql; - synchronized (queriesRunning) { - ql = queriesRunning.values(); - } - return ql; - } - - public Collection getCompleteQueries() { - Collection ql; - synchronized (queriesComplete) { - ql = queriesComplete.values(); - } - return ql; - } - - public void Update() { - // update routines below will set offline when update is failed. - this.setOnline(true); - - UpdateQueries(); - lastQueryUpdate = new Date().getTime(); - UpdateConnections(); - UpdateObjectPools(); - UpdateSystem(); - } - - protected String getUrl(String path) { - return "http://" + this.name + ":" + cluster.getOpt().webPort + path; + + public int getAndResetQueryCount() { + int count = queryCount.getAndSet(0); + histogram.update(count); + return count; } - protected void processAddQueryEvent(QCQuery q) { - QCQuery qExisting; - synchronized (queriesRunning) { - qExisting = queriesRunning.get(q.cuqId); - if (qExisting != null) { - if( !qExisting.Update(q) ) { - // not updated : do nothing - return; - } - } else { - this.queriesRunning.put(q.cuqId, q); - queryCount.incrementAndGet(); - } - } - this.cluster.addExportedRunningQuery(q); + private void setLastException() { + this.lastExceptionTime = System.currentTimeMillis(); } - protected void processRemoveQueryEvent(QCQuery q) { - synchronized (queriesRunning) { - this.queriesRunning.remove(q.cuqId); - } - this.cluster.removeExportedRunningQuery(q); + public void updateApi() { + + if (lastExceptionTime > 0 && lastExceptionTime + 60*1000 > System.currentTimeMillis()) { + return; + } + LOG.debug("updateApi {}.{}", cluster.getName(), name); + updateApiQueries(); + updateApiConnections(); + updateApiObjectPools(); + updateApiSystem(); } - protected void UpdateQueries() { - HttpUtil httpUtil = new HttpUtil(); - StringBuffer buf = new StringBuffer(); - int res = 0; - + private void updateApiQueries() { + + long t0 = System.currentTimeMillis(); try { - res = httpUtil.get( getUrl("/api/queries"), buf); - if (res == 200) { - Gson gSon = new Gson(); - Queries queries = gSon.fromJson(buf.toString(), Queries.class); - synchronized (queriesRunning) { - int added = 0; - int removed = 0; - int updated = 0; - List rqList = new ArrayList<>(); - Map rqMap = new HashMap<>(); - // process "current" running query list - for (QCQuery.QueryImport qi : queries.runningQueries) { - // add to map for reverse existence check below. - QCQuery q = new QCQuery(this, qi); - rqList.add(q); - rqMap.put(q.cuqId, q); - } - - // cross check queries in my list to remove "disappeared" queries. - Iterator> it = queriesRunning.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry entry = it.next(); - if (!rqMap.containsKey(entry.getKey())) { - this.cluster.removeExportedRunningQuery(entry.getValue()); - it.remove(); - removed++; - } - } - - for (QCQuery q : rqList) { - QCQuery qExisting = queriesRunning.get(q.cuqId); - if (qExisting != null) { - if ( qExisting.Update(q) ) { - updated++; - this.cluster.addExportedRunningQuery(qExisting); - } - } else { - this.queriesRunning.put(q.cuqId, q); - added++; - this.cluster.addExportedRunningQuery(q); - queryCount.incrementAndGet(); - } - } - - if (DEBUG) { - LOG.debug(this.name + ": running queries +" + added + " #" + updated + " -" + removed); - } - } - - synchronized (queriesComplete) { - int added = 0; - - // process "current" complete query list - // sort key endTime, ascending - Collections.sort(queries.completeQueries, new Comparator() { - @Override - public int compare(QCQuery.QueryImport o1, QCQuery.QueryImport o2) { - return (o2.endTime > o1.endTime) ? 1 : (o2.endTime == o1.endTime) ? 0 : -1; - } - }); - - for (QCQuery.QueryImport qi : queries.completeQueries) { - String cuqId = QCQuery.getCuqId(this, qi); - // complete query has no changes. process new complete queries only. - if (!this.queriesComplete.containsKey(cuqId)) { - QCQuery q = new QCQuery(this, qi); - this.queriesComplete.put(cuqId, q); - cluster.queueCompleteQuery(q); - added++; - } - } - - if (DEBUG) { - LOG.debug("{} : cq size = {}(+{})", - this.name, this.queriesComplete.size(), added); + String content = HttpUtil.get(queriesUrl); + Gson gSon = new Gson(); + ServerQueries queries = gSon.fromJson(content, ServerQueries.class); + + List runningList = new ArrayList<>(queries.getRunningQueries().size()); + for (QCQueryImport qi:queries.getRunningQueries()) { + QCQuery query = new QCQuery(cluster.getName(), name, qi); + runningList.add(query); + } + List completeList = new ArrayList<>(queries.getCompleteQueries().size()); + for (QCQueryImport qi:queries.getCompleteQueries()) { + QCQuery query = new QCQuery(cluster.getName(), name, qi); + completeList.add((query)); + } + + synchronized(runningMap) { + + Iterator> iter = runningMap.entrySet().iterator(); + while (iter.hasNext()) { + Entry entry = iter.next(); + QCQuery value = entry.getValue(); + if (!runningList.contains(value)) { + int compIdx = completeList.indexOf(value); + if (compIdx > -1) { + cluster.addQueryEvent(QCQueryEvent.RUN_QUERY_REMOVED, completeList.get(compIdx)); + } else { + cluster.addQueryEvent(QCQueryEvent.RUN_QUERY_REMOVED, value); + } + iter.remove(); + } + } + + for (QCQuery query:runningList) { + QCQuery rquery = runningMap.get(query.getCuqId()); + if (rquery != null) { + if (QCQuery.update(rquery, query)) { + cluster.addQueryEvent(QCQueryEvent.RUN_QUERY_UPDATED, rquery); + } + } else { + runningMap.put(query.getCuqId(), query); + cluster.addQueryEvent(QCQueryEvent.RUN_QUERY_ADDED, query); + queryCount.incrementAndGet(); + } + } + } + + synchronized (completeMap) { + int added = 0; + for (QCQuery query : completeList) { + // complete query has no changes. process new complete queries only. + if (!completeMap.containsKey(query.getCuqId())) { + completeMap.put(query.getCuqId(), query); + cluster.addQueryEvent(QCQueryEvent.RUN_QUERY_REMOVED, query); + added++; } } + LOG.debug("{}.{} : complete query. add={}, total={}", + cluster.getName(), name, added, completeMap.size()); } + + long t1 = System.currentTimeMillis(); + LOG.debug("update queries {} {} : took={}, run={}, complete={}", + cluster.getName(), name, (t1-t0), runningList.size(), completeList.size()); + } catch (ConnectException e) { + LOG.error("{}.{}: updating queries : {} {}", + cluster.getName(), name, queriesUrl, e.getMessage()); } catch (Exception e) { - if (e instanceof ConnectException) { - LOG.error(this.name + ": updating queries: connection refused."); - this.setOnline(false); - this.setLastException(e); - } - else { - LOG.error(this.name + ": updating queries: ", e); - this.setLastException(e); - } + LOG.error("{}.{}: updating queries : {} {}", + cluster.getName(), name, queriesUrl, e.getMessage(), e); + setLastException(); } } - - protected void UpdateConnections() { - HttpUtil httpUtil = new HttpUtil(); - StringBuffer buf = new StringBuffer(); - int res = 0; + + private void updateApiConnections() { + conDescList = null; try { - res = httpUtil.get( getUrl("/api/connections"), buf); - if (res == 200) { - Gson gSon = new Gson(); - connections = gSon.fromJson(buf.toString(), new TypeToken>() {}.getType()); - if (DEBUG) { - for (ConDesc con: connections) { - LOG.debug(this.name + " con " + con.driver + " free " + con.free + " using " + con.using); - } + String content = HttpUtil.get(connectionUrl); + Gson gSon = new Gson(); + conDescList = gSon.fromJson(content, new TypeToken>(){}.getType()); + if (LOG.isDebugEnabled()) { + for (ServerConDesc con: conDescList) { + LOG.debug("{}.{} con={} free={} using={}", + cluster.getName(), name, + con.getDriver(), con.getFree(), con.getUsing()); } } + } catch (ConnectException e) { + LOG.error("{}.{}: updating connections : {} {}", + cluster.getName(), name, connectionUrl, e.getMessage()); } catch (Exception e) { - if (e instanceof ConnectException) { - LOG.error(this.name + ": updating connections: connection refused."); - this.setOnline(false); - this.setLastException(e); - } - else { - LOG.error(this.name + ": updating connections,", e); - this.setLastException(e); - } + LOG.error("{}.{}: updating connections : {} {}", + cluster.getName(), name, connectionUrl, e.getMessage(), e); + setLastException(); } } - protected void UpdateObjectPools() { - HttpUtil httpUtil = new HttpUtil(); - StringBuffer buf = new StringBuffer(); - int res = 0; + private void updateApiObjectPools() { + objectPool = null; try { - res = httpUtil.get( getUrl("/api/objectpool"), buf); - if (res == 200) { - Gson gSon = new Gson(); - objectPool = gSon.fromJson(buf.toString(), ObjectPool.class); - if (DEBUG) { - LOG.debug(this.name + " pool " + objectPool.poolSize[0] + "," + - objectPool.poolSize[1] + "," + - objectPool.poolSize[2] + "," + - objectPool.poolSize[3]); - } - } + String content = HttpUtil.get(objectpoolUrl); + Gson gSon = new Gson(); + objectPool = gSon.fromJson(content, ServerObjectPool.class); + LOG.debug("{}.{} pool {} {} {} {} {}", + cluster.getName(), + name, + objectPool.getPoolSize()[0], + objectPool.getPoolSize()[1], + objectPool.getPoolSize()[2], + objectPool.getPoolSize()[3]); + } catch (ConnectException e) { + LOG.error("{}.{}: updating object pools : {} {}", + cluster.getName(), name, objectpoolUrl, e.getMessage()); + setLastException(); } catch (Exception e) { - if (e instanceof ConnectException) { - LOG.error(this.name + ": updating pools: connection refused."); - this.setOnline(false); - this.setLastException(e); - } - else { - LOG.error(this.name + ": updating pools,", e); - this.setLastException(e); - } + LOG.error("{}.{}: updating object pools : {} {}", + cluster.getName(), name, objectpoolUrl, e.getMessage(), e); + setLastException(); } } - protected void UpdateSystem() { - HttpUtil httpUtil = new HttpUtil(); - StringBuffer buf = new StringBuffer(); - int res = 0; + private void updateApiSystem() { + systemStats = null; try { - res = httpUtil.get( getUrl("/api/system"), buf); - if (res == 200) { - Gson gSon = new Gson(); - sysInfoCollection = gSon.fromJson(buf.toString(), SysInfoCollection.class); - if (DEBUG) { - LOG.debug(this.name + " total threads " + sysInfoCollection.threads.totalThreads); - LOG.debug(this.name + " runtime mem used " + (sysInfoCollection.jvm.memTotal - sysInfoCollection.jvm.memFree)); - } - } + String content = HttpUtil.get(systemUrl); + Gson gSon = new Gson(); + systemStats = gSon.fromJson(content, ServerSystemStats.class); + + LOG.debug("{}.{} total threads={}, runtime memused={}", + cluster.getName(), name, + systemStats.getThreads().getTotalThreads(), + (systemStats.getJvm().getMemTotal() - systemStats.getJvm().getMemFree()) + ); + } catch (ConnectException e) { + LOG.error("{}.{}: updating system stats : {} {}", + cluster.getName(), name, systemUrl, e.getMessage()); + setLastException(); } catch (Exception e) { - if (e instanceof ConnectException) { - LOG.error(this.name + ": updating system stats: connection refused."); - this.setOnline(false); - this.setLastException(e); - } - else { - LOG.error(this.name + ": updating system stats,", e); - this.setLastException(e); - } + LOG.error("{}.{}: updating system stats : {} {}", + cluster.getName(), name, systemUrl, e.getMessage(), e); + setLastException(); } } - public Collection exportRunningQueries() { - List ql; - synchronized (queriesRunning) { - ql = new ArrayList<>(queriesRunning.size()); - for(QCQuery q: queriesRunning.values()) { - ql.add( q.export() ); - } - } - return ql; + public ServerSystemStats getSystemStats() { + return systemStats; } - public Collection exportCompleteQueries() { - List ql; - synchronized (queriesComplete) { - ql = new ArrayList<>(queriesComplete.size()); - for(QCQuery q: queriesComplete.values()) { - ql.add( q.export() ); - } - } - return ql; + public ServerObjectPool getObjectPool() { + return objectPool; } - public SysInfoCollection getSysInfoCollection() { - return sysInfoCollection; + public List getConnDescList() { + return conDescList; } - - public ObjectPool getObjectPool() { - return objectPool; + + public List getRunningQueryList() { + synchronized(runningMap) { + return new ArrayList<>(runningMap.values()); + } } - - public List getConnections() { - return connections; + + public QCQuery getRunningQueryByCuqId(String cuqId) { + synchronized(runningMap) { + return runningMap.get(cuqId); + } } - - private QCClientWebSocket rtWebSocket = null; - public QCClientWebSocket getRtWebSocket() { - return rtWebSocket; + + public List getCompleteQueryList() { + synchronized(completeMap) { + return new ArrayList<>(completeMap.values()); + } } - public void removeRtWebSocket() { - rtWebSocket = null; + + public void processWebSocketAddEvent(QCQueryImport queryImport) { + + QCQuery query = new QCQuery(cluster.getName(), name, queryImport); + synchronized(runningMap) { + QCQuery rquery = runningMap.get(query.getCuqId()); + if (rquery != null) { + boolean updated = QCQuery.update(rquery, query); + if (updated) { + cluster.addQueryEvent(QCQueryEvent.RUN_QUERY_UPDATED, rquery); + } + } else { + runningMap.put(query.getCuqId(), query); + queryCount.incrementAndGet(); + cluster.addQueryEvent(QCQueryEvent.RUN_QUERY_ADDED, query); + } + } } - public void connectRealtimeWebSocket() { - String uri = "ws://" + this.name + ":" + cluster.getOpt().webPort + "/api/websocket"; - WebSocketClient c = new WebSocketClient(); - QCClientWebSocket s = new QCClientWebSocket(this); - rtWebSocket = null; - try { - c.start(); - URI rtUri = new URI(uri); - ClientUpgradeRequest req = new ClientUpgradeRequest(); - c.connect(s, rtUri, req); - rtWebSocket = s; - } catch (URISyntaxException e) { - LOG.error("invalid uri " + uri, e); - } catch (Exception e) { - LOG.error("websocket client error", e); - } + + public void processWebSocketRemoveEvent(QCQueryImport queryImport) { + + QCQuery query = new QCQuery(cluster.getName(), name, queryImport); + synchronized(runningMap) { + if (runningMap.containsKey(query.getCuqId())) { + runningMap.remove(query.getCuqId()); + } + } + synchronized(completeMap) { + if (!completeMap.containsKey(query.getCuqId())) { + completeMap.put(query.getCuqId(), query); + cluster.addQueryEvent(QCQueryEvent.RUN_QUERY_REMOVED, query); + } + } } + + private class LimitHashMap extends LinkedHashMap { + + private static final long serialVersionUID = -3768283519827515112L; + + private int MAX_SIZE = 0; + + public LimitHashMap(int maxSize) { + super(); + this.MAX_SIZE = maxSize; + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > MAX_SIZE; + } + }; } diff --git a/src/main/java/com/skplanet/checkmate/querycache/QCWebSocketClient.java b/src/main/java/com/skplanet/checkmate/querycache/QCWebSocketClient.java new file mode 100644 index 0000000..57f3914 --- /dev/null +++ b/src/main/java/com/skplanet/checkmate/querycache/QCWebSocketClient.java @@ -0,0 +1,224 @@ +package com.skplanet.checkmate.querycache; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.WebSocketListener; +import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.skplanet.checkmate.querycache.data.QCQueryImport; + +/** + * Created by nazgul33 on 15. 2. 23. + */ +public class QCWebSocketClient implements WebSocketListener { + + private static final Logger LOG = LoggerFactory.getLogger(QCWebSocketClient.class); + + public static final String EVENT_RUN_QUERY_ADDED = "runningQueryAdded"; + public static final String EVENT_RUN_QUERY_UPDATED = "runningQueryUpdated"; + public static final String EVENT_RUN_QUERY_REMOVED = "runningQueryRemoved"; + public static final String EVENT_RESULT = "result"; + public static final String EVENT_PONG = "pong"; + + private WebSocketClient client; + private Session session; + private long errorCount = 0; + private static final long MAX_ERROR_COUNT = 10; + + private Timer timer = new Timer(true); + + private QCServer listener; + private String cluster; + private String url; + private URI uri; + + public QCWebSocketClient(final QCServer server, final String url) throws URISyntaxException { + + this.cluster = server.getCluster().getName(); + this.listener = server; + this.url = url; + this.uri = new URI(url); + + client = new WebSocketClient(); + client.setDaemon(true); + } + + public void start() { + timer.schedule(new TimerTask() { + @Override + public void run() { + try { + if (session == null || !session.isOpen()) { + connect(); + } + } catch (Exception e) { + errorCount++; + LOG.error("websocket error {} {} {}", + listener.getCluster().getName(), + url, + e.getMessage()); + } + if (errorCount > MAX_ERROR_COUNT) { + try { + TimeUnit.MINUTES.sleep(1); + } catch (InterruptedException e) { + } + } + } + }, 0, 5*1000); + timer.schedule(new TimerTask() { + @Override + public void run() { + ping(); + } + }, 0, 5*1000); + } + + public void stop() { + timer.cancel(); + if (session != null) { + session.close(); + } + if (client != null) { + try { + client.stop(); + } catch (Exception e) { + LOG.error("webclient stop failed. {} {}", cluster, url, e); + } + } + } + + private void connect() throws Exception { + try { + if (!client.isStarted()) { + client.start(); + } + ClientUpgradeRequest req = new ClientUpgradeRequest(); + client.connect(this, uri, req); + errorCount = 0; + } catch (Exception e) { + throw e; + } + } + + @Override + public void onWebSocketBinary(byte[] payload, int offset, int len) { + /* not interested */ + } + + @Override + public void onWebSocketClose(int statusCode, String reason) { + LOG.info("websocket closed {} {} statusCode={}, reason={}", + cluster, url, statusCode, reason); + try { + session.disconnect(); + } catch (IOException e) { + LOG.error("websocket session error {} {} {}", cluster, url, e.getMessage()); + } + } + + @Override + public void onWebSocketConnect(Session session) { + + this.session = session; + LOG.info("websocket connected {} {}", cluster, url); + + RequestMsg req = new RequestMsg(); + req.request = "subscribe"; + req.channel = "runningQueries"; + + Gson gson = new Gson(); + String msg = gson.toJson(req); + + sendMessage(msg); + } + + @Override + public void onWebSocketError(Throwable cause) { + if (cause instanceof ConnectException) { + LOG.error("websocket error {} {} {}", cluster, url, cause.getMessage()); + } else { + LOG.error("websocket error {} {}", cluster, url, cause); + } + session = null; + } + + @Override + public void onWebSocketText(String message) { + Gson gson = new Gson(); + RunningQueryEvent evt = null; + try { + evt = gson.fromJson(message, RunningQueryEvent.class); + } catch (Exception e) { + LOG.error("Gson exception :", message); + } + + if (evt == null || evt.msgType == null) { + LOG.error("invalid event object received.", message); + return; + } + + switch (evt.msgType) { + case EVENT_RUN_QUERY_ADDED: + case EVENT_RUN_QUERY_UPDATED: + LOG.debug("new RT query {} {}", cluster, url); + if (evt.query != null) { + listener.processWebSocketAddEvent(evt.query); + } + break; + case EVENT_RUN_QUERY_REMOVED: + LOG.debug("removing RT query {} {}", cluster, url); + if (evt.query != null) { + listener.processWebSocketRemoveEvent(evt.query); + } + break; + case EVENT_RESULT: + LOG.debug("result {} {} {} ",cluster, url, evt.result); + break; + case EVENT_PONG: + LOG.debug("pong {} {}", cluster, url); + break; + default: + LOG.warn("ignoring message", message); + break; + } + } + + private void sendMessage(String message) { + if (session != null && session.isOpen()) { + session.getRemote().sendString(message, null); + } + } + + private void ping() { + LOG.debug("ping {} {}", cluster, url); + Gson gson = new Gson(); + RequestMsg msg = new RequestMsg(); + msg.request = "ping"; + sendMessage(gson.toJson(msg)); + } + + private static class RequestMsg { + public String request; + public String channel; + public String data; + } + + private static class RunningQueryEvent { + public String msgType = null; + public String result = null; + public QCQueryImport query = null; + public String queryId = null; + } +} diff --git a/src/main/java/com/skplanet/checkmate/querycache/data/ClusterServerPoolInfo.java b/src/main/java/com/skplanet/checkmate/querycache/data/ClusterServerPoolInfo.java new file mode 100644 index 0000000..c256a0f --- /dev/null +++ b/src/main/java/com/skplanet/checkmate/querycache/data/ClusterServerPoolInfo.java @@ -0,0 +1,37 @@ +package com.skplanet.checkmate.querycache.data; + +import java.util.Collections; +import java.util.List; + +public class ClusterServerPoolInfo { + private String server; + private ServerObjectPool objPool; + private List connPoolList; + + public ClusterServerPoolInfo(){} + + public ClusterServerPoolInfo(String server, ServerObjectPool objPool, List connPoolList) { + this.server = server; + this.objPool = (objPool!=null?objPool:new ServerObjectPool()); + this.connPoolList = (connPoolList!=null?connPoolList:Collections.EMPTY_LIST); + } + + public String getServer() { + return server; + } + public void setServer(String server) { + this.server = server; + } + public ServerObjectPool getObjPool() { + return objPool; + } + public void setObjPool(ServerObjectPool objPool) { + this.objPool = objPool; + } + public List getConnPoolList() { + return connPoolList; + } + public void setConnPoolList(List connPoolList) { + this.connPoolList = connPoolList; + } +} \ No newline at end of file diff --git a/src/main/java/com/skplanet/checkmate/querycache/data/ClusterServerSysStats.java b/src/main/java/com/skplanet/checkmate/querycache/data/ClusterServerSysStats.java new file mode 100644 index 0000000..3b9fec8 --- /dev/null +++ b/src/main/java/com/skplanet/checkmate/querycache/data/ClusterServerSysStats.java @@ -0,0 +1,42 @@ +package com.skplanet.checkmate.querycache.data; + +public class ClusterServerSysStats { + private String server; + private ServerRuntimeInfo jvm; + private ServerSystemInfo system; + private ServerThreadInfo threads; + + public ClusterServerSysStats(){} + + public ClusterServerSysStats(String server, ServerSystemStats svrStats) { + this.server = server; + this.jvm = (svrStats!=null?svrStats.getJvm():new ServerRuntimeInfo()); + this.system = (svrStats!=null?svrStats.getSystem():new ServerSystemInfo()); + this.threads = (svrStats!=null?svrStats.getThreads():new ServerThreadInfo()); + } + + public String getServer() { + return server; + } + public void setServer(String server) { + this.server = server; + } + public ServerRuntimeInfo getJvm() { + return jvm; + } + public void setJvm(ServerRuntimeInfo jvm) { + this.jvm = jvm; + } + public ServerSystemInfo getSystem() { + return system; + } + public void setSystem(ServerSystemInfo system) { + this.system = system; + } + public ServerThreadInfo getThreads() { + return threads; + } + public void setThreads(ServerThreadInfo threads) { + this.threads = threads; + } +} \ No newline at end of file diff --git a/src/main/java/com/skplanet/checkmate/querycache/data/QCQueryEvent.java b/src/main/java/com/skplanet/checkmate/querycache/data/QCQueryEvent.java new file mode 100644 index 0000000..0fc7af3 --- /dev/null +++ b/src/main/java/com/skplanet/checkmate/querycache/data/QCQueryEvent.java @@ -0,0 +1,54 @@ +package com.skplanet.checkmate.querycache.data; + +import com.skplanet.checkmate.querycache.QCQuery; + +public class QCQueryEvent { + + public static final String RUN_QUERY_ADDED = "runningQueryAdded"; + public static final String RUN_QUERY_UPDATED = "runningQueryUpdated"; + public static final String RUN_QUERY_REMOVED = "runningQueryRemoved"; + + private long timestamp; + private String msgType; + private QCQuery query; + private String cuqId; + + public QCQueryEvent(String msgType, QCQuery query) { + this.timestamp = System.currentTimeMillis(); + this.msgType = msgType; + this.query = query; + this.cuqId = query.getCuqId(); + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public String getMsgType() { + return msgType; + } + + public void setMsgType(String msgType) { + this.msgType = msgType; + } + + public QCQuery getQuery() { + return query; + } + + public void setQuery(QCQuery query) { + this.query = query; + } + + public String getCuqId() { + return cuqId; + } + + public void setCuqId(String cuqId) { + this.cuqId = cuqId; + } +} \ No newline at end of file diff --git a/src/main/java/com/skplanet/checkmate/querycache/data/QCQueryImport.java b/src/main/java/com/skplanet/checkmate/querycache/data/QCQueryImport.java new file mode 100644 index 0000000..0c2fc9b --- /dev/null +++ b/src/main/java/com/skplanet/checkmate/querycache/data/QCQueryImport.java @@ -0,0 +1,98 @@ +package com.skplanet.checkmate.querycache.data; + +public class QCQueryImport { + + private String queryId; + private String connType; + private String user; + private String queryType; + private String queryStr; + private String clientIp; + private String stmtState; + private long rowCnt; + private long startTime; + private long endTime; + // 0:exec/1:getmeta/2:fetch/3:stmtclose or + // 1:getschemas/2:fetch + private long[] timeHistogram = {0,0,0,0}; + private long[] execProfile = null; + private long[] fetchProfile = {0,0,0,-1,-1,-1,-1,0,0,-1,-1}; + public String getQueryId() { + return queryId; + } + public void setQueryId(String queryId) { + this.queryId = queryId; + } + public String getConnType() { + return connType; + } + public void setConnType(String connType) { + this.connType = connType; + } + public String getUser() { + return user; + } + public void setUser(String user) { + this.user = user; + } + public String getQueryType() { + return queryType; + } + public void setQueryType(String queryType) { + this.queryType = queryType; + } + public String getQueryStr() { + return queryStr; + } + public void setQueryStr(String queryStr) { + this.queryStr = queryStr; + } + public String getClientIp() { + return clientIp; + } + public void setClientIp(String clientIp) { + this.clientIp = clientIp; + } + public String getStmtState() { + return stmtState; + } + public void setStmtState(String stmtState) { + this.stmtState = stmtState; + } + public long getRowCnt() { + return rowCnt; + } + public void setRowCnt(long rowCnt) { + this.rowCnt = rowCnt; + } + public long getStartTime() { + return startTime; + } + public void setStartTime(long startTime) { + this.startTime = startTime; + } + public long getEndTime() { + return endTime; + } + public void setEndTime(long endTime) { + this.endTime = endTime; + } + public long[] getTimeHistogram() { + return timeHistogram; + } + public void setTimeHistogram(long[] timeHistogram) { + this.timeHistogram = timeHistogram; + } + public long[] getExecProfile() { + return execProfile; + } + public void setExecProfile(long[] execProfile) { + this.execProfile = execProfile; + } + public long[] getFetchProfile() { + return fetchProfile; + } + public void setFetchProfile(long[] fetchProfile) { + this.fetchProfile = fetchProfile; + } +} \ No newline at end of file diff --git a/src/main/java/com/skplanet/checkmate/querycache/data/ServerConDesc.java b/src/main/java/com/skplanet/checkmate/querycache/data/ServerConDesc.java new file mode 100644 index 0000000..2e475ec --- /dev/null +++ b/src/main/java/com/skplanet/checkmate/querycache/data/ServerConDesc.java @@ -0,0 +1,27 @@ +package com.skplanet.checkmate.querycache.data; + +public class ServerConDesc { + private String driver; + private int free; + private int using; + + public String getDriver() { + return driver; + } + public void setDriver(String driver) { + this.driver = driver; + } + public int getFree() { + return free; + } + public void setFree(int free) { + this.free = free; + } + public int getUsing() { + return using; + } + public void setUsing(int using) { + this.using = using; + } +} + diff --git a/src/main/java/com/skplanet/checkmate/querycache/data/ServerObjectPool.java b/src/main/java/com/skplanet/checkmate/querycache/data/ServerObjectPool.java new file mode 100644 index 0000000..c4f81ef --- /dev/null +++ b/src/main/java/com/skplanet/checkmate/querycache/data/ServerObjectPool.java @@ -0,0 +1,41 @@ +package com.skplanet.checkmate.querycache.data; + +public class ServerObjectPool { + // configuration + private long sReloadCycle = 0; + private int sMaxPoolSize = 0; + private float sReloadingThreshold = 0; + private int sCellCoeff = 0; + private int[] poolSize = {0, 0, 0, 0}; + + public long getsReloadCycle() { + return sReloadCycle; + } + public void setsReloadCycle(long sReloadCycle) { + this.sReloadCycle = sReloadCycle; + } + public int getsMaxPoolSize() { + return sMaxPoolSize; + } + public void setsMaxPoolSize(int sMaxPoolSize) { + this.sMaxPoolSize = sMaxPoolSize; + } + public float getsReloadingThreshold() { + return sReloadingThreshold; + } + public void setsReloadingThreshold(float sReloadingThreshold) { + this.sReloadingThreshold = sReloadingThreshold; + } + public int getsCellCoeff() { + return sCellCoeff; + } + public void setsCellCoeff(int sCellCoeff) { + this.sCellCoeff = sCellCoeff; + } + public int[] getPoolSize() { + return poolSize; + } + public void setPoolSize(int[] poolSize) { + this.poolSize = poolSize; + } +} \ No newline at end of file diff --git a/src/main/java/com/skplanet/checkmate/querycache/data/ServerQueries.java b/src/main/java/com/skplanet/checkmate/querycache/data/ServerQueries.java new file mode 100644 index 0000000..9ce3767 --- /dev/null +++ b/src/main/java/com/skplanet/checkmate/querycache/data/ServerQueries.java @@ -0,0 +1,22 @@ +package com.skplanet.checkmate.querycache.data; + +import java.util.ArrayList; +import java.util.List; + +public class ServerQueries { + private List runningQueries = new ArrayList<>(); + private List completeQueries = new ArrayList<>(); + + public List getRunningQueries() { + return runningQueries; + } + public void setRunningQueries(List runningQueries) { + this.runningQueries = runningQueries; + } + public List getCompleteQueries() { + return completeQueries; + } + public void setCompleteQueries(List completeQueries) { + this.completeQueries = completeQueries; + } +} \ No newline at end of file diff --git a/src/main/java/com/skplanet/checkmate/querycache/data/ServerRuntimeInfo.java b/src/main/java/com/skplanet/checkmate/querycache/data/ServerRuntimeInfo.java new file mode 100644 index 0000000..89c382f --- /dev/null +++ b/src/main/java/com/skplanet/checkmate/querycache/data/ServerRuntimeInfo.java @@ -0,0 +1,33 @@ +package com.skplanet.checkmate.querycache.data; + +public class ServerRuntimeInfo { + private int nProcessors; + private long memFree; + private long memTotal; + private long memMax; + + public int getnProcessors() { + return nProcessors; + } + public void setnProcessors(int nProcessors) { + this.nProcessors = nProcessors; + } + public long getMemFree() { + return memFree; + } + public void setMemFree(long memFree) { + this.memFree = memFree; + } + public long getMemTotal() { + return memTotal; + } + public void setMemTotal(long memTotal) { + this.memTotal = memTotal; + } + public long getMemMax() { + return memMax; + } + public void setMemMax(long memMax) { + this.memMax = memMax; + } +} \ No newline at end of file diff --git a/src/main/java/com/skplanet/checkmate/querycache/data/ServerSystemInfo.java b/src/main/java/com/skplanet/checkmate/querycache/data/ServerSystemInfo.java new file mode 100644 index 0000000..03ecdd5 --- /dev/null +++ b/src/main/java/com/skplanet/checkmate/querycache/data/ServerSystemInfo.java @@ -0,0 +1,47 @@ +package com.skplanet.checkmate.querycache.data; + +public class ServerSystemInfo { + private double loadSystem; + private double loadProcess; + private long memPhysFree; + private long memPhysTotal; + private long swapFree; + private long swapTotal; + + public double getLoadSystem() { + return loadSystem; + } + public void setLoadSystem(double loadSystem) { + this.loadSystem = loadSystem; + } + public double getLoadProcess() { + return loadProcess; + } + public void setLoadProcess(double loadProcess) { + this.loadProcess = loadProcess; + } + public long getMemPhysFree() { + return memPhysFree; + } + public void setMemPhysFree(long memPhysFree) { + this.memPhysFree = memPhysFree; + } + public long getMemPhysTotal() { + return memPhysTotal; + } + public void setMemPhysTotal(long memPhysTotal) { + this.memPhysTotal = memPhysTotal; + } + public long getSwapFree() { + return swapFree; + } + public void setSwapFree(long swapFree) { + this.swapFree = swapFree; + } + public long getSwapTotal() { + return swapTotal; + } + public void setSwapTotal(long swapTotal) { + this.swapTotal = swapTotal; + } +} \ No newline at end of file diff --git a/src/main/java/com/skplanet/checkmate/querycache/data/ServerSystemStats.java b/src/main/java/com/skplanet/checkmate/querycache/data/ServerSystemStats.java new file mode 100644 index 0000000..0c87ba5 --- /dev/null +++ b/src/main/java/com/skplanet/checkmate/querycache/data/ServerSystemStats.java @@ -0,0 +1,27 @@ +package com.skplanet.checkmate.querycache.data; + +public class ServerSystemStats { + + private ServerRuntimeInfo jvm; + private ServerSystemInfo system; + private ServerThreadInfo threads; + + public ServerRuntimeInfo getJvm() { + return jvm; + } + public void setJvm(ServerRuntimeInfo jvm) { + this.jvm = jvm; + } + public ServerSystemInfo getSystem() { + return system; + } + public void setSystem(ServerSystemInfo system) { + this.system = system; + } + public ServerThreadInfo getThreads() { + return threads; + } + public void setThreads(ServerThreadInfo threads) { + this.threads = threads; + } +} \ No newline at end of file diff --git a/src/main/java/com/skplanet/checkmate/querycache/data/ServerThreadInfo.java b/src/main/java/com/skplanet/checkmate/querycache/data/ServerThreadInfo.java new file mode 100644 index 0000000..08983dc --- /dev/null +++ b/src/main/java/com/skplanet/checkmate/querycache/data/ServerThreadInfo.java @@ -0,0 +1,41 @@ +package com.skplanet.checkmate.querycache.data; + +public class ServerThreadInfo { + private int webServerThreads; + private int webServerThreadsIdle; + private int handlerThreads; + private int handlerThreadsIdle; + private int totalThreads; + + public int getWebServerThreads() { + return webServerThreads; + } + public void setWebServerThreads(int webServerThreads) { + this.webServerThreads = webServerThreads; + } + public int getWebServerThreadsIdle() { + return webServerThreadsIdle; + } + public void setWebServerThreadsIdle(int webServerThreadsIdle) { + this.webServerThreadsIdle = webServerThreadsIdle; + } + public int getHandlerThreads() { + return handlerThreads; + } + public void setHandlerThreads(int handlerThreads) { + this.handlerThreads = handlerThreads; + } + public int getHandlerThreadsIdle() { + return handlerThreadsIdle; + } + public void setHandlerThreadsIdle(int handlerThreadsIdle) { + this.handlerThreadsIdle = handlerThreadsIdle; + } + public int getTotalThreads() { + return totalThreads; + } + public void setTotalThreads(int totalThreads) { + this.totalThreads = totalThreads; + } +} + diff --git a/src/main/java/com/skplanet/checkmate/servlet/CMApiServletQC.java b/src/main/java/com/skplanet/checkmate/servlet/CMApiServletQC.java index 2c6e546..331611f 100644 --- a/src/main/java/com/skplanet/checkmate/servlet/CMApiServletQC.java +++ b/src/main/java/com/skplanet/checkmate/servlet/CMApiServletQC.java @@ -1,40 +1,50 @@ package com.skplanet.checkmate.servlet; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; -import com.skplanet.checkmate.querycache.QCCluster; -import com.skplanet.checkmate.querycache.QCClusterManager; -import com.skplanet.checkmate.querycache.QCQuery; -import com.skplanet.checkmate.utils.HttpUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static java.lang.String.format; + +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.List; import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; -import java.lang.reflect.Type; -import java.util.Collection; -import java.util.Date; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import com.skplanet.checkmate.CheckMateServer; +import com.skplanet.checkmate.querycache.QCCluster; +import com.skplanet.checkmate.querycache.QCClusterManager; +import com.skplanet.checkmate.querycache.QCQuery; +import com.skplanet.checkmate.querycache.data.ClusterServerPoolInfo; +import com.skplanet.checkmate.querycache.data.ClusterServerSysStats; +import com.skplanet.checkmate.utils.HttpUtil; /** * Created by nazgul33 on 15. 1. 29. */ public class CMApiServletQC extends HttpServlet { - private static final boolean DEBUG = false; - private static final Logger LOG = LoggerFactory.getLogger("api"); + + private static final long serialVersionUID = 2564648276343514634L; + + private static final Logger LOG = LoggerFactory.getLogger(CMApiServletQC.class); private static final String ASYNC_REQ_ATTR = CMApiServletQC.class.getName() + ".async"; private static final String ASYNC_RETURN_ATTR = CMApiServletQC.class.getName() + ".async.return"; + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/json; charset=utf-8"); - if (DEBUG) { - LOG.debug(request.getRequestURI() + "?" + request.getQueryString()); - } + LOG.info("{}?{}",request.getRequestURI(),request.getQueryString()); PrintWriter writer = response.getWriter(); String path = request.getRequestURI().substring(request.getContextPath().length()); @@ -43,12 +53,12 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t urlWithParameter += "?" + request.getQueryString(); } - QCClusterManager qcMgr = QCClusterManager.getInstance(); + QCClusterManager qcMgr = CheckMateServer.getClusterManager(); Gson gson = new Gson(); switch (path) { case "/clusterList": { - Collection cl = qcMgr.getClusterList(); - Type qListType = new TypeToken>() {}.getType(); + List cl = qcMgr.getClusterNameList(); + Type qListType = new TypeToken>() {}.getType(); String jsonObj = gson.toJson(cl, qListType); writer.printf("{\"result\":\"ok\", \"msg\":\"ok\", \"data\":%s}", jsonObj); response.setStatus(HttpServletResponse.SC_OK); @@ -57,8 +67,8 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t case "/runningQueries": { QCCluster c = qcMgr.getCluster(request.getParameter("cluster")); if (c!=null) { - Collection ql = c.getExportedRunningQueries(); - Type qListType = new TypeToken>() { + List ql = c.getRunningQueries(); + Type qListType = new TypeToken>() { }.getType(); String jsonObj = gson.toJson(ql, qListType); writer.printf("{\"result\":\"ok\", \"msg\":\"ok\", \"data\":%s}", jsonObj); @@ -75,7 +85,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t case "/completeQueries": { QCCluster c = qcMgr.getCluster(request.getParameter("cluster")); if (c!=null) { - Collection ql = c.getExportedCompleteQueries(); + Collection ql = c.getCompleteQueries(); /* // TODO: paging, filtering @@ -91,7 +101,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t return ql.subList(page_number*page_size, (page_number + 1)*page_size); */ - Type qListType = new TypeToken>() {}.getType(); + Type qListType = new TypeToken>() {}.getType(); String jsonObj = gson.toJson(ql, qListType); writer.printf("{\"result\":\"ok\", \"msg\":\"ok\", \"data\":%s}", jsonObj); response.setStatus(HttpServletResponse.SC_OK); @@ -106,16 +116,22 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t case "/sysInfoList": { QCCluster c = qcMgr.getCluster(request.getParameter("cluster")); if (c!=null) { - Collection l = c.getSystemInfo(); - Type lType = new TypeToken>() { - }.getType(); - String jsonObj = gson.toJson(l, lType); - writer.printf("{\"result\":\"ok\", \"msg\":\"ok\", \"data\":%s}", jsonObj); + List list = c.getServerInfo(); + Type lType = new TypeToken>() {}.getType(); + JsonArray jarr = gson.toJsonTree(list , lType).getAsJsonArray(); + + JsonObject json = new JsonObject(); + json.addProperty("result", "ok"); + json.addProperty("msg", "ok"); + json.add("data", jarr); + writer.print(json.toString()); response.setStatus(HttpServletResponse.SC_OK); - } - else { + } else { LOG.error(urlWithParameter + " : invalid parameter"); - writer.print("{\"result\":\"error\", \"msg\":\"invalid parameter\"}"); + JsonObject json = new JsonObject(); + json.addProperty("result", "error"); + json.addProperty("msg", "invalid parameter"); + writer.print(json.toString()); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); } break; @@ -123,15 +139,22 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t case "/poolInfoList": { QCCluster c = qcMgr.getCluster(request.getParameter("cluster")); if (c != null) { - Collection l = c.getPoolInfo(); - Type lType = new TypeToken>() {}.getType(); - String jsonObj = gson.toJson(l, lType); - writer.printf("{\"result\":\"ok\", \"msg\":\"ok\", \"data\":%s}", jsonObj); + List list = c.getPoolInfo(); + Type lType = new TypeToken>() {}.getType(); + JsonArray jarr = gson.toJsonTree(list , lType).getAsJsonArray(); + + JsonObject json = new JsonObject(); + json.addProperty("result", "ok"); + json.addProperty("msg", "ok"); + json.add("data", jarr); + writer.print(json.toString()); response.setStatus(HttpServletResponse.SC_OK); - } - else { + } else { LOG.error(urlWithParameter + " : invalid parameter"); - writer.print("{\"result\":\"error\", \"msg\":\"invalid parameter\"}"); + JsonObject json = new JsonObject(); + json.addProperty("result", "error"); + json.addProperty("msg", "invalid parameter"); + writer.print(json.toString()); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); } break; @@ -146,7 +169,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t response.setStatus(HttpServletResponse.SC_BAD_REQUEST); break; } - final QCQuery.QueryExport eq = c.getExportedRunningQueryByCuqId(cuqId); + final QCQuery eq = c.getRunningQueryByCuqId(cuqId); if ( eq == null ) { response.setContentType("application/json; charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); @@ -155,7 +178,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t break; } - LOG.info("Starting cancel async job. " + eq.server + " " + eq.backend + " " + eq.id); + LOG.info("Starting cancel async job. " + eq.getServer() + " " + eq.getBackend() + " " + eq.getId()); final AsyncContext async = request.startAsync(); request.setAttribute(ASYNC_REQ_ATTR, new Boolean(true)); async.setTimeout(30000); @@ -163,10 +186,12 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t @Override public void run() { HttpUtil httpUtil = new HttpUtil(); - StringBuffer buf = new StringBuffer(); - int res = 0; + StringBuilder buf = new StringBuilder(); try { - String url = "http://" + eq.server + ":" + c.getOpt().webPort + "/api/cancelQuery?id="+eq.id+"&driver="+eq.backend; + String url = format("http://%s:%s%s", + eq.getServer(), + c.getOptions().getWebPort(), + "/api/cancelQuery?id="+eq.getId()+"&driver="+eq.getBackend()); httpUtil.get( url, buf ); async.getRequest().setAttribute(ASYNC_RETURN_ATTR, buf.toString()); diff --git a/src/main/java/com/skplanet/checkmate/servlet/CMQCServerWebSocket.java b/src/main/java/com/skplanet/checkmate/servlet/CMQCServerWebSocket.java index b45d1cb..966711b 100644 --- a/src/main/java/com/skplanet/checkmate/servlet/CMQCServerWebSocket.java +++ b/src/main/java/com/skplanet/checkmate/servlet/CMQCServerWebSocket.java @@ -2,6 +2,7 @@ import com.google.gson.Gson; +import com.skplanet.checkmate.CheckMateServer; import com.skplanet.checkmate.querycache.QCCluster; import com.skplanet.checkmate.querycache.QCClusterManager; import org.eclipse.jetty.websocket.api.Session; @@ -13,40 +14,32 @@ * Created by nazgul33 on 15. 2. 13. */ public class CMQCServerWebSocket implements WebSocketListener { - private static final boolean DEBUG = false; + private static final Logger LOG = LoggerFactory.getLogger("websocket-server-qc"); - private Session outbound; + private Session session; private int id = -1; - private QCClusterManager qcMgr = QCClusterManager.getInstance(); + private QCClusterManager qcMgr = CheckMateServer.getClusterManager(); private QCCluster cluster = null; - private static class RequestMsg { - public String request; - public String channel; - public String data; - } - @Override - public void onWebSocketBinary(byte[] payload, int offset, int len) - { + public void onWebSocketBinary(byte[] payload, int offset, int len) { /* only interested in test messages */ } @Override - public void onWebSocketClose(int statusCode, String reason) - { - this.outbound = null; - if ( this.id >= 0 && this.cluster != null) { - cluster.unSubscribe(id); + public void onWebSocketClose(int statusCode, String reason) { + session.close(); + if ( id >= 0 && cluster != null) { + cluster.unsubscribe(id); } - this.id = -1; - this.cluster = null; + id = -1; + cluster = null; } @Override public void onWebSocketConnect(Session session) { - this.outbound = session; + this.session = session; } @Override @@ -56,7 +49,7 @@ public void onWebSocketError(Throwable cause) { @Override public void onWebSocketText(String message) { - if (outbound == null || !outbound.isOpen()) + if (session == null || !session.isOpen()) return; Gson gson = new Gson(); @@ -64,15 +57,15 @@ public void onWebSocketText(String message) { switch (msg.request) { case "subscribe": { if ("cluster".equals(msg.channel)) { - this.cluster = qcMgr.getCluster(msg.data); - if (this.cluster != null) { - this.id = this.cluster.subscribe(this); + cluster = qcMgr.getCluster(msg.data); + if (cluster != null) { + id = cluster.subscribe(this); } } return; } case "ping": { - outbound.getRemote().sendString("{\"msgType\":\"pong\"}", null); + sendMessage("{\"msgType\":\"pong\"}"); return; } } @@ -81,8 +74,14 @@ public void onWebSocketText(String message) { } public void sendMessage(String message) { - if (outbound == null || !outbound.isOpen()) + if (session == null || !session.isOpen()) return; - outbound.getRemote().sendString(message, null); + session.getRemote().sendString(message, null); + } + + private static class RequestMsg { + public String request; + public String channel; + public String data; } } diff --git a/src/main/java/com/skplanet/checkmate/servlet/CMWebSocketServletQC.java b/src/main/java/com/skplanet/checkmate/servlet/CMWebSocketServletQC.java index 97ff6ec..0340b01 100644 --- a/src/main/java/com/skplanet/checkmate/servlet/CMWebSocketServletQC.java +++ b/src/main/java/com/skplanet/checkmate/servlet/CMWebSocketServletQC.java @@ -11,9 +11,15 @@ @WebServlet(name = "CheckMate WebSocket for QueryCache", urlPatterns = { "/api/qc/websocket" }) public class CMWebSocketServletQC extends WebSocketServlet { - @Override + + private static final long serialVersionUID = -1985362762429496022L; + + private static final int MAX_TEXT_MESSAGE_SIZE = 1*1024*1024; + + @Override public void configure(WebSocketServletFactory factory) { factory.getPolicy().setIdleTimeout(120*1000); // 2min + factory.getPolicy().setMaxTextMessageSize(MAX_TEXT_MESSAGE_SIZE); factory.register(CMQCServerWebSocket.class); } } diff --git a/src/main/java/com/skplanet/checkmate/utils/HttpUtil.java b/src/main/java/com/skplanet/checkmate/utils/HttpUtil.java index f246657..8113622 100644 --- a/src/main/java/com/skplanet/checkmate/utils/HttpUtil.java +++ b/src/main/java/com/skplanet/checkmate/utils/HttpUtil.java @@ -5,64 +5,92 @@ import java.net.HttpURLConnection; import java.net.URL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Created by nazgul33 on 15. 1. 27. */ public class HttpUtil { - private static boolean DEBUG = false; + + private static final Logger LOG = LoggerFactory.getLogger(HttpUtil.class); + private static int CONNECTION_TIMEOUT = 5000; private static int READ_TIMEOUT = 3000; public final String userAgent; + + public HttpUtil() { + this("CheckMate 1.0"); + } + public HttpUtil(String userAgent) { this.userAgent = userAgent; } - public HttpUtil() { - this.userAgent = "CheckMate 1.0"; - } // HTTP GET request // returns response code // if response code is 200, parameter "response" should have the result - public int get(String url, StringBuffer response) throws Exception { + public int get(String url, StringBuilder response) throws Exception { return get(url, response, null); } - public int getJson(String url, StringBuffer response) throws Exception { + public int getJson(String url, StringBuilder response) throws Exception { return get(url, response, "application/json"); } - public int get(String url, StringBuffer response, String accept) throws Exception { + public int get(String url, StringBuilder response, String accept) throws Exception { URL obj = new URL(url); - HttpURLConnection con = (HttpURLConnection) obj.openConnection(); - con.setConnectTimeout(CONNECTION_TIMEOUT); - con.setReadTimeout(READ_TIMEOUT); + HttpURLConnection con = null; + try { + con = (HttpURLConnection) obj.openConnection(); + con.setConnectTimeout(CONNECTION_TIMEOUT); + con.setReadTimeout(READ_TIMEOUT); + // optional default is GET + con.setRequestMethod("GET"); - // optional default is GET - con.setRequestMethod("GET"); + //add request header + con.setRequestProperty("User-Agent", userAgent); - //add request header - con.setRequestProperty("User-Agent", userAgent); + if (accept != null) { + con.setRequestProperty("Accept", accept); + } - if (accept != null) { - con.setRequestProperty("Accept", accept); - } + int responseCode = con.getResponseCode(); + LOG.debug("sending 'GET' request to URL : {}", url); + LOG.debug("Response Code : {}", responseCode); - int responseCode = con.getResponseCode(); - if (DEBUG) { - System.out.println("\nSending 'GET' request to URL : " + url); - System.out.println("Response Code : " + responseCode); - } + if (responseCode == 200) { + BufferedReader in = null; + try { + in = new BufferedReader( + new InputStreamReader(con.getInputStream())); + String inputLine; - if (responseCode == 200) { - BufferedReader in = new BufferedReader( - new InputStreamReader(con.getInputStream())); - String inputLine; - - while ((inputLine = in.readLine()) != null) { - response.append(inputLine); + while ((inputLine = in.readLine()) != null) { + response.append(inputLine).append("\n"); + } + } finally { + if (in != null) { + in.close(); + } + } } - in.close(); + return responseCode; + } finally { + if (con != null) { + con.disconnect(); + } } - return responseCode; + } + + public static String get(String url) throws Exception { + HttpUtil http = new HttpUtil(); + StringBuilder sb = new StringBuilder(); + int code = http.get(url, sb); + if (code == 200) { + return sb.toString(); + } else { + throw new RuntimeException("response code "+ code); + } } } diff --git a/src/main/java/com/skplanet/checkmate/utils/MailSender.java b/src/main/java/com/skplanet/checkmate/utils/MailSender.java index 692edb4..9980ee8 100644 --- a/src/main/java/com/skplanet/checkmate/utils/MailSender.java +++ b/src/main/java/com/skplanet/checkmate/utils/MailSender.java @@ -1,200 +1,181 @@ package com.skplanet.checkmate.utils; -/** - * Created by nazgul33 on 4/7/16. - */ - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; import javax.mail.Message; -import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.*; + +/** + * Created by nazgul33 on 4/7/16. + */ + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Created by nazgul33 on 10/26/15. */ -public class MailSender implements Runnable { - protected static final Logger LOG = LoggerFactory.getLogger(MailSender.class); - - static public class Mail { - public final String subject; - private String content; - - public Mail(String subject, String content) { - this.subject = subject; - this.content = content; - } - - public Mail(String subject, Exception e) { - this.subject = subject; - - StringWriter errors = new StringWriter(); - errors.append("Exception : ").append(e.getClass().getCanonicalName()).append("\n"); - errors.append(e.getMessage()).append("\n"); - errors.flush(); - e.printStackTrace(new PrintWriter(errors)); - this.content = errors.toString(); - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - } - - private final String smtpServer, smtpFrom; - private final int smtpPort; - private final InternetAddress[] recipientAddresses; - - public MailSender(String smtpServer, String smtpFrom, int smtpPort, List recipients) throws Exception { - this.smtpServer = smtpServer; - this.smtpFrom = smtpFrom; - this.smtpPort = smtpPort; - - recipientAddresses = new InternetAddress[recipients.size()]; - for (int i=0; i mailQueue = new ArrayList<>(); - public void addMail(Mail mail) { - synchronized (mailQueue) { - mailQueue.add(mail); - } - LOG.info("mail queued. {}", mail.subject); - } - - public void run() { - try { - Properties properties = System.getProperties(); - properties.setProperty("mail.smtp.host", smtpServer); - properties.setProperty("mail.smtp.port", String.valueOf(smtpPort)); - - Session session = Session.getDefaultInstance(properties); - - InternetAddress iFrom = new InternetAddress(smtpFrom); - Collection mq = new ArrayList<>(); - - LOG.info("MailSender started."); - - while (!Thread.interrupted()) { - synchronized (mailQueue) { - if (mailQueue.size() > 0) { - for (Mail mail : mailQueue) { - mq.add(mail); - } - mailQueue.clear(); - } - } - if (mq.size() == 0) { - try { - Thread.sleep(500); - } catch (InterruptedException e) { - // ignore - } - continue; - } - - for (Mail mail : mq) { - LOG.info("sending mail '{}'", mail.subject); - - try { - MimeMessage message = new MimeMessage(session); - message.setFrom(iFrom); - message.addRecipients(Message.RecipientType.TO, recipientAddresses); - message.setSubject(mail.subject); - message.setText(mail.content); - - Transport.send(message); - LOG.info("Mail sent to {}", recipientAddresses); - } catch (MessagingException e) { - LOG.error("Exception sending mail", e); - } - } - mq.clear(); - } - } catch (Exception e) { - LOG.error("Unexpected exception", e); - } - } - - public static void main(String args[]) { - String server = null; - int port = 25; - String from = null; - List recipients = null; - String title = null; - String msg = null; - try { - for (int i = 0; i < args.length; i++) { - switch (args[i]) { - case "-s": - server = args[++i]; - System.out.println("Server : " + server); - break; - case "-p": - port = Integer.valueOf(args[++i]); - System.out.println("Port : " + port); - break; - case "-f": - from = args[++i]; - System.out.println("From : " + from); - break; - case "-r": - recipients = Arrays.asList(args[++i].split(",")); - System.out.println("Recipients : " + recipients); - break; - case "-t": - title = args[++i]; - System.out.println("Title : " + title); - break; - case "-m": - msg = args[++i]; - System.out.println("Message : " + msg); - break; - default: - System.out.println("unknown option"); - break; - } - } - } catch (Exception e) { - System.err.println("error parsing arguments."); - e.printStackTrace(); - System.exit(1); - } - - MailSender mailSender = null; - try { - mailSender = new MailSender(server, from, port, recipients); - } catch (Exception e) { - System.err.println("error instantiating MailSender"); - e.printStackTrace(); - System.exit(1); - } - - Thread t = new Thread(mailSender); - t.setDaemon(false); - t.start(); - - Mail mail = new Mail(title, msg); - mailSender.addMail(mail); - - try { - Thread.sleep(10); - t.interrupt(); - t.join(); - } catch (Exception e) { - // ignore - } - } +public class MailSender { + + private static final Logger LOG = LoggerFactory.getLogger(MailSender.class); + + private static MailSender _this; + + private List mailQueue = new ArrayList<>(); + + private String server; + private String from; + private int port; + private InternetAddress[] tos; + private MailSenderThread thread; + + private MailSender(String server, int port, String from, String[] toList) throws Exception { + this.server = server; + this.port = port; + this.from = from; + + tos = new InternetAddress[toList.length]; + for (int i=0;i mq = new ArrayList<>(); + + while (running) { + synchronized (mailQueue) { + if (mailQueue.size() > 0) { + mq.addAll(mailQueue); + mailQueue.clear(); + } + } + for (Mail mail : mq) { + LOG.info("sending mail '{}'", mail.getSubject()); + + try { + MimeMessage message = new MimeMessage(session); + message.setFrom(iFrom); + message.addRecipients(Message.RecipientType.TO, tos); + message.setSubject(mail.getSubject()); + message.setText(mail.getContent()); + + Transport.send(message); + LOG.info("Mail sent to {}", Arrays.toString(tos)); + } catch (Exception e) { + LOG.error("Exception sending mail", e); + } + } + mq.clear(); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + break; + } + } + } catch (Exception e) { + LOG.error("Unexpected exception", e); + } + + LOG.info("MailSender thread started."); + } + + public void close() { + running = false; + interrupt(); + } + } + + public static class Mail { + private String subject; + private String content; + + public Mail(String subject) { + this(subject, (String)null); + } + + public Mail(String subject, String content) { + this.subject = subject; + this.content = content; + } + + public Mail(String subject, Exception e) { + this.subject = subject; + + StringWriter errors = new StringWriter(); + errors.append("Exception : ").append(e.getClass().getCanonicalName()).append("\n"); + errors.append(e.getMessage()).append("\n"); + errors.flush(); + e.printStackTrace(new PrintWriter(errors)); + this.content = errors.toString(); + } + + public String getSubject() { + return subject; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + } } diff --git a/src/main/java/com/skplanet/checkmate/yarn/YarnResourceManager.java b/src/main/java/com/skplanet/checkmate/yarn/YarnResourceManager.java index 8554ab1..6e07f2d 100644 --- a/src/main/java/com/skplanet/checkmate/yarn/YarnResourceManager.java +++ b/src/main/java/com/skplanet/checkmate/yarn/YarnResourceManager.java @@ -30,7 +30,7 @@ void GetUnfinishedApplications() { HttpUtil httpUtil = new HttpUtil(); int responseCode = 0; try { - StringBuffer buf = new StringBuffer(2048); + StringBuilder buf = new StringBuilder(2048); responseCode = httpUtil.getJson(url, buf); LOG.info("http response {}", responseCode); if (responseCode == 200) { diff --git a/www/css/querycache.css b/www/css/querycache.css index 681985c..dfa000a 100644 --- a/www/css/querycache.css +++ b/www/css/querycache.css @@ -94,10 +94,11 @@ table.fixed { table-layout: fixed; } + td.qServer, th.qServer { - width: 50px; + width: 100px; padding-left: 1px; padding-right: 1px; } @@ -105,7 +106,7 @@ th.qServer td.qId, th.qId { - width: 52px; + width: 102px; padding-left: 1px; padding-right: 1px; } @@ -113,7 +114,7 @@ th.qId td.qBackend, th.qBackend { - width: 72px; + width: 102px; padding-left: 1px; padding-right: 1px; } @@ -121,7 +122,7 @@ th.qBackend td.qUser, th.qUser { - width: 72px; + width: 102px; padding-left: 1px; padding-right: 1px; } @@ -137,7 +138,7 @@ th.qStatement td.qState, th.qState { - width: 48px; + width: 102px; padding-left: 1px; padding-right: 1px; } @@ -145,7 +146,7 @@ th.qState td.qClient, th.qClient { - width: 90px; + width: 122px; padding-left: 1px; padding-right: 1px; } @@ -156,12 +157,13 @@ th.qRows width: 72px; padding-left: 1px; padding-right: 1px; + text-align:right; } td.qStartTime, th.qStartTime { - width: 80px; + width: 120px; padding-left: 1px; padding-right: 1px; } @@ -169,7 +171,7 @@ th.qStartTime td.qElapsedTime, th.qElapsedTime { - width: 90px; + width: 150px; padding-left: 1px; padding-right: 1px; } diff --git a/www/qc/queries.funcs.html b/www/qc/queries.funcs.html index 9007c4d..12524b1 100644 --- a/www/qc/queries.funcs.html +++ b/www/qc/queries.funcs.html @@ -1,7 +1,6 @@ - <%@include file="queries.funcs.html"%>
-
-
-

Connection Pool Stats

- - - - - - -
serverbackendpool freepool useddirectcreated
-
+
+

Connection Pool Stats

+ + + + + + +
serverbackendpool freepool useddirectcreated
+
-
-

Object Pool Stats

- - - - - - -
serverTROWSETTROWTCOLUMNVALUETSTRINGVALUE
-
+
+

Object Pool Stats

+ + + + + + +
serverTROWSETTROWTCOLUMNVALUETSTRINGVALUE
+
-
diff --git a/www/qc/queries.funcs.html b/www/qc/queries.funcs.html index 12524b1..b8ad8f1 100644 --- a/www/qc/queries.funcs.html +++ b/www/qc/queries.funcs.html @@ -1,210 +1,203 @@ diff --git a/www/qc/queries.jsp b/www/qc/queries.jsp index 87d35f4..901dc39 100644 --- a/www/qc/queries.jsp +++ b/www/qc/queries.jsp @@ -1,37 +1,43 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%> - +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%> + +<% +boolean useWebSocket = clusters.getQcOption(request.getParameter("cluster")).isUseWebSocket(); +%> - - CheckMate :: QueryCache :: Queries + +CheckMate :: QueryCache :: Queries -<%@include file="header.jsp"%> - - - -<%@include file="queries.funcs.html"%> - -
-

In-Flight Queries

- - - - - - - - - - - - - - - - -
serveridtypeuserstatementstateclient iprowsstartTimeelapsedTimeCancel
+
+

In-Flight Queries

+ + + + + + + + + + + + + + + + + +
serveriduserquerystateclientrowsstartelapsedCancel
-

Complete Queries Manual Refresh

- - - - - - - - - - - - - - - -
serveridtypeuserstatementstateclient iprowsstartTimeelapsedTime
-
+

+ Complete Queries + Manual Refresh + +

+ + + + + + + + + + + + + + + + + +
serveriduserquerystateclientrowsstartendelapsed
+