【问题标题】:When I have required model relationships, how do I guard against errors?当我需要模型关系时,如何防止错误?
【发布时间】:2009-04-29 19:12:02
【问题描述】:

我有一个应用程序,它有很多相互依赖的数据库关系才能成功运行应用程序。应用程序中的铰链是一个名为 Schedule 的模型,但该计划将拉取 Blocks、一个 Employee、一个 JobTitle 和一个 Assignment(除此之外,每个 Block 也会从数据库中拉取一个 assignment)到制定全天的员工时间表。

在构建应用程序时,我非常重视验证,以确保在将所有内容保存到数据库之前,所有部分都必须到位。到目前为止,效果非常好,该应用程序已经上线并运行了近 6 个月,每月处理大约 150,000 个请求,没有出现任何问题或错误。直到上周。

上周,当有人更改日程时,看起来数据库出错了,并且日程被保存到数据库中,但其分配丢失。因为在每个视图中都会调用关联,所以无论何时从数据库调用此调度,应用程序都会抛出 NoMethod 错误以调用 nil。

在按照我所说的方式设计应用程序时,您是否防止数据库/验证部分可能出现故障?如果是这样,您如何以编程方式防御它?在将其发送到视图之前,您是否检查了每个关系以确保它不为零?

我知道这个问题泛泛而谈,如果我能更具体地表达我的意思,请在 cmets 中告诉我。

【问题讨论】:

    标签: ruby-on-rails ruby model


    【解决方案1】:

    我建议添加database-enforced foreign key constraintswrapping important groups of operations into transactions

    如果 Schedule 和 Assignment 之间有外键,数据库强制外键约束会阻止错误插入。此外,如果您将特定操作包装在事务中,您可以确保插入/更新/删除的整个流发生或失败,恢复到干净状态。

    【讨论】:

    • IIRC,在大多数数据库中,FK 约束可以应用于可为空的列,在这种情况下,它不会保护 OP 免受问题的影响。也许有一些关于 Ruby/Rails/特定数据库的特定内容会阻止这种情况......我的回答假设 FK 约束已经存在。
    • 我没有使用数据库强制的 FK 约束,只有 Rails 验证来确保子对象被创建和存在。谢谢(你的)信息!我将研究 FK 约束!我已经将多个数据库操作封装在一个事务中,但是我的数据库必须支持事务才能真正获得这种好处,不是吗?
    • 这就是 DBMS 应该强制执行约束的原因 - 以防万一在不定数量的应用程序中出现错误时出现故障。尚不完全清楚计划表是否具有引用分配表的外键列;但是,如果确实如此,则插入将失败并且不会发生问题。这是因为有一个 DBMS 并且可能有许多应用程序共享数据,所以验证应该由 DBMS 完成,因为应用程序都使用 DBMS,因此您可以获得最大的一致性。
    • 你是对的乔纳森,计划表确实包含分配表的外键。除了 FK 约束之外,还有模型验证是多余的吗? Rails 错误报告是否适用于 DBMS FK 约束,或者我是否继续在我的模型中使用 Rails 验证来加倍保护? PS,再次感谢您提供的所有信息!
    • 我会说它是提供冗余检查的数据库(这没关系 - 我们都需要我们的备份资源) - 但你不应该让数据库异常弹出到 UI。当操作发生在业务层时,数据库更难进行单元测试。
    【解决方案2】:

    除了您的验证以及添加其他答案中提到的一些数据库约束之外,您还可以运行一个后台作业,该作业会定期扫描数据库以查找孤儿。

    当它找到一个时,它会清理它(如果可能的话),或者将其删除,或者只是将其标记为非活动状态并向您发送电子邮件,以便您稍后查看。根据数据的数量和性质,每分钟一次、每小时一次、每天一次……

    这样,即使您采取了任何保护措施,如果不良数据确实进入了,您将早日知道它。

    【讨论】:

    • 我们在 Stack Overflow 上执行此操作 - 由于我们对大量数据进行非规范化以提高查询性能,因此我们有一些后台任务可以确保数据的有效性。
    • 这也是一个绝妙的主意,我已经在使用 backgroundrb 来扫描数据库以删除数据。 . .添加另一个工人以确保我没有孤立的对象将是一个很好的检查!谢谢!
    • 拥有一致性检查例程是个好主意。但这应该是为了解决问题,而不是清理碎片。如果您一直看到相同的不良数据,则情况尚未得到控制。
    【解决方案3】:

    我会就此争论非传统的智慧。您描述的约束不属于数据库,它们属于您的 OO 代码。 “数据库出错”这不是真的,毫无疑问,应用程序是插入不正确验证数据的东西。

    当您开始期望数据库承担这些检查的负担时,您就是在将业务规则放入架构中。至少,这会使编写单元测试变得更加困难(这可能是您一开始就应该发现这一点的地方;但现在是您添加另一个测试的机会。)

    理想情况下,您应该能够将 RDBMS 替换为其他一些通用数据存储,并且在适当的其他位置仍保持所有功能逻辑正确激活且未更改。 UI 不应该与 DAL 对话,更不用说直接处理数据库异常了。

    如果需要,您可以添加额外的数据库约束,但它应该严格作为备份。如您所见,优雅地处理数据库结构错误(尤其是涉及 UI 时)要困难得多。

    【讨论】:

    • 总的来说,我同意你的看法,但它会成为像 validates_uniqueness_of 这样的问题。由于多个同步进程,它实际上不能保证唯一性。
    • 并发进程本身不是问题,但可能是多个 Ruby 进程(我不使用 ActiveRecords,所以我不能说。)但是我已经实现了几乎你描述的调度应用程序,而且我确实需要将它设计为允许同时使用的用户,并且它不需要任何英勇的数据库编程。我只是讨厌看到一个受欢迎的答案暗示数据库不可避免地必须处理它是不可避免的,因为它无法在适当的抽象层中处理。不幸的是,这是传统观念。
    • 由于 Rails 是单线程的,传统的 Rails 安装可能有数百个单独的 Ruby 进程访问数据库。因此,在 validates_uniqueness_of 检查唯一性和保存记录之间,另一个进程可能会保存相同的记录。现在,如果你真的想保证 Rails 中的唯一性(你可能不会——那是一个完全独立的帖子!),很遗憾,数据库约束是你唯一的选择。
    • 如果你觉得有用,我会一次拿这些。 1)你说“你检查每一个关系以确保它在发送到视图之前不是 nil 吗?”你有几个选择。首先,您正在选择一个认为它有一个作业的计划,但发现了什么?分配键值?空值。那你拉它干嘛?如果上下文需要,您的查询不应选择没有分配的计划。 2)您指的“唯一性”检查到底是什么?我认为问题中没有描述。
    • 3) 可以有没有时间表的作业,就像有没有作业的时间表一样?如果不是,那么分配记录应该是时间表的关键,反之亦然。要查看是否分配了计划,您应该加入这些表。 (无论如何我可能会这样做。)
    【解决方案4】:

    如果它必须是真实的,应用程序才能运行,这就是 assert()s 的真正用途。我几乎没有使用过 Ruby,但我想它一定有这个概念。使用它们在整个代码的各个位置强制执行先决条件。结合清理和验证您的外部(用户)输入应该足以保护您。我认为,如果经过这么多检查后出现问题,您的应用程序理所当然地允许崩溃(当然,以受控方式)。

    我怀疑您遇到的问题是您的数据库中的错误。更有可能您忽略了验证中的某些极端情况。

    【讨论】:

      猜你喜欢
      • 2023-03-23
      • 2022-11-03
      • 2023-04-02
      • 1970-01-01
      • 2014-10-23
      • 1970-01-01
      • 1970-01-01
      • 2014-03-21
      • 2011-10-13
      相关资源
      最近更新 更多