2015-08-20 65 views
3

我有,有一個自我的借鑑的模型通過一個連接表時,定義如下:與自我的借鑑記錄防止無限循環

class Task < ActiveRecord::Base 
    has_many :dependency_dependents, foreign_key: :dependency_id, class_name: 'TaskDependency', dependent: :destroy 
    has_many :dependency_dependencies, foreign_key: :task_id, class_name: 'TaskDependency', dependent: :destroy, autosave: true 

    has_many :dependencies, through: :dependency_dependencies 
    has_many :dependents, through: :dependency_dependents, source: :task 
end 

class TaskDependency < ActiveRecord::Base 
    belongs_to :task 
    belongs_to :dependency, class_name: 'Task' 
end 

一切與加入效果很好。當記錄被保存時,它會根據它所依賴的記錄執行一堆計算,然後它會更新依賴記錄,然後對這些記錄執行相同的計算等等。

問題是,如果依賴任務鏈中的某處存在對某個任務上的其他任務的依賴關係,首先計算失敗,但最重要的是,它會導致無限循環的計算和更新。

有沒有一種很好的方式可以在保存記錄之前檢查此無限循環(理想情況下,在首先創建依賴關係之前)。

我很高興做到這一點,無論是使用純SQL或紅寶石,只是想知道如果任何人有一個乾淨的解決方案。

+0

Hm,週期檢測在鄰接列表中。它可以通過觸發器完成,但是必須鎖定表以確保沒有併發插入,否則可以通過兩個併發插入來創建循環,每個插入單獨確定,但一起出現問題。 –

+0

我一直在玩關於postgres的遞歸查詢。想想如果我在插入新的依賴項之前列出所有的依賴項,並且如果它已經存在於鏈中的某處,請不要保存它。但是對於兩個併發插入,你仍然是正確的:S – PaReeOhNos

回答

0

對,我想我已經明白了。當記錄驗證時,它會調用一個新的驗證方法,該方法運行一個遞歸查詢,該查詢抽取下面的層次結構中的所有ID,並且如果在依賴項列表中找到了您嘗試創建依賴關係的任務的ID ,那麼驗證失敗。對此的查詢如下:

WITH RECURSIVE dependencies(task_id, path) AS (
    SELECT task_id, ARRAY[task_id] 
    FROM task_dependencies 
    WHERE dependency_id = #{self.id} 

    UNION ALL 

    SELECT task_dependencies.task_id, path || task_dependencies.task_id 
    FROM dependencies 
    JOIN task_dependencies ON dependencies.task_id = task_dependencies.dependency_id 
    WHERE NOT task_dependencies.task_id = ANY(path) 

) 

SELECT task_id id FROM dependencies ORDER BY path 

永遠不知道,也許這會幫助沿途的人。 正如@ craig-ringer所提到的那樣,仍然有兩個併發插入導致競爭條件的可能性,但是這個查詢也可以鎖定表以防止在必要時發生這種情況。