ready for performance test
This commit is contained in:
parent
dd6b9c2281
commit
f61a1e672d
8 changed files with 226 additions and 37 deletions
|
|
@ -25,6 +25,11 @@
|
|||
<artifactId>contiguous-jdbc</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>nl.sanderhautvast</groupId>
|
||||
<artifactId>contiguous-jackson</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package nl.sanderhautvast.contiguous.demo;
|
||||
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import nl.sanderhautvast.contiguous.ListSerializer;
|
||||
import nl.sanderhautvast.contiguous.demo.repository.RandomStuffGenerator;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
|
|
@ -40,7 +43,7 @@ public class DemoApplication {
|
|||
jdbcTemplate.execute("drop table if exists customers");
|
||||
jdbcTemplate.execute("create table customers (name varchar(100), email varchar(100), streetname varchar(100), housenumber integer, city varchar(100), country varchar(100))");
|
||||
final RandomStuffGenerator generator = new RandomStuffGenerator();
|
||||
for (int i = 0; i < 100_000; i++) {
|
||||
for (int i = 0; i < 10_000; i++) {
|
||||
jdbcTemplate.update("insert into customers (name, email, streetname, housenumber, city, country) values(?,?,?,?,?,?)",
|
||||
ps -> {
|
||||
String firstName = generator.generateFirstName();
|
||||
|
|
@ -57,6 +60,14 @@ public class DemoApplication {
|
|||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Module jacksonModule() {
|
||||
final SimpleModule module = new SimpleModule("contiguous_module");
|
||||
module.addSerializer(new ListSerializer());
|
||||
return module;
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public JdbcTemplate jdbcTemplate() {
|
||||
return new JdbcTemplate(datasource());
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
package nl.sanderhautvast.contiguous.demo.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Customer {
|
||||
String name;
|
||||
String email;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
@Slf4j
|
||||
public class CustomerRepository {
|
||||
|
|
@ -21,7 +23,21 @@ public class CustomerRepository {
|
|||
}
|
||||
|
||||
public ContiguousList<Customer> getAllCustomers() {
|
||||
return jdbcTemplate.query("select * from customers limit 5", rs -> {
|
||||
return jdbcTemplate.query("select * from customers limit 10000", rs -> {
|
||||
return JdbcResults.toList(rs, Customer.class);
|
||||
});
|
||||
}
|
||||
|
||||
public List<Customer> getAllCustomersTraditional() {
|
||||
return jdbcTemplate.query("select * from customers", (rs, rowNum) -> new Customer(
|
||||
rs.getString("name"),rs.getString("email"),
|
||||
rs.getString("streetname"), rs.getInt("housenumber"),
|
||||
rs.getString("city"), rs.getString("country")
|
||||
));
|
||||
}
|
||||
|
||||
public List<Customer> getAllCustomersHybrid() {
|
||||
return jdbcTemplate.query("select * from customers", rs -> {
|
||||
return JdbcResults.toList(rs, Customer.class);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,14 +22,17 @@ public class DemoRestApi {
|
|||
}
|
||||
|
||||
@GetMapping(value = "/api/customers", produces = "application/json")
|
||||
public List<Customer> getCustomers() {
|
||||
try {
|
||||
ContiguousList<Customer> customers = customerRepository.getAllCustomers();
|
||||
log.info("customers {}", customers.size());
|
||||
return customers;
|
||||
} catch (Exception e) {
|
||||
log.error("Error", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
public ContiguousList<Customer> getCustomers() {
|
||||
return customerRepository.getAllCustomers();
|
||||
}
|
||||
|
||||
@GetMapping(value = "/api/customers/traditional", produces = "application/json")
|
||||
public List<Customer> getCustomersTraditional() {
|
||||
return customerRepository.getAllCustomersTraditional();
|
||||
}
|
||||
|
||||
@GetMapping(value = "/api/customers/hybrid", produces = "application/json")
|
||||
public List<Customer> getCustomersHybrid() {
|
||||
return customerRepository.getAllCustomersHybrid();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
logging.level.org.springframework=ERROR
|
||||
spring.jpa.hibernate.ddl-auto=none
|
||||
spring.datasource.driverClass=org.postgresql.Driver
|
||||
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
|
||||
|
|
|
|||
163
jmeter-test.jmx
Normal file
163
jmeter-test.jmx
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.5">
|
||||
<hashTree>
|
||||
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
|
||||
<stringProp name="TestPlan.comments"></stringProp>
|
||||
<boolProp name="TestPlan.functional_mode">false</boolProp>
|
||||
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
|
||||
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
|
||||
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="TestPlan.user_define_classpath"></stringProp>
|
||||
</TestPlan>
|
||||
<hashTree>
|
||||
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
|
||||
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
||||
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
||||
<boolProp name="LoopController.continue_forever">false</boolProp>
|
||||
<stringProp name="LoopController.loops">100</stringProp>
|
||||
</elementProp>
|
||||
<stringProp name="ThreadGroup.num_threads">1</stringProp>
|
||||
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
|
||||
<boolProp name="ThreadGroup.scheduler">false</boolProp>
|
||||
<stringProp name="ThreadGroup.duration"></stringProp>
|
||||
<stringProp name="ThreadGroup.delay"></stringProp>
|
||||
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
|
||||
</ThreadGroup>
|
||||
<hashTree>
|
||||
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="contiguous" enabled="false">
|
||||
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="HTTPSampler.domain">localhost</stringProp>
|
||||
<stringProp name="HTTPSampler.port">8080</stringProp>
|
||||
<stringProp name="HTTPSampler.protocol">http</stringProp>
|
||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||
<stringProp name="HTTPSampler.path">/api/customers</stringProp>
|
||||
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
||||
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||
</HTTPSamplerProxy>
|
||||
<hashTree/>
|
||||
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="traditional" enabled="true">
|
||||
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="HTTPSampler.domain">localhost</stringProp>
|
||||
<stringProp name="HTTPSampler.port">8080</stringProp>
|
||||
<stringProp name="HTTPSampler.protocol">http</stringProp>
|
||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||
<stringProp name="HTTPSampler.path">api/customers/traditional</stringProp>
|
||||
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
||||
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||
</HTTPSamplerProxy>
|
||||
<hashTree/>
|
||||
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="hybrid" enabled="false">
|
||||
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="HTTPSampler.domain">localhost</stringProp>
|
||||
<stringProp name="HTTPSampler.port">8080</stringProp>
|
||||
<stringProp name="HTTPSampler.protocol">http</stringProp>
|
||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||
<stringProp name="HTTPSampler.path">api/customers/hybrid</stringProp>
|
||||
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
||||
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||
</HTTPSamplerProxy>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
<ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="Aggregate Report" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<url>true</url>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="false">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<url>true</url>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
</hashTree>
|
||||
</jmeterTestPlan>
|
||||
|
|
@ -56,11 +56,11 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
/*
|
||||
* storage for dehydated objects
|
||||
*/
|
||||
private ByteBuffer data = ByteBuffer.allocate(32);
|
||||
private ByteBuffer data = ByteBuffer.allocate(4096);//TODO create constructor with capacity
|
||||
|
||||
private int bufferPosition;
|
||||
|
||||
private int[] elementIndices = new int[10]; // avoids autoboxing. Could also use standard ArrayList though
|
||||
private ArrayList<Integer> elementIndices = new ArrayList<>(); // avoids autoboxing. Could also use standard ArrayList though
|
||||
// is there a standard lib IntList??
|
||||
|
||||
private int size;
|
||||
|
|
@ -69,11 +69,7 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
|
||||
public ContiguousList(Class<E> type) {
|
||||
inspectType(type);
|
||||
elementIndices[0] = 0; // index of first element
|
||||
}
|
||||
|
||||
public Class<?> getElementType() {
|
||||
return rootHandler.getType();
|
||||
elementIndices.add(0); // index of first element
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -136,13 +132,7 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
return false;
|
||||
}
|
||||
storePropertyData(element, rootHandler);
|
||||
size += 1;
|
||||
|
||||
// keep track of where the objects are stored
|
||||
if (elementIndices.length < size + 1) {
|
||||
this.elementIndices = Arrays.copyOf(this.elementIndices, this.elementIndices.length * 2);
|
||||
}
|
||||
elementIndices[size] = bufferPosition;
|
||||
extend();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -189,7 +179,7 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
if (index < 0 || index >= size) {
|
||||
throw new IndexOutOfBoundsException("index <0 or >" + size);
|
||||
}
|
||||
data.position(elementIndices[index]);
|
||||
data.position(elementIndices.get(index));
|
||||
try {
|
||||
if (rootHandler instanceof BuiltinTypeHandler<?>) {
|
||||
Object read = ValueReader.read(data);
|
||||
|
|
@ -278,14 +268,14 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
if (index < 0 || index >= size) {
|
||||
throw new IndexOutOfBoundsException("index <0 or >" + size);
|
||||
}
|
||||
data.position(elementIndices[index]);
|
||||
data.position(elementIndices.get(index));
|
||||
if (rootHandler instanceof BuiltinTypeHandler<?>) {
|
||||
|
||||
BuiltinTypeHandler<?> handler = (BuiltinTypeHandler<?>) rootHandler;
|
||||
return getValue(handler);
|
||||
}
|
||||
// create a new instance of the list element type
|
||||
StringBuilder s = new StringBuilder();
|
||||
StringBuilder s = new StringBuilder(300);
|
||||
s.append("{");
|
||||
copyDataIntoStringBuilder(s, (CompoundTypeHandler) rootHandler);
|
||||
s.append("}");
|
||||
|
|
@ -303,7 +293,7 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
}
|
||||
|
||||
private static String quote(String out) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
StringBuilder s = new StringBuilder(out.length() + 2);
|
||||
out = s.append("\"").append(out).append("\"").toString();
|
||||
return out;
|
||||
}
|
||||
|
|
@ -315,9 +305,9 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
compoundType.getProperties().forEach(property -> {
|
||||
if (property instanceof BuiltinTypeHandler) {
|
||||
BuiltinTypeHandler<?> typeHandler = (BuiltinTypeHandler<?>) property;
|
||||
String name = typeHandler.getName();
|
||||
String value = getValue(typeHandler);
|
||||
s.append(quote(name)).append(": ").append(value);
|
||||
s.append(quote(typeHandler.getName()))
|
||||
.append(": ")
|
||||
.append(getValue(typeHandler));
|
||||
} else {
|
||||
CompoundTypeHandler p = (CompoundTypeHandler) property;
|
||||
s.append(p.getName()).append(":{");
|
||||
|
|
@ -608,16 +598,14 @@ public class ContiguousList<E> extends NotImplementedList<E> implements List<E>
|
|||
// used by SetterIterator
|
||||
void extend() {
|
||||
size += 1;
|
||||
// keep track of index of element in data
|
||||
elementIndices.add(bufferPosition);
|
||||
}
|
||||
|
||||
byte[] getData() {
|
||||
return Arrays.copyOfRange(data.array(), 0, bufferPosition);
|
||||
}
|
||||
|
||||
int[] getElementIndices() {
|
||||
return Arrays.copyOfRange(elementIndices, 0, size + 1);
|
||||
}
|
||||
|
||||
private void ensureFree(int length) {
|
||||
while (bufferPosition + length > data.capacity()) {
|
||||
byte[] bytes = this.data.array();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue