关于 table Common Lisp 与后现代包的更改的客户端通知
client side notification on table change in Common Lisp with Postmodern package
虽然在谷歌上搜索并寻找了很长一段时间的示例,但我找不到任何关于当数据库 table 更改时是否可以在客户端应用程序中获得通知“回调”的指示.
该功能在 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));
基于上面给出的 table,我创建了一个小的 Lisp 包,它允许发布一些服务(这是一个玩具服务发现,由数据库支持),使用 postmodern
postgresql包。
当然,除了广告,应用也会想要等待/搜索主动服务。由于轮询显然不是可行的方法,因此需要某种形式的通知。在 C API 级别,应用程序可以使用 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 中做事的惯用方式?
Common 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
此代码建立一个处理通知条件的处理程序。它有三个地方需要填写。
第一个应该 return true 当且仅当条件应该由这个处理程序处理。第二个 - 在 return 将控制权控制到警告站点之前,应该执行哪些步骤来处理这种情况。第三个应该包含应用程序逻辑,其中包含一些处理与 Postgres 通信的服务器代码。
所有这一切都是在不展开堆栈的情况下发生的(除非某些其他条件处理程序在其他地方明确执行非本地退出)。如果通知已处理,将从 WARN
站点继续执行,如果未处理,它将继续向标准输出输出警告。
虽然在谷歌上搜索并寻找了很长一段时间的示例,但我找不到任何关于当数据库 table 更改时是否可以在客户端应用程序中获得通知“回调”的指示.
该功能在 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));
基于上面给出的 table,我创建了一个小的 Lisp 包,它允许发布一些服务(这是一个玩具服务发现,由数据库支持),使用 postmodern
postgresql包。
当然,除了广告,应用也会想要等待/搜索主动服务。由于轮询显然不是可行的方法,因此需要某种形式的通知。在 C API 级别,应用程序可以使用 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 中做事的惯用方式?
Common 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
此代码建立一个处理通知条件的处理程序。它有三个地方需要填写。
第一个应该 return true 当且仅当条件应该由这个处理程序处理。第二个 - 在 return 将控制权控制到警告站点之前,应该执行哪些步骤来处理这种情况。第三个应该包含应用程序逻辑,其中包含一些处理与 Postgres 通信的服务器代码。
所有这一切都是在不展开堆栈的情况下发生的(除非某些其他条件处理程序在其他地方明确执行非本地退出)。如果通知已处理,将从 WARN
站点继续执行,如果未处理,它将继续向标准输出输出警告。