github.com/matrixorigin/matrixone@v1.2.0/pkg/taskservice/task_service_holder.go (about) 1 // Copyright 2022 Matrix Origin 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 taskservice 16 17 import ( 18 "context" 19 "fmt" 20 "sync" 21 22 "github.com/matrixorigin/matrixone/pkg/common/log" 23 "github.com/matrixorigin/matrixone/pkg/common/moerr" 24 "github.com/matrixorigin/matrixone/pkg/common/runtime" 25 "github.com/matrixorigin/matrixone/pkg/common/stopper" 26 logservicepb "github.com/matrixorigin/matrixone/pkg/pb/logservice" 27 "github.com/matrixorigin/matrixone/pkg/pb/task" 28 "go.uber.org/zap" 29 ) 30 31 var ( 32 ErrNotReady = moerr.NewInvalidStateNoCtx("task store not ready") 33 ) 34 35 type taskServiceHolder struct { 36 rt runtime.Runtime 37 addressFactory func(context.Context, bool) (string, error) 38 taskStorageFactorySelector func(string, string, string) TaskStorageFactory 39 mu struct { 40 sync.RWMutex 41 closed bool 42 store TaskStorage 43 service TaskService 44 } 45 } 46 47 // NewTaskServiceHolder create a task service hold, it will create task storage and task service from the hakeeper's schedule command. 48 func NewTaskServiceHolder( 49 rt runtime.Runtime, 50 addressFactory func(context.Context, bool) (string, error)) TaskServiceHolder { 51 return NewTaskServiceHolderWithTaskStorageFactorySelector(rt, addressFactory, func(username, password, database string) TaskStorageFactory { 52 return NewMySQLBasedTaskStorageFactory(username, password, database) 53 }) 54 } 55 56 // NewTaskServiceHolderWithTaskStorageFactorySelector is similar to NewTaskServiceHolder, but with a special 57 // task storage facroty selector 58 func NewTaskServiceHolderWithTaskStorageFactorySelector( 59 rt runtime.Runtime, 60 addressFactory func(context.Context, bool) (string, error), 61 selector func(string, string, string) TaskStorageFactory) TaskServiceHolder { 62 return &taskServiceHolder{ 63 rt: rt, 64 addressFactory: addressFactory, 65 taskStorageFactorySelector: selector, 66 } 67 } 68 69 func (h *taskServiceHolder) Close() error { 70 defer h.rt.Logger().LogAction("close service-holder", 71 log.DefaultLogOptions().WithLevel(zap.DebugLevel))() 72 73 h.mu.Lock() 74 defer h.mu.Unlock() 75 76 if h.mu.closed { 77 return nil 78 } 79 h.mu.closed = true 80 if h.mu.store == nil { 81 return nil 82 } 83 return h.mu.service.Close() 84 } 85 86 func (h *taskServiceHolder) Create(command logservicepb.CreateTaskService) error { 87 // TODO: In any case, the username and password are not printed in the log, morpc needs to fix 88 if command.User.Username == "" || command.User.Password == "" { 89 h.rt.Logger().Debug("start task runner skipped", 90 zap.String("reason", "empty task user and passwd")) 91 return moerr.NewInvalidStateNoCtx("empty task user and passwd") 92 } 93 94 h.mu.Lock() 95 defer h.mu.Unlock() 96 if h.mu.service != nil { 97 return nil 98 } 99 100 store := newRefreshableTaskStorage( 101 h.rt, 102 h.addressFactory, 103 h.taskStorageFactorySelector(command.User.Username, 104 command.User.Password, 105 command.TaskDatabase)) 106 h.mu.store = store 107 h.mu.service = NewTaskService(h.rt, store) 108 return nil 109 } 110 111 func (h *taskServiceHolder) Get() (TaskService, bool) { 112 h.mu.RLock() 113 defer h.mu.RUnlock() 114 if h.mu.service == nil { 115 return nil, false 116 } 117 return h.mu.service, true 118 } 119 120 type refreshableTaskStorage struct { 121 rt runtime.Runtime 122 refreshC chan string 123 stopper *stopper.Stopper 124 addressFactory func(context.Context, bool) (string, error) 125 storeFactory TaskStorageFactory 126 mu struct { 127 sync.RWMutex 128 closed bool 129 lastAddress string 130 store TaskStorage 131 } 132 } 133 134 func newRefreshableTaskStorage( 135 rt runtime.Runtime, 136 addressFactory func(context.Context, bool) (string, error), 137 storeFactory TaskStorageFactory) TaskStorage { 138 s := &refreshableTaskStorage{ 139 rt: rt, 140 refreshC: make(chan string, 1), 141 addressFactory: addressFactory, 142 storeFactory: storeFactory, 143 stopper: stopper.NewStopper("refresh-taskstorage", 144 stopper.WithLogger(rt.Logger().RawLogger())), 145 } 146 s.refresh(context.Background(), "") 147 if err := s.stopper.RunTask(s.refreshTask); err != nil { 148 panic(err) 149 } 150 return s 151 } 152 153 func (s *refreshableTaskStorage) Close() error { 154 defer s.rt.Logger().LogAction("close refreshable-storage", 155 log.DefaultLogOptions().WithLevel(zap.DebugLevel))() 156 157 var err error 158 s.mu.Lock() 159 if s.mu.closed { 160 s.mu.Unlock() 161 return nil 162 } 163 s.mu.closed = true 164 if s.mu.store != nil { 165 err = s.mu.store.Close() 166 } 167 s.mu.Unlock() 168 s.stopper.Stop() 169 close(s.refreshC) 170 return err 171 } 172 173 func (s *refreshableTaskStorage) AddAsyncTask(ctx context.Context, tasks ...task.AsyncTask) (int, error) { 174 var v int 175 var err error 176 s.mu.RLock() 177 lastAddress := s.mu.lastAddress 178 if s.mu.store == nil { 179 err = ErrNotReady 180 } else { 181 v, err = s.mu.store.AddAsyncTask(ctx, tasks...) 182 } 183 s.mu.RUnlock() 184 if err != nil { 185 s.maybeRefresh(lastAddress) 186 } 187 return v, err 188 } 189 190 func (s *refreshableTaskStorage) UpdateAsyncTask(ctx context.Context, tasks []task.AsyncTask, conditions ...Condition) (int, error) { 191 var v int 192 var err error 193 s.mu.RLock() 194 lastAddress := s.mu.lastAddress 195 if s.mu.store == nil { 196 err = ErrNotReady 197 } else { 198 v, err = s.mu.store.UpdateAsyncTask(ctx, tasks, conditions...) 199 } 200 s.mu.RUnlock() 201 if err != nil { 202 s.maybeRefresh(lastAddress) 203 } 204 return v, err 205 } 206 207 func (s *refreshableTaskStorage) DeleteAsyncTask(ctx context.Context, conditions ...Condition) (int, error) { 208 var v int 209 var err error 210 s.mu.RLock() 211 lastAddress := s.mu.lastAddress 212 if s.mu.store == nil { 213 err = ErrNotReady 214 } else { 215 v, err = s.mu.store.DeleteAsyncTask(ctx, conditions...) 216 } 217 s.mu.RUnlock() 218 if err != nil { 219 s.maybeRefresh(lastAddress) 220 } 221 return v, err 222 } 223 224 func (s *refreshableTaskStorage) QueryAsyncTask(ctx context.Context, conditions ...Condition) ([]task.AsyncTask, error) { 225 var v []task.AsyncTask 226 var err error 227 s.mu.RLock() 228 lastAddress := s.mu.lastAddress 229 if s.mu.store == nil { 230 err = ErrNotReady 231 } else { 232 v, err = s.mu.store.QueryAsyncTask(ctx, conditions...) 233 } 234 s.mu.RUnlock() 235 if err != nil { 236 s.maybeRefresh(lastAddress) 237 } 238 return v, err 239 } 240 241 func (s *refreshableTaskStorage) AddCronTask(ctx context.Context, tasks ...task.CronTask) (int, error) { 242 var v int 243 var err error 244 s.mu.RLock() 245 lastAddress := s.mu.lastAddress 246 if s.mu.store == nil { 247 err = ErrNotReady 248 } else { 249 v, err = s.mu.store.AddCronTask(ctx, tasks...) 250 } 251 s.mu.RUnlock() 252 if err != nil { 253 s.maybeRefresh(lastAddress) 254 } 255 return v, err 256 } 257 258 func (s *refreshableTaskStorage) QueryCronTask(ctx context.Context, c ...Condition) ([]task.CronTask, error) { 259 var v []task.CronTask 260 var err error 261 s.mu.RLock() 262 lastAddress := s.mu.lastAddress 263 if s.mu.store == nil { 264 err = ErrNotReady 265 } else { 266 v, err = s.mu.store.QueryCronTask(ctx, c...) 267 } 268 s.mu.RUnlock() 269 if err != nil { 270 s.maybeRefresh(lastAddress) 271 } 272 return v, err 273 } 274 275 func (s *refreshableTaskStorage) UpdateCronTask(ctx context.Context, cronTask task.CronTask, task task.AsyncTask) (int, error) { 276 var v int 277 var err error 278 s.mu.RLock() 279 lastAddress := s.mu.lastAddress 280 if s.mu.store == nil { 281 err = ErrNotReady 282 } else { 283 v, err = s.mu.store.UpdateCronTask(ctx, cronTask, task) 284 } 285 s.mu.RUnlock() 286 if err != nil { 287 s.maybeRefresh(lastAddress) 288 } 289 return v, err 290 } 291 292 func (s *refreshableTaskStorage) AddDaemonTask(ctx context.Context, tasks ...task.DaemonTask) (int, error) { 293 var v int 294 var err error 295 s.mu.RLock() 296 lastAddress := s.mu.lastAddress 297 if s.mu.store == nil { 298 err = ErrNotReady 299 } else { 300 v, err = s.mu.store.AddDaemonTask(ctx, tasks...) 301 } 302 s.mu.RUnlock() 303 if err != nil { 304 s.maybeRefresh(lastAddress) 305 } 306 return v, err 307 } 308 309 func (s *refreshableTaskStorage) UpdateDaemonTask(ctx context.Context, tasks []task.DaemonTask, conditions ...Condition) (int, error) { 310 var v int 311 var err error 312 s.mu.RLock() 313 lastAddress := s.mu.lastAddress 314 if s.mu.store == nil { 315 err = ErrNotReady 316 } else { 317 v, err = s.mu.store.UpdateDaemonTask(ctx, tasks, conditions...) 318 } 319 s.mu.RUnlock() 320 if err != nil { 321 s.maybeRefresh(lastAddress) 322 } 323 return v, err 324 } 325 326 func (s *refreshableTaskStorage) DeleteDaemonTask(ctx context.Context, conditions ...Condition) (int, error) { 327 var v int 328 var err error 329 s.mu.RLock() 330 lastAddress := s.mu.lastAddress 331 if s.mu.store == nil { 332 err = ErrNotReady 333 } else { 334 v, err = s.mu.store.DeleteDaemonTask(ctx, conditions...) 335 } 336 s.mu.RUnlock() 337 if err != nil { 338 s.maybeRefresh(lastAddress) 339 } 340 return v, err 341 } 342 343 func (s *refreshableTaskStorage) QueryDaemonTask(ctx context.Context, conditions ...Condition) ([]task.DaemonTask, error) { 344 var v []task.DaemonTask 345 var err error 346 s.mu.RLock() 347 lastAddress := s.mu.lastAddress 348 if s.mu.store == nil { 349 err = ErrNotReady 350 } else { 351 v, err = s.mu.store.QueryDaemonTask(ctx, conditions...) 352 } 353 s.mu.RUnlock() 354 if err != nil { 355 s.maybeRefresh(lastAddress) 356 } 357 return v, err 358 } 359 360 func (s *refreshableTaskStorage) HeartbeatDaemonTask(ctx context.Context, tasks []task.DaemonTask) (int, error) { 361 var v int 362 var err error 363 s.mu.RLock() 364 lastAddress := s.mu.lastAddress 365 if s.mu.store == nil { 366 err = ErrNotReady 367 } else { 368 v, err = s.mu.store.HeartbeatDaemonTask(ctx, tasks) 369 } 370 s.mu.RUnlock() 371 if err != nil { 372 s.maybeRefresh(lastAddress) 373 } 374 return v, err 375 } 376 377 func (s *refreshableTaskStorage) maybeRefresh(lastAddress string) bool { 378 s.mu.Lock() 379 defer s.mu.Unlock() 380 if s.mu.closed { 381 return false 382 } 383 384 select { 385 case s.refreshC <- lastAddress: 386 return true 387 default: 388 return false 389 } 390 } 391 392 func (s *refreshableTaskStorage) refreshTask(ctx context.Context) { 393 defer s.rt.Logger().LogAction("close refresh-task", 394 log.DefaultLogOptions().WithLevel(zap.DebugLevel))() 395 396 for { 397 select { 398 case <-ctx.Done(): 399 return 400 case lastAddress := <-s.refreshC: 401 s.refresh(ctx, lastAddress) 402 // see pkg/logservice/service_commands.go#132 403 select { 404 case <-ctx.Done(): 405 return 406 default: 407 } 408 } 409 } 410 } 411 412 func (s *refreshableTaskStorage) refresh(ctx context.Context, lastAddress string) { 413 s.mu.Lock() 414 defer s.mu.Unlock() 415 416 if s.mu.store != nil { 417 _ = s.mu.store.Close() 418 } 419 420 if s.mu.closed { 421 return 422 } 423 if lastAddress != "" && lastAddress != s.mu.lastAddress { 424 return 425 } 426 connectAddress, err := s.addressFactory(ctx, true) 427 if err != nil { 428 s.rt.Logger().Error("failed to refresh task storage", 429 zap.Error(err)) 430 return 431 } 432 433 s.mu.lastAddress = connectAddress 434 s.rt.Logger().Debug("trying to refresh task storage", zap.String("address", connectAddress)) 435 store, err := s.storeFactory.Create(connectAddress) 436 if err != nil { 437 s.rt.Logger().Error("failed to refresh task storage", 438 zap.String("address", connectAddress), 439 zap.Error(err)) 440 return 441 } 442 s.mu.store = store 443 s.rt.Logger().Debug("refresh task storage completed", zap.String("sql-address", connectAddress)) 444 } 445 446 type mysqlBasedStorageFactory struct { 447 username string 448 password string 449 database string 450 } 451 452 // NewMySQLBasedTaskStorageFactory creates a mysql based task storage factory using the special username, password and database 453 func NewMySQLBasedTaskStorageFactory(username, password, database string) TaskStorageFactory { 454 return &mysqlBasedStorageFactory{ 455 username: username, 456 password: password, 457 database: database, 458 } 459 } 460 461 func (f *mysqlBasedStorageFactory) Create(address string) (TaskStorage, error) { 462 dsn := fmt.Sprintf("%s:%s@tcp(%s)/?readTimeout=15s&writeTimeout=15s&timeout=15s&parseTime=true&loc=Local", 463 f.username, 464 f.password, 465 address) 466 return NewMysqlTaskStorage(dsn, f.database) 467 } 468 469 type fixedTaskStorageFactory struct { 470 store TaskStorage 471 } 472 473 // NewFixedTaskStorageFactory creates a fixed task storage factory which always returns the special taskstorage 474 func NewFixedTaskStorageFactory(store TaskStorage) TaskStorageFactory { 475 return &fixedTaskStorageFactory{ 476 store: store, 477 } 478 } 479 480 func (f *fixedTaskStorageFactory) Create(address string) (TaskStorage, error) { 481 return f.store, nil 482 }