additional improvements to cqld4 driver

This commit is contained in:
Jonathan Shook 2021-07-26 01:09:49 -05:00
parent 05b3a50ec9
commit e7668610c3
7 changed files with 79 additions and 58 deletions

View File

@ -3,8 +3,8 @@ package io.nosqlbench.adapter.cqld4;
import com.datastax.oss.driver.api.core.CqlSession;
import io.nosqlbench.adapter.cqld4.opdispensers.CqlD4PreparedBatchOpDispenser;
import io.nosqlbench.adapter.cqld4.opdispensers.Cqld4BatchStatementDispenser;
import io.nosqlbench.adapter.cqld4.opdispensers.Cqld4PreparedOpDispenser;
import io.nosqlbench.adapter.cqld4.opdispensers.Cqld4SimpleCqlStatementDispenser;
import io.nosqlbench.adapter.cqld4.opdispensers.Cqld4PreparedStmtDispenser;
import io.nosqlbench.adapter.cqld4.opdispensers.Cqld4SimpleCqlStmtDispenser;
import io.nosqlbench.engine.api.activityimpl.OpDispenser;
import io.nosqlbench.engine.api.activityimpl.OpMapper;
import io.nosqlbench.engine.api.activityimpl.uniform.DriverSpaceCache;
@ -32,11 +32,11 @@ public class Cqld4OpMapper implements OpMapper<Cqld4Op> {
if (prepared && batch) {
return new CqlD4PreparedBatchOpDispenser(session, cmd, cfg);
} else if (prepared) {
return new Cqld4PreparedOpDispenser(session, cmd, cfg);
return new Cqld4PreparedStmtDispenser(session, cmd);
} else if (batch) {
return new Cqld4BatchStatementDispenser(session, cmd, cfg);
} else {
return new Cqld4SimpleCqlStatementDispenser(session, cmd, cfg);
return new Cqld4SimpleCqlStmtDispenser(session, cmd, cfg);
}
}

View File

@ -1,21 +0,0 @@
package io.nosqlbench.adapter.cqld4;
import java.util.concurrent.ConcurrentHashMap;
/**
* Maintain a cache of objects to use in a CQLD4 context.
*/
public class Cqld4SpaceCache {
private final ConcurrentHashMap<String, Cqld4Space> clientscopes = new ConcurrentHashMap<>();
private final Cqld4DriverAdapter adapter;
public Cqld4SpaceCache(Cqld4DriverAdapter adapter) {
this.adapter = adapter;
}
public Cqld4Space getSpace(String name) {
return clientscopes.computeIfAbsent(name, newName -> {
return new Cqld4Space(adapter);
});
}
}

View File

@ -7,36 +7,28 @@ import io.nosqlbench.adapter.cqld4.Cqld4Op;
import io.nosqlbench.adapter.cqld4.optypes.Cqld4PreparedStatement;
import io.nosqlbench.engine.api.activityimpl.OpDispenser;
import io.nosqlbench.engine.api.templating.ParsedCommand;
import io.nosqlbench.nb.api.config.standard.NBConfiguration;
import io.nosqlbench.virtdata.core.templates.ParsedTemplate;
import java.util.function.LongFunction;
public class Cqld4PreparedOpDispenser implements OpDispenser<Cqld4Op> {
public class Cqld4PreparedStmtDispenser implements OpDispenser<Cqld4Op> {
private final CqlSession session;
private final ParsedCommand cmd;
private final NBConfiguration cfg;
private final LongFunction<Object[]> varbinder;
private final PreparedStatement preparedStmt;
public Cqld4PreparedOpDispenser(CqlSession session, ParsedCommand cmd, NBConfiguration cfg) {
public Cqld4PreparedStmtDispenser(CqlSession session, ParsedCommand cmd) {
this.session = session;
this.cmd = cmd;
this.cfg = cfg;
// if (cmd.isDefinedDynamic("stmt")) {
// throw new OpConfigError("You must have a static template to create prepared statements. (Do not make the stmt field a binding itself)");
// }
ParsedTemplate parsed = cmd.getStmtAsTemplate().orElseThrow();
String preparedQueryString = parsed.getPositionalStatement(s -> "?");
varbinder = cmd.newArrayBinderFromBindPoints(parsed.getBindPoints());
preparedStmt = session.prepare(preparedQueryString);
String preparedQueryString = parsed.getPositionalStatement(s -> "?");
preparedStmt = session.prepare(preparedQueryString);
}
// TODO: Explain in the dev guide that apply in the op dispenser should do all the "bind" level stuff
@Override
public Cqld4Op apply(long value) {
Object[] parameters = varbinder.apply(value);

View File

@ -8,13 +8,13 @@ import io.nosqlbench.engine.api.activityimpl.OpDispenser;
import io.nosqlbench.engine.api.templating.ParsedCommand;
import io.nosqlbench.nb.api.config.standard.NBConfiguration;
public class Cqld4SimpleCqlStatementDispenser implements OpDispenser<Cqld4Op> {
public class Cqld4SimpleCqlStmtDispenser implements OpDispenser<Cqld4Op> {
private final CqlSession session;
private final ParsedCommand cmd;
private final NBConfiguration cfg;
public Cqld4SimpleCqlStatementDispenser(CqlSession session, ParsedCommand cmd, NBConfiguration cfg) {
public Cqld4SimpleCqlStmtDispenser(CqlSession session, ParsedCommand cmd, NBConfiguration cfg) {
this.session = session;
this.cmd = cmd;
this.cfg = cfg;

View File

@ -17,15 +17,46 @@ import java.util.function.LongFunction;
* static and dynamic elements of the operation together.
* In most cases, implementations of OpDispenser will be constructed
* within the logic of an {@link OpMapper} which is responsible for
* determine the type of OpDispenser to use as associated with a specific
* type {@code (<T>)}.
* determining the type of OpDispenser to use as associated with a specific
* type {@code (<T>)}. The OpMapper is called for each type of operation
* that is active during activity initialization. It's primary responsibility
* is figuring out what types of {@link OpDispenser}s to create based
* on the op templates provided by users. Once the activity is initialized,
* a set of op dispensers is held as live dispensers to use as needed
* to synthesize new operations from generated data in real time.
* </p>
*
* <hr/>
* <h2>Implementation Strategy</h2>
* <p>OpDispenser implementations are intended to be implemented
* for each type of distinct operation that is supported by a
* {@link io.nosqlbench.engine.api.activityimpl.uniform.DriverAdapter}.
* That is not to say that an OpDispenser can't be responsible for
* producing multiple types of operations. Operations which are similar
* in what they need and how they are constructed make sense to be implemented
* in the same op dispenser. Those which need different construction
* logic or fundamentally different types of field values should be implemented
* separately. The rule of thumb is to ensure that op construction patterns
* are easy to understand at the mapping level ({@link OpMapper}),
* and streamlined for fast execution at the synthesis level ({@link OpDispenser}).
*
* @param <T> The parameter type of the actual operation which will be used
* to hold all the details for executing an operation,
* generally something that implements {@link Runnable}.
* something that implements {@link Runnable}.
*/
public interface OpDispenser<T> extends LongFunction<T> {
@Override
/**
* The apply method in an op dispenser should do all of the work of
* creating an operation that is executable by some other caller.
* The value produced by the apply method should not require
* additional processing if a caller wants to execute the operation
* multiple times, as for retries.
*
* @param value The cycle number which serves as the seed for any
* generated op fields to be bound into an operation.
* @return an executable operation
*/
@Override
T apply(long value);
}

View File

@ -8,23 +8,42 @@ import java.util.function.Function;
* <p>
* <h2>Synopsis</h2>
* An OpMapper is responsible for converting parsed op templates
* into dispensers of operations. the intention of the user,
* into dispensers of operations based on the intention of the user.
*
* Op Templates as expressed as a set of field values, some literal, and
* some virtualized (to be generated per-cycle). The op template is
* parsed into a {@link ParsedCommand}.
* some dynamic, to be generated based on a specific cycle value.
* </p>
*
* <p>
* <h2>Concepts</h2>
* The OpMapper is a function (the op mapper) that returns another function (the op synthesizer).
* The returned function is then used to create actual operations in some executable form.
* The difference
* between the OpMapper and the OpDispenser is this: The OpMapper is responsible for
* identifying exactly what type of operation the user intends, according to the rules
* op construction documented by the driver maintainer. The OpDispenser is responsible
* for efficiently dispensing objects of a given type which can be used to execute an
* operation. In short, mapping op templates to the users' intention must happen first, and
* then building an operation efficiently with that specific knowledge can happen after.
* The OpMapper is basically a function that returns another function. The responsibility
* for creating executable operations is shared between the {@link OpMapper} and the
* {@link OpDispenser}. The logic needed to determine the type of an operation intended
* by the user (mapping) is different from the logic you use to construct that specific
* type of operation once you know the intent (dispensing). If you look at a example
* of doing these together in code, there is always a point at which you know what is
* needed to construct an operation. If you draw a line at this point, it represents
* the separation of responsibilities between op mappers and op dispensers.
* </p>
*
* <p>This separation of responsibilities serves as both a conceptual clarification as
* well as a way to optimize runtime behavior. In the NoSQLBench model, all of the first step
* (mapping, the responsibility of this class) occurs at initialization time of an activity.
* This means that mapping logic can be as clear, readable, type-safe and obvious as
* possible without any negative effect on the later phase. In fact, clarity and obviousness
* at this level serves to keep implementations of the next phase much more straight-forward
* and streamlined, since all that is left to do is assemble the known elements together
* into an executable operation.</p>
*
* </hr>
* <h2>Implementation Strategy</h2>
* <p>
* A view of an op template is provided in the {@link ParsedCommand} API. This allows
* you to examine the fields provided by users. It also lets you see which
* of these fields are defined as dynamic and which are simply static values.
* When multiple types of operations are supported for a driver adapter, you must decide
* on a distinct signature
*
* </p>
*
* <p>

View File

@ -417,7 +417,7 @@ public class SimpleActivity implements Activity, ProgressCapable {
* @param <O>
* @return
*/
protected <O> OpSequence<OpDispenser<O>> createOpSequenceFromCommands(Function<CommandTemplate, OpDispenser<O>> opinit) {
protected <O extends Runnable> OpSequence<OpDispenser<O>> createOpSequenceFromCommands(Function<CommandTemplate, OpDispenser<O>> opinit) {
Function<OpTemplate, CommandTemplate> f = CommandTemplate::new;
Function<OpTemplate, OpDispenser<O>> opTemplateOFunction = f.andThen(opinit);