From 9cfe5f639ff90d685e8f8cbd1d7556e2c2c52495 Mon Sep 17 00:00:00 2001 From: "Heeill(Floyd)Wang" Date: Fri, 10 Aug 2018 16:54:13 +0200 Subject: [PATCH 01/41] test calculator --- app/conf/plugins.json | 12 ++++++++++++ app/plugins/tutorial/calculator.html | 4 ++++ app/plugins/tutorial/calculator.js | 7 +++++++ app/plugins/tutorial/tutorial.html | 4 ++++ 4 files changed, 27 insertions(+) mode change 100644 => 100755 app/conf/plugins.json create mode 100644 app/plugins/tutorial/calculator.html create mode 100644 app/plugins/tutorial/calculator.js mode change 100644 => 100755 app/plugins/tutorial/tutorial.html diff --git a/app/conf/plugins.json b/app/conf/plugins.json old mode 100644 new mode 100755 index f53e5a6aa..208075ef1 --- a/app/conf/plugins.json +++ b/app/conf/plugins.json @@ -53,4 +53,16 @@ "controller": "ShardsController" } } +}, { + "name": "calculator", + "uri": "static/plugins/tutorial/calculator.js", + "enterprise": false, + "routing": { + "/calculator": { + "name": "calculator", + "url": "/calculator", + "templateUrl": "static/plugins/tutorial/calculator.html", + "controller": "CalculatorController" + } + } }] diff --git a/app/plugins/tutorial/calculator.html b/app/plugins/tutorial/calculator.html new file mode 100644 index 000000000..ce767f2a1 --- /dev/null +++ b/app/plugins/tutorial/calculator.html @@ -0,0 +1,4 @@ +
+ test calculator + {{test}} +
\ No newline at end of file diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js new file mode 100644 index 000000000..6be893744 --- /dev/null +++ b/app/plugins/tutorial/calculator.js @@ -0,0 +1,7 @@ +'use strict'; + +// storage is measured in Bytes +// time is measured in hours +angular.module('calculator', ['sql', 'translation']).controller('CalculatorController', function($scope) { + $scope.test = 'test string'; +}); \ No newline at end of file diff --git a/app/plugins/tutorial/tutorial.html b/app/plugins/tutorial/tutorial.html old mode 100644 new mode 100755 index 4f994db9f..0556cb748 --- a/app/plugins/tutorial/tutorial.html +++ b/app/plugins/tutorial/tutorial.html @@ -77,6 +77,10 @@

{{:: 'HELP.ENTERPRISE_TITLE' | translate}}
{{:: 'HELP.ENTERPRISE_MSG' | translate }}
+ + + calculator + From 598d112462030368e6e5dade826931eaeef6a435 Mon Sep 17 00:00:00 2001 From: "Heeill(Floyd)Wang" Date: Fri, 10 Aug 2018 17:01:52 +0200 Subject: [PATCH 02/41] add .html and .js --- app/plugins/tutorial/calculator.html | 181 ++++++++++++++++++++++++++- app/plugins/tutorial/calculator.js | 95 +++++++++++++- 2 files changed, 272 insertions(+), 4 deletions(-) diff --git a/app/plugins/tutorial/calculator.html b/app/plugins/tutorial/calculator.html index ce767f2a1..204ccd10d 100644 --- a/app/plugins/tutorial/calculator.html +++ b/app/plugins/tutorial/calculator.html @@ -1,4 +1,179 @@
- test calculator - {{test}} -
\ No newline at end of file +

+ + The ultimative guide on sharding +

+ + + +

Hardware

+ + +

CPUs

+

How many cores do the machines have you are going to run your +nodes on?

+
CPUCoresPerNode
+ +
+ + selected cores/node: {{ CPUCoresPerNode }} +
+ + + +

Storage

+

You are using SSDs instead of HDDs? Good.

+ + + +

RAM/Storage

+

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. +

+
RAMStorageProportion
+ +
+ + each GB of RAM serves {{RAMStorageProportion}} of storage +
+ + + +

RAM

+

Each node is expected to have 64GB of RAM, since this is the max without causing major garbage collection issues.

+ + + + +

RAID?

+

Crate.io 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. +

+ + + + + +

Use-Case

+

Data

+

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.

+

How much is being inserted?

+
dataInsertedPerTime

+ + +

+ + data insertion amount: {{dataInsertedPerTime}} + + + data insertion unit prefix: {{dataInsertedPerTimeUnitPrefix}} + + + data insertion time unit: {{dataInsertedPerTimeTemporalUnit}} + + + keep time: {{keepTime}} + + + keep time unit: {{keepTimeTemporalUnit}} + +How long is it supposed to be stored?

+
keepTime

+ +

+ +Since crate is horizontally scalable you can also later decide that +the data is supposed to be stored for longer, by adding nodes.

+

OR, if it’s really not known yet, just specify some expected +table size.

+
expectedTableSize

+ + +
+ + expected table size: {{expectedTableSize}} + + + expected table size unit prefix: {{expectedTableSizeUnitPrefix}} +
+ +Partitioning

+

Crate organizes data by itself into logical units, usually by +time. If a good time frame for partitioning is set, this can enhance +the performance greatly. Choose a batch size in which the data will +be used later. If you have no idea, a month is a good sugestion.

+
partionSize

+ +
+ + partitionSize: {{partitionSize}} + + + partitionSizeTemporalUnit: {{partitionSizeTemporalUnit}} +
+ +
+

You have expected table size selected, in this case you need to specify the amount of partitions manually.

+ + partitionCount: {{manualPartitionCount}} +
+ +Redundancy

+

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.

+
replicas
+ +
+ + replicas: {{replicas}} +
+ + + + + + +

Calculation

+

+ By the data provided, this little script recommends to split your table into: shards. +

+ + + + diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js index 6be893744..cdeafdcdd 100644 --- a/app/plugins/tutorial/calculator.js +++ b/app/plugins/tutorial/calculator.js @@ -3,5 +3,98 @@ // storage is measured in Bytes // time is measured in hours angular.module('calculator', ['sql', 'translation']).controller('CalculatorController', function($scope) { - $scope.test = 'test string'; + $scope.diskLoadFactor = 0.85; + $scope.maxRAMPerNode = 64000000000; //64G + $scope.sizeFactor = 0.732; //from haudi's document + $scope.maxShardSize = 20000000000; //20G + $scope.maxShards = 1000; + $scope.CPUCoresPerNode='2'; + $scope.RAMStorageProportion='24'; + $scope.dataType='perTime'; + $scope.dataInsertedPerTime='10'; + $scope.expectedTableSize='10'; + $scope.expectedTableSizeUnitPrefix='Giga'; + $scope.dataInsertedPerTimeUnitPrefix='Giga'; + $scope.dataInsertedPerTimeTemporalUnit='day'; + $scope.expectedTableSize='10'; + $scope.keepTimeTemporalUnit='month'; + $scope.keepTime='6'; + $scope.partitionSize='1'; + $scope.partitionSizeTemporalUnit='month'; + $scope.manualPartitionCount = 4; + $scope.replicas='1'; + $scope.neededDiskSpace = function() { + var res; + if ($scope.dataType === 'absolute') { + res = $scope.expectedTableSize * $scope.prefix($scope.expectedTableSizeUnitPrefix) / $scope.sizeFactor / $scope.diskLoadFactor; + } else if ($scope.dataType === 'perTime') { + res = (($scope.prefix($scope.dataInsertedPerTimeUnitPrefix) * $scope.dataInsertedPerTime / $scope.temporalUnit($scope.dataInsertedPerTimeTemporalUnit)) * $scope.keepTime * $scope.temporalUnit($scope.keepTimeTemporalUnit) * (1 + $scope.replicas)) / $scope.diskLoadFactor / $scope.sizeFactor; + } + console.log("neededDiskSpace() returning: " + res); + return res; + }; + $scope.neededNodes = function() { + var res = Math.ceil(($scope.neededDiskSpace() / $scope.RAMStorageProportion) / $scope.maxRAMPerNode); + console.log("neededNodes() returning: " + res); + return res; + }; + $scope.partitions = function() { + var r; + if($scope.dataType === 'perTime') { + r = (($scope.keepTime * $scope.temporalUnit($scope.keepTimeTemporalUnit)) / ($scope.partitionSize * $scope.temporalUnit($scope.partitionSizeTemporalUnit))); + }else { + r = $scope.manualPartitionCount; + } + console.log("partitionCount: " + r); + return r; + }; + $scope.shards = function() { + var res = Math.ceil(($scope.neededNodes() * $scope.CPUCoresPerNode) / $scope.partitions()); + console.log("shards() returning: " + res); + return res; + }; + $scope.shardSize = function(shards) { + var res = $scope.neededDiskSpace / ((($scope.keepTime * $scope.temporalUnit($scope.keepTimeTemporalUnit)) / $scope.partitions()) * shards * $scope.replicas); + }; + $scope.prefix = function(x) { + switch (x) { + case "Terra": + return Math.pow(10, 12); + case "Giga": + return Math.pow(10, 9); + case "Mega": + return Math.pow(10, 6); + case "Kilo": + return Math.pow(10, 3); + default: + return Math.pow(10, 0); + } + }; + $scope.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 356 * 24; + default: + return 1; + } + }; + $scope.result = function () { + var s = shards(); + if(s > $scope.maxShards){ + return "maximum shard limit exceeded, please talk to an crate engineer about your use-case"; + } + while ($scope.shardSize(s) > $scope.maxShardSize){ + s++; + } + return s; + }; + }); \ No newline at end of file From 64f8300099a5f60cf72077ecbaa16db7245bc5eb Mon Sep 17 00:00:00 2001 From: "Heeill(Floyd)Wang" Date: Fri, 10 Aug 2018 17:13:09 +0200 Subject: [PATCH 03/41] calculator.js : put function --- app/plugins/tutorial/calculator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js index cdeafdcdd..7ba7ec895 100644 --- a/app/plugins/tutorial/calculator.js +++ b/app/plugins/tutorial/calculator.js @@ -2,7 +2,7 @@ // storage is measured in Bytes // time is measured in hours -angular.module('calculator', ['sql', 'translation']).controller('CalculatorController', function($scope) { +angular.module('calculator', ['sql', 'translation']).controller('CalculatorController', function($scope, SQLQuery) { $scope.diskLoadFactor = 0.85; $scope.maxRAMPerNode = 64000000000; //64G $scope.sizeFactor = 0.732; //from haudi's document From d99493d8d98afa193ae7c2836052066e0ace7661 Mon Sep 17 00:00:00 2001 From: Seonghye Han Date: Fri, 10 Aug 2018 17:25:44 +0200 Subject: [PATCH 04/41] change design --- app/plugins/tutorial/calculator.html | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) mode change 100644 => 100755 app/plugins/tutorial/calculator.html diff --git a/app/plugins/tutorial/calculator.html b/app/plugins/tutorial/calculator.html old mode 100644 new mode 100755 index 204ccd10d..f38526364 --- a/app/plugins/tutorial/calculator.html +++ b/app/plugins/tutorial/calculator.html @@ -56,6 +56,9 @@

RAID?

Use-Case

+ + +

Data

So, this is what crate is about. You have probably been using something else before, so you should generally know how much data @@ -65,7 +68,7 @@

Data

- + data insertion amount: {{dataInsertedPerTime}} data insertion time unit: {{dataInsertedPerTimeTemporalUnit}} - + keep time: {{keepTime}}
- + expected table size: {{expectedTableSize}} + partitionSize: {{partitionSize}} + partitionCount: {{manualPartitionCount}}
-Redundancy

+

Redundancy

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 @@ -160,7 +168,7 @@

Data

replicas
- + replicas: {{replicas}}
From c32466e441bf57277797c9413628bb9993f36444 Mon Sep 17 00:00:00 2001 From: Seonghye Han Date: Mon, 13 Aug 2018 15:26:20 +0200 Subject: [PATCH 05/41] change shard calculator to newest version --- app/plugins/tutorial/calculator.html | 416 +++++++++++++++------------ app/plugins/tutorial/calculator.js | 85 +++--- 2 files changed, 274 insertions(+), 227 deletions(-) mode change 100644 => 100755 app/plugins/tutorial/calculator.js diff --git a/app/plugins/tutorial/calculator.html b/app/plugins/tutorial/calculator.html index f38526364..1f31cc9d5 100755 --- a/app/plugins/tutorial/calculator.html +++ b/app/plugins/tutorial/calculator.html @@ -1,187 +1,229 @@ -
-

- - The ultimative guide on sharding -

- - - -

Hardware

- - -

CPUs

-

How many cores do the machines have you are going to run your -nodes on?

-
CPUCoresPerNode
- -
- - selected cores/node: {{ CPUCoresPerNode }} -
- - - -

Storage

-

You are using SSDs instead of HDDs? Good.

- - - -

RAM/Storage

-

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. -

-
RAMStorageProportion
- -
- - each GB of RAM serves {{RAMStorageProportion}} of storage -
- - - -

RAM

-

Each node is expected to have 64GB of RAM, since this is the max without causing major garbage collection issues.

- - - - -

RAID?

-

Crate.io 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. -

- - - - - -

Use-Case

- - - -

Data

-

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.

-

How much is being inserted?

-
dataInsertedPerTime

- - -

- - data insertion amount: {{dataInsertedPerTime}} - - - data insertion unit prefix: {{dataInsertedPerTimeUnitPrefix}} - - - data insertion time unit: {{dataInsertedPerTimeTemporalUnit}} - - - keep time: {{keepTime}} - - - keep time unit: {{keepTimeTemporalUnit}} - -How long is it supposed to be stored?

-
keepTime

- -

- -Since crate is horizontally scalable you can also later decide that -the data is supposed to be stored for longer, by adding nodes.

-

OR, if it’s really not known yet, just specify some expected -table size.

-
expectedTableSize
- - -
- - expected table size: {{expectedTableSize}} - - - expected table size unit prefix: {{expectedTableSizeUnitPrefix}} -
- - - - - - -

Partitioning

-

Crate organizes data by itself into logical units, usually by -time. If a good time frame for partitioning is set, this can enhance -the performance greatly. Choose a batch size in which the data will -be used later. If you have no idea, a month is a good sugestion.

-
partionSize
- -
- - partitionSize: {{partitionSize}} - - - partitionSizeTemporalUnit: {{partitionSizeTemporalUnit}} -
- -
-

You have expected table size selected, in this case you need to specify the amount of partitions manually.

- - partitionCount: {{manualPartitionCount}} -
- -

Redundancy

-

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.

-
replicas
- -
- - replicas: {{replicas}} -
- - - - - - -

Calculation

-

- By the data provided, this little script recommends to split your table into: shards. -

- -
- - + + + + + Sharding calculator + + + + + + + +
+ +

+ Sharding calculator +

+

+ A simple tool to calculate some generic sharding recommendation for a crate table. +

+

+ Hardware +

+

+ CPUs +

+

+ How many cores do the machines have you are going to run your + nodes on? +

+ +
+ +
+ +

+ Storage +

+

+ You are using SSDs instead of HDDs? Good. +

+ +

+ RAM/Storage +

+

+ 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. +

+ +
+ + Every GB of RAM serves  + + + +  GB of storage. + +
+ +

+ RAM +

+

Each node is expected to have 64GB of RAM, since this is the max without causing major garbage collection + issues. +

+ +

+ RAID? +

+

+ Crate.io 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. +

+

+ Use-Case +

+

+ Data +

+

+ 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. +

+ +
+

+ + How much is being inserted? +

+ +
+ + + + + + +  Byte per  + + + + +

+ And for how long is it supposed to be stored? +

+ + + + + +
+
+ +

+ Since crate is horizontally scalable you can also later decide that + the data is supposed to be stored for longer, by adding nodes. +

+ +
+

+ + OR, if it’s really not known yet, just specify some expected + table size. +

+ +
+ + + + + + +  Byte + + +
+
+ +

+ Partitioning +

+

+ Crate organizes data by itself into logical units, usually by + time. If a good time frame for partitioning is set, this can enhance + the performance greatly. Choose a batch size in which the data will + be used later. If you have no idea, a month is a good suggestion. +

+ +
+
+ + + +
+ +
+

+ You have expected table size selected, in this case you need to specify + the amount of partitions manually. +

+ +
+
+ +

+ Redundancy +

+

+ 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 +

+ +

+ By the data provided, this little script recommends to split your table into: + + shards. +

+ +
+ + diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js old mode 100644 new mode 100755 index 7ba7ec895..b572b907a --- a/app/plugins/tutorial/calculator.js +++ b/app/plugins/tutorial/calculator.js @@ -3,60 +3,66 @@ // storage is measured in Bytes // time is measured in hours angular.module('calculator', ['sql', 'translation']).controller('CalculatorController', function($scope, SQLQuery) { + // storage is measured in Bytes + // time is measured in hours $scope.diskLoadFactor = 0.85; $scope.maxRAMPerNode = 64000000000; //64G $scope.sizeFactor = 0.732; //from haudi's document $scope.maxShardSize = 20000000000; //20G $scope.maxShards = 1000; - $scope.CPUCoresPerNode='2'; - $scope.RAMStorageProportion='24'; - $scope.dataType='perTime'; - $scope.dataInsertedPerTime='10'; - $scope.expectedTableSize='10'; - $scope.expectedTableSizeUnitPrefix='Giga'; - $scope.dataInsertedPerTimeUnitPrefix='Giga'; - $scope.dataInsertedPerTimeTemporalUnit='day'; - $scope.expectedTableSize='10'; - $scope.keepTimeTemporalUnit='month'; - $scope.keepTime='6'; - $scope.partitionSize='1'; - $scope.partitionSizeTemporalUnit='month'; + $scope.CPUCoresPerNode = '2'; + $scope.RAMStorageProportion = '24'; + $scope.dataType = 'perTime'; + $scope.dataInsertedPerTime = '100'; + $scope.expectedTableSize = '10'; + $scope.expectedTableSizeUnitPrefix = 'Terra'; + $scope.dataInsertedPerTimeUnitPrefix = 'Giga'; + $scope.dataInsertedPerTimeTemporalUnit = 'day'; + $scope.expectedTableSize = '10'; + $scope.keepTimeTemporalUnit = 'month'; + $scope.keepTime = '6'; + $scope.partitionSize = '1'; + $scope.partitionSizeTemporalUnit = 'month'; $scope.manualPartitionCount = 4; - $scope.replicas='1'; - $scope.neededDiskSpace = function() { - var res; + $scope.replicas = '1'; + $scope.neededDiskSpace = function () { + var res = 1; if ($scope.dataType === 'absolute') { - res = $scope.expectedTableSize * $scope.prefix($scope.expectedTableSizeUnitPrefix) / $scope.sizeFactor / $scope.diskLoadFactor; + res = ($scope.expectedTableSize * $scope.prefix($scope.expectedTableSizeUnitPrefix) * (1 + Number($scope.replicas))) / $scope.sizeFactor / $scope.diskLoadFactor; } else if ($scope.dataType === 'perTime') { - res = (($scope.prefix($scope.dataInsertedPerTimeUnitPrefix) * $scope.dataInsertedPerTime / $scope.temporalUnit($scope.dataInsertedPerTimeTemporalUnit)) * $scope.keepTime * $scope.temporalUnit($scope.keepTimeTemporalUnit) * (1 + $scope.replicas)) / $scope.diskLoadFactor / $scope.sizeFactor; + res = (($scope.prefix($scope.dataInsertedPerTimeUnitPrefix) * $scope.dataInsertedPerTime / $scope.temporalUnit($scope.dataInsertedPerTimeTemporalUnit)) * $scope.keepTime * $scope.temporalUnit($scope.keepTimeTemporalUnit) * (1 + Number($scope.replicas))) / $scope.diskLoadFactor / $scope.sizeFactor; //explicit cast of replica to number is necessary, otherwise 1+1=11. thanks java script } - console.log("neededDiskSpace() returning: " + res); + console.log("neededDiskSpace: " + Math.round(res / Math.pow(10, 9)) + "GB"); return res; }; - $scope.neededNodes = function() { + $scope.neededNodes = function () { var res = Math.ceil(($scope.neededDiskSpace() / $scope.RAMStorageProportion) / $scope.maxRAMPerNode); - console.log("neededNodes() returning: " + res); + console.log("neededNodes: " + res); return res; }; - $scope.partitions = function() { - var r; - if($scope.dataType === 'perTime') { - r = (($scope.keepTime * $scope.temporalUnit($scope.keepTimeTemporalUnit)) / ($scope.partitionSize * $scope.temporalUnit($scope.partitionSizeTemporalUnit))); - }else { - r = $scope.manualPartitionCount; + $scope.partitions = function () { + var res = 1; + if ($scope.dataType === 'perTime') { + res = (($scope.keepTime * $scope.temporalUnit($scope.keepTimeTemporalUnit)) / ($scope.partitionSize * $scope.temporalUnit($scope.partitionSizeTemporalUnit))); + } else if ($scope.dataType === 'perTime') { + res = $scope.manualPartitionCount; } - console.log("partitionCount: " + r); - return r; + console.log("partitionCount: " + res); + return res; }; - $scope.shards = function() { + $scope.shards = function () { var res = Math.ceil(($scope.neededNodes() * $scope.CPUCoresPerNode) / $scope.partitions()); - console.log("shards() returning: " + res); + console.log("shards(): " + res); return res; }; - $scope.shardSize = function(shards) { - var res = $scope.neededDiskSpace / ((($scope.keepTime * $scope.temporalUnit($scope.keepTimeTemporalUnit)) / $scope.partitions()) * shards * $scope.replicas); + $scope.shardSize = function (shards) { + console.log("replicas: " + (1 + Number($scope.replicas))); + + var res = $scope.neededDiskSpace() / (shards * $scope.partitions() * (1 + Number($scope.replicas))); + console.log("shardSize: " + res); + return res; }; - $scope.prefix = function(x) { + $scope.prefix = function (x) { switch (x) { case "Terra": return Math.pow(10, 12); @@ -70,7 +76,7 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr return Math.pow(10, 0); } }; - $scope.temporalUnit = function(x) { + $scope.temporalUnit = function (x) { switch (x) { case "hour": return 1; @@ -81,20 +87,19 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr case "month": return 30 * 24; case "year": - return 356 * 24; + return 365 * 24; default: return 1; } }; $scope.result = function () { - var s = shards(); - if(s > $scope.maxShards){ + var s = $scope.shards(); + if (s > $scope.maxShards) { return "maximum shard limit exceeded, please talk to an crate engineer about your use-case"; } - while ($scope.shardSize(s) > $scope.maxShardSize){ + while ($scope.shardSize(s) > $scope.maxShardSize) { s++; } return s; }; - }); \ No newline at end of file From b458bf1f496404fd781262b12bb09d029f70df96 Mon Sep 17 00:00:00 2001 From: Seonghye Han Date: Mon, 13 Aug 2018 17:00:37 +0200 Subject: [PATCH 06/41] correct error in shard calculator --- app/plugins/tutorial/calculator.html | 32 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/app/plugins/tutorial/calculator.html b/app/plugins/tutorial/calculator.html index 1f31cc9d5..3c95e087e 100755 --- a/app/plugins/tutorial/calculator.html +++ b/app/plugins/tutorial/calculator.html @@ -10,7 +10,7 @@ -
+

Sharding calculator @@ -30,7 +30,7 @@

- +

@@ -55,7 +55,7 @@

Every GB of RAM serves  - +  GB of storage. @@ -91,15 +91,15 @@

- + How much is being inserted?

- + -

diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js index b572b907a..6a1eabc22 100755 --- a/app/plugins/tutorial/calculator.js +++ b/app/plugins/tutorial/calculator.js @@ -62,6 +62,31 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr console.log("shardSize: " + res); return res; }; + $scope.ramString = function () { + var res = $scope.RAMStorageProportion + "GB"; + console.log("ramString: " + res); + return res; + }; + $scope.storageString = function () { + return $scope.bytesToPrintableString(Math.round(Number($scope.neededDiskSpace()) / Number($scope.neededNodes()))); + }; + $scope.bytesToPrintableString = function (b) { + console.log("entering bytify..."); + var strg; + if (b < Math.pow(10, 3)) { + strg = b + "B"; + } else if (b < Math.pow(10, 6)){ + strg = (b / Math.pow(10, 3)).toFixed(2) + "KB"; + } else if (b < Math.pow(10, 9)){ + strg = (b / Math.pow(10, 6)).toFixed(2) + "MB"; + } else if (b < Math.pow(10, 12)) { + strg = (b / Math.pow(10, 9)).toFixed(2) + "GB"; + } else { + strg = (b / Math.pow(10, 12)).toFixed(2) + "TB" + } + console.log("bytify: " + strg); + return strg; + }; $scope.prefix = function (x) { switch (x) { case "Terra": From e10037c18d1b6fad31b20e5ae81bc10706673379 Mon Sep 17 00:00:00 2001 From: Seonghye Han Date: Thu, 16 Aug 2018 00:28:23 +0200 Subject: [PATCH 09/41] select schema, table from database and get it's name as string in JavaScript /testing --- app/conf/plugins.json | 12 +++++++++ app/plugins/tutorial/testing.html | 21 ++++++++++++++++ app/plugins/tutorial/testing.js | 42 +++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 app/plugins/tutorial/testing.html create mode 100644 app/plugins/tutorial/testing.js diff --git a/app/conf/plugins.json b/app/conf/plugins.json index 208075ef1..992c79024 100755 --- a/app/conf/plugins.json +++ b/app/conf/plugins.json @@ -65,4 +65,16 @@ "controller": "CalculatorController" } } +}, { + "name": "testing", + "uri": "static/plugins/tutorial/testing.js", + "enterprise": false, + "routing": { + "/testing": { + "name": "testing", + "url": "/testing", + "templateUrl": "static/plugins/tutorial/testing.html", + "controller": "testingController" + } + } }] diff --git a/app/plugins/tutorial/testing.html b/app/plugins/tutorial/testing.html new file mode 100644 index 000000000..9980fefae --- /dev/null +++ b/app/plugins/tutorial/testing.html @@ -0,0 +1,21 @@ +
+

choose your table name

+ + + + SCHEMA: + + + +
+ + TABLE: + + +
+
+ + {{selectedSchema}} {{selectedTable}} +
\ No newline at end of file diff --git a/app/plugins/tutorial/testing.js b/app/plugins/tutorial/testing.js new file mode 100644 index 000000000..aef414dab --- /dev/null +++ b/app/plugins/tutorial/testing.js @@ -0,0 +1,42 @@ +'use strict'; + +angular.module('testing', ['sql', 'translation']).controller('testingController', function($scope, $window, SQLQuery, queryResultToObjects) { + + $scope.selectedSchema = ""; + $scope.selectedTable = ""; + var flag1 = "0"; + $scope.getschema = function() { + if (flag1=="0"){ + var stmt = 'select schema_name from information_schema.schemata order by schema_name;' + var cols = ['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.schemaList = obj; + flag1="1"; + } + }; + var flag2 = "0"; + $scope.getTablename = function(num1) { + if (flag2=="0" || num1 =="1"){ + var stmt = "SELECT table_name FROM information_schema.tables WHERE table_schema ='"+$scope.selectedSchema+"';"; + var cols = ['table_name']; + var obj = []; + SQLQuery.execute(stmt, {}, false, false, false, false) + .then(function (query) { + $scope.sqlresult2 = queryResultToObjects(query, cols); + }) + for(var i=0; i<$scope.sqlresult2.length; i++) { + obj.push($scope.sqlresult2[i].table_name); + } + $scope.tableList = obj; + flag2="1"; + } + }; + +}); \ No newline at end of file From 177e25c0e45d6877e49b1179e3da1b190acdcd7d Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 16 Aug 2018 15:17:49 +0200 Subject: [PATCH 10/41] implement querying of host cpu cores --- app/plugins/tutorial/calculator.html | 17 ++++++++++++++--- app/plugins/tutorial/calculator.js | 22 +++++++++++++++++----- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/app/plugins/tutorial/calculator.html b/app/plugins/tutorial/calculator.html index dc99b2d69..57cda178b 100755 --- a/app/plugins/tutorial/calculator.html +++ b/app/plugins/tutorial/calculator.html @@ -15,9 +15,20 @@

Sharding calculator

-

- A simple tool to calculate some generic sharding recommendation for a crate table. -

+ +
+
+

+ A simple tool to calculate some generic sharding recommendation for a crate table. +

+
+
+

+ read from existing table +

+ +
+

Hardware

diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js index 6a1eabc22..d03f8fab6 100755 --- a/app/plugins/tutorial/calculator.js +++ b/app/plugins/tutorial/calculator.js @@ -2,9 +2,8 @@ // storage is measured in Bytes // time is measured in hours -angular.module('calculator', ['sql', 'translation']).controller('CalculatorController', function($scope, SQLQuery) { - // storage is measured in Bytes - // time is measured in hours + +angular.module('calculator', ['sql', 'translation']).controller('CalculatorController', function($scope, SQLQuery, queryResultToObjects) { $scope.diskLoadFactor = 0.85; $scope.maxRAMPerNode = 64000000000; //64G $scope.sizeFactor = 0.732; //from haudi's document @@ -13,7 +12,7 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr $scope.CPUCoresPerNode = '2'; $scope.RAMStorageProportion = '24'; $scope.dataType = 'perTime'; - $scope.dataInsertedPerTime = '100'; + $scope.dataInsertedPerTime = '20'; $scope.expectedTableSize = '10'; $scope.expectedTableSizeUnitPrefix = 'Terra'; $scope.dataInsertedPerTimeUnitPrefix = 'Giga'; @@ -25,6 +24,8 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr $scope.partitionSizeTemporalUnit = 'month'; $scope.manualPartitionCount = 4; $scope.replicas = '1'; + $scope.tables = ["table1", "table2"]; + $scope.selectTable = "none"; $scope.neededDiskSpace = function () { var res = 1; if ($scope.dataType === 'absolute') { @@ -71,7 +72,6 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr return $scope.bytesToPrintableString(Math.round(Number($scope.neededDiskSpace()) / Number($scope.neededNodes()))); }; $scope.bytesToPrintableString = function (b) { - console.log("entering bytify..."); var strg; if (b < Math.pow(10, 3)) { strg = b + "B"; @@ -127,4 +127,16 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr } return s; }; + $scope.tableSelected = function () { + console.log("selected table: " + $scope.selectTable); + $scope.loadData($scope.selectTable); + }; + $scope.loadData = function (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 + }); + console.log("queried cpu cores per node: " + $scope.CPUCoresPerNode); + }; }); \ No newline at end of file From 6ced847026f97debffb6fc1407adcea0883549d7 Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 16 Aug 2018 16:10:47 +0200 Subject: [PATCH 11/41] change iterative shardsize calculation to deterministic --- app/plugins/tutorial/calculator.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js index d03f8fab6..f4826ed50 100755 --- a/app/plugins/tutorial/calculator.js +++ b/app/plugins/tutorial/calculator.js @@ -58,7 +58,6 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr }; $scope.shardSize = function (shards) { console.log("replicas: " + (1 + Number($scope.replicas))); - var res = $scope.neededDiskSpace() / (shards * $scope.partitions() * (1 + Number($scope.replicas))); console.log("shardSize: " + res); return res; @@ -119,12 +118,12 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr }; $scope.result = function () { var s = $scope.shards(); + if ($scope.shardSize(s) > $scope.maxShardSize) { + s = Math.ceil(s * ($scope.shardSize(s) / $scope.maxShardSize)); + } if (s > $scope.maxShards) { return "maximum shard limit exceeded, please talk to an crate engineer about your use-case"; } - while ($scope.shardSize(s) > $scope.maxShardSize) { - s++; - } return s; }; $scope.tableSelected = function () { From 528a0523a2e28471edcf25a16ac975ee913b2afd Mon Sep 17 00:00:00 2001 From: Seonghye Han Date: Fri, 17 Aug 2018 10:34:15 +0200 Subject: [PATCH 12/41] read real table name --- app/plugins/tutorial/calculator.html | 6 +++--- app/plugins/tutorial/calculator.js | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/app/plugins/tutorial/calculator.html b/app/plugins/tutorial/calculator.html index 57cda178b..bcf1caccb 100755 --- a/app/plugins/tutorial/calculator.html +++ b/app/plugins/tutorial/calculator.html @@ -10,7 +10,7 @@ -
+

Sharding calculator @@ -26,7 +26,7 @@

read from existing table

- +

@@ -257,6 +257,6 @@

-

+
diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js index f4826ed50..565a621ba 100755 --- a/app/plugins/tutorial/calculator.js +++ b/app/plugins/tutorial/calculator.js @@ -26,6 +26,8 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr $scope.replicas = '1'; $scope.tables = ["table1", "table2"]; $scope.selectTable = "none"; + + $scope.neededDiskSpace = function () { var res = 1; if ($scope.dataType === 'absolute') { @@ -126,6 +128,25 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr } return s; }; + + + $scope.gettablename = function() { + //console.log("gettablename1: " + $scope.tableList[0]); + 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 = []; + //console.log("gettablename2: " + $scope.tableList[0]); + 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].table_name, $scope.sqlresult[i].schema_name]); + } + $scope.tables = obj; + }); + + }; + + $scope.tableSelected = function () { console.log("selected table: " + $scope.selectTable); $scope.loadData($scope.selectTable); From bcf4e4856e940e499b5d28bbbe41da3fd64a2b6e Mon Sep 17 00:00:00 2001 From: Seonghye Han Date: Fri, 17 Aug 2018 11:02:53 +0200 Subject: [PATCH 13/41] read real table name --- app/plugins/tutorial/calculator.html | 5 +++-- app/plugins/tutorial/calculator.js | 13 +++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/plugins/tutorial/calculator.html b/app/plugins/tutorial/calculator.html index bcf1caccb..23aa29952 100755 --- a/app/plugins/tutorial/calculator.html +++ b/app/plugins/tutorial/calculator.html @@ -24,9 +24,10 @@

- read from existing table + read from existing table (schema/table)

- +

diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js index 565a621ba..f811b100b 100755 --- a/app/plugins/tutorial/calculator.js +++ b/app/plugins/tutorial/calculator.js @@ -25,7 +25,9 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr $scope.manualPartitionCount = 4; $scope.replicas = '1'; $scope.tables = ["table1", "table2"]; + $scope.selectSchema = "none"; $scope.selectTable = "none"; + $scope.selected = "none"; $scope.neededDiskSpace = function () { @@ -139,7 +141,7 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr 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].table_name, $scope.sqlresult[i].schema_name]); + obj.push([$scope.sqlresult[i].schema_name, $scope.sqlresult[i].table_name]); } $scope.tables = obj; }); @@ -148,10 +150,13 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr $scope.tableSelected = function () { - console.log("selected table: " + $scope.selectTable); - $scope.loadData($scope.selectTable); + //console.log("selected table: " + $scope.selectSchema+" "+$scope.selectTable); + $scope.selectSchema = $scope.selected[0]; + $scope.selectTable = $scope.selected[1]; + console.log("selected table: " + $scope.selectSchema+" "+$scope.selectTable); + $scope.loadData($scope.selectSchema, $scope.selectTable); }; - $scope.loadData = function (tableName) { + $scope.loadData = 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) { From 212af05292ade7fcadbbf997b49049a1508b40bf Mon Sep 17 00:00:00 2001 From: Seonghye Han Date: Fri, 17 Aug 2018 14:53:24 +0200 Subject: [PATCH 14/41] exclude ram/storage --- app/plugins/tutorial/calculator.js | 68 ++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js index f811b100b..2735e7d5c 100755 --- a/app/plugins/tutorial/calculator.js +++ b/app/plugins/tutorial/calculator.js @@ -9,21 +9,21 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr $scope.sizeFactor = 0.732; //from haudi's document $scope.maxShardSize = 20000000000; //20G $scope.maxShards = 1000; - $scope.CPUCoresPerNode = '2'; - $scope.RAMStorageProportion = '24'; + $scope.CPUCoresPerNode = 2; + $scope.RAMStorageProportion = 24; $scope.dataType = 'perTime'; - $scope.dataInsertedPerTime = '20'; - $scope.expectedTableSize = '10'; + $scope.dataInsertedPerTime = 20; + $scope.expectedTableSize = 10; $scope.expectedTableSizeUnitPrefix = 'Terra'; $scope.dataInsertedPerTimeUnitPrefix = 'Giga'; $scope.dataInsertedPerTimeTemporalUnit = 'day'; - $scope.expectedTableSize = '10'; + $scope.expectedTableSize = 10; $scope.keepTimeTemporalUnit = 'month'; - $scope.keepTime = '6'; - $scope.partitionSize = '1'; + $scope.keepTime = 6; + $scope.partitionSize = 1; $scope.partitionSizeTemporalUnit = 'month'; $scope.manualPartitionCount = 4; - $scope.replicas = '1'; + $scope.replicas = 1; $scope.tables = ["table1", "table2"]; $scope.selectSchema = "none"; $scope.selectTable = "none"; @@ -148,13 +148,15 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr }; - $scope.tableSelected = function () { //console.log("selected table: " + $scope.selectSchema+" "+$scope.selectTable); $scope.selectSchema = $scope.selected[0]; $scope.selectTable = $scope.selected[1]; console.log("selected table: " + $scope.selectSchema+" "+$scope.selectTable); $scope.loadData($scope.selectSchema, $scope.selectTable); + $scope.loadTablesize($scope.selectSchema, $scope.selectTable); + $scope.loadPartition($scope.selectSchema, $scope.selectTable); + $scope.loadReplica($scope.selectSchema, $scope.selectTable); }; $scope.loadData = function (schemaName, tableName) { var stmt = "SELECT os_info['available_processors']\n" + @@ -164,4 +166,52 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr }); console.log("queried cpu cores per node: " + $scope.CPUCoresPerNode); }; + + $scope.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) { + $scope.expectedTableSize = (query.rows[0])[0]; + $scope.expectedTableSizeUnitPrefix = 'none'; + $scope.dataType = 'absolute'; + }); + }; + + $scope.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 (table_schema = '" + +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; + } + }); + }; + + $scope.loadReplica = function(schemaName, tableName) { + 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) { + $scope.replicas = (query.rows[0])[0]; + if(angular.isNumber($scope.replicas)==false){ + $scope.replicas = $scope.replicas.split("-")[1]; + stmt = "SELECT COUNT(*) FROM sys.nodes"; + SQLQuery.execute(stmt, {}, false, false, false, false).then(function (query) { + if ($scope.replicas =="all"){ + $scope.replicas = (query.rows[0])[0]-1; + } + else{ + $scope.replicas = Math.min(Number($scope.replicas),(query.rows[0])[0]-1); + } + }); + } + }); + }; + }); \ No newline at end of file From b762df31894f9f7c42db3ca5c2d4c97d8cc43778 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 20 Aug 2018 11:55:11 +0200 Subject: [PATCH 15/41] implement fetching of RAMStoragePropotion --- app/plugins/tutorial/calculator.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js index 2735e7d5c..7ea666e4d 100755 --- a/app/plugins/tutorial/calculator.js +++ b/app/plugins/tutorial/calculator.js @@ -157,6 +157,7 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr $scope.loadTablesize($scope.selectSchema, $scope.selectTable); $scope.loadPartition($scope.selectSchema, $scope.selectTable); $scope.loadReplica($scope.selectSchema, $scope.selectTable); + $scope.loadRAMStoragePropotion(); }; $scope.loadData = function (schemaName, tableName) { var stmt = "SELECT os_info['available_processors']\n" + @@ -214,4 +215,15 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr }); }; + $scope.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; + }); + }; }); \ No newline at end of file From 252adc39a0ba3931de87bafdc89c3ef85ff3821a Mon Sep 17 00:00:00 2001 From: Seonghye Han Date: Mon, 20 Aug 2018 14:20:00 +0200 Subject: [PATCH 16/41] fix some bugs --- app/plugins/tutorial/calculator.html | 2 +- app/plugins/tutorial/calculator.js | 36 ++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/app/plugins/tutorial/calculator.html b/app/plugins/tutorial/calculator.html index 23aa29952..edb5ab742 100755 --- a/app/plugins/tutorial/calculator.html +++ b/app/plugins/tutorial/calculator.html @@ -67,7 +67,7 @@

Every GB of RAM serves  - +  GB of storage. diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js index 7ea666e4d..6d6249489 100755 --- a/app/plugins/tutorial/calculator.js +++ b/app/plugins/tutorial/calculator.js @@ -72,7 +72,12 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr return res; }; $scope.storageString = function () { - return $scope.bytesToPrintableString(Math.round(Number($scope.neededDiskSpace()) / Number($scope.neededNodes()))); + if ($scope.neededNodes()!=0){ + return $scope.bytesToPrintableString(Math.round(Number($scope.neededDiskSpace()) / Number($scope.neededNodes()))); + } + else{ + return 0; + } }; $scope.bytesToPrintableString = function (b) { var strg; @@ -153,13 +158,13 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr $scope.selectSchema = $scope.selected[0]; $scope.selectTable = $scope.selected[1]; console.log("selected table: " + $scope.selectSchema+" "+$scope.selectTable); - $scope.loadData($scope.selectSchema, $scope.selectTable); + $scope.loadCPUCores($scope.selectSchema, $scope.selectTable); $scope.loadTablesize($scope.selectSchema, $scope.selectTable); $scope.loadPartition($scope.selectSchema, $scope.selectTable); $scope.loadReplica($scope.selectSchema, $scope.selectTable); $scope.loadRAMStoragePropotion(); }; - $scope.loadData = function (schemaName, tableName) { + $scope.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) { @@ -173,6 +178,9 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr +"'and table_name = '"+tableName+"' and primary=true;"; SQLQuery.execute(stmt, {}, false, false, false, false).then(function (query) { $scope.expectedTableSize = (query.rows[0])[0]; + if ((query.rows[0])[0]==null){ + $scope.expectedTableSize = 0; + } $scope.expectedTableSizeUnitPrefix = 'none'; $scope.dataType = 'absolute'; }); @@ -183,8 +191,8 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr +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 (table_schema = '" - +schemaName+"' and table_name = '"+tableName+"')) AS x;" + 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]; }); @@ -196,22 +204,30 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr }; $scope.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) { - $scope.replicas = (query.rows[0])[0]; - if(angular.isNumber($scope.replicas)==false){ - $scope.replicas = $scope.replicas.split("-")[1]; + rep = (query.rows[0])[0]; + console.log("^^^^^^^^^^^^^^^^^ "+ $scope.replicas); + if(rep.includes("-") == true){ + rep = rep.split("-")[1]; + console.log("^^^^^^^^^^^^^^^^^ "+ $scope.replicas); stmt = "SELECT COUNT(*) FROM sys.nodes"; SQLQuery.execute(stmt, {}, false, false, false, false).then(function (query) { - if ($scope.replicas =="all"){ + console.log("^^^^^^^^^^^^^^^^^ "+ (query.rows[0])[0]); + if (rep =="all"){ + console.log("^^^^^^^^^^^^^^^^^ in all "+ (query.rows[0])[0]); $scope.replicas = (query.rows[0])[0]-1; } else{ - $scope.replicas = Math.min(Number($scope.replicas),(query.rows[0])[0]-1); + $scope.replicas = Math.min(Number(rep),(query.rows[0])[0]-1); } }); } + else{ + $scope.replicas = Number(rep); + } }); }; From cfbf23243b112264ba79baa97f9122b828f96c93 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 20 Aug 2018 15:21:41 +0200 Subject: [PATCH 17/41] add proper link to calculator on help page --- .../static/icons/sharding-calculator-logo.png | Bin 0 -> 72011 bytes app/plugins/tutorial/tutorial.html | 6 +++++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 app/plugins/tutorial/static/icons/sharding-calculator-logo.png 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 index 0556cb748..f0eaf2497 100755 --- a/app/plugins/tutorial/tutorial.html +++ b/app/plugins/tutorial/tutorial.html @@ -79,7 +79,11 @@

{{:: 'HELP.ENTERPRISE_TITLE' | translate}} - calculator +
+
+

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

+
{{:: 'HELP.CALCULATOR_MSG' | translate}}
+
From 0aee585e7c2df8d6f5609a39238dc80eaa719fd0 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 20 Aug 2018 15:22:57 +0200 Subject: [PATCH 18/41] add translation support --- app/plugins/tutorial/calculator.html | 137 +++++++++++---------------- app/static/i18n/en.json | 52 +++++++++- 2 files changed, 107 insertions(+), 82 deletions(-) diff --git a/app/plugins/tutorial/calculator.html b/app/plugins/tutorial/calculator.html index 23aa29952..643f2d440 100755 --- a/app/plugins/tutorial/calculator.html +++ b/app/plugins/tutorial/calculator.html @@ -2,7 +2,7 @@ - Sharding calculator + {{:: 'CALCULATOR.TITLE' | translate}} @@ -13,32 +13,31 @@

- Sharding calculator + {{:: 'CALCULATOR.TITLE' | translate}}

- A simple tool to calculate some generic sharding recommendation for a crate table. + {{:: 'CALCULATOR.DESCRIPTION' | translate}}

- read from existing table (schema/table) + {{:: 'CALCULATOR.IMPORT_HEADING' | translate}}

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

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

- How many cores do the machines have you are going to run your - nodes on? + {{:: 'CALCULATOR.CPUS_DESCRIPTION' | translate}}

@@ -46,65 +45,55 @@

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

- You are using SSDs instead of HDDs? Good. + {{:: 'CALCULATOR.STORAGE_DESCRIPTION' | translate}}

- RAM/Storage + {{:: 'CALCULATOR.RAMSTORAGE_HEADING' | translate}}

- 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. + {{:: 'CALCULATOR.RAMSTORAGE_DESCRIPTION' | translate}}

- Every GB of RAM serves  + {{:: 'CALCULATOR.RAMSTORAGE_INPUT_1' | translate}} -  GB of storage. + {{:: 'CALCULATOR.RAMSTORAGE_INPUT_2' | translate}}

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

-

Each node is expected to have 64GB of RAM, since this is the max without causing major garbage collection - issues. +

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

- RAID? + {{:: 'CALCULATOR.RAID_HEADING' | translate}}

- Crate.io 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. + {{:: 'CALCULATOR.RAID_DESCRIPTION' | translate}}

- Use-Case + {{:: 'CALCULATOR.USECASE_HEADING' | translate}}

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

- 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. + {{:: 'CALCULATOR.DATA_DESCRIPTION_1' | translate}}

- How much is being inserted? + {{:: 'CALCULATOR.DATA_DESCRIPTION_2' | translate}}

@@ -118,43 +107,41 @@

-  Byte per  + {{:: 'CALCULATOR.DATA_DESCRIPTION_3' | translate}}

- And for how long is it supposed to be stored? + {{:: 'CALCULATOR.DATA_DESCRIPTION_4' | translate}}

- Since crate is horizontally scalable you can also later decide that - the data is supposed to be stored for longer, by adding nodes. + {{:: 'CALCULATOR.DATA_DESCRIPTION_5' | translate}}

- OR, if it’s really not known yet, just specify some expected - table size. + {{:: 'CALCULATOR.DATA_DESCRIPTION_6' | translate}}

@@ -168,60 +155,48 @@

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

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

- Crate organizes data by itself into logical units, usually by - time. If a good time frame for partitioning is set, this can enhance - the performance greatly. + {{:: 'CALCULATOR.PARTITION_DESCRIPTION_1' | translate}}

- Choose a batch size in which the data will be used later. If you have no idea, a month is a good suggestion.
+ {{:: 'CALCULATOR.PARTITION_DESCRIPTION_2' | translate}}

- You have expected table size selected, in this case you need to specify - the amount of partitions manually
+ {{:: 'CALCULATOR.PARTITION_DESCRIPTION_3' | translate}}
partitions.

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

- 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. + {{:: 'CALCULATOR.REDUNDANCY_DESCRIPTION' | translate}}

@@ -229,32 +204,32 @@

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

- With the data provided this little script came up with a recommendation. + {{:: 'CALCULATOR.CALCULATION_DESCRIPTION' | translate}}

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

- You should use + {{:: 'CALCULATOR.NODES_DESCRIPTION_1' | translate}} - nodes with + {{:: 'CALCULATOR.NODES_DESCRIPTION_2' | translate}} - of RAM and + {{:: 'CALCULATOR.NODES_DESCRIPTION_3' | translate}} - of storage each. + {{:: 'CALCULATOR.NODES_DESCRIPTION_4' | translate}}

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

- The partitions should be divided into + {{:: 'CALCULATOR.SHARDS_DESCRIPTION_1' | translate}} - shards each. + {{:: 'CALCULATOR.SHARDS_DESCRIPTION_2' | translate}}

diff --git a/app/static/i18n/en.json b/app/static/i18n/en.json index 8fe47465d..729a2d8e5 100644 --- a/app/static/i18n/en.json +++ b/app/static/i18n/en.json @@ -142,6 +142,56 @@ "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." + }, + "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 GB of RAM serves", + "RAMSTORAGE_INPUT_2": "GB of storage.", + "RAM_HEADING": "RAM", + "RAM_DESCRIPTION": "Each node is expected to have 64GB of RAM, since this is the max without causing major 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 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 also later 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, just 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 a good time frame for partitioning is set, this can enhance the performance greatly.", + "PARTITION_DESCRIPTION_2": "Choose a batch size in which the data will be used later. If you have no idea, a 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": "With the data provided 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" } } From 8f6befd84a3f5d8fde5b80b75813d97984dae329 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 21 Aug 2018 10:00:16 +0200 Subject: [PATCH 19/41] move translation to tutorial/static/i18n --- app/plugins/tutorial/static/i18n/en.json | 48 ++++++++++++++++++++++++ app/static/i18n/en.json | 48 ------------------------ 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/app/plugins/tutorial/static/i18n/en.json b/app/plugins/tutorial/static/i18n/en.json index 400133c4c..007bd36b6 100644 --- a/app/plugins/tutorial/static/i18n/en.json +++ b/app/plugins/tutorial/static/i18n/en.json @@ -16,5 +16,53 @@ "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 GB of RAM serves", + "RAMSTORAGE_INPUT_2": "GB of storage.", + "RAM_HEADING": "RAM", + "RAM_DESCRIPTION": "Each node is expected to have 64GB of RAM, since this is the max without causing major 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 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 also later 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, just 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 a good time frame for partitioning is set, this can enhance the performance greatly.", + "PARTITION_DESCRIPTION_2": "Choose a batch size in which the data will be used later. If you have no idea, a 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": "With the data provided 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/static/i18n/en.json b/app/static/i18n/en.json index 729a2d8e5..e8d47d4e6 100644 --- a/app/static/i18n/en.json +++ b/app/static/i18n/en.json @@ -145,53 +145,5 @@ "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." - }, - "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 GB of RAM serves", - "RAMSTORAGE_INPUT_2": "GB of storage.", - "RAM_HEADING": "RAM", - "RAM_DESCRIPTION": "Each node is expected to have 64GB of RAM, since this is the max without causing major 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 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 also later 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, just 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 a good time frame for partitioning is set, this can enhance the performance greatly.", - "PARTITION_DESCRIPTION_2": "Choose a batch size in which the data will be used later. If you have no idea, a 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": "With the data provided 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" } } From bdd9d46c8dcb9d9f5e973b4a19878380b0bb1c87 Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 21 Aug 2018 16:22:12 +0200 Subject: [PATCH 20/41] implement RAM as input --- app/plugins/tutorial/calculator.html | 25 ++++++- app/plugins/tutorial/calculator.js | 84 +++++++++++++++++++----- app/plugins/tutorial/static/i18n/en.json | 3 +- 3 files changed, 95 insertions(+), 17 deletions(-) diff --git a/app/plugins/tutorial/calculator.html b/app/plugins/tutorial/calculator.html index 7233354fd..487e2ef5b 100755 --- a/app/plugins/tutorial/calculator.html +++ b/app/plugins/tutorial/calculator.html @@ -71,8 +71,27 @@

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

-

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

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

+
+ + + + + +

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

+ +

{{:: 'CALCULATOR.RAID_HEADING' | translate}} @@ -101,6 +120,8 @@

-

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

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

@@ -210,7 +214,7 @@

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

diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js index 7d4f0f553..5e8dc626d 100755 --- a/app/plugins/tutorial/calculator.js +++ b/app/plugins/tutorial/calculator.js @@ -205,7 +205,7 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr 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;" + +schemaName+"' and table_name = '"+tableName+"') as x;"; SQLQuery.execute(stmt, {}, false, false, false, false).then(function (query) { $scope.manualPartitionCount = (query.rows[0])[0]; }); diff --git a/app/plugins/tutorial/static/i18n/de.json b/app/plugins/tutorial/static/i18n/de.json index 311d6a7df..540918217 100644 --- a/app/plugins/tutorial/static/i18n/de.json +++ b/app/plugins/tutorial/static/i18n/de.json @@ -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 GB RAM dient", + "RAMSTORAGE_INPUT_2": "GB 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. 64GB an Haupspeicher sind ein guter Richtwert.", + "RAM_DESCRIPTION_2": "Byte", + "RAM_DESCRIPTION_3": "Wenn mehr als 32GB der JVM als Heap zugewiesen werden, ist es gut möglich das sie probleme bei der Garbage Collection bekommt.", + "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 hasst 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 hasst, 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. Für 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 Infromationen, 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 904af49f9..b286ca09f 100644 --- a/app/plugins/tutorial/static/i18n/en.json +++ b/app/plugins/tutorial/static/i18n/en.json @@ -32,7 +32,8 @@ "RAMSTORAGE_INPUT_2": "GB 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. 64GB are a good guide value.", - "RAM_DESCRIPTION_2": "Having more than 32GB of Heap for the JVM is not very beneficial, it will run into garbage collection issues.", + "RAM_DESCRIPTION_2": "Byte", + "RAM_DESCRIPTION_3": "Having more than 32GB 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", diff --git a/app/static/i18n/de.json b/app/static/i18n/de.json index c9836eda5..a6c39f519 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." } } From d62b1e832123d6831a8fc8df64c47346a9d3a6a6 Mon Sep 17 00:00:00 2001 From: Seonghye Han Date: Tue, 21 Aug 2018 18:24:49 +0200 Subject: [PATCH 23/41] design --- app/conf/plugins.json | 1 + app/plugins/tutorial/calculator.css | 70 ++++ app/plugins/tutorial/calculator.html | 502 ++++++++++++++------------- 3 files changed, 327 insertions(+), 246 deletions(-) create mode 100644 app/plugins/tutorial/calculator.css diff --git a/app/conf/plugins.json b/app/conf/plugins.json index 992c79024..a51a88768 100755 --- a/app/conf/plugins.json +++ b/app/conf/plugins.json @@ -68,6 +68,7 @@ }, { "name": "testing", "uri": "static/plugins/tutorial/testing.js", + "stylesheet": "static/plugins/tutorial/calculator.css", "enterprise": false, "routing": { "/testing": { diff --git a/app/plugins/tutorial/calculator.css b/app/plugins/tutorial/calculator.css new file mode 100644 index 000000000..96925704f --- /dev/null +++ b/app/plugins/tutorial/calculator.css @@ -0,0 +1,70 @@ + +.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: 10px; + 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; + padding: 7px; + font-size: 13px; + overflow: hidden; + word-break: normal; + border: 1px solid #242424 !important; + border-radius: 3px; + background: #242424 !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:#353535; + border: 1px solid #545454; + border-radius: 4px; +} + +select { + width:auto; + background-color:#353535; + border: 1px solid #545454; + border-radius: 4px; +} + + + diff --git a/app/plugins/tutorial/calculator.html b/app/plugins/tutorial/calculator.html index 0e37d831a..2b6cd9eb8 100755 --- a/app/plugins/tutorial/calculator.html +++ b/app/plugins/tutorial/calculator.html @@ -1,265 +1,275 @@ - - - - - {{:: 'CALCULATOR.TITLE' | translate}} - - - - - - - -
- -

+
+
+

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

+

+ {{:: 'CALCULATOR.DESCRIPTION' | 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.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' | translate}} +

+
+ + +
+

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

+

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

+
+
+
-
-

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

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

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

- + +
+

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

+

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

+ +
+

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

+ +
+ + -

- {{:: 'CALCULATOR.DATA_DESCRIPTION_4' | 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.DATA_DESCRIPTION_5' | translate}} -

-
+
+

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

- - {{:: 'CALCULATOR.DATA_DESCRIPTION_6' | translate}} + {{:: 'CALCULATOR.CALCULATION_DESCRIPTION' | 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}}
- -

-
-
- -

- {{:: '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}} - - {{:: 'CALCULATOR.NODES_DESCRIPTION_3' | translate}} - - {{:: 'CALCULATOR.NODES_DESCRIPTION_4' | translate}} -

- -

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

-

- {{:: 'CALCULATOR.SHARDS_DESCRIPTION_1' | translate}} - - {{:: 'CALCULATOR.SHARDS_DESCRIPTION_2' | translate}} -

- - -
- - + +
+

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

+

+ {{:: 'CALCULATOR.NODES_DESCRIPTION_1' | translate}} + + {{:: 'CALCULATOR.NODES_DESCRIPTION_2' | translate}} + + {{:: 'CALCULATOR.NODES_DESCRIPTION_3' | translate}} + + {{:: '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 From 229901ff5d05a6240c84d5edf7f88fa9c05c6fd4 Mon Sep 17 00:00:00 2001 From: NinaFeuer <41051211+NinaFeuer@users.noreply.github.com> Date: Wed, 22 Aug 2018 08:43:34 +0200 Subject: [PATCH 24/41] Update de.json --- app/plugins/tutorial/static/i18n/de.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/plugins/tutorial/static/i18n/de.json b/app/plugins/tutorial/static/i18n/de.json index 540918217..789347861 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", @@ -27,18 +27,18 @@ "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_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 GB RAM dient", "RAMSTORAGE_INPUT_2": "GB 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. 64GB an Haupspeicher sind ein guter Richtwert.", + "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. 64GB an Haupspeicher sind ein guter Richtwert.", "RAM_DESCRIPTION_2": "Byte", - "RAM_DESCRIPTION_3": "Wenn mehr als 32GB der JVM als Heap zugewiesen werden, ist es gut möglich das sie probleme bei der Garbage Collection bekommt.", + "RAM_DESCRIPTION_3": "Wenn mehr als 32GB 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.", + "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 hasst vermutlich schon eine andere Datenbankt in verwendung gehabt und weißt vermutlich um viel Daten es geht.", + "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?", @@ -46,13 +46,13 @@ "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 hasst, dann ist ein Monat eine gute Empfehlung.", + "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. Für 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.", + "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 Infromationen, kommt dieses kleine Skript auf folgende Empfehlung:", + "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", From f3d436a84b1c87cac5573c5b7f541b213a683f7e Mon Sep 17 00:00:00 2001 From: NinaFeuer <41051211+NinaFeuer@users.noreply.github.com> Date: Wed, 22 Aug 2018 09:23:27 +0200 Subject: [PATCH 25/41] Update en.json --- app/plugins/tutorial/static/i18n/en.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/plugins/tutorial/static/i18n/en.json b/app/plugins/tutorial/static/i18n/en.json index b286ca09f..439fdd808 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.", @@ -39,20 +39,20 @@ "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 is being inserted?", + "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 also later 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, just specify some expected table size.", + "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 a good time frame for partitioning is set, this can enhance the performance greatly.", - "PARTITION_DESCRIPTION_2": "Choose a batch size in which the data will be used later. If you have no idea, a month is a good suggestion.", + "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": "With the data provided this little script came up with a recommendation.", + "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", From 6c3e1f681be1b23dfa723d1bca68e942c1aaa9fc Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 22 Aug 2018 10:45:34 +0200 Subject: [PATCH 26/41] re-add RAM as input parameter --- app/plugins/tutorial/calculator.html | 33 +++++++++++++++++++++--- app/plugins/tutorial/static/i18n/de.json | 2 +- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/app/plugins/tutorial/calculator.html b/app/plugins/tutorial/calculator.html index 2b6cd9eb8..49d09aae6 100755 --- a/app/plugins/tutorial/calculator.html +++ b/app/plugins/tutorial/calculator.html @@ -70,9 +70,32 @@

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

-

- {{:: 'CALCULATOR.RAM_DESCRIPTION' | translate}} -

+
+

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

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

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

+ +
+
@@ -117,6 +140,8 @@

+

+

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

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

@@ -137,31 +138,34 @@

- - - +

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

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

+

+

@@ -218,7 +223,9 @@

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

+

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

+

+

diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js index 5e8dc626d..628a24544 100755 --- a/app/plugins/tutorial/calculator.js +++ b/app/plugins/tutorial/calculator.js @@ -20,7 +20,7 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr $scope.expectedTableSizeUnitPrefix = 'Terra'; $scope.dataInsertedPerTimeUnitPrefix = 'Giga'; $scope.dataInsertedPerTimeTemporalUnit = 'day'; - $scope.expectedTableSize = 10; + $scope.expectedTableSize = 2; $scope.keepTimeTemporalUnit = 'month'; $scope.keepTime = 6; $scope.partitionSize = 1; From d24402b0290d2ded6a8fcb51a1279237148928a1 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 22 Aug 2018 11:56:38 +0200 Subject: [PATCH 28/41] fix loadTable size --- app/plugins/tutorial/calculator.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/plugins/tutorial/calculator.js b/app/plugins/tutorial/calculator.js index 628a24544..abf3707db 100755 --- a/app/plugins/tutorial/calculator.js +++ b/app/plugins/tutorial/calculator.js @@ -57,7 +57,7 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr var res = 1; if ($scope.dataType === 'perTime') { res = (($scope.keepTime * $scope.temporalUnit($scope.keepTimeTemporalUnit)) / ($scope.partitionSize * $scope.temporalUnit($scope.partitionSizeTemporalUnit))); - } else if ($scope.dataType === 'perTime') { + } else if ($scope.dataType === 'absolute') { res = $scope.manualPartitionCount; } console.log("partitionCount: " + res); @@ -171,7 +171,6 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr $scope.loadPartition($scope.selectSchema, $scope.selectTable); $scope.loadReplica($scope.selectSchema, $scope.selectTable); $scope.loadRAMStoragePropotion(); - console.log("*******************going to execute..."); $scope.loadRAM(); }; $scope.loadCPUCores = function (schemaName, tableName) { @@ -184,8 +183,8 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr }; $scope.loadTablesize = function(schemaName, tableName) { - var stmt = "select sum(size) from sys.shards where schema_name = '"+ schemaName - +"'and table_name = '"+tableName+"' and primary=true;"; + 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; @@ -193,7 +192,7 @@ angular.module('calculator', ['sql', 'translation']).controller('CalculatorContr return; } var size = (query.rows[0])[0]; - $scope.expectedTableSize = $scope.getPrefixedNumber(size); + $scope.expectedTableSize = Number($scope.getPrefixedNumber(size).split('.')[0]); //cast to whole numbered, respecting prefix $scope.expectedTableSizeUnitPrefix = $scope.getPrefix(size); $scope.dataType = 'absolute'; }); From bd3373c75a347d6dcf46052b7d51a2c67cbf94ea Mon Sep 17 00:00:00 2001 From: Seonghye Han Date: Wed, 22 Aug 2018 12:04:29 +0200 Subject: [PATCH 29/41] fix design --- app/plugins/tutorial/calculator.css | 23 +++-- app/plugins/tutorial/calculator.html | 123 ++++++++++++++------------- app/plugins/tutorial/calculator.js | 3 +- 3 files changed, 81 insertions(+), 68 deletions(-) diff --git a/app/plugins/tutorial/calculator.css b/app/plugins/tutorial/calculator.css index 96925704f..d36cb4e0f 100644 --- a/app/plugins/tutorial/calculator.css +++ b/app/plugins/tutorial/calculator.css @@ -15,7 +15,7 @@ margin: 5px; } .cr-panel-block__calculator__header{ - padding-bottom: 10px; + padding-bottom: 3px; letter-spacing: 0.6px; font-weight: bold; background-color: #353535; @@ -27,6 +27,8 @@ width:100%; margin-left:10px; margin-right:10px; + margin-top:0px; + margin-bottom:0px; padding: 7px; font-size: 13px; overflow: hidden; @@ -37,6 +39,16 @@ } +.result { + font-size:17px; +} + + +.last_row { + padding-bottom:0px !important; + margin-bottom:0px !important; +} + .column { float: left; width: 50%; @@ -52,19 +64,18 @@ -input { +input{ width:50px; - background-color:#353535; + background-color:#545454; border: 1px solid #545454; border-radius: 4px; } + select { width:auto; - background-color:#353535; + background-color:#545454; border: 1px solid #545454; border-radius: 4px; } - - diff --git a/app/plugins/tutorial/calculator.html b/app/plugins/tutorial/calculator.html index 49d09aae6..1d03bb561 100755 --- a/app/plugins/tutorial/calculator.html +++ b/app/plugins/tutorial/calculator.html @@ -3,7 +3,7 @@

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

-

+

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

@@ -40,9 +40,9 @@

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

-

+

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

+
@@ -76,9 +76,9 @@

- + -