社内se × プログラマ × ビッグデータ

プログラミングなどITに興味があります。

Android(kotlin) 既に起動中の Activity に onCreate を通らずに戻りたい

例えば、FieldActivity という Activity が既に起動していて、今は別の Activity が Active になっているとします。
以下のように、intent のインスタンスに Intent.FLAG_ACTIVITY_REORDER_TO_FRONT をセットし、それを startActivityIfNeeded() で呼び出してあげると、onCreate を通らずに、FieldActivity の状態を保持したままで戻ることができます。

val intent = Intent(applicationContext, FieldActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
startActivityIfNeeded(intent, 0)

FLAG_ACTIVITY_REORDER_TO_FRONT
スタック内の同一Activityを最前面に移動させます。

startActivityIfNeeded(Intent intent, int requestCode)
新たなActivityが必要な場合のみActivityを生成します。
※ requestCodeに0以上の値をセットした場合、起動したActivityのfinish()後に onActivityResult(int intRequestCode,int intResultCode, Intent intent) が呼ばれる。onActivityResult では intRequestCode と intResultCode の内容に応じた処理を実装する。

同一の Activityを最前面に移動させた上で、同一の Activityを改めて起動する必要はないため、startActivityIfNeeded は新たな Activity を生成しない。

Elasticsearch における Replication

Replication レプリケーションとは

フォールトトレラントとしてレプリケーションをサポートしている
※システムの一部に問題が生じても全体が機能停止するということなく動作し続けるようなシステム
Elasticsearch においてレプリレーション機能はデフォルト(フリー)で提供されている。

どのように動くか

  • レプリケーションはインデックスレベルで作成される
  • レプリケーションはシャードをコピーすることで行われる
  • コピー元のシャード(データの読み書き双方可能)は、プライマリシャードと呼ばれる
  • プライマリシャードから作成されたレプリカはレプリカシャード(データの読み込み可能)と呼ばれる
  • プライマリシャードとそのレプリカ群はまとめてレプリケーショングループと呼ばれる
  • レプリカシャードはプライマリシャードと同様にリクエスト(データの読み込み可能)を処理可能
  • レプリカの数はインデックスの生成時に構成される(index の設定に依存)
  • レプリカシャードはプライマリシャードと同じノード上に生成されることはない

⇒仮に1台消失しても、他のノード上に残っている状態を確保するため

あるインデックスにおける Replication group A の例

Replication group A

  • Primary Shard A
  • Replica A1
  • Replica A2

Replication group B

  • Primary Shard B
  • Replica B1
  • Replica B2

Primary Shard と Replica Shard の例

2台Node構成で2つの indices が存在している場合。
それぞれの index 用に Primary Shard と Replica Shard が作られ、それぞれ違うノードに保持される。
Node1

  • Primary Shard A
  • Primary Shard B

Node2

  • Replica Shard A
  • Replica Shard B

このケースでも2台同時にノードが壊れてしまった場合は復旧ができない。
いくつの replica shards を保持するべきかはケースによる。
一時的にでもデータのロストが許容される場合、1つの replica shard
一時的にでもデータのロストが許容されない場合、少なくとも2つの は replica shard を確保したい(合計3台のノード上でデータが保持される)

Snapshots

Elasticsearch は snapshots をデータバックアップ用としてサポートしている。
⇒ある時点(snapshots取得時)でのデータ復旧が可能
snapshots は index レベルあるいは、cluster 全体の何れかのレベルで取得可能。

Snapshots があれば replication は必要なのか ?
⇒ snapshots はバックアップのため、replication は高可用性のために必要
⇒ replication はデータロストを防ぐために必要だが、データをクエリを投げる前に戻すことはできない
⇒ 何か大きなクエリを投げる前に snapshots を取得しておき、もし失敗したら復旧に使う

例)
デイリーで snapshot をバックアップ用で取得する。
作業前に手動で snapshot をデータ復旧用に取得する。

Snapshots に対する Replication

replication はノードが壊れた時にでも、データを継続して使用し続けることが出来ることを保証する。
また、 データロストを防ぐのと同時に、スループットの向上にも期待ができる。
例)
ユーザーはあるインデックスに対して、何らかのクエリを大量に投げるとする
⇒ このインデックスはたくさんのクエリ(リクエスト)を受け取る
⇒ ただ、このインデックスは1つの Shardで構成されている、何故ならドキュメントの数が少ないから

どのようにしてスループットを向上させるか?
⇒ 例えば、一つノードを追加する
⇒ Node1 が Primary shard、Node2 が replica shardをもつ状態になる
⇒ この時点で、読み込みリクエストが半分に分散される

スループット向上のためにノードを有効に活用するには、replica shards の数を増やす必要がある
⇒ Replica shards の数を増やすことでスループットの向上が期待できる

Elasticsearch は自動的にクエリを実行するための shard を選択する
⇒ もし同じインデックスにリクエストが同時に3つあったとしても、もし3つの shard (primary + 2 replica) が存在していれば、異なる shard 上で同時に処理することが可能

2つしかノードがないのに、なぜ3つ処理できるのか?
⇒ CPUがマルチコア構成だから、1ノード上で複数のリクエストを同時に処理可能

よって、replication はデータの可用性と、データのスループットの向上に役立つ。
ただし、replica の数を増やすためには、shard を丸ごとコピーできる程のディスクサイズが必要。

kibana 上でインデックスを作成してみる

news という名前の インデックスを作成します。
f:id:blueskyarea:20200623235106p:plain

成功すれば以下のようなレスポンスが返ってきます。
f:id:blueskyarea:20200623235205p:plain

インデックスの状態をチェックすると、ステータスが yellow になっています。
f:id:blueskyarea:20200623235438p:plain

シャードの状態を以下のクエリで確認してみると、
f:id:blueskyarea:20200623235721p:plain

"primary shard(p)" は STARTED になっていますが、”replica shard(r)” は UNASSIGNED になっています。
f:id:blueskyarea:20200623235735p:plain

これはこのクラスターが1台のノードでしか構成されていないため、”replica shard(r)” はどこにも作られていないことを意味します。
従って、インデックスのステータスが yellow になっています。

Python for ループ用のインデックス変数はループ処理後も有効

Java における for ループ用のインデックス変数(以下の例では i ) については、for ループ内でしか有効ではない。

for (int i = 0; i < 10; i++) {
  print(i);
}

Python における for ループ用のインデックス変数 (以下の例では idx) については、forループ後も有効。

>>> array = ["A","B","C"]
>>> array_num = len(array)
>>> for idx in range(array_num):
...     print(array[idx])
... 
A
B
C

idx を参照してみると、ループ処理後の値が入っている。

>>> print(idx)
2

スクリプト言語なので、bash と同じ。

$ for i in {1..9}; do echo ${i};done
1
2
3
4
5
6
7
8
9
$ echo $i
9

Elasticsearch における Sharding

Sharding とは
1. Sharding とは、インデックスを小さなピースに分割するための手段
2. それぞれのピースは Shard として扱われる
3, Sharding はインデックスレベルで行われる(クラスタやノードレベルではない)
4. Sharding によってデータが均等にノードに分けられる
⇒1つのノードに収まらない大きなサイズのインデックスを複数のノードに分けて格納することが可能になる

Shard とは
1. インデックス単位で独立している(厳密には違うらしいが、そのように捉えても差し支えない)
2. Shard は Apache Lucene index である
3. 即ち、Elasticsearch のインデックスは複数の Lucene indices で構成されている
4. Shard はサイズは固定されておらず、ドキュメントの増加に伴い増加する
5. Shard は20億ものドキュメントを保持するケースもある

Sharding する目的
1. Sharding することで、数十億ものドキュメントを1インデックスに保持することがある
2. Sharding することで、データをノードに合わせたサイズにすることができる
3. Sharding することで、検索時のクエリが並行処理することが可能となり、パフォーマンスの向上
⇒複数のシャード上で並行処理が行われる

pri とは primary shard のことで、もし複数の Shard が存在している場合、ここに複数の番号が付与される。
f:id:blueskyarea:20200621223625p:plain

Elasticsearch バージョン7未満まではデフォルトで5つの Shard が生成されていた。
しかしながら、小さなインデックスに対して Shard を作成することは問題(over-sharding)が起こることがあった。

そのため、バージョン7からはデフォルトで1つのShard が生成される。
Shard を増やすためには、Split API を使用する。
Shard を減らすためには、Shrink API を使用する。
⇒どれくらいのドキュメントを格納するかによって、Shardの数を見積する必要がある。

最適な shard の数は?
求めるための公式はない。
⇒多くの要素に依存する
⇒ノードの数やキャパシティ、インデックスの数やサイズ、クエリの数など
もし1インデックスに対して、数百万のドキュメントが予想されるのであれば、5つのShardで様子を見てみるとよいかもしれない。

Elasticsearch に cURL で問い合わせするためのコマンドを kibana から取得

kibana のコンソールから cURL用のコマンドを取得することができます。
”Copy as cURL” をクリックします。
f:id:blueskyarea:20200621220835p:plain

貼り付けてみると、cURL 用のコマンドが取得できていることが分かります。
実運用上ではバッチなどから、cURL でコマンドを実行する必要があるかもしれません。
中には複雑なパラメータを要求するクエリも想定されるため、このように kibana からコマンドを取得出来るのを知っていると便利だと思います。
f:id:blueskyarea:20200621220903p:plain

Kibana から クエリを初めて実行してみる

elasticserarch クラスタの状態などを知るためのコマンド入門。
elasticserarch クラスタの状態を知る _cluster API と node や インデックス情報を取得する _cat API が良く使われそう。
問題が発生したときに主に使われると思われるが、実際に運用しつつ、問題がなくても定期的にチェックしてみて、状態の変化を見てみると面白いかもしれない。

kibana コンソールの dev tool からコンソールを起動する。
f:id:blueskyarea:20200621215246p:plain

elasticserarch クラスタの状態を知る _cluster API

_cluster が APIで、health がコマンドの意味。

elasticserarch クラスタの状態を知るクエリ
GET /_cluster/health

f:id:blueskyarea:20200621215336p:plain

レスポンス

f:id:blueskyarea:20200621215517p:plain

node や インデックス情報を取得する _cat API

※?v を付けることで各情報を意味するヘッダー付きで出力される

ノードを調べる
GET /_cat/nodes?v

f:id:blueskyarea:20200621215611p:plain

レスポンス

f:id:blueskyarea:20200621215631p:plain

インデックスを調べる
GET /_cat/indices?v

f:id:blueskyarea:20200621215708p:plain

レスポンス

f:id:blueskyarea:20200621215733p:plain

pyxel Pyxel Editor を使ってみる

付属するPyxel EditorでPyxelアプリケーションで使用する画像を作成できます。

Pyxel Editor

以下のコマンドで起動します。

pyxeleditor [Pyxelリソースファイル]
pyxeleditor snake

このようなドットを一つ入れただけのファイルを作成してみます。
f:id:blueskyarea:20200620223735p:plain

本体ソース

import pyxel

TILE_SIZE = 8
MAP_WIDTH = 29
MAP_HEIGHT = 23

class Snake:
  def __init__(self):
    pyxel.init( MAP_WIDTH * TILE_SIZE, MAP_HEIGHT * TILE_SIZE, scale=3, fps=20, caption="Snake Game")
    pyxel.load("snake.pyxres")
    pyxel.run(self.update, self.draw)

  def update(self):
    pass

  def draw(self):
    pyxel.bltm(0, 0, 0, 0, 0, MAP_WIDTH, MAP_HEIGHT)

Snake()

実行

実行すると以下のようにドットが描画されたゲームウィンドウが表示されます。
f:id:blueskyarea:20200620223950p:plain