34.5. パイプラインモード #

<title>Pipeline Mode</title>

<application>libpq</application> pipeline mode allows applications to send a query without having to read the result of the previously sent query. Taking advantage of the pipeline mode, a client will wait less for the server, since multiple queries/results can be sent/received in a single network transaction. libpqパイプラインモードを使用すると、アプリケーションは以前に送信された問い合わせの結果を読み込まなくても問い合わせを送信できます。 パイプラインモードを利用すると、1つのネットワークトランザクションで複数の問い合わせ/結果を送受信できるので、クライアントはサーバを待つ時間が少なくなります。

While pipeline mode provides a significant performance boost, writing clients using the pipeline mode is more complex because it involves managing a queue of pending queries and finding which result corresponds to which query in the queue. パイプラインモードではパフォーマンスが大幅に向上しますが、パイプラインモードを使用してクライアントを作成すると、保留中の問い合わせのキューを管理し、どの結果がキュー内のどの問い合わせに対応するかを見つける必要があるため、より複雑になります。

Pipeline mode also generally consumes more memory on both the client and server, though careful and aggressive management of the send/receive queue can mitigate this. This applies whether or not the connection is in blocking or non-blocking mode. パイプラインモードでは、一般にクライアントとサーバの両方でより多くのメモリを消費しますが、送受信キューを注意深く積極的に管理することでこれを軽減できます。 これは、接続がブロックモードか非ブロックモードかに関係なく適用されます。

While <application>libpq</application>'s pipeline API was introduced in <productname>PostgreSQL</productname> 14, it is a client-side feature which doesn't require special server support and works on any server that supports the v3 extended query protocol. For more information see <xref linkend="protocol-flow-pipelining"/>. libpqのパイプラインAPIは、PostgreSQL 14で導入されましたが、これは特別なサーバサポートを必要としないクライアント側の機能であり、v3拡張問い合わせプロトコルをサポートするすべてのサーバで機能します。 詳細については、55.2.4を参照してください。

34.5.1. パイプラインモードの使用 #

<title>Using Pipeline Mode</title>

To issue pipelines, the application must switch the connection into pipeline mode, which is done with <xref linkend="libpq-PQenterPipelineMode"/>. <xref linkend="libpq-PQpipelineStatus"/> can be used to test whether pipeline mode is active. In pipeline mode, only <link linkend="libpq-async">asynchronous operations</link> that utilize the extended query protocol are permitted, command strings containing multiple SQL commands are disallowed, and so is <literal>COPY</literal>. Using synchronous command execution functions such as <function>PQfn</function>, <function>PQexec</function>, <function>PQexecParams</function>, <function>PQprepare</function>, <function>PQexecPrepared</function>, <function>PQdescribePrepared</function>, <function>PQdescribePortal</function>, is an error condition. <function>PQsendQuery</function> is also disallowed, because it uses the simple query protocol. Once all dispatched commands have had their results processed, and the end pipeline result has been consumed, the application may return to non-pipelined mode with <xref linkend="libpq-PQexitPipelineMode"/>. パイプラインを発行するためには、アプリケーションは接続をパイプラインモードに切り替える必要があります。 これはPQenterPipelineModeで行われます。 PQpipelineStatusは、パイプラインモードがアクティブかどうかをテストするために使用できます。 パイプラインモードでは、非同期操作のみが許可され、複数のSQLコマンドを含むコマンド文字列、COPYは許可されません。 PQfnPQexecPQexecParamsPQpreparePQexecPreparedPQdescribePreparedPQdescribePortalなどの同期コマンド実行関数を使用すると、エラー状態になります。 PQsendQueryも禁止されています。なぜなら、簡易問い合わせプロトコルを使用するからです。 登録されたすべてのコマンドの結果が処理され、パイプラインの終了結果が消費されると、アプリケーションはPQexitPipelineModeを使用して非パイプラインモードに戻ることができます。


It is best to use pipeline mode with <application>libpq</application> in <link linkend="libpq-PQsetnonblocking">non-blocking mode</link>. If used in blocking mode it is possible for a client/server deadlock to occur. パイプラインモードをlibpqとともに非ブロックモードで使用するのが最善です。 ブロックモードで使用すると、クライアント/サーバのデッドロックが発生する可能性があります。 [15] 問い合わせ発行 #

<title>Issuing Queries</title>

After entering pipeline mode, the application dispatches requests using <xref linkend="libpq-PQsendQueryParams"/> or its prepared-query sibling <xref linkend="libpq-PQsendQueryPrepared"/>. These requests are queued on the client-side until flushed to the server; this occurs when <xref linkend="libpq-PQpipelineSync"/> is used to establish a synchronization point in the pipeline, or when <xref linkend="libpq-PQflush"/> is called. The functions <xref linkend="libpq-PQsendPrepare"/>, <xref linkend="libpq-PQsendDescribePrepared"/>, and <xref linkend="libpq-PQsendDescribePortal"/> also work in pipeline mode. Result processing is described below. パイプラインモードに入った後、アプリケーションはPQsendQueryParams、またはその準備された問い合わせ版の兄弟であるPQsendQueryPreparedを使用して要求を登録します。 これらの要求は、サーバにフラッシュされるまでクライアント側で待ち行列に入れられます。 これは、PQpipelineSyncがパイプラインに同期ポイントを確立するために使用された場合、またはPQflushが呼び出された場合に発生します。 PQsendPreparePQsendDescribePreparedPQsendDescribePortalの関数もパイプラインモードで動作します。 結果の処理については後述します。

The server executes statements, and returns results, in the order the client sends them. The server will begin executing the commands in the pipeline immediately, not waiting for the end of the pipeline. Note that results are buffered on the server side; the server flushes that buffer when a synchronization point is established with <function>PQpipelineSync</function>, or when <function>PQsendFlushRequest</function> is called. If any statement encounters an error, the server aborts the current transaction and does not execute any subsequent command in the queue until the next synchronization point; a <literal>PGRES_PIPELINE_ABORTED</literal> result is produced for each such command. (This remains true even if the commands in the pipeline would rollback the transaction.) Query processing resumes after the synchronization point. サーバは文を実行し、クライアントが送信した順に結果を返します。 サーバはパイプラインのコマンドの実行を即座に開始し、パイプラインの終了を待機しません。 結果はサーバ側でバッファされることに注意してください;同期ポイントがPQpipelineSyncで確立されたとき、またはPQsendFlushRequestが呼び出されたとき、サーバはバッファをフラッシュします。 いずれかの文でエラーが発生した場合、サーバは現在のトランザクションを中止し、次の同期ポイントまでキュー内の後続のコマンドを実行しません。 このようなコマンドごとにPGRES_PIPELINE_ABORTED結果が生成されます(パイプラインのコマンドがトランザクションをロールバックする場合でも同様です)。 問い合わせ処理は同期ポイント後に再開されます。

It's fine for one operation to depend on the results of a prior one; for example, one query may define a table that the next query in the same pipeline uses. Similarly, an application may create a named prepared statement and execute it with later statements in the same pipeline. 1つの操作が前の操作の結果に依存することは問題ありません。 たとえば、1つの問い合わせが同じパイプラインの次の問い合わせが使用するテーブルを定義することができます。 同様に、アプリケーションは名前付きのプリペアドステートメントを作成し、同じパイプラインの後のステートメントで実行することができます。 処理結果 #

<title>Processing Results</title>

To process the result of one query in a pipeline, the application calls <function>PQgetResult</function> repeatedly and handles each result until <function>PQgetResult</function> returns null. The result from the next query in the pipeline may then be retrieved using <function>PQgetResult</function> again and the cycle repeated. The application handles individual statement results as normal. When the results of all the queries in the pipeline have been returned, <function>PQgetResult</function> returns a result containing the status value <literal>PGRES_PIPELINE_SYNC</literal> パイプラインの1つの問い合わせの結果を処理するために、アプリケーションはPQgetResultを繰り返し呼び出し、PQgetResultがNULLを返すまで各結果を処理します。 パイプラインの次の問い合わせの結果は、再度PQgetResultを使用して取得され、サイクルが繰り返されます。 アプリケーションは個々の文の結果を通常どおり処理します。 パイプラインのすべての問い合わせの結果が返されると、PQgetResultは状態値PGRES_PIPELINE_SYNCを含む結果を返します。

The client may choose to defer result processing until the complete pipeline has been sent, or interleave that with sending further queries in the pipeline; see <xref linkend="libpq-pipeline-interleave"/>. クライアントは、完全なパイプラインが送信されるまで結果処理を延期するか、パイプラインでさらに問い合わせを送信して結果処理をインターリーブするかを選択できます。を参照してください。

To enter single-row mode, call <function>PQsetSingleRowMode</function> before retrieving results with <function>PQgetResult</function>. This mode selection is effective only for the query currently being processed. For more information on the use of <function>PQsetSingleRowMode</function>, refer to <xref linkend="libpq-single-row-mode"/>. 単一行モードに入るには、PQgetResultで結果を取得する前にPQsetSingleRowModeを呼び出します。 このモード選択は現在処理中の問い合わせに対してのみ有効です。 PQsetSingleRowModeの使用に関する詳細については、34.6を参照してください。

<function>PQgetResult</function> behaves the same as for normal asynchronous processing except that it may contain the new <type>PGresult</type> types <literal>PGRES_PIPELINE_SYNC</literal> and <literal>PGRES_PIPELINE_ABORTED</literal>. <literal>PGRES_PIPELINE_SYNC</literal> is reported exactly once for each <function>PQpipelineSync</function> at the corresponding point in the pipeline. <literal>PGRES_PIPELINE_ABORTED</literal> is emitted in place of a normal query result for the first error and all subsequent results until the next <literal>PGRES_PIPELINE_SYNC</literal>; see <xref linkend="libpq-pipeline-errors"/>. PQgetResultは通常の非同期処理と同じように動作しますが、新しいPGresultPGRES_PIPELINE_SYNCPGRES_PIPELINE_ABORTEDが含まれる場合があります。 PGRES_PIPELINE_SYNCは、パイプラインの対応するポイントの各PQpipelineSyncごとに1回だけ報告されます。 最初のエラーに対する通常の問い合わせ結果の代わりにPGRES_PIPELINE_ABORTEDが出力され、次のPGRES_PIPELINE_SYNCまでのすべての結果が出力されます。を参照してください。

<function>PQisBusy</function>, <function>PQconsumeInput</function>, etc operate as normal when processing pipeline results. In particular, a call to <function>PQisBusy</function> in the middle of a pipeline returns 0 if the results for all the queries issued so far have been consumed. パイプラインの結果を処理する場合、PQisBusyPQconsumeInputなどは通常どおりに動作します。 特に、パイプラインの途中でPQisBusyを呼び出した場合、これまでに発行されたすべての問い合わせの結果が消費されていれば0を返します。

<application>libpq</application> does not provide any information to the application about the query currently being processed (except that <function>PQgetResult</function> returns null to indicate that we start returning the results of next query). The application must keep track of the order in which it sent queries, to associate them with their corresponding results. Applications will typically use a state machine or a FIFO queue for this. libpqは、現在処理されている問い合わせに関する情報をアプリケーションに提供しません(PQgetResultはNULLを返し、次の問い合わせの結果を返し始めることを示します)。 アプリケーションは、問い合わせを送信した順序を追跡し、対応する結果と関連付ける必要があります。 アプリケーションは通常、ステートマシンまたはFIFOキューを使用します。 エラー処理 #

<title>Error Handling</title>

From the client's perspective, after <function>PQresultStatus</function> returns <literal>PGRES_FATAL_ERROR</literal>, the pipeline is flagged as aborted. <function>PQresultStatus</function> will report a <literal>PGRES_PIPELINE_ABORTED</literal> result for each remaining queued operation in an aborted pipeline. The result for <function>PQpipelineSync</function> is reported as <literal>PGRES_PIPELINE_SYNC</literal> to signal the end of the aborted pipeline and resumption of normal result processing. クライアント側から見ると、PQresultStatusPGRES_FATAL_ERRORを返した後、パイプラインは中断されたフラグが立てられます。 PQresultStatusは、中断されたパイプラインの残りのキュー操作ごとにPGRES_PIPELINE_ABORTED結果を報告します。 PQpipelineSyncの結果はPGRES_PIPELINE_SYNCとして報告され、中断されたパイプラインの終了と通常の結果処理の再開を通知します。

The client <emphasis>must</emphasis> process results with <function>PQgetResult</function> during error recovery. クライアントは、エラー修復中にPQgetResultで結果を処理しなければなりません

If the pipeline used an implicit transaction, then operations that have already executed are rolled back and operations that were queued to follow the failed operation are skipped entirely. The same behavior holds if the pipeline starts and commits a single explicit transaction (i.e. the first statement is <literal>BEGIN</literal> and the last is <literal>COMMIT</literal>) except that the session remains in an aborted transaction state at the end of the pipeline. If a pipeline contains <emphasis>multiple explicit transactions</emphasis>, all transactions that committed prior to the error remain committed, the currently in-progress transaction is aborted, and all subsequent operations are skipped completely, including subsequent transactions. If a pipeline synchronization point occurs with an explicit transaction block in aborted state, the next pipeline will become aborted immediately unless the next command puts the transaction in normal mode with <command>ROLLBACK</command>. パイプラインで暗黙的なトランザクションが使用された場合、すでに実行された操作はロールバックされ、失敗した操作に続くキューに入れられていた操作は完全にスキップされます。 パイプラインが開始され、単一の明示的なトランザクションをコミットした場合(つまり、最初の文がBEGIN、最後の文がCOMMIT)と同じ動作が成立します。 ただし、セッションはパイプラインの終了時に中断されたトランザクション状態のままです。 パイプラインに複数の明示的なトランザクションが含まれている場合、エラー以前にコミットされたすべてのトランザクションはコミットされたままになり、現在進行中のトランザクションは中断され、後続のトランザクションも含めて後続のすべての操作は完全にスキップされます。 パイプライン同期ポイントが中断状態の明示的なトランザクションブロックで発生した場合、次のコマンドがROLLBACKを使用してトランザクションを通常モードにしない限り、次のパイプラインは即時に中断されます。


The client must not assume that work is committed when it <emphasis>sends</emphasis> a <literal>COMMIT</literal> &mdash; only when the corresponding result is received to confirm the commit is complete. Because errors arrive asynchronously, the application needs to be able to restart from the last <emphasis>received</emphasis> committed change and resend work done after that point if something goes wrong. クライアントは、COMMIT—を送信したときに作業がコミットされたと想定してはなりません。 コミットが完了したことを確認するために対応する結果を受信したときだけです。 エラーは非同期で到着するため、アプリケーションは最後に受信したコミット済みの変更から再起動し、何か問題が発生した場合にはその時点以降に行われた作業を再送信できる必要があります。 インターリーブ結果処理と問い合わせ登録 #

<title>Interleaving Result Processing and Query Dispatch</title>

To avoid deadlocks on large pipelines the client should be structured around a non-blocking event loop using operating system facilities such as <function>select</function>, <function>poll</function>, <function>WaitForMultipleObjectEx</function>, etc. 大規模なパイプラインでデッドロックを回避するためには、クライアントはselectpollWaitForMultipleObjectExなどのオペレーティングシステム機能を使用して、ノンブロッキングイベントループを中心に構築する必要があります。

The client application should generally maintain a queue of work remaining to be dispatched and a queue of work that has been dispatched but not yet had its results processed. When the socket is writable it should dispatch more work. When the socket is readable it should read results and process them, matching them up to the next entry in its corresponding results queue. Based on available memory, results from the socket should be read frequently: there's no need to wait until the pipeline end to read the results. Pipelines should be scoped to logical units of work, usually (but not necessarily) one transaction per pipeline. There's no need to exit pipeline mode and re-enter it between pipelines, or to wait for one pipeline to finish before sending the next. 通常、クライアントアプリケーションは、登録される残りの作業キューと、登録されたがまだ結果が処理されていない作業キューを維持する必要があります。 ソケットが書き込み可能な場合は、より多くの作業を登録する必要があります。 ソケットが読み取り可能な場合は、結果を読み取って処理し、対応する結果キュー内の次のエントリと一致させる必要があります。 使用可能なメモリに基づいて、ソケットからの結果は頻繁に読み取られる必要があります:結果を読み取るためにパイプラインが終了するまで待つ必要はありません。 パイプラインは、通常(必ずしも必要ではありません)パイプラインごとに1つのトランザクションである論理作業単位にスコープされる必要があります。 パイプラインモードを終了してパイプライン間で再入力したり、次のパイプラインを送信する前に1つのパイプラインが終了するのを待つ必要はありません。

An example using <function>select()</function> and a simple state machine to track sent and received work is in <filename>src/test/modules/libpq_pipeline/libpq_pipeline.c</filename> in the PostgreSQL source distribution. 送受信された作業を追跡するためにselect()と単純なステートマシンを使用する例は、PostgreSQLソース配布物のsrc/test/modules/libpq_pipeline/libpq_pipeline.cにあります。

34.5.2. パイプラインモード関連関数 #

<title>Functions Associated with Pipeline Mode</title>
PQpipelineStatus #

Returns the current pipeline mode status of the <application>libpq</application> connection. libpq接続の現在のパイプラインモード状態を返します。

PGpipelineStatus PQpipelineStatus(const PGconn *conn);

<function>PQpipelineStatus</function> can return one of the following values: PQpipelineStatusは以下のいずれかの値を返すことができます。


The <application>libpq</application> connection is in pipeline mode. libpq接続はパイプラインモードです。


The <application>libpq</application> connection is <emphasis>not</emphasis> in pipeline mode. libpq接続はパイプラインモードではありません


The <application>libpq</application> connection is in pipeline mode and an error occurred while processing the current pipeline. The aborted flag is cleared when <function>PQgetResult</function> returns a result of type <literal>PGRES_PIPELINE_SYNC</literal>. libpq接続はパイプラインモードで、現在のパイプラインの処理中にエラーが発生しました。 PQgetResultPGRES_PIPELINE_SYNC型の結果を返すと、中断フラグがクリアされます。

PQenterPipelineMode #

Causes a connection to enter pipeline mode if it is currently idle or already in pipeline mode. 接続が現在アイドル状態であるか、すでにパイプラインモードになっている場合、接続をパイプラインモードにします。

int PQenterPipelineMode(PGconn *conn);

Returns 1 for success. Returns 0 and has no effect if the connection is not currently idle, i.e., it has a result ready, or it is waiting for more input from the server, etc. This function does not actually send anything to the server, it just changes the <application>libpq</application> connection state. 成功した場合は1を返します。 接続が現在アイドル状態でない場合、つまり結果を準備している場合や、サーバからの入力を待っている場合などには0を返し、何の効果もありません。 この関数は実際にはサーバに何も送信せず、単にlibpq接続状態を変更します。

PQexitPipelineMode #

Causes a connection to exit pipeline mode if it is currently in pipeline mode with an empty queue and no pending results. 接続が現在パイプラインモードにあり、キューが空で、保留中の結果がない場合、接続はパイプラインモードを終了します。

int PQexitPipelineMode(PGconn *conn);

Returns 1 for success. Returns 1 and takes no action if not in pipeline mode. If the current statement isn't finished processing, or <function>PQgetResult</function> has not been called to collect results from all previously sent query, returns 0 (in which case, use <xref linkend="libpq-PQerrorMessage"/> to get more information about the failure). 成功した場合は1を返します。 パイプラインモードでない場合は、1を返し、何も行いません。 現在の文の処理が終了していない場合、またはPQgetResultが以前に送信されたすべての問い合わせから結果を収集するために呼び出されていない場合は、0を返します(この場合、失敗に関する詳細情報を取得するにはPQerrorMessageを使用します)。

PQpipelineSync #

Marks a synchronization point in a pipeline by sending a <link linkend="protocol-flow-ext-query">sync message</link> and flushing the send buffer. This serves as the delimiter of an implicit transaction and an error recovery point; see <xref linkend="libpq-pipeline-errors"/>. 同期メッセージを送信し、送信バッファをフラッシュすることにより、パイプラインの同期ポイントをマークします。 これは暗黙的なトランザクションとエラー修復ポイントの区切り文字として機能します。を参照してください。

int PQpipelineSync(PGconn *conn);

Returns 1 for success. Returns 0 if the connection is not in pipeline mode or sending a <link linkend="protocol-flow-ext-query">sync message</link> failed. 成功した場合は1を返します。 接続がパイプラインモードでないか、同期メッセージの送信に失敗した場合は0を返します。

PQsendFlushRequest #

Sends a request for the server to flush its output buffer. サーバに出力バッファをフラッシュする要求を送信します。

int PQsendFlushRequest(PGconn *conn);

Returns 1 for success. Returns 0 on any failure. 成功した場合は1を返します。 失敗した場合は0を返します。

The server flushes its output buffer automatically as a result of <function>PQpipelineSync</function> being called, or on any request when not in pipeline mode; this function is useful to cause the server to flush its output buffer in pipeline mode without establishing a synchronization point. Note that the request is not itself flushed to the server automatically; use <function>PQflush</function> if necessary. サーバは、PQpipelineSyncが呼び出された結果として、あるいはパイプラインモードでない要求があった場合に、自動的に出力バッファをフラッシュします。 この関数は、同期ポイントを確立せずに、サーバにパイプラインモードで出力バッファをフラッシュさせるのに便利です。 要求自体は自動的にサーバにフラッシュされないことに注意してください。 必要であればPQflushを使用してください。

34.5.3. いつパイプラインモードを使用するか #

<title>When to Use Pipeline Mode</title>

Much like asynchronous query mode, there is no meaningful performance overhead when using pipeline mode. It increases client application complexity, and extra caution is required to prevent client/server deadlocks, but pipeline mode can offer considerable performance improvements, in exchange for increased memory usage from leaving state around longer. 非同期問い合わせモードと同様に、パイプラインモードを使用する場合、意味のあるパフォーマンスオーバーヘッドはありません。 これはクライアントアプリケーションの複雑さを増加させ、クライアント/サーバのデッドロックを防ぐために特別な注意が必要ですが、パイプラインモードは、状態をより長く残すことによるメモリ使用量の増加と引き換えに、かなりのパフォーマンス改善を提供することができます。

Pipeline mode is most useful when the server is distant, i.e., network latency (<quote>ping time</quote>) is high, and also when many small operations are being performed in rapid succession. There is usually less benefit in using pipelined commands when each query takes many multiples of the client/server round-trip time to execute. A 100-statement operation run on a server 300 ms round-trip-time away would take 30 seconds in network latency alone without pipelining; with pipelining it may spend as little as 0.3 s waiting for results from the server. パイプラインモードは、サーバが離れている場合、つまりネットワーク遅延(ping時間)が大きい場合や、多数の小さな操作が連続して実行されている場合に最も便利です。 各問い合わせが実行するのにクライアント/サーバのラウンドトリップ時間の何倍もかかる場合、パイプラインコマンドを使用するメリットは通常少なくなります。 ラウンドトリップ時間が300ミリ秒離れたサーバ上で100文の操作を実行すると、パイプライン処理なしでネットワーク遅延だけで30秒かかります。 パイプライン処理を使用すると、サーバからの結果を待つのに0.3秒ほどしかかかりません。

Use pipelined commands when your application does lots of small <literal>INSERT</literal>, <literal>UPDATE</literal> and <literal>DELETE</literal> operations that can't easily be transformed into operations on sets, or into a <literal>COPY</literal> operation. 集合に対する操作やCOPY操作に容易に変換できない小さなINSERTUPDATEDELETE操作をアプリケーションが大量に行う場合は、パイプラインコマンドを使用してください。

Pipeline mode is not useful when information from one operation is required by the client to produce the next operation. In such cases, the client would have to introduce a synchronization point and wait for a full client/server round-trip to get the results it needs. However, it's often possible to adjust the client design to exchange the required information server-side. Read-modify-write cycles are especially good candidates; for example: パイプラインモードは、クライアントが次のオペレーションを生成するために1つのオペレーションからの情報を必要とする場合には便利ではありません。 このような場合、クライアントは同期ポイントを導入し、クライアント/サーバの完全なラウンドトリップを待機して、必要な結果を取得する必要があります。 ただし、クライアント設計を調整して、必要な情報をサーバ側で交換することも可能です。 読み取り-変更-書き込みサイクルは特に適した候補です。 たとえば、次のようになります。

-- result: x=2
-- client adds 1 to x:
UPDATE mytable SET x = 3 WHERE id = 42;

could be much more efficiently done with:

UPDATE mytable SET x = x + 1 WHERE id = 42;

Pipelining is less useful, and more complex, when a single pipeline contains multiple transactions (see <xref linkend="libpq-pipeline-errors"/>). 単一のパイプラインに複数のトランザクションが含まれている場合、パイプライン化はあまり有用ではなく、複雑になります(を参照)。

[15] The client will block trying to send queries to the server, but the server will block trying to send results to the client from queries it has already processed. This only occurs when the client sends enough queries to fill both its output buffer and the server's receive buffer before it switches to processing input from the server, but it's hard to predict exactly when that will happen. クライアントはサーバに問い合わせを送信しようとするのをブロックしますが、サーバは既に処理した問い合わせから結果をクライアントに送信しようとするのをブロックします。 これは、クライアントが出力バッファとサーバの受信バッファの両方を満たすのに十分な問い合わせを送信してから、サーバからの入力処理に切り替える場合にのみ発生しますが、いつ発生するかを正確に予測するのは困難です。