Remove app initialization from WebHub (#18687)

- Make it a Server method.
- Pass Server instead of App.
- Remove global app instance from NewServer, rather
create local instances whenever needed.
- Remove App from Websocket router, and create dynamically
on every request.
- Remove HubStart and HubStop from App methods.
- Explicitly using s.Log instead of the global logger
to indicate dependency on Server. We could have passed the logger
explicitly but it doesn't look ideal.

```release-note
NONE
```
Этот коммит содержится в:
Agniva De Sarker
2021-10-18 12:36:13 +05:30
коммит произвёл GitHub
родитель 609fea0002
Коммит d949bd1638
7 изменённых файлов: 50 добавлений и 102 удалений

Просмотреть файл

@@ -212,8 +212,6 @@ type AppIface interface {
HasRemote(channelID string, remoteID string) (bool, error)
// HubRegister registers a connection to a hub.
HubRegister(webConn *WebConn)
// HubStart starts all the hubs.
HubStart()
// HubUnregister unregisters a connection from a hub.
HubUnregister(webConn *WebConn)
// InstallMarketplacePlugin installs a plugin listed in the marketplace server. It will get the plugin bundle
@@ -244,8 +242,6 @@ type AppIface interface {
MoveChannel(c *request.Context, team *model.Team, channel *model.Channel, user *model.User) *model.AppError
// NewWebConn returns a new WebConn instance.
NewWebConn(cfg *WebConnConfig) *WebConn
// NewWebHub creates a new Hub.
NewWebHub() *Hub
// NotifySessionsExpired is called periodically from the job server to notify any mobile sessions that have expired.
NotifySessionsExpired() *model.AppError
// OverrideIconURLIfEmoji changes the post icon override URL prop, if it has an emoji icon,
@@ -805,7 +801,6 @@ type AppIface interface {
HasPermissionToTeam(askingUserId string, teamID string, permission *model.Permission) bool
HasPermissionToUser(askingUserId string, userID string) bool
HasSharedChannel(channelID string) (bool, error)
HubStop()
ImageProxy() *imageproxy.ImageProxy
ImageProxyAdder() func(string) string
ImageProxyRemover() (f func(string) string)

Просмотреть файл

@@ -10449,36 +10449,6 @@ func (a *OpenTracingAppLayer) HubRegister(webConn *app.WebConn) {
a.app.HubRegister(webConn)
}
func (a *OpenTracingAppLayer) HubStart() {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.HubStart")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
a.app.HubStart()
}
func (a *OpenTracingAppLayer) HubStop() {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.HubStop")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
a.app.HubStop()
}
func (a *OpenTracingAppLayer) HubUnregister(webConn *app.WebConn) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.HubUnregister")
@@ -11530,23 +11500,6 @@ func (a *OpenTracingAppLayer) NewWebConn(cfg *app.WebConnConfig) *app.WebConn {
return resultVar0
}
func (a *OpenTracingAppLayer) NewWebHub() *app.Hub {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.NewWebHub")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0 := a.app.NewWebHub()
return resultVar0
}
func (a *OpenTracingAppLayer) NotifyAndSetWarnMetricAck(warnMetricId string, sender *model.User, forceAck bool, isBot bool) *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.NotifyAndSetWarnMetricAck")

Просмотреть файл

@@ -280,8 +280,7 @@ func NewServer(options ...Option) (*Server, error) {
// It is important to initialize the hub only after the global logger is set
// to avoid race conditions while logging from inside the hub.
app := New(ServerConnector(s.Channels()))
app.HubStart()
s.HubStart()
if *s.Config().LogSettings.EnableDiagnostics && *s.Config().LogSettings.EnableSentry {
if strings.Contains(SentryDSN, "placeholder") {
@@ -543,7 +542,6 @@ func NewServer(options ...Option) (*Server, error) {
s.WebSocketRouter = &WebSocketRouter{
handlers: make(map[string]webSocketHandler),
app: app,
}
mailConfig := s.MailServiceConfig()
@@ -666,7 +664,8 @@ func NewServer(options ...Option) (*Server, error) {
// if enabled - perform initial product notices fetch
if *s.Config().AnnouncementSettings.AdminNoticesEnabled || *s.Config().AnnouncementSettings.UserNoticesEnabled {
go func() {
if err := app.UpdateProductNotices(); err != nil {
appInstance := New(ServerConnector(s.Channels()))
if err := appInstance.UpdateProductNotices(); err != nil {
mlog.Warn("Failied to perform initial product notices fetch", mlog.Err(err))
}
}()
@@ -678,8 +677,9 @@ func NewServer(options ...Option) (*Server, error) {
c := request.EmptyContext()
s.AddConfigListener(func(oldConfig *model.Config, newConfig *model.Config) {
appInstance := New(ServerConnector(s.Channels()))
if *oldConfig.GuestAccountsSettings.Enable && !*newConfig.GuestAccountsSettings.Enable {
if appErr := app.DeactivateGuests(c); appErr != nil {
if appErr := appInstance.DeactivateGuests(c); appErr != nil {
mlog.Error("Unable to deactivate guest accounts", mlog.Err(appErr))
}
}
@@ -687,16 +687,18 @@ func NewServer(options ...Option) (*Server, error) {
// Disable active guest accounts on first run if guest accounts are disabled
if !*s.Config().GuestAccountsSettings.Enable {
if appErr := app.DeactivateGuests(c); appErr != nil {
appInstance := New(ServerConnector(s.Channels()))
if appErr := appInstance.DeactivateGuests(c); appErr != nil {
mlog.Error("Unable to deactivate guest accounts", mlog.Err(appErr))
}
}
if s.runEssentialJobs {
s.Go(func() {
appInstance := New(ServerConnector(s.Channels()))
s.runLicenseExpirationCheckJob()
runCheckAdminSupportStatusJob(app, c)
runDNDStatusExpireJob(app)
runCheckAdminSupportStatusJob(appInstance, c)
runDNDStatusExpireJob(appInstance)
})
s.runJobs()
}
@@ -907,7 +909,7 @@ func (s *Server) removeUnlicensedLogTargets(license *model.License) {
})
}
func (s *Server) startInterClusterServices(license *model.License, app *App) error {
func (s *Server) startInterClusterServices(license *model.License) error {
if license == nil {
mlog.Debug("No license provided; Remote Cluster services disabled")
return nil
@@ -956,7 +958,8 @@ func (s *Server) startInterClusterServices(license *model.License, app *App) err
return nil
}
scs, err := sharedchannel.NewSharedChannelService(s, app)
appInstance := New(ServerConnector(s.Channels()))
scs, err := sharedchannel.NewSharedChannelService(s, appInstance)
if err != nil {
return err
}
@@ -1417,7 +1420,7 @@ func (s *Server) Start() error {
}
}
if err := s.startInterClusterServices(s.License(), s.WebSocketRouter.app); err != nil {
if err := s.startInterClusterServices(s.License()); err != nil {
mlog.Error("Error starting inter-cluster services", mlog.Err(err))
}

Просмотреть файл

@@ -50,7 +50,7 @@ type Hub struct {
// connectionCount should be kept first.
// See https://github.com/mattermost/mattermost-server/pull/7281
connectionCount int64
app *App
srv *Server
connectionIndex int
register chan *WebConn
unregister chan *WebConn
@@ -65,10 +65,10 @@ type Hub struct {
checkConn chan *webConnCheckMessage
}
// NewWebHub creates a new Hub.
func (a *App) NewWebHub() *Hub {
// newWebHub creates a new Hub.
func newWebHub(s *Server) *Hub {
return &Hub{
app: a,
srv: s,
register: make(chan *WebConn),
unregister: make(chan *WebConn),
broadcast: make(chan *model.WebSocketEvent, broadcastQueueSize),
@@ -87,21 +87,21 @@ func (a *App) TotalWebsocketConnections() int {
}
// HubStart starts all the hubs.
func (a *App) HubStart() {
func (s *Server) HubStart() {
// Total number of hubs is twice the number of CPUs.
numberOfHubs := runtime.NumCPU() * 2
mlog.Info("Starting websocket hubs", mlog.Int("number_of_hubs", numberOfHubs))
s.Log.Info("Starting websocket hubs", mlog.Int("number_of_hubs", numberOfHubs))
hubs := make([]*Hub, numberOfHubs)
for i := 0; i < numberOfHubs; i++ {
hubs[i] = a.NewWebHub()
hubs[i] = newWebHub(s)
hubs[i].connectionIndex = i
hubs[i].Start()
}
// Assigning to the hubs slice without any mutex is fine because it is only assigned once
// during the start of the program and always read from after that.
a.ch.srv.hubs = hubs
s.hubs = hubs
}
func (a *App) invalidateCacheForWebhook(webhookID string) {
@@ -117,10 +117,6 @@ func (s *Server) HubStop() {
}
}
func (a *App) HubStop() {
a.Srv().HubStop()
}
// GetHubForUserId returns the hub for a given user id.
func (s *Server) GetHubForUserId(userID string) *Hub {
// TODO: check if caching the userID -> hub mapping
@@ -356,7 +352,7 @@ func (h *Hub) Broadcast(message *model.WebSocketEvent) {
// And possibly, we can look into doing the hub initialization inside
// NewServer itself.
if h != nil && message != nil {
if metrics := h.app.Metrics(); metrics != nil {
if metrics := h.srv.Metrics; metrics != nil {
metrics.IncrementWebSocketBroadcastBufferSize(strconv.Itoa(h.connectionIndex), 1)
}
select {
@@ -416,6 +412,8 @@ func (h *Hub) Start() {
ticker := time.NewTicker(inactiveConnReaperInterval)
defer ticker.Stop()
appInstance := New(ServerConnector(h.srv.Channels()))
connIndex := newHubConnectionIndex(inactiveConnReaperInterval)
for {
@@ -449,7 +447,7 @@ func (h *Hub) Start() {
connIndex.RemoveInactiveConnections()
case webConn := <-h.register:
var oldConn *WebConn
if *h.app.Config().ServiceSettings.EnableReliableWebSockets {
if *h.srv.Config().ServiceSettings.EnableReliableWebSockets {
// Delete the old conn from connIndex if it exists.
oldConn = connIndex.RemoveInactiveByConnectionID(
webConn.GetSession().UserId,
@@ -474,7 +472,7 @@ func (h *Hub) Start() {
case webConn := <-h.unregister:
// If already removed (via queue full), then removing again becomes a noop.
// But if not removed, mark inactive.
if *h.app.Config().ServiceSettings.EnableReliableWebSockets {
if *h.srv.Config().ServiceSettings.EnableReliableWebSockets {
webConn.active = false
} else {
connIndex.Remove(webConn)
@@ -488,8 +486,8 @@ func (h *Hub) Start() {
conns := connIndex.ForUser(webConn.UserId)
if len(conns) == 0 || areAllInactive(conns) {
h.app.Srv().Go(func() {
h.app.SetStatusOffline(webConn.UserId, false)
h.srv.Go(func() {
appInstance.SetStatusOffline(webConn.UserId, false)
})
continue
}
@@ -503,9 +501,9 @@ func (h *Hub) Start() {
}
}
if h.app.IsUserAway(latestActivity) {
h.app.Srv().Go(func() {
h.app.SetStatusLastActivityAt(webConn.UserId, latestActivity)
if appInstance.IsUserAway(latestActivity) {
h.srv.Go(func() {
appInstance.SetStatusLastActivityAt(webConn.UserId, latestActivity)
})
}
case userID := <-h.invalidateUser:
@@ -533,7 +531,7 @@ func (h *Hub) Start() {
connIndex.Remove(directMsg.conn)
}
case msg := <-h.broadcast:
if metrics := h.app.Metrics(); metrics != nil {
if metrics := h.srv.Metrics; metrics != nil {
metrics.DecrementWebSocketBroadcastBufferSize(strconv.Itoa(h.connectionIndex), 1)
}
msg = msg.PrecomputeJSON()
@@ -565,7 +563,7 @@ func (h *Hub) Start() {
case <-h.stop:
for webConn := range connIndex.All() {
webConn.Close()
h.app.SetStatusOffline(webConn.UserId, false)
appInstance.SetStatusOffline(webConn.UserId, false)
}
h.explicitStop = true

Просмотреть файл

@@ -66,7 +66,7 @@ func TestHubStopWithMultipleConnections(t *testing.T) {
s := httptest.NewServer(dummyWebsocketHandler(t))
defer s.Close()
th.App.HubStart()
th.Server.HubStart()
wc1 := registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
wc2 := registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
wc3 := registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
@@ -84,12 +84,12 @@ func TestHubStopRaceCondition(t *testing.T) {
// So we just use this quick hack for the test.
s := httptest.NewServer(dummyWebsocketHandler(t))
th.App.HubStart()
th.Server.HubStart()
wc1 := registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
defer wc1.Close()
hub := th.App.Srv().hubs[0]
th.App.HubStop()
th.Server.HubStop()
done := make(chan bool)
go func() {
@@ -347,7 +347,7 @@ func TestHubIsRegistered(t *testing.T) {
s := httptest.NewServer(dummyWebsocketHandler(t))
defer s.Close()
th.App.HubStart()
th.Server.HubStart()
wc1 := registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
wc2 := registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
wc3 := registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
@@ -418,7 +418,7 @@ func BenchmarkGetHubForUserId(b *testing.B) {
th := Setup(b).InitBasic()
defer th.TearDown()
th.App.HubStart()
th.Server.HubStart()
b.ResetTimer()
for i := 0; i < b.N; i++ {

Просмотреть файл

@@ -153,7 +153,7 @@ func Fuzz(data []byte) int {
s := httptest.NewServer(dummyWebsocketHandler())
th.App.HubStart()
th.Server.HubStart()
u1 := th.CreateUser()
u2 := th.CreateUser()

Просмотреть файл

@@ -16,7 +16,6 @@ type webSocketHandler interface {
}
type WebSocketRouter struct {
app *App
handlers map[string]webSocketHandler
}
@@ -27,13 +26,13 @@ func (wr *WebSocketRouter) Handle(action string, handler webSocketHandler) {
func (wr *WebSocketRouter) ServeWebSocket(conn *WebConn, r *model.WebSocketRequest) {
if r.Action == "" {
err := model.NewAppError("ServeWebSocket", "api.web_socket_router.no_action.app_error", nil, "", http.StatusBadRequest)
returnWebSocketError(wr.app, conn, r, err)
returnWebSocketError(conn.App, conn, r, err)
return
}
if r.Seq <= 0 {
err := model.NewAppError("ServeWebSocket", "api.web_socket_router.bad_seq.app_error", nil, "", http.StatusBadRequest)
returnWebSocketError(wr.app, conn, r, err)
returnWebSocketError(conn.App, conn, r, err)
return
}
@@ -48,7 +47,7 @@ func (wr *WebSocketRouter) ServeWebSocket(conn *WebConn, r *model.WebSocketReque
return
}
session, err := wr.app.GetSession(token)
session, err := conn.App.GetSession(token)
if err != nil {
conn.WebSocket.Close()
return
@@ -59,15 +58,15 @@ func (wr *WebSocketRouter) ServeWebSocket(conn *WebConn, r *model.WebSocketReque
// TODO: Same logic to reconnect queue as api4/websocket.go
wr.app.HubRegister(conn)
conn.App.HubRegister(conn)
wr.app.Srv().Go(func() {
wr.app.SetStatusOnline(session.UserId, false)
wr.app.UpdateLastActivityAtIfNeeded(*session)
conn.App.Srv().Go(func() {
conn.App.SetStatusOnline(session.UserId, false)
conn.App.UpdateLastActivityAtIfNeeded(*session)
})
resp := model.NewWebSocketResponse(model.StatusOk, r.Seq, nil)
hub := wr.app.GetHubForUserId(conn.UserId)
hub := conn.App.GetHubForUserId(conn.UserId)
if hub == nil {
return
}
@@ -78,14 +77,14 @@ func (wr *WebSocketRouter) ServeWebSocket(conn *WebConn, r *model.WebSocketReque
if !conn.IsAuthenticated() {
err := model.NewAppError("ServeWebSocket", "api.web_socket_router.not_authenticated.app_error", nil, "", http.StatusUnauthorized)
returnWebSocketError(wr.app, conn, r, err)
returnWebSocketError(conn.App, conn, r, err)
return
}
handler, ok := wr.handlers[r.Action]
if !ok {
err := model.NewAppError("ServeWebSocket", "api.web_socket_router.bad_action.app_error", nil, "", http.StatusInternalServerError)
returnWebSocketError(wr.app, conn, r, err)
returnWebSocketError(conn.App, conn, r, err)
return
}