10

【茶包射手日記】偶發型 Oracle Client 版本不相容錯誤

 3 years ago
source link: https://blog.darkthread.net/blog/shared-apppool-oracle-ver-issue/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
【茶包射手日記】偶發型 Oracle Client 版本不相容錯誤-黑暗執行緒

同事報案,有個 ASP.NET 網站「偶爾」會出現提供者與 Oracle 從屬版本不相容錯誤。這是 Unmanaged ODP.NET 的經典茶包, 舉凡 Oracle Client 沒裝好、存取權限跑掉多版本並存都很容易踩到雷。(由衷建議大家改用 Managed ODP.NET,能有效改善生活品質)

但這次跟以前遇到的狀況不太一樣,不定期發生,出錯後 IISRESET 可恢復,若單純只是 Oracle Client 安裝或設定問題,不該時好時壞,看起來是枚變種茶包。

由同事提供資訊我找到一處可疑:該台 IIS 有多個 WebApplication,且部分 WebApplication 共用 AppPool。於是我大膽假設:會不會是共用 AppPool 的 WebApplication 使用不同版本 Oracle 彼此干擾?而啟動順序又決定是否出錯,例如:WebAP1 先啟動再跑 WebAP2 沒事;若 WebAP2 先起來則會讓 WebAP1 壞掉。

此推論若能成立,「不定期發生」跟「IISRESET 後正常」便可得到合理解釋。

我設計了一個實驗驗證假設,在 IIS 建立兩個 WebApplication - OraAP12 及 OraAP19,放入同一隻程式 default.aspx:

<%@ Page Language="C#" %>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
	var cs = "Data Source=MYORA;User ID=*****;Password=****;";
	var codeBase = typeof(Oracle.DataAccess.Client.OracleConnection).Assembly.CodeBase;
	Response.ContentType = "text/plain";
	try {
		using (var cn = new Oracle.DataAccess.Client.OracleConnection(cs))
		{
			cn.Open();
			var cmd = cn.CreateCommand();
			cmd.CommandText = "SELECT 'HELLO' AS t FROM DUAL";
			var dr = cmd.ExecuteReader();
			dr.Read();
			Response.Write(dr["t"] + "\n" + codeBase);
		}
	}
	catch (System.ApplicationException ex) 
	{
		Response.Write("ERROR-" + ex.Message);
		if (ex.InnerException != null) 
		{
			Response.Write("\n" + ex.InnerException.Message);
		}
	}
}
</script>

兩個資料夾只差在 bin\Oracle.DataAccess.dll 版本不同,OraAP12 是 2.122.1,OraAP19 則是 2.122.19,二者共用同一個 AppPool。

正常執行的話應要看到 HELLO 及 ODP.NET 版號:(遇有一個現象值得注意,Inline ASPX 採現場編譯,bin\Oracle.DataAccess.dll 會決定參照版本,但因 GAC 已註冊,實際用的是 GAC 版本。延伸閱讀:ASP.NET /bin 組件載入跟你想的不一樣)

來看看先啟動 OraAP12 或先啟動 OraAP19 結果會不會不一樣? 這種情境,Windows Terminal 的分割窗格特別好用,在 IISRESET 之後先啟用 OraAP19 再啟用 OraAP12 (下圖上方),兩個 WebApplication 都正常; 若 IISRESET 後先啟用 OraAP12 再啟用 OraAP19,則 OraAP19 會發生 'Oracle.DataAccess.Client.OracleConnection' 的類型初始設定式發生例外狀況。無法載入 DLL 'OraOps19.dll': 找不到指定的程序。 (發生例外狀況於 HRESULT: 0x8007007F) 錯誤。

由實驗結果可知- 若 WebApplication 共用 AppPool 又使用不同版本 Unmanaged ODP.NET,二者可能互相干擾,且出錯與否由 WebApplication 啟動順序決定。

因此,實務上應避免共用 AppPool,除了避免互相干擾,網站發生問題時能從工作管理員確認各 WebApplication 使用的 CPU、記憶體,有利於釐清問題。

最後不免推一把 - 多想兩分鐘,你可以不要用 Unmanaged ODP.NET,改用 Managed ODP.NET 人生更快活。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK