在谷歌搜索和寻找示例很长一段时间后,我找不到任何迹象表明,当数据库表发生变化时,是否可以在客户端应用程序中获得通知“回调”。
postgresql C库支持该特性(至少在某种程度上),因此数据库本身似乎支持此用例。
CREATE TABLE IF NOT EXISTS disco (
instance TEXT NOT NULL,
service TEXT NOT NULL,
attributes TEXT[] DEFAULT '{}',
endpoints TEXT[],
CONSTRAINT service_instance PRIMARY KEY(instance,service));基于上面给出的表,我使用postmodern PostgreSQL包创建了一个小的Lisp包,它允许广告一些服务(它是一个玩具服务发现,由数据库支持)。
当然,除了广告之外,应用程序还需要等待/搜索活动服务。由于轮询显然不是可行的方式,因此需要某种形式的通知。在can级别,应用程序可以使用select()等待套接字,连接到数据库和PQNotify()或类似的东西。
我的问题是,如果postmodern也支持这个用例,我正在寻找一个例子。
这是我到目前为止所做的LISP包。
(ql:quickload :postmodern)
(defpackage :disco
(:use :common-lisp :postmodern)
(:export
:connect-toplevel
:ensure-disco-table
:advertise
:stop-advertise
:advertised)
(:shadow :connect-toplevel))
(in-package :disco)
(defun ensure-disco-table ()
(postmodern:query "CREATE TABLE IF NOT EXISTS disco (
instance TEXT NOT NULL,
service TEXT NOT NULL,
attributes TEXT[] DEFAULT '{}',
endpoints TEXT[],
CONSTRAINT service_instance PRIMARY KEY(instance,service));"))
(defun connect-toplevel ()
(progn
(postmodern:connect-toplevel "postgres" "postgres" "password" "localhost")
(ensure-disco-table)))
(defun advertise (instance service endpoints &optional attributes)
(let ((attribs (if attributes attributes #())))
(postmodern:query (:insert-into 'disco :set 'instance instance
'service service
'attributes attribs
'endpoints endpoints))))
(defun stop-advertise (instance service)
(postmodern:query (:delete-from 'disco
:where (:and
(:= 'instance instance)
(:= 'service service)))))
(defun advertised (instance service endpoints handler &optional attributes)
(if (advertise instance service endpoints attributes)
(progn
(funcall handler)
(stop-advertise instance service)
T)
nil))对于搜索部分,我想要一个类似如下的函数:
(defun with-services (filters continuation)
; ...
)其中filters允许查找例如服务名称和属性(真的与问题无关),一旦所有需要的服务都可用,将使用相关服务的行列表调用continuation函数。
with-services函数可以阻塞,直到需要的每个服务都存在,也可以异步执行。
想法,如果不扩展后现代套餐,这是可能的吗?它会是什么样子呢?
更新
因为我找不到文档,所以我深入到了源代码级别,并在文件protocol.lisp中找到了通知是如何处理的:
(defun get-notification (socket)
"Read an asynchronous notification message from the socket and
signal a condition for it."
(let ((pid (read-int4 socket))
(channel (read-str socket))
(payload (read-str socket)))
(warn 'postgresql-notification
:pid pid
:channel channel
:payload payload
:format-control "Asynchronous notification ~S~@[ (payload: ~S)~]
received from ~ server process with PID ~D."
:format-arguments (list channel payload pid))))因此,在我还没有受过良好训练的LISP眼睛看来,通知似乎以某种方式映射到了某种异常。在其他语言中,这通常是一种代码气味。这是不是意味着,通知处理是隐藏的非功能,或者这是在Common Lisp中惯用的方式?
发布于 2020-07-29 16:25:52
常见的Lisp条件不是例外,因为发出信号并不会展开堆栈。这是Common Lisp条件系统的自然用法,C++/Java/Python等流行的纯抛出异常系统无法实现这一点。
函数WARN发出条件信号,允许匹配条件类型POSTGRESQL-NOTIFICATION的条件处理程序触发,然后调用MUFFLE-WARNING重新启动以继续执行。这里使用WARN在MUFFLE-WARNING未处理警告的情况下将警告消息打印到标准输出。
让我们考虑以下代码:
(labels ((handle-notification-p (condition)
(let ((pid (postgresql-notification-pid condition))
(channel (postgresql-notification-channel condition))
(payload (postgresql-notification-payload condition)))
...)) ;; return true if we want to handle this notification
(handle-notification (condition)
(when (handle-notification-p condition)
... ;; perform actions to handle the notifications
(invoke-restart 'muffle-warning condition))))
(handler-bind ((postgresql-notification #'handle-notification))
...)) ;; application logic goes here这段代码建立一个处理通知条件的处理程序。它有三个地方需要填写。
当条件应该由这个处理程序处理时,第一个函数应该返回true。第二个问题-在将控制返回到警告站点之前,应该执行哪些步骤来处理这种情况。第三个应该包含应用程序逻辑,其中包含一些处理与Postgres通信的服务器代码。
所有这些都是在不展开堆栈的情况下发生的(除非其他条件处理程序显式地在其他地方执行非本地退出)。如果处理了通知,将从WARN站点继续执行,如果未处理,将继续将警告打印到标准输出。
https://stackoverflow.com/questions/63108594
复制相似问题