Skip to content

Commit 4a326d1

Browse files
committed
1223: Warn when web and database server time differ
1 parent 630ce92 commit 4a326d1

4 files changed

Lines changed: 69 additions & 26 deletions

File tree

api/src/org/labkey/api/data/dialect/SqlDialect.java

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.labkey.api.module.ModuleLoader;
6060
import org.labkey.api.query.FieldKey;
6161
import org.labkey.api.util.ExceptionUtil;
62+
import org.labkey.api.util.HtmlString;
6263
import org.labkey.api.util.MemTracker;
6364
import org.labkey.api.util.StringUtilsLabKey;
6465
import org.labkey.api.util.SystemMaintenance;
@@ -80,6 +81,8 @@
8081
import java.sql.SQLException;
8182
import java.sql.Statement;
8283
import java.sql.Types;
84+
import java.time.Duration;
85+
import java.time.LocalDateTime;
8386
import java.util.ArrayList;
8487
import java.util.Arrays;
8588
import java.util.Calendar;
@@ -1980,12 +1983,60 @@ public final Collection<String> getExecutionPlan(DbScope scope, SQLFragment sql,
19801983
// Add any database configuration warnings (e.g., missing aggregate function or deprecated database server version)
19811984
// to display in the page header for administrators. This will be called:
19821985
// - Only on the LabKey DataSource's dialect instance (not external data sources)
1983-
// - After the core module has been upgraded and the dialect has been prepared for the last time, meaning the dialect
1986+
// - After the core module has been upgraded, and the dialect has been prepared for the last time, meaning the dialect
19841987
// should reflect the final database configuration
19851988
public void addAdminWarningMessages(Warnings warnings, boolean showAllWarnings)
19861989
{
19871990
}
19881991

1992+
public static final long TIME_DIFFERENCE_WARNING_SECONDS = 10;
1993+
1994+
public static ServerDatabaseTimeDifference getServerDatabaseTimeDifference(DbScope scope)
1995+
{
1996+
LocalDateTime serverTime = LocalDateTime.now();
1997+
LocalDateTime databaseTime = new SqlSelector(scope, "SELECT CURRENT_TIMESTAMP").getObject(LocalDateTime.class);
1998+
1999+
return new ServerDatabaseTimeDifference(serverTime, databaseTime);
2000+
}
2001+
2002+
public record ServerDatabaseTimeDifference(LocalDateTime serverTime, LocalDateTime databaseTime)
2003+
{
2004+
public long getSeconds()
2005+
{
2006+
return Math.abs(Duration.between(serverTime, databaseTime).toSeconds());
2007+
}
2008+
2009+
public boolean exceedsWarningThreshold()
2010+
{
2011+
return getSeconds() > TIME_DIFFERENCE_WARNING_SECONDS;
2012+
}
2013+
}
2014+
2015+
// GH Issue #1223: Add a site configuration warning for administrators if the server and database clocks differ.
2016+
protected void addTimeDifferenceWarning(Warnings warnings, boolean showAllWarnings)
2017+
{
2018+
try
2019+
{
2020+
ServerDatabaseTimeDifference difference = getServerDatabaseTimeDifference(DbScope.getLabKeyScope());
2021+
2022+
if (difference.exceedsWarningThreshold())
2023+
warnings.add(getTimeDifferenceWarning(difference.getSeconds()));
2024+
else if (showAllWarnings)
2025+
warnings.add(getTimeDifferenceWarning(TIME_DIFFERENCE_WARNING_SECONDS + 1));
2026+
}
2027+
catch (Exception e)
2028+
{
2029+
LOG.warn("Unable to compare web server and database server times", e);
2030+
}
2031+
}
2032+
2033+
private HtmlString getTimeDifferenceWarning(long seconds)
2034+
{
2035+
return HtmlString.of("The web server and database server times differ by " + seconds + " seconds. " +
2036+
"LabKey Server often relies on comparing timestamps stored in the database with timestamps generated by " +
2037+
"the web server, so this difference can lead to data integrity issues. Synchronize the clocks on these servers.");
2038+
}
2039+
19892040
public abstract List<SQLFragment> getChangeStatements(TableChange change);
19902041

19912042
public abstract void purgeTempSchema(Map<String, TempTableTracker> createdTableNames);
@@ -2007,7 +2058,7 @@ protected void trackTempTables(Map<String, TempTableTracker> createdTableNames)
20072058
// Defragment an index, if necessary
20082059
public void defragmentIndex(DbSchema schema, String tableSelectName, String indexName)
20092060
{
2010-
// By default do nothing
2061+
// By default, do nothing
20112062
}
20122063

20132064
public boolean isTableExists(DbScope scope, String schema, String name)

core/src/org/labkey/core/admin/admin.jsp

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<%@ page import="org.apache.commons.lang3.StringUtils" %>
2020
<%@ page import="org.labkey.api.admin.AdminBean" %>
2121
<%@ page import="org.labkey.api.data.DbScope" %>
22-
<%@ page import="org.labkey.api.data.SqlSelector" %>
22+
<%@ page import="org.labkey.api.data.dialect.SqlDialect" %>
2323
<%@ page import="org.labkey.api.files.FileContentService" %>
2424
<%@ page import="org.labkey.api.module.DefaultModule" %>
2525
<%@ page import="org.labkey.api.module.Module" %>
@@ -33,13 +33,12 @@
3333
<%@ page import="org.labkey.api.view.NavTree" %>
3434
<%@ page import="org.labkey.core.admin.AdminController" %>
3535
<%@ page import="java.text.DecimalFormat" %>
36-
<%@ page import="java.time.Duration" %>
37-
<%@ page import="java.time.LocalDateTime" %>
3836
<%@ page import="java.time.format.DateTimeFormatter" %>
3937
<%@ page import="java.util.Collection" %>
4038
<%@ page import="java.util.Map" %>
4139
<%@ page import="java.util.TreeMap" %>
4240
<%@ page import="org.apache.commons.lang3.Strings" %>
41+
<%@ page import="org.labkey.api.util.DateUtil" %>
4342
<%@ page extends="org.labkey.api.jsp.JspBase" %>
4443
<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %>
4544
<%
@@ -89,15 +88,12 @@
8988
<%
9089
row = 0;
9190
92-
LocalDateTime serverTime = LocalDateTime.now();
93-
LocalDateTime databaseTime = new SqlSelector(DbScope.getLabKeyScope(), "SELECT CURRENT_TIMESTAMP").getObject(LocalDateTime.class);
94-
long duration = Math.abs(Duration.between(serverTime, databaseTime).toSeconds());
91+
SqlDialect.ServerDatabaseTimeDifference timeDifference = SqlDialect.getServerDatabaseTimeDifference(DbScope.getLabKeyScope());
92+
boolean exceedsThreshold = timeDifference.exceedsWarningThreshold();
9593
96-
// Warn if greater than this many seconds
97-
long warningSeconds = 10;
98-
99-
HtmlString style = unsafe(duration > warningSeconds ? " style=\"color:red;\"" : "");
100-
HtmlString warning = unsafe(duration > warningSeconds ? " - Warning: Web and database server times differ by " + duration + " seconds!" : "");
94+
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DateUtil.getJsonDateTimeFormatString());
95+
HtmlString style = unsafe(exceedsThreshold ? " style=\"color:red;\"" : "");
96+
HtmlString warning = unsafe(exceedsThreshold ? " - Warning: Web and database server times differ by " + timeDifference.getSeconds() + " seconds!" : "");
10197
%>
10298
<h4>Runtime Information</h4>
10399
<table class="labkey-data-region-legacy labkey-show-borders">
@@ -123,8 +119,8 @@
123119
<tr class="<%=getShadeRowClass(row++)%>"><td>Server GUID</td><td style="font-family:monospace"><%=h(AdminBean.serverGuid)%></td></tr>
124120
<tr class="<%=getShadeRowClass(row++)%>"><td>Server Session GUID</td><td style="font-family:monospace"><%=h(AdminBean.serverSessionGuid)%></td></tr>
125121
<tr class="<%=getShadeRowClass(row++)%>"><td>Server Startup Time</td><td<%=style%>><%=h(AdminBean.serverStartupTime)%></td></tr>
126-
<tr class="<%=getShadeRowClass(row++)%>"><td>Web Server Time</td><td<%=style%>><%=h(serverTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")))%><%=warning%></td></tr>
127-
<tr class="<%=getShadeRowClass(row++)%>"><td>Database Server Time</td><td<%=style%>><%=h(databaseTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")))%><%=warning%></td></tr>
122+
<tr class="<%=getShadeRowClass(row++)%>"><td>Web Server Time</td><td<%=style%>><%=h(timeDifference.serverTime().format(dateTimeFormatter))%><%=warning%></td></tr>
123+
<tr class="<%=getShadeRowClass(row++)%>"><td>Database Server Time</td><td<%=style%>><%=h(timeDifference.databaseTime().format(dateTimeFormatter))%><%=warning%></td></tr>
128124
</table>
129125
</labkey:panel>
130126
<labkey:panel id="links" className="lk-admin-section">

core/src/org/labkey/core/dialect/PostgreSql92Dialect.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,8 @@ public void addAdminWarningMessages(Warnings warnings, boolean showAllWarnings)
371371
super.addAdminWarningMessages(warnings, showAllWarnings);
372372
if (showAllWarnings)
373373
warnings.add(HtmlString.of(PostgreSqlDialectFactory.getStandardWarningMessage("has not been tested against", getMajorVersion() + ".x")));
374+
375+
addTimeDifferenceWarning(warnings, showAllWarnings);
374376
}
375377

376378
private int getIdentifierMaxByteLength()

experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,6 @@
139139
import java.io.IOException;
140140
import java.io.UncheckedIOException;
141141
import java.sql.Timestamp;
142-
import java.time.Duration;
143-
import java.time.LocalDateTime;
144142
import java.util.ArrayList;
145143
import java.util.Arrays;
146144
import java.util.Collection;
@@ -1406,19 +1404,15 @@ private _MaterializedQueryHelper getOrCreateMQH()
14061404
*/
14071405
private static boolean isIncrementalUpdateDisabled()
14081406
{
1407+
// Disable if web server and database time differ
14091408
if (_incrementalUpdateDisabled == null)
14101409
{
1411-
// borrowed from core/admin.jsp
1412-
LocalDateTime databaseTime = new SqlSelector(DbScope.getLabKeyScope(), "SELECT CURRENT_TIMESTAMP").getObject(LocalDateTime.class);
1413-
LocalDateTime serverTime = LocalDateTime.now();
1414-
1415-
// Disable if greater than this many seconds
1416-
long thresholdSeconds = 10;
1417-
long deltaSeconds = Math.abs(Duration.between(serverTime, databaseTime).toSeconds());
1418-
_incrementalUpdateDisabled = deltaSeconds > thresholdSeconds;
1410+
DbScope scope = DbScope.getLabKeyScope();
1411+
SqlDialect.ServerDatabaseTimeDifference difference = SqlDialect.getServerDatabaseTimeDifference(scope);
1412+
_incrementalUpdateDisabled = difference.exceedsWarningThreshold();
14191413

14201414
if (_incrementalUpdateDisabled)
1421-
_log.warn("Incremental update disabled for samples. Web and database server time differ by {} seconds which exceeds the threshold of {} seconds. You may experience degraded sample query performance.", deltaSeconds, thresholdSeconds);
1415+
_log.warn("Incremental update disabled for samples. Web and database server time differ by {} seconds which exceeds the threshold of {} seconds. You may experience degraded sample query performance.", difference.getSeconds(), SqlDialect.TIME_DIFFERENCE_WARNING_SECONDS);
14221416
}
14231417

14241418
return _incrementalUpdateDisabled;

0 commit comments

Comments
 (0)