I am getting too many deadlocks on OrientDb while I am using Java API to query the vertices. After the deadlock happens, the entire database becomes unresponsive and I have to kill the daemon and start again. As example, the error that I get from deadlocks is :
com.orientechnologies.common.concur.OTimeoutException: Can not lock record for 2000 ms. seems record is deadlocked by other record
at com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.acquireReadLock(OAbstractPaginatedStorage.java:1300)
at com.orientechnologies.orient.core.tx.OTransactionAbstract.lockRecord(OTransactionAbstract.java:120)
at com.orientechnologies.orient.core.id.ORecordId.lock(ORecordId.java:282)
at com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.lockRecord(OAbstractPaginatedStorage.java:1776)
at com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.readRecord(OAbstractPaginatedStorage.java:1416)
at com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.readRecord(OAbstractPaginatedStorage.java:694)
at com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx.executeReadRecord(ODatabaseDocumentTx.java:1569)
at com.orientechnologies.orient.core.tx.OTransactionNoTx.loadRecord(OTransactionNoTx.java:80)
at com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx.load(ODatabaseDocumentTx.java:1434)
at com.orientechnologies.orient.server.network.protocol.binary.ONetworkProtocolBinary.readRecord(ONetworkProtocolBinary.java:1456)
at com.orientechnologies.orient.server.network.protocol.binary.ONetworkProtocolBinary.executeRequest(ONetworkProtocolBinary.java:346)
at com.orientechnologies.orient.server.network.protocol.binary.OBinaryNetworkProtocolAbstract.execute(OBinaryNetworkProtocolAbstract.java:216)
at com.orientechnologies.common.thread.OSoftThread.run(OSoftThread.java:65)
Following is the block that I use to query edges and create associations between vertices
public User generateFriend(String mobile, String userRID) {
StringBuilder errorMsg = new StringBuilder();
Iterable<OrientVertex> vertexes;
//Retrieve friends of the user
List<User> friendsList = new ArrayList<User>();
vertexes = db.queryVertices("select expand( unionAll(inE('E_Friend').out,outE('E_Friend').in) ) from " + userRID,errorMsg);
if (!errorMsg.toString().equals("")) {
throw new DbException("Db exception occured, " + errorMsg);
}
for (OrientVertex v : vertexes){
friendsList.add(vertexToUser(v));
}
//Create edges if between the user and other users with mobile number in the list and if the edge is not yet created
User u = findUserByMobileNo(friendsList,mobile);
if ( u == null){
u = findByMobileNo(mobile);
if (u != null) {
//create edge
db.executeQuery("select createEdge('E_Friend','" + userRID + "','" + u.getRid() + "') from " + userRID, new HashMap<String, Object>(), errorMsg);
if (!errorMsg.toString().equals("")) {
throw new DbException("Db exception occured, " + errorMsg);
}
}
}
return u;
}
public Iterable<OrientVertex> queryVertices(String query, StringBuilder errMsg){
logger.error("before getGraph, " + errMsg.toString());
graph = getGraph(errMsg);
if (!errMsg.toString().equals("")){
return null;
}
logger.error("after getGraph, " + errMsg.toString());
Iterable<OrientVertex> vertices = null;
try {
OSQLSynchQuery<OrientVertex> qr = new OSQLSynchQuery<OrientVertex>(query);
vertices = graph.command(qr).execute();
logger.error("after graph command execute, " + errMsg.toString());
}
catch (Exception ex){
errMsg.append(ex.getMessage());
logger.error("graph command exception, " + errMsg.toString());
}
logger.error("before return vertices, " + errMsg.toString());
return vertices;
}
public List<ODocument> executeQuery(String sql, HashMap<String,Object> params,StringBuilder errMsg) {
List<ODocument> result = new ArrayList<ODocument>();
try {
db = getDatabase(errMsg);
if (!errMsg.toString().equals("")){
return null;
}
OSQLSynchQuery<ODocument> query = new OSQLSynchQuery<ODocument>(sql);
if (params.isEmpty()) {
result = db.command(query).execute();
} else {
result = db.command(query).execute(params);
}
} catch (Exception e) {
errMsg.append(e.getMessage());
//TODO: Add threaded error log saving mechanism
}
return result;
}
Due to index missing on table deadlock come, so check your all table which are involved in this operation and find out that indexes are present or not on column.
Refer link in which I have a same problem of deadlock.
Related
I am facing an issue when i try to query the queue using createquery api to fetch the queue element.
I am getting an error at the while statement stating the error below as
errorjava.lang.illegalstateexception :unread block data
i dont know why i am getting this error. I can able to use the fetchcount() api to get the count of workitem in the queue but the hasnext() api is not working nor next().
Is there any reason why this statement is not getting executed. is this related to any java issue. Can any one help
The code is
VWSession session = new VWSession();
session.setBootstrapCEURI(Ceuri);
session.logon(cename, fnPassword, connectionPoint);
VWQueue queue = session.getQueue(queue));
int queryFlag = VWQueue.QUERY_NO_OPTIONS;
int fetchType = VWFetchType.FETCH_TYPE_STEP_ELEMENT;
VWQueueQuery queueQuery = queue.createQuery(null,null, null,queryFlag, null, null, fetchType);
while (queueQuery.hasNext()) {
queueElement = (VWStepElement) queueQuery.next();
}
In you main (calling) method, do this :
VWSession vwsession = new VWSession();
vwsession.setBootstrapCEURI("http://servername:9080/wsi/FNCEWS40MTOM/");
vwsession.logon("userid", "password", "ConnPTName");
IteratePEWorkItems queueTest = new IteratePEWorkItems();
queueTest.testQueueElements(vwsession);
Later on create below metioned helper method:
public void testQueueElements(VWSession vwsession) {
System.out.println("Inside getListOfWorkitems: : ");
VWRoster roster = vwsession.getRoster("DefaultRoster");
int fetchType = VWFetchType.FETCH_TYPE_STEP_ELEMENT;
int queryFlags = VWQueue.QUERY_READ_UNWRITABLE;
try {
dispatchWorkItems(roster, fetchType, queryFlags, vwsession);
} catch (Exception exception) {
log.error(exception.getMessage());
}
}
public void dispatchWorkItems(VWRoster roster, int fetchType, int queryFlags, VWSession vwsession) {
String filter = "SLA_Date>=:A";
// get value and replace with 1234567890 as shown in process administrator
Object[] subVars = { 1234567890 };
VWRosterQuery rosterQuery = roster.createQuery(null, null, null,
VWRoster.QUERY_MIN_VALUES_INCLUSIVE | VWRoster.QUERY_MAX_VALUES_INCLUSIVE, filter, subVars,
VWFetchType.FETCH_TYPE_WORKOBJECT);
int i = 0;
// Iterate work items here...
while (rosterQuery.hasNext() == true) {
VWWorkObject workObject = (VWWorkObject) rosterQuery.next();
try {
i++;
System.out.println(" Subject: " + workObject.getFieldValue("F_Subject") + " Count: " + i);
} catch (Exception exception) {
exception.printStackTrace();
log.error(exception);
}
}
}
Try it and share the output.
I want to discover all the destinations from solace (queues and topics)
I tried using MBeanServerConnection and query after names (but I didn't find a proper way to use this) or JNDI lookups Destination dest = (Destination) context.lookup(Dest_name), but I don't have the names of the queues/topics.
I am using solace - jms library.
I am searching for smth like this: (but for solace, not activeMq)
get all Queue from activeMQ
You will need to make use of SEMP over the management interface for this.
Sample commands:
curl -d '<rpc><show><queue><name>*</name></queue></show></rpc>' -u semp_username:semp_password http://your_management_ip:your_management_port/SEMP
curl -d '<rpc><show><topic-endpoint><name>*</name></topic-endpoint></show></rpc>' -u semp_username:semp_password http://your_management_ip:your_management_port/SEMP
Note that I'm using curl for simplicity, but any application can perform HTTP POSTs to execute these commands.
If you are using Java, you can refer to the SempHttpSetRequest sample found within the Solace API samples.
Documentation on SEMP can be found here.
However, the larger question here is why do you need to discover all destinations?
One of the features of the message broker is to decouple the publishers and consumers.
If you need to know if your persistent message is being published to a topic with no consumers, you can make use of the reject-msg-to-sender-on-no-subscription-match setting in the publishing application's client-profile.
This means that the publisher will obtain a negative acknowledgement in the event that it tries to publish a message on a topic that has no matching subscribers.
You can refer to "Handling Guaranteed Messages with No Matches" at https://docs.solace.com/Configuring-and-Managing/Configuring-Client-Profiles.htm for further details.
Here is some source code that might help. With the appliance configured correctly, SEMP is also available over JMS on topic "#SEMP/(router)/SHOW".
/**
* Return the SolTopicInfo for this topic (or all topics if 'topic' is null).
*
* #param session
* #param endpointName
* #return
*/
public static SolTopicInfo[] getTopicInfo(JCSMPSession session, String endpointName, String vpn,
String sempVersion) {
XMLMessageConsumer cons = null;
XMLMessageProducer prod = null;
Map<String, SolTopicInfo> tiMap = new HashMap<String, SolTopicInfo>();
try {
// Create a producer and a consumer, and connect to appliance.
prod = session.getMessageProducer(new PubCallback());
cons = session.getMessageConsumer(new SubCallback());
cons.start();
if (vpn == null) vpn = (String) session.getProperty(JCSMPProperties.VPN_NAME);
if (sempVersion == null) sempVersion = getSempVersion(session);
// Extract the router name.
final String SEMP_SHOW_TE_TOPICS = "<rpc semp-version=\""
+ sempVersion
+ "\"><show><topic-endpoint><name>"
+ endpointName
+ "</name><vpn-name>"+ vpn + "</vpn-name></topic-endpoint></show></rpc>";
RpcReply teTopics = sendRequest(session, SEMP_SHOW_TE_TOPICS);
for (TopicEndpoint2 te : teTopics.getRpc().getShow().getTopicEndpoint().getTopicEndpoints()
.getTopicEndpointArray()) {
SolTopicInfo ti = new SolTopicInfo();
ti.setBindCount(te.getInfo().getBindCount());
//qi.setDescription(qt.getInfo().getNetworkTopic());
ti.setEndpoint(te.getName());
ti.setMessageVPN(te.getInfo().getMessageVpn());
ti.setTopic(te.getInfo().getDestination());
ti.setDurable(te.getInfo().getDurable());
ti.setInSelPres(te.getInfo().getIngressSelectorPresent());
ti.setHwmMB(formatter.format(te.getInfo().getHighWaterMarkInMb()));
ti.setSpoolUsageMB(formatter.format(te.getInfo().getCurrentSpoolUsageInMb()));
ti.setMessagesSpooled(te.getInfo().getNumMessagesSpooled().longValue());
String status = te.getInfo().getIngressConfigStatus().substring(0, 1).toUpperCase();
status += " " + te.getInfo().getEgressConfigStatus().substring(0, 1).toUpperCase();
status += " " + te.getInfo().getIngressSelectorPresent().substring(0, 1).toUpperCase();
status += " " + te.getInfo().getType().substring(0, 1).toUpperCase();
ti.setStatus(status);
tiMap.put(ti.getEndpoint(), ti);
}
} catch (JCSMPException e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
if (cons != null)
cons.close();
if (prod != null)
prod.close();
}
return tiMap.values().toArray(new SolTopicInfo[0]);
}
/**
* Return the SolQueueInfo for this queue (or all queues if 'queue' is null).
*
* #param session
* #param queue
* #param vpn (if null, use the session's vpn name)
* #param sempVersion, if null use 'soltr/7_1_1'
* #return
*/
public static SolQueueInfo[] getQueueInfo(JCSMPSession session, String queue, String vpn,
String sempVersion) {
XMLMessageConsumer cons = null;
XMLMessageProducer prod = null;
Map<String, SolQueueInfo> qiMap = new HashMap<String, SolQueueInfo>();
try {
// Create a producer and a consumer, and connect to appliance.
prod = session.getMessageProducer(new PubCallback());
cons = session.getMessageConsumer(new SubCallback());
cons.start();
if (vpn == null) vpn = (String) session.getProperty(JCSMPProperties.VPN_NAME);
if (sempVersion == null) sempVersion = getSempVersion(session);
// Extract the router name.
final String SEMP_SHOW_QUEUE_SUBS = "<rpc semp-version=\""
+ sempVersion
+ "\"><show><queue><name>"
+ queue
+ "</name><vpn-name>"+ vpn + "</vpn-name><subscriptions/><count/><num-elements>200</num-elements></queue></show></rpc>";
RpcReply queueSubs = sendRequest(session, SEMP_SHOW_QUEUE_SUBS);
for (QueueType qt : queueSubs.getRpc().getShow().getQueue().getQueues().getQueueArray()) {
SolQueueInfo qi = new SolQueueInfo();
qi.setBindCount(qt.getInfo().getBindCount());
//qi.setDescription(qt.getInfo().getNetworkTopic());
qi.setName(qt.getName());
qi.setMessageVPN(qt.getInfo().getMessageVpn());
qi.setDurable(qt.getInfo().getDurable());
qi.setEgSelPres(qt.getInfo().getEgressSelectorPresent());
qi.setHwmMB(formatter.format(qt.getInfo().getHighWaterMarkInMb()));
qi.setMessagesSpooled(qt.getInfo().getNumMessagesSpooled().longValue());
qi.setSpoolUsageMB(formatter.format(qt.getInfo().getCurrentSpoolUsageInMb()));
String status = qt.getInfo().getIngressConfigStatus().substring(0, 1).toUpperCase();
status += " " + qt.getInfo().getEgressConfigStatus().substring(0, 1).toUpperCase();
status += " " + qt.getInfo().getAccessType().substring(0, 1).toUpperCase();
status += " " + qt.getInfo().getEgressSelectorPresent().substring(0, 1).toUpperCase();
status += " " + qt.getInfo().getType().substring(0, 1).toUpperCase();
status += qt.getInfo().getDurable() ? " D" : " N";
qi.setStatus(status);
for (Subscription sub : qt.getSubscriptions().getSubscriptionArray()) {
qi.addSubscription(sub.getTopic());
}
qiMap.put(qi.getName(), qi);
}
} catch (JCSMPException e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
if (cons != null)
cons.close();
if (prod != null)
prod.close();
}
return qiMap.values().toArray(new SolQueueInfo[0]);
}
private static String getSempVersion(JCSMPSession session)
{
String retval = "soltr/7_1_1";
try {
String peerVersion = (String)session.getCapability(CapabilityType.PEER_SOFTWARE_VERSION);
if (peerVersion != null)
{
retval = "soltr/";
String[] version = peerVersion.split("\\.");
retval += version[0];
retval += "_" + version[1];
if (!version[2].equals("0")) retval += "_" + version[2];
}
} catch (Throwable e) {
System.err.println(e);
}
return retval;
}
private static RpcReply sendRequest(JCSMPSession session,
final String requestStr) {
try {
// Set up the requestor and request message.
String routerName = (String) session
.getCapability(CapabilityType.PEER_ROUTER_NAME);
final String SEMP_TOPIC_STRING = String.format("#SEMP/%s/SHOW",
routerName);
final Topic SEMP_TOPIC = JCSMPFactory.onlyInstance().createTopic(
SEMP_TOPIC_STRING);
Requestor requestor = session.createRequestor();
BytesXMLMessage requestMsg = JCSMPFactory.onlyInstance().createMessage(
BytesXMLMessage.class);
requestMsg.writeAttachment(requestStr.getBytes());
BytesXMLMessage replyMsg = requestor
.request(requestMsg, 5000, SEMP_TOPIC);
String replyStr = new String();
if (replyMsg.getAttachmentContentLength() > 0) {
byte[] bytes = new byte[replyMsg.getAttachmentContentLength()];
replyMsg.readAttachmentBytes(bytes);
replyStr = new String(bytes, "US-ASCII");
}
RpcReplyDocument doc = RpcReplyDocument.Factory.parse(replyStr);
RpcReply reply = doc.getRpcReply();
if (reply.isSetPermissionError()) {
throw new RuntimeException(
"Permission Error: Make sure SEMP over message bus SHOW commands are enabled for this VPN");
}
if( reply.isSetParseError() ) {
throw new RuntimeException( "SEMP Parse Error: " + reply.getParseError() );
}
if( reply.isSetLimitError() ) {
throw new RuntimeException( "SEMP Limit Error: " + reply.getLimitError() );
}
if( reply.isSetExecuteResult() && reply.getExecuteResult().isSetReason() ) { // axelp: encountered this error on invalid 'queue' name
throw new RuntimeException( "SEMP Execution Error: " + reply.getExecuteResult().getReason() );
}
return reply;
} catch (JCSMPException e) {
throw new RuntimeException(e.getMessage(), e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e.getMessage(), e);
} catch (XmlException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
You can get message VPN specific queues and topics using following SEMPv2 command.
curl -s -X GET -u semp_user:semp_pass management_host:management_port/SEMP/v2/monitor/msgVpns/{vpn-name}/queues?select="queueName"
curl -s -X GET -u semp_user:semp_pass management_host:management_port/SEMP/v2/monitor/msgVpns/{vpn-name}/topicEndpoints?select="topicEndpointName"
I want to wait until my processes finish before I return my speechlet response, otherwise it seems to cut my process off and thus, not complete it, I actually believe it may freeze the process, but thats not my desire.
How do I go about waiting?
#Override
public SpeechletResponse onIntent(SpeechletRequestEnvelope<IntentRequest> requestEnvelope) {
IntentRequest request = requestEnvelope.getRequest();
Intent intent = request.getIntent();
String intentName = (intent != null) ? intent.getName() : null;
if (intentName == null) return null;
switch (intentName) {
case IntentTitle.NEW_TICKET:
switch (request.getDialogState()) {
case STARTED:
return Response.getDialogueResponse(intent, true);
case IN_PROGRESS:
return Response.getDialogueResponse(intent, false);
case COMPLETED:
String numberString = intent.getSlot(SlotTitle.ID).getValue();
if (!NumberUtils.isCreatable(numberString)) return Response.ERROR;
Member member = Info.GUILD.getMemberById(numberString);
User sender = UserDB.getUser(member);
CommissionTicket commissionTicket = new CommissionTicket(sender);
commissionTicket.create();
//wait until processes finish before continuing
return Response.NEW_TICKED_CREATED;
}
}
return null;
}
UPDATE:
Had a look at the CloudWatch logs, and well, pretty much what I expected was happening is happening... have a look at the times for these logs (I ran them 3 different times, so 3 different times are logged, but apart of the same lambda session)
public void create() {
System.out.println("log2");
GuildController guildController = Info.GUILD.getController();
guildController.createTextChannel(ticketType.name().toLowerCase() + "-" + creator.getName() + "-" + id.value()).queue(channel -> {
System.out.println("log3");
ChannelManager channelManager = channel.getManager();
GuildManager guildManager = channelManager.getGuild().getManager();
List<Member> members = guildManager.getGuild().getMembers();
List<Member> admins = new ArrayList<>();
for (Member member : members) {
for (Role role : member.getRoles()) {
if (!role.getName().equalsIgnoreCase(Info.ADMIN_STRING)) continue;
admins.add(member);
}
}
System.out.println("log4");
for (Member member : members) {
if (member.equals(creator.getMember())) continue;
channel.createPermissionOverride(member).setDeny(Permission.MESSAGE_READ).queue();
}
System.out.println("log5");
for (Member admin : admins) {
if (admin.equals(creator.getMember())) continue;
channel.createPermissionOverride(admin).setAllow(Permission.MESSAGE_READ).queue();
}
System.out.println("log6");
BotMessage botMessage = new BotMessage();
botMessage
.setTitle("New Ticket! User: " + creator.getName())
.setColour(Color.CYAN)
.setDescription("Please enter your full request here! \n" +
"Make sure to let us know whether you are looking for a quote/timeframe,\n" +
"or have a budget in mind, and we will work around you!\n\n" +
"A sales representative will be with you as soon as possible!")
.send((TextChannel) channel);
System.out.println("log7");
this.textChannel = (TextChannel) channel;
TicketDB.addTicket(this);
System.out.println("log8");
}
);
Logs:
https://gyazo.com/0ad2baa8d1438be8364dd1112159c4f4
https://gyazo.com/e197f33335046afe3c9f8f1ace267d30
UPDATE
Implemented the Future class, worked, but still a bit buggy.
It now completely creates the ticket, which is great, however, when I go to send the same call again, it for some reason sends a message in the same channel before preceding to create the next ticket.
So, to simulate...
Function call through AWS Lambda
Creates ticket completely
Function call through AWS Lambda again
Sends a message or two in the previous tickets channel
Creates new ticket completely
https://gyazo.com/dc6e4391f4964f41a73f1c3be92190f9
#Override
public SpeechletResponse onIntent(SpeechletRequestEnvelope<IntentRequest> requestEnvelope) {
IntentRequest request = requestEnvelope.getRequest();
Intent intent = request.getIntent();
String intentName = (intent != null) ? intent.getName() : null;
if (intentName == null) return null;
switch (intentName) {
case IntentTitle.NEW_TICKET:
switch (request.getDialogState()) {
case STARTED:
return Response.getDialogueResponse(intent, true);
case IN_PROGRESS:
return Response.getDialogueResponse(intent, false);
case COMPLETED:
String numberString = intent.getSlot(SlotTitle.ID).getValue();
if (!NumberUtils.isCreatable(numberString)) return Response.ERROR;
Member member = Info.GUILD.getMemberById(numberString);
User sender = UserDB.getUser(member);
System.out.println("log1");
Future<Ticket> commissionTicket = new CommissionTicket(sender).create();
try {
commissionTicket.get(10000, TimeUnit.MILLISECONDS);
} catch (Exception e) {
e.printStackTrace();
}
//wait until processes finish before continuing
return Response.NEW_TICKED_CREATED;
}
}
return null;
}
In the Ticket class:
public Future<Ticket> create() {
System.out.println("log2");
GuildController guildController = Info.GUILD.getController();
RequestFuture<Channel> channelRequestFuture = guildController.createTextChannel(ticketType.name().toLowerCase() + "-" + creator.getName() + "-" + id.value()).submit();
try {
Channel channel = channelRequestFuture.get(10000, TimeUnit.MILLISECONDS);
System.out.println("log3");
ChannelManager channelManager = channel.getManager();
GuildManager guildManager = channelManager.getGuild().getManager();
List<Member> members = guildManager.getGuild().getMembers();
List<Member> admins = new ArrayList<>();
for (Member member : members) {
for (Role role : member.getRoles()) {
if (!role.getName().equalsIgnoreCase(Info.ADMIN_STRING)) continue;
admins.add(member);
}
}
System.out.println("log4");
for (Member member : members) {
if (member.equals(creator.getMember())) continue;
channel.createPermissionOverride(member).setDeny(Permission.MESSAGE_READ).queue();
}
System.out.println("log5");
for (Member admin : admins) {
if (admin.equals(creator.getMember())) continue;
channel.createPermissionOverride(admin).setAllow(Permission.MESSAGE_READ).queue();
}
System.out.println("log6");
BotMessage botMessage = new BotMessage();
botMessage
.setTitle("New Ticket! User: " + creator.getName())
.setColour(Color.CYAN)
.setDescription("Please enter your full request here! \n" +
"Make sure to let us know whether you are looking for a quote/timeframe,\n" +
"or have a budget in mind, and we will work around you!\n\n" +
"A sales representative will be with you as soon as possible!")
.send((TextChannel) channel);
System.out.println("log7");
this.textChannel = (TextChannel) channel;
TicketDB.addTicket(this);
System.out.println("log8");
Future<Ticket> future = ConcurrentUtils.constantFuture(this);
return future;
} catch (Exception e) {
e.printStackTrace();
}
if (!userIsInTicket(creator)) users.add(creator);
return null;
}
I'm not sure what's going on inside commissionTicket.create(); (I assume it's your code, not part of some library, and that it's running asynchronously somehow), but one solution would be to have that method return a Future<> object and then wait on it to finish. Something along the lines of:
Future<CommissionTicket> commissionTicketFuture = CommissionTicket.create(sender);
commissionTicketFuture.get(SOME_TIMEOUT, TimeUnit.MILLIS);
return Response.NEW_TICKET_CREATED;
I am trying to find count of rows in all tables of a database on source and destination, source being Greenplum and destination being Hive(on HDFS).
To do the parallel processing, I have created two threads which calls the methods that calculate the counts on both the ends independently. The code can be seen below:
new Thread(new Runnable() {
#Override
public void run() {
try {
gpTableCount = getGpTableCount();
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
#Override
public void run() {
try {
hiveTableCount = getHiveTableCount();
} catch (SQLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
while(!(gpTableCount != null && gpTableCount.size() > 0 && hiveTableCount != null && hiveTableCount.size() > 0)) {
Thread.sleep(5000);
}
The results of both the threads are stored in two separate Java Hashmaps.
Below is the count for calculating the GP counts. Method of calculating Hive counts is same except the database name, hence I just gave one method.
public Map<String,String> getGpTableCount() throws SQLException {
Connection gpAnalyticsCon = (Connection) DbManager.getGpConnection();
while(keySetIterator_gpTableList.hasNext()) {
gpTabSchemakey = keySetIterator_gpTableList.next();
tablesnSSNs = gpTabSchemakey.split(",");
target = tablesnSSNs[1].split(":");
analyticsTable = target[0].split("\\.");
gpCountQuery = "select '" + analyticsTable[1] + "' as TableName, count(*) as Count, source_system_name, max(xx_last_update_tms) from " + tablesnSSNs[0] + " where source_system_name = '" + target[1] + "' group by source_system_name";
try {
gp_pstmnt = gpAnalyticsCon.prepareStatement(gpCountQuery);
ResultSet gpCountRs = gp_pstmnt.executeQuery();
while(gpCountRs.next()) {
gpCountRs.getLong(2) + ", Max GP Tms: " + gpCountRs.getTimestamp(4).toString());
gpDataMap.put(gpCountRs.getString(1) + "," + gpCountRs.getString(3), gpCountRs.getLong(2) + "," + gpCountRs.getTimestamp(4).toString());
}
} catch(org.postgresql.util.PSQLException e) {
e.printStackTrace();
} catch(SQLException e) {
e.printStackTrace();
} catch(Exception e) {
e.printStackTrace();
}
}
System.out.println("GP Connection closed");
gp_pstmnt.close();
gpAnalyticsCon.close();
return gpDataMap;
}
Hive's Method:
public Map<String, String> getHiveTableCount() throws IOException, SQLException {
Connection hiveConnection = DbManager.getHiveConnection();
while(hiveIterator.hasNext()) {
gpHiveRec = hiveIterator.next();
hiveArray = gpHiveRec.split(",");
hiveDetails = hiveArray[1].split(":");
hiveTable = hiveDetails[0].split("\\.");
hiveQuery = "select '" + hiveTable[1] + "' as TableName, count(*) as Count, source_system_name, max(xx_last_update_tms) from " + hiveDetails[0] + " where source_system_name='" + hiveDetails[1] + "' group by source_system_name";
try {
hive_pstmnt = hiveConnection.prepareStatement(hiveQuery);
ResultSet hiveCountRs = hive_pstmnt.executeQuery();
while(hiveCountRs.next()) {
hiveDataMap.put(hiveCountRs.getString(1) + "," + hiveCountRs.getString(3), hiveCountRs.getLong(2) + "," + hiveCountRs.getTimestamp(4).toString());
}
} catch(HiveSQLException e) {
e.printStackTrace();
} catch(SQLException e) {
e.printStackTrace();
} catch(Exception e) {
e.printStackTrace();
}
}
return hiveDataMap;
}
When the jar is submitted, both the threads are launched and the SQL Queries for GP & Hive start executing simultaneously.
But the problem here is, as soon as the thread for GP finishes the execution of the method: getGpTableCount(), I see the print statement: GP Connection closed and the hive's thread hangs for atleast 30mins before resuming.
If checked for locks on Hive tables incase there would be none locked. After 30-40mins, the hive threads starts again and finishes. This happens even for less number of tables (like 20 tables) on hive.
This is how I submit the jar:
/usr/jdk64/jdk1.8.0_112/bin/java -Xdebug -Dsun.security.krb5.debug=true -Djava.security.krb5.conf=/etc/krb5.conf -Djava.security.krb5.realm=PROD.COM -Djava.security.krb5.kdc=ip-xx-xxx-xxx-xxx.ec2.internal -Djavax.security.auth.useSubjectCredsOnly=false -jar /home/etl/ReconTest/ReconAuto_Test_Prod.jar
Could anyone let me know if there is any issue with the way I create threads in the code and how can I fix it ?
Assuming your gpTableCount and hiveTableCount are normal HashMaps, you're running in to synchronization issues.
This is a broad topic to fully explain here, but here's a short intro:
Since they are populated in different threads, your main thread does not 'see' these changes until the memory is synchronized. There's no guarantee when this happens (and it's best to assume it will never happen unless you force it).
To do this properly, either use threadsafe versions (see Collections.synchronizedMap or ConcurrentHashMap), or manually synchronize your checks on the same monitor. (i.e. but the check itself in a synchronized method, and put the code that populated the map in a synchronized method, too). Alternatively, you could put the count itself in two volatile ints, and update those in the other two threads.
I have a piece of JAVA code that is accessed by multiple threads.
synchronized (this.getClass())
{
System.out.println("stsrt");
certRequest.setRequestNbr(
generateRequestNumber(
certInsuranceRequestAddRq.getAccountInfo().getAccountNumberId()));
System.out.println("outside funcvtion"+certRequest.getRequestNbr());
reqId = Utils.getUniqueId();
certRequest.setRequestId(reqId);
System.out.println(reqId);
ItemIdInfo itemIdInfo = new ItemIdInfo();
itemIdInfo.setInsurerId(certRequest.getRequestId());
certRequest.setItemIdInfo(itemIdInfo);
dao.insert(certRequest);
addAccountRel();
System.out.println("end");
}
the function generateRequestNumber() generates a request number based on the data fetched from two database tables.
public String generateRequestNumber(String accNumber) throws Exception
{
String requestNumber = null;
if (accNumber != null)
{
String SQL_QUERY = "select CERTREQUEST.requestNbr from CertRequest as CERTREQUEST, "
+ "CertActObjRel as certActObjRel where certActObjRel.certificateObjkeyId=CERTREQUEST.requestId "
+ " and certActObjRel.certObjTypeCd=:certObjTypeCd "
+ " and certActObjRel.certAccountId=:accNumber ";
String[] parameterNames = { "certObjTypeCd", "accNumber" };
Object[] parameterVaues = new Object[]
{
Constants.REQUEST_RELATION_CODE, accNumber
};
List<?> resultSet = dao.executeNamedQuery(SQL_QUERY,
parameterNames, parameterVaues);
// List<?> resultSet = dao.retrieveTableData(SQL_QUERY);
if (resultSet != null && resultSet.size() > 0) {
requestNumber = (String) resultSet.get(0);
}
int maxRequestNumber = -1;
if (requestNumber != null && requestNumber.length() > 0) {
maxRequestNumber = maxValue(resultSet.toArray());
requestNumber = Integer.toString(maxRequestNumber + 1);
} else {
requestNumber = Integer.toString(1);
}
System.out.println("inside function request number"+requestNumber);
return requestNumber;
}
return null;
}
The tables CertRequest and CertActObjRel used in generateRequestNumber() are updated by the functions "dao.insert(certRequest);" and "addAccountRel();" used in my initial code respectively. Also the System.out.println() statements used in my initial code have following output.
stsrt
inside function request number73
outside funcvtion73
A1664886-5F84-45A9-AB5F-C69768B83EAD
end
stsrt
inside function request number73
outside funcvtion73
44DAD872-6A1D-4524-8A32-15741FAC0CA9
end
If you notice both the threads run in a synchronized manner, but when the request number is generated , it's the same. My assumption is the database updation for CertRequest and CertActObjRel is done when both the threads finish their execution.
Could anyone help me to fix this?