先日、このようなエントリを書きました。
Apache GeodeでのBucketとキーの配置について(概要/API編) - CLOVER
その後編を書きたいと思います。
前回のエントリのテーマは
- Partitioned RegionのBucketとデータの分散状況を確認するためのAPIについて(ただし、単一メンバー)
- クラスタを構成して、複数のメンバー間でデータが分散して配置されている様子を確認する
のうち前者でしたが、今回は後者について書いていきます。
Partitioned RegionやBucket、Primaryやバックアップなどの単語の説明はほぼ端折りますので、そのあたりは前回のエントリをご確認ください。
お題
Apache GeodeでPartitioned Regionを使ったクラスタを構成し、Bucketおよびキーの分散配置状況を確認してみます。
簡単なCLIツールと、単純に浮いてもらっているだけのサーバー×2の3メンバー構成とし、プロセスをダウンさせたりしてデータの移動などについても見てみたいと思います。
プログラムと設定ファイル
それでは、プログラムを作成します。
Maven依存関係については、以下があれば十分です。
<dependency> <groupId>org.apache.geode</groupId> <artifactId>gemfire-core</artifactId> <version>1.0.0-incubating.M1</version> </dependency>
利用するApache Geodeのバージョンは、1.0.0-incubating.M1とします。
gemfire.propertiesは用意せず、gemfire.propertiesで指定するパラメーターは、gemfire.〜システムプロパティで起動時に指定することにしますが、Cache XMLは用意します。
src/main/resources/cache.xml
<?xml version="1.0" encoding="UTF-8"?> <cache xmlns="http://schema.pivotal.io/gemfire/cache" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schema.pivotal.io/gemfire/cache http://schema.pivotal.io/gemfire/cache/cache-8.1.xsd" version="8.1"> <region name="partitionRegion" refid="PARTITION"> <region-attributes> <partition-attributes redundant-copies="1"/> </region-attributes> </region> </cache>
バックアップ数1で、Partitioned Regionをひとつだけ定義しました。
続いて、プログラム。
単純な浮いていてもらうだけのサーバー。
src/main/java/org/littlewings/geode/bucketkey/Server.java
package org.littlewings.geode.bucketkey; import com.gemstone.gemfire.cache.Cache; import com.gemstone.gemfire.cache.CacheFactory; public class Server { public static void main(String... args) { Cache cache = new CacheFactory() .create(); System.console().readLine("> Enter, stop server."); cache.close(); } }
Enterを打つと終了する、ものすごく単純なサーバーです。
そして、動作確認のためのCLIツール。
src/main/java/org/littlewings/geode/bucketkey/Cli.java
package org.littlewings.geode.bucketkey; import java.io.Console; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; import com.gemstone.gemfire.cache.Cache; import com.gemstone.gemfire.cache.CacheFactory; import com.gemstone.gemfire.cache.Region; import com.gemstone.gemfire.cache.control.RebalanceOperation; import com.gemstone.gemfire.cache.control.RebalanceResults; import com.gemstone.gemfire.cache.control.ResourceManager; import com.gemstone.gemfire.cache.partition.PartitionRegionHelper; import com.gemstone.gemfire.distributed.DistributedMember; import com.gemstone.gemfire.distributed.DistributedSystem; import com.gemstone.gemfire.internal.cache.PartitionedRegion; import com.gemstone.gemfire.internal.cache.PartitionedRegionDataStore; import com.gemstone.gemfire.internal.cache.PartitionedRegionHelper; public class Cli { public static void main(String... args) { try (Cache cache = new CacheFactory().create()) { Console console = System.console(); boolean cliContinue = true; while (cliContinue) { String line = console.readLine("> "); if (line == null || line.trim().isEmpty()) { continue; } try { Region<String, String> region; String[] tokens = line.split("\\s+"); String command = tokens[0]; switch (command) { // コマンドに応じた処理を実装 } } catch (Exception e) { System.out.printf("Unknown Command, or Parse Error, [%s].%n", line); e.printStackTrace(); } } } } }
コメント部は、個別に記載していきます。
クラスタに参加しているメンバー全体を見れる、「members」コマンド。
case "members": if (tokens.length == 1) { DistributedSystem ds = cache.getDistributedSystem(); System.out.printf("Self = %s.%n", ds.getDistributedMember().getName()); System.out.printf("Other Members = %s.%n", ds.getAllOtherMembers().stream().map(m -> m.getName()).collect(Collectors.joining(", ", "[", "]"))); System.out.printf("Other Members, alias = %s.%n", cache.getMembers().stream().map(m -> m.getName()).collect(Collectors.joining(", ", "[", "]"))); } else { region = cache.getRegion(tokens[1]); System.out.printf("Region[%s] Other Members = %s.%n", region.getName(), cache.getMembers(region).stream().map(m -> m.getName()).collect(Collectors.joining(", ", "[", "]"))); } break;
指定した範囲で機械的にキーと値を生成してRegionに登録する、「range-put」コマンド。
case "range-put": region = cache.getRegion(tokens[1]); int start = Integer.parseInt(tokens[2]); int end = Integer.parseInt(tokens[3]); IntStream.rangeClosed(start, end).forEach(i -> { String key = "key" + i; String value = "value" + i; region.put(key, value); System.out.printf("Region[%s] putted, key = %s, value = %s.%n", region.getName(), key, value); }); break;
現在Regionに登録されているすべてのキーと値、Bucket ID、そしてデータのPrimaryとバックアップの保持メンバーを表示する「get-all」コマンド。
case "get-all": region = cache.getRegion(tokens[1]); System.out.printf("Region[%s]%n", region.getName()); region .entrySet() .stream() .forEach(entry -> { System.out.printf(" key = %s, value = %s%n", entry.getKey(), entry.getValue()); if (region instanceof PartitionedRegion) { DistributedMember primary = PartitionRegionHelper.getPrimaryMemberForKey(region, entry.getKey()); Set<DistributedMember> allMembers = PartitionRegionHelper.getAllMembersForKey(region, entry.getKey()); Set<DistributedMember> backups = new LinkedHashSet<>(allMembers); backups.remove(primary); System.out.printf(" bucket id = %d%n", PartitionedRegionHelper.getHashKey((PartitionedRegion) region, null, entry.getKey(), null, null)); /* もしくは System.out.printf(" bucket id = %d%n", PartitionedRegionHelper.getHashKey((PartitionedRegion) region, entry.getKey())); */ System.out.printf(" primary = %s%n", primary.getName()); System.out.printf(" backup = %s%n", backups.stream().map(m -> m.getName()).collect(Collectors.joining(", ", "[", "]"))); } }); break;
現在のローカルメンバー(CLIツール)に割り当てられている、キーと値、全BucketのID、Primaryとして割り当てられているBucketのID、バックアップを含めたBucketのIDを出力する「get-all-local」コマンド。
case "get-all-local": region = cache.getRegion(tokens[1]); if (region instanceof PartitionedRegion) { System.out.printf("Region[%s]%n", region.getName()); PartitionedRegion partitionedRegion = (PartitionedRegion) region; PartitionedRegionDataStore dataStore = partitionedRegion.getDataStore(); Set<Integer> bucketIds = dataStore.getAllLocalBucketIds(); System.out.printf(" All Buckets%n => %s%n", bucketIds); System.out.println(); System.out.printf(" [%s]'s Primary Local Buckets%n => %s%n", cache.getName(), partitionedRegion.getDataStore().getAllLocalPrimaryBucketIds()); dataStore .getAllLocalPrimaryBucketRegions() .forEach(bucketRegion -> { int bucketId = bucketRegion.getId(); bucketRegion .entrySet() .forEach(e -> { Map.Entry<String, String> entry = (Map.Entry<String, String>) e; System.out.printf(" BucketId[%d]: key = %s, value = %s%n", bucketId, entry.getKey(), entry.getValue()); }); }); System.out.println(); System.out.printf(" [%s]'s All Local Buckets%n => %s%n", cache.getName(), partitionedRegion.getDataStore().getAllLocalBucketIds()); dataStore .getAllLocalBucketRegions() .forEach(bucketRegion -> { int bucketId = bucketRegion.getId(); bucketRegion .entrySet() .forEach(e -> { Map.Entry<String, String> entry = (Map.Entry<String, String>) e; System.out.printf(" BucketId[%d]: key = %s, value = %s%n", bucketId, entry.getKey(), entry.getValue()); }); }); } else { System.out.printf("Region[%s] is not Partitioned Region.%n", tokens[1]); } break;
データのリバランスを行う、「rebalance」コマンド。
case "rebalance": region = cache.getRegion(tokens[1]); ResourceManager resourceManager = cache.getResourceManager(); RebalanceOperation operation = resourceManager.createRebalanceFactory().start(); RebalanceResults results = operation.getResults(); System.out.printf("Region[%s] Rebalance Results: totalBucketTransferBytes = %d, totalBucketCreatesCompleted = %d%n", region.getName(), results.getTotalBucketTransferBytes(), results.getTotalBucketCreatesCompleted()); break;
「exit」で終了、また上記以外までのコマンドを入力すると無視されます。
case "exit": cliContinue = false; break; default: System.out.printf("Unknown Command[%s].%n", line); break;
では、これらのプログラムを使って動作確認してみたいと思います。
動作確認
## CLI $ mvn compile exec:java \ -Dexec.mainClass=org.littlewings.geode.bucketkey.Cli \ -Dgemfire.name=MEMBER-CLI \ -Dgemfire.start-locator=localhost[10334] \ -Dgemfire.locators=localhost[10334],localhost[10335],localhost[10336] ## NODE-A $ mvn compile exec:java \ -Dexec.mainClass=org.littlewings.geode.bucketkey.Server \ -Dgemfire.name=MEMBER-NODE-A \ -Dgemfire.start-locator=localhost[10335] \ -Dgemfire.locators=localhost[10334],localhost[10335],localhost[10336] ## NODE-B $ mvn compile exec:java \ -Dexec.mainClass=org.littlewings.geode.bucketkey.Server \ -Dgemfire.name=MEMBER-NODE-B \ -Dgemfire.start-locator=localhost[10336] \ -Dgemfire.locators=localhost[10334],localhost[10335],localhost[10336]
見やすいように、gemfire.nameシステムプロパティで名前を付けました。
> members Self = MEMBER-CLI. Other Members = [MEMBER-NODE-A, MEMBER-NODE-B]. Other Members, alias = [MEMBER-NODE-A, MEMBER-NODE-B].
自分自身は、「MEMBER-CLI」です。
データを10個登録してみます。
> range-put partitionRegion 1 10
全データを確認してみます。
> get-all partitionRegion Region[partitionRegion] key = key10, value = value10 bucket id = 84 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key1, value = value1 bucket id = 85 primary = MEMBER-CLI backup = [MEMBER-NODE-B] key = key2, value = value2 bucket id = 86 primary = MEMBER-NODE-A backup = [MEMBER-CLI] key = key3, value = value3 bucket id = 87 primary = MEMBER-NODE-B backup = [MEMBER-NODE-A] key = key4, value = value4 bucket id = 88 primary = MEMBER-NODE-A backup = [MEMBER-CLI] key = key5, value = value5 bucket id = 89 primary = MEMBER-NODE-B backup = [MEMBER-NODE-A] key = key6, value = value6 bucket id = 90 primary = MEMBER-CLI backup = [MEMBER-NODE-B] key = key7, value = value7 bucket id = 91 primary = MEMBER-NODE-B backup = [MEMBER-NODE-A] key = key8, value = value8 bucket id = 92 primary = MEMBER-NODE-A backup = [MEMBER-CLI] key = key9, value = value9 bucket id = 93 primary = MEMBER-CLI backup = [MEMBER-NODE-B]
※初回はログがたくさん出るので、2回目の実行結果を表示しています
それぞれ、Primaryとバックアップがわかるようになっていますね。
このうち、CLIツールに割り当てられているデータを確認してみましょう。
> get-all-local partitionRegion Region[partitionRegion] All Buckets => [1, 2, 4, 5, 6, 8, 10, 11, 12, 14, 15, 16, 17, 20, 21, 24, 25, 27, 28, 30, 31, 32, 35, 36, 37, 39, 41, 42, 43, 44, 46, 48, 49, 50, 51, 53, 55, 56, 57, 59, 61, 62, 65, 66, 67, 68, 69, 70, 73, 74, 75, 77, 79, 80, 81, 83, 84, 85, 86, 88, 90, 92, 93, 95, 96, 97, 100, 101, 103, 104, 106, 107, 108, 109, 111] [MEMBER-CLI]'s Primary Local Buckets => [67, 4, 6, 70, 8, 73, 11, 75, 77, 16, 17, 81, 20, 84, 85, 25, 90, 28, 93, 30, 95, 32, 96, 35, 101, 39, 103, 106, 43, 44, 109, 48, 51, 55, 57, 59, 62] BucketId[84]: key = key10, value = value10 BucketId[90]: key = key6, value = value6 BucketId[93]: key = key9, value = value9 BucketId[85]: key = key1, value = value1 [MEMBER-CLI]'s All Local Buckets => [1, 2, 4, 5, 6, 8, 10, 11, 12, 14, 15, 16, 17, 20, 21, 24, 25, 27, 28, 30, 31, 32, 35, 36, 37, 39, 41, 42, 43, 44, 46, 48, 49, 50, 51, 53, 55, 56, 57, 59, 61, 62, 65, 66, 67, 68, 69, 70, 73, 74, 75, 77, 79, 80, 81, 83, 84, 85, 86, 88, 90, 92, 93, 95, 96, 97, 100, 101, 103, 104, 106, 107, 108, 109, 111] BucketId[84]: key = key10, value = value10 BucketId[88]: key = key4, value = value4 BucketId[86]: key = key2, value = value2 BucketId[90]: key = key6, value = value6 BucketId[92]: key = key8, value = value8 BucketId[93]: key = key9, value = value9 BucketId[85]: key = key1, value = value1
先ほどの「get-all」で、
primary = MEMBER-CLI
となっているものや、
backup = [MEMBER-CLI]
に絞られてBucketの情報が表示されましたね。
※参考までに、このPartition Regionが保持している全BucketのIDも出力しています
では、ここで「MEMBER-NODE-A」を停止してみます。「MEMBER-NODE-A」側のコンソールで、Enterキーを打つと停止します。
[info 2016/04/03 14:24:31.758 JST <org.littlewings.geode.bucketkey.Server.main()> tid=0xb] GemFireCache[id = 194268085; isClosing = true; isShutDownAll = false; created = Sun Apr 03 14:22:46 JST 2016; server = false; copyOnRead = false; lockLease = 120; lockTimeout = 60]: Now closing. [info 2016/04/03 14:24:31.880 JST <org.littlewings.geode.bucketkey.Server.main()> tid=0xb] Shutting down DistributionManager 192.168.254.129(MEMBER-NODE-A:124841)<ec><v0>:1024. [info 2016/04/03 14:24:32.077 JST <org.littlewings.geode.bucketkey.Server.main()> tid=0xb] Now closing distribution for 192.168.254.129(MEMBER-NODE-A:124841)<ec><v0>:1024 [info 2016/04/03 14:24:32.078 JST <org.littlewings.geode.bucketkey.Server.main()> tid=0xb] Stopping membership services [info 2016/04/03 14:24:33.082 JST <org.littlewings.geode.bucketkey.Server.main()> tid=0xb] GMSHealthMonitor server socket is closed in stopServices(). [info 2016/04/03 14:24:33.083 JST <Geode Failure Detection Server thread 0> tid=0x1f] GMSHealthMonitor server thread exiting [info 2016/04/03 14:24:33.089 JST <org.littlewings.geode.bucketkey.Server.main()> tid=0xb] GMSHealthMonitor serverSocketExecutor is terminated [info 2016/04/03 14:24:33.121 JST <org.littlewings.geode.bucketkey.Server.main()> tid=0xb] DistributionManager stopped in 1,239ms. [info 2016/04/03 14:24:33.123 JST <org.littlewings.geode.bucketkey.Server.main()> tid=0xb] Marking DistributionManager 192.168.254.129(MEMBER-NODE-A:124841)<ec><v0>:1024 as closed. [info 2016/04/03 14:24:33.124 JST <org.littlewings.geode.bucketkey.Server.main()> tid=0xb] Stopping Distribution Locator on localhost/127.0.0.1[10335] [info 2016/04/03 14:24:33.128 JST <Distribution Locator on localhost/127.0.0.1[10335]> tid=0x10] locator shutting down [info 2016/04/03 14:24:33.129 JST <org.littlewings.geode.bucketkey.Server.main()> tid=0xb] Distribution Locator on localhost/127.0.0.1[10335] is stopped
確認。
> get-all partitionRegion Region[partitionRegion] key = key10, value = value10 bucket id = 84 primary = MEMBER-CLI backup = [] key = key1, value = value1 bucket id = 85 primary = MEMBER-CLI backup = [MEMBER-NODE-B] key = key2, value = value2 bucket id = 86 primary = MEMBER-CLI backup = [] key = key3, value = value3 bucket id = 87 primary = MEMBER-NODE-B backup = [] key = key4, value = value4 bucket id = 88 primary = MEMBER-CLI backup = [] key = key5, value = value5 bucket id = 89 primary = MEMBER-NODE-B backup = [] key = key6, value = value6 bucket id = 90 primary = MEMBER-CLI backup = [MEMBER-NODE-B] key = key7, value = value7 bucket id = 91 primary = MEMBER-NODE-B backup = [] key = key8, value = value8 bucket id = 92 primary = MEMBER-CLI backup = [] key = key9, value = value9 bucket id = 93 primary = MEMBER-CLI backup = [MEMBER-NODE-B]
なんか薄くなりましたね??
リバランスしてみましょう。
> rebalance partitionRegion
再配置されました。
> get-all partitionRegion Region[partitionRegion] key = key10, value = value10 bucket id = 84 primary = MEMBER-CLI backup = [MEMBER-NODE-B] key = key1, value = value1 bucket id = 85 primary = MEMBER-CLI backup = [MEMBER-NODE-B] key = key2, value = value2 bucket id = 86 primary = MEMBER-CLI backup = [MEMBER-NODE-B] key = key3, value = value3 bucket id = 87 primary = MEMBER-NODE-B backup = [MEMBER-CLI] key = key4, value = value4 bucket id = 88 primary = MEMBER-CLI backup = [MEMBER-NODE-B] key = key5, value = value5 bucket id = 89 primary = MEMBER-NODE-B backup = [MEMBER-CLI] key = key6, value = value6 bucket id = 90 primary = MEMBER-CLI backup = [MEMBER-NODE-B] key = key7, value = value7 bucket id = 91 primary = MEMBER-NODE-B backup = [MEMBER-CLI] key = key8, value = value8 bucket id = 92 primary = MEMBER-CLI backup = [MEMBER-NODE-B] key = key9, value = value9 bucket id = 93 primary = MEMBER-CLI backup = [MEMBER-NODE-B]
「MEMBER-NODE-B」も落としてみます。
すると、バックアップがなくなってしまいます。
> get-all partitionRegion Region[partitionRegion] key = key10, value = value10 bucket id = 84 primary = MEMBER-CLI backup = [] key = key1, value = value1 bucket id = 85 primary = MEMBER-CLI backup = [] key = key2, value = value2 bucket id = 86 primary = MEMBER-CLI backup = [] key = key3, value = value3 bucket id = 87 primary = MEMBER-CLI backup = [] key = key4, value = value4 bucket id = 88 primary = MEMBER-CLI backup = [] key = key5, value = value5 bucket id = 89 primary = MEMBER-CLI backup = [] key = key6, value = value6 bucket id = 90 primary = MEMBER-CLI backup = [] key = key7, value = value7 bucket id = 91 primary = MEMBER-CLI backup = [] key = key8, value = value8 bucket id = 92 primary = MEMBER-CLI backup = [] key = key9, value = value9 bucket id = 93 primary = MEMBER-CLI backup = [] >
ここで再度、「MEMBER-NODE-A」を起動してみましょう。
$ mvn compile exec:java \ -Dexec.mainClass=org.littlewings.geode.bucketkey.Server \ -Dgemfire.name=MEMBER-NODE-A \ -Dgemfire.start-locator=localhost[10335] \ -Dgemfire.locators=localhost[10334],localhost[10335],localhost[10336]
> members Self = MEMBER-CLI. Other Members = [MEMBER-NODE-A]. Other Members, alias = [MEMBER-NODE-A].
バックアップにも割り当てられるようになります。
> get-all partitionRegion Region[partitionRegion] key = key10, value = value10 bucket id = 84 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key1, value = value1 bucket id = 85 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key2, value = value2 bucket id = 86 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key3, value = value3 bucket id = 87 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key4, value = value4 bucket id = 88 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key5, value = value5 bucket id = 89 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key6, value = value6 bucket id = 90 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key7, value = value7 bucket id = 91 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key8, value = value8 bucket id = 92 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key9, value = value9 bucket id = 93 primary = MEMBER-CLI backup = [MEMBER-NODE-A]
「MEMBER-NODE-B」も起こしてみましょう。
$ mvn compile exec:java \ -Dexec.mainClass=org.littlewings.geode.bucketkey.Server \ -Dgemfire.name=MEMBER-NODE-B \ -Dgemfire.start-locator=localhost[10336] \ -Dgemfire.locators=localhost[10334],localhost[10335],localhost[10336]
…割り当てが変わりません。
> get-all partitionRegion Region[partitionRegion] key = key10, value = value10 bucket id = 84 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key1, value = value1 bucket id = 85 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key2, value = value2 bucket id = 86 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key3, value = value3 bucket id = 87 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key4, value = value4 bucket id = 88 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key5, value = value5 bucket id = 89 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key6, value = value6 bucket id = 90 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key7, value = value7 bucket id = 91 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key8, value = value8 bucket id = 92 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key9, value = value9 bucket id = 93 primary = MEMBER-CLI backup = [MEMBER-NODE-A]
リバランスしてみます。
> rebalance partitionRegion
ちょっと「MEMBER-CLI」に偏っていますが、「MEMBER-NODE-B」にも割り当てられるようになりましたね。
> get-all partitionRegion Region[partitionRegion] key = key10, value = value10 bucket id = 84 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key1, value = value1 bucket id = 85 primary = MEMBER-NODE-B backup = [MEMBER-NODE-A] key = key2, value = value2 bucket id = 86 primary = MEMBER-CLI backup = [MEMBER-NODE-B] key = key3, value = value3 bucket id = 87 primary = MEMBER-NODE-B backup = [MEMBER-NODE-A] key = key4, value = value4 bucket id = 88 primary = MEMBER-CLI backup = [MEMBER-NODE-B] key = key5, value = value5 bucket id = 89 primary = MEMBER-NODE-B backup = [MEMBER-NODE-A] key = key6, value = value6 bucket id = 90 primary = MEMBER-CLI backup = [MEMBER-NODE-B] key = key7, value = value7 bucket id = 91 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key8, value = value8 bucket id = 92 primary = MEMBER-CLI backup = [MEMBER-NODE-A] key = key9, value = value9 bucket id = 93 primary = MEMBER-CLI backup = [MEMBER-NODE-A]
なんですが、「MEMBER-NODE-A」はPrimaryにならないんでしょうかねぇ…。
リバランスについて
先ほどから、メンバーが離脱する度にリバランスしていますが、これはデフォルトではメンバーダウン時にリバランスを自動的にはしてくれないからです。
Configure Member Crash Redundancy Recovery for a Partitioned Region
自動的にリバランスしてもらう場合は、以下の様に「recovery-delay」を指定します(単位はmilliseconds)。
<region name="partitionRegion" refid="PARTITION"> <region-attributes> <partition-attributes redundant-copies="1" recovery-delay="1000"/> </region-attributes> </region>
デフォルトでは、この「recovery-delay」が-1になっているため、自動でリバランスされません。
結果は載せませんが、今回は1秒設定で試すとリバランスしてくれました。
また、メンバー追加時もデフォルトではリバランスしてくれない…と書かれています…(そう?)。一応、こちらも設定で可能なようですが。
http://geode.docs.pivotal.io/docs/developing/partitioned_regions/set_join_redundancy_recovery.html
<region name="partitionRegion" refid="PARTITION"> <region-attributes> <partition-attributes redundant-copies="1" startup-recovery-delay="1000"/> </region-attributes> </region>
単独のメンバー時から、さらに2つメンバー追加しても、2つめのメンバーには結局自分でリバランスさせないといけないような挙動に…。あくまで、バックアップの補完という意味合いということでしょうかね。
多重障害のケース
バックアップがあるとはいえ、Primary+バックアップ数のメンバーが同時にダウンしてしまうとデータのロストが発生します。
今回の構成ですと、Primary+Backupがひとつなので、メンバーが2つ同時にダウンするとデータがロストします。このあたりは、リソースと耐障害性の兼ね合いというところですね。
ちょっと気になること
あとからクラスタに参加したメンバーは、Primaryにはなりにくいように見えるのですが、そういうもの?
例えば、CLIだけ起動してデータを入れて、次にメンバーを追加した場合はその追加メンバーはPrimaryにはなれていないような…。そこからさらに追加してリバランスすると、2つめに追加したメンバーはPrimaryになれるっぽいんですけどね。
追加されたメンバーは、最初はバックアップに全部割り当てられてしまうからでしょうか?
まとめ
2回に渡って、Apache GeodeのPartitioned RegionのBucketについての話や、実際にクラスタ構成でデータがメンバー間を移動していく様子を確認してみました。
これを調べる過程で、データの持ち方、構造的なところも知れて良かったかなと思います。