istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/server/instance.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package server
    16  
    17  import (
    18  	"sync"
    19  	"time"
    20  
    21  	"istio.io/istio/pkg/log"
    22  )
    23  
    24  type Component func(stop <-chan struct{}) error
    25  
    26  // Instance is a server that is composed a number of Component tasks.
    27  type Instance interface {
    28  	// Start this Server. Any components that were already added
    29  	// will be run immediately. If any error is returned,
    30  	// Start will terminate and return the error immediately.
    31  	//
    32  	// Once all startup components have been run, starts a polling
    33  	// loop to continue monitoring for new components and returns nil.
    34  	Start(stop <-chan struct{}) error
    35  
    36  	// RunComponent adds the given component to the server's run queue.
    37  	RunComponent(name string, t Component)
    38  
    39  	// RunComponentAsync runs the given component asynchronously.
    40  	RunComponentAsync(name string, t Component)
    41  
    42  	// RunComponentAsyncAndWait runs the given component asynchronously. When
    43  	// the server Instance is shutting down, it will wait for the component
    44  	// to complete before exiting.
    45  	// Note: this is best effort; a process can die at any time.
    46  	RunComponentAsyncAndWait(name string, t Component)
    47  
    48  	// Wait for this server Instance to shutdown.
    49  	Wait()
    50  }
    51  
    52  var _ Instance = &instance{}
    53  
    54  // New creates a new server Instance.
    55  func New() Instance {
    56  	return &instance{
    57  		done:       make(chan struct{}),
    58  		components: make(chan task, 1000), // should be enough?
    59  	}
    60  }
    61  
    62  type instance struct {
    63  	components chan task
    64  	done       chan struct{}
    65  
    66  	// requiredTerminations keeps track of tasks that should block instance exit
    67  	// if they are not stopped. This allows important cleanup tasks to be completed.
    68  	// Note: this is still best effort; a process can die at any time.
    69  	requiredTerminations sync.WaitGroup
    70  }
    71  
    72  func (i *instance) Start(stop <-chan struct{}) error {
    73  	shutdown := func() {
    74  		close(i.done)
    75  	}
    76  
    77  	// First, drain all startup tasks and immediately return if any fail.
    78  	for startupDone := false; !startupDone; {
    79  		select {
    80  		case next := <-i.components:
    81  			t0 := time.Now()
    82  			if err := next.task(stop); err != nil {
    83  				// Startup error: terminate and return the error.
    84  				shutdown()
    85  				return err
    86  			}
    87  			runtime := time.Since(t0)
    88  			log := log.WithLabels("name", next.name, "runtime", runtime)
    89  			log.Debugf("started task")
    90  			if runtime > time.Second {
    91  				log.Warnf("slow startup task")
    92  			}
    93  		default:
    94  			// We've drained all of the initial tasks.
    95  			// Break out of the loop and run asynchronously.
    96  			startupDone = true
    97  		}
    98  	}
    99  
   100  	// Start the run loop to continue tasks added after the instance is started.
   101  	go func() {
   102  		for {
   103  			select {
   104  			case <-stop:
   105  				// Wait for any tasks that are required for termination.
   106  				i.requiredTerminations.Wait()
   107  
   108  				// Indicate that this instance is not terminated.
   109  				shutdown()
   110  				return
   111  			case next := <-i.components:
   112  				t0 := time.Now()
   113  				if err := next.task(stop); err != nil {
   114  					logComponentError(next.name, err)
   115  				}
   116  				runtime := time.Since(t0)
   117  				log := log.WithLabels("name", next.name, "runtime", runtime)
   118  				log.Debugf("started post-start task")
   119  				if runtime > time.Second {
   120  					log.Warnf("slow post-start task")
   121  				}
   122  			}
   123  		}
   124  	}()
   125  
   126  	return nil
   127  }
   128  
   129  type task struct {
   130  	name string
   131  	task Component
   132  }
   133  
   134  func (i *instance) RunComponent(name string, t Component) {
   135  	select {
   136  	case <-i.done:
   137  		log.Warnf("attempting to run a new component %q after the server was shutdown", name)
   138  	default:
   139  		i.components <- task{name, t}
   140  	}
   141  }
   142  
   143  func (i *instance) RunComponentAsync(name string, task Component) {
   144  	i.RunComponent(name, func(stop <-chan struct{}) error {
   145  		go func() {
   146  			err := task(stop)
   147  			if err != nil {
   148  				logComponentError(name, err)
   149  			}
   150  		}()
   151  		return nil
   152  	})
   153  }
   154  
   155  func (i *instance) RunComponentAsyncAndWait(name string, task Component) {
   156  	i.RunComponent(name, func(stop <-chan struct{}) error {
   157  		i.requiredTerminations.Add(1)
   158  		go func() {
   159  			err := task(stop)
   160  			if err != nil {
   161  				logComponentError(name, err)
   162  			}
   163  			i.requiredTerminations.Done()
   164  		}()
   165  		return nil
   166  	})
   167  }
   168  
   169  func (i *instance) Wait() {
   170  	<-i.done
   171  }
   172  
   173  func logComponentError(name string, err error) {
   174  	log.Errorf("failure in server component %q: %v", name, err)
   175  }