Resource Group in Presto


この記事はDistributed computing Advent Calendar16日目の記事です。

PrestoはFacebookがメインとなって開発しているオンメモリでの処理を特徴とする分散クエリエンジンです。各タスクの中間データをメモリにのせたまま処理するためHiveなどと比べて高速に大きなデータに対して処理を行うことができます。Prestoは現在でも活発に開発が進められており最新のバージョンは0.160となります。この2ヶ月程でもminor versionが5つくらい出ているので安定バージョンがなかなか得られない代わりにbug fixやコミュニティからのfeedback迅速に取り入れられています。

今回はまだexperimentalなものではありますが、新しい機能であるPrestoのResource Groupについて書きたいと思います。

Resource Groupとは

Resource Groupはグループやアカウント毎に利用できるリソースに制限をかけることのできる機能です。これまでもPrestoではquery.max-memoryなどでクエリ毎、node毎に利用できるメモリは制限できたのですが、アカウントやsource propertyに従って柔軟に制限を設定することは簡単ではありませんでした。またアカウントの同時実行数などもかけることはできませんでした。これらをPrestoの機能として提供するものがResource Groupです。詳細はResource Group Configurationに記載されています。

Resource Groupの使い方

設定は形式はJSONファイルで下記の用に記載します。このファイルの場所はresource-groups.properties内のresource-groups.config-file propertyで指定します。

{
  "rootGroups": [
    {
      "name": "global",
      "softMemoryLimit": "80%",
      "maxRunning": 100,
      "maxQueued": 1000,
      "schedulingPolicy": "weighted",
      "jmxExport": true,
      "subGroups": [
        {
          "name": "adhoc_${USER}",
          "softMemoryLimit": "10%",
          "maxRunning": 2,
          "maxQueued": 1,
          "schedulingWeight": 9,
          "schedulingPolicy": "query_priority"
        },
        {
          "name": "pipeline",
          "softMemoryLimit": "20%",
          "maxRunning": 5,
          "maxQueued": 100,
          "schedulingWeight": 1,
          "jmxExport": true,
          "subGroups": [
            {
              "name": "pipeline_${USER}",
              "softMemoryLimit": "10%",
              "maxRunning": 1,
              "maxQueued": 100,
              "schedulingPolicy": "query_priority"
            }
          ]
        }
      ]
    },
    {
      "name": "admin",
      "softMemoryLimit": "100%",
      "maxRunning": 100,
      "maxQueued": 100,
      "schedulingPolicy": "query_priority",
      "jmxExport": true
    }
  ],
  "selectors": [
    {
      "user": "bob",
      "group": "admin"
    },
    {
      "source": ".*pipeline.*",
      "group": "global.pipeline.pipeline_${USER}"
    },
    {
      "group": "global.adhoc_${USER}"
    }
  ],
  "cpuQuotaPeriod": "1h"
}

まず大きくわけて3つの項目があります。

  • rootGroups: Resource Groupは上記のように入れ子に記述することができ、すべてのResource GroupはこのrootGroupsの下に置かれます。
  • selectors: すべてのクエリはどれかのResource Groupに関連づけられますが、このときのマッチングルールを記載するのがこのselectorsです。詳細は後述します。
  • cpuQuotaPeriod: CPUに関する制限を計算するときの基準となる値です。詳細は後述します。

ひとつずつ見ていきます。

rootGroups

各Resource Groupは入れ子にすることができ、以下の要素を持ちます。

名前 意味
name Resource Groupにつける一意な名前。selectorで使う
maxQueued queued状態のクエリの最大数
maxRunning running状態のクエリの最大数
softMemoryLimit 利用できる最大メモリ。容量(e.g. 10GB)かクラスタ全体からの割合(e.g. 10%)で指定。これを超えてからsubmitされたクエリはqueued状態になる。
softCpuLimit cpuQuotaPeriodで指定された時間内に利用できる最大CPU時間。これを超えると実行可能な最大クエリ数が減らされる
hardCpuLimit cpuQuotaPeriodに利用できる最大CPU時間。これを超えると新しく入ってきたクエリはqueued状態になる。
schedulingPolicy queued状態のクエリがどのように実行されるかを指定するパラメタ。fair, weighted,query_priorityの3種類がある
schedulingWeight 同階層のサブグループと比較した場合も重み
jmxExport JMXでResource Groupのメトリクスを出すかどうか
subGroups サブグループのリスト

schedulingPolicyで指定されるpolicyそれぞれは

  • fair: FIFO.クエリ、サブグループともに早いもの勝ちで処理されていく
  • weighted: query_priorityに比例した形で確率的に次のクエリ、サブグループが選ばれていく
  • query_priority: 確率的にではなくquery_priorityが大きい順に厳密に次のクエリ、サブグループが選ばれていく

となります。クエリが所属するgroupはleafとなっているgroup、つまりsubGroupsを持たないものになります。またある設定値がsubGroupsがoverrideされてない場合は親の設定値が使われます。

selectors

submitされたクエリはselectorによって所属するgroupを決められます。selectorが持てる設定値は下記の3つです。

  • user: session propertyのuserにマッチするような正規表現
  • source: session propertyのsourceにマッチするような正規表現
  • group: マッチしたクエリが所属するgroup名

つまりselectorはクエリが所属するgroupを決めるためのマッチングロジックを記述するためのものになります。マッチするものが複数ある場合には先頭のselectorが優先されます。

ちなみに内部的にはselectorはSelectionContextというクラスタが担っているのですが、これらに加えて認証情報とクエリの優先度も持っているので将来的にこれらでgroupをマッチングさせることもできるかもしれません。

動的なResource Group

設定ファイルは管理者が生成するファイルですが、ユーザや部署毎に異なるResource Groupを作りたい場合もあると思います。この場合同じような設定を並べると、いちいち設定をする必要があり面倒です。実は上記の例でお気づきかもしれませんが、Resource Groupの名前、selectorにはplace holder的に変数を使うことができます。例えば各account毎にscheduledとadhocクエリを使い分けたい場合。下記のように書くことで新規ユーザがクエリを投げた場合でもsourceを指定することでadhoc, scheduledの両Resource Groupを利用することができます。対応するResource Groupへの初めてのクエリの場合には自動的に作成されます。

{
  "rootGroups": [
    {
      "name": "global",
      "schedulingPolicy": "weighted",
      "subGroups": [
        {
          "name": "adhoc_${USER}",
          //
        },
        {
          "name": "scheduled_${USER}",
          //
        }
      ]
    }
  ],
  "selectors": [
    {
      "source": "scheduled",
      "group": "global.scheduled_${USER}"
    },
    {
      "source": "adhoc",
      "group": "global.adhoc_${USER}"
    }
  ]
}

このplace holderに利用できる変数はselectorで利用する${USER}${SOURCE}の2種類があります。

Last but not least

大事なことですが、Resource Groupはまだexperimentalな機能として提供されています。そのためdefaultでは有効になっておらず利用するためには下記のpropertyをONにする必要があります。

experimental.resource-groups-enabled=true

私が所属するTreasure Dataでも現在このResource Groupの評価、試験を行っており細かいリソース制限が簡単にかけられることを確認しています。是非試してみてください。

Learn more in Resource Groups