ATOM
Qmonus SDKは、オブジェクト指向プログラミングを実現するための汎用データモデルとしてATOM(Asynchronous Tree propagational Object Model)
を提供しています。Frontalが提供するクラスエディタ上でフィールドやメソッド、ステートマシンなどを記述することですぐにクラスとして利用することができます。
Note
Pythonネイティブな通常のクラスを記述して利用したい場合は、プラグインモジュールで定義して利用することができます。
ATOMには、以下の特徴があります。
- 自律的に駆動するステートマシンを持つことができる。
- ステート遷移は、トランザクションサービスによって管理され、一貫性が保証される。
- 参照フィールドの値変化が自律伝搬する。
- メソッド呼び出しが自律伝搬する。
- 継承メカニズムがあり、サブクラス化できます。
- シナリオのグローバル変数として利用できる。
- 永続化モードでは、データベーススキーマ自動生成とORM(Object-Relational Mapping)が自動で行われ、CRUDメソッドがバンドルされる。
- フィールド、メソッド、およびステートマシンは、開発者が自由に定義できます。
ATOM定義が保存されると、永続化モードやAPI生成モードに従って、永続化するデータベーステーブルやCRUDするためのRestful APIが自動的に生成され、すぐに利用することができます。
ATOMで定義する情報
メタ情報定義
メタ情報定義では、ATOMクラス名や各種動作モードを定義します。
属性 | 概要 | 備考 |
---|---|---|
category |
ATOMが属する分類を指定してください。 | この情報はクラスエディタでの階層表示のためのタグとしてのみ使用され、ATOMの動作にはなんら影響しません。 |
name |
ATOMの名前を指定します(クラス名と同義)。 | ユニークである必要があります。 |
extends |
継承するスーパーATOMを列挙します。 | - |
persistence |
永続化モードを指定します。 | True の場合、ATOM定義を保存するとクラス構造に対応するデータベーステーブルが自動的に生成され、データベースに対するCRUDの組込みメソッドが実装されます。デフォルトはTrue です。False の場合は、ATOMインスタンスをデータベースに永続化することはできません。クラスメソッドで機能だけを提供するようなファンクションクラスの場合は、False を利用すると良いでしょう。 |
abstract |
抽象クラスモードを指定します。 | デフォルトはFalse です。True の場合、インスタンス化は可能ですが、永続化することはできません。また、後述するapi_generation がTrue の場合、生成されるAPIはそのクラスのサブクラスをCRUDできるものになります。 |
api_generation |
API自動生成モードを指定します。 | API自動生成モードでは、ATOMをCRUDするためのRestful APIが自動的に生成されます。デフォルトはTrue です。ATOMのCRUDをシナリオの中で行うケースが多く見受けられるため、将来デフォルト値はFalse に変更となる可能性があります。 |
scope |
API自動生成モードで生成されるAPIルーティングのスコープを指定します。 | デフォルトはsecure です。 |
api_basepath |
API自動生成モードで生成されるAPIルーティングの基底パスを指定します。 | デフォルトは/apis です。 |
api_auto_response |
API自動生成モードで生成されたCUD要求に対して自動応答するモードを指定します。 | デフォルトはTrue です。False の場合、開発者はCUDに対応する特別なメソッドをオーバーライドして応答を作成する必要があります。 |
fields | - | フィールドはATOMクラスの属性定義です。各項目についての詳細は後述します。 |
instance field | ATOMインスタンスの識別子フィールドです。 | Qmonusメタクラスによって自動的に割り当てられるため、開発者が設定することはできません。 |
identifier field | ATOMインスタンスを一意に識別するIDを定義するフィールドです。 | 定義されていない場合、内部割当されるinstance field が唯一のIDになります。永続化モードでは、データベーステーブルの本フィールドにユニークインデックスが自動付与されることに注意してください。 |
local fields | ATOMインスタンスが保持するローカルフィールドです。 | 詳細は後述のlocal fieldsをご覧ください。 |
reference fields | 異なるATOMクラスのフィールドを外部参照するフィールドです。 | 詳細は後述のlocal fieldsをご覧ください。 |
magic fields | 指定した値でアクセスした場合、特殊な動きをするフィールドです。 | 詳細は後述のmagic fieldsをご覧ください。 |
methods | - | クラス, インスタンスメソッドを実装定義することができます。各項目についての詳細は後述します。 |
class methods | クラスメソッドを実装定義します。 | 詳細は後述のclass methodsをご覧ください。 |
instance methods | インスタンスメソッドを実装定義します。 | 詳細は後述のinstance methodsをご覧ください。 |
fields
local fields
ATOMインスタンスが保持するローカルフィールドです。ATOMインスタンスが保持するローカルフィールドです。 基本データ型に加え、他のATOMやATOMのリストを保持することが可能です。また、状態属性の場合は、状態マシンを設定することができます。
設定項目 | 必須 | 初期値 | 説明 |
---|---|---|---|
field_name | ◯ | None | フィールド名を指定します。Pythonの識別子として利用できる文字列である必要があります。 |
field_type | ◯ | string | フィールドの型を指定します。フィールドに設定可能な基本型は以下の通りです。これらに加えて定義されている他のATOMやATOMのリストが指定できます。string integer number boolean DateTime object array MU array<MU> |
field_alias | - | None | フィールドの別名を指定します。field_aliasを設定した場合、後述する組込メソッド(localfields yaml_format api_format dictionary )の結果はフィールド名ではなくエイリアスで出力されます。 |
field_dbtype | - | None | フィールドのデータベースカラムの型を指定します。ATOMはデータベーススキーマの生成にModelサービスを利用しています。Docs » Scenario » モデル を参照してください。 |
field_length | - | None | フィールドの値長を指定します。 |
field_persistence | ◯ | True | フィールドの永続化モードを指定します。False の場合は本フィールドはデータベースカラムとして生成されません。 |
field_nullable | ◯ | True | フィールドの値にNullを許容するかを指定します。 |
field_immutable | ◯ | False | フィールドの値が不変かを指定します。True の場合、本フィールドの値変更は許可されません。 |
field_unique | ◯ | False | フィールドが複合ユニークインデックスの対象かを指定します。 |
field_format | - | None | フィールドのフォーマットをjsonschemaもしくは正規表現で指定します。 |
field_default | - | None | フィールドのデフォルト値を指定します。 |
field_enum | - | None | フィールドに代入できる値がリテラル候補値である場合にリテラル列挙型で指定します。 |
field_fsm | - | None | フィールドが状態属性である場合に状態マシンを定義します。 |
field_metadata | - | {} | フィールドに任意のメタ情報を定義します。メタ情報はdict型のデータを設定してください。localfields 組込メソッドではメタ情報によるフィルタリングが利用できます。ATOMインスタンスをAPIのリクエストbodyに変換する際に役立ちます。 |
local fields - field_fsm
状態マシン
ローカルフィールドはfield_fsmを利用することで状態マシンを定義することができます。
状態マシンは、有限数の状態値をキーとして使用するマップによって管理され、各状態値は、以下の属性を保有します。
属性 | 必須 | 説明 |
---|---|---|
status | ◯ | 状態名です。 |
execution_method | ◯ | 状態を確定するためのアクションです。 |
success_transition | - | アクションに成功したら次に自律遷移する状態です。 |
failure_transition | - | アクションに失敗したら次に自律遷移する状態です。 |
status_value | - | アクションが成功した場合の状態値です。指定しない場合は、status が状態値となりますがこの設定で収束値を強制できます。シングルステートで更新系の状態を管理したい場合などに利用します。 |
pre_statuses | - | アクション実行時点の事前状態を強制するオプションです。状態名をリストで管理します。 |
status_type | - | FrontalのATOM編集画面に表示される状態遷移図にヒントを与えるための属性です。ランタイムにはなんら影響を及ぼさないパラメータです。 |
field_fsm:
pending_create:
execution_method: create
success_transition: active
failure_transition: pending_delete
active:
execution_method: create_confirm
failure_transition: pending_delete
pending_delete:
execution_method: delete
success_transition: deleted
deleted:
execution_method: delete_confirm
pending_update:
execution_method: update
success_transition: updated
failure_transition: pending_cancel_update
updated:
execution_method: update_confirm
failure_transition: pending_cancel_update
status_value: active
cancel_update:
execution_method: cancel
success_transition: updated
reference fields
異なるATOMクラスのフィールドを外部参照するフィールドです。
ATOMインスタンスツリー上で参照先のオブジェクトフィールドが変化すると自動伝播されてきます。データベース定義において本フィールドには外部キーが設定されます。
設定項目 | 必須 | 初期値 | 説明 |
---|---|---|---|
field_name | ◯ | None | フィールド名を指定します。Pythonの識別子として利用できる文字列である必要があります。 |
field_type | ◯ | string | フィールドの型を指定します。フィールドに設定可能な基本型は以下の通りです。これらに加えて定義されている他のATOMやATOMのリストが指定できます。string integer number boolean DateTime object array MU array<MU> |
field_dbtype | - | None | フィールドのデータベースカラムの型を指定します。ATOMはデータベーススキーマの生成にModelサービスを利用しています。Docs » Scenario » モデル を参照してください。 |
field_length | - | None | フィールドの値長を指定します。 |
field_persistence | ◯ | True | フィールドの永続化モードを指定します。False の場合は本フィールドはデータベースカラムとして生成されません。 |
field_unique | ◯ | False | フィールドが複合ユニークインデックスの対象かを指定します。 |
field_metadata | - | {} | フィールドに任意のメタ情報を定義します。メタ情報はdict型のデータを設定してください。localfields 組込メソッドではメタ情報によるフィルタリングが利用できます。ATOMインスタンスをAPIのリクエストbodyに変換する際に役立ちます。 |
ref_class | ◯ | None | 参照するATOMクラスを指定します。 |
ref_class_field | ◯ | None | 参照するATOMクラスのフィールド名を指定します。 |
magic fields
マジックフィールドは、field_name
でアクセスした場合は、ローカルフィールドと同等の振る舞いをしますが、field_magic_name
でアクセスした場合は、特殊な動きをするフィールドです。field_magic_name
を参照した場合は、そのタイミングで動的に値を導出
します。前述した参照フィールドは、包含関係上の上位オブジェクトから自身に対しての最短パスでのみ値が伝播されるため、上位オブジェクトが包含する別のオブジェクトのフィールドを得ることはできません。
マジックフィールドは、同じルートオブジェクト配下にコンポジットされているATOMインスタンスであればどこに存在していても動的に参照して導出できるフィールドです。これは、同階層にいる直接関係を持たないインスタンスをサーチして情報を得る
必要がある場合に役立ちます。
設定項目 | 必須 | 初期値 | 説明 |
---|---|---|---|
field_name | ◯ | None | フィールド名を指定します。Pythonの識別子として利用できる文字列である必要があります。 |
field_type | ◯ | string | フィールドの型を指定します。フィールドに設定可能な基本型は以下の通りです。これらに加えて定義されている他のATOMやATOMのリストが指定できます。string integer number boolean DateTime object array MU array<MU> |
field_dbtype | - | None | フィールドのデータベースカラムの型を指定します。ATOMはデータベーススキーマの生成にModelサービスを利用しています。Docs » Scenario » モデル を参照してください。 |
field_immutable | ◯ | False | フィールドの値が不変かを指定します。True の場合、本フィールドの値変更は許可されません。 |
field_length | - | None | フィールドの値長を指定します。 |
field_nullable | ◯ | True | フィールドの値にNullを許容するかを指定します。 |
field_enum | - | None | フィールドに代入できる値がリテラル候補値である場合にリテラル列挙型で指定します。 |
field_persistence | ◯ | True | フィールドの永続化モードを指定します。False の場合は本フィールドはデータベースカラムとして生成されません。 |
field_metadata | - | {} | フィールドに任意のメタ情報を定義します。メタ情報はdict型のデータを設定してください。localfields 組込メソッドではメタ情報によるフィルタリングが利用できます。ATOMインスタンスをAPIのリクエストbodyに変換する際に役立ちます。 |
ref_class | ◯ | None | 動的に参照するATOMクラスを指定します。 |
ref_class_field | ◯ | None | 動的に参照するATOMクラスのフィールド名を指定します。*(アスタリスク) を指定した場合は、動的にマッチングしたATOMインスタンス自体を取得できます。 |
ref_class_match_field | - | 動的に参照したATOMインスタンスに対してマッチングするフィールド名を指定します。指定しない場合は、field_nameが自動適用されます。 | |
field_magic_name | ◯ | None | 導出属性としてのフィールド名を指定します。 |
field_magic_script | - | 動的なインスタンスサーチによって取得された値をカスタムスクリプトで加工できます。 |
Note
マジックフィールドの挙動は複雑で分かりにくいものです。field_name
でアクセスした場合は、ローカルフィールドのように代入されている値を返却しますが、field_magic_name
でアクセスした場合には、代入されている値をキーに動的に参照したいインスタンスをサーチして狙ったフィールドの値を取得します。
インスタンスのサーチは、自身が所属しているインスタンスツリー全体に対して、ref_class
のインスタンスを検出し、該当インスタンスのref_class_match_field
の値がマジックフィールドに代入されている値と一致するものを検索していきます。一致するインスタンスを発見したら参照インスタンスとみなし、ref_class_field
の値を取得します。さらにfield_magic_script
が設定されていれば取得した値をカスタムスクリプトに渡して実行し、その結果を値として返却します。
クラウドサービスにおけるIaaSやPaaSのオーケストレーションを実現するためにマイクロサービスのラッパーをATOMで作成するケースがあります。これらはATOMのコンポジット構成でCRUDを行うメソッドを実装、状態マシンを適切に設定し、ATOMのメソッド伝搬機構を利用することで宣言的にパラメータを与え、リソースツリーをターゲットサービス上に生成することができます。しかしながらリソースツリーをトップダウンで処理するだけではAPIのパラメータを補完仕切れないケースがあります。これは、リソース生成時に関連オブジェクト(兄弟や従兄弟に当たるようなリソースインスタンス)の情報を指定しないと生成できないリソースも存在するからです。そのようなユースケースにおいても特殊なプログラミングを避け、宣言的コンフィギュレーションベースで実現するためにマジックフィールドが提供されています。
Tip
マジックフィールドはfield_unique
モードを指定できません。
field_unique
モードはローカルフィールドと参照フィールドにのみ設定することができ、複合ユニークインデックスを生成します。
ただし、マジックフィールドについては導出フィールドでありデータベースに保存されないため設定することができません。
field_metadata
ATOMオブジェクトのフィールドには単一階層のKeyValue辞書形式でメタデータを設定することができます。
メタデータは組込みメソッドlocalfields
を使用することで、metadata
キーワード引数によりフィルタリングされたディクショナリを取得することができます。
Tip
POST/PUTキーとブール値のメタデータは特別なメタデータとして扱われ、API自動生成処理に暗黙的に使用されます。
これにより、開発者はPOSTで提供されるものとPUTで提供されるものをカスタマイズすることができます。
フィールドにメタデータを定義したサンプルATOM
category: sample
name: Tenant
attributes:
identifier:
field_name: tenantId
field_type: string
field_persistence: true
field_immutable: true
field_metadata:
POST: false
PUT: false
field_default: uuid.uuid1().hex
local_fields:
- field_name: name
field_type: string
field_persistence: true
field_nullable: true
field_immutable: false
field_unique: false
field_metadata:
POST: true
PUT: false
- field_name: region
field_type: string
field_persistence: true
field_nullable: true
field_immutable: false
field_unique: false
field_metadata:
POST: true
PUT: false
- field_name: description
field_type: string
field_persistence: true
field_nullable: true
field_immutable: false
field_unique: false
field_metadata:
POST: true
PUT: true
ref_fields: []
methods:
class_methods: []
instance_methods: []
persistence: true
abstract: false
api_generation: false
上記のATOMのメタデータフィルタの使用例
>>> t = atom.Tenant(name="sampleTenant", region="jp", description="simple Tenant")↵
... print(t.localfields())↵
... ↵
↵
{'tenantId': '13a1a528339e11eb89d7acde48001122', 'name': 'sampleTenant', 'region': 'jp', 'description': 'simple Tenant'}
>>> print(t.localfields(metadata={"POST": True}))↵
... ↵
↵
{'name': 'sampleTenant', 'region': 'jp', 'description': 'simple Tenant'}
>>> print(t.localfields(metadata={"PUT": True}))↵
... ↵
↵
{'description': 'simple Tenant'}
>>>
Note
ATOMの履歴管理機能
ATOMのローカルフィールドの変更は8世代の履歴管理が暗黙的に行われています。履歴管理は、最初のsaveメソッドが実行されて永続化された後に開始されます。履歴情報を使用してオブジェクト属性をロールバックできます。ロールバックは、discard_changes
プロパティメソッドを実行することで実現できます。履歴は_changes
というインビジブルフィールドで管理され、JSONシリアライズの対象となるため、トランザクションサーバにもバックアップされます(変更のロールバックなどで利用できる)。save
メソッドによるデータベースへの保存対象にはならないため、メモリ空間とトランザクション空間にのみ存在する履歴情報であることに注意してください。
>>> t = atom.Tenant(name="sample", description="1st commit")↵
... await t.save()↵
>>> print(t._changes)↵
... ↵
↵
{}
>>> t.description = "2nd commit"↵
... print(t.description)↵
... print(t._changes)↵
... ↵
↵
2nd commit
{'description': ['1st commit']}
>>> t.description = "3rd commit"↵
... print(t.description)↵
... print(t._changes)↵
... ↵
↵
3rd commit
{'description': ['1st commit', '2nd commit']}
>>> t.discard_changes↵
... print(t.description)↵
... print(t._changes)↵
... ↵
↵
2nd commit
{'description': ['1st commit']}
>>> t.discard_changes↵
... print(t.description)↵
... print(t._changes)↵
... ↵
↵
1st commit
{'description': []}
>>>
methods
メソッドを定義することができます。設定項目は共通です。
class method
クラスメソッドを実装定義します。ATOMのクラスメソッドは、通常のPythonにおけるクラスメソッドと同じでメソッド本体を自由に実装できます。インスタンスツリーのようなクラスを横断する概念はないため、ATOMの特徴であるメソッド伝搬の概念も適用されないピュアなクラスメソッドです。ただし、メソッドが実行される名前空間は、Qmonus SDKのビルトイン空間となるため、Docs » リファレンス » ビルトインオブジェクト
に記載されている組込オブジェクトが利用できます。
instance method
インスタンスメソッドを実装定義します。ATOMのインスタンスメソッドは、通常のPythonにおけるインスタンスメソッドとは異なり、インスタンスツリー上で伝搬する特性を与えることができます。
設定項目 | 必須 | 初期値 | 説明 |
---|---|---|---|
propagation_mode | ◯ | True | メソッド伝搬有効化モードです。True の場合、包含するインスタンスに実装されている同一名称のメソッドを呼び出して伝搬します。 |
topdown | ◯ | True | トップダウンでメソッド実行するかボトムアップにメソッド実行するかを指定します。True の場合、自身のメソッド実行が完了してから子のメソッドを実行します。False の場合は、子のメソッド実行が完了してから自身のメソッドを実行します。リソースツリーの生成時は親から、削除時は子から実行することで依存解決ができます。 |
multiplexable_number | ◯ | 1 | ATOMリスト型のフィールドに格納されている複数のインスタンスに対してメソッド呼び出しを多重実行できます。 |
field_order | ◯ | ascend | 伝搬する順序を指定します。ascend descend parallel が指定できます。ascend : fieldの定義昇順順でシリアルに伝搬します。descend : fieldの定義降順でシリアルに伝搬します。parallel : 全ての伝搬フィールドに並列で伝搬します。リソースツリーの生成時は昇順、削除時は降順となるケースが多いでしょう。 |
timeout | - | 300 | メソッドのタイムアウトを指定します。 |
auto_rollback | ◯ | True | メソッドの実行に失敗した場合に自動ロールバックを行うかを指定します。 メソッドの実行が正常に完了すると状態マシンに従い、 success_transition へ自律的に遷移していきますが、メソッドの実行が失敗した場合は、トランザクションを一旦中断し、処理を停止します。True の場合は、中断すると同時にトランザクションのcancel を起動し、状態マシンに従ってfailure_transition への遷移が継続します。 |
Caution
Qmonus SDKのランライムは、asyncio
ベースのIOループでシングルスレッドで動作しています。非同期処理を行うメソッドにおいては、async/await
キーワードの記述漏れに十分注意してください。尚、インスタンスメソッドの伝搬は、非同期メソッドに対してのみ行われる点に注意してください。
特殊メソッド
ATOMでは、以下の組込み演算子は、オーバーライドされています。
特殊メソッド | 使用イメージ | 説明 |
---|---|---|
__iter__ |
[print(i) for i in atomObj] |
オブジェクトの属性値をイテレーションします。 |
__repr__ |
print(atomObj) |
オブジェクトの文字列表現を返します。 |
__iadd__ |
atomObj+=response.body |
JSON化できるオブジェクト( dict str bytes )とのインプレース加算は、一致するキーに値をマージします。 |
__eq__ |
print(atomObj1==atomObj2) |
クラス及び、すべての属性値が一致する場合は真を返します。 |
__ne__ |
print(atomObj1!=atomObj2) |
クラス及び、すべての属性値が一致していない場合は真を返します。 |
__contains__ |
print(atomObj2 in atomObj1) |
オブジェクトが含まれている場合は真を返します。 |
ATOMに実装されているプロパティメソッド
ATOMでは、以下のプロパティメソッドが実装されています。
プロパティメソッド | 説明 |
---|---|
dictionary | ATOMインスタンスを辞書型(dict)に変換して返却します。 |
api_format | API自動生成モードで生成されるGET APIの表現形式を辞書型(dict)に変換して返却します。 |
yaml_format | api_format をyaml変換した文字列を返却します。 |
discard_changes | ATOMインスタンスのローカルフィールド値を1世代ロールバックします。 |
referring | ATOMインスタンスが参照されているかどうかをbool値で返却します。プラグイン開発者が使用することはありません。 |
transaction_endpoint | ATOMインスタンスが利用するトランザクションのエンドポイントを返却します。プラグイン開発者が使用することはありません。 |
xcommand | ATOMインスタンスに現在指令されているトランザクションコマンドを返却します。プラグイン開発者が使用することはありません。 |
forwarding | ATOMインスタンスの現在のトランザクション状態が正常ルートかどうかを返却します。プラグイン開発者が使用することはありません。 |
ignore_error_mode | ATOMインスタンスの現在のトランザクションが強制リカバリもしくは強制キャンセルかどうかを返却します。プラグイン開発者が使用することはありません。 |
isTransactionOwner | ATOMインスタンスがトランザクションのオーナーかどうかを返却します。プラグイン開発者が使用することはありません。 |
serialize | ATOMインスタンスのJSONシリアライズ表現を返却します。プラグイン開発者が使用することはありません。 |
callback | ATOMのメソッド内でのloggingやprintをフックするコールバック関数を返却します。プラグイン開発者が使用することはありません。 |
ATOMに実装されている組込みメソッド
ATOMでは、以下の組込みメソッドが実装されています。
組込メソッド | 種別 | 説明 |
---|---|---|
fieldnames | class method | ATOMクラスに定義されているfield名のリストを返却します。キーワード引数でメタデータを指定したフィルタリングができます。 |
retrieve | class method | ATOMインスタンスをデータベースから検索ロードします。 |
load | class method | ATOMインスタンスをデータベースからロードします。 |
getServiceConfig | class method | Config サービスからリソースを取得します。 |
FSM | class method | ステートマシンオブジェクトを取得します。プラグイン開発者が使用することはありません。 |
class_hierarchy | class method | クラスの包含ツリーを返却します。プラグイン開発者が使用することはありません。 |
instantiation | classmethod | パラメータ宣言情報(yaml json dict 形式をサポート)からATOMインスタンスツリーを生成します。 |
rollback_image | instance method | 前述したdiscard_changes プロパティメソッドを実行した場合のロールバックイメージのATOMインスタンスを取得します。 |
localfields | instance method | ローカルフィールドのキーと値を辞書型(dict)に変換して返却します。 |
search_instance | instance method | インスタンスツリーから目的のインスタンスをサーチします。プラグイン開発者が使用することはありません。 |
save | instance method | ATOMインスタンスをデータベースに永続化します。 |
destroy | instance method | ATOMインスタンスをデータベースから削除します。 |
begin | instance method | トランザクションを開始します。プラグイン開発者が使用することはありません。 |
checkpoint | instance method | トランザクションのチェックポイントを実行します。プラグイン開発者が使用することはありません。 |
savepoint | instance method | トランザクションのセーブポイントを実行します。プラグイン開発者が使用することはありません。 |
abort | instance method | トランザクションを中断します。プラグイン開発者が使用することはありません。 |
commit | instance method | トランザクションをコミットします。プラグイン開発者が使用することはありません。 |
global_counter | instance method | グローバルカウンタを払い出します。後方互換の為、残置していますが非推奨 です。Counter 組込オブジェクトの利用を推奨しています。 |
timeout_backoff | instance method | メソッド伝搬によるトランザクションタイムアウトをバックオフさせます。プラグイン開発者が使用することはありません。 |
instance_hierarchy | instance method | インスタンスの包含ツリーを返却します。プラグイン開発者が使用することはありません。 |
インスタンス化
ATOMオブジェクトのインスタンス化について
ATOMを定義すると、動的にatom
という共通コンテキストに格納されます。atom
コンテキストについてはDocs » 名前空間 » リファレンス » 共通コンテキスト
を参照してください。ATOMのインスタンス化は、initialize
というインタフェースメソッドの実装に合わせて適切に行う必要があります。initialize
メソッドを実装しない場合とinitialize
を同期メソッドで定義した場合は、通常のオブジェクトインスタンス同様にインスタンス化できます。initialize
メソッドを非同期で実装した場合は、await
を使用してインスタンス化する必要があります。
initialize
インタフェースメソッドは、オブジェクトの初期化動作をプラグイン開発者がカスタマイズするためのもので、ATOMインスタンス化時に暗黙的に呼び出されます。
# initializeメソッドが定義されていない場合、もしくは非同期でない場合のインスタンス化
object = atom.SyncObject()
# initializeメソッドが非同期で定義されている場合のインスタンス化
object = await atom.AsyncObject()
Tip
インスタンス化は、通常のPythonクラス同様にコンストラクタを用いて実行できますが、instantiation
クラスメソッドを用いるとyaml
やjson
形式のパラメータ定義からインスタンス化することができます。尚、本組込みクラスメソッドは、Qmonus SDK v20.12 and later
で使用できます。
# 通常のコンストラクタを用いてインスタンス化し、インスタンスツリーを構成する例
>>> tenant = atom.Tenant(tenant_name="hoge")↵
... nw = atom.Network(name="nw1")↵
... subnet1 = atom.Subnet(name="subnw1")↵
... subnet2 = atom.Subnet(name="subnw2")↵
... nw.subnets = [subnet1, subnet2]↵
... tenant.networks = [nw]↵
... print(tenant)↵
... ↵
↵
Tenant(instance='VGVuYW50OmM1NTE3OTE4MzUxYzExZWI4NjY2YWNkZTQ4MDAxMTIy', xid=None, xname=None, tenant_id=None, tenant_name='hoge', description=None, networks=[Network(instance='TmV0d29yazpjNTUxODE1NjM1MWMxMWViODY2NmFjZGU0ODAwMTEyMg==', xid=None, xname=None, id=None, name='nw1', description=None, status=None, tenant_id=None, subnets=[Subnet(instance='U3VibmV0OmM1NTE4YjkyMzUxYzExZWI4NjY2YWNkZTQ4MDAxMTIy', xid=None, xname=None, id=None, name='subnw1', description=None, status=None, tenant_id=None, network_id=None), Subnet(instance='U3VibmV0OmM1NTE5NmZhMzUxYzExZWI4NjY2YWNkZTQ4MDAxMTIy', xid=None, xname=None, id=None, name='subnw2', description=None, status=None, tenant_id=None, network_id=None)])])
# yamlでパラメータ定義して、インスタンスツリーを生成する例
>>> yamltext = """↵
... Tenant:↵
... tenant_name: hoge↵
... networks:↵
... - Network:↵
... name: nw1↵
... subnets:↵
... - Subnet:↵
... name: subnw1↵
... - Subnet:↵
... name: subnw2↵
... """↵
... ↵
↵
>>> tenant = await atom.Tenant.instantiation(yamltext)↵
... print(tenant)↵
... ↵
↵
Tenant(instance='VGVuYW50OjBlOWQyN2Q4MzUxZTExZWI4NjY2YWNkZTQ4MDAxMTIy', xid=None, xname=None, tenant_id=None, tenant_name='hoge', description=None, networks=[Network(instance='TmV0d29yazowZTljYmI5YTM1MWUxMWViODY2NmFjZGU0ODAwMTEyMg==', xid=None, xname=None, id=None, name='nw1', description=None, status=None, tenant_id=None, subnets=[Subnet(instance='U3VibmV0OjBlOWNhMjU0MzUxZTExZWI4NjY2YWNkZTQ4MDAxMTIy', xid=None, xname=None, id=None, name='subnw1', description=None, status=None, tenant_id=None, network_id=None), Subnet(instance='U3VibmV0OjBlOWNhZmM0MzUxZTExZWI4NjY2YWNkZTQ4MDAxMTIy', xid=None, xname=None, id=None, name='subnw2', description=None, status=None, tenant_id=None, network_id=None)])])
>>>
Tip
ATOM自身のinstantiation
クラスメソッド呼び出しを行うためには入力されたyaml
がどのATOMクラスをルートとする定義データかを知っている必要があります。ルートクラスを知る必要がない場合や、ルートクラスが複数ある(yamlがリスト構造)の場合は、atom
コンテキストのクラスメソッドであるatom.instantiation
を利用することでインスタンスもしくはインスタンスリストを生成することができます。詳しくはDocs » 名前空間 » リファレンス » 共通コンテキスト
のatom
コンテキストを参照ください。
Caution
yamlでインスタンスツリー生成したいがスキーマがわからないという方は以下のようにしてスキーマを確認できます。隠しクラスメソッドの_jsonschemas
は、ATOMのフィールド定義からJSONスキーマを生成します。
>>> print(MU(atom.Tenant._jsonschemas(ignore_mgmt_field=True, ignore_atom_field=False, target_method=POST)).yaml_format)↵
... ↵
↵
properties:
Tenant:
additionalProperties: false
properties:
description:
type:
- string
- 'null'
networks:
items:
properties:
Network:
additionalProperties: false
properties:
description:
type:
- string
- 'null'
name:
type: string
subnets:
items:
properties:
Subnet:
additionalProperties: false
properties:
description:
type:
- string
- 'null'
name:
type: string
network_id:
type:
- string
- 'null'
tenant_id:
type:
- string
- 'null'
required:
- name
type: object
required:
- Subnet
title: Subnet
type: object
type: array
tenant_id:
type:
- string
- 'null'
required:
- name
type: object
required:
- Network
title: Network
type: object
type: array
tenant_name:
type:
- string
- 'null'
type: object
required:
- Tenant
title: Tenant
type: object
>>>
永続化
ATOMオブジェクトの永続化について
前述したATOMに実装されている組込みメソッド
において最も利用頻度の高い永続化に関する操作について補足します。ATOMは、定義された際にModel
サービスと連携し、データベース上に自身のインスタンスを格納するためのテーブルを生成してSQLAlchemy
によるORマップクラスに暗黙的にバインドし、以下のCRUDを実現するための組み込みメソッドを提供します。
組込メソッド | 種別 | 引数 | データベース操作 |
---|---|---|---|
save | instance method | conn=None, tran=None, propagation_mode=True, **kwargs | UPSERT |
destroy | instance method | conn=None, tran=None, propagation_mode=True, delay_seconds=0 | DELETE |
load | class method | key, conn=None, shallow=False | GET |
retrieve | class method | conn=None, shallow=False, order_by=[], offset=0, limit=None, **kwargs | SELECT |
CRUD operation can be confirmed in the interactive shell as follows:
>>> hello = atom.Hello(msg="Hello, ATOM!!")↵
... await hello.save()↵
... ↵
↵
>>> select * from Hello;↵
| instance | xid | xname | msg |
SGVsbG86OWU3NmFiMTIzNDVjMTFlYmJjMDdhY2RlNDgwMDExMjI= NULL NULL Hello, ATOM!!
>>> instances = await atom.Hello.retrieve()↵
... print(instances)↵
... ↵
↵
[Hello(instance='SGVsbG86OWU3NmFiMTIzNDVjMTFlYmJjMDdhY2RlNDgwMDExMjI=', xid=None, xname=None, msg='Hello, ATOM!!')]
>>> instances[0].msg = "Hello, ATOM!! Nice to meet you."↵
>>> await instances[0].save()↵
... ↵
>>> hello = await atom.Hello.load(instances[0].instance)↵
... print(hello)↵
... ↵
↵
Hello(instance='SGVsbG86OWU3NmFiMTIzNDVjMTFlYmJjMDdhY2RlNDgwMDExMjI=', xid=None, xname=None, msg='Hello, ATOM!! Nice to meet you.')
>>> await hello.destroy()↵
... instances = await atom.Hello.retrieve()↵
... print(instances)↵
... ↵
↵
[]
>>> select * from Hello;↵
Empty set
>>>
Tip
Scenario
などのコマンドでデータベースセッションをオープンし、ATOMのCRUDを排他的スコープで操作したい場合は、オープンした接続やデータベーストランザクションをconn
やtran
キーワード引数で渡すことができます。
async with model.aiodb() as conn:
async with conn.begin() as tran:
await object.save(conn=conn, tran=tran)
Tip
ATOMオブジェクトの情報変更は、通常インスタンスフィールドに新しい値を代入してsave
メソッドを呼び出すことで変更できますが、save
メソッドにキーワード引数でフィールド名=値
を指定すると、対応するフィールド情報のみを変更して保存する動作になります。
>>> hello = atom.Hello(msg="Hello, ATOM!!")↵
... await hello.save()↵
... ↵
↵
>>> select * from Hello;↵
| instance | xid | xname | msg |
SGVsbG86YmY4YTQ3ODAzNDVlMTFlYmJjMDdhY2RlNDgwMDExMjI= NULL NULL Hello, ATOM!!
>>> await hello.save(msg="Hello, ATOM!! Nice to meet you.")↵
... ↵
↵
>>> select * from Hello;↵
| instance | xid | xname | msg |
SGVsbG86YmY4YTQ3ODAzNDVlMTFlYmJjMDdhY2RlNDgwMDExMjI= NULL NULL Hello, ATOM!! Nice to meet you.
>>> ↵
Tip
loadメソッドは、Qmonus SDKが自動的に割り当てるインスタンスIDでロードできます。また、identifier field
が定義されている場合は対応するフィールドの値を指定することにより、インスタンスを取得できます。
>>> e = atom.Employee(employeeCode="E5813836", name="Qmonus太郎", Affiliation="Devops")↵
>>> await e.save()↵
>>> print(e)↵
... ↵
↵
Employee(instance='RW1wbG95ZWU6YWMxZWMwODAzNDVmMTFlYmJjMDdhY2RlNDgwMDExMjI=', xid=None, xname=None, employeeCode='E5813836', name='Qmonus太郎', Affiliation='Devops')
>>> o = await atom.Employee.load("RW1wbG95ZWU6YWMxZWMwODAzNDVmMTFlYmJjMDdhY2RlNDgwMDExMjI=")↵
... print(o)↵
... ↵
↵
Employee(instance='RW1wbG95ZWU6YWMxZWMwODAzNDVmMTFlYmJjMDdhY2RlNDgwMDExMjI=', xid=None, xname=None, employeeCode='E5813836', name='Qmonus太郎', Affiliation='Devops')
>>> o = await atom.Employee.load("E5813836")↵
... print(o)↵
... ↵
↵
Employee(instance='RW1wbG95ZWU6YWMxZWMwODAzNDVmMTFlYmJjMDdhY2RlNDgwMDExMjI=', xid=None, xname=None, employeeCode='E5813836', name='Qmonus太郎', Affiliation='Devops')
>>>
Tip
retrieveメソッドはkwargs
で値を指定して検索する機能があります。
以下のように指定することで特定の条件に合ったインスタンスを取得することができます。
>>> hello = atom.Hello(msg="Hello, ATOM!!")↵
... await hello.save()↵
... ↵
↵
>>> select * from Hello;↵
| instance | xid | xname | msg |
SGVsbG86YjJhNWY0N2NjNjQ4MTFlZWJiODUyZTBmNjRkOGEyYzI= NULL NULL Hello, ATOM!!
>>> instances = await atom.Hello.retrieve(msg="Hello, WORLD!!")↵
... print(instances)↵
... ↵
[]
>>> instances = await atom.Hello.retrieve(msg="Hello, ATOM!!")↵
... print(instances)↵
... ↵
[Hello(instance='SGVsbG86YjJhNWY0N2NjNjQ4MTFlZWJiODUyZTBmNjRkOGEyYzI=', xid=None, xname=None, msg='Hello, ATOM!!')]
Tip
ATOMインスタンスツリーが巨大化すると、retrieve
やload
メソッドは、すべての子インスタンスをデータベースからメモリ上にロードするため、パフォーマンスが低下します。子インスタンスの検索ロードを抑止して最上位階層のオブジェクトのみを検索する場合は、キーワード引数でshallow=True
を指定することで処理の低下を回避します。子オブジェクトをロードする必要があり、パフォーマンスを維持したい場合は、offset/limit
キーワード引数を使用して再帰的に読み取るなどスループットを維持する読み取りを行うよう設計してください。
Tip
destroy
メソッドは、delay_seconds
を指定することで実行を遅らせることができます。遅延実行タスクは、スケジュールサービスへのタスク予約によって実現されます。予約情報を削除することでキャンセルできます。
Note
データベースへのATOMオブジェクトの永続化については、上記の通りですが、Scenario
のグローバル変数としてATOMを利用する場合や、ATOMのメソッド実行によって自律駆動するトランザクションのスナップショットでは、JSONシリアライズによってメモリストアにATOMオブジェクトが保存されます。これらは、Qmonus SDKによって処理され、プラグイン開発者はそれを意識する必要はありません。ただし、内部動作は理解しておく必要があります。ATOMはjson.dumps
で直列化文字列を生成できます。また、JSON文字列からのインスタンスの復元は、組込み関数deserialize
によって実行されます。トランザクションサーバへの名前空間スナップショットとインスタンスリカバリは、このメカニズムを使用しています。詳細は、Docs » リファレンス » ビルトインオブジェクト
に記述されているdeserialize
を参照してください。
伝搬
フィールド値の伝搬について
ATOMオブジェクトの参照フィールドは、上位オブジェクトの特定のフィールドへの参照であり、参照フィールドの値は、上位オブジェクトのフィールドの値の更新と連動して自動的に反映されます。
Example class diagram
>>> tenant = atom.Tenant(tenant_name="SampleTenant")↵
... nw = atom.Network(name="SampleNetwork")↵
... subnet1 = atom.Subnet(name="SampleSubnet1")↵
... subnet2 = atom.Subnet(name="SampleSubnet2")↵
... nw.subnets = [subnet1, subnet2]↵
... tenant.networks = [nw]↵
... ↵
↵
>>> print(tenant.tenant_id)↵
... print(tenant.networks[0].tenant_id)↵
... print(tenant.networks[0].subnets[0].tenant_id)↵
... print(tenant.networks[0].subnets[1].tenant_id)↵
... ↵
↵
None
None
None
None
>>> tenant.tenant_id = uuid.uuid1().hex↵
... print(tenant.tenant_id)↵
... print(tenant.networks[0].tenant_id)↵
... print(tenant.networks[0].subnets[0].tenant_id)↵
... print(tenant.networks[0].subnets[1].tenant_id)↵
... ↵
↵
1d20dec034fc11eb8551acde48001122
1d20dec034fc11eb8551acde48001122
1d20dec034fc11eb8551acde48001122
1d20dec034fc11eb8551acde48001122
>>>
メソッド実行の伝搬について
ATOMオブジェクトのインスタンスメソッドが実行され、そのオブジェクトに別のATOMオブジェクトが包含され、実行されたメソッドと同じ名前のメソッドがある場合、伝播が実行されます。伝播アクションは、対応するインスタンスメソッドの伝播モードがTrue
の場合のみです。伝播のターゲットが複数存在する場合、多重化可能数を指定して並列に実行することもできます。伝播順序は、デフォルトでクラス定義で宣言されたフィールド順序ですが、メソッドのフィールド順序属性で昇順または降順を指定できます。
トランザクション連携
トランザクション自動アシスト
状態マシンに関連付けられたATOMインスタンスメソッドが実行されると、トランザクションが暗黙的に発行され、非同期伝播ツリー操作全体のアトミック性が保証されます。
トランザクションタイムアウトのバックオフ
ATOMトランザクションは、開始時にデフォルトのタイムアウト10分
で生成されます。メソッド呼び出しが発生すると、トランザクションの残りの寿命が、メソッドの定義時に指定できるタイムアウト値で満たされるかどうかを判断し、不足分を自動的に延長します。ツリーの伝播中に、このメカニズムが動作します。
クラウドリソースのオーケストレーションでは、状態変化の待ち時間が多く、最悪の値を積み上げてタイムアウトを設計した場合、非常に長い時間となります。この場合、トランザクションを駆動しているシナリオサーバがダウンして自律的に中断できない場合、トランザクションサーバがタイムアウトを検出するまで長時間サイレント障害となります。ATOMのタイムアウトバックオフ機構は、不足分を自動延長することでサイレント障害の時間を大幅に短縮します。
シナリオからATOMトランザクションメソッドを呼び出す方法
ATOMの状態マシンに関連づけられたメソッドを呼び出すと、呼び出されたルートオブジェクトでトランザクションを開始します。シナリオ側でトランザクションをオーナーしてそのトランザクションスコープでATOMの状態遷移を駆動させる場合は以下のようにATOMインスタンスに対してトランザクションIDをセットする必要があります。
API自動生成
API自動生成モードでATOMを定義すると、CRUDに必要なRESTful APIが自動生成されます。包含関係がある場合は、子オブジェクトを操作するためのAPIも生成されます。デフォルトでは、APIが要求された場合、次の組み込みメソッドのみが機能します。
HTTP Method | Request Path | Built-in Action | description |
---|---|---|---|
POST |
/{api_base_path}/{class} |
save | ATOMオブジェクトのインスタンス化を行いデータベースに永続化します。 |
PUT |
/{api_base_path}/{class}/{instance} |
save | ATOMオブジェクトインスタンスの情報を変更し、データベースに永続化します。 |
DELETE |
/{api_base_path}/{class}/{instance} |
destroy | ATOMオブジェクトインスタンスをデータベースから削除します。 |
GET |
/{api_base_path}/{class}/{instance} |
load | ATOMオブジェクトインスタンスをデータベースから取得します。 |
GET |
/{api_base_path}/{class} |
retrieve | ATOMオブジェクトインスタンスをデータベースから検索します。 |
Note
identifier field
を定義している場合、APIパスの{instance}
部分は{identifier}
となります。
上記のPOST、PUT、およびDELETEの組込みのCUD動作に加え、プラグイン開発者は動作をカスタマイズできます。プラグイン開発者は、以下のインタフェースメソッドをオーバーライドすることにより、API呼び出し時の動作を拡張できます。
Instance Method | description |
---|---|
create |
POST 時に暗黙的に呼び出される特別なインタフェースメソッド。 |
update |
PUT で暗黙的に呼び出される特別なインタフェースメソッド。 |
delete |
DELETE で暗黙的に呼び出される特別なインタフェースメソッド |
ATOM Development Journey
Learn More
ATOM demonstration movie