2

基本的な 2 ノードの Cassandra クラスターがあります。どちらのノードも、クラスタリングを有効にするための最小限の構成でバージョン 3.9 を実行します。ノードの 1 つが間違った世代を送信しているため、他のノードで警告が表示される

WARN  [GossipStage:1] Gossiper.java:1146 - received an invalid gossip generation for peer /10.3.185.234; local time = 1479469393, received generation = 1872927836

問題を引き起こしているノード 1 には、次の出力があります。

nodetool gossipinfo

/10.3.185.234

generation: 1872927836

1872927836 エポックは遠い日付です (Tue, 08 May 2029 09:43:56 GMT)。ノード 2 は正当にパケットを破棄しています。node-1 を修正して正しい世代を送ることはできますか?

4

3 に答える 3

5

cqlsh を使用して system.local テーブルの gossip_generation 値を変更することで問題を修正しました

update system.local  set gossip_generation = 1479472176 where key='local';

この変更後にサービスを再起動します

于 2016-11-18T13:10:32.703 に答える
3

scylla(version = 2.1.3-0.20180501.8e33e80ad) を使用しており、クラスター全体を再起動せずにこれを回避できました。

私たちのクラスターは最近、ノードを失っていました。これらのノードが再起動され、ブートアップのゴシップ フェーズ中にクラスターへの参加が許可されなかったためです。理由: status=UN (up & normal) のノードが次のエラーを吐き出し、影響を受けるノードがゴシップ フェーズ中にクラスタに参加することを許可しませんでした。私たちの場合、エラーメッセージは次のとおりです。

Jul 04 01:54:17 host-10.3.7.77 scylla[30263]: [シャード 0] ゴシップ - ピア 10.3.7.7 の無効なゴシップ生成を受信しました。ローカル世代 = 1526993447、受信世代 = 1562158865

上記のエラー メッセージの詳細とコンテキストを見てみましょう。

  • すべてのノードはシードのリストで構成されており、ブートアップ中にゴシップしてクラスター情報を収集しようとします。
  • 起動時に、ゴシップ中にシード ホストと共有する「世代」番号 (世代番号はエポック) を作成します。

gossiper.register (this->shared_from_this());

auto generation_number=db::system_keyspace::increment_and_get_generation().get0();

_gossiper.start_gossiping(generation_number, app_states, gms::bind_messaging_port(bool(do_bind))).get();

  • 最初の起動時のノードは、その世代番号をシードおよびシード ゴシップに送信し、他のノードと一緒に情報を渡します。シードは、この世代番号を参照として格納します。これは、上記のエラー メッセージで参照されている local_generation 用語と呼ばれます。つまり、UN ノード 10.3.7.77 は、ピア 10.3.7.7 が世代番号 1562158865 を送信していたが (つまり、receive_generation と呼ばれる)、参照 1526993447 として保存されていたと述べていました。 1526993447 は 2018 年 5 月 22 日のエポックを指し、1562158865 は 2019 年 7 月 3 日のエポックを指すことに注意してください。
  • 2 つのエポックの差が 1 年を超えているため、UN ノードは他のノードの参加を拒否します。

int64_t MAX_GENERATION_DIFFERENCE = 86400 * 365;

if (local_generation > 2 && remote_generation > local_generation + MAX_GENERATION_DIFFERENCE) { // 一部のピアがメモリを破損し、別のピア (またはそれ自体) に関する信じられない世代をブロードキャストしていると仮定します

logger.warn("ピアの無効なゴシップ生成を受信しました..... }

  • bootup 中の increment_and_get のロジックは次のとおりです。
auto req = format("SELECT gossip_generation FROM system.{} WHERE key='{}'", LOCAL, LOCAL);
return qctx->qp().execute_internal(req).then([] (auto rs) {
    int generation;
    if (rs->empty() || !rs->one().has("gossip_generation")) {
        // seconds-since-epoch isn't a foolproof new generation
        // (where foolproof is "guaranteed to be larger than the last one seen at this ip address"),
        // but it's as close as sanely possible
        generation = service::get_generation_number();
    } else {
        // Other nodes will ignore gossip messages about a node that have a lower generation than previously seen.
        int stored_generation = rs->one().template get_as<int>("gossip_generation") + 1;
        int now = service::get_generation_number();
        if (stored_generation >= now) {
            slogger.warn("Using stored Gossip Generation {} as it is greater than current system time {}."
                        "See CASSANDRA-3654 if you experience problems", stored_generation, now);
            generation = stored_generation;
        } else {
            generation = now;
        }
    }
    auto req = format("INSERT INTO system.{} (key, gossip_generation) VALUES ('{}', ?)", LOCAL, LOCAL);
  • 上記のロジックから、サーバーは最初に system.local テーブルから世代番号を検索します。値が空の場合、生成番号を生成するロジックは現在の時刻のみに依存するため、新しい番号、つまり現在の時刻が生成されます。空でない場合は、現在の時刻と比較して、より大きな値、つまり最近の時刻を使用して、それを system.local テーブルに書き戻します。

int get_generation_number() { .... auto now = high_resolution_clock::now().time_since_epoch(); int generation_number = duration_cast(now).count(); ....}

  • そのため、起動時にノードによって生成されてシードに送信される世代番号は、通常、常に現在の時刻に近くなりますが、シード UN ノードによってローカル参照として保存される世代番号は変更されません。

  • クラスタの再起動を完全に回避するには: 上記で説明したコード ロジックに基づいて、本番環境でこのアプローチを採用しました。

    -- 根本的な問題は、UN シード ノードに格納されている問題のあるノードのローカル世代が変更されていないことでした。(ただし、再起動のたびに問題のあるノードは、現在の時刻に近い新しい世代番号を送信します)

    -- IDEA : 問題のあるノードが送信するリモート世代番号が 1 年以内になるように、UN ノードに格納されている問題のあるノードのローカル世代を更新しましょう。

    -- では、UN シード ノードでこの値をどのように更新しますか? 問題のあるノードに、UN シード ノードに格納されているローカルの世代番号の 1 年間のウィンドウに入る値を持つ世代番号 (エポック) を送信させる必要があります。しかし、コードは常に世代番号として現在の時刻を取得し、現在の時刻は 2019 年 7 月であるため、どうすればよいでしょうか?

    -- 問題のあるノードの TIME を 1526993447 から 1 年以内の値に戻します。1 年ウィンドウの終わりに向かってエポック値を選択します。つまり、システム時刻を 2019 年 3 月 31 日などの値に変更します。 2018 年 10 月 2 日 & ノードを再起動します。ノードは再起動し、gen 番号 1554030000 (system.local テーブルを検索するため) または現在の時刻 (2019 年 3 月 31 日) をシードに送信します。

    -- UN シード ノードはこの値を取得し、問題のあるノードから送信されたリモート世代番号が 2018 年 5 月 22 日から 1 年以内であることを検証するため、その参照 (ローカル世代) の更新に進みます。

else if (remote_generation > local_generation) { logger.trace("ハートビート状態の生成を {} から {} に更新中", remote_generation, local_generation, ep); // 主要な状態変更は、リモート状態を直接挿入することで更新を処理します this->handle_major_state_change(ep, remote_state); } ....

-- UN シード ノードに格納されている問題のあるノードの参照 (ローカル ジェネ) を正常に更新しました。-- ここで、問題のあるノードを停止し、問題のあるノードの時間を現在の時刻にリセットして再起動します。問題のあるノードは、たとえば 2019 年 7 月 4 日の最新のエポック、つまりエポック 1562215230 を送信します。 (最新の時刻を使用して問題のあるノードに送信された gen) から 1554030000 (UN シード ノードに保存されているローカル参照) を差し引いて 1 年未満の場合、問題のあるノードはクラスターに参加できます。

-- 1 年間のウィンドウの終わりに向かってエポック/日付を選択することをお勧めしますが、1 年以内に、選択した日付から新しい 1 年間のウィンドウが開始されるため、遅いほど良いでしょう。この問題は、その長い LOL で軽減されます – はいこの問題は、実行時間の長いクラスターで発生します。これが意味することは、1 年の期間を延長するために、毎年ローリング再起動を行う必要があるということです。

手順の手順は次のとおりです。

手順:

  1. 問題のあるノードが 10.3.7.7 で、たとえば 10.3.7.77 (UN ノード) でエラーが報告されている場合は、10.3.7.7 のシードが 10.3.7.77 であることを確認してください。これにより、このノードとの通信が保証され、検索する必要がなくなります。クラスター内で誰が話しているかを見つけます。7.7 ノードのシードがエラーを報告しているノードと異なる場合は、シード ノードによって出力されたエラー メッセージを見て、どのエポックもリセットするかを決定します。私たちの場合、7.77 でエラーが発生したため、7.7 のシードを 7.77 ノードに変更しました。

  2. 問題のあるノードを起動します。

  3. シード ノードはエラーの出力を開始する必要があります。ノードのエラーメッセージをキャプチャし、リセットする日付を選択できるようにローカルの世代番号を書き留めます。私たちの場合、メッセージは次のとおりです。

Jul 04 01:54:17 host-10.3.7.77 scylla[30263]: [シャード 0] ゴシップ – ピア 10.3.7.7 の無効なゴシップ生成を受信しました。ローカル世代 = 1526993447、受信世代 = 1562158865

  1. 問題のあるノード 10.3.7.7 に cqlsh し、世代番号を 1526993447 の 1 年以内のエポックに更新します。新しい 1 年間のウィンドウが長くなります。

  2. 問題のあるノードで、コマンドを実行します

    5.1 'update system.local set gossip_generation = 1554030000 where key='local';'

    5.2 「nodetool フラッシュ」

  3. 問題のあるノードを停止する

  4. 構成ファイルを編集し、CQL (native_transport_port) を 9042 から 9043 に変更して、クライアントが接続およびデータを挿入できないようにします。このフェーズでデータを挿入すると、正しくない 2019 年 3 月のタイムスタンプを持つレコードが設定されます。つまり、データの破損が防止されます。これは予防策です

  5. システム時刻を変更します。つまり、「date -s '31 MAR 2019 11:03:25'」</p>

  6. date コマンドを実行して、システム時刻が変更されたことを確認します
  7. UN シード ノードの問題のあるノードとテール ログを開始すると、エラーは解消されるはずです。
  8. ゴシップが発生するまでしばらく待ち(数分で十分です)、問題のあるノードが現在UNであるかどうかを確認します.
  9. 別のノードでコマンド「nodetool status」を実行して、UN かどうかを確認します。
  10. UN シード ノードのログを追跡し、引き続きエラーが発生するかどうかを確認できます。エラーが再び表示される場合は、手順を最初からやり直してください。あなたは何かを逃しました。
  11. ノードが UN と宣言されると、次のようになります。

    14.1 ノードのシャットダウン

    14.2 構成ファイルで CQL (native_transport_port) を 9043 から 9042 に変更します。

    14.3 ボックスのシステム時間をリセットする

    14.4 システム時刻が正常に戻ったことを確認する

  12. 時刻とポートを元に戻したら、ノードを起動します。ノードはまだ UN である必要があります。

告白:

  1. はい、本番環境でこの演習を行いました。とにかくノードは死んでいると見なされたため、デッドノードを台無しにしても違いが生じないため、リスクは最小限でした。手順が失敗した場合、1 つのノードのみを犠牲にするため、クラスターを再起動する唯一のオプションが残されます。
  2. マスター ブランチの scylla コード ベースをスキャンして、クラスター通信でのシステム時間の使用法を調べたところ、システム時間の変更が機能することを確信できる 2 つの場所しか見つかりませんでした。また、CQL ポートを 9043 に変更することで、クライアントによる既存データの汚染を排除しました。

物語の教訓:

  1. これは scylla の 2.1 バージョンで発生し、2019 年 7 月 4 日現在、scylla のマスター ブランチにはまだ同じコード ロジックがあるため、バージョン 3 以降でも発生する可能性があります。2 .数か月ごとにノードのローリング再起動を行うと、ノードがゴシップ用の新しい世代番号を送信し、1 年間のウィンドウが延長されます。
  2. 1 年を超える長時間実行されているクラスターがある場合、ノードが再起動されると、このエラーの影響を受けます。ノードの再起動が増えるほど、流行が広がります。
  3. コードロジックが同じであれば、これはカサンドラで機能します。

参考文献:

https://github.com/scylladb/scylla/blob/134b59a425da71f6dfa86332322cc63d47a88cd7/gms/gossiper.cc

https://github.com/scylladb/scylla/blob/94d2194c771dfc2fb260b00f7f525b8089092b41/service/storage_service.cc

https://github.com/scylladb/scylla/blob/077c639e428a643cd4f0ffe8e90874c80b1dc669/db/system_keyspace.cc

上記の説明/修正の詳細は、私のブログhttps://mash213.wordpress.com/2019/07/05/scylla-received-an-invalid-gossip-generation-for-peer-how-to-resolveでも見つけることができ ます/

于 2019-07-07T05:09:00.427 に答える
1

手動での設定がどのような影響を与えるかはわかりませんが、それを修正する別の方法は、クラスター全体を一度に再起動することです。これはおそらく私たちにとってうまくいったでしょう。(修正後にこれを見つけました)。

ドキュメントには、修正されるまで複数回のローリング再起動を行うように記載されています (これは私たちにとってはうまくいきませんでした)。ただし、クラスター全体を再起動する大ハンマーは機能しました。すべての世代が適切に設定されました。

于 2018-01-05T20:10:42.360 に答える