Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ef068e3

Browse files
committed
o.a.alarm.beast: Batched RDB updates (Jaka)
1 parent a8b7061 commit ef068e3

7 files changed

Lines changed: 452 additions & 343 deletions

File tree

applications/plugins/org.csstudio.alarm.beast.server/src/org/csstudio/alarm/beast/server/AlarmRDB.java

Lines changed: 133 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@
2020
import org.csstudio.alarm.beast.SeverityLevel;
2121
import org.csstudio.alarm.beast.TimestampHelper;
2222
import org.csstudio.alarm.beast.TreeItem;
23+
import org.csstudio.alarm.beast.server.AlarmServer.Update;
2324
import org.csstudio.platform.utility.rdb.RDBUtil;
2425

2526
/** Alarm RDB Handler
2627
* @author Kay Kasemir
2728
* @author Lana Abadie - Disable autocommit as needed.
29+
* @author Jaka Bobnar - RDB batching
2830
*/
2931
@SuppressWarnings("nls")
3032
public class AlarmRDB
@@ -283,7 +285,7 @@ public void readConfigurationUpdate(final AlarmPV pv) throws Exception
283285
* @param timestamp
284286
* @throws Exception on error
285287
*/
286-
public void writeStateUpdate(final AlarmPV pv, final SeverityLevel current_severity,
288+
private void writeStateUpdate(final AlarmPV pv, final SeverityLevel current_severity,
287289
String current_message, final SeverityLevel severity, String message,
288290
final String value, final org.epics.util.time.Timestamp timestamp) throws Exception
289291
{
@@ -293,12 +295,6 @@ public void writeStateUpdate(final AlarmPV pv, final SeverityLevel current_sever
293295
if (current_message == null || current_message.isEmpty())
294296
current_message = SeverityLevel.OK.getDisplayName();
295297

296-
// According to JProfiler, this is the part of the code
297-
// that uses most of the CPU:
298-
// Compared to receiving updates from PVs and sending them
299-
// to JMS clients, the (Oracle) RDB update dominates
300-
// the combined time spent in CPU usage and network I/O.
301-
302298
// These are usually quick accesses to local caches,
303299
// but could fail when trying to add new values to RDB, so give detailed error
304300
final int current_severity_id;
@@ -337,41 +333,88 @@ public void writeStateUpdate(final AlarmPV pv, final SeverityLevel current_sever
337333
{
338334
throw new Exception("Failed to map alarm message " + message + ": " + ex.getMessage(), ex);
339335
}
336+
337+
updateStateStatement.setInt(1, current_severity_id);
338+
updateStateStatement.setInt(2, current_message_id);
339+
updateStateStatement.setInt(3, severity_id);
340+
updateStateStatement.setInt(4, message_id);
341+
//Truncate the value to avoid Truncation Exception thrown by some SQL Servers
342+
String newValue = value;
343+
if (newValue != null && newValue.length() > 100)
344+
{
345+
newValue = newValue.substring(0,99);
346+
Activator.getLogger().log(Level.WARNING,
347+
"Value truncated. Too many characters: " + pv.getName() + "; " + timestamp.toDate() + " " + value);
348+
}
349+
updateStateStatement.setString(5, newValue);
350+
Timestamp sql_time = TimestampHelper.toSQLTime(timestamp);
351+
if (sql_time.getTime() == 0)
352+
{ // MySQL will throw Data Truncation exception on 0 time stamps
353+
sql_time = new Timestamp(new Date().getTime());
354+
Activator.getLogger().log(Level.INFO,
355+
"State update for {0} corrects time stamp {1} to now",
356+
new Object[] { pv.getPathName(), timestamp });
357+
}
358+
updateStateStatement.setTimestamp(6, sql_time);
359+
updateStateStatement.setInt(7, pv.getID());
360+
updateStateStatement.addBatch();
361+
}
340362

341-
// The isConnected() check in here is expensive, but what's
342-
// the alternative if we want convenient auto-reconnect?
363+
/** Persists all the updates into DB.
364+
* @param updates the updates to persist
365+
* @param batchSize maximum batch size
366+
*
367+
* @throws Exception
368+
*/
369+
public void persistAllStates(final Update[] updates, final int batchSize) throws Exception
370+
{
343371
final Connection actual_connection = rdb.getConnection();
344372
actual_connection.setAutoCommit(false);
373+
374+
// New or changed connection?
375+
if (actual_connection != connection || updateStateStatement == null)
376+
{
377+
connection = actual_connection;
378+
updateStateStatement = null;
379+
updateStateStatement = actual_connection.prepareStatement(sql.update_pv_state);
380+
}
345381
try
346382
{
347-
if (actual_connection != connection || updateStateStatement == null)
348-
{ // (Re-)create statement on new connection
349-
connection = actual_connection;
350-
updateStateStatement = null;
351-
updateStateStatement = connection.prepareStatement(sql.update_pv_state);
352-
}
353-
// Bulk of the time is spent in execute() & commit
354-
updateStateStatement.setInt(1, current_severity_id);
355-
updateStateStatement.setInt(2, current_message_id);
356-
updateStateStatement.setInt(3, severity_id);
357-
updateStateStatement.setInt(4, message_id);
358-
updateStateStatement.setString(5, value);
359-
Timestamp sql_time = TimestampHelper.toSQLTime(timestamp);
360-
if (sql_time.getTime() == 0)
361-
{ // MySQL will throw Data Truncation exception on 0 time stamps
362-
sql_time = new Timestamp(new Date().getTime());
363-
Activator.getLogger().log(Level.INFO,
364-
"State update for {0} corrects time stamp {1} to now",
365-
new Object[] { pv.getPathName(), timestamp });
383+
int count = 0;
384+
for (Update u : updates)
385+
{
386+
try
387+
{
388+
writeStateUpdate(u.pv, u.currentSeverity, u.currentMessage, u.alarmSeverity,
389+
u.alarmMessage, u.value, u.timestamp);
390+
count++;
391+
}
392+
catch (Exception e)
393+
{
394+
//this is about 4-times faster than StringBuilder
395+
String s = "Error updating state: current severity=" + u.currentSeverity +
396+
"; current message=" + u.currentMessage + "; severity=" + u.alarmSeverity +
397+
"; message=" + u.alarmMessage + "; value=" + u.value + "; timestamp=" + u.timestamp +
398+
"; pv=" + u.pv.getName() + '(' + u.pv.getID() + "). Message skipped.";
399+
Activator.getLogger().log(Level.SEVERE, s);
400+
}
401+
if (count == batchSize)
402+
{ // Periodically submit as batch
403+
updateStateStatement.executeBatch();
404+
actual_connection.commit();
405+
count = 0;
406+
}
366407
}
367-
updateStateStatement.setTimestamp(6, sql_time);
368-
updateStateStatement.setInt(7, pv.getID());
369-
updateStateStatement.execute();
370-
actual_connection.commit();
408+
// Submit remaining statements
409+
if (count > 0)
410+
{
411+
updateStateStatement.executeBatch();
412+
actual_connection.commit();
413+
}
371414
}
372-
catch(Exception e)
415+
catch (Exception e)
373416
{
374-
actual_connection.rollback();
417+
rollbackBatchUpdate(actual_connection,updateStateStatement);
375418
throw e;
376419
}
377420
finally
@@ -380,33 +423,63 @@ public void writeStateUpdate(final AlarmPV pv, final SeverityLevel current_sever
380423
}
381424
}
382425

383-
/** Update 'global' alarm indicator in RDB
384-
* @param pv
385-
* @param active Is there an active 'global' alarm on the PV?
386-
* @throws Exception on error
426+
/** Persists all the global updates in batches of the given size.
427+
*
428+
* @param updates the updates to persist
429+
* @param batchSize maximum batch size
430+
*
431+
* @throws Exception
387432
*/
388-
public void writeGlobalUpdate(final AlarmPV pv, final boolean active) throws Exception
433+
public void persistGlobalUpdates(final Update[] updates, final int batchSize) throws Exception
389434
{
390-
// The isConnected() check in here is expensive, but what's
391-
// the alternative if we want convenient auto-reconnect?
392435
final Connection actual_connection = rdb.getConnection();
393436
actual_connection.setAutoCommit(false);
437+
394438
try
395439
{
440+
int count = 0;
396441
if (actual_connection != connection || updateGlobalStatement == null)
397442
{ // (Re-)create statement on new connection
398443
connection = actual_connection;
399444
updateGlobalStatement = null;
400445
updateGlobalStatement = connection.prepareStatement(sql.update_global_state);
401446
}
402-
updateGlobalStatement.setBoolean(1, active);
403-
updateGlobalStatement.setInt(2, pv.getID());
404-
updateGlobalStatement.execute();
405-
actual_connection.commit();
447+
448+
for (Update u : updates)
449+
{
450+
try
451+
{
452+
updateGlobalStatement.setBoolean(1, u.currentSeverity.isActive());
453+
updateGlobalStatement.setInt(2, u.pv.getID());
454+
updateGlobalStatement.addBatch();
455+
count++;
456+
}
457+
catch (Exception e)
458+
{
459+
//this is about 4-times faster than StringBuilder
460+
String s = "Error updating global state: current severity=" + u.currentSeverity +
461+
"; current message=" + u.currentMessage + "; severity=" + u.alarmSeverity +
462+
"; message=" + u.alarmMessage + "; value=" + u.value + "; timestamp=" + u.timestamp +
463+
"; pv=" + u.pv.getName() + '(' + u.pv.getID() + "). Message skipped.";
464+
Activator.getLogger().log(Level.SEVERE, s);
465+
}
466+
if (count == batchSize)
467+
{
468+
updateGlobalStatement.executeBatch();
469+
actual_connection.commit();
470+
count = 0;
471+
}
472+
}
473+
// Submit remaining batch
474+
if (count > 0)
475+
{
476+
updateGlobalStatement.executeBatch();
477+
actual_connection.commit();
478+
}
406479
}
407-
catch(Exception e)
480+
catch (Exception e)
408481
{
409-
actual_connection.rollback();
482+
rollbackBatchUpdate(actual_connection, updateGlobalStatement);
410483
throw e;
411484
}
412485
finally
@@ -447,6 +520,19 @@ public void writeEnablementUpdate(final AlarmPV pv, final boolean enabled) throw
447520
}
448521
}
449522

523+
private void rollbackBatchUpdate(final Connection actualConnection, final PreparedStatement statement)
524+
{
525+
try
526+
{
527+
statement.clearBatch();
528+
actualConnection.rollback();
529+
}
530+
catch (Exception ex)
531+
{
532+
Activator.getLogger().log(Level.SEVERE, "State update rollback error.", ex);
533+
}
534+
}
535+
450536
/** Must be called to release resources */
451537
public void close()
452538
{

0 commit comments

Comments
 (0)