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 }