io.trino.benchmark.driver.BenchmarkDriverOptions Maven / Gradle / Ivy
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.trino.benchmark.driver;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.net.HostAndPort;
import io.airlift.units.Duration;
import io.trino.client.ClientSession;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.CharsetEncoder;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.util.Locale.ENGLISH;
import static java.util.Objects.requireNonNull;
import static picocli.CommandLine.Option;
public class BenchmarkDriverOptions
{
private static final Splitter NAME_VALUE_SPLITTER = Splitter.on('=').limit(2);
private static final CharMatcher PRINTABLE_ASCII = CharMatcher.inRange((char) 0x21, (char) 0x7E);
private static final String DEFAULT_VALUE = "(default: ${DEFAULT-VALUE})";
@Option(names = "--server", paramLabel = "", defaultValue = "localhost:8080", description = "Trino server location " + DEFAULT_VALUE)
public String server;
@Option(names = "--user", paramLabel = "", description = "Username " + DEFAULT_VALUE)
public String user = System.getProperty("user.name");
@Option(names = "--catalog", paramLabel = "", description = "Default catalog")
public String catalog;
@Option(names = "--schema", paramLabel = "", description = "Default schema")
public String schema;
@Option(names = "--suite", paramLabel = "", description = "Suite to execute")
public List suites = new ArrayList<>();
@Option(names = "--suite-config", paramLabel = "", defaultValue = "suite.json", description = "Suites configuration file " + DEFAULT_VALUE)
public String suiteConfigFile;
@Option(names = "--sql", paramLabel = "", defaultValue = "sql", description = "Directory containing sql files " + DEFAULT_VALUE)
public String sqlTemplateDir;
@Option(names = "--query", paramLabel = "", description = "Queries to execute")
public List queries = new ArrayList<>();
@Option(names = "--debug", description = "Enable debug information")
public boolean debug;
@Option(names = "--session", paramLabel = "", description = "Session property (property can be used multiple times; format is key=value)")
public final List sessionProperties = new ArrayList<>();
@Option(names = "--extra-credential", paramLabel = "", description = "Extra credentials (property can be used multiple times; format is key=value)")
public final List extraCredentials = new ArrayList<>();
@Option(names = "--runs", paramLabel = "", defaultValue = "3", description = "Number of times to run each query " + DEFAULT_VALUE)
public int runs;
@Option(names = "--warm", paramLabel = "", defaultValue = "1", description = "Number of times to run each query for a warm-up " + DEFAULT_VALUE)
public int warm;
@Option(names = "--max-failures", paramLabel = "", defaultValue = "10", description = "Max number of consecutive failures before benchmark fails " + DEFAULT_VALUE)
public int maxFailures;
@Option(names = "--socks", paramLabel = "", description = "Socks proxy to use")
public HostAndPort socksProxy;
@Option(names = "--client-request-timeout", paramLabel = "", defaultValue = "2m", description = "Client request timeout " + DEFAULT_VALUE)
public Duration clientRequestTimeout;
@Option(names = "--disable-compression", description = "Disable compression of query results")
public boolean disableCompression;
public ClientSession getClientSession()
{
return new ClientSession(
parseServer(server),
user,
Optional.empty(),
"trino-benchmark",
Optional.empty(),
ImmutableSet.of(),
null,
catalog,
schema,
null,
ZoneId.systemDefault(),
Locale.getDefault(),
ImmutableMap.of(),
toProperties(this.sessionProperties),
ImmutableMap.of(),
ImmutableMap.of(),
extraCredentials.stream()
.collect(toImmutableMap(ClientExtraCredential::getName, ClientExtraCredential::getValue)),
null,
clientRequestTimeout,
disableCompression);
}
private static URI parseServer(String server)
{
server = server.toLowerCase(ENGLISH);
if (server.startsWith("http://") || server.startsWith("https://")) {
return URI.create(server);
}
HostAndPort host = HostAndPort.fromString(server);
try {
return new URI("http", null, host.getHost(), host.getPortOrDefault(80), null, null, null);
}
catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
}
private static Map toProperties(List sessionProperties)
{
ImmutableMap.Builder builder = ImmutableMap.builder();
for (ClientSessionProperty sessionProperty : sessionProperties) {
String name = sessionProperty.getName();
if (sessionProperty.getCatalog().isPresent()) {
name = sessionProperty.getCatalog().get() + "." + name;
}
builder.put(name, sessionProperty.getValue());
}
return builder.build();
}
public static final class ClientSessionProperty
{
private static final Splitter NAME_VALUE_SPLITTER = Splitter.on('=').limit(2);
private static final Splitter NAME_SPLITTER = Splitter.on('.');
private final Optional catalog;
private final String name;
private final String value;
public ClientSessionProperty(String property)
{
List nameValue = NAME_VALUE_SPLITTER.splitToList(property);
checkArgument(nameValue.size() == 2, "Session property: %s", property);
List nameParts = NAME_SPLITTER.splitToList(nameValue.get(0));
checkArgument(nameParts.size() == 1 || nameParts.size() == 2, "Invalid session property: %s", property);
if (nameParts.size() == 1) {
catalog = Optional.empty();
name = nameParts.get(0);
}
else {
catalog = Optional.of(nameParts.get(0));
name = nameParts.get(1);
}
value = nameValue.get(1);
verifyProperty(catalog, name, value);
}
public ClientSessionProperty(Optional catalog, String name, String value)
{
this.catalog = requireNonNull(catalog, "catalog is null");
this.name = requireNonNull(name, "name is null");
this.value = requireNonNull(value, "value is null");
verifyProperty(catalog, name, value);
}
private static void verifyProperty(Optional catalog, String name, String value)
{
checkArgument(catalog.isEmpty() || !catalog.get().isEmpty(), "Invalid session property: %s.%s:%s", catalog, name, value);
checkArgument(!name.isEmpty(), "Session property name is empty");
CharsetEncoder charsetEncoder = US_ASCII.newEncoder();
checkArgument(catalog.orElse("").indexOf('=') < 0, "Session property catalog must not contain '=': %s", name);
checkArgument(charsetEncoder.canEncode(catalog.orElse("")), "Session property catalog is not US_ASCII: %s", name);
checkArgument(name.indexOf('=') < 0, "Session property name must not contain '=': %s", name);
checkArgument(charsetEncoder.canEncode(name), "Session property name is not US_ASCII: %s", name);
checkArgument(charsetEncoder.canEncode(value), "Session property value is not US_ASCII: %s", value);
}
public Optional getCatalog()
{
return catalog;
}
public String getName()
{
return name;
}
public String getValue()
{
return value;
}
@Override
public String toString()
{
return (catalog.isPresent() ? catalog.get() + '.' : "") + name + '=' + value;
}
@Override
public int hashCode()
{
return Objects.hash(catalog, name, value);
}
@Override
public boolean equals(Object obj)
{
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
ClientSessionProperty other = (ClientSessionProperty) obj;
return Objects.equals(this.catalog, other.catalog) &&
Objects.equals(this.name, other.name) &&
Objects.equals(this.value, other.value);
}
}
public static final class ClientExtraCredential
{
private final String name;
private final String value;
public ClientExtraCredential(String extraCredential)
{
List nameValue = NAME_VALUE_SPLITTER.splitToList(extraCredential);
checkArgument(nameValue.size() == 2, "Extra credential: %s", extraCredential);
this.name = nameValue.get(0);
this.value = nameValue.get(1);
checkArgument(!name.isEmpty(), "Credential name is empty");
checkArgument(!value.isEmpty(), "Credential value is empty");
checkArgument(PRINTABLE_ASCII.matchesAllOf(name), "Credential name contains spaces or is not US_ASCII: %s", name);
checkArgument(name.indexOf('=') < 0, "Credential name must not contain '=': %s", name);
checkArgument(PRINTABLE_ASCII.matchesAllOf(value), "Credential value contains space or is not US_ASCII: %s", name);
}
public ClientExtraCredential(String name, String value)
{
this.name = requireNonNull(name, "name is null");
this.value = value;
}
public String getName()
{
return name;
}
public String getValue()
{
return value;
}
@Override
public String toString()
{
return name + '=' + value;
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ClientExtraCredential other = (ClientExtraCredential) o;
return Objects.equals(name, other.name) && Objects.equals(value, other.value);
}
@Override
public int hashCode()
{
return Objects.hash(name, value);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy