CLOVER🍀

That was when it all began.

Apache GeodeでPartitioned Regionを使った時の、Member増減時の挙動と設定を確認する

Apache Geodeでちょっと気になっていることのひとつとして、Partitioned Regionを使った時にMemberが
増減した際の挙動があります。

前に少し試したことがあるのですが、

Apache GeodeでのBucketとキーの配置について(概要/API編) - CLOVER

Apache GeodeでのBucketとキーの配置について(クラスタ動作確認編) - CLOVER

クラスタ内のMemberが増えたり減ったりした時の挙動がちょっと気になるので、もうちょっと落ち着いて
見てみたいと思います。

前提と準備

基本的には、Client/Server構成で確認します。

  • Apache Geodeクラスタ環境は、Docker Composeで簡単に実現する
  • Locatorはひとつ(172.19.0.2:10334)
  • Serverは1〜3の間で増減させる
  • 上記のLocatorとServerはgfshから起動する
  • Clientからデータの登録を行う
  • データを持たないEmbeddedなServerをひとつ追加し、データの配置状況を確認する

Docker Composeでクラスタを構成すると書きましたが、
docker-compose.yml

version: "3"
services:
  locator:
    image: apachegeode/geode:1.1.1
    container_name: geodelocator
    hostname: geodelocator
    ports:
      - "10334:10334"
      - "40404:40404"
      - "1099:1099"
      - "7070:7070"
    command: sh -c "gfsh start locator --name=locator-`hostname` && tail -f /locator-`hostname`/locator-`hostname`.log"
  server:
    image: apachegeode/geode:1.1.1
    depends_on:
      - locator
    links:
      - locator
    volumes:
      - ./cache.xml:/cache.xml:ro
    command: >
      bash -c 'until gfsh start server --name=server-`hostname` --bind-address=`hostname -i` --locators=geodelocator[10334] --cache-xml-file=/cache.xml;\
               do \
                 sleep 3; \
               done && \
               tail -f /server-`hostname`/server-`hostname`.log'

なお、こちらのDocker Composeの内容については、別エントリで書いているので興味があれば参照してください。

Docker Composeで、Apache Geodeの簡単なクラスタ環境を構築する - CLOVER

Cacheの設定は、最初はこのようにします。
cache.xml

<?xml version="1.0" encoding="UTF-8"?>
<cache
    xmlns="http://geode.apache.org/schema/cache"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://geode.apache.org/schema/cache
                        http://geode.apache.org/schema/cache/cache-1.0.xsd"
    version="1.0">
  <region name="myRegion" refid="PARTITION">
    <region-attributes>
      <partition-attributes redundant-copies="1"/>
    </region-attributes>
  </region>
</cache>

「PARTITION」ではなく「PARTITION_REDUNDANT」にしても意味は同じなのですが、このあとでpartition-attributesを
変えていくので、あらかじめわけておくことにします。

「redundant-copies」について

と、こう書くと「redundant-copies」について触れないわけにはいかない気がします。

Set the Number of Redundant Copies | Geode Docs

要するに、バックアップをいくつ持つか?という話になります。「PARTITION_REDUNDANT」を選択すると「redundant-copies」を
1にしたことを同じになるので、バックアップがひとつ確保されます。

「redundant-copies」に特に設定のない「PARTITION」であれば、バックアップがないためServerがダウンすると、該当のServerが
持っていたデータは失われることになります。

ただ、このエントリの後の方で出てくる「リバランス」はバックアップ数が0でも行うことができます。

Locatorと最初のServerを起動

では、Cacheの設定をこちらでいってみます。

  <region name="myRegion" refid="PARTITION">
    <region-attributes>
      <partition-attributes redundant-copies="1"/>
    </region-attributes>
  </region>

まず、Locatorと最初のServerを起動します。

$ docker-compose up

これで、Apache Geodeクラスタが起動しました。

Serverのgfshから、クラスタのMemberを見てみましょう。

$ docker exec -it geodecluster_server_1 bash
[root@a978e278d01c /]# gfsh
    _________________________     __
   / _____/ ______/ ______/ /____/ /
  / /  __/ /___  /_____  / _____  / 
 / /__/ / ____/  _____/ / /    / /  
/______/_/      /______/_/    /_/    1.1.1

Monitor and Manage Apache Geode
gfsh>connect --jmx-manager=geodelocator[1099]
Connecting to Manager at [host=geodelocator, port=1099] ..
Successfully connected to: [host=geodelocator, port=1099]

gfsh>list members
        Name         | Id
-------------------- | --------------------------------------------------------
locator-geodelocator | 172.19.0.2(locator-geodelocator:37:locator)<ec><v0>:1024
server-a978e278d01c  | 172.19.0.3(server-a978e278d01c:139)<v3>:1024

LocatorとServerがひとつですね。

データの登録と確認

続いて、データを登録します。ここでは、Clientから登録しましょう。

Maven依存関係。

        <dependency>
            <groupId>org.apache.geode</groupId>
            <artifactId>geode-core</artifactId>
            <version>1.1.1</version>
        </dependency>

作成したプログラムはこちら。
src/main/java/org/littlewings/geode/rebalancing/GeodeClient.java

package org.littlewings.geode.rebalancing;

import java.util.stream.IntStream;

import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientCacheFactory;
import org.apache.geode.cache.client.ClientRegionShortcut;

public class GeodeClient {
    public static void main(String... args) {
        String mode = args[0];

        try (ClientCache clientCache = new ClientCacheFactory().addPoolLocator("172.19.0.2", 10334).create()) {
            Region<String, String> region =
                    clientCache
                            .<String, String>createClientRegionFactory(ClientRegionShortcut.PROXY).create("myRegion");
            switch (mode) {
                case "show-all-keyvalues":
                    showAllKeyValues(clientCache, region);
                    break;
                case "register-data":
                    registerData(clientCache, region);
                    break;
                default:
                    System.out.printf("Unknown Mode[%s]%n", mode);
                    break;
            }
        }
    }

    static void showAllKeyValues(ClientCache clientCache, Region<String, String> region) {
        int size = 10;

        IntStream
                .rangeClosed(1, size)
                .forEach(i -> System.out.printf("key: %s, value: %s%n", "key" + i, region.get("key" + i)));
    }

    static void registerData(ClientCache clientCache, Region<String, String> region) {
        int size = 10;

        IntStream.rangeClosed(1, size).forEach(i -> region.put("key" + i, "value" + i));
    }
}

形式は固定的ですが、エントリを10個登録、参照する処理を書いています。

引数「register-data」でデータ登録。

$ mvn compile exec:java -Dexec.mainClass=org.littlewings.geode.rebalancing.GeodeClient -Dexec.args=register-data

「show-all-keyvalues」で確認。

$ mvn compile exec:java -Dexec.mainClass=org.littlewings.geode.rebalancing.GeodeClient -Dexec.args=show-all-keyvalues

key: key1, value: value1
key: key2, value: value2
key: key3, value: value3
key: key4, value: value4
key: key5, value: value5
key: key6, value: value6
key: key7, value: value7
key: key8, value: value8
key: key9, value: value9
key: key10, value: value10

まあ、データが入ったことがわかるだけですね。

データのServer上の配置状況を確認したい

で、このあとでServerを追加していきたいわけですが、データの配置状況を確認できないとなにが起こっているかわかりません。

というわけで、クラスタには参加するけれども、データは持たないServerを書いてみたいと思います。

作成したのはこちら。
src/main/java/org/littlewings/geode/rebalancing/NoDataServer.java

package org.littlewings.geode.rebalancing;

import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.PartitionAttributesFactory;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.control.RebalanceOperation;
import org.apache.geode.cache.control.RebalanceResults;
import org.apache.geode.cache.control.ResourceManager;
import org.apache.geode.cache.partition.PartitionRegionHelper;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.internal.cache.PartitionedRegion;

public class NoDataServer {
    public static void main(String... args) {
        String mode = args[0];

        try (Cache cache = new CacheFactory().set("name", "no-data-server").set("locators", "172.19.0.2[10334]").create()) {
            Region<String, String> region =
                    cache
                            .<String, String>createRegionFactory(RegionShortcut.PARTITION_PROXY)
                            .setPartitionAttributes(new PartitionAttributesFactory<>().setRedundantCopies(1).create())
                            .create("myRegion");
            switch (mode) {
                case "show-members":
                    showMembers(cache, region);
                    break;
                case "show-locate":
                    showLocate(cache, region);
                    break;
                case "rebalance":
                    rebalance(cache, region);
                    break;
                default:
                    System.out.printf("Unknown Mode[%s]%n", mode);
                    break;
            }
        }
    }

    static void showMembers(Cache cache, Region<String, String> region) {
        DistributedMember self = cache.getDistributedSystem().getDistributedMember();
        Set<DistributedMember> otherMembers = cache.getDistributedSystem().getAllOtherMembers();

        System.out.printf("self: %s%n", self.getName());
        otherMembers.forEach(member -> System.out.printf("other: %s%n", member.getName()));
    }

    static void showLocate(Cache cache, Region<String, String> region) {
        region
                .entrySet()
                .stream()
                .sorted(Comparator.comparing(e -> Integer.parseInt(e.getKey().substring(3))))
                .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("   primary = %s%n", primary.getName());
                        System.out.printf("   backup = %s%n",
                                backups.stream().map(m -> m.getName()).collect(Collectors.joining(", ", "[", "]")));
                    }
                });
    }

    static void rebalance(Cache cache, Region<String, String> region) {
        try {
            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());
        } catch (InterruptedException e) {
            // ignore
        }
    }
}

3つのことができるようにしてあります。

現在のクラスタ内のMember確認。

    static void showMembers(Cache cache, Region<String, String> region) {
        DistributedMember self = cache.getDistributedSystem().getDistributedMember();
        Set<DistributedMember> otherMembers = cache.getDistributedSystem().getAllOtherMembers();

        System.out.printf("self: %s%n", self.getName());
        otherMembers.forEach(member -> System.out.printf("other: %s%n", member.getName()));
    }

データの配置状況の確認。PrimaryのServerと、バックアップの持ち主がわかるようにしています。

    static void showLocate(Cache cache, Region<String, String> region) {
        region
                .entrySet()
                .stream()
                .sorted(Comparator.comparing(e -> Integer.parseInt(e.getKey().substring(3))))
                .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("   primary = %s%n", primary.getName());
                        System.out.printf("   backup = %s%n",
                                backups.stream().map(m -> m.getName()).collect(Collectors.joining(", ", "[", "]")));
                    }
                });
    }

最後にリバランスです。

    static void rebalance(Cache cache, Region<String, String> region) {
        try {
            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());
        } catch (InterruptedException e) {
            // ignore
        }
    }

このServerの名前は、わかりやすいように「no-data-server」としました。

        try (Cache cache = new CacheFactory().set("name", "no-data-server").set("locators", "172.19.0.2[10334]").create()) {

また、Regionの定義は「PARTITION_PROXY」を指定して、Serverではあるもののデータを持たないようにしています。

            Region<String, String> region =
                    cache
                            .<String, String>createRegionFactory(RegionShortcut.PARTITION_PROXY)
                            .setPartitionAttributes(new PartitionAttributesFactory<>().setRedundantCopies(1).create())
                            .create("myRegion");

ただ、「redundant-copies」の値は合わせておかないとクラスタに入れないようなので、こちらは同じ値を指定しておきます。

仮に指定しなかった場合は、今回のServer側の設定(redundant-copies=1)だとこうなります。

[warn 2017/04/20 23:15:57.648 JST <main> tid=0x1] Initialization failed for Region /myRegion
java.lang.IllegalStateException: Requested redundancy  0  is incompatible with existing redundancy  1
	at org.apache.geode.internal.cache.PartitionRegionConfigValidator.validatePartitionAttrsFromPRConfig(PartitionRegionConfigValidator.java:82)
	at org.apache.geode.internal.cache.PartitionedRegion.registerPartitionedRegion(PartitionedRegion.java:1240)
	at org.apache.geode.internal.cache.PartitionedRegion.initPRInternals(PartitionedRegion.java:885)
	at org.apache.geode.internal.cache.PartitionedRegion.initialize(PartitionedRegion.java:1058)
	at org.apache.geode.internal.cache.GemFireCacheImpl.createVMRegion(GemFireCacheImpl.java:3308)
	at org.apache.geode.internal.cache.GemFireCacheImpl.basicCreateRegion(GemFireCacheImpl.java:3203)
	at org.apache.geode.internal.cache.GemFireCacheImpl.createRegion(GemFireCacheImpl.java:3191)
	at org.apache.geode.cache.RegionFactory.create(RegionFactory.java:762)

というわけで、合わせておきましょう、と。

では、とりあえずMember一覧を見てみましょう。

$ mvn compile exec:java -Dexec.mainClass=org.littlewings.geode.rebalancing.NoDataServer -Dexec.args=show-members

self: no-data-server
other: server-a978e278d01c
other: locator-geodelocator

はい、一瞬だけですが3つになります。

データの配置状況を見てみます。

$ mvn compile exec:java -Dexec.mainClass=org.littlewings.geode.rebalancing.NoDataServer -Dexec.args=show-locate

key = key1, value = value1
   primary = server-a978e278d01c
   backup = []
key = key2, value = value2
   primary = server-a978e278d01c
   backup = []
key = key3, value = value3
   primary = server-a978e278d01c
   backup = []
key = key4, value = value4
   primary = server-a978e278d01c
   backup = []
key = key5, value = value5
   primary = server-a978e278d01c
   backup = []
key = key6, value = value6
   primary = server-a978e278d01c
   backup = []
key = key7, value = value7
   primary = server-a978e278d01c
   backup = []
key = key8, value = value8
   primary = server-a978e278d01c
   backup = []
key = key9, value = value9
   primary = server-a978e278d01c
   backup = []
key = key10, value = value10
   primary = server-a978e278d01c
   backup = []

データが「server-a978e278d01c」にあることが確認できますね。「no-data-server」には割り当てられていません。
よって、実質Serverがひとつということになるので、バックアップは作られていません。

ここで、Serverをひとつ増やしてみます。

$ docker-compose scale server=2
Creating and starting geodecluster_server_2 ... done

gfshで見ると、このようになります。「server-8a580ed2f3f6」が追加Serverですね。

gfsh>list members
        Name         | Id
-------------------- | --------------------------------------------------------
locator-geodelocator | 172.19.0.2(locator-geodelocator:37:locator)<ec><v0>:1024
server-a978e278d01c  | 172.19.0.3(server-a978e278d01c:139)<v3>:1024
server-8a580ed2f3f6  | 172.19.0.4(server-8a580ed2f3f6:39)<v18>:1024

データの配置状況を見ると、バックアップに追加したServerが割り当てられています。

key = key1, value = value1
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key2, value = value2
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key3, value = value3
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key4, value = value4
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key5, value = value5
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key6, value = value6
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key7, value = value7
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key8, value = value8
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key9, value = value9
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key10, value = value10
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]

この挙動なんですけど、こちらのドキュメントに内容が書かれています。
Configure Member Join Redundancy Recovery for a Partitioned Region | Geode Docs

「startup-recovery-delay」という属性で、Regionにバックアップ数が満たされていなかった場合の挙動を設定することが
できます。

「startup-recovery-delay」はデフォルトでは「0」になっていて、つまり以下と同じです。

  <region name="myRegion" refid="PARTITION">
    <region-attributes>
      <partition-attributes redundant-copies="1" startup-recovery-delay="0"/>
    </region-attributes>
  </region>

この挙動を変える場合は「startup-recovery-delay」の値を変更するわけですが、「-1」にした場合はそもそもこの挙動が無効になります。
新しいServerが追加されても、リカバリは行われないと。0以上の値を指定した場合は、Serverが追加されてから指定した値(ミリ秒)が
経過した後にリカバリが行われます。デフォルトは0なので、Serverが追加されたら即時、ですね。

なお、これはリバランスではなくてリカバリなので、バックアップ数が満たされたこの状態でServerが増えても、実はなにも
変わらなかったりします。

3つ目のServerを追加してみましょう。

$ docker-compose scale server=3
Creating and starting geodecluster_server_3 ... done

「server-b5af22a32b74」が追加されました。

gfsh>list members
        Name         | Id
-------------------- | --------------------------------------------------------
locator-geodelocator | 172.19.0.2(locator-geodelocator:37:locator)<ec><v0>:1024
server-a978e278d01c  | 172.19.0.3(server-a978e278d01c:139)<v3>:1024
server-8a580ed2f3f6  | 172.19.0.4(server-8a580ed2f3f6:39)<v18>:1024
server-b5af22a32b74  | 172.19.0.5(server-b5af22a32b74:39)<v21>:1024

データの配置状況を見てみます。

key = key1, value = value1
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key2, value = value2
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key3, value = value3
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key4, value = value4
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key5, value = value5
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key6, value = value6
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key7, value = value7
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key8, value = value8
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key9, value = value9
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key10, value = value10
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]

「server-b5af22a32b74」は登場しません。つまり、Serverが増えたはいいですが、なにも変わっていないわけですね。

この状態を変えるには、リバランスします。

リバランス

リバランスは、gfshまたはAPIから可能です。というか、APIは先に載せてしまいましたが…。

ドキュメントは、こちら。

Rebalancing Partitioned Region Data | Geode Docs

リバランスを行うと、バックアップ数が満たされていない時に補充するとともに、Server間でのデータ量をバランシングし直します。

リバランスを行う…前に、シミュレーションをしてみましょう。

gfsh>rebalance --simulate

Simulated partition regions  /myRegion

                                       Rebalanced Stats                                         | Value
----------------------------------------------------------------------------------------------- | -----
Total bytes in all redundant bucket copies created during this rebalance                        | 0
Total time (in milliseconds) spent creating redundant bucket copies during this rebalance       | 0
Total number of redundant copies created during this rebalance                                  | 0
Total bytes in buckets moved during this rebalance                                              | 222
Total time (in milliseconds) spent moving buckets during this rebalance                         | 0
Total number of buckets moved during this rebalance                                             | 140
Total time (in milliseconds) spent switching the primary state of buckets during this rebalance | 0
Total primaries transferred during this rebalance                                               | 17
Total time (in milliseconds) for this rebalance                                                 | 0

こんな感じになるみたいです。

では、実行。

gfsh>rebalance

Rebalanced partition regions  /myRegion

                                       Rebalanced Stats                                         | Value
----------------------------------------------------------------------------------------------- | -----
Total bytes in all redundant bucket copies created during this rebalance                        | 0
Total time (in milliseconds) spent creating redundant bucket copies during this rebalance       | 0
Total number of redundant copies created during this rebalance                                  | 0
Total bytes in buckets moved during this rebalance                                              | 222
Total time (in milliseconds) spent moving buckets during this rebalance                         | 3806
Total number of buckets moved during this rebalance                                             | 140
Total time (in milliseconds) spent switching the primary state of buckets during this rebalance | 482
Total primaries transferred during this rebalance                                               | 17
Total time (in milliseconds) for this rebalance                                                 | 4357

gfsh>

4秒くらいかかりました…。

データの配置状況を見てみましょう。

key = key1, value = value1
   primary = server-a978e278d01c
   backup = [server-b5af22a32b74]
key = key2, value = value2
   primary = server-b5af22a32b74
   backup = [server-8a580ed2f3f6]
key = key3, value = value3
   primary = server-a978e278d01c
   backup = [server-b5af22a32b74]
key = key4, value = value4
   primary = server-b5af22a32b74
   backup = [server-8a580ed2f3f6]
key = key5, value = value5
   primary = server-a978e278d01c
   backup = [server-b5af22a32b74]
key = key6, value = value6
   primary = server-b5af22a32b74
   backup = [server-8a580ed2f3f6]
key = key7, value = value7
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key8, value = value8
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key9, value = value9
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key10, value = value10
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]

リバランスされたみたいですね。

APIの場合は、こうなります。

            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());

シミュレーションする場合は、startをsimulateに変えるのだとか。

            RebalanceOperation operation = resourceManager.createRebalanceFactory().simulate();

で、リバランスについてもう少しドキュメントを見てみましょう。デフォルトでかからないあたり、なにかありそうですね?

Rebalancing Partitioned Region Data | Geode Docs

注意点として、リバランスの実行時はトランザクションが失敗するようになるらしいです。TransactionDataRebalancedExceptionがスローされると。

また、複数のRegionを並列にリバランスするには、「gemfire.resource.manager.threads」でスレッド数を調整することになるそうです。

リバランス中は、Read、Write操作は実行可能ですが、Functionの実行中にデータが移動した場合はパフォーマンスが低下する可能性があると。

各Serverのデータ保持量を調整する場合は、「partition-attributes」の「local-max-memory」で行いますと。

Serverがダウンした場合

最後に、Serverをダウンさせてみます。

scaleを2へ。

$ docker-compose scale server=2
Stopping and removing geodecluster_server_3 ...

1台いなくなりました。

gfsh>list members
        Name         | Id
-------------------- | --------------------------------------------------------
locator-geodelocator | 172.19.0.2(locator-geodelocator:37:locator)<ec><v0>:1024
server-a978e278d01c  | 172.19.0.3(server-a978e278d01c:139)<v3>:1024
server-8a580ed2f3f6  | 172.19.0.4(server-8a580ed2f3f6:39)<v18>:1024

データの配置状況を見てみましょう。

key = key1, value = value1
   primary = server-a978e278d01c
   backup = []
key = key2, value = value2
   primary = server-8a580ed2f3f6
   backup = []
key = key3, value = value3
   primary = server-a978e278d01c
   backup = []
key = key4, value = value4
   primary = server-8a580ed2f3f6
   backup = []
key = key5, value = value5
   primary = server-a978e278d01c
   backup = []
key = key6, value = value6
   primary = server-8a580ed2f3f6
   backup = []
key = key7, value = value7
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key8, value = value8
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key9, value = value9
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key10, value = value10
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]

なんと、穴空きになります。バックアップが欠損した場合も補填されません。

リバランスすれば、バックアップも補填されたうえでバランシングされます。

gfsh>rebalance

Rebalanced partition regions  /myRegion

                                       Rebalanced Stats                                         | Value
----------------------------------------------------------------------------------------------- | -----
Total bytes in all redundant bucket copies created during this rebalance                        | 222
Total time (in milliseconds) spent creating redundant bucket copies during this rebalance       | 2431
Total number of redundant copies created during this rebalance                                  | 100
Total bytes in buckets moved during this rebalance                                              | 0
Total time (in milliseconds) spent moving buckets during this rebalance                         | 0
Total number of buckets moved during this rebalance                                             | 0
Total time (in milliseconds) spent switching the primary state of buckets during this rebalance | 52
Total primaries transferred during this rebalance                                               | 2
Total time (in milliseconds) for this rebalance                                                 | 391

結果。

key = key1, value = value1
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key2, value = value2
   primary = server-8a580ed2f3f6
   backup = [server-a978e278d01c]
key = key3, value = value3
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key4, value = value4
   primary = server-8a580ed2f3f6
   backup = [server-a978e278d01c]
key = key5, value = value5
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key6, value = value6
   primary = server-8a580ed2f3f6
   backup = [server-a978e278d01c]
key = key7, value = value7
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key8, value = value8
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key9, value = value9
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]
key = key10, value = value10
   primary = server-a978e278d01c
   backup = [server-8a580ed2f3f6]

この挙動についてですが、以下のドキュメントに記載があります。

Configure Member Crash Redundancy Recovery for a Partitioned Region | Geode Docs

「partition-attributes」の「recovery-delay」で調整します。デフォルトは、「recovery-delay」が「-1」となっており
Serverがダウンしてもなにも起こりません。

0以上を指定すると、Serverがダウンした時にバックアップを補填するようになります。

試してみましょう。例えば、次のような設定にします。

  <region name="myRegion" refid="PARTITION">
    <region-attributes>
      <partition-attributes redundant-copies="1" recovery-delay="1000"/>
    </region-attributes>
  </region>

1度クラスタを停止して、再度構成し直します。

クラスタのMember。

gfsh>list members
        Name         | Id
-------------------- | --------------------------------------------------------
locator-geodelocator | 172.19.0.2(locator-geodelocator:37:locator)<ec><v0>:1024
server-f300ccfee8dc  | 172.19.0.3(server-f300ccfee8dc:138)<v3>:1024
server-a1eaffc17622  | 172.19.0.4(server-a1eaffc17622:39)<v4>:1024
server-803b5706988b  | 172.19.0.5(server-803b5706988b:39)<v4>:1024

データの配置状況。

key = key1, value = value1
   primary = server-a1eaffc17622
   backup = [server-803b5706988b]
key = key2, value = value2
   primary = server-a1eaffc17622
   backup = [server-f300ccfee8dc]
key = key3, value = value3
   primary = server-803b5706988b
   backup = [server-a1eaffc17622]
key = key4, value = value4
   primary = server-a1eaffc17622
   backup = [server-f300ccfee8dc]
key = key5, value = value5
   primary = server-803b5706988b
   backup = [server-a1eaffc17622]
key = key6, value = value6
   primary = server-f300ccfee8dc
   backup = [server-a1eaffc17622]
key = key7, value = value7
   primary = server-f300ccfee8dc
   backup = [server-803b5706988b]
key = key8, value = value8
   primary = server-f300ccfee8dc
   backup = [server-a1eaffc17622]
key = key9, value = value9
   primary = server-f300ccfee8dc
   backup = [server-803b5706988b]
key = key10, value = value10
   primary = server-803b5706988b
   backup = [server-f300ccfee8dc]

1台停止。

$ docker-compose scale server=2
Stopping and removing geodecluster_server_3 ... 

「server-803b5706988b」がいなくなりました。

gfsh>list members
        Name         | Id
-------------------- | --------------------------------------------------------
locator-geodelocator | 172.19.0.2(locator-geodelocator:37:locator)<ec><v0>:1024
server-f300ccfee8dc  | 172.19.0.3(server-f300ccfee8dc:138)<v3>:1024
server-a1eaffc17622  | 172.19.0.4(server-a1eaffc17622:39)<v4>:1024

データの配置状況を見てみましょう。

key = key1, value = value1
   primary = server-a1eaffc17622
   backup = [server-f300ccfee8dc]
key = key2, value = value2
   primary = server-a1eaffc17622
   backup = [server-f300ccfee8dc]
key = key3, value = value3
   primary = server-a1eaffc17622
   backup = [server-f300ccfee8dc]
key = key4, value = value4
   primary = server-a1eaffc17622
   backup = [server-f300ccfee8dc]
key = key5, value = value5
   primary = server-a1eaffc17622
   backup = [server-f300ccfee8dc]
key = key6, value = value6
   primary = server-f300ccfee8dc
   backup = [server-a1eaffc17622]
key = key7, value = value7
   primary = server-f300ccfee8dc
   backup = [server-a1eaffc17622]
key = key8, value = value8
   primary = server-f300ccfee8dc
   backup = [server-a1eaffc17622]
key = key9, value = value9
   primary = server-f300ccfee8dc
   backup = [server-a1eaffc17622]
key = key10, value = value10
   primary = server-f300ccfee8dc
   backup = [server-a1eaffc17622]

バックアップが補填されていますね。

まとめ

Partitioned Regionで、クラスタ内のServerの増減に伴うデータリカバリの設定および、リバランスについて見てみました。

注意点としては、新規にServerが追加されたり、Serverがダウンしても設定によってはリカバリは行われますが、
リバランスが行われるわけではないということですね。

とりあえず、両者の違いは把握しておきましょうと。

前回軽く試してちょっと気になっていたので、これですっきりしました。