Skip to content

DA-359: Cbstats collection dialog #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ private Map<CBTools.Type, String> getToolsMap(String toolKey, String os) {
} else if (ALL_TOOLS.equals(toolKey)) {
map.put(CBTools.Type.CBC_PILLOW_FIGHT, path + "cbc-pillowfight" + suffix);
map.put(CBTools.Type.MCTIMINGS, path + "mctimings" + suffix);
map.put(CBTools.Type.CBSTATS, path + "cbstats" + suffix);

} else {
throw new IllegalStateException("Not implemented yet");
Expand Down Expand Up @@ -155,16 +156,24 @@ public void downloadDependencies() throws Exception {

ToolSpec cbTools = downloads.get(ALL_TOOLS);
String toolsDir = toolsPath + File.separator + cbTools.getInstallationPath();
if (CBTools.getTool(CBTools.Type.CBC_PILLOW_FIGHT).getStatus() == ToolStatus.NOT_AVAILABLE
&& !isInstalled(toolsPath, downloads.get(ALL_TOOLS), CBTools.Type.CBC_PILLOW_FIGHT)) {

Log.info("Downloading CB tools. The feature will be automatically enabled when the download is complete.");
CBTools.getTool(CBTools.Type.CBC_PILLOW_FIGHT).setStatus(ToolStatus.DOWNLOADING);
CBTools.getTool(CBTools.Type.MCTIMINGS).setStatus(ToolStatus.DOWNLOADING);
CBTools.Type[] toolTypes = {CBTools.Type.CBC_PILLOW_FIGHT, CBTools.Type.MCTIMINGS, CBTools.Type.CBSTATS};

boolean shouldDownload = false;
for (CBTools.Type toolType : toolTypes) {
if (CBTools.getTool(toolType).getStatus() == ToolStatus.NOT_AVAILABLE
&& !isInstalled(toolsPath, downloads.get(ALL_TOOLS), toolType)) {
shouldDownload = true;
CBTools.getTool(toolType).setStatus(ToolStatus.DOWNLOADING);
} else {
Log.debug(toolType + " tool is already installed");
setToolActive(ToolStatus.AVAILABLE, toolsDir, cbTools);
}
}

if (shouldDownload) {
Log.info("Downloading tools. The features will be automatically enabled when the download is complete.");
downloadAndUnzip(toolsDir, cbTools);
} else {
Log.debug("CB Tools are already installed");
setToolActive(ToolStatus.AVAILABLE, toolsDir, cbTools);
}

}
Expand Down
82 changes: 82 additions & 0 deletions src/main/java/com/couchbase/intellij/tools/CBStats.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.couchbase.intellij.tools;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.couchbase.intellij.database.ActiveCluster;
import com.couchbase.intellij.workbench.Log;

import utils.OSUtil;
import utils.ProcessUtils;

public class CBStats {
private final String bucketName;
private final String scopeName;
private final String collectionName;

private final String type;

public CBStats(String bucketName, String scopeName, String collectionName, String type) {
this.bucketName = bucketName;
this.scopeName = scopeName;
this.collectionName = collectionName;
this.type = type;
}

public String executeCommand() throws IOException, InterruptedException {
StringBuilder output;

List<String> command = new ArrayList<>();
// command.add(CBTools.getTool(CBTools.Type.CBSTATS).getPath());
String osArch = OSUtil.getOSArch();

// Determine the command path based on the operating system and architecture
switch (osArch) {
case OSUtil.MACOS_ARM:
case OSUtil.MACOS_64:
command.add("/Applications/Couchbase Server.app/Contents/Resources/couchbase-core/bin/cbstats");
break;
case OSUtil.WINDOWS_ARM:
case OSUtil.WINDOWS_64:
command.add("C:\\Program Files\\Couchbase\\Server\\bin\\cbstats");
break;
case OSUtil.LINUX_ARM:
case OSUtil.LINUX_64:
command.add("/opt/couchbase/bin/cbstats");
break;
default:
throw new UnsupportedOperationException("Unsupported operating system: " + osArch);
}
command.add((ActiveCluster.getInstance().getClusterURL().replaceFirst("^couchbase://", "")) + ":"
+ (ActiveCluster.getInstance().isSSLEnabled() ? "11207" : "11210"));
command.add("-u");
command.add(ActiveCluster.getInstance().getUsername());
command.add("-p");
command.add(ActiveCluster.getInstance().getPassword());

command.add("-b");
command.add(bucketName);

if (type.equalsIgnoreCase("collection")) {
command.add("collections");
command.add(scopeName + "." + collectionName);
} else if (type.equalsIgnoreCase("scope")) {
command.add("scopes");
command.add(scopeName);
}

ProcessBuilder processBuilder = new ProcessBuilder(command);
Process process = processBuilder.start();

output = new StringBuilder(ProcessUtils.returnOutput(process));

if (process.waitFor() == 0) {
Log.info("Command executed successfully");
} else {
Log.error("Command execution failed");
}

return output.toString();
}
}
3 changes: 2 additions & 1 deletion src/main/java/com/couchbase/intellij/tools/CBTools.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public enum Type {
CB_IMPORT,
CB_EXPORT,
CBC_PILLOW_FIGHT,
MCTIMINGS
CBSTATS,
MCTIMINGS,
}
}
151 changes: 151 additions & 0 deletions src/main/java/com/couchbase/intellij/tools/dialog/CbstatsDialog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package com.couchbase.intellij.tools.dialog;

import java.awt.BorderLayout;

import javax.swing.JComponent;
import javax.swing.JPanel;

import org.jetbrains.annotations.Nullable;

import com.couchbase.intellij.tools.CBStats;
import com.couchbase.intellij.workbench.Log;
import com.intellij.openapi.ui.DialogWrapper;

import utils.TemplateUtil;

public class CbstatsDialog extends DialogWrapper {

private final String bucketName;
private final String scopeName;
private final String collectionName;
private final String type;

private static final String MEMORY_USED_BY_COLLECTION = "Memory Used By Collection";
private static final String COLLECTION_DATA_SIZE = "Collection Data Size";
private static final String NUMBER_OF_ITEMS = "Number Of Items";
private static final String COLLECTION_NAME = "Collection Name";
private static final String NUMBER_OF_DELETE_OPERATIONS = "Number Of Delete Operations";
private static final String NUMBER_OF_GET_OPERATIONS = "Number Of Get Operations";
private static final String NUMBER_OF_STORE_OPERATIONS = "Number Of Store Operations";
private static final String SCOPE_NAME = "Scope Name";

public CbstatsDialog(String bucket, String scope, String collection, String type) {
super(true);
this.bucketName = bucket;
this.scopeName = scope;
this.collectionName = collection;
this.type = type;
init();
setTitle("Stats for " + type);
setResizable(true);
}

@Nullable
@Override
protected JComponent createCenterPanel() {
JPanel dialogPanel = new JPanel(new BorderLayout());

CBStats cbStats = new CBStats(bucketName, scopeName, collectionName, type);
String output = "";
try {
output = cbStats.executeCommand();
} catch (Exception ex) {
Log.error(ex);
}

String[] lines = output.split("\n");
String[] keys = new String[lines.length];
String[] values = new String[lines.length];
String[] helpTexts = new String[lines.length]; // array for help texts

for (int i = 0; i < lines.length; i++) {
int keyStartIndex = lines[i].indexOf(':', lines[i].indexOf(':') + 1) + 1;
int valueStartIndex = lines[i].lastIndexOf(':') + 1;
keys[i] = getFriendlyKeyName(lines[i].substring(keyStartIndex, valueStartIndex - 1).trim());
values[i] = getFriendlyValue(lines[i].substring(valueStartIndex).trim(), keys[i]);
helpTexts[i] = getHelpText(keys[i]); // get help text for each key
}

JPanel keyValuePanel = TemplateUtil.createKeyValuePanelWithHelp(keys, values, helpTexts, 1);

dialogPanel.add(keyValuePanel, BorderLayout.CENTER);

return dialogPanel;
}

private String getHelpText(String key) {
switch (key) {
case MEMORY_USED_BY_COLLECTION:
return "This is the memory used by the collection. It is measured in bytes.";
case COLLECTION_DATA_SIZE:
return "This is the size of the data in the collection. It is also measured in bytes.";
case NUMBER_OF_ITEMS:
return "This represents the number of items in the collection. It doesn't have a unit as it's a count.";
case COLLECTION_NAME:
return "This is the name of the collection. It's a string and doesn't have a unit.";
case NUMBER_OF_DELETE_OPERATIONS:
return "This is the number of delete operations that have been performed on the collection. It doesn't have a unit as it's a count.";
case NUMBER_OF_GET_OPERATIONS:
return "This is the number of get operations that have been performed on the collection. It doesn't have a unit as it's a count.";
case NUMBER_OF_STORE_OPERATIONS:
return "This is the number of store operations that have been performed on the collection. It doesn't have a unit as it's a count.";
case SCOPE_NAME:
return "This is the name of the scope that contains the collection. It's a string and doesn't have a unit.";
default:
return "";
}
}

private String getFriendlyKeyName(String key) {
switch (key) {
case "collections_mem_used":
return MEMORY_USED_BY_COLLECTION;
case "data_size":
return COLLECTION_DATA_SIZE;
case "items":
return NUMBER_OF_ITEMS;
case "name":
return COLLECTION_NAME;
case "ops_delete":
return NUMBER_OF_DELETE_OPERATIONS;
case "ops_get":
return NUMBER_OF_GET_OPERATIONS;
case "ops_store":
return NUMBER_OF_STORE_OPERATIONS;
case "scope_name":
return SCOPE_NAME;
default:
return key;
}
}

private String getFriendlyValue(String value, String key) {
switch (key) {
case MEMORY_USED_BY_COLLECTION:
case COLLECTION_DATA_SIZE:
long bytes = Long.parseLong(value);
double sizeInMB = bytes / (1024.0 * 1024.0);
if (sizeInMB >= 1024) {
double sizeInGB = sizeInMB / 1024.0;
return String.format("%.2f GB", sizeInGB);
} else {
return String.format("%.2f MB", sizeInMB);
}
case NUMBER_OF_DELETE_OPERATIONS:
case NUMBER_OF_GET_OPERATIONS:
case NUMBER_OF_STORE_OPERATIONS:
case NUMBER_OF_ITEMS:
long count = Long.parseLong(value);
if (count >= 1_000_000) {
return String.format("%.2f M", count / 1_000_000.0);
} else if (count >= 1_000) {
return String.format("%.2f K", count / 1_000.0);
} else {
return value;
}
default:
return value;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
import com.couchbase.intellij.tools.CBImport;
import com.couchbase.intellij.tools.CBTools;
import com.couchbase.intellij.tools.PillowFightDialog;
import com.couchbase.intellij.tools.dialog.CbstatsDialog;
import com.couchbase.intellij.tools.dialog.DDLExportDialog;
import com.couchbase.intellij.tools.dialog.ExportDialog;
import com.couchbase.intellij.tree.NewEntityCreationDialog.EntityType;
import com.couchbase.intellij.tree.docfilter.DocumentFilterDialog;
import com.couchbase.intellij.tree.node.*;
import com.couchbase.intellij.tree.overview.IndexOverviewDialog;
Expand Down Expand Up @@ -596,6 +596,21 @@ public void actionPerformed(@NotNull AnActionEvent e) {
actionGroup.add(simpleExport);
}

actionGroup.addSeparator();
AnAction viewStats = new AnAction("View Collection Statistics") {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
CbstatsDialog cbstatsDialog = new CbstatsDialog(
col.getBucket(),
col.getScope(),
col.getText(),
"collection"
);
cbstatsDialog.show();
}
};
actionGroup.add(viewStats);

showPopup(e, tree, actionGroup);
}
}
22 changes: 22 additions & 0 deletions src/main/java/utils/ProcessUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,26 @@ public static void printOutput(Process process, String message) throws IOExcepti
}
}
}

public static String returnOutput(Process process) throws IOException {
StringBuilder output = new StringBuilder();

// Process standard output
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
}

// Process standard error
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
}

return output.toString();
}
}
Loading