Skip to content
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

Merge dashboard stress test to 24.12 #2204

Merged
merged 2 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 13 additions & 19 deletions src/org/labkey/test/WebDriverWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@
import org.labkey.test.selenium.EphemeralWebElement;
import org.labkey.test.util.CodeMirrorHelper;
import org.labkey.test.util.Crawler;
import org.labkey.test.util.OptionalFeatureHelper;
import org.labkey.test.util.Ext4Helper;
import org.labkey.test.util.ExtHelper;
import org.labkey.test.util.LabKeyExpectedConditions;
import org.labkey.test.util.LogMethod;
import org.labkey.test.util.LoggedParam;
import org.labkey.test.util.OptionalFeatureHelper;
import org.labkey.test.util.PasswordUtil;
import org.labkey.test.util.RelativeUrl;
import org.labkey.test.util.TestLogger;
Expand Down Expand Up @@ -2265,21 +2265,12 @@ public WebElement doAndWaitForElementToRefresh(Runnable func, Locator loc, WebDr
* Wait for Supplier to return non-null non-false value
* @param wait milliseconds
* @return final result of Supplier.get()
* @see Timer#waitFor(Supplier)
*/
@Contract(pure = true)
public static <T> T waitFor(Supplier<T> checker, int wait)
{
long startTime = System.currentTimeMillis();
T result;
do
{
result = checker.get();
if (result != null && !Boolean.FALSE.equals(result))
break;
sleep(100);
} while ((System.currentTimeMillis() - startTime) < wait);

return result;
return new Timer(Duration.ofMillis(wait)).waitFor(checker);
}

public static void waitForEquals(String message, Supplier<?> expected, Supplier<?> actual, int wait)
Expand All @@ -2298,17 +2289,20 @@ public static void waitForNotEquals(String message, Supplier<?> expected, Suppli
}
}

public static void waitFor(Supplier<Boolean> checker, String failMessage, int wait)
/**
* @see Timer#waitFor(Supplier, String)
*/
public static <T> T waitFor(Supplier<T> checker, String failMessage, int wait)
{
waitFor(checker, () -> failMessage, wait);
return new Timer(Duration.ofMillis(wait)).waitFor(checker, failMessage);
}

public static void waitFor(Supplier<Boolean> checker, Supplier<String> failMessage, int wait)
/**
* @see Timer#waitFor(Supplier, Supplier)
*/
public static <T> T waitFor(Supplier<T> checker, Supplier<String> failMessageSupplier, int wait)
{
if (!waitFor(checker, wait))
{
throw new TimeoutException(failMessage.get() + TestLogger.formatElapsedTime(wait));
}
return new Timer(Duration.ofMillis(wait)).waitFor(checker, failMessageSupplier);
}

public File clickAndWaitForDownload(Locator elementToClick)
Expand Down
78 changes: 63 additions & 15 deletions src/org/labkey/test/WebTestHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
import org.labkey.remoteapi.CommandResponse;
import org.labkey.remoteapi.Connection;
import org.labkey.remoteapi.SimplePostCommand;
import org.labkey.remoteapi.query.DeleteRowsCommand;
import org.labkey.remoteapi.query.Filter;
import org.labkey.remoteapi.query.SelectRowsCommand;
import org.labkey.serverapi.reader.Readers;
import org.labkey.test.util.InstallCert;
import org.labkey.test.util.LogMethod;
Expand Down Expand Up @@ -79,9 +82,11 @@
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
Expand All @@ -107,6 +112,7 @@ public class WebTestHelper
private static boolean USE_CONTAINER_RELATIVE_URL = true;
private static final Map<String, Map<String, Cookie>> savedCookies = new HashMap<>();
private static final Map<String, String> savedSessionKeys = new HashMap<>();
private static final Map<String, String> savedApiKeys = new HashMap<>();

static { TestProperties.load(); }

Expand Down Expand Up @@ -139,31 +145,73 @@ public static String getSessionKey(String user)
}
String sessionId = sessionCookie.getValue();

if (!savedSessionKeys.containsKey(sessionId))
return savedSessionKeys.computeIfAbsent(sessionId, k -> createApiKey(getRemoteApiConnection(user, true), "session", null));
}

public static String createApiKey(Connection connection)
{
String description = UUID.randomUUID().toString(); // Something unique that we can query for deletion
String apiKey = createApiKey(connection, API_KEY, description);
savedApiKeys.put(apiKey, description);
return apiKey;
}

private static String createApiKey(Connection connection, String type, String description)
{
SimplePostCommand command = new SimplePostCommand("security", "createApiKey");
JSONObject json = new JSONObject();
json.put("type", type);
json.put("description", description);
command.setJsonObject(json);

try
{
CommandResponse response = command.execute(connection, "/");
String apikey = (String) response.getParsedData().get("apikey");
if (apikey == null)
{
TestLogger.error(response.getText());
throw new RuntimeException("Failed to generate %s key".formatted(type));
}
return apikey;
}
catch (CommandException | IOException e)
{
Connection connection = getRemoteApiConnection(user, true);
SimplePostCommand command = new SimplePostCommand("security", "createApiKey");
JSONObject json = new JSONObject();
json.put("type", "session");
command.setJsonObject(json);
throw new RuntimeException("Failed to generate %s key".formatted(type), e);
}
}

public static void deleteApiKey(Connection connection, String apiKey)
{
String description = savedApiKeys.get(apiKey);
if (description != null)
{
try
{
CommandResponse response = command.execute(connection, "/");
Object apikey = response.getParsedData().get("apikey");
if (apikey == null)
SelectRowsCommand selectRowsCommand = new SelectRowsCommand("core", "apiKeys");
selectRowsCommand.setFilters(List.of(new Filter("Description", description)));
List<Map<String, Object>> rows = selectRowsCommand.execute(connection, null).getRows();
if (rows.size() == 1)
{
TestLogger.error(response.getText());
throw new RuntimeException("Failed to generate session key");
DeleteRowsCommand deleteRowsCommand = new DeleteRowsCommand("core", "apiKeys");
deleteRowsCommand.setRows(rows);
deleteRowsCommand.execute(connection, null);
savedApiKeys.remove(apiKey);
}
else
{
TestLogger.log("Skipping apiKey deletion. Unexpected number of rows found: " + rows.size());
}
savedSessionKeys.put(sessionId, (String) apikey);
}
catch (CommandException | IOException e)
catch (IOException | CommandException e)
{
throw new RuntimeException("Unable to generate session key", e);
throw new RuntimeException(e);
}
}
return savedSessionKeys.get(sessionId);
else
{
TestLogger.warn("Refusing to delete an API key not created by this test");
}
}

public static boolean isUseContainerRelativeUrl()
Expand Down
16 changes: 16 additions & 0 deletions src/org/labkey/test/stress/AbstractScenario.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import org.jetbrains.annotations.Nullable;
import org.junit.Assert;
import org.labkey.remoteapi.Connection;
import org.labkey.test.TestProperties;
import org.labkey.test.teamcity.TeamCityUtils;
import org.labkey.test.util.TestDateUtils;
import org.labkey.test.util.TestLogger;

Expand Down Expand Up @@ -183,6 +185,20 @@ public final Set<T> finishScenario() throws InterruptedException
shutdownNow();
throw t;
}
finally
{
if (getResultsFile() != null)
{
if (TestProperties.isTestRunningOnTeamCity())
{
TeamCityUtils.publishArtifact(getResultsFile(), null);
}
else
{
TestLogger.log("Perf data available in: " + getResultsFile().getAbsolutePath());
}
}
}
}

/**
Expand Down
15 changes: 13 additions & 2 deletions src/org/labkey/test/util/APIContainerHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@
import org.labkey.remoteapi.security.DeleteContainerCommand;
import org.labkey.remoteapi.security.GetContainersCommand;
import org.labkey.test.BaseWebDriverTest;
import org.labkey.test.TestProperties;
import org.labkey.test.TestTimeoutException;
import org.labkey.test.WebTestHelper;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

Expand Down Expand Up @@ -147,15 +149,14 @@ public void deleteContainer(String path, boolean failIfNotFound, int wait) throw
{
WebTestHelper.logToServer("=Test= Starting container delete: " + path);

Timer deleteTimer = new Timer(Duration.ofMillis(wait));
DeleteContainerCommand dcc = new DeleteContainerCommand();
dcc.setTimeout(wait);
try
{
Connection defaultConnection = _test.createDefaultConnection();
defaultConnection.setTimeout(wait);
dcc.execute(defaultConnection, path);

WebTestHelper.logToServer("=Test= Finished container delete: " + path);
}
catch (CommandException e)
{
Expand All @@ -164,6 +165,14 @@ public void deleteContainer(String path, boolean failIfNotFound, int wait) throw
if (failIfNotFound)
fail("Container not found: " + path);
}
else if (TestProperties.isServerRemote() && e.getStatusCode() == HttpStatus.SC_GATEWAY_TIMEOUT)
{
TestLogger.log("Waiting for container deletion after Gateway Timeout (504) error");
deleteTimer.waitFor(() -> !doesContainerExist(path), () -> {
WebTestHelper.logToServer("=Test= Timed out deleting container: " + path);
return "Timed out deleting container [%s]".formatted(path);
});
}
else
{
throw new RuntimeException("Failed to delete container: " + path, e);
Expand All @@ -178,6 +187,8 @@ public void deleteContainer(String path, boolean failIfNotFound, int wait) throw
{
throw new RuntimeException("Failed to delete container: " + path, e);
}

WebTestHelper.logToServer("=Test= Finished container delete: " + path);
}

@Override
Expand Down
44 changes: 44 additions & 0 deletions src/org/labkey/test/util/Timer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@
package org.labkey.test.util;

import org.apache.commons.lang3.time.StopWatch;
import org.jetbrains.annotations.Contract;
import org.openqa.selenium.TimeoutException;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.function.Supplier;

import static org.labkey.test.WebDriverWrapper.sleep;

public class Timer
{
Expand Down Expand Up @@ -72,4 +77,43 @@ public boolean isTimedOut()
{
return _cancelled || timeRemaining().isNegative();
}

/**
* Wait for Supplier to return non-null non-false value. Supplier is guaranteed to run at least once.
* @return final result of Supplier.get()
*/
@Contract(pure = true)
public <T> T waitFor(Supplier<T> checker)
{
T result;
do
{
result = checker.get();
if (isSuccessResult(result))
break;
sleep(100);
} while (!isTimedOut());

return result;
}

public <T> T waitFor(Supplier<T> checker, Supplier<String> failMessageSupplier)
{
T result = waitFor(checker);
if (!isSuccessResult(result))
{
throw new TimeoutException(failMessageSupplier.get() + TestLogger.formatElapsedTime(_timeout.toMillis()));
}
return result;
}

public <T> T waitFor(Supplier<T> checker, String failMessage)
{
return waitFor(checker, () -> failMessage);
}

private static <T> boolean isSuccessResult(T result)
{
return result != null && !Boolean.FALSE.equals(result);
}
}
5 changes: 4 additions & 1 deletion src/org/labkey/test/util/perf/JsonPerfScenarioHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.labkey.test.params.perf.PerfScenario;
import org.labkey.test.stress.AbstractScenario;
import org.labkey.test.stress.RequestInfoTsvWriter;
import org.labkey.test.util.LogMethod;
import org.labkey.test.util.TestDateUtils;
import org.labkey.test.util.TestLogger;
import org.labkey.test.util.Timer;
Expand Down Expand Up @@ -56,6 +57,7 @@ public JsonPerfScenarioHelper setImportThreads(int importThreads)
return this;
}

@LogMethod
public Map<String, Result> runPerfScenarios(List<PerfScenario> scenarios) throws Exception
{
final ExecutorService importExecutor = Executors.newFixedThreadPool(importThreads);
Expand Down Expand Up @@ -94,7 +96,8 @@ private Result startImport(PerfScenario perfScenario)
}
ImportDataCommand command = new ImportDataCommand(schemaName, perfScenario.getTypeName());
command.setFile(perfDataFileSupplier.apply(perfScenario.getFileName()));
command.setTimeout(Math.max(connection.getTimeout(), perfScenario.getAverage() * importThreads));
command.setTimeout(Math.max(connection.getTimeout(), perfScenario.getAverage() * importThreads) * 2);
command.setInsertOption(ImportDataCommand.InsertOption.MERGE);
Timer timer = new Timer();
String msgSuffix = "";
try
Expand Down
Loading