地獄からのネットワークデータの競合状態
別の開発者が作成した非常に古い(Encore 32/77)ワークステーションで同様のアプリケーションを使用するために、ネットワーククライアント/サーバー(Windows XP / C#)を作成していました。
アプリケーションが本質的に行ったことは、ホスト上の特定のデータを共有/操作して、システムを実行しているホストプロセスをファンシーPCベースのマルチモニタータッチスクリーンUIで制御することでした。
3層構造でこれを行いました。通信プロセスは、ホストとの間でデータの読み取り/書き込みを行い、必要なすべての形式変換(エンディアン、浮動小数点形式など)を行い、データベースとの間で値を読み書きしました。データベースは、通信とタッチスクリーンUI間のデータ仲介として機能しました。タッチスクリーンUIのアプリは、PCに接続されているモニターの数に基づいてタッチスクリーンインターフェイスを生成しました(これは自動的に検出されました)。
ホストとPCの間の値のパケットが与えられた時間枠では、ラウンドトリップあたり最大110ミリ秒の最大遅延で、一度に最大128個の値しかワイヤを介して送信できませんでした(UDPは、コンピューター)。そのため、接続されたタッチスクリーンの可変数に基づいて許可される変数の数は厳密に制御されていました。また、ホスト(リアルタイムコンピューティングに使用される共有メモリバスを備えた非常に複雑なマルチプロセッサアーキテクチャを備えています)は、私の携帯電話の約100分の1の処理能力を備えていたため、可能な限り少ない処理とサーバーこれを保証するために/ clientをアセンブリで記述する必要がありました(ホストは、プログラムの影響を受けない完全なリアルタイムシミュレーションを実行していました)。
問題はそうでした。一部の値は、タッチスクリーンで変更すると、新しく入力した値だけを取得するのではなく、その値と前の値の間でランダムに循環します。これは、特定のページの組み合わせを持ついくつかの特定のページのいくつかの特定の値でのみ、これまでに症状を示していました。最初の顧客受け入れプロセスを実行し始めるまで、問題をほぼ完全に見落としていました。
問題を特定するために、振動値の1つを選択しました。
- タッチスクリーンアプリを確認しましたが、振動していました
- 振動してデータベースを確認しました
- コミュニケーションアプリを確認し、振動しました
次に、wiresharkを開始し、パケットキャプチャの手動デコードを開始しました。結果:
- 振動していませんが、パケットが正しく見えなかったため、データが多すぎました。
コミュニケーションコードのすべての詳細を100回ステップスルーして、欠陥やエラーを見つけませんでした。
最後に、私は他の開発者にメールを送り始め、私が行方不明になったものがあるかどうかを確認するために彼の終わりがどのように働いたかを詳細に尋ねました。それから私はそれを見つけました。
どうやら、彼がデータを送信したとき、送信前にデータの配列をフラッシュしなかったため、本質的に、彼は古い値を上書きする新しい値で使用された最後のバッファを上書きしていましたが、上書きされていない古い値はまだ送信されています。
したがって、値がデータ配列の位置80にあり、要求された値のリストが80未満に変更されたが、その同じ値が新しいリストに含まれていた場合、両方の値は任意の特定のバッファーのデータバッファーに存在します与えられた時間。
データベースから読み取られる値は、UIが値を要求していたときのタイムスライスに依存していました。
修正は非常に簡単でした。データバッファ(実際にはパケットプロトコルの一部として含まれていた)に着信するアイテムの数を読み込み、そのアイテム数を超えてバッファを読み込まないでください。
学んだ教訓:
現代のコンピューティングパワーを当然のことと考えないでください。コンピューターがイーサネットをサポートしておらず、アレイのフラッシュが高価であると見なされる場合がありました。あなたが本当にどこまで来たかを見たいなら、動的なメモリ割り当ての形を実質的に持たないシステムを想像してください。IE、エグゼクティブプロセスは、すべてのプログラムにすべてのメモリを順番に事前に割り当てる必要があり、プログラムはその境界を超えて成長することはできませんでした。IE、システム全体を再コンパイルせずにプログラムにより多くのメモリを割り当てると、大規模なクラッシュを引き起こす可能性があります。いつか人々は同じ光の中でゴミ収集前の日について話すのだろうか。
カスタムプロトコルを使用してネットワーキングを実行する場合(または一般的にバイナリデータ表現を処理する場合)、パイプを介して送信されるすべての値のすべての機能を理解するまで、必ず仕様を読んでください。つまり、目が痛くなるまで読んでください。個々のビットまたはバイトを操作してデータを処理する人は、非常に賢明で効率的な方法を使用します。最も細かい部分がないと、システムが壊れる可能性があります。
修正に要する全体の時間は2〜3日でしたが、そのことにほとんどの時間を費やして他のことに取り組みました。
SideNote:問題のホストコンピューターはデフォルトでイーサネットをサポートしていませんでした。それを駆動するカードはカスタムメイドで改造され、プロトコルスタックは事実上存在しませんでした。私が一緒に働いていた開発者はプログラマの地獄でした。彼はこのプロジェクトのシステムにUDPの完全なバージョンと最小限の偽のイーサネットスタック(プロセッサがフルイーサネットスタックを処理するほど強力ではなかった)を実装しただけではありませんでも彼は一週間もしないうちにそれをやった。彼はまた、そもそもOSを設計およびプログラムした元のプロジェクトチームリーダーの一人でした。彼がコンピューター/プログラミング/アーキテクチャーについて共有しなければならなかったことは、どれだけ長く巻き込まれていても、私がどれだけ新しくても、すべての言葉を聞いていました。