WithCheckDuplicateReplaceable() and helper functions for efficient replaceable event matching.

This commit is contained in:
fiatjaf 2025-03-12 00:19:41 -03:00
parent d2ceac48f6
commit 441f94563f
4 changed files with 88 additions and 4 deletions

View File

@ -154,3 +154,49 @@ func extractEventID(jsonStr string) string {
// get 64 characters of the id
return jsonStr[start : start+64]
}
func extractDTag(jsonStr string) string {
// look for ["d", pattern
start := strings.Index(jsonStr, `["d"`)
if start == -1 {
return ""
}
// move to the next quote
offset := strings.IndexRune(jsonStr[start+4:], '"')
start += 4 + offset + 1
// find the ending quote
end := strings.IndexRune(jsonStr[start:], '"')
if end == -1 {
return ""
}
// get the contents
return jsonStr[start : start+end]
}
func extractTimestamp(jsonStr string) Timestamp {
// look for "created_at": pattern
start := strings.Index(jsonStr, `"created_at"`)
if start == -1 {
return 0
}
// move to the next number
offset := strings.IndexAny(jsonStr[start+12:], "9876543210")
if offset == -1 {
return 0
}
start += 12 + offset
// find the end
end := strings.IndexAny(jsonStr[start:], ",} ")
if end == -1 {
return 0
}
// get the contents
ts, _ := strconv.ParseInt(jsonStr[start:start+end], 10, 64)
return Timestamp(ts)
}

View File

@ -43,4 +43,26 @@ func TestSubIdExtract(t *testing.T) {
}
}
func TestDTagExtract(t *testing.T) {
{
data := `["EVENT", "xxz" ,{"kind":30023,"id":"6b5988e9471fa340880a40df815befc69c901420facfb670acd8308012088f16","pubkey":"67ada8e344532cbf82f0e702472e24c7896e0e1c96235eacbaaa4b8616052171","created_at":1736909072,"tags":[["d", "balalaika"]["e","cfdf18b78527455097515545be4ccbe17e9b88f64539a566c632e405e2c0d08a","","root"],["e","f1ec9c301383be082f1860f7e24e49164d855bfab67f8e5c3ed17f6f3f867cca","","reply"],["p","1afe0c74e3d7784eba93a5e3fa554a6eeb01928d12739ae8ba4832786808e36d"],["p","8aa642e26e65072139e10db59646a89aa7538a59965aab3ed89191d71967d6c3"],["p","f4d89779148ccd245c8d50914a284fd62d97cb0fb68b797a70f24a172b522db9"],["p","18905d0a5d623ab81a98ba98c582bd5f57f2506c6b808905fc599d5a0b229b08"],["p","9a0e2043afaa056a12b8bbe77ac4c3185c0e2bc46b12aac158689144323c0e3c"],["p","45f195cffcb8c9724efc248f0507a2fb65b579dfabe7cd35398598163cab7627"]],"content":"🫡","sig":"d21aaf43963b07a3cb5f85ac8809c2b2e4dd3269195f4d810e1b7650895178fe01cf685ab3ee93f193cdde1f8d17419ff05332c6e3fc7429bbbe3d70016b8638"}]`
require.Equal(t, "balalaika", extractDTag(data))
}
{
data := `{"kind":1,"pubkey":"67ada8e344532cbf82f0e702472e24c7896e0e1c96235eacbaaa4b8616052171","created_at":1736909072,"tags":[["e","cfdf18b78527455097515545be4ccbe17e9b88f64539a566c632e405e2c0d08a","","root"],["e","f1ec9c301383be082f1860f7e24e49164d855bfab67f8e5c3ed17f6f3f867cca","","reply"],["p","1afe0c74e3d7784eba93a5e3fa554a6eeb01928d12739ae8ba4832786808e36d"],["p","8aa642e26e65072139e10db59646a89aa7538a59965aab3ed89191d71967d6c3"],["p","f4d89779148ccd245c8d50914a284fd62d97cb0fb68b797a70f24a172b522db9"],["p","18905d0a5d623ab81a98ba98c582bd5f57f2506c6b808905fc599d5a0b229b08"],["p","9a0e2043afaa056a12b8bbe77ac4c3185c0e2bc46b12aac158689144323c0e3c"],["p","45f195cffcb8c9724efc248f0507a2fb65b579dfabe7cd35398598163cab7627"]],"content":"🫡","sig":"d21aaf43963b07a3cb5f85ac8809c2b2e4dd3269195f4d810e1b7650895178fe01cf685ab3ee93f193cdde1f8d17419ff05332c6e3fc7429bbbe3d70016b8638","id": "6b5988e9471fa340880a40df815befc69c901420facfb670acd8308012088f16" }`
require.Equal(t, "", extractDTag(data))
}
}
func TestTimestampExtract(t *testing.T) {
{
data := `["EVENT", "xxz" ,{"kind":30023,"id":"6b5988e9471fa340880a40df815befc69c901420facfb670acd8308012088f16","pubkey":"67ada8e344532cbf82f0e702472e24c7896e0e1c96235eacbaaa4b8616052171","created_at": 1736909072 ,"tags":[["d", "balalaika"]["e","cfdf18b78527455097515545be4ccbe17e9b88f64539a566c632e405e2c0d08a","","root"],["e","f1ec9c301383be082f1860f7e24e49164d855bfab67f8e5c3ed17f6f3f867cca","","reply"],["p","1afe0c74e3d7784eba93a5e3fa554a6eeb01928d12739ae8ba4832786808e36d"],["p","8aa642e26e65072139e10db59646a89aa7538a59965aab3ed89191d71967d6c3"],["p","f4d89779148ccd245c8d50914a284fd62d97cb0fb68b797a70f24a172b522db9"],["p","18905d0a5d623ab81a98ba98c582bd5f57f2506c6b808905fc599d5a0b229b08"],["p","9a0e2043afaa056a12b8bbe77ac4c3185c0e2bc46b12aac158689144323c0e3c"],["p","45f195cffcb8c9724efc248f0507a2fb65b579dfabe7cd35398598163cab7627"]],"content":"🫡","sig":"d21aaf43963b07a3cb5f85ac8809c2b2e4dd3269195f4d810e1b7650895178fe01cf685ab3ee93f193cdde1f8d17419ff05332c6e3fc7429bbbe3d70016b8638"}]`
require.Equal(t, Timestamp(1736909072), extractTimestamp(data))
}
{
data := `{"kind":1,"pubkey":"67ada8e344532cbf82f0e702472e24c7896e0e1c96235eacbaaa4b8616052171","tags":[["e","cfdf18b78527455097515545be4ccbe17e9b88f64539a566c632e405e2c0d08a","","root"],["e","f1ec9c301383be082f1860f7e24e49164d855bfab67f8e5c3ed17f6f3f867cca","","reply"],["p","1afe0c74e3d7784eba93a5e3fa554a6eeb01928d12739ae8ba4832786808e36d"],["p","8aa642e26e65072139e10db59646a89aa7538a59965aab3ed89191d71967d6c3"],["p","f4d89779148ccd245c8d50914a284fd62d97cb0fb68b797a70f24a172b522db9"],["p","18905d0a5d623ab81a98ba98c582bd5f57f2506c6b808905fc599d5a0b229b08"],["p","9a0e2043afaa056a12b8bbe77ac4c3185c0e2bc46b12aac158689144323c0e3c"],["p","45f195cffcb8c9724efc248f0507a2fb65b579dfabe7cd35398598163cab7627"]],"content":"🫡","sig":"d21aaf43963b07a3cb5f85ac8809c2b2e4dd3269195f4d810e1b7650895178fe01cf685ab3ee93f193cdde1f8d17419ff05332c6e3fc7429bbbe3d70016b8638","id": "6b5988e9471fa340880a40df815befc69c901420facfb670acd8308012088f16" ,"created_at":01736909054}`
require.Equal(t, Timestamp(1736909054), extractTimestamp(data))
}
}
func ptr[S any](s S) *S { return &s }

View File

@ -231,9 +231,13 @@ func (r *Relay) ConnectWithTLS(ctx context.Context, tlsConfig *tls.Config) error
// if this is an "EVENT" we will have this preparser logic that should speed things up a little
// as we skip handling duplicate events
subid := extractSubID(message)
subscription, ok := r.Subscriptions.Load(subIdToSerial(subid))
if ok && subscription.checkDuplicate != nil {
if subscription.checkDuplicate(extractEventID(message[10+len(subid):]), r.URL) {
sub, ok := r.Subscriptions.Load(subIdToSerial(subid))
if ok && sub.checkDuplicate != nil {
if sub.checkDuplicate(extractEventID(message[10+len(subid):]), r.URL) {
continue
}
} else if sub.checkDuplicateReplaceable != nil {
if sub.checkDuplicateReplaceable(extractDTag(message), extractTimestamp(message)) {
continue
}
}
@ -437,6 +441,8 @@ func (r *Relay) PrepareSubscription(ctx context.Context, filters Filters, opts .
label = string(o)
case WithCheckDuplicate:
sub.checkDuplicate = o
case WithCheckDuplicateReplaceable:
sub.checkDuplicateReplaceable = o
}
}

View File

@ -33,10 +33,14 @@ type Subscription struct {
// Context will be .Done() when the subscription ends
Context context.Context
// if it is not nil, CheckDuplicate will be called for every event received
// if it is not nil, checkDuplicate will be called for every event received
// if it returns true that event will not be processed further.
checkDuplicate func(id string, relay string) bool
// if it is not nil, checkDuplicateReplaceable will be called for every event received
// if it returns true that event will not be processed further.
checkDuplicateReplaceable func(d string, ts Timestamp) bool
match func(*Event) bool // this will be either Filters.Match or Filters.MatchIgnoringTimestampConstraints
live atomic.Bool
eosed atomic.Bool
@ -63,9 +67,15 @@ type WithCheckDuplicate func(id, relay string) bool
func (_ WithCheckDuplicate) IsSubscriptionOption() {}
// WithCheckDuplicateReplaceable sets checkDuplicateReplaceable on the subscription
type WithCheckDuplicateReplaceable func(d string, ts Timestamp) bool
func (_ WithCheckDuplicateReplaceable) IsSubscriptionOption() {}
var (
_ SubscriptionOption = (WithLabel)("")
_ SubscriptionOption = (WithCheckDuplicate)(nil)
_ SubscriptionOption = (WithCheckDuplicateReplaceable)(nil)
)
func (sub *Subscription) start() {