Skip to content

Commit

Permalink
Merge pull request #756 from frank-weinberg/feature/no-float
Browse files Browse the repository at this point in the history
eliminate floating point operations from backend & make metrics collection optional
  • Loading branch information
frank-weinberg authored Aug 11, 2024
2 parents 0513d50 + 91e6eb5 commit 39d323c
Show file tree
Hide file tree
Showing 22 changed files with 135 additions and 86 deletions.
13 changes: 9 additions & 4 deletions src/com/carolinarollergirls/scoreboard/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,18 @@ public void start() {
if (!Version.load()) { stop(null); }
} catch (IOException e) { stop(e); }

scoreBoard = new ScoreBoardImpl();
scoreBoard = new ScoreBoardImpl(useMetrics);

// JSON updates.
final JSONStateManager jsm = scoreBoard.getJsm();
new ScoreBoardJSONListener(scoreBoard, jsm);

// Controllers.
JettyServletScoreBoardController jetty = new JettyServletScoreBoardController(scoreBoard, jsm, host, port);
JettyServletScoreBoardController jetty =
new JettyServletScoreBoardController(scoreBoard, jsm, host, port, useMetrics);

// Viewers.
new ScoreBoardMetricsCollector(scoreBoard).register();
if (useMetrics) { new ScoreBoardMetricsCollector(scoreBoard).register(); }

final File autoSaveDir = new File(BasePath.get(), "config/autosave");
scoreBoard.runInBatch(new Runnable() {
Expand All @@ -79,7 +80,7 @@ public void run() {
});

// Only start auto-saves once everything is loaded in.
final AutoSaveJSONState autosaver = new AutoSaveJSONState(jsm, autoSaveDir);
final AutoSaveJSONState autosaver = new AutoSaveJSONState(jsm, autoSaveDir, useMetrics);
jetty.start();

Runtime.getRuntime().addShutdownHook(new Thread() {
Expand Down Expand Up @@ -127,6 +128,8 @@ private void parseArgv(String[] argv) {
host = arg.split("=", 2)[1];
} else if (arg.startsWith("--import=") || arg.startsWith("-i=")) {
importPath = arg.split("=", 2)[1];
} else if (arg.equals("--metrics") || arg.equals("-m")) {
useMetrics = true;
}
}

Expand Down Expand Up @@ -251,5 +254,7 @@ private void createGui() {

private File logFile = new File(BasePath.get(), "logs/crg.log");

private boolean useMetrics = false;

private static ScoreBoard scoreBoard;
}
12 changes: 10 additions & 2 deletions src/com/carolinarollergirls/scoreboard/core/ScoreBoardImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
import com.carolinarollergirls.scoreboard.utils.Version;

public class ScoreBoardImpl extends ScoreBoardEventProviderImpl<ScoreBoard> implements ScoreBoard {
public ScoreBoardImpl() {
public ScoreBoardImpl(boolean useMetrics) {
super(null, "", null);
this.useMetrics = useMetrics;
jsm = new JSONStateManager(useMetrics);
addProperties(props);
setupScoreBoard();
}
Expand Down Expand Up @@ -134,11 +136,17 @@ public JSONStateManager getJsm() {
return jsm;
}

@Override
public boolean useMetrics() {
return useMetrics;
}

@Override
public boolean isInitialLoadDone() {
return initialLoadDone;
}

private JSONStateManager jsm = new JSONStateManager();
private JSONStateManager jsm;
private boolean useMetrics;
private boolean initialLoadDone = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,9 @@ protected boolean isDisplayChange(long current, long last) {
// i.e. 3600ms will be displayed as 3s on a count up clock and as 4s on a count
// down clock.
if (isCountDirectionDown()) {
return Math.floor(((double) current - 1) / 1000) != Math.floor(((double) last - 1) / 1000);
return Math.floorDiv(current - 1, 1000) != Math.floorDiv(last - 1, 1000);
} else {
return Math.floor((double) current / 1000) != Math.floor((double) last / 1000);
return Math.floorDiv(current, 1000) != Math.floorDiv(last, 1000);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ public GameImpl(ScoreBoard sb, PreparedTeam team1, PreparedTeam team2, Ruleset r
initReferences(rs, null);
getTeam(Team.ID_1).loadPreparedTeam(team1);
getTeam(Team.ID_2).loadPreparedTeam(team2);
jsonSnapshotter = new JSONStateSnapshotter(sb.getJsm(), this);
jsonSnapshotter = new JSONStateSnapshotter(sb.getJsm(), this, sb.useMetrics());
}
public GameImpl(ScoreBoard parent, String id) {
super(parent, id, ScoreBoard.GAME);
initReferences(scoreBoard.getRulesets().getRuleset(Rulesets.ROOT_ID), null);
jsonSnapshotter = new JSONStateSnapshotter(getScoreBoard().getJsm(), this);
jsonSnapshotter = new JSONStateSnapshotter(getScoreBoard().getJsm(), this, parent.useMetrics());
}
public GameImpl(Game source) {
super(source.getScoreBoard(), UUID.randomUUID().toString(), ScoreBoard.GAME);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public interface ScoreBoard extends ScoreBoardEventProvider {
public CurrentGame getCurrentGame();

public JSONStateManager getJsm();
public boolean useMetrics();

public boolean isInitialLoadDone();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,17 @@
import io.prometheus.client.hotspot.DefaultExports;

public class JettyServletScoreBoardController {
public JettyServletScoreBoardController(ScoreBoard sb, JSONStateManager jsm, String host, int port) {
public JettyServletScoreBoardController(ScoreBoard sb, JSONStateManager jsm, String host, int port,
boolean useMetrics) {
scoreBoard = sb;
this.jsm = jsm;
this.host = host;
this.port = port;

init();
init(useMetrics);
}

protected void init() {
protected void init(boolean useMetrics) {
server = new Server();
ServerConnector sC = new ServerConnector(server);
sC.setHost(host);
Expand Down Expand Up @@ -78,12 +79,14 @@ protected void init() {
urlsServlet = new UrlsServlet(server);
sch.addServlet(new ServletHolder(urlsServlet), "/urls/*");

ws = new WS(scoreBoard, jsm);
ws = new WS(scoreBoard, jsm, useMetrics);
sch.addServlet(new ServletHolder(ws), "/WS/*");

DefaultExports.initialize();
metricsServlet = new MetricsServlet();
sch.addServlet(new ServletHolder(metricsServlet), "/metrics");
if (useMetrics) {
DefaultExports.initialize();
metricsServlet = new MetricsServlet();
sch.addServlet(new ServletHolder(metricsServlet), "/metrics");
}

HttpServlet sjs = new SaveJsonScoreBoard(jsm);
sch.addServlet(new ServletHolder(sjs), "/SaveJSON/*");
Expand Down
57 changes: 36 additions & 21 deletions src/com/carolinarollergirls/scoreboard/jetty/WS.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,31 @@

public class WS extends WebSocketServlet {

public WS(ScoreBoard s, JSONStateManager j) {
public WS(ScoreBoard s, JSONStateManager j, boolean useMetrics) {
sb = s;
jsm = j;
this.useMetrics = useMetrics;
if (useMetrics) {
connectionsActive =
Gauge.build().name("crg_websocket_active_connections").help("Current WebSocket connections").register();
messagesReceived = Counter.build()
.name("crg_websocket_messages_received")
.help("Number of WebSocket messages received")
.register();
messagesSentDuration = Histogram.build()
.name("crg_websocket_messages_sent_duration_seconds")
.help("Time spent sending WebSocket messages")
.register();
messagesSentFailures = Counter.build()
.name("crg_websocket_messages_sent_failed")
.help("Number of WebSocket messages we failed to send")
.register();
} else {
connectionsActive = null;
messagesReceived = null;
messagesSentDuration = null;
messagesSentFailures = null;
}
}

@Override
Expand All @@ -66,21 +88,12 @@ private boolean hasPermission(Device device, String action) {

private ScoreBoard sb;
private JSONStateManager jsm;
private boolean useMetrics;

private static final Gauge connectionsActive =
Gauge.build().name("crg_websocket_active_connections").help("Current WebSocket connections").register();
private static final Counter messagesReceived = Counter.build()
.name("crg_websocket_messages_received")
.help("Number of WebSocket messages received")
.register();
private static final Histogram messagesSentDuration = Histogram.build()
.name("crg_websocket_messages_sent_duration_seconds")
.help("Time spent sending WebSocket messages")
.register();
private static final Counter messagesSentFailures = Counter.build()
.name("crg_websocket_messages_sent_failed")
.help("Number of WebSocket messages we failed to send")
.register();
private final Gauge connectionsActive;
private final Counter messagesReceived;
private final Histogram messagesSentDuration;
private final Counter messagesSentFailures;

public class ScoreBoardWebSocketCreator implements WebSocketCreator {
@Override
Expand All @@ -106,7 +119,7 @@ public ScoreBoardWebSocket(String httpSessionId, String remoteAddress, String so

@OnWebSocketMessage
public synchronized void onMessage(Session session, String message_data) {
messagesReceived.inc();
if (useMetrics) { messagesReceived.inc(); }
try {
Map<String, Object> json = JSON.std.mapFrom(message_data);
String action = (String) json.get("action");
Expand Down Expand Up @@ -245,21 +258,23 @@ public void run() {
}

public void send(Map<String, Object> json) {
Histogram.Timer timer = messagesSentDuration.startTimer();
Histogram.Timer timer = useMetrics ? messagesSentDuration.startTimer() : null;
try {
wsSession.getRemote().sendStringByFuture(
JSON.std.with(JSON.Feature.WRITE_NULL_PROPERTIES).composeString().addObject(json).finish());
} catch (Exception e) {
Logger.printMessage("Error sending JSON update: " + e);
Logger.printStackTrace(e);
messagesSentFailures.inc();
} finally { timer.observeDuration(); }
if (useMetrics) { messagesSentFailures.inc(); }
} finally {
if (useMetrics) { timer.observeDuration(); }
}
}

@OnWebSocketConnect
public void onOpen(Session session) {
wsSession = session;
connectionsActive.inc();
if (useMetrics) { connectionsActive.inc(); }
jsm.register(this);
device.access();

Expand All @@ -278,7 +293,7 @@ public void onOpen(Session session) {

@OnWebSocketClose
public void onClose(int closeCode, String message) {
connectionsActive.dec();
if (useMetrics) { connectionsActive.dec(); }
jsm.unregister(this);
sb.getClients().removeClient(sbClient);

Expand Down
18 changes: 11 additions & 7 deletions src/com/carolinarollergirls/scoreboard/json/AutoSaveJSONState.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@

public class AutoSaveJSONState implements Runnable {

public AutoSaveJSONState(JSONStateManager jsm, File dir) {
public AutoSaveJSONState(JSONStateManager jsm, File dir, boolean useMetrics) {
this.dir = dir;
this.jsm = jsm;
this.useMetrics = useMetrics;
autosaveDuration = useMetrics ? Histogram.build()
.name("crg_json_autosave_write_duration_seconds")
.help("Time spent writing JSON autosaves to disk")
.register()
: null;

try {
FileUtils.forceMkdir(dir);
Expand All @@ -40,7 +46,7 @@ public AutoSaveJSONState(JSONStateManager jsm, File dir) {

@Override
public synchronized void run() {
Histogram.Timer timer = autosaveDuration.startTimer();
Histogram.Timer timer = useMetrics ? autosaveDuration.startTimer() : null;
try {
int n = AUTOSAVE_FILES;
getFile(n).delete();
Expand All @@ -51,7 +57,7 @@ public synchronized void run() {
}
writeAutoSave(getFile(0));
} catch (Exception e) { Logger.printMessage("WARNING: Unable to auto-save scoreboard : " + e.getMessage()); }
timer.observeDuration();
if (useMetrics) { timer.observeDuration(); }
}

private void writeAutoSave(File file) {
Expand Down Expand Up @@ -139,13 +145,11 @@ public static void loadFile(ScoreBoard scoreBoard, File f, Source source) throws

private File dir;
private JSONStateManager jsm;
private boolean useMetrics;
private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

private static final int AUTOSAVE_FILES = 6;
private static final int INTERVAL_SECONDS = 10;

private static final Histogram autosaveDuration = Histogram.build()
.name("crg_json_autosave_write_duration_seconds")
.help("Time spent writing JSON autosaves to disk")
.register();
private final Histogram autosaveDuration;
}
34 changes: 20 additions & 14 deletions src/com/carolinarollergirls/scoreboard/json/JSONStateManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@
import io.prometheus.client.Histogram;

public class JSONStateManager {
public JSONStateManager(boolean useMetrics) {
this.useMetrics = useMetrics;
updateStateDuration = useMetrics ? Histogram.build()
.name("crg_json_state_disk_snapshot_duration_seconds")
.help("Time spent writing JSON state snapshots to disk")
.register()
: null;
updateStateUpdates = useMetrics ? Histogram.build()
.name("crg_json_update_state_updates")
.help("Updates sent to JSONStateManager.updateState function")
.exponentialBuckets(1, 2, 10)
.register()
: null;
}

public synchronized void register(JSONStateListener source) {
ExecutorService executor = Executors.newSingleThreadExecutor();
Expand Down Expand Up @@ -41,7 +55,7 @@ public void updateState(String key, Object value) {
}

public synchronized void updateState(List<WSUpdate> updates) {
Histogram.Timer timer = updateStateDuration.startTimer();
Histogram.Timer timer = useMetrics ? updateStateDuration.startTimer() : null;
StateTrie changedState = new StateTrie();

for (WSUpdate update : updates) { changedState.add(update.getKey(), update.getValue()); }
Expand All @@ -65,8 +79,8 @@ public void run() {
});
}
}
timer.observeDuration();
updateStateUpdates.observe(updates.size());
if (useMetrics) { timer.observeDuration(); }
if (useMetrics) { updateStateUpdates.observe(updates.size()); }
}

public synchronized Map<String, Object> getState() { return state.getAll(false); }
Expand All @@ -84,15 +98,7 @@ protected void waitForSent() {
private StateTrie state = new StateTrie();
private final AtomicInteger pending = new AtomicInteger();

private static final Histogram updateStateDuration =
Histogram.build()
.name("crg_json_update_state_duration_seconds")
.help("Time spent in JSONStateManager.updateState function")
.register();
private static final Histogram updateStateUpdates =
Histogram.build()
.name("crg_json_update_state_updates")
.help("Updates sent to JSONStateManager.updateState function")
.exponentialBuckets(1, 2, 10)
.register();
private boolean useMetrics;
private final Histogram updateStateDuration;
private final Histogram updateStateUpdates;
}
Loading

0 comments on commit 39d323c

Please sign in to comment.