Edited at

Camundaのマルチテナンシー機能に関するメモ

More than 3 years have passed since last update.

昨日リリースされた Camunda 7.5 の目玉機能の一つに、マルチテナンシーがあります。

今後何度も参照することになると思うので、共有定義パターンに関する部分を中心に、自分のためにまとめておきます。


マルチテナンシー / Multi-Tenancy

マルチテナンシーは、一つのCamundaエンジンで複数のテナントを扱うための仕組みだ。

各テナントに対して、データ分離が保証される必要がある。あるテナントのプロセスは他のテナントのプロセスに干渉してはならない。

マルチテナンシーは2つの方法で実現することができる。


  1. テナント毎に一つのプロセスエンジンを使う方法 (One Process Engine Per Tenant)


  2. 全てのテナントを一つのプロセスエンジンで扱い、データをテナント識別子で管理する方法 (Single Process Engine With Tenant-Identifiers)


この2つの方法は、データ分離のレベル、メンテナンス性、スケーラビリティが異なる。2つの方法の組み合わせて使うことも可能だ。


1. テナント識別子を用いて単一のプロセスエンジンでマルチテナントを実現する方法 / Single Process Engine With Tenant-Identifiers

一つのカラムとして保存されるテナント識別子でテナント間のデータ分離は実現される。

テナント毎にデプロイを分ける方法と、単一のデプロイ定義を複数のテナントで共有する方法がある。

大量のテナントを管理するようなケースでは、共有定義を使うと、デプロイの管理をシンプルにすることができる。

multi-tenancy-tenant-identifiers.png


1.1. テナント毎のデプロイ定義 / Deploy Definitions for a Tenant

テナント識別子によってテナント毎にデプロイ定義を設定することができる。デプロイ定義に付与されたテナント識別子はプロセス定義やインスタンスなど全ての定義に伝播する。

デプロイ定義にテナント識別子が付与されなかった場合は、デプロイとその定義は全てのテナントに紐付くことになる。


1.1.1. Java APIを用いたテナント毎のデプロイ定義 / Specify the Tenant Identifier via Java API

(省略)


1.1.2. processes.xmlを用いたテナント毎のデプロイ定義 / Specify the Tenant Identifier via Deployment Descriptor

(省略)


1.1.3. Spring設定ファイルを用いたテナント毎のデプロイ定義 / Specify the Tenant Identifier via Spring Configuration

(省略)


1.1.4. テナント別のバージョニング / Versioning of Tenant-Specific Definitions

バージョニングはテナント毎に独立している。例えば、一つの新しいデプロイ定義がテナントAとテナントBにデプロイされた場合、両方のデプロイがバージョン1となる。


1.2. テナント間のデータ分離 / Query Data of a Tenant

テナントIDを指定することによって、テナントに属するデータを取得することができる。テナント識別子は1つ以上指定することができる。テナント識別子が指定されない場合は、全てのテナントに属するデータを取得することができる。

1.4. Transparent Access Restrictions for Tenants が影響しうることに注意。


1.2.1. テナントを指定してデプロイ情報を取得する方法 / Query Deployments of a Tenant

(省略)


1.2.2. テナントを指定してプロセス定義を取得する方法 / Query Definitions of a Tenant

(省略)


1.3. テナントを指定してコマンドを実行する方法 / Run Commands for a Tenant

一つのデプロイ定義を複数テナントにデプロイした場合は、プロセスの開始などのコマンドを実行する際にテナント識別子を指定する必要がある。

1.4. Transparent Access Restrictions for Tenants が影響しうることに注意。


1.3.1. Create a Process Instance

(省略)


1.3.2. Correlate a Message

(省略)


1.3.3. Send a Signal

(省略)


1.3.4. Create a Case Instance

(省略)


1.3.5. Evaluate a Decision Table

(省略)


1.4. Transparent Access Restrictions for Tenants

別のアプリケーションにCamundaを組み込んで使っている場合、Camundaに対するAPIコールの都度テナントIDを渡す必要があると面倒だ。

このように毎回テナントIDを指定することを避けるため、identityService.setAuthenticationを使うことができる。

下記の様に、setAuthenticationclearAuthenticationの間に実行される処理は、setAuthenticationの引数として渡されたテナントに対してのみ実行される。

try {

identityService.setAuthentication("mary", asList("accounting"), asList("tenant1"));

// All api calls executed here have "tenant1" transparently set as tenantId

}
finally {
identityService.clearAuthentication();
}


1.4.1. Query Example

(省略)


1.4.2. Task Access Example

(省略)


1.4.3. Getting a user’s Tenant Ids from the Identity Service

(省略)


1.4.4. Camunda Rest API and Web Applications

(省略)


1.4.5. Disable the Transparent Access Restrictions

(省略)


1.4.6. Access all Tenants as Administrator

(省略)


1.5. 一つのデプロイ定義を複数のテナントで共有する方法 / Shared Definitions for all Tenants

セクション「1.1. テナント毎のデプロイ定義」で、テナント毎に別々のプロセス定義やディシジョン定義をデプロイする方法を説明した。これによって、プロセス定義やディシジョン定義を特定のテナントだけに参照可能で他のテナントからは見えないようにすることができた。これは各テナントが別々のプロセスやディシジョンを持つ場合には便利だ。しかし、全てのテナントが同じ定義を共有しなければならないケースも多い。

このような状況においては、定義を一度だけデプロイし、全てのテナントがその定義を参照可能であることが求められる。

そして、インスタンスが生成されたときには、そのインスタンスを生成したテナントと管理者だけがそのインスタンスを参照可能であるべきだ。

こういった要求は「共有定義(Shared Definitions)」と呼ばれる利用パターンで実現することができる。

利用パターンという用語は、「Camundaそれ自体の機能ではないが、要求される振る舞いを実現するための決まったやり方」ということを意味している。


1.5.1. 共有定義のデプロイ / Deploy a Shared Definition

共有定義のデプロイは、テナントIDをデプロイに付与しない、“ごく普通”のデプロイです。

repositoryService

.createDeployment()
.addClasspathResource("processes/default/mainProcess.bpmn")
.addClasspathResource("processes/default/subProcess.bpmn")
.deploy();


1.5.2. デプロイ定義を共有している場合のクエリ / Include Shared Definitions in a Query

デプロイ定義を共有している場合も、プロセス定義を抽出するときなどにテナントIDを指定することができる。

repositoryService.createProcessDefinitionQuery()

.tenantIdIn("someTenantId")
.includeProcessDefinitionsWithoutTenantId()
.list();


1.5.3. 共有定義の初期化 / Instantiate a Shared Definition

通常、新しくプロセスインスタンスを生成する(プロセスを開始する)時、そのプロセス定義のテナントIDがプロセスインスタンスに自動的に付与される。

しかし共有定義を使用する場合、プロセス定義はテナントIDを持たないから、プロセスインスタンスにテナントIDが付与されるない。

プロセスインスタンスにプロセスを開始したユーザーのテナントを割り当てるには、TenantIdProvider SPI(Service Provider Interface) の実装を提供する必要がある。

TenantIdProviderはプロセス定義・ケース定義・ディシジョン定義のインスタンスが生成された時にコールバックを受け取り、生成されたインスタンスにテナントIDを付与する。

下記の例では、現在のユーザー権限に応じてテナントIDをインスタンスに付与する方法を示しています。

public class CustomTenantIdProvider implements TenantIdProvider {

@Override
public String provideTenantIdForProcessInstance(TenantIdProviderProcessInstanceContext ctx) {
return getTenantIdOfCurrentAuthentication();
}

@Override
public String provideTenantIdForCaseInstance(TenantIdProviderCaseInstanceContext ctx) {
return getTenantIdOfCurrentAuthentication();
}

@Override
public String provideTenantIdForHistoricDecisionInstance(TenantIdProviderHistoricDecisionInstanceContext ctx) {
return getTenantIdOfCurrentAuthentication();
}

protected String getTenantIdOfCurrentAuthentication() {

IdentityService identityService = Context.getProcessEngineConfiguration().getIdentityService();
Authentication currentAuthentication = identityService.getCurrentAuthentication();

if (currentAuthentication != null) {

List<String> tenantIds = currentAuthentication.getTenantIds();
if (tenantIds.size() == 1) {
return tenantIds.get(0);

} else if (tenantIds.isEmpty()) {
throw new IllegalStateException("no authenticated tenant");

} else {
throw new IllegalStateException("more than one authenticated tenant");
}

} else {
throw new IllegalStateException("no authentication");
}
}
}

TenantIdProviderを使うためには、プロセスエンジンの設定に登録しておく必要がある。下記はcamunda.cfg.xmlを使った例だ。

<beans>

<bean id="processEngineConfiguration" class="org.camunda.bpm.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- ... -->

<property name="tenantIdProvider" ref="tenantIdProvider" />
</bean>

<bean id="tenantIdProvider" class="org.camunda.bpm.CustomTenantIdProvider">
</beans>

共有プロセスエンジンの場合、TenantIdProviderProcess Engine Plugin を介して設定することもできる。


1.5.4. テナント固有の振る舞いをコールアクティビティを使って実現する方法 / Tenant-specific behavior with Call Activities

ここまでで、テナントが同じプロセス定義を持つ場合、共有定義が有効なパターンであることを見てきた。

その優位性は同じプロセス定義をテナント毎にデプロイする必要がない点にある。

しかし現実には、複数のテナントが大部分のプロセス定義を共有しているが、幾つかのテナントは独自のバリエーションを持っているという状況も往々にしてある。

こういった要求は一般に、テナント固有の振る舞い(プロセス)を別に定義しておいて、コールアクティビティを使って呼び出すことによって解決される。

テナント固有のディシジョンロジック(例えばデシジョンテーブルなど)を利用するために、ビジネスルールタスクを使うことも一般的だ。

これを実現するために、コールアクティビティやビジネスルールタスクは、現在のプロセスインスタンスのテナントIDに基いて正しいプロセス定義を選択する必要がある。


参考リンク


2. テナント毎にプロセスエンジンを用意することによってマルチテナントを実現する方法 / One Process Engine Per Tenant

multi-tenancy-process-engine.png

(省略)


2.1. Configure the Process Engines

(省略)


2.2. Deploy Definitions for a Tenant

(省略)


2.3. Access the Process Engine of a Tenant

(省略)