split up read and write charts and added cache efficiency chart

(because we will add further read/write-specific stats like total number of files read/written, bytes encrypted/decrypted, etc)
This commit is contained in:
Sebastian Stenzel
2020-06-25 17:16:42 +02:00
parent 60dd9349a8
commit 9c034f3be6
15 changed files with 269 additions and 152 deletions

View File

@@ -2,9 +2,12 @@ package org.cryptomator.common.vaults;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.LongProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.util.Duration;
@@ -28,6 +31,7 @@ public class VaultStats {
private final ScheduledService<Optional<CryptoFileSystemStats>> updateService;
private final LongProperty bytesPerSecondRead = new SimpleLongProperty();
private final LongProperty bytesPerSecondWritten = new SimpleLongProperty();
private final DoubleProperty cacheHitRate = new SimpleDoubleProperty();
@Inject
VaultStats(AtomicReference<CryptoFileSystem> fs, ObjectProperty<VaultState> state, ExecutorService executor) {
@@ -55,6 +59,17 @@ public class VaultStats {
assert Platform.isFxApplicationThread();
bytesPerSecondRead.set(stats.map(CryptoFileSystemStats::pollBytesRead).orElse(0l));
bytesPerSecondWritten.set(stats.map(CryptoFileSystemStats::pollBytesWritten).orElse(0l));
cacheHitRate.set(stats.map(this::getCacheHitRate).orElse(0.0));
}
private double getCacheHitRate(CryptoFileSystemStats stats) {
long accesses = stats.pollChunkCacheAccesses();
long hits = stats.pollChunkCacheHits();
if (accesses == 0) {
return 0.0;
} else {
return hits / (double) accesses;
}
}
private class UpdateStatsService extends ScheduledService<Optional<CryptoFileSystemStats>> {
@@ -94,4 +109,13 @@ public class VaultStats {
public long getBytesPerSecondWritten() {
return bytesPerSecondWritten.get();
}
public DoubleProperty cacheHitRateProperty() {
return cacheHitRate;
}
public double getCacheHitRate() {
return cacheHitRate.get();
}
}

View File

@@ -28,7 +28,7 @@ public enum FxmlFile {
UNLOCK_INVALID_MOUNT_POINT("/fxml/unlock_invalid_mount_point.fxml"), //
UNLOCK_SUCCESS("/fxml/unlock_success.fxml"), //
VAULT_OPTIONS("/fxml/vault_options.fxml"), //
VAULT_STATISTICS("/fxml/vault_statistics.fxml"), //
VAULT_STATISTICS("/fxml/stats.fxml"), //
WRONGFILEALERT("/fxml/wrongfilealert.fxml");
private final String ressourcePathString;

View File

@@ -1,5 +1,6 @@
package org.cryptomator.ui.common;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.binding.LongBinding;
import javafx.beans.binding.StringBinding;
import javafx.beans.value.ObservableObjectValue;
@@ -50,4 +51,23 @@ public final class WeakBindings {
};
}
/**
* Create a new DoubleBinding that listens to changes from the given observable without being strongly referenced by it.
*
* @param observable The observable
* @return a DoubleBinding weakly referenced from the given observable
*/
public static DoubleBinding bindDouble(ObservableValue<Number> observable) {
return new DoubleBinding() {
{
bind(observable);
}
@Override
protected double computeValue() {
return observable.getValue().doubleValue();
}
};
}
}

View File

@@ -20,7 +20,7 @@ import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.migration.MigrationComponent;
import org.cryptomator.ui.removevault.RemoveVaultComponent;
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
import org.cryptomator.ui.vaultstatistics.VaultStatisticsComponent;
import org.cryptomator.ui.stats.VaultStatisticsComponent;
import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
import javax.inject.Provider;

View File

@@ -6,7 +6,7 @@ import javafx.fxml.FXML;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.VaultService;
import org.cryptomator.ui.vaultstatistics.VaultStatisticsComponent;
import org.cryptomator.ui.stats.VaultStatisticsComponent;
import javax.inject.Inject;

View File

@@ -1,4 +1,4 @@
package org.cryptomator.ui.vaultstatistics;
package org.cryptomator.ui.stats;
import dagger.BindsInstance;
import dagger.Lazy;

View File

@@ -1,13 +1,17 @@
package org.cryptomator.ui.vaultstatistics;
package org.cryptomator.ui.stats;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.binding.LongBinding;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.stage.Stage;
@@ -18,13 +22,14 @@ import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.WeakBindings;
import javax.inject.Inject;
import java.util.Arrays;
import java.util.ResourceBundle;
@VaultStatisticsScoped
public class VaultStatisticsController implements FxController {
private static final int IO_SAMPLING_STEPS = 100;
private static final double IO_SAMPLING_INTERVAL = 0.5;
private static final int IO_SAMPLING_STEPS = 30;
private static final double IO_SAMPLING_INTERVAL = 1;
private final VaultStats stats;
private final Series<Number, Number> readData;
@@ -32,19 +37,27 @@ public class VaultStatisticsController implements FxController {
private final Timeline ioAnimation;
private final LongBinding bpsRead;
private final LongBinding bpsWritten;
private final DoubleBinding cacheHitRate;
private final DoubleBinding cacheHitDregrees;
private final DoubleBinding cacheHitPercentage;
public LineChart<Number, Number> lineGraph;
public AreaChart<Number, Number> readChart;
public AreaChart<Number, Number> writeChart;
public NumberAxis readChartXAxis;
public NumberAxis readChartYAxis;
public NumberAxis writeChartXAxis;
public NumberAxis writeChartYAxis;
@Inject
public VaultStatisticsController(@VaultStatisticsWindow Stage window, @VaultStatisticsWindow Vault vault, ResourceBundle resourceBundle) {
this.stats = vault.getStats();
this.readData = new Series<>();
this.writeData = new Series<>();
this.bpsRead = WeakBindings.bindLong(stats.bytesPerSecondReadProperty());
this.bpsWritten = WeakBindings.bindLong(stats.bytesPerSecondWrittenProperty());
this.readData = new Series<>();
readData.setName(resourceBundle.getString("vaultstatistics.readDataLabel"));
this.writeData = new Series<>();
writeData.setName(resourceBundle.getString("vaultstatistics.writtenDataLabel"));
this.cacheHitRate = WeakBindings.bindDouble(stats.cacheHitRateProperty());
this.cacheHitDregrees = cacheHitRate.multiply(-270);
this.cacheHitPercentage = cacheHitRate.multiply(100);
this.ioAnimation = new Timeline(); //TODO Research better timer
ioAnimation.getKeyFrames().add(new KeyFrame(Duration.seconds(IO_SAMPLING_INTERVAL), new IoSamplingAnimationHandler(readData, writeData)));
@@ -60,14 +73,18 @@ public class VaultStatisticsController implements FxController {
@FXML
public void initialize() {
lineGraph.getData().addAll(readData, writeData);
readChart.getData().addAll(readData);
writeChart.getData().addAll(writeData);
}
private class IoSamplingAnimationHandler implements EventHandler<ActionEvent> {
private static final double BYTES_TO_MEGABYTES_FACTOR = 1.0 / IO_SAMPLING_INTERVAL / 1024.0 / 1024.0;
private long step = IO_SAMPLING_STEPS;
private final Series<Number, Number> decryptedBytesRead;
private final Series<Number, Number> encryptedBytesWrite;
private final long[] maxBuf = new long[IO_SAMPLING_STEPS];
public IoSamplingAnimationHandler(Series<Number, Number> readData, Series<Number, Number> writeData) {
this.decryptedBytesRead = readData;
@@ -82,23 +99,28 @@ public class VaultStatisticsController implements FxController {
@Override
public void handle(ActionEvent event) {
// move all values one step:
for (int i = 0; i < IO_SAMPLING_STEPS - 1; i++) {
int j = i + 1;
Number tmp = decryptedBytesRead.getData().get(j).getYValue();
decryptedBytesRead.getData().get(i).setYValue(tmp);
final long currentStep = step++;
final long decBytes = stats.bytesPerSecondReadProperty().get();
final long encBytes = stats.bytesPerSecondWrittenProperty().get();
tmp = encryptedBytesWrite.getData().get(j).getYValue();
encryptedBytesWrite.getData().get(i).setYValue(tmp);
}
maxBuf[(int) currentStep % IO_SAMPLING_STEPS] = Math.max(decBytes, encBytes);
long allTimeMax = Arrays.stream(maxBuf).max().orElse(0l);
// remove oldest value:
decryptedBytesRead.getData().remove(0);
encryptedBytesWrite.getData().remove(0);
// add latest value:
final long decBytes = stats.bytesPerSecondReadProperty().get();
final double decMb = decBytes * BYTES_TO_MEGABYTES_FACTOR;
final long encBytes = stats.bytesPerSecondWrittenProperty().get();
final double encMb = encBytes * BYTES_TO_MEGABYTES_FACTOR;
decryptedBytesRead.getData().get(IO_SAMPLING_STEPS - 1).setYValue(decMb);
encryptedBytesWrite.getData().get(IO_SAMPLING_STEPS - 1).setYValue(encMb);
decryptedBytesRead.getData().add(new Data<>(currentStep, decBytes));
encryptedBytesWrite.getData().add(new Data<>(currentStep, encBytes));
// adjust ranges:
readChartXAxis.setLowerBound(currentStep - IO_SAMPLING_STEPS);
readChartXAxis.setUpperBound(currentStep);
readChartYAxis.setUpperBound(allTimeMax);
writeChartXAxis.setLowerBound(currentStep - IO_SAMPLING_STEPS);
writeChartXAxis.setUpperBound(currentStep);
writeChartYAxis.setUpperBound(allTimeMax);
}
}
@@ -119,4 +141,20 @@ public class VaultStatisticsController implements FxController {
public long getBpsWritten() {
return bpsWritten.get();
}
public DoubleBinding cacheHitPercentageProperty() {
return cacheHitPercentage;
}
public double getCacheHitPercentage() {
return cacheHitPercentage.get();
}
public DoubleBinding cacheHitDregreesProperty() {
return cacheHitDregrees;
}
public double getCacheHitDregrees() {
return cacheHitDregrees.get();
}
}

View File

@@ -1,4 +1,4 @@
package org.cryptomator.ui.vaultstatistics;
package org.cryptomator.ui.stats;
import dagger.Binds;
import dagger.Module;
@@ -38,7 +38,7 @@ abstract class VaultStatisticsModule {
@VaultStatisticsScoped
static Stage provideStage(StageFactory factory, ResourceBundle resourceBundle, @VaultStatisticsWindow Vault vault) {
Stage stage = factory.create();
stage.setTitle(String.format(resourceBundle.getString("vaultstatistics.title"), vault.getDisplayableName()));
stage.setTitle(String.format(resourceBundle.getString("stats.title"), vault.getDisplayableName()));
stage.setResizable(false);
var weakStage = new WeakReference<>(stage);
vault.stateProperty().addListener(new ChangeListener<>() {
@@ -60,7 +60,7 @@ abstract class VaultStatisticsModule {
@FxmlScene(FxmlFile.VAULT_STATISTICS)
@VaultStatisticsScoped
static Scene provideVaultStatisticsScene(@VaultStatisticsWindow FXMLLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene("/fxml/vault_statistics.fxml");
return fxmlLoaders.createScene("/fxml/stats.fxml");
}
// ------------------

View File

@@ -1,4 +1,4 @@
package org.cryptomator.ui.vaultstatistics;
package org.cryptomator.ui.stats;
import javax.inject.Scope;
import java.lang.annotation.Documented;

View File

@@ -1,4 +1,4 @@
package org.cryptomator.ui.vaultstatistics;
package org.cryptomator.ui.stats;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;

View File

@@ -874,59 +874,52 @@
* I/O Statistics *
* *
******************************************************************************/
.chart {
.cache-arc-background {
-fx-fill: transparent;
-fx-stroke: MUTED_BG;
-fx-stroke-type: centered;
-fx-stroke-width: 12;
-fx-stroke-line-cap: butt;
}
.cache-arc-foreground {
-fx-fill: transparent;
-fx-stroke: PRIMARY;
-fx-stroke-type: centered;
-fx-stroke-width: 12;
-fx-stroke-line-cap: butt;
}
.chart.io-stats {
-fx-padding: 10px;
-fx-horizontal-grid-lines-visible: false;
-fx-horizontal-zero-line-visible: false;
-fx-vertical-grid-lines-visible: false;
-fx-vertical-zero-line-visible: false;
}
.axis.io-stats {
-fx-tick-mark-visible: false;
-fx-minor-tick-visible: false;
-fx-tick-labels-visible: false;
}
.chart-plot-background {
-fx-background-color: MAIN_BG;
-fx-padding: 20px;
-fx-background-color: transparent;
}
/* content */
.chart-content {
-fx-padding: 10px;
-fx-text-fill: TEXT_FILL;
-fx-tick-label-fill: GRAY_3;
-fx-minor-tick-visible: false
}
.chart-horizontal-grid-lines {
-fx-stroke: PRIMARY_L2;
}
.chart-vertical-zero-line,
.chart-horizontal-zero-line {
-fx-stroke: PRIMARY_L2;
}
.chart-series-line {
-fx-stroke-width: 2px;
}
.chart-horizontal-zero-line,
.chart-alternative-row-fill {
-fx-fill: GRAY_3;
-fx-stroke: transparent;
-fx-stroke-width: 0;
}
.axis {
-fx-tick-label-fill: TEXT_FILL;
-fx-tick-length: 20;
-fx-minor-tick-length: 10;
}
.axis-label {
-fx-text-fill: TEXT_FILL;
}
.default-color0.chart-series-line { -fx-stroke: PRIMARY; }
.default-color1.chart-series-line { -fx-stroke: RED_5 ; }
/* legend */
.chart-legend {
-fx-text-fill: TEXT_FILL;
-fx-background-color: transparent;
-fx-padding: 0.4em;
.default-color0.chart-series-area-line {
-fx-stroke: PRIMARY;
}
.chart-line-symbol {
-fx-background-radius: 5px;
-fx-padding: 5px;
.default-color0.chart-series-area-fill {
-fx-fill: linear-gradient(to bottom, PRIMARY, transparent);
-fx-stroke: transparent;
}
.default-color0.chart-line-symbol { -fx-background-color: PRIMARY; }
.default-color1.chart-line-symbol { -fx-background-color: RED_5; }

View File

@@ -884,48 +884,52 @@
/* content */
.chart-content {
.cache-arc-background {
-fx-fill: transparent;
-fx-stroke: MUTED_BG;
-fx-stroke-type: centered;
-fx-stroke-width: 12;
-fx-stroke-line-cap: butt;
}
.cache-arc-foreground {
-fx-fill: transparent;
-fx-stroke: PRIMARY;
-fx-stroke-type: centered;
-fx-stroke-width: 12;
-fx-stroke-line-cap: butt;
}
.chart.io-stats {
-fx-padding: 10px;
-fx-text-fill: TEXT_FILL;
-fx-tick-label-fill: GRAY_3;
-fx-minor-tick-visible: false
-fx-horizontal-grid-lines-visible: false;
-fx-horizontal-zero-line-visible: false;
-fx-vertical-grid-lines-visible: false;
-fx-vertical-zero-line-visible: false;
}
.chart-horizontal-grid-lines {
-fx-stroke: PRIMARY_D2;
.axis.io-stats {
-fx-tick-mark-visible: false;
-fx-minor-tick-visible: false;
-fx-tick-labels-visible: false;
}
.chart-plot-background {
-fx-background-color: transparent;
}
.chart-vertical-zero-line,
.chart-horizontal-zero-line {
-fx-stroke: PRIMARY_D2;
}
.chart-series-line {
-fx-stroke-width: 2px;
}
.chart-horizontal-zero-line,
.chart-alternative-row-fill {
-fx-fill: GRAY_8;
-fx-stroke: transparent;
-fx-stroke-width: 0;
}
.axis {
-fx-tick-label-fill: TEXT_FILL;
-fx-tick-length: 20;
-fx-minor-tick-length: 10;
}
.axis-label {
-fx-text-fill: TEXT_FILL;
}
.default-color0.chart-series-line { -fx-stroke: PRIMARY; }
.default-color1.chart-series-line { -fx-stroke: RED_5 ; }
/* legend */
.chart-legend {
-fx-text-fill: TEXT_FILL;
-fx-background-color: transparent;
-fx-padding: 0.4em;
.default-color0.chart-series-area-line {
-fx-stroke: PRIMARY;
}
.chart-line-symbol {
-fx-background-radius: 5px;
-fx-padding: 5px;
.default-color0.chart-series-area-fill {
-fx-fill: linear-gradient(to bottom, PRIMARY, transparent);
-fx-stroke: transparent;
}
.default-color0.chart-line-symbol { -fx-background-color: PRIMARY; }
.default-color1.chart-line-symbol { -fx-background-color: RED_5; }

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.chart.AreaChart?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.Cursor?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Arc?>
<?import org.cryptomator.ui.controls.FormattedLabel?>
<?import org.cryptomator.ui.controls.ThrougputLabel?>
<?import javafx.scene.Group?>
<HBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.cryptomator.ui.stats.VaultStatisticsController"
prefWidth="600.0" spacing="12">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<!-- Caching -->
<VBox prefWidth="200" prefHeight="200">
<StackPane>
<Group>
<Arc styleClass="cache-arc-background" centerX="100" centerY="100" radiusX="100" radiusY="100" startAngle="225" length="-270"/>
<Arc styleClass="cache-arc-foreground" centerX="100" centerY="100" radiusX="100" radiusY="100" startAngle="225" length="${controller.cacheHitDregrees}"/>
</Group>
<VBox StackPane.alignment="CENTER" alignment="CENTER">
<FormattedLabel styleClass="label-large" format="\%1.0f %%" arg1="${controller.cacheHitPercentage}" />
<Label text="%stats.cacheHitRate" />
</VBox>
</StackPane>
</VBox>
<!-- Read -->
<VBox prefWidth="200" prefHeight="200">
<HBox spacing="12">
<Label text="%stats.readDataLabel"/>
<ThrougputLabel styleClass="label-large" alignment="CENTER_RIGHT" minWidth="60" idleFormat="%main.vaultDetail.throughput.idle" kibsFormat="%main.vaultDetail.throughput.kbps"
mibsFormat="%main.vaultDetail.throughput.mbps" bytesPerSecond="${controller.bpsRead}"/>
</HBox>
<AreaChart fx:id="readChart" styleClass="io-stats" createSymbols="false" animated="false">
<xAxis>
<NumberAxis fx:id="readChartXAxis" styleClass="io-stats" autoRanging="false" forceZeroInRange="false" side="BOTTOM"/>
</xAxis>
<yAxis>
<NumberAxis fx:id="readChartYAxis" styleClass="io-stats" autoRanging="false" forceZeroInRange="true" side="LEFT" tickUnit="Infinity"/>
</yAxis>
<cursor>
<Cursor fx:constant="DEFAULT"/>
</cursor>
</AreaChart>
</VBox>
<!-- Write -->
<VBox prefWidth="200" prefHeight="200">
<HBox>
<Label text="%stats.writtenDataLabel"/>
<ThrougputLabel styleClass="label-large" alignment="CENTER_RIGHT" minWidth="60" idleFormat="%main.vaultDetail.throughput.idle" kibsFormat="%main.vaultDetail.throughput.kbps"
mibsFormat="%main.vaultDetail.throughput.mbps" bytesPerSecond="${controller.bpsWritten}"/>
</HBox>
<AreaChart fx:id="writeChart" styleClass="io-stats" createSymbols="false" animated="false">
<xAxis>
<NumberAxis fx:id="writeChartXAxis" styleClass="io-stats" autoRanging="false" forceZeroInRange="false" side="BOTTOM"/>
</xAxis>
<yAxis>
<NumberAxis fx:id="writeChartYAxis" styleClass="io-stats" autoRanging="false" forceZeroInRange="true" side="LEFT" tickUnit="Infinity"/>
</yAxis>
<cursor>
<Cursor fx:constant="DEFAULT"/>
</cursor>
</AreaChart>
</VBox>
</HBox>

View File

@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.chart.LineChart?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.Cursor?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import org.cryptomator.ui.controls.ThrougputLabel?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.cryptomator.ui.vaultstatistics.VaultStatisticsController"
prefHeight="400.0" prefWidth="600.0">
<LineChart fx:id="lineGraph" VBox.vgrow="ALWAYS" createSymbols="false" legendVisible="true" prefHeight="372.0" prefWidth="423.0" visible="true" animated="false"
title="%vaultstatistics.throughputTitle" verticalZeroLineVisible="true" verticalGridLinesVisible="false" horizontalGridLinesVisible="true">
<xAxis>
<NumberAxis autoRanging="false" lowerBound="0" side="BOTTOM" tickUnit="5" upperBound="100" label="%vaultstatistics.xAxisTimeLabel"/>
</xAxis>
<yAxis>
<NumberAxis autoRanging="true" lowerBound="0" side="LEFT" tickUnit="1024" upperBound="100" label="%vaultstatistics.yAxisThroughputLabel" forceZeroInRange="true"/>
</yAxis>
<cursor>
<Cursor fx:constant="DEFAULT"/>
</cursor>
</LineChart>
<HBox alignment="CENTER_RIGHT" spacing="6">
<Label styleClass="label-small,label-muted" text="%main.vaultDetail.bytesPerSecondRead"/>
<ThrougputLabel styleClass="label-small,label-muted" alignment="CENTER_RIGHT" minWidth="60" idleFormat="%main.vaultDetail.throughput.idle" kibsFormat="%main.vaultDetail.throughput.kbps"
mibsFormat="%main.vaultDetail.throughput.mbps" bytesPerSecond="${controller.bpsRead}"/>
</HBox>
<HBox alignment="CENTER_RIGHT" spacing="6">
<Label styleClass="label-small,label-muted" text="%main.vaultDetail.bytesPerSecondWritten"/>
<ThrougputLabel styleClass="label-small,label-muted" alignment="CENTER_RIGHT" minWidth="60" idleFormat="%main.vaultDetail.throughput.idle" kibsFormat="%main.vaultDetail.throughput.kbps"
mibsFormat="%main.vaultDetail.throughput.mbps" bytesPerSecond="${controller.bpsWritten}"/>
</HBox>
</VBox>

View File

@@ -161,12 +161,10 @@ preferences.donationKey.getDonationKey=Get a donation key
preferences.about=About
# Vault Statistics
vaultstatistics.title=Statistics for %s
vaultstatistics.xAxisTimeLabel=Seconds
vaultstatistics.yAxisThroughputLabel=Throughput in KiB/s
vaultstatistics.throughputTitle=Read and Writes
vaultstatistics.readDataLabel=Read Data
vaultstatistics.writtenDataLabel=Written Data
stats.title=Statistics for %s
stats.readDataLabel=Read Data
stats.writtenDataLabel=Written Data
stats.cacheHitRate=Cache Hit Rate
# Main Window
main.closeBtn.tooltip=Close