XPathScanner and XPathHandler build static lookup tables without synchronization; concurrent first use throws "Item has already been added"
まだ誰も着手していません。
評価
調査の方向性
XPathScanner.FunctionTable、XPathScanner.AxesTable、XPathHandler.MethodTable を読み、遅延初期化を比較して、適用する同期パターンを特定します。提供されたマルチスレッドの XPathExpression コンソールアプリで競合を再現し、並行コンパイルで Item has already been added が発生しなくなり、使用前に各テーブルが完全に初期化されることを確認します。
索引モデルが issue の本文から書いたものです。
説明
Summary
XPathScanner.FunctionTable, XPathScanner.AxesTable and XPathHandler.MethodTable in Microsoft.SqlServer.Management.Sdk.Sfc are static Hashtables built lazily on first use with no lock. When several threads compile their first XPath containing a function call at the same time, two of them fill the same table and one throws ArgumentException: Item has already been added. The same race can let a thread read a table another is still filling.
This is reached through ordinary SMO use from separate Server instances on separate threads, for example Database.PrefetchObjects or Scripter.Script, since URN filters such as [@IsSystemObject=false()] are compiled through it.
Repro
No server needed. Run this console app repeatedly:
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlServer.Management.Sdk.Sfc;
const int threads = 16;
using var barrier = new Barrier(threads);
var tasks = Enumerable.Range(0, threads).Select(i => Task.Factory.StartNew(() => {
barrier.SignalAndWait();
_ = new XPathExpression("Server/Database[@Name='db']/Table[@IsSystemObject=false()]");
}, TaskCreationOptions.LongRunning)).ToArray();
try {
Task.WaitAll(tasks);
return 0;
} catch (AggregateException e) {
Console.WriteLine(e.InnerExceptions[0].GetType().Name + ": " + e.InnerExceptions[0].Message);
return 1;
}
Over 200 runs, 199 failed:
197 ArgumentException: Item has already been added. Key in dictionary: 'position' Key being added: 'position'
2 ArgumentException: Item has already been added. Key in dictionary: 'last' Key being added: 'last'
Compiling one such expression on a single thread before starting the others prevented it: 0 failures in 200 runs.
The same exception from Database.PrefetchObjects called on 8 threads, each with its own Server and connection:
System.ArgumentException: Item has already been added. Key in dictionary: 'position' Key being added: 'position'
at System.Collections.Hashtable.Insert(Object key, Object nvalue, Boolean add)
at Microsoft.SqlServer.Management.Sdk.Sfc.XPathScanner.get_FunctionTable()
at Microsoft.SqlServer.Management.Sdk.Sfc.XPathHandler.ParseMethod(AstNode qyInput)
...
at Microsoft.SqlServer.Management.Sdk.Sfc.XPathExpression.Compile(String strXPathExpression)
at Microsoft.SqlServer.Management.Sdk.Sfc.UrnImpl.get_XPathExpression()
at Microsoft.SqlServer.Management.Smo.SqlSmoObject.InitChildLevel(Urn levelFilter, ScriptingPreferences sp, Boolean forScripting, IEnumerable`1 extraFields)
at Microsoft.SqlServer.Management.Smo.Database.PrefetchStoredProcedures(ScriptingPreferences options)
at Microsoft.SqlServer.Management.Smo.Database.PrefetchObjectsImpl(Type objectType, ScriptingPreferences scriptingPreferences)
Cause
Each getter assigns the field before filling it, with no synchronization:
internal static Hashtable FunctionTable {
get {
if (_FunctionTable == null) {
_FunctionTable = new Hashtable(36);
_FunctionTable.Add("last", XPathTokenType.FuncLast);
_FunctionTable.Add("position", XPathTokenType.FuncPosition);
...
XPathScanner.AxesTable and XPathHandler.MethodTable follow the same pattern. Building each table in a static initializer, or through Lazy<T>, would remove the race.
Environment
- Microsoft.SqlServer.SqlManagementObjects 181.37.1, .NET 10, Linux x64
- 主要言語
- C#
- スター
- 144
- フォーク
- 29
- 平均マージ
- 2日 1時間
- マージ済み PR(30日)
- 5
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
microsoft/sqlmanagementobjects のほかの issue
-
Path parsing functions are incorrect when running on Linux対応中かも @shueybubbles が 31 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
-
難易度 3/5 1〜2日 初心者へのやさしさ 72/100
-
難易度 3/5 1〜2日 初心者へのやさしさ 72/100
-
難易度 4/5 3〜5日 初心者へのやさしさ 74/100
microsoft/sqlmanagementobjects の issue をすべて見る
似ている issue
-
请让 FPS 叠加层的字号大小可单独设置オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
MahdiyarGHD/GheychiApp#39 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1時間未満 初心者へのやさしさ 90/100
-
bug effort:S P3
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
nightscout/nocturne#3136 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100