4Dと4D Serverは、マルチユーザまたはマルチプロセスのコンフリクトを防ぐことによってマルチユーザデータベースを自動的に管理します。2人のユーザまたは2つのプロセスが、同時に同じレコードやオブジェクトを修正することはできませんが、2番目のユーザやプロセスはレコードやオブジェクトに読み込みのみのアクセスを得ることができます。
この章のマルチユーザコマンドを使用しなければならない状況がいくつかあります:
- プログラミング言語を使用してレコードを更新する。
- マルチユーザ環境でカスタムユーザインタフェースを使用する。
- トランザクション内で関連する変更を保存する。
マルチプロセスデータベースでコマンドを使用するときに注意すべき重要な概念が3つあります:
- プロセス内では、各テーブルは、読み込み専用または読み書き可能のどちらかに設定される。
- レコードは、ロードされた時点で他のプロセスに対しロック状態となり、アンロ-ドされた時点でロック解除になる。
- ロックされたレコードは、更新することはできない。
この章では、マルチユーザデータベースで作業を実行する人をローカルユーザと呼びます。マルチユーザデータベースを使用する他の人を他のユーザと呼びます。この節では、ローカルユーザの観点で説明します。またマルチプロセスの観点から、データベース上で処理を実行しているプロセスをカレントプロセスと呼びます。その他の実行中のプロセスは他のプロセスと呼びます。この章では、カレントプロセスの観点で説明します。
ローカルユーザやカレントプロセスは、ロックされたレコードを更新できません。ロックされたレコードをロードすることは可能ですが、更新することはできません。他のユーザやプロセスが更新するためにレコードをロードした時点またはスタックした時点で、そのレコードはロックされます。レコードを更新しているユーザだけが、そのレコードをロックされていない状態で取り扱うことができます。その他のすべてのユーザやプロセスは、レコードがロック状態になるため更新することはできません。ロックされていない状態でレコードをロードするには、テーブルを必ず読み書き状態に設定しなければなりません。
データベース中の各テーブルは、データベースの各ユーザおよび各プロセスに対して読み込みのみ状態と読み書き状態のいずれかになっています。読み込みのみとは、テーブルからレコードをロードすることはできるが更新することはできないという状態です。読み書きとは、テーブルのレコードをロード可能であり、他のユーザが先にそのレコードをロックしていない場合には更新することができる状態です。
テーブルの状態を変更すると、その変更は次からロードされるレコードに影響を及ぼすことに注意してください。テーブル状態を変更する際に既にロードされていたレコードは、状態変更によって影響を受けることはありません。
テーブルが読み込みのみ状態に設定されると、ロードされたレコードは常に更新不可にされます。つまり、ロックされたレコードの表示と印刷を実行することはできますが、更新することはできません。
この読み込みのみ状態は、既存レコードの更新処理に対してのみ適用されることに注目してください。読み込みのみ状態は新規レコードの作成に影響を与えません。デザインモードのメニューやCREATE RECORD、ADD RECORDを使って読み込みのみテーブルにレコードを追加することができます(この場合、作成されたレコードは他の目的に対しては全てロックされています)。ARRAY TO SELECTION コマンドは、レコードの作成と更新と両方が可能なので読み込みのみ状態に影響されないという点に注意して下さい。
4Dは、レコードに対する書き込み動作を必要としないコマンド利用時、テーブルを自動的に読み込みのみ状態にします。以下そのコマンドを示します:
テーブルの現在の状態を知るには、Read only state ファンクションを使用します。
これらのコマンドを実行する前に、4Dはカレントプロセスにおけるテーブルのそのときの状態 (読み込みのみまたは読み書き) を保持します。コマンド実行後、テーブルの状態は元に戻されます。
読み書き状態のテーブルからロードされたレコードは、他のユーザがそのレコードを先にロックしていなければ更新可能になります。レコードが他のユーザによってロックされている場合にはレコードをロックされたレコードとしてロードできますが、更新はできません。
テーブルが読み書き状態に設定され、ロードしたレコードがロックされていない時に、レコードの更新が可能になります。
あるユーザが読み書き状態のテーブルからレコードをロードすると、他のユーザはレコードをロードできますが、更新することはできません。しかし他のユーザはデザインモード内での手動によるレコード追加、あるいはCREATE RECORDやADD RECORDを使ってのレコード追加をすることはできます。
データベースが開かれたりまたは新規プロセスが開始された時点で、すべてのテーブルはデフォルトで読み書き状態です。
READ ONLY およびREAD WRITE コマンドを使って、テーブル状態を変更することができます。あるレコードを読み込みのみ状態または読み書き状態にするために任意テーブルの状態を変更したい場合は、そのレコードをロードする前にコマンドを実行する必要があります。既にロードされたレコードは、READ ONLY およびREAD WRITEによって影響を受けることはありません。
各プロセス毎に、データベース内の各テーブルに対する独自の状態 (読み込みのみまたは読み書き) を持っています。
デフォルトで、コマンドを使用しない場合、全てのテーブルは読み書き可能モードにあります。
ローカルユーザがレコードを更新するためには、テーブルが読み書き状態でかつ、ロードしたレコードがロックされていない状態でなければなりません。
NEXT RECORD, QUERY, ORDER BY, RELATE ONE等 のカレントレコードをロードするコマンドは、そのレコードの状態をロックまたはロック解除状態にします。レコードは、テーブルのその時の状態 (読み込みのみまたは読み書き) とそのレコードの状態に応じてロードされます。自動リレートを使用するコマンドを使用した場合、リレート先テーブルからもレコードがロードされます。
テーブルが ユーザーあるいはプロセスに対して読み込みのみ状態になっている場合、そのテーブルからロードされたレコードは全て読み込みのみモードでロードされています。つまり、このプロセスあるいはユーザーではレコードの編集あるいは削除はできないということです。これはデータの閲覧・取得などに推奨されています。なぜなら他のユーザーやプロセスがこのテーブルのレコードに読み書き可能モードでアクセスする必要がある場合に、それと干渉しないからです。
テーブルがユーザーあるいはプロセスに対して読み書き状態になっている場合、そのテーブルからロードされたレコードは(他のユーザやプロセスが先にロックしていない限り)読み書き可能でロードされます。レコードが読み書き可能な状態で正常にロードされた場合、カレントプロセス、あるいはユーザーに対してはアンロックの状態になり(編集および保存が可能になり)、他のユーザーやプロセスに対してはロックされます。レコードの編集および保存のためには、レコードをロードする前にテーブルを読み書き状態にしなければなりません。
レコードを修正する場合、Locked関数を使って、他のユーザがレコードをロックしていないかどうかを調べます。レコードがロックされている (LockedがTrueを返す) 場合、レコードをLOAD RECORDコマンドでロードし、そのレコードがロックされているかどうかを再び調べます。レコードがロック解除される (LockedがFalseを返す) までこの処理を繰り返します。
レコードの更新が完了したら、UNLOAD RECORDを使ってそのレコードを 解放 (他のユーザに対しロック解除) しなければなりません。レコードがアンロードされないと、他のカレントレコードが選択されるまで、すべての他のユーザに対してロックされた状態のままにな ります。テーブルのカレントレコードを変えると、前のカレントレコードは自動的にロック解除されます。カレントレコードを変更しない場合は、UNLOAD RECORDコマンドを明示的に呼び出す必要があります。これは既存レコードの場合だけであり、新しいレコードを作成する場合、そのレコードが属するテーブルの状態に関係なく保存することができます。
注: トランザクションの中でUNLOAD RECORDコマンドを使用した場合、トランザクションを管理するプロセス中でのみカレントレコードがアンロードされます。他のプロセスから見た場合、レコードはロックされたままとなり、トランザクションを完了 (または取消し) されるまで維持されます。
LOCKED ATTRIBUTESコマンドを使用すると、レコードをロックしているユーザやプロセスを知ることができます。
注: 良い方法としては、各プロセス開始時に全てのテーブルを(
READ ONLY(*)シンタックスを使用して)読み込みのみモードに設定し、必要に応じてそれぞれのテーブルを読み書き可能状態に設定することが挙げられます。読み込みのみモードの方が、テーブルへのアクセスが速く、メモリも節約するからです。それ以上に、テーブルの状態の変更は余計なネットワークのトラフィックを生じないたいめ、クライアント/サーバーモードでは最適化されています。テーブルへのアクセスが必要なコマンドを実行するときのみ、サーバーに情報が送信されます。
以下の例は、アンロックされたレコードをロードするための最も単純なループ処理を示しています:
READ WRITE([Customers])
Repeat
LOAD RECORD([Customers])
If(Locked([Customers]))
DELAY PROCESS(Current process;5)
End if
Until(Not(Locked([Customers])))
READ ONLY([Customers])
このループ処理は、レコードがロック解除されるまで繰り返されます。
このようなループ処理は、ユーザがループが終了するまで待たされるため、レコードが他のユーザにロックされる可能性がほとんどない場合にのみ使用することができます。したがって、メソッドからのみレコードを更新するような場合以外では使用することはできません。
以下の例は先のループを使用してアンロックされたレコードをロードし、更新を行っています:
READ WRITE([Inventory])
Repeat
LOAD RECORD([Inventory])
If(Locked([Inventory]))
DELAY PROCESS(Current process;5)
End if
Until(Not(Locked([Inventory])))
[Inventory]Part Qty:=[Inventory]Part Qty-1
SAVE RECORD([Inventory])
UNLOAD RECORD([Inventory])
READ ONLY([Inventory])
MODIFY RECORDコマンドはレコードがロックされている場合には、自動的にそれをユーザに通知し、レコードが更新されることを防ぎます。以下の例では最初にLocked関数を使用してレコードの状態を調べることで、この自動的な通知を避けています。レコードがロックされている場合には、ユーザが処理を中断できるようにします。
以下の例は、[commands]テーブルのカレントレコードがロックされているかどうかを調べます。ロックされている場合は、プロセスがメソッドによって1秒間延期されます。この技法は、マルチユーザやマルチプロセスの場合両方に用いることができます:
Repeat
READ ONLY([Commands])
QUERY([Commands])
If((OK=1) & (Records in selection([Commands])>0))
READ WRITE([Commands])
LOAD RECORD([Commands])
While(Locked([Commands]) & (OK=1))
LOCKED BY([Commands];$Process;$User;$SessionUser;$Name)
If($Process=-1)
ALERT("The record has been deleted in the meantime.")
OK:=0
Else
If($User="")
$User:="you"
End if
CONFIRM("The record is already used by "+$User+" in the "+$Name+" Process.")
If(OK=1)
DELAY PROCESS(Current process;60)
LOAD RECORD([Commands])
End if
End if
End while
If(OK=1)
MODIFY RECORD([Commands])
UNLOAD RECORD([Commands])
End if
READ ONLY([Commands])
OK:=1
End if
Until(OK=0)
いくつかのコマンドは、レコードがロックされていることを検知した時点で、特定の処理を実行します。これらのコマンドは、レコードがロックされていない場合には、通常どおりの処理を遂行します。
以下に、レコードがロックされていることを検知した場合の各コマンドの処理を示します。
- MODIFY RECORD: レコードが使用されていることを示すダイアログボックスを表示します。レコードは表示されず、ユーザはレコードを修正できません。デザインモ-ドでは、レコ-ドは読み込みのみ状態で表示されます。
- MODIFY SELECTION: ユーザがレコードをダブルクリックして修正しようとした場合を除いて通常どおりの処理を行います。MODIFY SELECTIONはレコードが使用されていることを示すダイアログボックスを表示し、読み込みのみ状態でレコードのアクセスを許可します。
- APPLY TO SELECTION: ロックされたレコードをロードしますが、そのレコードを更新しません。APPLY TO SELECTIONは、特別な処置を行わずにテーブルからレコードを読み取ります。ロックされたレコードを検知すると、コマンドはそのレコードをLockedSetシステムセットに格納します。
- DELETE SELECTION: ロックされたレコードを削除せずにスキップします。コマンドはロックされたレコードを見使えると、それをLockedSetシステムセットに格納します。
- DELETE RECORD: レコードがロックされている場合、このコマンドは何も行いません。エラーも返しません。このコマンドを実行する前にレコードがアンロックされていることを確認する必要があります。
- SAVE RECORD: レコードがロックされている場合、このコマンドは何も行いません。エラーも返しません。このコマンドを実行する前にレコードがアンロックされていることを確認する必要があります。
- ARRAY TO SELECTION: ロックされたレコードは保存しません。ロックされたレコードを検知すると、コマンドはそのレコードをLockedSetシステムセットに格納します。
- GOTO RECORD: マルチユーザ/マルチプロセスデータベース上では、他のユーザがレコードを削除、または追加することができます。したがって、レコード番号が変更される場 合があるため、マルチユーザデータベース上でレコード番号を使用して直接レコードを参照する場合には、十分注意してください。
- : セットに使用される情報を他のユーザやプロセスが変更する可能性があるため、セットの使用には細心の注意が必要です。