├── 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 |
--------------------------------------------------------------------------------