Implement comprehensive WebSocket subscription stability fixes
- 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:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user