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 (
"bytes"
"context"
"io"
"log"
"net/http"
"sort"
@ -51,19 +52,16 @@ func (a *goBlog) serveBlogrollExport(w http.ResponseWriter, r *http.Request) {
a.serveError(w, r, "", http.StatusInternalServerError)
return
}
opmlBuf := bufferpool.Get()
defer bufferpool.Put(opmlBuf)
if err = opml.Render(opmlBuf, &opml.OPML{
pr, pw := io.Pipe()
go func() {
_ = pw.CloseWithError(opml.Render(pw, &opml.OPML{
Version: "2.0",
DateCreated: time.Now().UTC(),
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)
_ = 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) {

View File

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

View File

@ -5,7 +5,6 @@ import (
"io"
"net/http"
"go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/contenttype"
)
@ -89,15 +88,12 @@ func (a *goBlog) serveGeoMapTracks(w http.ResponseWriter, r *http.Request) {
}
}
buf := bufferpool.Get()
defer bufferpool.Put(buf)
err = json.NewEncoder(buf).Encode(tracks)
if err != nil {
a.serveError(w, r, "", http.StatusInternalServerError)
return
}
pr, pw := io.Pipe()
go func() {
_ = pw.CloseWithError(json.NewEncoder(pw).Encode(tracks))
}()
w.Header().Set(contentType, contenttype.JSONUTF8)
_, _ = io.Copy(w, buf)
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
}
const geoMapLocationsSubpath = "/locations.json"
@ -135,13 +131,10 @@ func (a *goBlog) serveGeoMapLocations(w http.ResponseWriter, r *http.Request) {
}
}
buf := bufferpool.Get()
defer bufferpool.Put(buf)
err = json.NewEncoder(buf).Encode(locations)
if err != nil {
a.serveError(w, r, "", http.StatusInternalServerError)
return
}
pr, pw := io.Pipe()
go func() {
_ = pw.CloseWithError(json.NewEncoder(pw).Encode(locations))
}()
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"
"encoding/json"
"errors"
"io"
"net/http"
"net/url"
"strings"
@ -11,7 +12,6 @@ import (
"github.com/google/uuid"
"github.com/hacdias/indieauth/v2"
"go.goblog.app/app/pkgs/bufferpool"
"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"},
"code_challenge_methods_supported": indieauth.CodeChallengeMethods,
}
buf := bufferpool.Get()
defer bufferpool.Put(buf)
if err := json.NewEncoder(buf).Encode(resp); err != nil {
a.serveError(w, r, "Encoding failed", http.StatusInternalServerError)
return
}
pr, pw := io.Pipe()
go func() {
_ = pw.CloseWithError(json.NewEncoder(pw).Encode(resp))
}()
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
@ -168,14 +166,12 @@ func (a *goBlog) indieAuthVerification(w http.ResponseWriter, r *http.Request, w
resp["access_token"] = token
resp["scope"] = strings.Join(data.Scopes, " ")
}
buf := bufferpool.Get()
defer bufferpool.Put(buf)
if err = json.NewEncoder(buf).Encode(resp); err != nil {
a.serveError(w, r, "Encoding failed", http.StatusInternalServerError)
return
}
pr, pw := io.Pipe()
go func() {
_ = pw.CloseWithError(json.NewEncoder(pw).Encode(resp))
}()
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
@ -236,14 +232,12 @@ func (a *goBlog) indieAuthTokenVerification(w http.ResponseWriter, r *http.Reque
"scope": strings.Join(data.Scopes, " "),
}
}
buf := bufferpool.Get()
defer bufferpool.Put(buf)
if err = json.NewEncoder(buf).Encode(res); err != nil {
a.serveError(w, r, "Encoding failed", http.StatusInternalServerError)
return
}
pr, pw := io.Pipe()
go func() {
_ = pw.CloseWithError(json.NewEncoder(pw).Encode(res))
}()
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.

View File

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

View File

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