;; 每分钟扫描到期的远程仓库;任务进程负责真正的同步与并发锁。
(defmodule lamina_sync_sched
(behaviour gen_server)
(export all))
(defun tick-ms () 60000)
(defun local-refresh-ms () 600000)
(defun start-link ()
(gen_server:start_link #(local lamina_sync_sched)
'lamina_sync_sched 'none (list)))
(defun start_link () (start-link))
(defun init (_args)
(erlang:send_after 0 (self) 'tick)
`#(ok #M(last_local_refresh 0)))
(defun handle_call (message _from state)
(case message
('run-now
(run-due)
`#(reply 'ok ,state))
(_ `#(reply 'unknown ,state))))
(defun handle_info (message state)
(case message
('tick
(let ((now (erlang:monotonic_time 'millisecond))
(last (maps:get 'last_local_refresh state 0)))
(run-due)
(if (>= (- now last) (local-refresh-ms))
(refresh-local)
'ok)
(erlang:send_after (tick-ms) (self) 'tick)
`#(noreply ,(if (>= (- now last) (local-refresh-ms))
(maps:put 'last_local_refresh now state)
state))))
(_ `#(noreply ,state))))
(defun run-due ()
(case (catch
(lamina_db:q
"SELECT id FROM repositories WHERE source='remote' AND sync_interval_minutes IS NOT NULL AND sync_interval_minutes > 0 AND (last_synced_at IS NULL OR datetime(last_synced_at) <= datetime('now', '-' || sync_interval_minutes || ' minutes'));"))
(rows (lists:foreach #'spawn-sync/1 rows))
(_ 'ok)))
(defun spawn-sync (row)
(case row
(`(,repo-id) (spawn (lambda () (catch (lamina_sync_task:run-sync repo-id)))))
(_ 'ok)))
(defun refresh-local ()
(case (catch (lamina_db:q
"SELECT id,fossil_path FROM repositories WHERE source='local';"))
(rows (lists:foreach #'refresh-one/1 rows))
(_ 'ok)))
(defun refresh-one (row)
(case row
(`(,repo-id ,path) (spawn (lambda () (catch (lamina_sync_task:refresh-local-timeline-and-push repo-id path)))))
(_ 'ok)))
(defun terminate (_reason _state) 'ok)
(defun code-change (_old state _extra) `#(ok ,state))