Hi,
We get this exception when we enable multithreading option along with
lock_mode = 0. We dont mind getting a bit outdated data (we dont have
transactional level data) but we dont want exceptions to be thrown. It
would be really helpful if we make multithreading option to work
somehow because we get extremely nice throughput when we try this
option. I dont mind having more memory than 4 GB or more CPUs but
increasing them gives no better throughput. When we went through H2
code we found a database level synchronising without multi threading
option which we know is reliable but slow according to us.
To give us the history of our implementation. we spent a lot of time
investigating the available technology for caching data as we have a
application with extremely high read and write requirements. We tried
lot of in-memory databases and cache; jboss cache, Teracotta, Derby,
H2 to mention a few. H2 clearly outperformed all of them for our
application. Even though jboss cache was good, their searchable cache
was unreliable and extremely slow.
We use in memory mode. Using larger cache size did not help at all. As
said adding even 6 64-bit more CPUs (to the existing 2 64-bit CPUS)
never did anything good.
Below is one of our important table and its index details
TABLE CURRENT_MAP_POSITION_CACHE
(
CLIENT_ID INT NOT NULL,
DEVICE_ID INT NOT NULL,
STATUS NUMBER ,
GPS_STATUS NUMBER ,
INVOKE_CODE NUMBER ,
OBSERVATION_TIME TIMESTAMP ,
LOC_OBS_TIME TIMESTAMP ,
UPDATE_TIME TIMESTAMP ,
HEADING NUMBER ,
SPEED NUMBER ,
LATITUDE DOUBLE(53) ,
LONGITUDE DOUBLE(53) ,
MRP NUMBER ,
MILEAGE NUMBER ,
DST NUMBER ,
GEO_COUNT NUMBER ,
SWITCH_STATUS NUMBER ,
AUX1 NUMBER ,
STOP_DURATION NUMBER ,
MONTH_DATE VARCHAR2(50) ,
SCREEN_NAME VARCHAR2(50) ,
LOCATION_MESSAGE VARCHAR2(100) ,
MAP_HEADING VARCHAR2(50) ,
SWITCH1 NUMBER NOT NULL ,
SWITCH2 NUMBER NOT NULL ,
SWITCH3 NUMBER NOT NULL ,
SWITCH4 NUMBER NOT NULL ,
AUX2 NUMBER ,
AUX3 NUMBER ,
AUX4 NUMBER ,
AUX5 NUMBER ,
AUX6 NUMBER ,
AUX7 NUMBER ,
AUX8 NUMBER ,
AUX9 NUMBER ,
AUX10 NUMBER ,
STATUS_NUMBER NUMBER,
MESSAGE_ID NUMBER,
MESSAGE_READ VARCHAR2(1),
BATTERY_VOLTAGE NUMBER,
TETHER_STATUS NUMBER NOT NULL ,
ADDRESS NUMBER,
STREET VARCHAR2(40),
CROSS_STREET VARCHAR2(40),
ZIP VARCHAR2(40),
COUNTY VARCHAR2(40),
CITY VARCHAR2(40),
STATE VARCHAR2(40),
LANDMARK_ID NUMBER,
LANDMARK VARCHAR2(50),
COUNTRY VARCHAR2(40),
LOC_EVENT_TIME TIMESTAMP,
HOUSE_NUMBER VARCHAR2(40),
CARD_ID VARCHAR2(25),
CARD_EVENT_TIME TIMESTAMP NOT NULL ,
CARD_EVENT_TYPE NUMBER,
LOGIN_TIME TIMESTAMP,
IS_PINGABLE NUMBER,
STOP1 NUMBER,
STOP2 NUMBER,
STOP3 NUMBER,
DEVICE_TYPE_ID NUMBER,
TIME_ZONE_ID NUMBER,
POSITION_COLOR NUMBER,
IS_POCKET_EDITION NUMBER,
DEVICE_GROUP_ID NUMBER,
EID VARCHAR2(60),
FIRST_NAME VARCHAR2(60),
LAST_NAME VARCHAR2(60),
LAST_UPDATE_TIME TIMESTAMP
)
INDEX IDX1_CURRENT_MAP_POSITION_CACHE ON CURRENT_MAP_POSITION_CACHE
(DEVICE_ID )
INDEX IDX2_CURRENT_MAP_POSITION_CACHE ON CURRENT_MAP_POSITION_CACHE
(CLIENT_ID)
INDEX IDX3_CURRENT_MAP_POSITION_CACHE ON CURRENT_MAP_POSITION_CACHE
(CLIENT_ID, DEVICE_ID)
INDEX IDX4_CURRENT_MAP_POSITION_CACHE ON CURRENT_MAP_POSITION_CACHE
(CLIENT_ID, DEVICE_ID, LOC_EVENT_TIME )
Lot of Read queries happen as below with sample bind values
SELECT
count(device_id) AS cnt,
FLOOR (ABS ((latitude - -90)) / 15.002643002755175 ) AS x,
FLOOR (ABS ((longitude - -180.0)) / 22.5) AS y,
MIN(ROUND(longitude + 180,
6) *1000000 + ROUND(latitude + 90,
6) *0.0001) AS centroid ,
CASE WHEN COUNT(device_id) <= 5 THEN group_concat
(device_id separator ', ')
ELSE NULL END ids ,
LAST_UPDATE_TIME
FROM
current_map_position_cache cmp
WHERE
cmp.invoke_code != -1
AND cmp.client_id = 21113
AND ( latitude BETWEEN -90
AND 90.0
OR latitude BETWEEN 90.0
AND -90 )
AND ( longitude BETWEEN -180.0
AND 180.0
OR longitude BETWEEN 180.0
AND -180.0 )
group by
x,
y
Merge statements are also executed along side with the following
queries but at less speeds which syncs with database. When we gone
through the H2 code we found that it actually deletes and inserts the
data.
MERGE INTO CURRENT_MAP_POSITION_CACHE
(DEVICE_ID,CLIENT_ID,STATUS,GPS_STATUS,INVOKE_CODE,OBSERVATION_TIME,
LOC_OBS_TIME,UPDATE_TIME,HEADING,SPEED,LATITUDE,LONGITUDE,MRP,MILEAGE,
DST,GEO_COUNT,SWITCH_STATUS,AUX1,STOP_DURATION,MONTH_DATE,SCREEN_NAME,
LOCATION_MESSAGE,MAP_HEADING,SWITCH1,SWITCH2,SWITCH3,SWITCH4,AUX2,AUX3,
AUX4,AUX5,AUX6,AUX7,AUX8,AUX9,AUX10,STATUS_NUMBER,MESSAGE_ID,
MESSAGE_READ,BATTERY_VOLTAGE,TETHER_STATUS,ADDRESS,STREET,
CROSS_STREET,ZIP,COUNTY,CITY,STATE,LANDMARK_ID,LANDMARK,COUNTRY,LOC_EVENT_TIME,
HOUSE_NUMBER,CARD_ID,CARD_EVENT_TIME,CARD_EVENT_TYPE,LOGIN_TIME,
IS_PINGABLE,STOP1,STOP2,STOP3,DEVICE_TYPE_ID,TIME_ZONE_ID,POSITION_COLOR,
IS_POCKET_EDITION,DEVICE_GROUP_ID,LAST_UPDATE_TIME, EID, FIRST_NAME,
LAST_NAME) KEY(DEVICE_ID)
values
( ?, ? , ? , ? , ? ,? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ?
, ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ?
, ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? ,
? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ?)
I also wrote a small test program to simulate the writes made by the
application, which read from a list of data and tried to update the
database with batch sizes of 25 and does about 6 updates per second.
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.jdbcx.JdbcConnectionPool;
public class H2WriteCacheTest extends Thread
{
private static final String FILE_NAME = "rel_devices.log";
public static JdbcConnectionPool connectionPool = null;
private static String url = "jdbc:h2:tcp://
10.40.51.30:9092/
mem:vehiclecache;IFEXISTS=TRUE;LOCK_MODE=0;LOG=0";
private static String userName = "sa";
private static String password = "";
private static int SIZE=149564;
private static int[] deviceIds = new int[SIZE];
private static int[] clientIds = new int[SIZE];
private static String[] screenNames = new String[SIZE];
private static String UPDATE_CMP_RECORD = new String(
"UPDATE /* FDCCMPUpdateAgent:CMP 2 */ current_map_position_cache
SET " +
"status=?, gps_status=?, invoke_code=?, loc_obs_time=?, " +
"update_time=?, heading=?, speed=?, latitude=?, longitude=?, mrp=?,
mileage=?, dst=?, " +
"stop_duration=?, screen_name=?, " +
"loc_event_time=?,
address=? ,HOUSE_NUMBER=? ,STREET=? ,CITY=? ,STATE=? ,ZIP=? ,COUNTRY=? ,"
+
"CROSS_STREET=? ,COUNTY=? ,LANDMARK_ID =? ,LANDMARK =? WHERE
device_id=? AND client_id=? AND ? >= loc_event_time");
public H2WriteCacheTest(String str)
{
connectionPool = JdbcConnectionPool.create(url, userName,
password);
}
public static void main(String args[]) throws FileNotFoundException,
IOException, InterruptedException
{
new H2WriteCacheTest("H2WriteCacheTest");
loadDevicesFromFile();
updateCMP();
}
public static void updateCMP()
{
int HITS = 150;
int BATCH_SIZE = 25;
int waitSecs = 1000 / (HITS/BATCH_SIZE);
System.out.println("WaitSecs = "+waitSecs);
try
{
Connection conn = connectionPool.getConnection();
PreparedStatement stmt = conn.prepareStatement(UPDATE_CMP_RECORD);
int count1 = 0;
int failureCount = 0;
while(true)
{
try
{
int gpsStatus = random(0, 2);
int invokeCode = random(0, 50);
int latitude = random(-180, 180);
int longitude = random(-180, 180);
int stopDuration = random(0, 50);
//java.sql.Timestamp locEventTime = new java.sql.Timestamp
(System.currentTimeMillis());
java.sql.Timestamp locEventTime = new java.sql.Timestamp(new
java.util.Date().getTime());
java.sql.Timestamp loc_obs_time = locEventTime;
java.sql.Timestamp update_time = locEventTime;
int address = random(1, 10000);
String houseNo = "h"
+ random(1, 1000000);
String street = "s" + random(1, 10000) + random(1, 10000);
String city = " city" + random(1, 10000);
String state = "state " + random(1, 10000);
String zip = "" + random(1, 10000);
String country = "country "
+ random(1, 10000);
String crossSt = "crossSt "
+ random(1, 10000);
String county = " county" + random(1, 10000);
String landmark = "landmark "
+ random(1, 10000);
int status = 0;
int heading = 45;
int speed=80;
int mrp = 255;
double mileage = 50.0;
int dst = 1;
int landmarkId = 0;
for (int i = 0; i < deviceIds.length; i++)
{
count1++;
int index = 1;
stmt.setInt(index++, status);
stmt.setInt(index++, gpsStatus);
stmt.setInt(index++, invokeCode);
stmt.setTimestamp(index++, loc_obs_time);
stmt.setTimestamp(index++, update_time);
stmt.setInt(index++, heading);
stmt.setInt(index++, speed);
stmt.setInt(index++, latitude);
stmt.setInt(index++, longitude);
stmt.setInt(index++, mrp);
stmt.setDouble(index++, mileage);
stmt.setDouble(index++, dst);
stmt.setInt(index++, stopDuration);
stmt.setString(index++, screenNames[i]);
stmt.setTimestamp(index++, locEventTime);
stmt.setInt(index++, address);
stmt.setString(index++, houseNo);
stmt.setString(index++, street);
stmt.setString(index++, city);
stmt.setString(index++, state);
stmt.setString(index++, zip);
stmt.setString(index++, country);
stmt.setString(index++, crossSt);
stmt.setString(index++, county);
stmt.setInt(index++, landmarkId);
stmt.setString(index++, landmark);
//where clause
stmt.setInt(index++, deviceIds[i]);
stmt.setInt(index++, clientIds[i]);
stmt.setTimestamp(index++, locEventTime);
//System.out.println("Updating record "+i+",
device_id="+deviceIds[i]+",screen name:"+screenNames[i]
+",clientId="+clientIds[i]);
stmt.addBatch();
if((i+1) % BATCH_SIZE == 0 || (i+1) ==
deviceIds.length)
{
long prevTime = System.currentTimeMillis();
int count[] = null;
try
{
count = stmt.executeBatch();
//if(count != null)
// System.out.println("UpdatedCount="+count.length);
System.out.println("Executed "+count1+" Records in "+
(System.currentTimeMillis() - prevTime));
}
catch(Exception e)
{
System.out.println("****Exception in Exception
block, failure count="+(++failureCount)+" ******");
e.printStackTrace();
continue;
}
catch(Throwable t)
{
System.out.println("****Exception in Throwable
block, failure count="+(++failureCount)+" ******");
t.printStackTrace();
continue;
}
//System.out.print(".");
//Thread.sleep(waitSecs);
count1 = 0;
}
}
}
catch(org.h2.jdbc.JdbcSQLException sqe)
{
sqe.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static int random(int min, int max)
{
return (int) ( min + (max - min) * Math.random());
}
private static void loadDevicesFromFile() throws
FileNotFoundException, IOException
{
BufferedReader in = new BufferedReader(new FileReader
(FILE_NAME));
int i = 0;
String nextLine = null;
while((nextLine = in.readLine()) != null)
{
String line = nextLine;
String arr[] = line.split(",");
try
{
deviceIds[i]=Integer.parseInt(arr[0]);
clientIds[i]=Integer.parseInt(arr[1]);
screenNames[i]=arr[2];
}
catch(Throwable t)
{
System.out.println("Error line="+line);
System.out.println("line number="+i);
}
i++;
}
System.out.println("#Devices
loaded:"+i);
}
}
snippet of Input file used
================
291569669,70515,fake5
291569670,70515,fake6
291569671,70515,fake7
291569672,70515,fake8
-369098744,20698,SimDev109969
-1107296256,70515,be00
170991,21915,EE003711
170996,21915,EE031035
171003,21915,BC000967
171004,21915,BC004132
171006,21915,DB009284
171007,21915,DD015141
171018,21915,CA024106
171037,21915,FE000126
171041,21915,AE020809
171017,21915,AA024550
171020,21915,DE015179
171022,21915,ED029549
171025,21915,DA018732
171027,21915,EB013660
171030,21915,CA009119
171031,21915,BA015708