sourcegraph/cmd/precise-code-intel-worker/shared/shared.go
Joe Chen 2589fef13e
lib/background: upgrade Routine interface with context and errors (#62136)
This PR is a result/followup of the improvements we've made in the [SAMS repo](https://github.com/sourcegraph/sourcegraph-accounts/pull/199) that allows call sites to pass down a context (primarily to indicate deadline, and of course, cancellation if desired) and collects the error returned from `background.Routine`s `Stop` method.

Note that I did not adopt returning error from `Stop` method because I realize in monorepo, the more common (and arguably the desired) pattern is to hang on the call of `Start` method until `Stop` is called, so it is meaningless to collect errors from `Start` methods as return values anyway, and doing that would also complicate the design and semantics more than necessary.

All usages of the the `background.Routine` and `background.CombinedRoutines` are updated, I DID NOT try to interpret the code logic and make anything better other than fixing compile and test errors.

The only file that contains the core change is the [`lib/background/background.go`](https://github.com/sourcegraph/sourcegraph/pull/62136/files#diff-65c3228388620e91f8c22d91c18faac3f985fc67d64b08612df18fa7c04fafcd).
2024-05-24 10:04:55 -04:00

156 lines
5.0 KiB
Go

package shared
import (
"context"
"database/sql"
"net/http"
"time"
smithyhttp "github.com/aws/smithy-go/transport/http"
"github.com/sourcegraph/log"
"github.com/sourcegraph/sourcegraph/internal/authz"
"github.com/sourcegraph/sourcegraph/internal/authz/providers"
srp "github.com/sourcegraph/sourcegraph/internal/authz/subrepoperms"
"github.com/sourcegraph/sourcegraph/internal/codeintel"
codeintelshared "github.com/sourcegraph/sourcegraph/internal/codeintel/shared"
"github.com/sourcegraph/sourcegraph/internal/codeintel/shared/lsifuploadstore"
"github.com/sourcegraph/sourcegraph/internal/codeintel/uploads"
"github.com/sourcegraph/sourcegraph/internal/conf"
"github.com/sourcegraph/sourcegraph/internal/conf/conftypes"
"github.com/sourcegraph/sourcegraph/internal/database"
connections "github.com/sourcegraph/sourcegraph/internal/database/connections/live"
"github.com/sourcegraph/sourcegraph/internal/encryption/keyring"
"github.com/sourcegraph/sourcegraph/internal/goroutine"
"github.com/sourcegraph/sourcegraph/internal/honey"
"github.com/sourcegraph/sourcegraph/internal/httpserver"
"github.com/sourcegraph/sourcegraph/internal/observation"
"github.com/sourcegraph/sourcegraph/internal/service"
"github.com/sourcegraph/sourcegraph/internal/uploadstore"
"github.com/sourcegraph/sourcegraph/lib/errors"
)
const addr = ":3188"
func Main(ctx context.Context, observationCtx *observation.Context, ready service.ReadyFunc, config Config) error {
logger := observationCtx.Logger
// Initialize tracing/metrics
observationCtx = observation.NewContext(logger, observation.Honeycomb(&honey.Dataset{
Name: "codeintel-worker",
}))
if err := keyring.Init(ctx); err != nil {
return errors.Wrap(err, "initializing keyring")
}
// Connect to databases
db := database.NewDB(logger, mustInitializeDB(observationCtx))
codeIntelDB := mustInitializeCodeIntelDB(observationCtx)
// Migrations may take a while, but after they're done we'll immediately
// spin up a server and can accept traffic. Inform external clients we'll
// be ready for traffic.
ready()
// Initialize sub-repo permissions client
authz.DefaultSubRepoPermsChecker = srp.NewSubRepoPermsClient(db.SubRepoPerms())
services, err := codeintel.NewServices(codeintel.ServiceDependencies{
DB: db,
CodeIntelDB: codeIntelDB,
ObservationCtx: observationCtx,
})
if err != nil {
return errors.Wrap(err, "creating codeintel services")
}
// Initialize stores
uploadStore, err := lsifuploadstore.New(ctx, observationCtx, config.LSIFUploadStoreConfig)
if err != nil {
return errors.Wrap(err, "creating upload store")
}
if err := initializeUploadStore(ctx, uploadStore); err != nil {
return errors.Wrap(err, "initializing upload store")
}
// Initialize worker
worker := uploads.NewUploadProcessorJob(
observationCtx,
services.UploadsService,
db,
uploadStore,
config.WorkerConcurrency,
config.WorkerBudget,
config.WorkerPollInterval,
config.MaximumRuntimePerJob,
)
// Initialize health server
server := httpserver.NewFromAddr(addr, &http.Server{
ReadTimeout: 75 * time.Second,
WriteTimeout: 10 * time.Minute,
Handler: httpserver.NewHandler(nil),
})
// Go!
return goroutine.MonitorBackgroundRoutines(ctx, append(worker, server)...)
}
func mustInitializeDB(observationCtx *observation.Context) *sql.DB {
dsn := conf.GetServiceConnectionValueAndRestartOnChange(func(serviceConnections conftypes.ServiceConnections) string {
return serviceConnections.PostgresDSN
})
sqlDB, err := connections.EnsureNewFrontendDB(observationCtx, dsn, "precise-code-intel-worker")
if err != nil {
log.Scoped("init db").Fatal("Failed to connect to frontend database", log.Error(err))
}
//
// START FLAILING
ctx := context.Background()
db := database.NewDB(observationCtx.Logger, sqlDB)
go func() {
for range time.NewTicker(providers.RefreshInterval(conf.Get())).C {
allowAccessByDefault, authzProviders, _, _, _ := providers.ProvidersFromConfig(ctx, conf.Get(), db)
authz.SetProviders(allowAccessByDefault, authzProviders)
}
}()
// END FLAILING
//
return sqlDB
}
func mustInitializeCodeIntelDB(observationCtx *observation.Context) codeintelshared.CodeIntelDB {
dsn := conf.GetServiceConnectionValueAndRestartOnChange(func(serviceConnections conftypes.ServiceConnections) string {
return serviceConnections.CodeIntelPostgresDSN
})
db, err := connections.EnsureNewCodeIntelDB(observationCtx, dsn, "precise-code-intel-worker")
if err != nil {
log.Scoped("init db").Fatal("Failed to connect to codeintel database", log.Error(err))
}
return codeintelshared.NewCodeIntelDB(observationCtx.Logger, db)
}
func initializeUploadStore(ctx context.Context, uploadStore uploadstore.Store) error {
for {
if err := uploadStore.Init(ctx); err == nil || !isRequestError(err) {
return err
}
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(250 * time.Millisecond):
}
}
}
func isRequestError(err error) bool {
return errors.HasType(err, &smithyhttp.RequestSendError{})
}