Implement comprehensive WebSocket subscription stability fixes
Some checks failed
Go / build (push) Has been cancelled
Go / release (push) Has been cancelled

- Resolved critical issues causing subscriptions to drop after 30-60 seconds due to unconsumed receiver channels.
- Introduced per-subscription consumer goroutines to ensure continuous event delivery and prevent channel overflow.
- Enhanced REQ parsing to handle both wrapped and unwrapped filter arrays, eliminating EOF errors.
- Updated publisher logic to correctly send events to receiver channels, ensuring proper event delivery to subscribers.
- Added extensive documentation and testing tools to verify subscription stability and performance.
- Bumped version to v0.26.2 to reflect these significant improvements.
This commit is contained in:
2025-11-06 18:21:00 +00:00
parent d604341a27
commit 581e0ec588
23 changed files with 3054 additions and 81 deletions

View File

@@ -6,6 +6,7 @@ import (
"io"
"lol.mleku.dev/chk"
"lol.mleku.dev/log"
"next.orly.dev/pkg/encoders/envelopes"
"next.orly.dev/pkg/encoders/filter"
"next.orly.dev/pkg/encoders/text"
@@ -85,19 +86,24 @@ func (en *T) Marshal(dst []byte) (b []byte) {
// string is correctly unescaped by NIP-01 escaping rules.
func (en *T) Unmarshal(b []byte) (r []byte, err error) {
r = b
log.I.F("%s", r)
if en.Subscription, r, err = text.UnmarshalQuoted(r); chk.E(err) {
return
}
log.I.F("%s", r)
if r, err = text.Comma(r); chk.E(err) {
return
}
log.I.F("%s", r)
en.Filters = new(filter.S)
if r, err = en.Filters.Unmarshal(r); chk.E(err) {
return
}
log.I.F("%s", r)
if r, err = envelopes.SkipToTheEnd(r); chk.E(err) {
return
}
log.I.F("%s", r)
return
}

View File

@@ -47,17 +47,24 @@ func (s *S) Marshal(dst []byte) (b []byte) {
}
// Unmarshal decodes one or more filters from JSON.
// This handles both array-wrapped filters [{},...] and unwrapped filters {},...
func (s *S) Unmarshal(b []byte) (r []byte, err error) {
r = b
if len(r) == 0 {
return
}
r = r[1:]
// Handle empty array "[]"
if len(r) > 0 && r[0] == ']' {
// Check if filters are wrapped in an array
isArrayWrapped := r[0] == '['
if isArrayWrapped {
r = r[1:]
return
// Handle empty array "[]"
if len(r) > 0 && r[0] == ']' {
r = r[1:]
return
}
}
for {
if len(r) == 0 {
return
@@ -73,13 +80,17 @@ func (s *S) Unmarshal(b []byte) (r []byte, err error) {
return
}
if r[0] == ',' {
// Next element in the array
// Next element
r = r[1:]
continue
}
if r[0] == ']' {
// End of the enclosed array; consume and return
r = r[1:]
// End of array or envelope
if isArrayWrapped {
// Consume the closing bracket of the filter array
r = r[1:]
}
// Otherwise leave it for the envelope parser
return
}
// Unexpected token

View File

@@ -1 +1 @@
v0.26.0
v0.26.2