python SQLAlchemy 緩存問題
背景
公司自動化框架采用的python的 SQLAlchemy 進行數據庫的操作,在編寫一條自動化用例的時候發現,從mysql從獲取的數據不對,有個字段一直拿到錯誤的值(None)
自動化用例設計場景如下:
- 數據准備階段,自動化代碼刪除mysql中的數據;
- 數據准備階段,自動化代碼往mysql中insert一條數據;
- 業務代碼處理,update剛剛新增的數據;
- 斷言階段,自動化代碼獲取數據進行斷言;
過程
過程是坎坷的,而且一開始就跑錯了方向
明確現象
從mysql的角度來看,數據是update成功了的,符合預期了的,只是自動化代碼去拿數據拿錯了的
問題就是為什么自動化代碼拿不到那個字段的數據
推測
- 簡化重現流程,在sql_template層寫了一個main方式,直接通過sqlalchemy的方式去獲取數據,如下:
if __name__ == '__main__':
import pymysql, assertpy.assertpy
pymysql.install_as_MySQLdb()
import pkgutil
from features import model
for importer_sql, modname, ispkg_sql in pkgutil.walk_packages(path=model.__path__,
prefix=model.__name__ + '.',
onerror=lambda x: None):
exec('from ' + modname + ' import *')
sql_session = SqlTemplate()
params_dict = {
"material_type": 131,
"creative_source_id": 718567,
"adv_user_id": 148
}
expect_result = {
"deleted_at": "${~is_not_none}"
}
model_name = "AdvCreativeWarehouse"
result = sql_session.selectOne(eval(model_name), params_dict)
print(result)
result_dict = result.as_dict()
print(result_dict)
這里發現從mysql中取出的數據是對的,deleted_at字段取出來正確的值
那么排除了selectOne方法的問題,排除了AdvCreativeWarehouse這個實現類的問題
- 推測是不是因為pyc文件導致,在執行python文件的時候,還是引用的老的編譯文件
- 采用了python setup.py install 重新打包了項目
- 直接刪除pyc文件
find {指定目錄} -name "*.pyc" | xargs rm -rf
一頓報錯過來發現沒有解決問題
- 拆減重現步驟,簡化重現步驟,排除干擾項目
- 自動化代碼插入數據
- 手動update數據
- 自動化代碼讀取數據
發現重現了問題,排除業務代碼的問題
又發現了重大問題,c步驟取出來的數據,是a步驟插入的數據,意味着c取出來的數據可能是去到的緩存的數據
**
- 縮小范文,細讀代碼
- 重新review了自動化框架,發現insert的動作被封裝過,每次insert完會調用sqlalchemy的query查詢一次數據,第二次查詢其實也是同一條數據
- 直接google查詢sqlalchemy確實有緩存機制
- 解決問題
發現了問題就比較好解決,在query完后強制刷新,如下:
def selectOne(self, class_or_type_or_tuple, param):
obj = None
try:
obj = self.session.query(class_or_type_or_tuple).filter_by(**param).one()
self.session.expire_all() # 就是這句,強制刷新
except NoResultFound:
return None
except(MultipleResultsFound) as e:
print(e)
return obj
Expires all persistent instances within this Session
額外學習
pyc文件
.pyc文件是由.py文件經過編譯后生成的字節碼文件,其加載速度相對於之前的.py文件有所提高,而且還可以實現源碼隱藏,以及一定程度上的反編譯。
Python的程序中,是把原始程序代碼放在.py文件里,而Python會在執行.py文件的時候。將.py形式的程序編譯成中間式文件(byte-compiled)的.pyc文件,這么做的目的就是為了加快下次執行文件的速度。
所以,在我們運行python文件的時候,就會自動首先查看是否具有.pyc文件,如果有的話,而且.py文件的修改時間和.pyc的修改時間一樣,就會讀取.pyc文件,否則,Python就會讀原來的.py文件。
其實並不是所有的.py文件在與運行的時候都會差生.pyc文件,只有在import相應的.py文件的時候,才會生成相應的.pyc文件
編譯語言vs解釋語言
計算機是不能夠識別高級語言的,所以當我們運行一個高級語言程序的時候,就需要一個“翻譯機”來從事把高級語言轉變成計算機能讀懂的機器語言的過程。這個過程分成兩類,第一種是編譯,第二種是解釋。
編譯型語言在程序執行之前,先會通過編譯器對程序執行一個編譯的過程,把程序轉變成機器語言。運行時就不需要翻譯,而直接執行就可以了。最典型的例子就是C語言。
解釋型語言就沒有這個編譯的過程,而是在程序運行的時候,通過解釋器對程序逐行作出解釋,然后直接運行,最典型的例子是Ruby。
通過以上的例子,我們可以來總結一下解釋型語言和編譯型語言的優缺點,因為編譯型語言在程序運行之前就已經對程序做出了“翻譯”,所以在運行時就少掉了“翻譯”的過程,所以效率比較高。但是我們也不能一概而論,一些解釋型語言也可以通過解釋器的優化來在對程序做出翻譯時對整個程序做出優化,從而在效率上接近編譯型語言,而不能超過編譯型語言。
此外,隨着Java等基於虛擬機的語言的興起,我們又不能把語言純粹地分成解釋型和編譯型這兩種。
用Java來舉例,Java首先是通過編譯器編譯成字節碼文件,然后在運行時通過解釋器給解釋成機器文件。所以我們說Java是一種先編譯后解釋的語言。
參考資料
pyc相關
https://www.zhihu.com/question/30296617
https://blog.csdn.net/weixin_30345577/article/details/96035867?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-1&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-1
編譯語言vs解釋語言
https://www.cnblogs.com/zoe233/p/6993972.html(推薦)
sqlalchemy緩存問題
https://stackoverflow.com/questions/12108913/how-to-avoid-caching-in-sqlalchemy
https://www.thinbug.com/q/10210080