Replace some buffers with pipes (should reduce memory usage)

This commit is contained in:
Jan-Lukas Else 2023-01-24 09:40:54 +01:00
parent c3611a32d6
commit ffe7065621
6 changed files with 58 additions and 78 deletions

View File

@ -3,6 +3,7 @@ package main
import ( import (
"bytes" "bytes"
"context" "context"
"io"
"log" "log"
"net/http" "net/http"
"sort" "sort"
@ -51,19 +52,16 @@ func (a *goBlog) serveBlogrollExport(w http.ResponseWriter, r *http.Request) {
a.serveError(w, r, "", http.StatusInternalServerError) a.serveError(w, r, "", http.StatusInternalServerError)
return return
} }
opmlBuf := bufferpool.Get() pr, pw := io.Pipe()
defer bufferpool.Put(opmlBuf) go func() {
if err = opml.Render(opmlBuf, &opml.OPML{ _ = pw.CloseWithError(opml.Render(pw, &opml.OPML{
Version: "2.0", Version: "2.0",
DateCreated: time.Now().UTC(), DateCreated: time.Now().UTC(),
Outlines: outlines.([]*opml.Outline), Outlines: outlines.([]*opml.Outline),
}); err != nil { }))
log.Printf("Failed to render OPML: %v", err) }()
a.serveError(w, r, "", http.StatusInternalServerError)
return
}
w.Header().Set(contentType, contenttype.XMLUTF8) w.Header().Set(contentType, contenttype.XMLUTF8)
_ = a.min.Get().Minify(contenttype.XML, w, opmlBuf) _ = pr.CloseWithError(a.min.Get().Minify(contenttype.XML, w, pr))
} }
func (a *goBlog) getBlogrollOutlines(blog string) ([]*opml.Outline, error) { func (a *goBlog) getBlogrollOutlines(blog string) ([]*opml.Outline, error) {

View File

@ -78,10 +78,8 @@ func (a *goBlog) generateFeed(blog string, f feedType, w http.ResponseWriter, r
} }
pipeReader, pipeWriter := io.Pipe() pipeReader, pipeWriter := io.Pipe()
go func() { go func() {
writeErr := feedWriteFunc(pipeWriter) _ = pipeWriter.CloseWithError(feedWriteFunc(pipeWriter))
_ = pipeWriter.CloseWithError(writeErr)
}() }()
w.Header().Set(contentType, feedMediaType+contenttype.CharsetUtf8Suffix) w.Header().Set(contentType, feedMediaType+contenttype.CharsetUtf8Suffix)
minifyErr := a.min.Get().Minify(feedMediaType, w, pipeReader) _ = pipeReader.CloseWithError(a.min.Get().Minify(feedMediaType, w, pipeReader))
_ = pipeReader.CloseWithError(minifyErr)
} }

View File

@ -5,7 +5,6 @@ import (
"io" "io"
"net/http" "net/http"
"go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/contenttype" "go.goblog.app/app/pkgs/contenttype"
) )
@ -89,15 +88,12 @@ func (a *goBlog) serveGeoMapTracks(w http.ResponseWriter, r *http.Request) {
} }
} }
buf := bufferpool.Get() pr, pw := io.Pipe()
defer bufferpool.Put(buf) go func() {
err = json.NewEncoder(buf).Encode(tracks) _ = pw.CloseWithError(json.NewEncoder(pw).Encode(tracks))
if err != nil { }()
a.serveError(w, r, "", http.StatusInternalServerError)
return
}
w.Header().Set(contentType, contenttype.JSONUTF8) w.Header().Set(contentType, contenttype.JSONUTF8)
_, _ = io.Copy(w, buf) _ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
} }
const geoMapLocationsSubpath = "/locations.json" const geoMapLocationsSubpath = "/locations.json"
@ -135,13 +131,10 @@ func (a *goBlog) serveGeoMapLocations(w http.ResponseWriter, r *http.Request) {
} }
} }
buf := bufferpool.Get() pr, pw := io.Pipe()
defer bufferpool.Put(buf) go func() {
err = json.NewEncoder(buf).Encode(locations) _ = pw.CloseWithError(json.NewEncoder(pw).Encode(locations))
if err != nil { }()
a.serveError(w, r, "", http.StatusInternalServerError)
return
}
w.Header().Set(contentType, contenttype.JSONUTF8) w.Header().Set(contentType, contenttype.JSONUTF8)
_, _ = io.Copy(w, buf) _ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
} }

View File

@ -4,6 +4,7 @@ import (
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"errors" "errors"
"io"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@ -11,7 +12,6 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hacdias/indieauth/v2" "github.com/hacdias/indieauth/v2"
"go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/contenttype" "go.goblog.app/app/pkgs/contenttype"
) )
@ -44,14 +44,12 @@ func (a *goBlog) indieAuthMetadata(w http.ResponseWriter, r *http.Request) {
"scopes_supported": []string{"create", "update", "delete", "undelete", "media"}, "scopes_supported": []string{"create", "update", "delete", "undelete", "media"},
"code_challenge_methods_supported": indieauth.CodeChallengeMethods, "code_challenge_methods_supported": indieauth.CodeChallengeMethods,
} }
buf := bufferpool.Get() pr, pw := io.Pipe()
defer bufferpool.Put(buf) go func() {
if err := json.NewEncoder(buf).Encode(resp); err != nil { _ = pw.CloseWithError(json.NewEncoder(pw).Encode(resp))
a.serveError(w, r, "Encoding failed", http.StatusInternalServerError) }()
return
}
w.Header().Set(contentType, contenttype.JSONUTF8) w.Header().Set(contentType, contenttype.JSONUTF8)
_ = a.min.Get().Minify(contenttype.JSON, w, buf) _ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
} }
// Parse Authorization Request // Parse Authorization Request
@ -168,14 +166,12 @@ func (a *goBlog) indieAuthVerification(w http.ResponseWriter, r *http.Request, w
resp["access_token"] = token resp["access_token"] = token
resp["scope"] = strings.Join(data.Scopes, " ") resp["scope"] = strings.Join(data.Scopes, " ")
} }
buf := bufferpool.Get() pr, pw := io.Pipe()
defer bufferpool.Put(buf) go func() {
if err = json.NewEncoder(buf).Encode(resp); err != nil { _ = pw.CloseWithError(json.NewEncoder(pw).Encode(resp))
a.serveError(w, r, "Encoding failed", http.StatusInternalServerError) }()
return
}
w.Header().Set(contentType, contenttype.JSONUTF8) w.Header().Set(contentType, contenttype.JSONUTF8)
_ = a.min.Get().Minify(contenttype.JSON, w, buf) _ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
} }
// Save the authorization request and return the code // Save the authorization request and return the code
@ -236,14 +232,12 @@ func (a *goBlog) indieAuthTokenVerification(w http.ResponseWriter, r *http.Reque
"scope": strings.Join(data.Scopes, " "), "scope": strings.Join(data.Scopes, " "),
} }
} }
buf := bufferpool.Get() pr, pw := io.Pipe()
defer bufferpool.Put(buf) go func() {
if err = json.NewEncoder(buf).Encode(res); err != nil { _ = pw.CloseWithError(json.NewEncoder(pw).Encode(res))
a.serveError(w, r, "Encoding failed", http.StatusInternalServerError) }()
return
}
w.Header().Set(contentType, contenttype.JSONUTF8) w.Header().Set(contentType, contenttype.JSONUTF8)
_ = a.min.Get().Minify(contenttype.JSON, w, buf) _ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
} }
// Checks the database for the token and returns the indieAuthData with client and scope. // Checks the database for the token and returns the indieAuthData with client and scope.

View File

@ -13,7 +13,6 @@ import (
"github.com/yuin/goldmark/renderer" "github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html" "github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/util" "github.com/yuin/goldmark/util"
"go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/highlighting" "go.goblog.app/app/pkgs/highlighting"
"go.goblog.app/app/pkgs/htmlbuilder" "go.goblog.app/app/pkgs/htmlbuilder"
) )
@ -83,13 +82,12 @@ func (a *goBlog) renderText(s string) string {
if s == "" { if s == "" {
return "" return ""
} }
buf := bufferpool.Get() pr, pw := io.Pipe()
defer bufferpool.Put(buf) go func() {
err := a.renderMarkdownToWriter(buf, s, false) _ = pw.CloseWithError(a.renderMarkdownToWriter(pw, s, false))
if err != nil { }()
return "" text, err := htmlTextFromReader(pr)
} _ = pr.CloseWithError(err)
text, err := htmlTextFromReader(buf)
if err != nil { if err != nil {
return "" return ""
} }
@ -100,13 +98,12 @@ func (a *goBlog) renderMdTitle(s string) string {
if s == "" { if s == "" {
return "" return ""
} }
buf := bufferpool.Get() pr, pw := io.Pipe()
defer bufferpool.Put(buf) go func() {
err := a.titleMd.Convert([]byte(s), buf) _ = pw.CloseWithError(a.titleMd.Convert([]byte(s), pw))
if err != nil { }()
return "" text, err := htmlTextFromReader(pr)
} _ = pr.CloseWithError(err)
text, err := htmlTextFromReader(buf)
if err != nil { if err != nil {
return "" return ""
} }

View File

@ -35,22 +35,22 @@ type microformatsResult struct {
} }
func (a *goBlog) parseMicroformats(u string, cache bool) (*microformatsResult, error) { func (a *goBlog) parseMicroformats(u string, cache bool) (*microformatsResult, error) {
buf := bufferpool.Get() pr, pw := io.Pipe()
defer bufferpool.Put(buf)
rb := requests.URL(u). rb := requests.URL(u).
Method(http.MethodGet). Method(http.MethodGet).
Accept(contenttype.HTMLUTF8). Accept(contenttype.HTMLUTF8).
Client(a.httpClient). Client(a.httpClient).
ToBytesBuffer(buf) ToWriter(pw)
if cache { if cache {
a.initMicroformatsCache() a.initMicroformatsCache()
rb.Transport(httpcachetransport.NewHttpCacheTransport(a.httpClient.Transport, a.mfCache, 10*time.Minute)) rb.Transport(httpcachetransport.NewHttpCacheTransport(a.httpClient.Transport, a.mfCache, 10*time.Minute))
} }
err := rb.Fetch(context.Background()) go func() {
if err != nil { _ = pw.CloseWithError(rb.Fetch(context.Background()))
return nil, err }()
} result, err := parseMicroformatsFromReader(u, pr)
return parseMicroformatsFromReader(u, buf) _ = pr.CloseWithError(err)
return result, err
} }
func parseMicroformatsFromReader(u string, r io.Reader) (*microformatsResult, error) { func parseMicroformatsFromReader(u string, r io.Reader) (*microformatsResult, error) {