ボストンにあるノースイースタン大学でコンピュータサイエンスの修士課程に在籍しているウトカルシュ・ジャダブです。edXでは過去13週間、プラットフォームチームと共に働いてきました。プラットフォームチームはOpen edX製品のインフラ構築、特にコード実行速度の向上に取り組んでいます。今回の投稿では、私の主要プロジェクトの一つであるコールスタックマネージャーについてお話ししたいと思います。これは、関数、メソッド、そしてDjangoモデルクラスのコールスタックを追跡するツールです。
コースウェア学生モジュール(CSM) の三脚とコースウェア学生モジュール履歴 (CSMH) edx-platform のコードは、問題に取り組む学生のユーザー状態とそれぞれの成績を維持する役割を担っています。edx.org ウェブサイトの学習者数は最近5万人に達しました。CSM と CSMH の規模の拡大により、ストレージ容量とデータベースの障害に関する深刻な懸念が生じています。コードベースとデータベースの容量が増加するにつれ、edx-platform において LMS や CMS といったモノリシックなコード領域を分割することは極めて重要です。
edX ユーザー ステート クライアント (eUSC)
元々、ユーザー状態クライアントの構造は次のとおりでした。

CSM/CSMH を介した edx プラットフォームと MySQL データベース間の通信を示すユーザー状態クライアントの以前の構造
建物全体が edx/edxプラットフォームユーザ状態クライアントは、次のMySQLテーブルと直接通信していました。 コースウェア_学生モジュール の三脚と コースウェア_学生モジュール履歴DjangoORM 経由。
インターンシップを始めた時点で、チームは既にこのアーキテクチャの欠点を認識していました。これを修正するために、私は次の図に示す提案されたアーキテクチャの実装に協力しました。

プラットフォームとデータベースバックエンドの間に edx-user-state-client 層を示すユーザー状態クライアントの提案構造
この新しいアーキテクチャでは、 edx ユーザー状態クライアント edxプラットフォームとデータベースバックエンド間のレイヤーとして機能します。このような構造には多くの利点があります。
- eUSCは、データベースへのすべての呼び出しが実行される抽象化されたレベルの単一のインターフェースとして機能します。このようなパターンは、最終的にはデータベースとの効率的な通信に役立ちます。
- eUSCでは、異なるバックエンド間の切り替えも容易になります。このAPIにより、分散タスクであってもバックエンドの選択が可能になります。
この構造を作るための最初のステップとして、私はリポジトリを作成しました。 edx/edx ユーザー状態クライアントこのリポジトリにはインターフェースが含まれています XBlockUserStateClientこれは、Django モデル クラスによるデータベースへのすべての呼び出しを処理します。
コールスタックマネージャー
その XBlockUserStateClient データベースへのすべての呼び出しは、このクラスが担当します。しかし、edXのコードベースの膨大な量、サードパーティの拡張機能(XBlocksなど)の使用、そして様々な場所でのデータベースへの多数の呼び出しを考慮すると、インターフェースを経由しないデータベースへの呼び出しをキャッチすることは確かに価値があります。 XBlockUserStateClient.
このニーズに応えるために、私は「 コールスタックマネージャーこれは、インターフェースを経由せず、データベースと直接通信する呼び出しを追跡できるライブラリです。コールスタックマネージャーは、このような呼び出しをLMSログに記録します。
ライブラリは 2 つの主要なデコレータを実装します。
- @トラックイット – 装飾されたエンティティを追跡する
- @donottrack – 装飾されたエンティティの追跡を停止します @トラックイット.
このライブラリを開発する主な目的は、CSMとCSMHのモデルクラスの呼び出しを追跡することでした。 学生モジュール の三脚と 学生モジュール履歴Djangoにおけるデータベースとの通信は、 Django 'Model' クラスモデルクラスは クエリセットAPI データベースを作成、取得、更新します。QuerySet APIによる呼び出しは、カスタムマネージャーを使用してオーバーライドできます。 コールスタックマネージャー – ライブラリCall Stack Managerで定義されています。これにより、データベースに直接アクセスするDjangoモデルクラスを追跡するという特殊なケースが処理されました。
初期バージョンの Call Stack Manager を実行しているときに、次の問題が発生しました。
- 通話ログが繰り返し作成され、LMS ログが乱雑になりました。
- すでに知っていた多くの通話が不必要に録音されました。
- 通話記録には不要なフレームが含まれていて、長くなり読みにくくなっていました。
これらの問題に対処するために、私は新しいデコレータを導入しました。 @donottrackは、このデコレータでデコレートされた関数のスコープの追跡を停止します。一般的に、追跡対象メソッドの呼び出しは、新しいインターフェース実装によって行われるものと、そうでないものという2つのカテゴリに分けられます。既に分かっていて期待されている呼び出し、つまり新しい実装からの呼び出しは、知らない呼び出しを捕捉することだけに興味があるので無視できます。したがって、この @donottrack デコレータを使用して、その実装によって行われた追跡対象の呼び出しを非表示にします。この時点では、追跡対象となる呼び出しは、新しい実装の外部から行われた呼び出しのみになります。
このようにして、データベースへの期待される既知の呼び出し(例えば、インターフェース経由)が XBlockUserStateClient)はログに記録されなかったため、不明な呼び出しが何を行っていたかを明確に把握できました。また、コールスタック内の重複フレームは正規表現フィルターを使用してフィルタリングされました。これにより、記録される呼び出しの数が減り、より正確になり、読みやすくなりました。
上記のコールスタックの例は次のとおりです。
新しいコールスタック番号 4 をログに記録しています:
ファイル「/edx/app/edxapp/edx-platform/lms/djangoapps/instructor/views/api.py」、行番号240、ラップされた
func(*args, **kwargs) を返す
ファイル「/edx/app/edxapp/edx-platform/lms/djangoapps/instructor/views/api.py」、行番号176、ラップされた
func(*args, **kwargs) を返す
ファイル「/edx/app/edxapp/edx-platform/lms/djangoapps/instructor/views/api.py」、行番号127、ラップされた
return func(request, *args, **kwargs)
ファイル「/edx/app/edxapp/edx-platform/lms/djangoapps/instructor/views/api.py」、行番号1896、rescore_problem
instruction_task.api.submit_rescore_problem_for_student(リクエスト、module_state_key、学生)
ファイル「/edx/app/edxapp/edx-platform/lms/djangoapps/instructor_task/api.py」、110行目、submit_rescore_problem_for_student
戻り値: submit_task(request, task_type, task_class, usage_key.course_key, task_input, task_key)
ファイル「/edx/app/edxapp/edx-platform/lms/djangoapps/instructor_task/api_helper.py」、346行目、submit_task
task_class.apply_async(task_args, task_id=タスクID)
ファイル「/edx/app/edxapp/edx-platform/lms/djangoapps/instructor_task/tasks.py」、行番号80、rescore_problem
run_main_task(entry_id, visit_fcn, action_name) を返します
ファイル「/edx/app/edxapp/edx-platform/lms/djangoapps/instructor_task/tasks_helper.py」、279行目、run_main_task
task_progress = task_fcn(エントリID、コースID、タスク入力、アクション名)
ファイル”/edx/app/edxapp/edx-platform/lms/djangoapps/instructor_task/tasks_helper.py”、行番号345、perform_module_state_update
modules_to_update = StudentModule.objects.filter(course_id=コースID、module_state_key__in=使用キー)
コールスタックマネージャライブラリの開発中、Djangoモデルクラスの効率的な処理、テストのために実行時にDjangoモデルクラスを作成する、関数のアイデンティティを失わないようにラップする、他のデコレータとの衝突を処理するなど、数多くの基本的なPythonレベルの問題を解決する必要がありました。 @契約 PyContracts など、他にも多数あります。
汎用ライブラリとしてのコールスタックマネージャ
コールスタックマネージャの主な目的は、 学生モジュール の三脚と 学生モジュール履歴さらに、コードのどのレベルでも任意のPython関数を追跡できます。必要に応じて追跡を停止することもできます。このライブラリを使用することで、不明な呼び出しを追跡することで、不要な関数を効果的に非推奨にすることができます。このツールをあらゆるDjangoプロジェクトに適用可能な汎用ツールとして開発していくことは興味深いでしょう。
結論
Call Stack Manager の一般化されたソリューションは、さらなる追加や変更を加えることで、Django/Python プロジェクトのプラグインまたは標準ライブラリとして使用できると強く信じています。
インターンシップの経験を振り返ると、edXのコードベースでの作業は大変楽しかったです。世界に大きな影響を与える大規模なオープンソースプロジェクトに携われることは、非常に刺激的です。edXは優秀なコーダーチームを擁し、インターン生を正社員のように扱い、あらゆるレベルで最大限の経験を積ませてくれました。私は最先端技術に携わり、プラットフォームチームとのあらゆる技術的な議論に参加しました。特に、Pythonの基礎レベルで作業し、珍しい予期せぬ問題を解決できたことは、大きなやりがいを感じました。John Eskew氏、Calen Pennington氏、Ali Mohammad氏、Brian Beggs氏、Miki Goyal氏、Adam Palay氏、そしてNed Batchelder氏には、継続的な支援とサポートをいただき、感謝申し上げます。edXでの仕事は、魅力的でやりがいのある機会であり、今後何年も大切にしていきたいと思います。
![]()