├── client ├── data │ └── README.md └── src │ ├── README.md │ ├── GeoFenceBenchmark.java │ ├── BenchmarkCallback.java │ ├── BenchmarkConfig.java │ ├── DeviceSimulator.java │ └── BaseBenchmark.java ├── db ├── deployment.xml ├── ddl.sql └── src │ └── procedures │ └── PositionUpdate.java ├── start_db.sh ├── clean.sh ├── compile.sh ├── update_schema.sh ├── run_client.sh └── README.md /client/data/README.md: -------------------------------------------------------------------------------- 1 | # Geo-Fencing Data README 2 | 3 | Data file can be downloaded from: 4 | http://www.census.gov/geo/maps-data/data/docs/gazetteer/Gaz_zcta_national.zip 5 | -------------------------------------------------------------------------------- /db/deployment.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /start_db.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | . ./compile.sh 4 | 5 | HOST=localhost 6 | LICENSE=$VOLTDB_HOME/voltdb/license.xml 7 | DEPLOY=deployment.xml 8 | 9 | # start VoltDB in background (4.0 syntax) 10 | voltdb create ${CATALOG_NAME}.jar -H $HOST -d $DEPLOY -l $LICENSE --background 11 | 12 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # clean up temp files & folders in this project 4 | 5 | 6 | # db 7 | cd db 8 | rm -rf obj log debugoutput statement-plans voltdbroot *.jar catalog-report.html 9 | cd .. 10 | 11 | # client 12 | cd client 13 | rm -rf obj log loader_logs 14 | cd .. 15 | 16 | echo "removed temp files & folders" 17 | -------------------------------------------------------------------------------- /compile.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CATALOG_NAME="GEO_FENCE" 4 | 5 | # This script assumes voltdb/bin is in your path 6 | VOLTDB_HOME=$(dirname $(dirname "$(which voltdb)")) 7 | 8 | # entire script runs within this directory: 9 | cd db 10 | 11 | # compile java stored procedures 12 | if find src -name "*.java" &> /dev/null; then 13 | SRC=`find src -name "*.java"` 14 | CLASSPATH=`ls -1 $VOLTDB_HOME/voltdb/voltdb-*.jar` 15 | if [ ! -f $CLASSPATH ]; then 16 | echo "voltdb-*.jar file not found for CLASSPATH, edit this script to provide the correct path" 17 | exit 18 | fi 19 | mkdir -p obj 20 | javac -classpath $CLASSPATH -d obj $SRC 21 | # stop if compilation fails 22 | if [ $? != 0 ]; then exit; fi 23 | fi 24 | # compile VoltDB catalog 25 | if [ -f ddl.sql ]; then 26 | voltdb compile --classpath obj -o ${CATALOG_NAME}.jar ddl.sql 27 | # stop if compilation fails 28 | if [ $? != 0 ]; then exit; fi 29 | fi 30 | 31 | # Success 32 | echo "Successfully compiled ${CATALOG_NAME}.jar" 33 | 34 | 35 | -------------------------------------------------------------------------------- /update_schema.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CATALOG_NAME="GEO_FENCE" 4 | 5 | # This script assumes voltdb/bin is in your path 6 | VOLTDB_HOME=$(dirname $(dirname "$(which voltdb)")) 7 | 8 | # entire script runs within this directory: 9 | cd db 10 | 11 | # compile java stored procedures 12 | if find src -name "*.java" &> /dev/null; then 13 | SRC=`find src -name "*.java"` 14 | CLASSPATH=`ls -1 $VOLTDB_HOME/voltdb/voltdb-*.jar` 15 | if [ ! -f $CLASSPATH ]; then 16 | echo "voltdb-*.jar file not found for CLASSPATH, edit this script to provide the correct path" 17 | exit 18 | fi 19 | mkdir -p obj 20 | javac -classpath $CLASSPATH -d obj $SRC 21 | # stop if compilation fails 22 | if [ $? != 0 ]; then exit; fi 23 | fi 24 | # compile VoltDB catalog 25 | if [ -f ddl.sql ]; then 26 | voltdb compile --classpath obj -o ${CATALOG_NAME}.jar ddl.sql 27 | # stop if compilation fails 28 | if [ $? != 0 ]; then exit; fi 29 | fi 30 | 31 | # live update of schema 32 | voltadmin update ${CATALOG_NAME}.jar deployment.xml 33 | 34 | -------------------------------------------------------------------------------- /run_client.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SERVERS=localhost 4 | 5 | # This script assumes voltdb/bin is in your path 6 | VOLTDB_HOME=$(dirname $(dirname "$(which voltdb)")) 7 | 8 | # entire script runs within this directory: 9 | cd client 10 | 11 | # clean 12 | rm -rf obj log 13 | 14 | # set the classpath 15 | CLASSPATH=`ls -1 $VOLTDB_HOME/voltdb/voltdb-*.jar` 16 | if [ ! -f $CLASSPATH ]; then 17 | echo "voltdb-*.jar file not found for CLASSPATH, edit this script to provide the correct path" 18 | exit 19 | fi 20 | 21 | # the benchmark uses Apache commons CLI 22 | CLASSPATH="$CLASSPATH:`ls -1 $VOLTDB_HOME/lib/commons-cli-*.jar`" 23 | 24 | # compile 25 | mkdir -p obj 26 | SRC=`find src -name "*.java"` 27 | javac -classpath $CLASSPATH -d obj $SRC 28 | # stop if compilation fails 29 | if [ $? != 0 ]; then exit; fi 30 | 31 | # run the benchmark application 32 | echo "running benchmark..." 33 | java -classpath obj:$CLASSPATH:obj -Dlog4j.configuration=file://$VOLTDB_HOME/voltdb/log4j.xml \ 34 | client.GeoFenceBenchmark \ 35 | --displayinterval=5 \ 36 | --warmup=3 \ 37 | --duration=60 \ 38 | --servers=$SERVERS \ 39 | --autotune=true \ 40 | --latencytarget=1 \ 41 | --devices=1000000 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /client/src/README.md: -------------------------------------------------------------------------------- 1 | README for client package 2 | ========================= 3 | 4 | This java client source is based on the VoltDB example application "voter", specifically the AsyncBenchmark, but refactored to separate boilerplate from application-specific code. 5 | 6 | BaseBenchmark.java: 7 | - boilerplate 8 | - should not need to be modified 9 | - establishes connection, collects statistics, prints periodic stats, drives the main loops of the benchmark 10 | 11 | BenchmarkCallback.java 12 | - a general-purpose callback class for tracking the results of asynchronous stored-procedure calls. 13 | - Keeps a thread-safe count of invocations, commits, and rollbacks 14 | - provides a summary report 15 | 16 | BenchmarkConfig.java 17 | - defines the commmand-line arguments for the benchmark 18 | 19 | GeoFenceBenchmark.java 20 | - extends BaseBenchmark.java 21 | - uses command-line arguments from BenchmarkConfig.java 22 | - Provides the implementation for application-specific actions: 23 | initialize() - executed once, pre-populates devices 24 | iterate() 25 | - executed at a controlled rate throughout the duration of the benchmark 26 | - picks a random device and moves it randomly to a new location a few miles from the previous position 27 | - stored procedure in database will record this new position and detect entry/exit events 28 | printResults() - customized to list the results of the particular stored procedures involved. 29 | 30 | DeviceSimulator.java 31 | - called by GeoFenceBenchmark.java 32 | - loadZipcodes: loads zipcodes from a file 33 | - initializeDevices: generates data for devices using the zipcodes as potential "home" locations 34 | - randomMovement: generates data for random movement of a device 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | VoltDB Example: Geo-Fencing 2 | ============================ 3 | 4 | Use Case 5 | -------- 6 | One million devices are tracked as they provide continuous position (lat/long) updates and pass in and out of personalized geo-fence boundaries. Alert events are generated whenever a device exits or enters the fence, which is defined individually for each device based on a "home" location and a radius in miles. 7 | 8 | The client application creates the set of devices with their geo-fence defintions and then begins generating random device updates. Each device starts at it's home location and then each subsequent update is a based on a "random walk" incremental change from the last position. 9 | 10 | The database ingests these updates and keeps a history of the positions for each device. As it processes each update, it also checks to see if this device has a geo-fence enabled and if so, whether the device was previously in-bounds or out-of-bounds. It calculates the distance from "home" to the new location to see if it is in or out of bounds. If this is a change in status, it generates an "exit" or "entry" event record, which would result in a notification to the device owner. All of this logic is found in the "PositionUpdate" stored procedure. 11 | 12 | Code organization 13 | ----------------- 14 | ### db 15 | The database schema and stored procedures 16 | ### client 17 | The java benchmark client application. 18 | 19 | Instructions 20 | ------------ 21 | 22 | 1. Start the database in the background 23 | 24 | ./start_db.sh 25 | 26 | 2. Run the client application 27 | 28 | ./run_client.sh 29 | 30 | 3. Open a web browser to VoltDB Studio 31 | 32 | http://localhost:8080/studio 33 | 34 | 4. Run some queries: 35 | 36 | -- check for events: 37 | SELECT * FROM device_event; 38 | 39 | -- see the settings for a device: 40 | SELECT * FROM devices WHERE id = ?; 41 | 42 | -- see the position history for a device: 43 | SELECT * FROM device_location WHERE id = ? ORDER BY ts; 44 | 45 | 5. When finished, stop the database and clean up any temp files 46 | 47 | voltadmin shutdown 48 | ./clean.sh 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /client/src/GeoFenceBenchmark.java: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package client; 25 | 26 | import java.io.*; 27 | import java.util.*; 28 | import org.voltdb.*; 29 | import org.voltdb.client.*; 30 | 31 | public class GeoFenceBenchmark extends BaseBenchmark { 32 | 33 | private DeviceSimulator sim = new DeviceSimulator(); 34 | private int deviceCount = 0; 35 | 36 | // constructor 37 | public GeoFenceBenchmark(BenchmarkConfig config) { 38 | super(config); 39 | 40 | this.deviceCount = config.devices; 41 | } 42 | 43 | // this gets run once before the benchmark begins 44 | public void initialize() throws Exception { 45 | 46 | // ------ load zipcodes + positions into a map 47 | sim.loadZipcodes(config.zipcode_filename); 48 | 49 | // initialize devices 50 | sim.initializeDevices(deviceCount, client); 51 | 52 | } 53 | 54 | public void iterate() throws Exception { 55 | 56 | // pick a random device, with new position 57 | sim.randomMovement(client); 58 | 59 | } 60 | 61 | public static void main(String[] args) throws Exception { 62 | BenchmarkConfig config = BenchmarkConfig.getConfig("GeoFenceBenchmark",args); 63 | 64 | BaseBenchmark c = new GeoFenceBenchmark(config); 65 | c.runBenchmark(); 66 | } 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /db/ddl.sql: -------------------------------------------------------------------------------- 1 | -------------------- EXAMPLE SQL ----------------------------------------------- 2 | -- CREATE TABLE example_of_types ( 3 | -- id INTEGER NOT NULL, -- java int, 4-byte signed integer, -2,147,483,647 to 2,147,483,647 4 | -- name VARCHAR(40), -- java String 5 | -- data VARBINARY(256), -- java byte array 6 | -- status TINYINT, -- java byte, 1-byte signed integer, -127 to 127 7 | -- type SMALLINT, -- java short, 2-byte signed integer, -32,767 to 32,767 8 | -- pan BIGINT, -- java long, 8-byte signed integer, -9,223,372,036,854,775,807 to 9,223,372,036,854,775,807 9 | -- balance_open FLOAT, -- java double, 8-byte numeric 10 | -- balance DECIMAL, -- java BigDecimal, 16-byte fixed scale of 12 and precision of 38 11 | -- last_updated TIMESTAMP, -- java long, org.voltdb.types.TimestampType, 8-byte signed integer (milliseconds since epoch) 12 | -- CONSTRAINT pk_example_of_types PRIMARY KEY (id) 13 | -- ); 14 | -- PARTITION TABLE example_of_types ON COLUMN id; 15 | -- CREATE INDEX idx_example_pan ON example_of_types (pan); 16 | -- 17 | -- CREATE VIEW view_example AS 18 | -- SELECT type, COUNT(*) AS records, SUM(balance) 19 | -- FROM example_of_types 20 | -- GROUP BY type; 21 | -- 22 | -- CREATE PROCEDURE foo AS SELECT * FROM foo; 23 | -- CREATE PROCEDURE FROM CLASS procedures.UpsertSymbol; 24 | -- PARTITION PROCEDURE UpsertSymbol ON TABLE symbols COLUMN symbol PARAMETER 0; 25 | --------------------------------------------------------------------------------- 26 | 27 | CREATE TABLE devices ( 28 | id INTEGER NOT NULL, 29 | home_zip VARCHAR(5), 30 | home_lat FLOAT, 31 | home_long FLOAT, 32 | has_geofence TINYINT, 33 | fence_radius FLOAT, 34 | inside_geofence TINYINT, 35 | CONSTRAINT pk_devices PRIMARY KEY (id) 36 | ); 37 | PARTITION TABLE devices ON COLUMN id; 38 | 39 | 40 | CREATE TABLE device_location ( 41 | id INTEGER NOT NULL, 42 | ts TIMESTAMP NOT NULL, 43 | pos_lat FLOAT NOT NULL, 44 | pos_long FLOAT NOT NULL 45 | ); 46 | PARTITION TABLE device_location ON COLUMN id; 47 | CREATE INDEX idx_device_location ON device_location (id,ts); 48 | 49 | CREATE TABLE device_event ( 50 | id INTEGER NOT NULL, 51 | ts TIMESTAMP NOT NULL, 52 | is_exit TINYINT, 53 | is_entry TINYINT 54 | ); 55 | PARTITION TABLE device_event ON COLUMN id; 56 | 57 | 58 | CREATE PROCEDURE FROM CLASS procedures.PositionUpdate; 59 | PARTITION PROCEDURE PositionUpdate ON TABLE devices COLUMN id PARAMETER 0; 60 | -------------------------------------------------------------------------------- /client/src/BenchmarkCallback.java: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package client; 25 | 26 | import java.util.*; 27 | import com.google_voltpatches.common.collect.ConcurrentHashMultiset; 28 | import com.google_voltpatches.common.collect.Multiset; 29 | import org.voltdb.client.ClientResponse; 30 | import org.voltdb.client.ProcedureCallback; 31 | 32 | public class BenchmarkCallback implements ProcedureCallback { 33 | 34 | private static Multiset calls = ConcurrentHashMultiset.create(); 35 | private static Multiset commits = ConcurrentHashMultiset.create(); 36 | private static Multiset rollbacks = ConcurrentHashMultiset.create(); 37 | 38 | String procedureName; 39 | long maxErrors; 40 | 41 | public static void printProcedureResults(String procedureName) { 42 | System.out.println(" " + procedureName); 43 | System.out.println(" calls: " + calls.count(procedureName)); 44 | System.out.println(" commits: " + commits.count(procedureName)); 45 | System.out.println(" rollbacks: " + rollbacks.count(procedureName)); 46 | } 47 | 48 | public static void printAllResults() { 49 | for (String e : calls.elementSet()) { 50 | printProcedureResults(e); 51 | } 52 | } 53 | 54 | public BenchmarkCallback(String procedure, long maxErrors) { 55 | super(); 56 | this.procedureName = procedure; 57 | this.maxErrors = maxErrors; 58 | } 59 | 60 | public BenchmarkCallback(String procedure) { 61 | this(procedure, 5l); 62 | } 63 | 64 | @Override 65 | public void clientCallback(ClientResponse cr) { 66 | 67 | calls.add(procedureName,1); 68 | 69 | if (cr.getStatus() == ClientResponse.SUCCESS) { 70 | commits.add(procedureName,1); 71 | } else { 72 | long totalErrors = rollbacks.add(procedureName,1); 73 | 74 | System.err.println("DATABASE ERROR: " + cr.getStatusString()); 75 | 76 | if (totalErrors > maxErrors) { 77 | System.err.println("exceeded " + maxErrors + " maximum database errors - exiting client"); 78 | System.exit(-1); 79 | } 80 | 81 | } 82 | } 83 | } 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /client/src/BenchmarkConfig.java: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package client; 25 | 26 | import org.voltdb.CLIConfig; 27 | 28 | /** 29 | * Uses CLIConfig class to declaratively state command line options 30 | * with defaults and validation. 31 | */ 32 | public class BenchmarkConfig extends CLIConfig { 33 | 34 | // STANDARD BENCHMARK OPTIONS 35 | @Option(desc = "Comma separated list of the form server[:port] to connect to.") 36 | String servers = "localhost"; 37 | 38 | @Option(desc = "Volt user name") 39 | public String user = ""; 40 | 41 | @Option(desc = "Volt password") 42 | public String password = ""; 43 | 44 | @Option(desc = "Benchmark duration, in seconds.") 45 | int duration = 20; 46 | 47 | @Option(desc = "Interval for performance feedback, in seconds.") 48 | long displayinterval = 5; 49 | 50 | @Option(desc = "Warmup duration in seconds.") 51 | int warmup = 2; 52 | 53 | @Option(desc = "Maximum TPS rate for benchmark.") 54 | int ratelimit = 100000; 55 | 56 | @Option(desc = "Determine transaction rate dynamically based on latency.") 57 | boolean autotune = true; 58 | 59 | @Option(desc = "Server-side latency target for auto-tuning.") 60 | int latencytarget = 6; 61 | 62 | @Option(desc = "Filename to write raw summary statistics to.") 63 | String statsfile = ""; 64 | 65 | // CUSTOM OPTIONS 66 | @Option(desc = "Input data filename") 67 | String zipcode_filename = "data/Gaz_zcta_national.txt"; 68 | 69 | @Option(desc = "Number of devices") 70 | int devices = 1000000; 71 | 72 | public BenchmarkConfig() { 73 | } 74 | 75 | public static BenchmarkConfig getConfig(String classname, String[] args) { 76 | BenchmarkConfig config = new BenchmarkConfig(); 77 | config.parse(classname, args); 78 | return config; 79 | } 80 | 81 | @Override 82 | public void validate() { 83 | if (duration <= 0) exitWithMessageAndUsage("duration must be > 0"); 84 | if (warmup < 0) exitWithMessageAndUsage("warmup must be >= 0"); 85 | if (displayinterval <= 0) exitWithMessageAndUsage("displayinterval must be > 0"); 86 | if (ratelimit <= 0) exitWithMessageAndUsage("ratelimit must be > 0"); 87 | if (latencytarget <= 0) exitWithMessageAndUsage("latencytarget must be > 0"); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /db/src/procedures/PositionUpdate.java: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package procedures; 25 | 26 | import java.lang.Math; 27 | import java.lang.Double; 28 | import org.voltdb.*; 29 | import org.voltdb.types.TimestampType; 30 | import org.voltdb.client.ClientResponse; 31 | 32 | public class PositionUpdate extends VoltProcedure { 33 | 34 | public static double calcDistance(double latA, double longA, double latB, double longB) 35 | { 36 | double theDistance = (Math.sin(Math.toRadians(latA)) * 37 | Math.sin(Math.toRadians(latB)) + 38 | Math.cos(Math.toRadians(latA)) * 39 | Math.cos(Math.toRadians(latB)) * 40 | Math.cos(Math.toRadians(longA - longB))); 41 | 42 | return (Math.toDegrees(Math.acos(theDistance))) * 69.09; // in miles 43 | } 44 | 45 | public final SQLStmt getDevice = new SQLStmt( 46 | "SELECT * FROM devices WHERE id = ?;"); 47 | 48 | public final SQLStmt newPosition = new SQLStmt( 49 | "INSERT INTO device_location VALUES (?,?,?,?);"); 50 | 51 | public final SQLStmt newEvent = new SQLStmt( 52 | "INSERT INTO device_event VALUES (?,?,?,?);"); 53 | 54 | 55 | public final SQLStmt updateInsideFence = new SQLStmt( 56 | "UPDATE devices SET inside_geofence = ? WHERE id = ?;"); 57 | 58 | public long run(int id, 59 | TimestampType ts, 60 | double newLat, 61 | double newLong 62 | ) throws VoltAbortException { 63 | 64 | // get device record 65 | voltQueueSQL(getDevice,id); 66 | // insert new location 67 | voltQueueSQL(newPosition,id,ts,newLat,newLong); 68 | 69 | VoltTable[] a = voltExecuteSQL(); 70 | VoltTable t = a[0]; 71 | t.advanceRow(); 72 | int hasFence = (int)t.getLong(4); 73 | 74 | // if has_geofence, calculate distance from home 75 | if (hasFence == 1) { 76 | double homeLat = t.getDouble(2); 77 | double homeLong = t.getDouble(3); 78 | double radius = t.getDouble(5); 79 | int inside = (int)t.getLong(6); 80 | double dist = calcDistance(homeLat,homeLong, 81 | newLat,newLong); 82 | 83 | // if previously inside fence, and now beyond radius 84 | if (inside == 1 && dist > radius) { 85 | // exit event 86 | voltQueueSQL(newEvent,id,ts,1,0); 87 | // update inside_geofence = 0 88 | voltQueueSQL(updateInsideFence,0,id); 89 | voltExecuteSQL(true); 90 | } 91 | 92 | // if previously outside fence, and now within radius 93 | if (inside == 0 && dist < radius) { 94 | // entry event 95 | voltQueueSQL(newEvent,id,ts,0,1); 96 | // update inside_geofence = 1 97 | voltQueueSQL(updateInsideFence,1,id); 98 | voltExecuteSQL(true); 99 | } 100 | 101 | } 102 | 103 | return ClientResponse.SUCCESS; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /client/src/DeviceSimulator.java: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package client; 25 | 26 | import java.io.*; 27 | import java.util.*; 28 | import org.voltdb.client.*; 29 | import org.voltdb.types.TimestampType; 30 | 31 | public class DeviceSimulator { 32 | private Random rand = new Random(); 33 | 34 | static class Position { 35 | public double latitude; 36 | public double longitude; 37 | } 38 | private Map zipcodePositions = new HashMap(); 39 | private String[] zipcodes; 40 | private Position[] devicePositions; 41 | 42 | private int[] radii = {25,50,100,250}; 43 | 44 | //constructor 45 | public DeviceSimulator() { 46 | } 47 | 48 | public void loadZipcodes(String filename) throws Exception { 49 | 50 | int i=1; 51 | try { 52 | 53 | // read from file 54 | FileInputStream fis = new FileInputStream(filename); 55 | BufferedReader br = new BufferedReader(new InputStreamReader(fis, "UTF-8")); 56 | System.out.println("reading from file: " + filename); 57 | 58 | // skip header line 59 | String line = br.readLine(); 60 | 61 | // load file 62 | while ((line = br.readLine()) != null) { 63 | i++; 64 | String[] record = line.split("\\t"); // split by tab 65 | 66 | // add to zipcodePositions set 67 | String zipcode = record[0]; 68 | Position p = new Position(); 69 | p.latitude = Double.parseDouble(record[7]); 70 | p.longitude = Double.parseDouble(record[8]); 71 | zipcodePositions.put(zipcode,p); 72 | 73 | if (i % 10000 == 0) 74 | System.out.println(" read " + i + " lines"); 75 | } 76 | br.close(); 77 | System.out.println(" read " + Integer.toString(i) + " lines"); 78 | 79 | zipcodes = zipcodePositions.keySet().toArray(new String[0]); 80 | System.out.println("zipcodes array has length " + zipcodes.length); 81 | 82 | } catch (Exception e) { 83 | System.err.println("Exception reading line " + i + " of " + filename); 84 | throw new Exception(e); 85 | } 86 | 87 | } 88 | 89 | public void initializeDevices(int devices, Client client) throws Exception { 90 | 91 | System.out.println("Generating and storing " + devices + " devices..."); 92 | 93 | devicePositions = new Position[devices]; 94 | 95 | for (int i=0; i System.currentTimeMillis()) { 258 | iterate(); 259 | } 260 | 261 | printHeading("Starting Benchmark"); 262 | 263 | // reset the stats after warmup 264 | fullStatsContext.fetchAndResetBaseline(); 265 | periodicStatsContext.fetchAndResetBaseline(); 266 | 267 | // print periodic statistics to the console 268 | benchmarkStartTS = System.currentTimeMillis(); 269 | schedulePeriodicStats(); 270 | 271 | // Run the benchmark loop for the requested duration 272 | // The throughput may be throttled depending on client configuration 273 | System.out.println("\nRunning benchmark..."); 274 | final long benchmarkEndTime = System.currentTimeMillis() + (1000l * config.duration); 275 | while (benchmarkEndTime > System.currentTimeMillis()) { 276 | iterate(); 277 | } 278 | 279 | // cancel periodic stats printing 280 | timer.cancel(); 281 | 282 | // block until all outstanding txns return 283 | client.drain(); 284 | 285 | // print the summary results 286 | printResults(); 287 | 288 | // close down the client connections 289 | client.close(); 290 | } 291 | 292 | /** 293 | * Prints headings 294 | */ 295 | public static void printHeading(String heading) { 296 | System.out.print("\n"+HORIZONTAL_RULE); 297 | System.out.println(" " + heading); 298 | System.out.println(HORIZONTAL_RULE); 299 | } 300 | 301 | } 302 | --------------------------------------------------------------------------------