单个SQLAlchemy映射对象导致内存中的许多不同实例

时间:2013-02-14 20:24:03

标签: python sqlalchemy

我知道这个标题很复杂,所以我会试着给出一个我想要做的简单例子。请跟我说一下。

我正在进行选项值计算和投资组合风险分析服务器,我遇到了一个问题,我不太确定在加载持久化对象图然后访问一些缓存属性时如何处理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在某个时刻将其标记为收集。

希望这有助于将来。

0 个答案:

没有答案