diff --git a/app/server.go b/app/server.go index 1b3bc02996..bf957dd808 100644 --- a/app/server.go +++ b/app/server.go @@ -227,11 +227,6 @@ func NewServer(options ...Option) (*Server, error) { } } - // Following outlines the specific set of steps - // performed during server bootup. They are sensitive to order - // and has dependency requirements with the previous step. - // - // Step 1: Config. if s.configStore == nil { innerStore, err := config.NewFileStore("config.json") if err != nil { @@ -245,18 +240,30 @@ func NewServer(options ...Option) (*Server, error) { s.configStore = configStore } - // Step 2: Logging if err := s.initLogging(); err != nil { mlog.Error("Could not initiate logging", mlog.Err(err)) } + var imgErr error + s.imgDecoder, imgErr = imaging.NewDecoder(imaging.DecoderOptions{ + ConcurrencyLevel: runtime.NumCPU(), + }) + if imgErr != nil { + return nil, errors.Wrap(imgErr, "failed to create image decoder") + } + s.imgEncoder, imgErr = imaging.NewEncoder(imaging.EncoderOptions{ + ConcurrencyLevel: runtime.NumCPU(), + }) + if imgErr != nil { + return nil, errors.Wrap(imgErr, "failed to create image encoder") + } + // This is called after initLogging() to avoid a race condition. mlog.Info("Server is initializing...", mlog.String("go_version", runtime.Version())) s.httpService = httpservice.MakeHTTPService(s) - // Step 3: Initialize products. - // Depends on s.httpService. + // Initialize products for name, initializer := range products { prod, err := initializer(s) if err != nil { @@ -266,8 +273,49 @@ func NewServer(options ...Option) (*Server, error) { s.products[name] = prod } - // Step 4: Search Engine - // Depends on Step 1 (config). + // It is important to initialize the hub only after the global logger is set + // to avoid race conditions while logging from inside the hub. + s.HubStart() + + if *s.Config().LogSettings.EnableDiagnostics && *s.Config().LogSettings.EnableSentry { + if strings.Contains(SentryDSN, "placeholder") { + mlog.Warn("Sentry reporting is enabled, but SENTRY_DSN is not set. Disabling reporting.") + } else { + if err := sentry.Init(sentry.ClientOptions{ + Dsn: SentryDSN, + Release: model.BuildHash, + AttachStacktrace: true, + BeforeSend: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { + // sanitize data sent to sentry to reduce exposure of PII + if event.Request != nil { + event.Request.Cookies = "" + event.Request.QueryString = "" + event.Request.Headers = nil + event.Request.Data = "" + } + return event + }, + }); err != nil { + mlog.Warn("Sentry could not be initiated, probably bad DSN?", mlog.Err(err)) + } + } + } + + if *s.Config().ServiceSettings.EnableOpenTracing { + tracer, err := tracing.New() + if err != nil { + return nil, err + } + s.tracer = tracer + } + + s.pushNotificationClient = s.httpService.MakeClient(true) + + if err := utils.TranslationsPreInit(); err != nil { + return nil, errors.Wrapf(err, "unable to load Mattermost translation files") + } + model.AppErrorInit(i18n.T) + searchEngine := searchengine.NewBroker(s.Config()) bleveEngine := bleveengine.NewBleveEngine(s.Config()) if err := bleveEngine.Start(); err != nil { @@ -276,25 +324,41 @@ func NewServer(options ...Option) (*Server, error) { searchEngine.RegisterBleveEngine(bleveEngine) s.SearchEngine = searchEngine - // Step 5: Init Enterprise - // Depends on step 3 (s.Channels() must be non-nil) - // and step 4 (s.SearchEngine must be non-nil) - s.initEnterprise() - - // Step 6: Cache provider. - // At the moment we only have this implementation + // at the moment we only have this implementation // in the future the cache provider will be built based on the loaded config s.CacheProvider = cache.NewProvider() if err := s.CacheProvider.Connect(); err != nil { return nil, errors.Wrapf(err, "Unable to connect to cache provider") } - // It is important to initialize the hub only after the global logger is set - // to avoid race conditions while logging from inside the hub. - s.HubStart() + var err error + if s.seenPendingPostIdsCache, err = s.CacheProvider.NewCache(&cache.CacheOptions{ + Size: PendingPostIDsCacheSize, + }); err != nil { + return nil, errors.Wrap(err, "Unable to create pending post ids cache") + } + if s.statusCache, err = s.CacheProvider.NewCache(&cache.CacheOptions{ + Size: model.StatusCacheSize, + Striped: true, + StripedBuckets: maxInt(runtime.NumCPU()-1, 1), + }); err != nil { + return nil, errors.Wrap(err, "Unable to create status cache") + } + if s.openGraphDataCache, err = s.CacheProvider.NewCache(&cache.CacheOptions{ + Size: openGraphMetadataCacheSize, + }); err != nil { + return nil, errors.Wrap(err, "Unable to create opengraphdata cache") + } + + s.createPushNotificationsHub() + + if err2 := i18n.InitTranslations(*s.Config().LocalizationSettings.DefaultServerLocale, *s.Config().LocalizationSettings.DefaultClientLocale); err2 != nil { + return nil, errors.Wrapf(err2, "unable to load Mattermost translation files") + } + + // initEnterprise needs to be called after products initialization. + s.initEnterprise() - // Step 7: Store. - // Depends on Step 1 (config), 5 (metrics, cluster) and 6 (cacheProvider). if s.newStore == nil { s.newStore = func() (store.Store, error) { s.sqlStore = sqlstore.New(s.Config().SqlSettings, s.Metrics) @@ -331,81 +395,6 @@ func NewServer(options ...Option) (*Server, error) { } } - var err error - s.Store, err = s.newStore() - if err != nil { - return nil, errors.Wrap(err, "cannot create store") - } - - // ------------------------------------------------------------------------- - // Everything below this is not order sensitive and safe to be moved around. - // If you are adding a new field that is non-channels specific, please add - // below this. Otherwise, please add it to Channels struct in app/channels.go. - // ------------------------------------------------------------------------- - - if *s.Config().LogSettings.EnableDiagnostics && *s.Config().LogSettings.EnableSentry { - if strings.Contains(SentryDSN, "placeholder") { - mlog.Warn("Sentry reporting is enabled, but SENTRY_DSN is not set. Disabling reporting.") - } else { - if err2 := sentry.Init(sentry.ClientOptions{ - Dsn: SentryDSN, - Release: model.BuildHash, - AttachStacktrace: true, - BeforeSend: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { - // sanitize data sent to sentry to reduce exposure of PII - if event.Request != nil { - event.Request.Cookies = "" - event.Request.QueryString = "" - event.Request.Headers = nil - event.Request.Data = "" - } - return event - }, - }); err2 != nil { - mlog.Warn("Sentry could not be initiated, probably bad DSN?", mlog.Err(err2)) - } - } - } - - if *s.Config().ServiceSettings.EnableOpenTracing { - tracer, err2 := tracing.New() - if err2 != nil { - return nil, err2 - } - s.tracer = tracer - } - - s.pushNotificationClient = s.httpService.MakeClient(true) - - if err2 := utils.TranslationsPreInit(); err2 != nil { - return nil, errors.Wrapf(err2, "unable to load Mattermost translation files") - } - model.AppErrorInit(i18n.T) - - if s.seenPendingPostIdsCache, err = s.CacheProvider.NewCache(&cache.CacheOptions{ - Size: PendingPostIDsCacheSize, - }); err != nil { - return nil, errors.Wrap(err, "Unable to create pending post ids cache") - } - if s.statusCache, err = s.CacheProvider.NewCache(&cache.CacheOptions{ - Size: model.StatusCacheSize, - Striped: true, - StripedBuckets: maxInt(runtime.NumCPU()-1, 1), - }); err != nil { - return nil, errors.Wrap(err, "Unable to create status cache") - } - if s.openGraphDataCache, err = s.CacheProvider.NewCache(&cache.CacheOptions{ - Size: openGraphMetadataCacheSize, - }); err != nil { - return nil, errors.Wrap(err, "Unable to create opengraphdata cache") - } - - s.createPushNotificationsHub() - - if err2 := i18n.InitTranslations(*s.Config().LocalizationSettings.DefaultServerLocale, *s.Config().LocalizationSettings.DefaultClientLocale); err2 != nil { - return nil, errors.Wrapf(err2, "unable to load Mattermost translation files") - } - templatesDir, ok := templates.GetTemplateDirectory() if !ok { return nil, errors.New("Failed find server templates in \"templates\" directory or MM_SERVER_PATH") @@ -421,6 +410,11 @@ func NewServer(options ...Option) (*Server, error) { }) s.htmlTemplateWatcher = htmlTemplateWatcher + s.Store, err = s.newStore() + if err != nil { + return nil, errors.Wrap(err, "cannot create store") + } + s.userService, err = users.New(users.ServiceConfig{ UserStore: s.Store.User(), SessionStore: s.Store.Session(), @@ -714,20 +708,6 @@ func NewServer(options ...Option) (*Server, error) { } }) - var imgErr error - s.imgDecoder, imgErr = imaging.NewDecoder(imaging.DecoderOptions{ - ConcurrencyLevel: runtime.NumCPU(), - }) - if imgErr != nil { - return nil, errors.Wrap(imgErr, "failed to create image decoder") - } - s.imgEncoder, imgErr = imaging.NewEncoder(imaging.EncoderOptions{ - ConcurrencyLevel: runtime.NumCPU(), - }) - if imgErr != nil { - return nil, errors.Wrap(imgErr, "failed to create image encoder") - } - return s, nil } @@ -2290,6 +2270,19 @@ func (s *Server) ReadFile(path string) ([]byte, *model.AppError) { return result, nil } +// func (s *Server) WriteFile(fr io.Reader, path string) (int64, *model.AppError) { +// backend, err := s.FileBackend() +// if err != nil { +// return 0, err +// } + +// result, nErr := backend.WriteFile(fr, path) +// if nErr != nil { +// return result, model.NewAppError("WriteFile", "api.file.write_file.app_error", nil, nErr.Error(), http.StatusInternalServerError) +// } +// return result, nil +// } + func createDNDStatusExpirationRecurringTask(a *App) { a.ch.srv.dndTaskMut.Lock() a.ch.srv.dndTask = model.CreateRecurringTaskFromNextIntervalTime("Unset DND Statuses", a.UpdateDNDStatusOfUsers, 5*time.Minute)