From 561bc968c8737bd44ee0c6e0f08314f7ea0f5897 Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Mon, 11 Oct 2021 19:24:50 +0530 Subject: [PATCH] Creating empty Channels shell (#18611) * Creating empty Channels shell This is the first step in moving to a product-based isolated architecture. For now, Channels is empty and does not contain anything. Next step is to change App to contain Channels instead of Server. Some of the initialization code in NewServer would need to be moved inside NewChannels. This would complete the full pass-through mode of accessing everything. The last step would be to gradually move Channels related fields from Server into Channels, keeping Server to be just the global level struct. ```release-note NONE ``` * fix vet failure ```release-note NONE ``` --- app/app.go | 3 +++ app/channels.go | 29 +++++++++++++++++++++++++++++ app/product.go | 15 +++++++++++++++ app/server.go | 31 +++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 app/channels.go create mode 100644 app/product.go diff --git a/app/app.go b/app/app.go index e8a9006915..252fcef0e7 100644 --- a/app/app.go +++ b/app/app.go @@ -20,6 +20,9 @@ import ( "github.com/mattermost/mattermost-server/v6/utils" ) +// App is a pure functional component that does not have any fields, except Server. +// It is a request-scoped struct constructed every time a request hits the server, +// and its only purpose is to provide business logic to Server via its methods. type App struct { srv *Server } diff --git a/app/channels.go b/app/channels.go new file mode 100644 index 0000000000..6ae4a057e6 --- /dev/null +++ b/app/channels.go @@ -0,0 +1,29 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package app + +// Channels contains all channels related state. +type Channels struct { + s *Server +} + +func init() { + RegisterProduct("channels", func(s *Server) (Product, error) { + return NewChannels(s) + }) +} + +func NewChannels(s *Server) (*Channels, error) { + return &Channels{ + s: s, + }, nil +} + +func (c *Channels) Start() error { + return nil +} + +func (c *Channels) Stop() error { + return nil +} diff --git a/app/product.go b/app/product.go new file mode 100644 index 0000000000..cc6c4f55a0 --- /dev/null +++ b/app/product.go @@ -0,0 +1,15 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package app + +type Product interface { + Start() error + Stop() error +} + +var products = make(map[string]func(*Server) (Product, error)) + +func RegisterProduct(name string, f func(*Server) (Product, error)) { + products[name] = f +} diff --git a/app/server.go b/app/server.go index 89b2cd2ed1..34e5ccb47d 100644 --- a/app/server.go +++ b/app/server.go @@ -209,6 +209,8 @@ type Server struct { dndTaskMut sync.Mutex dndTask *model.ScheduledTask + + products map[string]Product } func NewServer(options ...Option) (*Server, error) { @@ -222,6 +224,7 @@ func NewServer(options ...Option) (*Server, error) { licenseListeners: map[string]func(*model.License, *model.License){}, hashSeed: maphash.MakeSeed(), uploadLockMap: map[string]bool{}, + products: make(map[string]Product), } for _, option := range options { @@ -691,6 +694,17 @@ func NewServer(options ...Option) (*Server, error) { s.ShutDownPlugins() } }) + + // Initialize products + for name, initializer := range products { + prod, err := initializer(s) + if err != nil { + return nil, errors.Wrapf(err, "error initializing product: %s", name) + } + + s.products[name] = prod + } + s.AddConfigListener(func(oldCfg, newCfg *model.Config) { if !oldCfg.FeatureFlags.TimedDND && newCfg.FeatureFlags.TimedDND { runDNDStatusExpireJob(app) @@ -985,6 +999,15 @@ func (s *Server) Shutdown() { defer sentry.Flush(2 * time.Second) + // Stop products. + // This needs to happen before because products are dependent + // on parent services. + for name, product := range s.products { + if err := product.Stop(); err != nil { + mlog.Warn("Unable to cleanly stop product", mlog.String("name", name), mlog.Err(err)) + } + } + s.HubStop() s.ShutDownPlugins() s.RemoveLicenseListener(s.licenseListenerId) @@ -1380,6 +1403,14 @@ func (s *Server) Start() error { mlog.Error("Error starting inter-cluster services", mlog.Err(err)) } + // Start products. + // This needs to happen after the server has started. + for name, product := range s.products { + if err := product.Start(); err != nil { + return errors.Wrapf(err, "Unable to start %s", name) + } + } + return nil }