Repository: pingcap/tidb
File: pkg/session/session.go (Go)

Code before:
		s.statsCollector.Delete()
	}
	if s.idxUsageCollector != nil {
		s.idxUsageCollector.Flush()
	}
	telemetry.GlobalBuiltinFunctionsUsage.Collect(s.GetBuiltinFunctionUsage())
	bindValue := s.Value(bindinfo.SessionBindInfoKeyType)
	if bindValue != nil {
		bindValue.(bindinfo.SessionBindingHandle).Close()
	}
	ctx := context.WithValue(context.TODO(), inCloseSession{}, struct{}{})
	s.RollbackTxn(ctx)
	s.sessionVars.WithdrawAllPreparedStmt()
	if s.stmtStats != nil {
		s.stmtStats.SetFinished()
	}
	s.sessionVars.ClearDiskFullOpt()
	if s.sessionPlanCache != nil {
		s.sessionPlanCache.Close()
	}
	if s.sessionVars.ConnectionID != 0 {
		memory.RemovePoolFromGlobalMemArbitrator(s.sessionVars.ConnectionID)
	}
	// Detach session trackers as part of the session cleanup workflow.
	// ANALYZE statements attach the session's MemTracker to GlobalAnalyzeMemoryTracker,
	// and proper detachment ensures that closed sessions can be garbage collected.
	if s.sessionVars.MemTracker != nil {
		s.sessionVars.MemTracker.Detach()
	}
	if s.sessionVars.DiskTracker != nil {
		s.sessionVars.DiskTracker.Detach()
	}
}

// GetSessionVars implements the context.Context interface.
func (s *session) GetSessionVars() *variable.SessionVars {
	return s.sessionVars
}

// GetPlanCtx returns the PlanContext.
func (s *session) GetPlanCtx() planctx.PlanContext {
	return s.pctx
}

// GetExprCtx returns the expression context of the session.
func (s *session) GetExprCtx() exprctx.ExprContext {
	return s.exprctx
}

// GetTableCtx returns the table.MutateContext
func (s *session) GetTableCtx() tblctx.MutateContext {

Review comment (suggestion):
```suggestion
	// Detach session trackers during session cleanup.
	// ANALYZE attaches session MemTracker to GlobalAnalyzeMemoryTracker; without
	// detachment, closed sessions cannot be garbage collected.
```

Code after:
		s.statsCollector.Delete()
	}
	if s.idxUsageCollector != nil {
		s.idxUsageCollector.Flush()
	}
	telemetry.GlobalBuiltinFunctionsUsage.Collect(s.GetBuiltinFunctionUsage())
	bindValue := s.Value(bindinfo.SessionBindInfoKeyType)
	if bindValue != nil {
		bindValue.(bindinfo.SessionBindingHandle).Close()
	}
	ctx := context.WithValue(context.TODO(), inCloseSession{}, struct{}{})
	s.RollbackTxn(ctx)
	s.sessionVars.WithdrawAllPreparedStmt()
	if s.stmtStats != nil {
		s.stmtStats.SetFinished()
	}
	s.sessionVars.ClearDiskFullOpt()
	if s.sessionPlanCache != nil {
		s.sessionPlanCache.Close()
	}
	if s.sessionVars.ConnectionID != 0 {
		memory.RemovePoolFromGlobalMemArbitrator(s.sessionVars.ConnectionID)
	}
	// Detach session trackers during session cleanup.
	// ANALYZE attaches session MemTracker to GlobalAnalyzeMemoryTracker; without
	// detachment, closed sessions cannot be garbage collected.
	if s.sessionVars.MemTracker != nil {
		s.sessionVars.MemTracker.Detach()
	}
	if s.sessionVars.DiskTracker != nil {
		s.sessionVars.DiskTracker.Detach()
	}
}

// GetSessionVars implements the context.Context interface.
func (s *session) GetSessionVars() *variable.SessionVars {
	return s.sessionVars
}

// GetPlanCtx returns the PlanContext.
func (s *session) GetPlanCtx() planctx.PlanContext {
	return s.pctx
}

// GetExprCtx returns the expression context of the session.
func (s *session) GetExprCtx() exprctx.ExprContext {
	return s.exprctx
}

// GetTableCtx returns the table.MutateContext
func (s *session) GetTableCtx() tblctx.MutateContext {