スレッドの問題
ThingWorx アプリケーションでは次のような場合にパフォーマンスやその他の安定性の問題が生じます。
多数のスレッドが長時間ブロックされている
共有リソースの競合が発生している
スレッドが長時間実行している
このセクションでは、これらのタイプの問題について概要を説明します。
スレッドがブロックされている
多数のスレッドが長時間ブロックされている場合、JVM レベルでリソースへの高い競合が発生しているか、デッドロックが生じています。多数のスレッドがブロックされていて、データベースアクセスなどの共有リソースを待機している場合、ユーザーは一般的に操作を実行できません。
高い競合は自然に解決することがあります。たとえば、大規模なデータベース書き込みオペレーションが完了した場合に解決します。ただし、スレッドが長時間ブロックされたままになっている場合、次のことが考えられます。
デッドロックの状況が発生し、スレッドが循環パターンで相互にロックし合っている可能性があります。
あるいは、必要な時間だけ処理を続ける、単一のブロックトランザクションが存在する可能性があります。
以下を行うことによってこの問題が発生していないかチェックできます。
ブロックされているスレッドの数をサーチします。
あるスナップショットでブロックされているスレッドが以降のスナップショットでもブロックされたままとなっているかどうかをチェックします。
以下の図に、ブロックされているスレッドを示します。
この例では、TWEventProcessor-10 の unzip オペレーションがブロックされています。これは TWEventProcessor-9 スレッドで別の unzip オペレーションが完了するのを待機しています。ThingWorx では 2 人のユーザーが同時にファイルを unzip することが可能であり、2 人目のユーザーは 1 人目のユーザーがオペレーションを完了するまで待つ必要があります。どちらのオペレーションも 0x2f9a4f4 オブジェクトにアクセスしており、このオブジェクトは TWEventProcessor-9 でロックされています。
多数のイベントプロセッサスレッドもブロックされている場合、さらに重大なサーバー安定性の問題が発生している可能性があります。さらに、イベント処理サブシステムでアクティブスレッド数の増加が示される場合があります。さらに、ブロックされている状況がただちに解決されない場合、未処理イベントのキューも増大していきます。
共有リソースの競合が発生している
次の理由により、ThingWorx ではデータベースとイベントのスレッドがパフォーマンスのボトルネックとなることがあります。
C3P0PooledConnectionPoolManager グループと TWEventProcessor グループでスレッドの競合が発生していないかチェックします。これらのカテゴリで多数のスレッドがブロックされていたり、多数のスレッドがアクティブに実行していたりする場合、パフォーマンスのボトルネックが存在している可能性があります。
データベースとイベントのすべてのスレッドがいっぱいになっている場合、サーバー上でキューに入っているイベントが存在する可能性があります。
以下の例について考えてみます。この場合、イベントのスレッドによってデータベースコネクタのすべてのスレッドがブロックされています。セルスタックのイベントスレッド 11 が大規模なデータベース更新オペレーションを実行しています。
"C3P0PooledConnectionPoolManager[identityToken->2v1py89n68drc71w36rx7|68cba7db]-HelperThread-#7" tid=0xa0 in BLOCKED
Blocked: 414717[-1ms], Waited: 416585[-1ms]
User CPU: 5s320ms
at java.lang.Object.wait(Native Method)
- waiting on <0x56978cbf> (a com.mchange.v2.async.ThreadPoolAsynchronousRunner), held by TWEventProcessor-11
"C3P0PooledConnectionPoolManager[identityToken->2v1py89n68drc71w36rx7|68cba7db]-HelperThread-#6" tid=0x9f in BLOCKED
Blocked: 415130[-1ms], Waited: 416954[-1ms]
User CPU: 5s530ms
at java.lang.Object.wait(Native Method)
- waiting on <0x56978cbf> (a com.mchange.v2.async.ThreadPoolAsynchronousRunner), held by TWEventProcessor-11
次の例に示すように、このオペレーションを実行しているカスタムサービスのロジックを最適化できます。
"TWEventProcessor-11" tid=0x92 in RUNNABLE
Blocked: 3913[-1ms], Waited: 37924[-1ms]
User CPU: 45s430ms
at com.thingworx.persistence.common.sql.SqlDataTableProvider.updateEntry(SqlDataTableProvider.java:13)
at com.thingworx.persistence.TransactionFactory.createDataTransactionAndReturn(TransactionFactory.java:155)
at com.thingworx.persistence.common.BaseEngine.createTransactionAndReturn(BaseEngine.java:176)
スレッドが長時間実行している
スレッドが長時間実行している場合、ThingWorx アプリケーション内のカスタムコードを最適化しなければならないことがあります。一般的なユーザートランザクションは 1 秒未満から数秒の範囲で JVM レベルで速やかに完了します。ただし、10 分以上実行しているなど、長時間実行しているスレッドで、カスタムコードが使用されている場合、問題がある可能性があります。10 分間にわたる複数のスレッドスナップショットを収集して、オペレーションがそのスレッドを実行するときにかかった時間を調べます。
たとえば、次のような 60 分間のキャプチャでは、同じスレッドで同じ実行パターンが見られます。
http-nio-8443-exec-25" tid=0xc7 in RUNNABLE
Blocked: 750[-1ms], Waited: 8866[-1ms]
User CPU: 1h35m
- synchronizer <0x35153c2f> (a java.util.concurrent.ThreadPoolExecutor$Worker)
at com.thingworx.types.data.sorters.GenericSorter.compare(GenericSorter.java:93)
at com.thingworx.types.data.sorters.GenericSorter.compare(GenericSorter.java:20)
at java.util.TimSort.countRunAndMakeAscending(TimSort.java:360)
at java.util.TimSort.sort(TimSort.java:234)
at java.util.Arrays.sort(Arrays.java:1512)
at java.util.ArrayList.sort(ArrayList.java:1454)
at java.util.Collections.sort(Collections.java:175)
at com.thingworx.types.InfoTable.quickSort(InfoTable.java:722)

この例では、スレッドはインフォテーブルに対して並べ替えを実行しています。並べ替えのオペレーションは大きなインフォテーブル内のアイテムごとに繰り返し実行されています。このオペレーションが最後に 1 回発生するように最適化できます。長時間実行しているオペレーションによって、メモリレベル、データベースレベル、またはその他のサーバーリソースで競合が発生します。このため、カスタム ThingWorx アプリケーション内で長時間実行しているサービスを特定して対処することが重要です。