From 28f2d19de6bf66ee1d6bfb9694d2f434610a7eae Mon Sep 17 00:00:00 2001 From: Leo Date: Sun, 30 Jun 2019 15:21:55 +0200 Subject: [PATCH] implement sharding calculator --- CHANGES.rst | 9 + app/conf/plugins.json | 13 + app/plugins/tutorial/calculator.css | 103 ++++++ app/plugins/tutorial/calculator.html | 319 ++++++++++++++++++ app/plugins/tutorial/calculator.js | 258 ++++++++++++++ app/plugins/tutorial/static/i18n/de.json | 54 ++- app/plugins/tutorial/static/i18n/en.json | 54 ++- .../static/icons/sharding-calculator-logo.png | Bin 0 -> 72011 bytes app/plugins/tutorial/tutorial.html | 8 + app/scripts/filter/numbers.js | 2 +- app/static/i18n/de.json | 4 +- app/static/i18n/en.json | 4 +- 12 files changed, 821 insertions(+), 7 deletions(-) mode change 100644 => 100755 app/conf/plugins.json create mode 100644 app/plugins/tutorial/calculator.css create mode 100755 app/plugins/tutorial/calculator.html create mode 100755 app/plugins/tutorial/calculator.js create mode 100644 app/plugins/tutorial/static/icons/sharding-calculator-logo.png mode change 100644 => 100755 app/plugins/tutorial/tutorial.html diff --git a/CHANGES.rst b/CHANGES.rst index 89dfa8ea5..d64d3690c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,15 @@ Changes for Crate Admin Interface Unreleased ========== +2019/06/30 1.15.1 +================= + +Feature +------- + +- sharding-calculator: a simple tool to walk a user through the process, how + to split their crate table into shards. Can also read stats from existing table. + 2019/04/03 1.15.0 ================= diff --git a/app/conf/plugins.json b/app/conf/plugins.json old mode 100644 new mode 100755 index f53e5a6aa..279dcc641 --- a/app/conf/plugins.json +++ b/app/conf/plugins.json @@ -53,4 +53,17 @@ "controller": "ShardsController" } } +}, { + "name": "calculator", + "uri": "static/plugins/tutorial/calculator.js", + "stylesheet": "static/plugins/tutorial/calculator.css", + "enterprise": false, + "routing": { + "/calculator": { + "name": "calculator", + "url": "/calculator", + "templateUrl": "static/plugins/tutorial/calculator.html", + "controller": "CalculatorController" + } + } }] diff --git a/app/plugins/tutorial/calculator.css b/app/plugins/tutorial/calculator.css new file mode 100644 index 000000000..9a5b9f647 --- /dev/null +++ b/app/plugins/tutorial/calculator.css @@ -0,0 +1,103 @@ + +.cr-panel-block--calculator{ /* refer to .cr-panel-block--info */ + /* big box */ + background-color: #353535; + margin-top: 10px; + margin-bottom: 10px; + padding-top: 0px; + padding-right: 10px; + padding-bottom: 10px; + padding-left: 10px; + border: 1px solid #545454; +} + +.cr-panel-block__calculator{ /* refer to cr-panel-block__item */ + margin: 5px; +} +.cr-panel-block__calculator__header{ + padding-bottom: 3px; + letter-spacing: 0.6px; + font-weight: bold; + background-color: #353535; + font-size: 10px; +} + +.cr-panel-block__calculator__content{ + /* small box */ + width:100%; + margin-left:10px; + margin-right:10px; + margin-top:0px; + margin-bottom:0px; + padding: 7px; + font-size: 13px; + overflow: hidden; + word-break: normal; + border: 1px solid #242424 !important; + border-radius: 3px; + background: #242424 !important; +} + + +.result { + font-size:14px; + font-weight: bold; + color: #55d4f5; /*crate blue*/ +} + + +.last_row { + padding-bottom:0px !important; + margin-bottom:0px !important; +} + +.column { + float: left; + width: 50%; + padding: 5px; +} + +/* Clear floats after the columns */ +.row:after { + content: ""; + display: table; + clear: both; +} + +input{ + width:50px; + background-color:#545454; + border: 1px solid #545454; + border-radius: 4px; +} + + +input:disabled { + color: #606060; +} + +select { + width:auto; + background-color:#545454; + border: 1px solid #545454; + border-radius: 4px; +} + +select:disabled { + color: #606060; +} + +.crb-conatiner { /*crb ... cool-radio-button*/ + width: 100%; + overflow: hidden; +} + +.crb-button { /*crb ... cool-radio-button*/ + float: left; + padding-left: -25px; + margin-right: -15px; +} + +.crb-description { /*crb ... cool-radio-button*/ + padding-top: 0.3%; +} \ No newline at end of file diff --git a/app/plugins/tutorial/calculator.html b/app/plugins/tutorial/calculator.html new file mode 100755 index 000000000..94df472e8 --- /dev/null +++ b/app/plugins/tutorial/calculator.html @@ -0,0 +1,319 @@ +
+
+

+ {{:: 'CALCULATOR.TITLE' | translate}} +

+

+ {{:: 'CALCULATOR.DESCRIPTION' | translate}} +

+ +
+
+
+

{{:: 'CALCULATOR.IMPORT_HEADING' | translate}}

+ +
+ +
+

+ {{:: 'CALCULATOR.HARDWARE_HEADING' | translate}} +

+ + +
+

+ {{:: 'CALCULATOR.CPUS_HEADING' | translate}} +

+

+ {{:: 'CALCULATOR.CPUS_DESCRIPTION' | translate}} +

+
+ +
+
+ + +
+

+ {{:: 'CALCULATOR.STORAGE_HEADING' | translate}} +

+
+ {{:: 'CALCULATOR.STORAGE_DESCRIPTION' | translate}} +
+
+ + +
+

+ {{:: 'CALCULATOR.RAMSTORAGE_HEADING' | translate}} +

+

+ {{:: 'CALCULATOR.RAMSTORAGE_DESCRIPTION' | translate}} +

+ +
+ + {{:: 'CALCULATOR.RAMSTORAGE_INPUT_1' | translate}} + + + + {{:: 'CALCULATOR.RAMSTORAGE_INPUT_2' | translate}} + +
+
+ + +
+

+ {{:: 'CALCULATOR.RAM_HEADING' | translate}} +

+
+

+ {{:: 'CALCULATOR.RAM_DESCRIPTION_1' | translate}} +

+
+ +

+ + + + + + {{:: 'CALCULATOR.RAM_DESCRIPTION_2' | translate}} + +

+ +

+ {{:: 'CALCULATOR.RAM_DESCRIPTION_3' | translate}} +

+ +
+
+
+ + +
+

+ {{:: 'CALCULATOR.RAID_HEADING' | translate}} +

+
+ {{:: 'CALCULATOR.RAID_DESCRIPTION' | translate}} +
+
+
+
+ + + + + +
+
+

+ {{:: 'CALCULATOR.USECASE_HEADING' | translate}} +

+ + +
+

+ {{:: 'CALCULATOR.DATA_HEADING' | translate}} +

+

+ {{:: 'CALCULATOR.DATA_DESCRIPTION_1' | translate}} +

+ +
+

+

+
+ +
+
+ {{:: 'CALCULATOR.DATA_DESCRIPTION_2' | translate}} +
+
+

+ +
+ + + + + + + {{:: 'CALCULATOR.DATA_DESCRIPTION_3' | translate}} + + + +

+

+ {{:: 'CALCULATOR.DATA_DESCRIPTION_4' | translate}} +

+

+ + + +

+
+
+ +

+ {{:: 'CALCULATOR.DATA_DESCRIPTION_5' | translate}} +

+ +
+

+

+
+ +
+
+ {{:: 'CALCULATOR.DATA_DESCRIPTION_6' | translate}} +
+
+

+ +
+ + + + + + + {{:: 'CALCULATOR.DATA_DESCRIPTION_7' | translate}} + + +
+
+
+ + + +
+

+ {{:: 'CALCULATOR.PARTITION_HEADING' | translate}} +

+

+ {{:: 'CALCULATOR.PARTITION_DESCRIPTION_1' | translate}} +

+ +
+
+

+ {{:: 'CALCULATOR.PARTITION_DESCRIPTION_2' | translate}}

+ + + + +
+ +
+

+ {{:: 'CALCULATOR.PARTITION_DESCRIPTION_3' | translate}}

+ + partitions. + +
+
+
+ + + + +
+

+ {{:: 'CALCULATOR.REDUNDANCY_HEADING' | translate}} +

+

+ {{:: 'CALCULATOR.REDUNDANCY_DESCRIPTION' | translate}} +

+ +
+ +
+
+
+
+
+ + + +
+

+ {{:: 'CALCULATOR.CALCULATION_HEADING' | translate}} +

+

+ {{:: 'CALCULATOR.CALCULATION_DESCRIPTION' | translate}} +

+ + +
+

+ {{:: 'CALCULATOR.NODES_HEADING' | translate}} +

+
+ {{:: 'CALCULATOR.NODES_DESCRIPTION_1' | translate}} + + {{:: 'CALCULATOR.NODES_DESCRIPTION_2' | translate}} + + {{selectedRAM() | bytes:0}} + + {{:: 'CALCULATOR.NODES_DESCRIPTION_3' | translate}} + + {{storagePerNode() | bytes:2}} + + {{:: 'CALCULATOR.NODES_DESCRIPTION_4' | translate}} +
+
+ + + +
+

+ {{:: 'CALCULATOR.SHARDS_HEADING' | translate}} +

+
+ {{:: 'CALCULATOR.SHARDS_DESCRIPTION_1' | translate}} + + {{:: 'CALCULATOR.SHARDS_DESCRIPTION_2' | translate}} +
+
+
+
+
\ No newline at end of file diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js new file mode 100755 index 000000000..2dc83b822 --- /dev/null +++ b/app/plugins/tutorial/calculator.js @@ -0,0 +1,258 @@ +'use strict'; + +// storage is measured in Bytes +// time is measured in hours + +angular.module('calculator', ['sql', 'translation']).controller('CalculatorController', function($scope, SQLQuery, queryResultToObjects) { + var diskLoadFactor = 0.85; + var maxRAMPerNode = 64000000000; //64G + $scope.RAMInput = 64; + $scope.RAMInputUnitPrefix = 'Gibi'; + $scope.hideGCHint = true; + var sizeFactor = 0.732; //from haudi's document + var maxShardSize = 32000000000; //32G, compromise from haudi's and andrei's opinions + var maxShards = 1000; + $scope.CPUCoresPerNode = 2; + $scope.RAMStorageProportion = 24; + $scope.dataType = 'perTime'; + $scope.dataInsertedPerTime = 20; + $scope.expectedTableSize = 2; + $scope.expectedTableSizeUnitPrefix = 'Tebi'; + $scope.dataInsertedPerTimeUnitPrefix = 'Gibi'; + $scope.dataInsertedPerTimeTemporalUnit = 'day'; + $scope.keepTimeTemporalUnit = 'month'; + $scope.keepTime = 6; + $scope.partitionSize = 1; + $scope.partitionSizeTemporalUnit = 'month'; + $scope.manualPartitionCount = 4; + $scope.replicas = 1; + $scope.tables = []; + var selectSchema = "none"; + var selectTable = "none"; + $scope.selected = "none"; + + $scope.selectedRAM = function (){ + var r = $scope.RAMInput * prefix($scope.RAMInputUnitPrefix); + $scope.hideGCHint = r <= maxRAMPerNode; + return r; + }; + var neededDiskSpace = function () { + var res = 1; + if ($scope.dataType === 'absolute') { + res = ($scope.expectedTableSize * prefix($scope.expectedTableSizeUnitPrefix) * (1 + Number($scope.replicas))) / sizeFactor / diskLoadFactor; + } else if ($scope.dataType === 'perTime') { + res = ((prefix($scope.dataInsertedPerTimeUnitPrefix) * $scope.dataInsertedPerTime / temporalUnit($scope.dataInsertedPerTimeTemporalUnit)) * $scope.keepTime * temporalUnit($scope.keepTimeTemporalUnit) * (1 + Number($scope.replicas))) / diskLoadFactor / sizeFactor; //explicit cast of replica to number is necessary, otherwise 1+1=11. thanks java script + } + return res; + }; + $scope.neededNodes = function () { + return Math.ceil((neededDiskSpace() / $scope.RAMStorageProportion) / $scope.selectedRAM()); + }; + $scope.partitions = function () { + var res = 1; + if ($scope.dataType === 'perTime') { + res = (($scope.keepTime * temporalUnit($scope.keepTimeTemporalUnit)) / ($scope.partitionSize * temporalUnit($scope.partitionSizeTemporalUnit))); + } else if ($scope.dataType === 'absolute') { + res = $scope.manualPartitionCount; + } + return res; + }; + var shards = function () { + return Math.ceil(($scope.neededNodes() * $scope.CPUCoresPerNode) / $scope.partitions()); + }; + var shardSize = function (shards) { + return neededDiskSpace() / (shards * $scope.partitions() * (1 + Number($scope.replicas))); + }; + $scope.storagePerNode = function () { + if ($scope.neededNodes()!==0){ + return Number(neededDiskSpace()) / Number($scope.neededNodes()); + } + else{ + return 0; + } + }; + var prefix = function (x) { + switch (x) { + case "Tebi": + return Math.pow(2, 40); + case "Gibi": + return Math.pow(2, 30); + case "Mebi": + return Math.pow(2, 20); + case "Kibi": + return Math.pow(2, 10); + default: + return Math.pow(10, 0); + } + }; + var temporalUnit = function (x) { + switch (x) { + case "hour": + return 1; + case "day": + return 24; + case "week": + return 7 * 24; + case "month": + return 30 * 24; + case "year": + return 365 * 24; + default: + return 1; + } + }; + $scope.result = function () { + var s = shards(); + if (shardSize(s) > maxShardSize) { + s = Math.ceil(s * (shardSize(s) / maxShardSize)); + } + if (s > maxShards) { + return "maximum shard limit exceeded, please talk to an crate engineer about your use-case"; + } + return s; + }; + + + $scope.gettablename = function() { + var stmt = "SELECT table_name, table_schema FROM information_schema.tables WHERE table_schema NOT IN ('information_schema', 'pg_catalog', 'sys', 'blob') order by table_schema, table_name"; + var cols = ['table_name', 'schema_name']; + var obj = []; + SQLQuery.execute(stmt, {}, false, false, false, false).then(function (query) { + $scope.sqlresult = queryResultToObjects(query, cols); + for(var i=0; i<$scope.sqlresult.length; i++) { + obj.push([$scope.sqlresult[i].schema_name, $scope.sqlresult[i].table_name]); + } + $scope.tables = obj; + }); + + }; + + $scope.tableSelected = function () { + selectSchema = $scope.selected[0]; + selectTable = $scope.selected[1]; + loadCPUCores(selectSchema, selectTable); + loadTablesize(selectSchema, selectTable); + loadPartition(selectSchema, selectTable); + loadReplica(selectSchema, selectTable); + loadRAMStoragePropotion(); + loadRAM(); + }; + var loadCPUCores = function (schemaName, tableName) { + var stmt = "SELECT os_info['available_processors']\n" + + "FROM sys.nodes limit 100;"; + SQLQuery.execute(stmt, {}, false, false, false, false).then(function (query) { + $scope.CPUCoresPerNode = (query.rows[0])[0]; //we get a 2d array returned + }); + }; + + var loadTablesize = function(schemaName, tableName) { + var stmt = "select sum(size) from sys.shards where schema_name = '" + schemaName + + "'and table_name = '"+tableName+"' and primary=true;"; + SQLQuery.execute(stmt, {}, false, false, false, false).then(function (query) { + if ((query.rows[0])[0]===null){ + $scope.expectedTableSize = 0; + $scope.expectedTableSizeUnitPrefix = '1'; + $scope.dataType = 'absolute'; + return; + } + var size = (query.rows[0])[0]; + $scope.expectedTableSize = Number(getPrefixedNumber(size)); + $scope.expectedTableSizeUnitPrefix = getPrefix(size); + $scope.dataType = 'absolute'; + }); + }; + + var loadPartition = function(schemaName, tableName) { + var stmt = "select partitioned_by from information_schema.tables where table_schema = '" + +schemaName+"' and table_name = '"+tableName+"';"; + SQLQuery.execute(stmt, {}, false, false, false, false).then(function (query) { + if((query.rows[0])[0]!==null){ + stmt = "SELECT COUNT(*) FROM(select * from information_schema.table_partitions WHERE schema_name = '" + +schemaName+"' and table_name = '"+tableName+"') as x;"; + SQLQuery.execute(stmt, {}, false, false, false, false).then(function (query) { + $scope.manualPartitionCount = (query.rows[0])[0]; + }); + } + else{ + $scope.manualPartitionCount = 1; + } + }); + }; + + var loadReplica = function(schemaName, tableName) { + var rep = ""; + var stmt = "SELECT number_of_replicas FROM information_schema.tables WHERE table_schema='" + +schemaName+"' and table_name = '"+tableName+"';"; + SQLQuery.execute(stmt, {}, false, false, false, false).then(function (query) { + rep = (query.rows[0])[0]; + if(rep.includes("-") === true){ + rep = rep.split("-")[1]; + stmt = "SELECT COUNT(*) FROM sys.nodes"; + SQLQuery.execute(stmt, {}, false, false, false, false).then(function (query) { + if (rep ==="all"){ + $scope.replicas = (query.rows[0])[0]-1; + } + else{ + $scope.replicas = Math.min(Number(rep),(query.rows[0])[0]-1); + } + }); + } + else{ + $scope.replicas = Number(rep); + } + }); + }; + + var loadRAMStoragePropotion = function() { + var stmt = "SELECT fs['total']['available']/(heap['used']+heap['free']) AS RAMStoragePropotion FROM sys.nodes;"; + SQLQuery.execute(stmt, {}, false, false, false, false).then(function (query) { + var sum = 0; + for(var i = 0; i < query.rows.length; i++){ + sum += query.rows[i]; + } + var avg = Math.round(sum / query.rows.length); + $scope.RAMStorageProportion = avg; + }); + }; + + var getPrefix = function(x){ + if (x < Math.pow(2, 10)) { + return "1"; + } else if (x < Math.pow(2, 20)){ + return "Kibi"; + } else if (x < Math.pow(2, 30)){ + return "Mebi"; + } else if (x < Math.pow(2, 40)) { + return "Gibi"; + } else { + return "Tebi"; + } + }; + + var getPrefixedNumber = function(x){ + if (x < Math.pow(2, 10)) { + return x; + } else if (x < Math.pow(2, 20)){ + return (x / Math.pow(2, 10)).toFixed(1); + } else if (x < Math.pow(2, 30)){ + return (x / Math.pow(2, 20)).toFixed(1); + } else if (x < Math.pow(2, 40)) { + return (x / Math.pow(2, 30)).toFixed(1); + } else { + return (x / Math.pow(2, 40)).toFixed(1); + } + }; + + var loadRAM = function () { + var stmt = "SELECT (heap['used']+heap['free']) AS total_ram FROM sys.nodes;"; + SQLQuery.execute(stmt, {}, false, false, false, false).then(function (query) { + var sum = 0; + for(var i = 0; i < query.rows.length; i++){ + sum += Number(query.rows[i]); //thanks for adding objects here javascript + } + var avg = Math.round(sum / query.rows.length); + $scope.RAMInputUnitPrefix = getPrefix(avg); + $scope.RAMInput = Number(getPrefixedNumber(avg)); + }); + }; +}); diff --git a/app/plugins/tutorial/static/i18n/de.json b/app/plugins/tutorial/static/i18n/de.json index 311d6a7df..850179cc7 100644 --- a/app/plugins/tutorial/static/i18n/de.json +++ b/app/plugins/tutorial/static/i18n/de.json @@ -4,11 +4,11 @@ }, "TUTORIAL": { "TITLE": "Mit Beispieldaten loslegen", - "PARAGRAPH_1": "Die Arbeit mit Beispieldaten hilft dir CrateDB etwas besser zu verstehen. Mit diesem Twitter-Importer ist das ganz einfach und du kannst schnell und mühelos deine neue CrateDB Instanz mit realen Daten testen. Klicke auf den Button, und beobachte wie dein Cluster in Echtzeit mit Live-Tweets gefüllt wird. Keine Sorge, diese können später immer noch gelöscht werden.", + "PARAGRAPH_1": "Die Arbeit mit Beispieldaten hilft dir, CrateDB etwas besser zu verstehen. Mit diesem Twitter-Importer ist das ganz einfach und du kannst schnell und mühelos deine neue CrateDB Instanz mit realen Daten testen. Klicke auf den Button, und beobachte wie dein Cluster in Echtzeit mit Live-Tweets gefüllt wird. Keine Sorge, diese können später immer noch gelöscht werden.", "PARAGRAPH_2": "Nach der Authentifizierung werden Tweets einzeln importiert, was natürlich langsam ist. Allerdings können in einer Produktionsumgebung bis zu mehreren tausend Datensätze pro Sekunde indiziert werden.", "INSTRUCTION_1": "Deine Tweets werden in der Tabelle {tweets} gespeichert", "TWEETS": "tweets", - "INSTRUCTION_2": "Klicke in der linken Menüleiste auf {tables} um alle Tabellen und deren Statistiken zu sehen. Du müsstest sehen, wie deren Datensätze steigen während Twitter importiert.", + "INSTRUCTION_2": "Klicke in der linken Menüleiste auf {tables} um alle Tabellen und deren Statistiken zu sehen. Du siehst, wie die Anzahl der Datensätze steigt während neue Daten von Twitter importiert werden.", "TABLES": "TABLES", "INSTRUCTION_3": "Klicke in der linken Menüleiste auf {console} um eine SQL-Abfrage zu starten. Zum Beispiel: {query}", "CONSOLE": "KONSOLE", @@ -16,5 +16,55 @@ "IMPORTING_TWEETS": "Importiere Tweets.", "TWEETS_IMPORTED": "Tweets importiert.", "STOP_IMPORTING_TWEETS": "Genug! Import gestoppt." + }, + "CALCULATOR": { + "TITLE": "Shard Rechner", + "DESCRIPTION": "Ein einfaches Werkzeug um eine generische Empfehlung für die Shard Größe für eine Tabelle in der CrateDB.", + "IMPORT_HEADING": "Daten von einer bereits existierenden Tabelle einlesen", + "HARDWARE_HEADING": "Hardware", + "CPUS_HEADING": "CPUs", + "CPUS_DESCRIPTION": "Wie viele CPU Kerne haben die Rechner auf welchen die Knoten laufen?", + "STORAGE_HEADING": "Präsistenter Speicher", + "STORAGE_DESCRIPTION": "Du verwendest SSDs anstadt HDDs? Gut.", + "RAMSTORAGE_HEADING": "Haupspeicher im Verhaltnis zu präsistentem Speicher", + "RAMSTORAGE_DESCRIPTION": "Das Verhältnis von Haupspeicher und prästistentem Speicher beinflusst die Leistung ganz allgemein. Ein Verältnis um 1:24 wird empfohlen. Wenn die Geschwindigkeit nicht so wichtig ist, kann auch darunter gegangen werden. Für eine schnelleres Cluster, kann ein engeres Verhältnis gewählt werden, aber astronomische Werte bringen natürlich nichts.", + "RAMSTORAGE_INPUT_1": "Jedes GiB RAM dient", + "RAMSTORAGE_INPUT_2": "GiB von präsistentem Speicher.", + "RAM_HEADING": "RAM", + "RAM_DESCRIPTION_1": "Wie viel Hauptspeicher haben die Rechner auf welchen die Knoten laufen sollen? Es wird empfohlen die Hälfte des Haupspeichers der virtuellen Java Maschiene zur Verfügung zu stellen und den Rest dem Betriebsystem. 64GiB an Haupspeicher sind ein guter Richtwert.", + "RAM_DESCRIPTION_2": "Byte", + "RAM_DESCRIPTION_3": "Wenn mehr als 32GiB der JVM als Heap zugewiesen werden, ist es gut möglich, dass es Probleme bei der Garbage Collection gibt.", + "RAID_HEADING": "RAID?", + "RAID_DESCRIPTION": "CrateDB ist verteilt und selbst-heilend, Replikate der Daten werden im Cluster verteilt, wenn sie eingeschaltet sind. Das ist empfehlenswert, denn es macht RAID1 überflüssig und ermöglicht sogar den sicheren Einsatz von RAID0.", + "USECASE_HEADING": "Anwendungsfall", + "DATA_HEADING": "Daten", + "DATA_DESCRIPTION_1": "Hierum dreht es sich bei crate. Du hast vermutlich schon eine andere Datenbankt in Verwendung gehabt und weißt vermutlich um viel Daten es geht.", + "DATA_DESCRIPTION_2": "Wie viel wird eingefügt?", + "DATA_DESCRIPTION_3": "Byte pro", + "DATA_DESCRIPTION_4": "Und wie lange sollen sie gespeichert werden?", + "DATA_DESCRIPTION_5": "Weil crate horizontal skallierbar ist, können auch immer später noch weitere Knoten hinzugefügt werden, um die Daten länger auf zu bewahren.", + "DATA_DESCRIPTION_6": "ODER, wenn nicht bekannt ist, wann wie viel Daten eingefügt werden, kannst du auch einfach eine erwartete Tabellen Größe angeben.", + "DATA_DESCRIPTION_7": "Byte", + "PARTITION_HEADING": "Partitionierung", + "PARTITION_DESCRIPTION_1": "crate organisirt die Daten in logische Einheiten, typischerweise nach Zeit. Wenn ein guter Zeitraum gewählt ist, kann das die Leistung drastisch erhöhen.", + "PARTITION_DESCRIPTION_2": "Wähle eine Größeneinheit, in der die Daten später verwendet werden. Wenn du keine Ahnung hast, dann ist ein Monat eine gute Empfehlung.", + "PARTITION_DESCRIPTION_3": "Du hast 'erwartete Tabellen Größe' ausgewählt, in dem Fall musst du die Anzahl der Partitionen manuell festlegen.", + "REDUNDANCY_HEADING": "Redundanz", + "REDUNDANCY_DESCRIPTION": "Wie gesagt verteilt crate Replikate über das ganze Cluster, ein Replikat sollte speicherplatzmäßig leistbar sein. Um Redundanz und um Datenintegrität zu gewährleisten. Keine Replikate zu haben ist naturlich am sparsamsten was den Speicher angeht, ist aber nicht empfehlenswert. Mehrere Replikate können die Abfragegeschwindigkeit signiffikant erhöhen, denn die Abfrage kann simultan auf mehreren Kopien des selben Datensatzes laufen. Der Nachteil ist natürlich der Speicherbedarf und es beinträchtigt sogar die Schreibgeschwindigkeit ein Bisschen. Demnach einst ein Replikat der normale Weg, wenn aber die Lesegeschwindigkeit wichtig ist kann man auch 3 oder 4 nehmen. Abgesehen davon, mehr Replikate als Knoten machen keinen Sinn. Sie können nirgendwo hin.", + "CALCULATION_HEADING": "Berechnung", + "CALCULATION_DESCRIPTION": "Mit den bereitgestellten Informationen, kommt dieses kleine Skript auf folgende Empfehlung:", + "NODES_HEADING": "Knoten", + "NODES_DESCRIPTION_1": "Du solltest", + "NODES_DESCRIPTION_2": "Knoten mit jeweils", + "NODES_DESCRIPTION_3": "Haupspeicher und", + "NODES_DESCRIPTION_4": "an präsistentem Speicher einsetzen.", + "SHARDS_HEADING": "Shards", + "SHARDS_DESCRIPTION_1": "Die Partitionen sollten jeweils in", + "SHARDS_DESCRIPTION_2": "Shards unterteilt werden.", + "HOUR": "Stunde", + "DAY": "Tag", + "WEEK": "Woche", + "MONTH": "Monat", + "YEAR": "Jahr" } } diff --git a/app/plugins/tutorial/static/i18n/en.json b/app/plugins/tutorial/static/i18n/en.json index 400133c4c..c78c90df6 100644 --- a/app/plugins/tutorial/static/i18n/en.json +++ b/app/plugins/tutorial/static/i18n/en.json @@ -4,8 +4,8 @@ }, "TUTORIAL": { "TITLE": "Get started with sample data", - "PARAGRAPH_1": "Sometimes it helps just to test with something real. The Twitter importer lets you quickly test your new CrateDB instance with real data. Press the button, and watch your cluster quickly fill up in real time with live Tweets. You can always delete them later.", - "PARAGRAPH_2": "After authentication Twitter allows you to consume a small fraction of their public stream, hence the slow import. In a production environment you can index hundreds of thousands of records per second.", + "PARAGRAPH_1": "Sometimes it helps to test with something real. The Twitter importer let's you quickly test your new CrateDB instance with real data. Press the button, and watch your cluster fill up in real time with live Tweets. You can always delete them later.", + "PARAGRAPH_2": "After the authentication, Twitter allows you to consume a small fraction of their public stream, hence the slow import. In a production environment you can import hundreds of thousands of records per second.", "INSTRUCTION_1": "Your imported tweets will be stored in a table called {tweets}.", "TWEETS": "tweets", "INSTRUCTION_2": "Click {tables} on the left to see the table and its stats. You should see its records increase as the tweets import.", @@ -16,5 +16,55 @@ "IMPORTING_TWEETS": "Importing tweets.", "TWEETS_IMPORTED": "tweets imported", "STOP_IMPORTING_TWEETS": "Enough! Stop importing tweets" + }, + "CALCULATOR": { + "TITLE": "Sharding calculator", + "DESCRIPTION": "A simple tool to calculate some generic sharding recommendation for a crate table.", + "IMPORT_HEADING": "Read data from existing table", + "HARDWARE_HEADING": "Hardware", + "CPUS_HEADING": "CPUs", + "CPUS_DESCRIPTION": "How many cores do the machines have you are going to run your nodes on?", + "STORAGE_HEADING": "Storage", + "STORAGE_DESCRIPTION": "You are using SSDs instead of HDDs? Good.", + "RAMSTORAGE_HEADING": "RAM/Storage", + "RAMSTORAGE_DESCRIPTION": "The proportion of RAM and storage generally influences the performance. A ratio around 1:24 is recommended, if you do not care for speed that much, you can go a little below that, if you do, you can go higher, but astronomical are not really improve performance anymore.", + "RAMSTORAGE_INPUT_1": "Every GiB of RAM serves", + "RAMSTORAGE_INPUT_2": "GiB of storage.", + "RAM_HEADING": "RAM", + "RAM_DESCRIPTION_1": "How much RAM do the machines have you are going to run your nodes on? It is good practice to have half of it assigned as heap to the JVM and the rest for the OS. 64GiB are a good guide value.", + "RAM_DESCRIPTION_2": "Byte", + "RAM_DESCRIPTION_3": "Having more than 32GiB of Heap for the JVM is not very beneficial, it will run into garbage collection issues.", + "RAID_HEADING": "RAID?", + "RAID_DESCRIPTION": "CrateDB is distributed and self healing, replicas of the data are distributed among the cluster, if they are enabled. This is probably something you want, because it makes something like RAID1 superfluous and even enables the use of RAID0 in a safe way.", + "USECASE_HEADING": "Use-Case", + "DATA_HEADING": "Data", + "DATA_DESCRIPTION_1": "So, this is what crate is about. You have probably been using something else before, so you should generally know how much data there is.", + "DATA_DESCRIPTION_2": "How much data is being inserted?", + "DATA_DESCRIPTION_3": "Byte per", + "DATA_DESCRIPTION_4": "And for how long is it supposed to be stored?", + "DATA_DESCRIPTION_5": "Since crate is horizontally scalable you can later on decide that the data is supposed to be stored for longer, by adding nodes.", + "DATA_DESCRIPTION_6": "OR, if it’s really not known yet, simply specify some expected table size.", + "DATA_DESCRIPTION_7": "Byte", + "PARTITION_HEADING": "Partitioning", + "PARTITION_DESCRIPTION_1": "Crate organizes data by itself into logical units, usually by time. If setting a good time frame for partitioning is set, can greatly enhance the performance.", + "PARTITION_DESCRIPTION_2": "Choose a batch size in which the data will be used later. If you have no idea, one month is a good suggestion.", + "PARTITION_DESCRIPTION_3": "You have expected table size selected, in this case you need to specify the amount of partitions manually.", + "REDUNDANCY_HEADING": "Redundancy", + "REDUNDANCY_DESCRIPTION": "As said crate distributes replicas of the data among its network, one replica should be affordable storage wise, for redundancy and data integrity. Having no replicas is of course easy on storage but not advisable. By having more replicas the query speed can be enhanced significantly, because one query then can run on several copies of the same data simultaneously. Sadly that is very storage intense and does affect the write speed a little. So one replica is the normal way to go if you are very into query speed you can go with 3-4. Aside from that, having more replicas than nodes never makes sense. They have nowhere to go.", + "CALCULATION_HEADING": "Calculation", + "CALCULATION_DESCRIPTION": "Based on the provided data, this little script came up with a recommendation.", + "NODES_HEADING": "Nodes", + "NODES_DESCRIPTION_1": "You should use", + "NODES_DESCRIPTION_2": "nodes with", + "NODES_DESCRIPTION_3": "of RAM and", + "NODES_DESCRIPTION_4": "of storage each.", + "SHARDS_HEADING": "Shards", + "SHARDS_DESCRIPTION_1": "The partitions should be divided into", + "SHARDS_DESCRIPTION_2": "shards each.", + "HOUR": "hour", + "DAY": "day", + "WEEK": "week", + "MONTH": "month", + "YEAR": "year" } } diff --git a/app/plugins/tutorial/static/icons/sharding-calculator-logo.png b/app/plugins/tutorial/static/icons/sharding-calculator-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..476aed457a38eca4b22969bb524f8eb3f0db8ff5 GIT binary patch literal 72011 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 literal 0 HcmV?d00001 diff --git a/app/plugins/tutorial/tutorial.html b/app/plugins/tutorial/tutorial.html old mode 100644 new mode 100755 index 4f994db9f..f0eaf2497 --- a/app/plugins/tutorial/tutorial.html +++ b/app/plugins/tutorial/tutorial.html @@ -77,6 +77,14 @@

{{:: 'HELP.ENTERPRISE_TITLE' | translate}}
{{:: 'HELP.ENTERPRISE_MSG' | translate }}
+ + +
+
+

{{:: 'HELP.CALCULATOR_TITLE' | translate}}

+
{{:: 'HELP.CALCULATOR_MSG' | translate}}
+
+
diff --git a/app/scripts/filter/numbers.js b/app/scripts/filter/numbers.js index 12fba473e..8a357a44c 100644 --- a/app/scripts/filter/numbers.js +++ b/app/scripts/filter/numbers.js @@ -27,7 +27,7 @@ const filters_numbers = angular.module('filters_numbers', []) }; }) .filter('bytes', function($sce) { - var units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; + var units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']; return function(bytes, precision) { if (isNaN(parseFloat(bytes)) || !isFinite(bytes)) { return '-'; diff --git a/app/static/i18n/de.json b/app/static/i18n/de.json index f4c5d24c4..4b2306e7e 100644 --- a/app/static/i18n/de.json +++ b/app/static/i18n/de.json @@ -142,6 +142,8 @@ "DOCUMENTATION_TITLE": "CrateDB Dokumentation", "DOCUMENTATION_MSG": "Die CrateDB Online Dokumentation.", "ENTERPRISE_TITLE": "Enterprise Support", - "ENTERPRISE_MSG": "Mehr über den Crate.io Enterprise Support." + "ENTERPRISE_MSG": "Mehr über den Crate.io Enterprise Support.", + "CALCULATOR_TITLE": "Shard Rechner", + "CALCULATOR_MSG": "Interaktives Werkzeug zur Berechnung, von Shard Größen." } } \ No newline at end of file diff --git a/app/static/i18n/en.json b/app/static/i18n/en.json index 878a76be9..36ad4d735 100644 --- a/app/static/i18n/en.json +++ b/app/static/i18n/en.json @@ -142,6 +142,8 @@ "DOCUMENTATION_TITLE": "CrateDB Documentation", "DOCUMENTATION_MSG": "Read the CrateDB documentation.", "ENTERPRISE_TITLE": "Enterprise Support", - "ENTERPRISE_MSG": "For enterprise support, please check our pricing page." + "ENTERPRISE_MSG": "For enterprise support, please check our pricing page.", + "CALCULATOR_TITLE": "Sharding Calculator", + "CALCULATOR_MSG": "Get some help on how to split your table into shards." } } \ No newline at end of file