为什么我的续集模型实例会丢失其ID?

时间:2019-06-13 15:11:01

标签: javascript node.js sequelize.js

我在postgres上构建了一个基于节点的微服务,它使用sequelize执行查询。我有一张Pets表,每个表都有一个id(uuid)和一个name(字符串)。而且,我有一个用于按名称从数据库中获取Pets的函数,该函数包装了看起来很讨厌的sequelize调用:

async function getPetByName( petName ) {
    const sqlzPetInstance = Database.Pet.findOne({
        where: { name: { [Sequelize.Op.iLike]: petName } }
    })

    if(!sqlzPetInstance) return undefined
    return sqlzPetInstance
}

效果很好。

稍后,为了提高性能,我向该函数添加了一些非常短暂的缓存,如下所示:

async function getPetByName( petName ) {
    if( ramCache.get(petName) ) return ramCache.get(petName)

    const sqlzPetInstance = await Database.Pet.findOne({ ... })
    if(!sqlzPetInstance) return undefined

    return ramCache.set(petName, sqlzPetInstance) // persists for 5 seconds
}

现在,我注意到从缓存中投放的项目有时会删除其id道具! WTF?!

我添加了日志记录,发现ramCache条目仍在可靠地定位,并且该值仍然是sqlz Pet模型的实例。模型上的所有其他属性仍然存在,但是dataValues.id未定义。我还注意到_previousDataValues.id的值正确,这对我来说确实是我想要的模型实例,但是由于某些原因而进行了修改。

这可以解释什么?如果获得该模型的调用者通过分配给id来对其进行突变,这是我会看到的吗?是什么导致_previousDataValuesdataValues分开?我可以使用一些很酷的sqlz技术来捕获问题的根源(也许通过定义记录或抛出的自定义设置器)?


编辑:实验表明,我 无法通过分配id来覆盖它。太酷了,但是现在我几乎没有主意了。如果这不是某种不负责任的突变(我可以防止这种突变),那么我就不会想到会导致删除ID的任何sqlz实例方法。

1 个答案:

答案 0 :(得分:0)

我没有抽烟的枪,但是我可以描述我所写的修复方法以及形成该修复方法的假设。

正如我所说,我将序列化模型实例存储在RAM中:

ramCache[ cacheKey ] = sqlzModelInstance

我的假设是,通过为每个调用者提供相同的实例,我造成了一种顽皮的调用者可以使共享实例发生变异的情况。

我从不知道这种突变是如何发生的。通过实验证明,我无法通过覆盖来修改id属性:

// this does not work
sqlzModelInstance.id = 'some-fake-id'
// unchanged

但是,我阅读了sqlz文档中的a few things,该文档建议每个实例都保留某种指向中央授权机构的不可见链接,因此有可能“远距离执行怪异的动作”。

因此,为了切断该链接,我修改了缓存系统以存储原始数据(而不是sqlz模型实例),并在检索时自动重新水合该原始数据。

严重:

function saveInCache( cacheKey, sqlzModelInstance ) {
    cache[ cacheKey ] = sqlzModelInstance.get({ plain: true })
}

function getFromCache( cacheKey ) {
    let data = cache[ cacheKey ]
    if(!data) return undefined
    return MySqlzClass.build( data, { isNewRecord: false, raw: true } )
}

我从来没有找到调皮的调用者-我的常规做法是避免更改参数,因此不太可能发生任何直接的更改-但我描述的更改已修复了我遇到的易于复制的错误。所以,我认为我的假设是正确的。


我将暂时避免将我的答案标记为正确,以希望有人能进一步阐明问题。