move the memory intensive and sometimes fragile image code to a special backend process
Ted Unangst tedu@tedunangst.com
Sat, 09 Nov 2019 02:15:15 -0500
4 files changed,
106 insertions(+),
5 deletions(-)
M
activity.go
→
activity.go
@@ -34,7 +34,6 @@
"humungus.tedunangst.com/r/webs/cache" "humungus.tedunangst.com/r/webs/gate" "humungus.tedunangst.com/r/webs/httpsig" - "humungus.tedunangst.com/r/webs/image" "humungus.tedunangst.com/r/webs/junk" )@@ -175,8 +174,7 @@ if len(data) == 10*1024*1024 {
log.Printf("truncation likely") } if strings.HasPrefix(media, "image") { - img, err := image.Vacuum(&buf, - image.Params{LimitSize: 4200 * 4200, MaxWidth: 2048, MaxHeight: 2048}) + img, err := shrinkit(data) if err != nil { log.Printf("unable to decode image: %s", err) localize = false
A
backend.go
@@ -0,0 +1,101 @@
+// +// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com> +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package main + +import ( + "bytes" + "log" + "net" + "net/rpc" + "os" + "os/exec" + + "humungus.tedunangst.com/r/webs/image" +) + +type Shrinker struct { +} + +type ShrinkerArgs struct { + Buf []byte + Params image.Params +} + +type ShrinkerResult struct { + Image *image.Image +} + +func (s *Shrinker) Shrink(args *ShrinkerArgs, res *ShrinkerResult) error { + img, err := image.Vacuum(bytes.NewReader(args.Buf), args.Params) + if err != nil { + return err + } + res.Image = img + return nil +} + +func backendSockname() string { + return dataDir + "/backend.sock" +} + +func shrinkit(data []byte) (*image.Image, error) { + cl, err := rpc.Dial("unix", backendSockname()) + if err != nil { + return nil, err + } + defer cl.Close() + var res ShrinkerResult + err = cl.Call("Shrinker.Shrink", &ShrinkerArgs{ + Buf: data, + Params: image.Params{LimitSize: 4200 * 4200, MaxWidth: 2048, MaxHeight: 2048}, + }, &res) + if err != nil { + return nil, err + } + return res.Image, nil +} + +func backendServer() { + log.Printf("backend server running") + shrinker := new(Shrinker) + srv := rpc.NewServer() + err := srv.Register(shrinker) + if err != nil { + log.Panicf("unable to register shrinker: %s", err) + } + + sockname := backendSockname() + err = os.Remove(sockname) + if err != nil && !os.IsNotExist(err) { + log.Panicf("unable to unlink socket: %s", err) + } + + lis, err := net.Listen("unix", sockname) + if err != nil { + log.Panicf("unable to register shrinker: %s", err) + } + srv.Accept(lis) +} + +func startBackendServer() { + proc := exec.Command(os.Args[0], "-datadir", dataDir, "backend") + proc.Stdout = os.Stdout + proc.Stderr = os.Stderr + err := proc.Start() + if err != nil { + log.Panicf("can't exec backend: %s", err) + } +}
M
web.go
→
web.go
@@ -36,7 +36,6 @@
"github.com/gorilla/mux" "humungus.tedunangst.com/r/webs/cache" "humungus.tedunangst.com/r/webs/httpsig" - "humungus.tedunangst.com/r/webs/image" "humungus.tedunangst.com/r/webs/junk" "humungus.tedunangst.com/r/webs/login" "humungus.tedunangst.com/r/webs/rss"@@ -1399,7 +1398,7 @@ file.Close()
data := buf.Bytes() xid := xfiltrate() var media, name string - img, err := image.Vacuum(&buf, image.Params{MaxWidth: 2048, MaxHeight: 2048}) + img, err := shrinkit(data) if err == nil { data = img.Data format := img.Format@@ -2060,6 +2059,7 @@ listener, err := openListener()
if err != nil { log.Fatal(err) } + startBackendServer() go enditall() go redeliverator() go tracker()