| 
					
				 | 
			
			
				@@ -6,7 +6,6 @@ import ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	"time" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	"golib/v4/features/mo" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	"golib/v4/gio" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	"golib/v4/infra/ii" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	"golib/v4/log" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -74,7 +73,9 @@ func (s *Service) FindAll(name ii.Name) ([]Row, error) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	opts := mo.Options.Find() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	opts.SetSort(mo.NewSorter(ii.CreationTime, mo.SortDESC)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	cursor, err := s.openColl(info).Find(gio.ContextTimeout(s.Timeout), mo.D{}, opts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ctx, cancel := s.newContext() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	cursor, err := s.openColl(info).Find(ctx, mo.D{}, opts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.FindAll.%s: %s", name, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return nil, errors.Join(ErrInternalError, err) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -124,7 +125,9 @@ func (s *Service) FindWith(name ii.Name, filter, sort, project mo.Filter, skip, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			opts.SetLimit(findLimitRows) // 如果没有过滤条件, 限制返回数量 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	cursor, err := s.openColl(info).Find(s.newContext(), query, opts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ctx, cancel := s.newContext() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	cursor, err := s.openColl(info).Find(ctx, query, opts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.Find.%s: %s filter: %v", name, err, query) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return errors.Join(ErrInternalError, err) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -160,7 +163,9 @@ func (s *Service) FindOneWith(name ii.Name, filter, sort, project mo.Filter, v a 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		opts.SetSort(mo.NewSorter(ii.CreationTime, mo.SortDESC).Done()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	cursor := s.openColl(info).FindOne(s.newContext(), query, opts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ctx, cancel := s.newContext() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	cursor := s.openColl(info).FindOne(ctx, query, opts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if err := cursor.Err(); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		if errors.Is(err, mo.ErrNoDocuments) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			return err 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -187,7 +192,9 @@ func (s *Service) FindOneAndDelete(name ii.Name, filter mo.Filter) error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.FindOneAndDelete.%s: QueryFilterCheck: %s filter: %v", name, err, query) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return errors.Join(ErrDataError, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	result := s.openColl(info).FindOneAndDelete(s.newContext(), query) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ctx, cancel := s.newContext() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	result := s.openColl(info).FindOneAndDelete(ctx, query) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if err := result.Err(); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		if IsErrNoDocuments(err) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			return err 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -208,7 +215,9 @@ func (s *Service) DeleteOne(name ii.Name, filter mo.Filter) error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return ErrItemNotfound 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	query := filter.Done() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	result, err := s.openColl(info).DeleteOne(s.newContext(), query) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ctx, cancel := s.newContext() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	result, err := s.openColl(info).DeleteOne(ctx, query) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.DeleteOne.%s: %s filter: %v", name, err, query) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return errors.Join(ErrInternalError, err) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -226,7 +235,9 @@ func (s *Service) DeleteMany(name ii.Name, filter mo.Filter) error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return ErrItemNotfound 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	query := filter.Done() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	result, err := s.openColl(info).DeleteMany(s.newContext(), query) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ctx, cancel := s.newContext() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	result, err := s.openColl(info).DeleteMany(ctx, query) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.DeleteMany.%s: %s filter: %v", name, err, query) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return errors.Join(ErrInternalError, err) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -250,7 +261,9 @@ func (s *Service) FindOneAndUpdate(name ii.Name, filter, updater mo.Filter) erro 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return errors.Join(ErrDataError, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	update := updater.Done() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	result := s.openColl(info).FindOneAndUpdate(s.newContext(), query, update) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ctx, cancel := s.newContext() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	result := s.openColl(info).FindOneAndUpdate(ctx, query, update) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if err := result.Err(); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		if errors.Is(err, mo.ErrNoDocuments) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			return err 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -271,7 +284,9 @@ func (s *Service) EstimatedDocumentCount(name ii.Name) (int64, error) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.EstimatedDocumentCount.%s: item not found", name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return 0, ErrItemNotfound 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	length, err := s.openColl(info).EstimatedDocumentCount(s.newContext()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ctx, cancel := s.newContext() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	length, err := s.openColl(info).EstimatedDocumentCount(ctx) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.EstimatedDocumentCount.%s: %s", name, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return 0, errors.Join(ErrInternalError, err) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -291,7 +306,9 @@ func (s *Service) CountDocuments(name ii.Name, filter mo.Filter) (int64, error) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.CountDocuments.%s: QueryFilterCheck: %s filter: %v", name, err, query) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return 0, errors.Join(ErrDataError, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	length, err := s.openColl(info).CountDocuments(s.newContext(), query) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ctx, cancel := s.newContext() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	length, err := s.openColl(info).CountDocuments(ctx, query) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.CountDocuments.%s: %s filter: %v", name, err, query) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return 0, errors.Join(ErrInternalError, err) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -317,7 +334,9 @@ func (s *Service) InsertOne(name ii.Name, value any) (mo.ObjectID, error) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.InsertOne.%s: PrepareInsert: %s", name, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return mo.NilObjectID, errors.Join(ErrDataError, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	result, err := s.openColl(info).InsertOne(s.newContext(), doc) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ctx, cancel := s.newContext() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	result, err := s.openColl(info).InsertOne(ctx, doc) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.InsertOne.%s: %s data: %v", name, err, doc) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return mo.NilObjectID, errors.Join(ErrInternalError, err) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -348,7 +367,9 @@ func (s *Service) InsertMany(name ii.Name, value any) (mo.A, error) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.InsertMany.%s: toMaps: %s", name, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return nil, errors.Join(ErrDataError, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	result, err := s.openColl(info).InsertMany(s.newContext(), docs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ctx, cancel := s.newContext() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	result, err := s.openColl(info).InsertMany(ctx, docs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.InsertMany.%s: %s", name, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return nil, errors.Join(ErrInternalError, err) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -384,7 +405,9 @@ func (s *Service) UpdateOne(name ii.Name, filter, updater mo.Filter) error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	_, upsert := mo.HasOptIn(update, mo.OptSetOnInsert) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	opts.SetUpsert(upsert) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	_, err := s.openColl(info).UpdateOne(s.newContext(), query, update, opts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ctx, cancel := s.newContext() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	_, err := s.openColl(info).UpdateOne(ctx, query, update, opts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.UpdateOne.%s: %s filter: %v updater: %v", name, err, filter, update) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return errors.Join(ErrInternalError, err) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -425,7 +448,9 @@ func (s *Service) UpdateMany(name ii.Name, filter, updater mo.Filter) error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	_, upsert := mo.HasOptIn(update, mo.OptSetOnInsert) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	opts.SetUpsert(upsert) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	result, err := s.openColl(info).UpdateMany(s.newContext(), query, update, opts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ctx, cancel := s.newContext() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	result, err := s.openColl(info).UpdateMany(ctx, query, update, opts) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.UpdateMany.%s: %s filter: %v updater: %v", name, err, query, update) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return errors.Join(ErrInternalError, err) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -465,7 +490,9 @@ func (s *Service) Aggregate(name ii.Name, pipe mo.Pipeline, v any) error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		stage, lookup = s.Cache.SpitPipe(info, pipe) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	cursor, err := s.openColl(info).Aggregate(s.newContext(), stage) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ctx, cancel := s.newContext() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	cursor, err := s.openColl(info).Aggregate(ctx, stage) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		s.Log.Error("svc.Aggregate.%s: %s pipe: %v", name, err, pipe) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return errors.Join(ErrInternalError, err) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -514,12 +541,15 @@ func (s *Service) refreshCache(info *ii.ItemInfo) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 func (s *Service) handleRefresh() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	for info := range s.refreshCh { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		qt := time.Now() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		cursor, err := s.openColl(info).Find(s.newContext(), mo.D{}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		ctx, cancel := s.newContext() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		cursor, err := s.openColl(info).Find(ctx, mo.D{}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			s.Cache.Clear(info.Name) // 查询失败时则清除内存缓存, 防止信息不一致 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			s.Log.Error("svc.refreshCache: %s->%s", info.Name, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			continue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		qts := time.Now().Sub(qt) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		dt := time.Now() 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -545,14 +575,15 @@ func (s *Service) openColl(info *ii.ItemInfo) *mo.Collection { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	return s.Client.Database(info.Name.Database()).Collection(info.Name.Collection()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *Service) newContext() context.Context { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	root := gio.ContextTimeout(s.Timeout) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (s *Service) newContext() (context.Context, context.CancelFunc) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	root, cancel := context.WithTimeout(context.Background(), s.Timeout) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	go func(s *Service) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		d := time.Duration(s.Timeout.Seconds()*0.8) * time.Second 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		ctx, cancel := context.WithTimeout(root, d) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		defer cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		<-ctx.Done() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		s.Log.Warn("svc.HealthCheck: Warning: Exec has been running for %f seconds.", d.Seconds()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		select { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		case <-time.After(d): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			s.Log.Warn("svc.HealthCheck: Warning: Exec has been running for %f seconds.", d.Seconds()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		case <-root.Done(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	}(s) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	return root 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return root, cancel 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 |