我知道这个标题很复杂,所以我会试着给出一个我想要做的简单例子。请跟我说一下。
我正在进行选项值计算和投资组合风险分析服务器,我遇到了一个问题,我不太确定在加载持久化对象图然后访问一些缓存属性时如何处理ve动态添加数据库负载。
说我有以下映射类:
Base = declarative_base()
class Portfolio(Base):
__tablename__ = "portfolios"
id = Column(Integer, primary_key=True)
asset_classes = relationship("AssetClass", backref="portfolios")
class AssetClass(Base):
__tablename__ = "asset_classes"
id = Column(Integer, primary_key=True)
portolio_id = Column(Integer, ForeignKey("portfolios.id"))
assets = relationship("Asset", backref="asset_classes")
class Asset(Base):
id = Column(Integer, primary_key=True)
asset_class_id = Column(Integer, ForeignKey("asset_classes.id"))
然后我加载一个Portfolio并按如下方式进行初始化:
session = Session()
# pretend there's only one Portfolio
portfolio = session.query(Portfolio).one()
init_portfolio(portfolio)
while(keep_server_alive):
# session stays open for server life
gevent.sleep(0)
session.close()
def init_portfolio(p):
# note that this is a dynamically added property
portfolio.values_store = ValuesStore()
for asset_class in p.asset_classes:
init_asset_class(asset_class)
def init_asset_class(ac):
for asset in ac.assets:
init_asset(asset)
一旦所有内容都被初始化,我使用一个长期运行的gevent线程来处理价格更新并计算某些派生值,如下所示:
def on_underlying_update(asset, underlying_price):
# the next line non-deterministically fails with an AttributeError
values_store = asset.asset_class.portfolio.values_store
values_store.get_asset_price(asset, underlying_price)
这些计算可能非常昂贵,因此ValuesStore对象具有存储结果的缓存策略。在随机传递on_underlying_update函数时,我将得到一个AttributeError,指出Portfolio对象没有values_store属性。
起初这让我很困惑,但是当我调试这个问题时,我似乎发现引用同一个Portfolio的子AssetClass对象实际上可能引用了内存中的不同对象。在使用python的id()函数在每次访问期间检查Portfolio.id与内存ID之后,我开始相信这一点。这样做,我会经常发现虽然我可能只访问过一个Portfolio.id,但我引用了几个不同的内存地址,具体取决于哪个孩子正在访问。
我知道这是漫长的啰嗦,我感谢任何一直困扰着我的人。我的问题是,有没有办法让我将整个对象图一次加载到内存中一次?从资源的角度来看,这个ValuesStore对象可能很昂贵,我宁愿尽可能少地实例化它。
作为参考,我使用的是Python 2.7.3,SQLAlchemy 0.8,gevent 1.0rc2,我在Windows 7和OS X 10.8上都遇到了同样的问题。
再次感谢。
**编辑**
我花了很多时间浏览我的代码,寻找会导致Portfolio对象与当前会话分离并附加到新会话的语句。我找不到那种东西。我试图收听各种Instance和Session事件,每次都可以看到正在加载的“new”投资组合,但它是由原始Session加载的。
Portfolio对象是否可能被垃圾收集?我尝试通过生成一个新的Gevent线程来测试这个假设,以保持对Portfolio的强引用,如下所示:
def keep_portfolio_alive(p):
portfolio = p
while True:
gevent.sleep(0)
这似乎解决了这个问题。我从未看到Session第二次加载该组合。
**编辑2 **
所以我不确定,但我相信我发现了自己的问题。在定义映射类之间的关系时,我实际上使用了attribute_mapped_collections而不是普通列表。在初始化类时,我会迭代子对象,如下所示:
for asset_class in portfolio.asset_classes.itervalues():
# do something
由于其中一些类是多重嵌套的,因此沿着层次结构有一个使用此复制迭代器引用的类,然后使用另一个复制迭代器引用它自己的子类。我最好的猜测是,Portfolio类最终没有它与它的父对象之间或它及其任何子对象之间的强引用。然后GC在某个时刻将其标记为收集。
希望这有助于将来。