mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 14:42:26 -05:00 
			
		
		
		
	[bugfix] Return useful err on server start failure (#3879)
		
	* [bugfix] Return useful err on `server start` failure * remove scheduler started func * remove tryUntil
This commit is contained in:
		
					parent
					
						
							
								829143d263
							
						
					
				
			
			
				commit
				
					
						69461c461b
					
				
			
		
					 9 changed files with 86 additions and 81 deletions
				
			
		|  | @ -38,7 +38,9 @@ import ( | |||
| func initState(ctx context.Context) (*state.State, error) { | ||||
| 	var state state.State | ||||
| 	state.Caches.Init() | ||||
| 	state.Caches.Start() | ||||
| 	if err := state.Caches.Start(); err != nil { | ||||
| 		return nil, fmt.Errorf("error starting caches: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Only set state DB connection. | ||||
| 	// Don't need Actions or Workers for this (yet). | ||||
|  |  | |||
|  | @ -125,7 +125,9 @@ func setupList(ctx context.Context) (*list, error) { | |||
| 	} | ||||
| 
 | ||||
| 	state.Caches.Init() | ||||
| 	state.Caches.Start() | ||||
| 	if err := state.Caches.Start(); err != nil { | ||||
| 		return nil, fmt.Errorf("error starting caches: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Only set state DB connection. | ||||
| 	// Don't need Actions or Workers for this. | ||||
|  |  | |||
|  | @ -42,7 +42,9 @@ func setupPrune(ctx context.Context) (*prune, error) { | |||
| 	var state state.State | ||||
| 
 | ||||
| 	state.Caches.Init() | ||||
| 	state.Caches.Start() | ||||
| 	if err := state.Caches.Start(); err != nil { | ||||
| 		return nil, fmt.Errorf("error starting caches: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Scheduler is required for the | ||||
| 	// cleaner, but no other workers | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ import ( | |||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"net/netip" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"runtime" | ||||
|  | @ -117,8 +118,9 @@ var Start action.GTSAction = func(ctx context.Context) error { | |||
| 	) | ||||
| 
 | ||||
| 	defer func() { | ||||
| 		// Stop caches with | ||||
| 		// background tasks. | ||||
| 		// Stop any started caches. | ||||
| 		// | ||||
| 		// Noop if never started. | ||||
| 		state.Caches.Stop() | ||||
| 
 | ||||
| 		if route != nil { | ||||
|  | @ -133,6 +135,8 @@ var Start action.GTSAction = func(ctx context.Context) error { | |||
| 		// Stop any currently running | ||||
| 		// worker processes / scheduled | ||||
| 		// tasks from being executed. | ||||
| 		// | ||||
| 		// Noop on unstarted workers. | ||||
| 		state.Workers.Stop() | ||||
| 
 | ||||
| 		if state.Timelines.Home != nil { | ||||
|  | @ -202,7 +206,9 @@ var Start action.GTSAction = func(ctx context.Context) error { | |||
| 
 | ||||
| 	// Initialize caches | ||||
| 	state.Caches.Init() | ||||
| 	state.Caches.Start() | ||||
| 	if err := state.Caches.Start(); err != nil { | ||||
| 		return fmt.Errorf("error starting caches: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Open connection to the database now caches started. | ||||
| 	dbService, err := bundb.NewBunDBService(ctx, state) | ||||
|  | @ -240,10 +246,17 @@ var Start action.GTSAction = func(ctx context.Context) error { | |||
| 		return fmt.Errorf("error opening storage backend: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Parse http client allow | ||||
| 	// and block range exceptions. | ||||
| 	ranges, err := parseClientRanges() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Prepare wrapped httpclient with config. | ||||
| 	client := httpclient.New(httpclient.Config{ | ||||
| 		AllowRanges:           config.MustParseIPPrefixes(config.GetHTTPClientAllowIPs()), | ||||
| 		BlockRanges:           config.MustParseIPPrefixes(config.GetHTTPClientBlockIPs()), | ||||
| 		AllowRanges:           ranges.allow, | ||||
| 		BlockRanges:           ranges.block, | ||||
| 		Timeout:               config.GetHTTPClientTimeout(), | ||||
| 		TLSInsecureSkipVerify: config.GetHTTPClientTLSInsecureSkipVerify(), | ||||
| 	}) | ||||
|  | @ -617,3 +630,44 @@ func compileWASM(ctx context.Context) error { | |||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func parseClientRanges() ( | ||||
| 	*struct { | ||||
| 		allow []netip.Prefix | ||||
| 		block []netip.Prefix | ||||
| 	}, | ||||
| 	error, | ||||
| ) { | ||||
| 	parseF := func(ips []string, ranges []netip.Prefix, flag string) error { | ||||
| 		for i, ip := range ips { | ||||
| 			p, err := netip.ParsePrefix(ip) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("error parsing %s value %s: %w", flag, ip, err) | ||||
| 			} | ||||
| 			ranges[i] = p | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	allowIPs := config.GetHTTPClientAllowIPs() | ||||
| 	allowRanges := make([]netip.Prefix, len(allowIPs)) | ||||
| 	allowFlag := config.HTTPClientAllowIPsFlag() | ||||
| 	if err := parseF(allowIPs, allowRanges, allowFlag); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	blockIPs := config.GetHTTPClientBlockIPs() | ||||
| 	blockRanges := make([]netip.Prefix, len(blockIPs)) | ||||
| 	blockFlag := config.HTTPClientBlockIPsFlag() | ||||
| 	if err := parseF(blockIPs, blockRanges, blockFlag); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &struct { | ||||
| 		allow []netip.Prefix | ||||
| 		block []netip.Prefix | ||||
| 	}{ | ||||
| 		allow: allowRanges, | ||||
| 		block: blockRanges, | ||||
| 	}, nil | ||||
| } | ||||
|  |  | |||
|  | @ -76,7 +76,9 @@ func (suite *ListsStandardTestSuite) SetupSuite() { | |||
| 
 | ||||
| func (suite *ListsStandardTestSuite) SetupTest() { | ||||
| 	suite.state.Caches.Init() | ||||
| 	suite.state.Caches.Start() | ||||
| 	if err := suite.state.Caches.Start(); err != nil { | ||||
| 		panic("error starting caches: " + err.Error()) | ||||
| 	} | ||||
| 	testrig.StartNoopWorkers(&suite.state) | ||||
| 
 | ||||
| 	testrig.InitTestConfig() | ||||
|  |  | |||
							
								
								
									
										21
									
								
								internal/cache/cache.go
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								internal/cache/cache.go
									
										
									
									
										vendored
									
									
								
							|  | @ -23,6 +23,7 @@ import ( | |||
| 	"codeberg.org/gruf/go-cache/v3/ttl" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/cache/headerfilter" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtserror" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/log" | ||||
| ) | ||||
| 
 | ||||
|  | @ -124,16 +125,18 @@ func (c *Caches) Init() { | |||
| 
 | ||||
| // Start will start any caches that require a background | ||||
| // routine, which usually means any kind of TTL caches. | ||||
| func (c *Caches) Start() { | ||||
| func (c *Caches) Start() error { | ||||
| 	log.Infof(nil, "start: %p", c) | ||||
| 
 | ||||
| 	tryUntil("starting webfinger cache", 5, func() bool { | ||||
| 		return c.Webfinger.Start(5 * time.Minute) | ||||
| 	}) | ||||
| 	if !c.Webfinger.Start(5 * time.Minute) { | ||||
| 		return gtserror.New("could not start webfinger cache") | ||||
| 	} | ||||
| 
 | ||||
| 	tryUntil("starting statusesFilterableFields cache", 5, func() bool { | ||||
| 		return c.StatusesFilterableFields.Start(5 * time.Minute) | ||||
| 	}) | ||||
| 	if !c.StatusesFilterableFields.Start(5 * time.Minute) { | ||||
| 		return gtserror.New("could not start statusesFilterableFields cache") | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Stop will stop any caches that require a background | ||||
|  | @ -141,8 +144,8 @@ func (c *Caches) Start() { | |||
| func (c *Caches) Stop() { | ||||
| 	log.Infof(nil, "stop: %p", c) | ||||
| 
 | ||||
| 	tryUntil("stopping webfinger cache", 5, c.Webfinger.Stop) | ||||
| 	tryUntil("stopping statusesFilterableFields cache", 5, c.StatusesFilterableFields.Stop) | ||||
| 	_ = c.Webfinger.Stop() | ||||
| 	_ = c.StatusesFilterableFields.Stop() | ||||
| } | ||||
| 
 | ||||
| // Sweep will sweep all the available caches to ensure none | ||||
|  |  | |||
							
								
								
									
										18
									
								
								internal/cache/util.go
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								internal/cache/util.go
									
										
									
									
										vendored
									
									
								
							|  | @ -19,11 +19,9 @@ package cache | |||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"time" | ||||
| 
 | ||||
| 	errorsv2 "codeberg.org/gruf/go-errors/v2" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/log" | ||||
| ) | ||||
| 
 | ||||
| // SentinelError is an error that can be returned and checked against to indicate a non-permanent | ||||
|  | @ -51,19 +49,3 @@ type nocopy struct{} | |||
| func (*nocopy) Lock() {} | ||||
| 
 | ||||
| func (*nocopy) Unlock() {} | ||||
| 
 | ||||
| // tryUntil will attempt to call 'do' for 'count' attempts, before panicking with 'msg'. | ||||
| func tryUntil(msg string, count int, do func() bool) { | ||||
| 	for i := 0; i < count; i++ { | ||||
| 		if do() { | ||||
| 			// success. | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		// Sleep for a little before retry (a bcakoff). | ||||
| 		time.Sleep(time.Millisecond * 1 << (i + 1)) | ||||
| 	} | ||||
| 
 | ||||
| 	// panic on total failure as this shouldn't happen. | ||||
| 	log.Panicf(nil, "failed %s after %d tries", msg, count) | ||||
| } | ||||
|  |  | |||
|  | @ -1,39 +0,0 @@ | |||
| // GoToSocial | ||||
| // Copyright (C) GoToSocial Authors admin@gotosocial.org | ||||
| // SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU Affero General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU Affero General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU Affero General Public License | ||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| package config | ||||
| 
 | ||||
| import ( | ||||
| 	"net/netip" | ||||
| 
 | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/log" | ||||
| ) | ||||
| 
 | ||||
| func MustParseIPPrefixes(in []string) []netip.Prefix { | ||||
| 	prefs := make([]netip.Prefix, 0, len(in)) | ||||
| 
 | ||||
| 	for _, i := range in { | ||||
| 		pref, err := netip.ParsePrefix(i) | ||||
| 		if err != nil { | ||||
| 			log.Panicf(nil, "error parsing ip prefix from %q: %v", i, err) | ||||
| 		} | ||||
| 
 | ||||
| 		prefs = append(prefs, pref) | ||||
| 	} | ||||
| 
 | ||||
| 	return prefs | ||||
| } | ||||
|  | @ -24,11 +24,11 @@ import ( | |||
| 	"math/rand" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"net/netip" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/httpclient" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/queue" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/transport/delivery" | ||||
|  | @ -44,11 +44,8 @@ func TestDeliveryWorkerPool(t *testing.T) { | |||
| 
 | ||||
| func testDeliveryWorkerPool(t *testing.T, sz int, input []*testrequest) { | ||||
| 	wp := new(delivery.WorkerPool) | ||||
| 	wp.Init(httpclient.New(httpclient.Config{ | ||||
| 		AllowRanges: config.MustParseIPPrefixes([]string{ | ||||
| 			"127.0.0.0/8", | ||||
| 		}), | ||||
| 	})) | ||||
| 	allowLocal := []netip.Prefix{netip.MustParsePrefix("127.0.0.0/8")} | ||||
| 	wp.Init(httpclient.New(httpclient.Config{AllowRanges: allowLocal})) | ||||
| 	wp.Start(sz) | ||||
| 	defer wp.Stop() | ||||
| 	test(t, &wp.Queue, input) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue