perf(relay): cache parsed metadata and pre-load HTML template
- Add ParsedMetadata struct to LeaseEntry for O(1) access - Parse metadata once in updateLeaseInternal instead of per-request - Pre-load portal.html template with sync.Once for efficient SSR - Remove sdk import from view.go (no longer needed) Eliminates O(N) JSON parsing and file I/O per HTTP request.
cognitive-glitch committed
Dec 9, 2025 at 13:11 UTC
dfa34b7dc95d0a2d29ba33001a819125ab26e2f0
3 files changed
+69
-26
cmd/relay-server/serve.go
+22
-7
@@ -13,6 +13,18 @@ import (
13
"gosuda.org/portal/utils"
14
)
15
16
+// Cached portal.html template for efficient SSR
17
+var (
18
+ cachedPortalHTML []byte
19
+ cachedPortalHTMLOnce sync.Once
20
+)
21
+
22
+func initPortalHTMLCache() error {
23
+ var err error
24
+ cachedPortalHTML, err = distFS.ReadFile("dist/app/portal.html")
25
+ return err
26
+}
27
+
28
func serveAsset(mux *http.ServeMux, route, assetPath, contentType string) {
29
mux.HandleFunc(route, func(w http.ResponseWriter, r *http.Request) {
30
// Read from dist/app subdirectory of the embedded FS
@@ -34,17 +46,20 @@ func serveAsset(mux *http.ServeMux, route, assetPath, contentType string) {
46
func servePortalHTMLWithSSR(w http.ResponseWriter, r *http.Request, serv *portal.RelayServer) {
47
utils.SetCORSHeaders(w)
48
37
- // Read portal.html from embedded FS
38
- fullPath := path.Join("dist", "app", "portal.html")
39
- htmlContent, err := distFS.ReadFile(fullPath)
40
- if err != nil {
41
- log.Error().Err(err).Msg("Failed to read portal.html")
49
+ // Initialize cache on first use
50
+ cachedPortalHTMLOnce.Do(func() {
51
+ if err := initPortalHTMLCache(); err != nil {
52
+ log.Error().Err(err).Msg("Failed to cache portal.html")
53
+ }
54
+ })
55
+
56
+ if cachedPortalHTML == nil {
57
http.NotFound(w, r)
58
return
59
}
60
46
- // Inject SSR data
47
- injectedHTML := injectServerData(string(htmlContent), serv)
61
+ // Inject SSR data into cached template
62
+ injectedHTML := injectServerData(string(cachedPortalHTML), serv)
63
64
// Set headers
65
w.Header().Set("Content-Type", "text/html; charset=utf-8")
cmd/relay-server/view.go
+5
-11
@@ -17,7 +17,6 @@ import (
17
"github.com/rs/zerolog/log"
18
19
"gosuda.org/portal/portal"
20
- "gosuda.org/portal/sdk"
20
"gosuda.org/portal/utils"
21
)
22
@@ -344,10 +343,6 @@ func convertLeaseEntriesToAdminRows(serv *portal.RelayServer) []leaseRow {
343
lease := leaseEntry.Lease
344
identityID := string(lease.Identity.Id)
345
347
- // Metadata parsing
348
- var meta sdk.Metadata
349
- _ = json.Unmarshal([]byte(lease.Metadata), &meta)
350
-
346
// Calculate TTL
347
ttl := time.Until(leaseEntry.Expires)
348
ttlStr := ""
@@ -430,7 +425,7 @@ func convertLeaseEntriesToAdminRows(serv *portal.RelayServer) []leaseRow {
425
TTL: ttlStr,
426
Link: link,
427
StaleRed: !connected && since >= 15*time.Second,
433
- Hide: meta.Hide,
428
+ Hide: leaseEntry.ParsedMetadata != nil && leaseEntry.ParsedMetadata.Hide,
429
Metadata: lease.Metadata,
430
BPS: bps,
431
}
@@ -466,10 +461,8 @@ func convertLeaseEntriesToRows(serv *portal.RelayServer) []leaseRow {
461
continue
462
}
463
469
- // Metadata parsing
470
- var meta sdk.Metadata
471
- _ = json.Unmarshal([]byte(lease.Metadata), &meta)
472
- if meta.Hide {
464
+ // Use cached parsed metadata
465
+ if leaseEntry.ParsedMetadata != nil && leaseEntry.ParsedMetadata.Hide {
466
continue
467
}
468
@@ -557,10 +550,11 @@ func convertLeaseEntriesToRows(serv *portal.RelayServer) []leaseRow {
550
TTL: ttlStr,
551
Link: link,
552
StaleRed: !connected && since >= 15*time.Second,
560
- Hide: meta.Hide,
553
+ Hide: leaseEntry.ParsedMetadata != nil && leaseEntry.ParsedMetadata.Hide,
554
Metadata: lease.Metadata,
555
}
556
557
+ // Hidden entries are already filtered above, but keep check for safety
558
if !row.Hide {
559
rows = append(rows, row)
560
}
portal/lease.go
+42
-8
@@ -1,6 +1,7 @@
1
package portal
2
3
import (
4
+ "encoding/json"
5
"regexp"
6
"sync"
7
"time"
@@ -9,12 +10,23 @@ import (
10
"gosuda.org/portal/portal/core/proto/rdverb"
11
)
12
13
+// ParsedMetadata contains pre-parsed lease metadata fields.
14
+// Defined locally to avoid sdk dependency in core package.
15
+type ParsedMetadata struct {
16
+ Description string
17
+ Tags []string
18
+ Thumbnail string
19
+ Owner string
20
+ Hide bool
21
+}
22
+
23
// LeaseEntry represents a registered lease with expiration tracking.
24
type LeaseEntry struct {
14
- Lease *rdverb.Lease
15
- Expires time.Time
16
- LastSeen time.Time
17
- ConnectionID int64 // Store the connection ID
25
+ Lease *rdverb.Lease
26
+ Expires time.Time
27
+ LastSeen time.Time
28
+ ConnectionID int64 // Store the connection ID
29
+ ParsedMetadata *ParsedMetadata // Cached parsed metadata
30
}
31
32
// leaseCmd is the command interface for LeaseManager event loop.
@@ -320,11 +332,33 @@ func (lm *LeaseManager) updateLeaseInternal(lease *rdverb.Lease, connectionID in
332
}
333
}
334
335
+ // Parse metadata once for cached access
336
+ var parsedMeta *ParsedMetadata
337
+ if lease.Metadata != "" {
338
+ var meta struct {
339
+ Description string `json:"description"`
340
+ Tags []string `json:"tags"`
341
+ Thumbnail string `json:"thumbnail"`
342
+ Owner string `json:"owner"`
343
+ Hide bool `json:"hide"`
344
+ }
345
+ if err := json.Unmarshal([]byte(lease.Metadata), &meta); err == nil {
346
+ parsedMeta = &ParsedMetadata{
347
+ Description: meta.Description,
348
+ Tags: meta.Tags,
349
+ Thumbnail: meta.Thumbnail,
350
+ Owner: meta.Owner,
351
+ Hide: meta.Hide,
352
+ }
353
+ }
354
+ }
355
+
356
lm.leases[identityID] = &LeaseEntry{
324
- Lease: lease,
325
- Expires: expires,
326
- LastSeen: time.Now(),
327
- ConnectionID: connectionID,
357
+ Lease: lease,
358
+ Expires: expires,
359
+ LastSeen: time.Now(),
360
+ ConnectionID: connectionID,
361
+ ParsedMetadata: parsedMeta,
362
}
363
364
return true