diff --git a/benchmark/charts/Insert Records-total-time.png b/benchmark/charts/Insert Records-total-time.png new file mode 100644 index 00000000..da4ecb36 Binary files /dev/null and b/benchmark/charts/Insert Records-total-time.png differ diff --git a/benchmark/charts/Search All Records-total-time.png b/benchmark/charts/Search All Records-total-time.png new file mode 100644 index 00000000..93ab0f50 Binary files /dev/null and b/benchmark/charts/Search All Records-total-time.png differ diff --git a/benchmark/charts/Search Each Record-total-time.png b/benchmark/charts/Search Each Record-total-time.png new file mode 100644 index 00000000..4a7f1027 Binary files /dev/null and b/benchmark/charts/Search Each Record-total-time.png differ diff --git a/benchmark/charts/Stream Records-total-time.png b/benchmark/charts/Stream Records-total-time.png new file mode 100644 index 00000000..3b37ec34 Binary files /dev/null and b/benchmark/charts/Stream Records-total-time.png differ diff --git a/benchmark/src/main/scala/benchmark/bench/Bench.scala b/benchmark/src/main/scala/benchmark/bench/Bench.scala index 21ee17b2..0b168a62 100644 --- a/benchmark/src/main/scala/benchmark/bench/Bench.scala +++ b/benchmark/src/main/scala/benchmark/bench/Bench.scala @@ -1,26 +1,30 @@ package benchmark.bench trait Bench { - val RecordCount: Int = 1_000_000 + val RecordCount: Int = 5_000_000 val StreamIterations: Int = 1 val SearchIterations: Int = 1 val tasks: List[Task] = List( Task("Insert Records", RecordCount, insertRecords), - Task("Stream Records", StreamIterations, streamRecords), + Task("Stream Records", StreamIterations * RecordCount, streamRecords), Task("Search Each Record", StreamIterations * RecordCount, searchEachRecord), - Task("Search All Records", StreamIterations, searchAllRecords) + Task("Search All Records", StreamIterations * RecordCount, searchAllRecords) ) + def name: String + def init(): Unit - protected def insertRecords(status: StatusCallback): Unit + protected def insertRecords(status: StatusCallback): Int + + protected def streamRecords(status: StatusCallback): Int - protected def streamRecords(status: StatusCallback): Unit + protected def searchEachRecord(status: StatusCallback): Int - protected def searchEachRecord(status: StatusCallback): Unit + protected def searchAllRecords(status: StatusCallback): Int - protected def searchAllRecords(status: StatusCallback): Unit + def size(): Long def dispose(): Unit } diff --git a/benchmark/src/main/scala/benchmark/bench/BenchmarkReport.scala b/benchmark/src/main/scala/benchmark/bench/BenchmarkReport.scala index 12a06180..d80d35bd 100644 --- a/benchmark/src/main/scala/benchmark/bench/BenchmarkReport.scala +++ b/benchmark/src/main/scala/benchmark/bench/BenchmarkReport.scala @@ -2,7 +2,11 @@ package benchmark.bench import fabric.rw.RW -case class BenchmarkReport(name: String, maxProgress: Double, logs: List[StatusLog]) +case class BenchmarkReport(benchName: String, + name: String, + maxProgress: Double, + size: Long, + logs: List[StatusLog]) object BenchmarkReport { implicit val rw: RW[BenchmarkReport] = RW.gen diff --git a/benchmark/src/main/scala/benchmark/bench/Runner.scala b/benchmark/src/main/scala/benchmark/bench/Runner.scala index 3ec58939..465e7213 100644 --- a/benchmark/src/main/scala/benchmark/bench/Runner.scala +++ b/benchmark/src/main/scala/benchmark/bench/Runner.scala @@ -1,6 +1,6 @@ package benchmark.bench -import benchmark.bench.impl.LightDBBench +import benchmark.bench.impl.{DerbyBench, H2Bench, LightDBBench, PostgreSQLBench, SQLiteBench} import fabric.io.JsonFormatter import fabric.rw.Convertible import lightdb.duckdb.DuckDBIndexer @@ -24,6 +24,10 @@ object Runner { "ldbHaloSQLite" -> LightDBBench(HaloDBStore, SQLiteIndexer), "ldbHaloH2" -> LightDBBench(HaloDBStore, H2Indexer), "ldbHaloDuck" -> LightDBBench(HaloDBStore, DuckDBIndexer), + "SQLite" -> SQLiteBench, + "PostgreSQL" -> PostgreSQLBench, + "H2" -> H2Bench, + "Derby" -> DerbyBench ) def main(args: Array[String]): Unit = { @@ -37,11 +41,19 @@ object Runner { val status = StatusCallback() status.start() scribe.info(s"Executing ${task.name} task...") - task.f(status) + val count = task.f(status) status.finish() + if (count != task.maxProgress.toInt) { + throw new RuntimeException(s"${bench.name} - ${task.name} expected ${task.maxProgress.toInt}, but received: $count") + } val logs = status.logs scribe.info(s"Completed in ${logs.last.elapsed} seconds") - BenchmarkReport(task.name, task.maxProgress, logs) + BenchmarkReport( + benchName = bench.name, + name = task.name, + maxProgress = task.maxProgress, + size = bench.size(), + logs = logs) } scribe.info(s"Disposing $implName benchmark...") bench.dispose() diff --git a/benchmark/src/main/scala/benchmark/bench/StatusCallback.scala b/benchmark/src/main/scala/benchmark/bench/StatusCallback.scala index 02b82622..e65abe35 100644 --- a/benchmark/src/main/scala/benchmark/bench/StatusCallback.scala +++ b/benchmark/src/main/scala/benchmark/bench/StatusCallback.scala @@ -5,7 +5,7 @@ import com.sun.management.OperatingSystemMXBean import java.lang.management.ManagementFactory -case class StatusCallback(every: Long = 1_000L) { +case class StatusCallback(every: Long = 10_000L) { val progress = new AtomicDouble(0.0) def logs: List[StatusLog] = _logs.reverse diff --git a/benchmark/src/main/scala/benchmark/bench/Task.scala b/benchmark/src/main/scala/benchmark/bench/Task.scala index e4699cc9..609edda7 100644 --- a/benchmark/src/main/scala/benchmark/bench/Task.scala +++ b/benchmark/src/main/scala/benchmark/bench/Task.scala @@ -1,3 +1,3 @@ package benchmark.bench -case class Task(name: String, maxProgress: Double = 1.0, f: StatusCallback => Unit) \ No newline at end of file +case class Task(name: String, maxProgress: Double = 1.0, f: StatusCallback => Int) \ No newline at end of file diff --git a/benchmark/src/main/scala/benchmark/bench/impl/DerbyBench.scala b/benchmark/src/main/scala/benchmark/bench/impl/DerbyBench.scala new file mode 100644 index 00000000..acf0d3fa --- /dev/null +++ b/benchmark/src/main/scala/benchmark/bench/impl/DerbyBench.scala @@ -0,0 +1,158 @@ +package benchmark.bench.impl + +import benchmark.bench.{Bench, StatusCallback} +import lightdb.util.Unique + +import java.io.File +import java.sql.{Connection, DriverManager} +import scala.collection.parallel.CollectionConverters._ + +object DerbyBench extends Bench { + private lazy val connection: Connection = { + val path = new File("db/derby").getCanonicalPath + val c = DriverManager.getConnection(s"jdbc:derby:$path;create=true") + c.setAutoCommit(false) + c + } + + override def name: String = "Derby" + + override def init(): Unit = { +// executeUpdate("DROP TABLE IF EXISTS people") + executeUpdate("CREATE TABLE people(id VARCHAR(255), name VARCHAR(255), age INTEGER)") + executeUpdate("CREATE INDEX id_idx ON people(id)") + executeUpdate("CREATE INDEX age_idx ON people(age)") + } + + override protected def insertRecords(status: StatusCallback): Int = { + val ps = connection.prepareStatement("INSERT INTO people(id, name, age) VALUES (?, ?, ?)") + try { + (0 until RecordCount) + .foldLeft(0)((total, index) => { + val person = Person( + name = Unique(), + age = index + ) + ps.setString(1, person.id) + ps.setString(2, person.name) + ps.setInt(3, person.age) + ps.addBatch() + status.progress.set(index + 1) + total + 1 + }) + } finally { + ps.executeBatch() + ps.close() + connection.commit() + } + } + + private def countRecords(): Int = { + val s = connection.createStatement() + try { + val rs = s.executeQuery("SELECT COUNT(*) FROM people") + try { + rs.next() + rs.getInt(1) + } finally { + rs.close() + } + } finally { + s.close() + } + } + + override protected def streamRecords(status: StatusCallback): Int = (0 until StreamIterations) + .foldLeft(0)((total, iteration) => { + val s = connection.createStatement() + val rs = s.executeQuery("SELECT * FROM people") + var count = 0 + while (rs.next()) { + count += 1 + } + rs.close() + s.close() + if (count != RecordCount) { + scribe.warn(s"RecordCount was not $RecordCount, it was $count") + } + status.progress.set(iteration + 1) + total + count + }) + + override protected def searchEachRecord(status: StatusCallback): Int = { + var counter = 0 + (0 until StreamIterations) + .foreach { iteration => + val ps = connection.prepareStatement("SELECT * FROM people WHERE age = ?") + (0 until RecordCount) + .foreach { index => + ps.setInt(1, index) + val rs = ps.executeQuery() + rs.next() + val person = Person( + name = rs.getString("name"), + age = rs.getInt("age"), + id = rs.getString("id") + ) + if (person.age != index) { + scribe.warn(s"${person.age} was not $index") + } + if (rs.next()) { + scribe.warn(s"More than one result for $index") + } + rs.close() + counter += 1 + status.progress.set((iteration + 1) * (index + 1)) + } + ps.close() + } + counter + } + + override protected def searchAllRecords(status: StatusCallback): Int = { + var counter = 0 + (0 until StreamIterations) + .par + .foreach { iteration => + val s = connection.createStatement() + val rs = s.executeQuery("SELECT * FROM people") + var count = 0 + while (rs.next()) { + count += 1 + counter += 1 + } + rs.close() + s.close() + if (count != RecordCount) { + scribe.warn(s"RecordCount was not $RecordCount, it was $count") + } + status.progress.set(iteration + 1) + } + counter + } + + override def size(): Long = { + def recurse(file: File): Long = if (file.isDirectory) { + file.listFiles().map(recurse).sum + } else { + file.length() + } + recurse(new File("db/derby")) + } + + override def dispose(): Unit = { + connection.commit() + connection.close() + } + + private def executeUpdate(sql: String): Unit = { + val s = connection.createStatement() + try { + s.executeUpdate(sql) + } finally { + s.close() + } + } + + case class Person(name: String, age: Int, id: String = Unique()) +} \ No newline at end of file diff --git a/benchmark/src/main/scala/benchmark/bench/impl/H2Bench.scala b/benchmark/src/main/scala/benchmark/bench/impl/H2Bench.scala new file mode 100644 index 00000000..1cf3a331 --- /dev/null +++ b/benchmark/src/main/scala/benchmark/bench/impl/H2Bench.scala @@ -0,0 +1,147 @@ +package benchmark.bench.impl + +import benchmark.bench.{Bench, StatusCallback} +import lightdb.util.Unique + +import java.io.File +import java.sql.{Connection, DriverManager} +import scala.collection.parallel.CollectionConverters._ + +object H2Bench extends Bench { + private lazy val connection: Connection = { + val path = new File("db/h2").getCanonicalPath + val c = DriverManager.getConnection(s"jdbc:h2:$path") + c.setAutoCommit(false) + c + } + + override def name: String = "H2" + + override def init(): Unit = { + executeUpdate("DROP TABLE IF EXISTS people") + executeUpdate("CREATE TABLE people(id VARCHAR NOT NULL, name TEXT, age INTEGER, PRIMARY KEY (id))") + executeUpdate("CREATE INDEX age_idx ON people(age)") + } + + override protected def insertRecords(status: StatusCallback): Int = { + val ps = connection.prepareStatement("INSERT INTO people(id, name, age) VALUES (?, ?, ?)") + try { + (0 until RecordCount) + .foldLeft(0)((total, index) => { + val person = Person( + name = Unique(), + age = index + ) + ps.setString(1, person.id) + ps.setString(2, person.name) + ps.setInt(3, person.age) + ps.addBatch() + status.progress.set(index + 1) + total + 1 + }) + } finally { + ps.executeBatch() + ps.close() + connection.commit() + } + } + + private def countRecords(): Int = { + val s = connection.createStatement() + try { + val rs = s.executeQuery("SELECT COUNT(*) FROM people") + try { + rs.next() + rs.getInt(1) + } finally { + rs.close() + } + } finally { + s.close() + } + } + + override protected def streamRecords(status: StatusCallback): Int = (0 until StreamIterations) + .foldLeft(0)((total, iteration) => { + val s = connection.createStatement() + val rs = s.executeQuery("SELECT * FROM people") + var count = 0 + while (rs.next()) { + count += 1 + } + rs.close() + s.close() + if (count != RecordCount) { + scribe.warn(s"RecordCount was not $RecordCount, it was $count") + } + status.progress.set(iteration + 1) + total + count + }) + + override protected def searchEachRecord(status: StatusCallback): Int = { + var counter = 0 + (0 until StreamIterations) + .foreach { iteration => + val ps = connection.prepareStatement("SELECT * FROM people WHERE age = ?") + (0 until RecordCount) + .foreach { index => + ps.setInt(1, index) + val rs = ps.executeQuery() + rs.next() + val person = Person( + name = rs.getString("name"), + age = rs.getInt("age"), + id = rs.getString("id") + ) + if (person.age != index) { + scribe.warn(s"${person.age} was not $index") + } + if (rs.next()) { + scribe.warn(s"More than one result for $index") + } + rs.close() + counter += 1 + status.progress.set((iteration + 1) * (index + 1)) + } + ps.close() + } + counter + } + + override protected def searchAllRecords(status: StatusCallback): Int = { + var counter = 0 + (0 until StreamIterations) + .par + .foreach { iteration => + val s = connection.createStatement() + val rs = s.executeQuery("SELECT * FROM people") + var count = 0 + while (rs.next()) { + count += 1 + counter += 1 + } + rs.close() + s.close() + if (count != RecordCount) { + scribe.warn(s"RecordCount was not $RecordCount, it was $count") + } + status.progress.set(iteration + 1) + } + counter + } + + override def size(): Long = new File("db/sqlite.db").length() + + override def dispose(): Unit = connection.close() + + private def executeUpdate(sql: String): Unit = { + val s = connection.createStatement() + try { + s.executeUpdate(sql) + } finally { + s.close() + } + } + + case class Person(name: String, age: Int, id: String = Unique()) +} \ No newline at end of file diff --git a/benchmark/src/main/scala/benchmark/bench/impl/LightDBBench.scala b/benchmark/src/main/scala/benchmark/bench/impl/LightDBBench.scala index c8b77b95..1cd07f4b 100644 --- a/benchmark/src/main/scala/benchmark/bench/impl/LightDBBench.scala +++ b/benchmark/src/main/scala/benchmark/bench/impl/LightDBBench.scala @@ -15,6 +15,8 @@ import java.nio.file.Path import scala.collection.parallel.CollectionConverters._ case class LightDBBench(sm: StoreManager, im: IndexerManager) extends Bench { + override def name: String = s"LightDB - ${sm.getClass.getSimpleName.replace("$", "")} - ${im.getClass.getSimpleName.replace("$", "")}" + override def init(): Unit = { val dbDir = new File("db") FileUtils.deleteDirectory(dbDir) @@ -25,7 +27,7 @@ case class LightDBBench(sm: StoreManager, im: IndexerManager) extends Bench { scribe.info("Initialized!") } - override protected def insertRecords(status: StatusCallback): Unit = DB.people.transaction { implicit transaction => + override protected def insertRecords(status: StatusCallback): Int = DB.people.transaction { implicit transaction => (0 until RecordCount) .foreach { index => DB.people.set(Person( @@ -34,26 +36,25 @@ case class LightDBBench(sm: StoreManager, im: IndexerManager) extends Bench { )) status.progress.set(index + 1) } + RecordCount } - override protected def streamRecords(status: StatusCallback): Unit = DB.people.transaction { implicit transaction => + override protected def streamRecords(status: StatusCallback): Int = DB.people.transaction { implicit transaction => (0 until StreamIterations) - .par .foreach { iteration => val count = DB.people.iterator.size if (count != RecordCount) { scribe.warn(s"RecordCount was not $RecordCount, it was $count") } - status.progress.set(iteration + 1) + status.progress.set((iteration + 1) * count) } + StreamIterations * RecordCount } - override protected def searchEachRecord(status: StatusCallback): Unit = DB.people.transaction { implicit transaction => + override protected def searchEachRecord(status: StatusCallback): Int = DB.people.transaction { implicit transaction => (0 until StreamIterations) - .par .foreach { iteration => (0 until RecordCount) - .par .foreach { index => val list = DB.people.query.filter(_.age === index).search.docs.list if (list.size != 1) { @@ -65,9 +66,10 @@ case class LightDBBench(sm: StoreManager, im: IndexerManager) extends Bench { status.progress.set((iteration + 1) * (index + 1)) } } + StreamIterations * RecordCount } - override protected def searchAllRecords(status: StatusCallback): Unit = DB.people.transaction { implicit transaction => + override protected def searchAllRecords(status: StatusCallback): Int = DB.people.transaction { implicit transaction => (0 until StreamIterations) .par .foreach { iteration => @@ -77,6 +79,16 @@ case class LightDBBench(sm: StoreManager, im: IndexerManager) extends Bench { } status.progress.set(iteration + 1) } + StreamIterations * RecordCount + } + + override def size(): Long = { + def recurse(file: File): Long = if (file.isDirectory) { + file.listFiles().map(recurse).sum + } else { + file.length() + } + recurse(new File("db")) } override def dispose(): Unit = DB.dispose() diff --git a/benchmark/src/main/scala/benchmark/bench/impl/PostgreSQLBench.scala b/benchmark/src/main/scala/benchmark/bench/impl/PostgreSQLBench.scala new file mode 100644 index 00000000..b333701d --- /dev/null +++ b/benchmark/src/main/scala/benchmark/bench/impl/PostgreSQLBench.scala @@ -0,0 +1,152 @@ +package benchmark.bench.impl + +import benchmark.bench.{Bench, StatusCallback} +import lightdb.util.Unique + +import java.io.File +import java.sql.{Connection, DriverManager} +import scala.collection.parallel.CollectionConverters._ + +object PostgreSQLBench extends Bench { + private lazy val connection: Connection = { + val c = DriverManager.getConnection("jdbc:postgresql://localhost:5432/imdb", "postgres", "password") + c.setAutoCommit(false) + c + } + + override def name: String = "PostgreSQL" + + override def init(): Unit = { + executeUpdate("DROP TABLE IF EXISTS people") + executeUpdate("CREATE TABLE people(id VARCHAR NOT NULL, name TEXT, age INTEGER, PRIMARY KEY (id))") + executeUpdate("CREATE INDEX age_idx ON people(age)") + } + + override protected def insertRecords(status: StatusCallback): Int = { + var batchSize = 0 + val ps = connection.prepareStatement("INSERT INTO people(id, name, age) VALUES (?, ?, ?)") + try { + (0 until RecordCount) + .foldLeft(0)((total, index) => { + val person = Person( + name = Unique(), + age = index + ) + ps.setString(1, person.id) + ps.setString(2, person.name) + ps.setInt(3, person.age) + ps.addBatch() + status.progress.set(index + 1) + batchSize += 1 + if (batchSize > 1_000_000) { + ps.executeBatch() + batchSize = 0 + } + total + 1 + }) + } finally { + ps.executeBatch() + ps.close() + connection.commit() + } + } + + private def countRecords(): Int = { + val s = connection.createStatement() + try { + val rs = s.executeQuery("SELECT COUNT(*) FROM people") + try { + rs.next() + rs.getInt(1) + } finally { + rs.close() + } + } finally { + s.close() + } + } + + override protected def streamRecords(status: StatusCallback): Int = (0 until StreamIterations) + .foldLeft(0)((total, iteration) => { + val s = connection.createStatement() + val rs = s.executeQuery("SELECT * FROM people") + var count = 0 + while (rs.next()) { + count += 1 + } + rs.close() + s.close() + if (count != RecordCount) { + scribe.warn(s"RecordCount was not $RecordCount, it was $count") + } + status.progress.set(iteration + 1) + total + count + }) + + override protected def searchEachRecord(status: StatusCallback): Int = { + var counter = 0 + (0 until StreamIterations) + .foreach { iteration => + val ps = connection.prepareStatement("SELECT * FROM people WHERE age = ?") + (0 until RecordCount) + .foreach { index => + ps.setInt(1, index) + val rs = ps.executeQuery() + rs.next() + val person = Person( + name = rs.getString("name"), + age = rs.getInt("age"), + id = rs.getString("id") + ) + if (person.age != index) { + scribe.warn(s"${person.age} was not $index") + } + if (rs.next()) { + scribe.warn(s"More than one result for $index") + } + rs.close() + counter += 1 + status.progress.set((iteration + 1) * (index + 1)) + } + ps.close() + } + counter + } + + override protected def searchAllRecords(status: StatusCallback): Int = { + var counter = 0 + (0 until StreamIterations) + .par + .foreach { iteration => + val s = connection.createStatement() + val rs = s.executeQuery("SELECT * FROM people") + var count = 0 + while (rs.next()) { + count += 1 + counter += 1 + } + rs.close() + s.close() + if (count != RecordCount) { + scribe.warn(s"RecordCount was not $RecordCount, it was $count") + } + status.progress.set(iteration + 1) + } + counter + } + + override def size(): Long = new File("db/sqlite.db").length() + + override def dispose(): Unit = connection.close() + + private def executeUpdate(sql: String): Unit = { + val s = connection.createStatement() + try { + s.executeUpdate(sql) + } finally { + s.close() + } + } + + case class Person(name: String, age: Int, id: String = Unique()) +} \ No newline at end of file diff --git a/benchmark/src/main/scala/benchmark/bench/impl/SQLiteBench.scala b/benchmark/src/main/scala/benchmark/bench/impl/SQLiteBench.scala new file mode 100644 index 00000000..400ad02b --- /dev/null +++ b/benchmark/src/main/scala/benchmark/bench/impl/SQLiteBench.scala @@ -0,0 +1,146 @@ +package benchmark.bench.impl + +import benchmark.bench.{Bench, StatusCallback} +import lightdb.util.Unique + +import java.io.File +import java.sql.{Connection, DriverManager} +import scala.collection.parallel.CollectionConverters._ + +object SQLiteBench extends Bench { + private lazy val connection: Connection = { + val c = DriverManager.getConnection("jdbc:sqlite:db/sqlite.db") + c.setAutoCommit(false) + c + } + + override def name: String = "SQLite" + + override def init(): Unit = { + executeUpdate("DROP TABLE IF EXISTS people") + executeUpdate("CREATE TABLE people(id VARCHAR NOT NULL, name TEXT, age INTEGER, PRIMARY KEY (id))") + executeUpdate("CREATE INDEX age_idx ON people(age)") + } + + override protected def insertRecords(status: StatusCallback): Int = { + val ps = connection.prepareStatement("INSERT INTO people(id, name, age) VALUES (?, ?, ?)") + try { + (0 until RecordCount) + .foldLeft(0)((total, index) => { + val person = Person( + name = Unique(), + age = index + ) + ps.setString(1, person.id) + ps.setString(2, person.name) + ps.setInt(3, person.age) + ps.addBatch() + status.progress.set(index + 1) + total + 1 + }) + } finally { + ps.executeBatch() + ps.close() + connection.commit() + } + } + + private def countRecords(): Int = { + val s = connection.createStatement() + try { + val rs = s.executeQuery("SELECT COUNT(*) FROM people") + try { + rs.next() + rs.getInt(1) + } finally { + rs.close() + } + } finally { + s.close() + } + } + + override protected def streamRecords(status: StatusCallback): Int = (0 until StreamIterations) + .foldLeft(0)((total, iteration) => { + val s = connection.createStatement() + val rs = s.executeQuery("SELECT * FROM people") + var count = 0 + while (rs.next()) { + count += 1 + } + rs.close() + s.close() + if (count != RecordCount) { + scribe.warn(s"RecordCount was not $RecordCount, it was $count") + } + status.progress.set(iteration + 1) + total + count + }) + + override protected def searchEachRecord(status: StatusCallback): Int = { + var counter = 0 + (0 until StreamIterations) + .foreach { iteration => + val ps = connection.prepareStatement("SELECT * FROM people WHERE age = ?") + (0 until RecordCount) + .foreach { index => + ps.setInt(1, index) + val rs = ps.executeQuery() + rs.next() + val person = Person( + name = rs.getString("name"), + age = rs.getInt("age"), + id = rs.getString("id") + ) + if (person.age != index) { + scribe.warn(s"${person.age} was not $index") + } + if (rs.next()) { + scribe.warn(s"More than one result for $index") + } + rs.close() + counter += 1 + status.progress.set((iteration + 1) * (index + 1)) + } + ps.close() + } + counter + } + + override protected def searchAllRecords(status: StatusCallback): Int = { + var counter = 0 + (0 until StreamIterations) + .par + .foreach { iteration => + val s = connection.createStatement() + val rs = s.executeQuery("SELECT * FROM people") + var count = 0 + while (rs.next()) { + count += 1 + counter += 1 + } + rs.close() + s.close() + if (count != RecordCount) { + scribe.warn(s"RecordCount was not $RecordCount, it was $count") + } + status.progress.set(iteration + 1) + } + counter + } + + override def size(): Long = new File("db/sqlite.db").length() + + override def dispose(): Unit = connection.close() + + private def executeUpdate(sql: String): Unit = { + val s = connection.createStatement() + try { + s.executeUpdate(sql) + } finally { + s.close() + } + } + + case class Person(name: String, age: Int, id: String = Unique()) +} \ No newline at end of file diff --git a/build.sbt b/build.sbt index fabb9e78..d14d409f 100644 --- a/build.sbt +++ b/build.sbt @@ -240,6 +240,7 @@ lazy val benchmark = project.in(file("benchmark")) "org.postgresql" % "postgresql" % "42.7.3", "org.mariadb.jdbc" % "mariadb-java-client" % "3.3.3", "org.xerial" % "sqlite-jdbc" % sqliteVersion, + "org.apache.derby" % "derby" % "10.17.1.0", "commons-io" % "commons-io" % "2.16.1", "co.fs2" %% "fs2-io" % "3.9.4", "com.outr" %% "scarango-driver" % "3.20.0", diff --git a/run_benchmarks.sh b/run_benchmarks.sh index c055bae3..b0162a1e 100755 --- a/run_benchmarks.sh +++ b/run_benchmarks.sh @@ -1,8 +1,15 @@ #!/bin/bash -declare -a arr=("ldbHaloLucene" "ldbMapLucene" "ldbRocksLucene" "ldbAtomicLucene" "ldbMapLucene" "ldbHaloSQLite" "ldbHaloH2" "ldbHaloDuck") +set -e + +#declare -a arr=("ldbHaloLucene" "ldbMapLucene" "ldbRocksLucene" "ldbAtomicLucene" "ldbMapLucene" "ldbHaloSQLite" "ldbHaloH2" "ldbHaloDuck") +#declare -a arr=("ldbHaloLucene" "SQLite") +#declare -a arr=("PostgreSQL") +declare -a arr=("ldbHaloLucene" "SQLite" "H2" "Derby") for i in "${arr[@]}" do sbt "benchmark / runMain benchmark.bench.Runner $i" -done \ No newline at end of file +done + +sbt "benchmark / runMain benchmark.bench.ReportGenerator" \ No newline at end of file