| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | package archiver | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/restic/restic/internal/debug" | 
					
						
							|  |  |  | 	"github.com/restic/restic/internal/restic" | 
					
						
							| 
									
										
										
										
											2022-05-27 19:08:50 +02:00
										 |  |  | 	"golang.org/x/sync/errgroup" | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // FutureTree is returned by Save and will return the data once it | 
					
						
							|  |  |  | // has been processed. | 
					
						
							|  |  |  | type FutureTree struct { | 
					
						
							|  |  |  | 	ch  <-chan saveTreeResponse | 
					
						
							|  |  |  | 	res saveTreeResponse | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-12 21:40:31 +02:00
										 |  |  | // Wait blocks until the data has been received or ctx is cancelled. | 
					
						
							|  |  |  | func (s *FutureTree) Wait(ctx context.Context) { | 
					
						
							|  |  |  | 	select { | 
					
						
							|  |  |  | 	case <-ctx.Done(): | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	case res, ok := <-s.ch: | 
					
						
							|  |  |  | 		if ok { | 
					
						
							|  |  |  | 			s.res = res | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-12 21:40:31 +02:00
										 |  |  | // Node returns the node. | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | func (s *FutureTree) Node() *restic.Node { | 
					
						
							|  |  |  | 	return s.res.node | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-12 21:40:31 +02:00
										 |  |  | // Stats returns the stats for the file. | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | func (s *FutureTree) Stats() ItemStats { | 
					
						
							|  |  |  | 	return s.res.stats | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TreeSaver concurrently saves incoming trees to the repo. | 
					
						
							|  |  |  | type TreeSaver struct { | 
					
						
							|  |  |  | 	saveTree func(context.Context, *restic.Tree) (restic.ID, ItemStats, error) | 
					
						
							|  |  |  | 	errFn    ErrorFunc | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:20:59 +02:00
										 |  |  | 	ch chan<- saveTreeJob | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewTreeSaver returns a new tree saver. A worker pool with treeWorkers is | 
					
						
							|  |  |  | // started, it is stopped when ctx is cancelled. | 
					
						
							| 
									
										
										
										
											2022-05-27 19:08:50 +02:00
										 |  |  | func NewTreeSaver(ctx context.Context, wg *errgroup.Group, treeWorkers uint, saveTree func(context.Context, *restic.Tree) (restic.ID, ItemStats, error), errFn ErrorFunc) *TreeSaver { | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 	ch := make(chan saveTreeJob) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s := &TreeSaver{ | 
					
						
							|  |  |  | 		ch:       ch, | 
					
						
							|  |  |  | 		saveTree: saveTree, | 
					
						
							|  |  |  | 		errFn:    errFn, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := uint(0); i < treeWorkers; i++ { | 
					
						
							| 
									
										
										
										
											2022-05-27 19:08:50 +02:00
										 |  |  | 		wg.Go(func() error { | 
					
						
							|  |  |  | 			return s.worker(ctx, ch) | 
					
						
							| 
									
										
										
										
											2018-05-08 22:28:37 +02:00
										 |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return s | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-27 19:08:50 +02:00
										 |  |  | func (s *TreeSaver) TriggerShutdown() { | 
					
						
							|  |  |  | 	close(s.ch) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | // Save stores the dir d and returns the data once it has been completed. | 
					
						
							| 
									
										
										
										
											2020-04-22 22:23:02 +02:00
										 |  |  | func (s *TreeSaver) Save(ctx context.Context, snPath string, node *restic.Node, nodes []FutureNode, complete CompleteFunc) FutureTree { | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 	ch := make(chan saveTreeResponse, 1) | 
					
						
							| 
									
										
										
										
											2018-05-08 22:28:37 +02:00
										 |  |  | 	job := saveTreeJob{ | 
					
						
							| 
									
										
										
										
											2020-04-22 22:23:02 +02:00
										 |  |  | 		snPath:   snPath, | 
					
						
							|  |  |  | 		node:     node, | 
					
						
							|  |  |  | 		nodes:    nodes, | 
					
						
							|  |  |  | 		ch:       ch, | 
					
						
							|  |  |  | 		complete: complete, | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-05-08 22:28:37 +02:00
										 |  |  | 	select { | 
					
						
							|  |  |  | 	case s.ch <- job: | 
					
						
							|  |  |  | 	case <-ctx.Done(): | 
					
						
							| 
									
										
										
										
											2018-05-12 21:40:31 +02:00
										 |  |  | 		debug.Log("not saving tree, context is cancelled") | 
					
						
							| 
									
										
										
										
											2018-05-08 22:28:37 +02:00
										 |  |  | 		close(ch) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return FutureTree{ch: ch} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type saveTreeJob struct { | 
					
						
							| 
									
										
										
										
											2020-04-22 22:23:02 +02:00
										 |  |  | 	snPath   string | 
					
						
							|  |  |  | 	nodes    []FutureNode | 
					
						
							|  |  |  | 	node     *restic.Node | 
					
						
							|  |  |  | 	ch       chan<- saveTreeResponse | 
					
						
							|  |  |  | 	complete CompleteFunc | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type saveTreeResponse struct { | 
					
						
							|  |  |  | 	node  *restic.Node | 
					
						
							|  |  |  | 	stats ItemStats | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // save stores the nodes as a tree in the repo. | 
					
						
							|  |  |  | func (s *TreeSaver) save(ctx context.Context, snPath string, node *restic.Node, nodes []FutureNode) (*restic.Node, ItemStats, error) { | 
					
						
							|  |  |  | 	var stats ItemStats | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-26 17:18:42 +02:00
										 |  |  | 	tree := restic.NewTree(len(nodes)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 	for _, fn := range nodes { | 
					
						
							|  |  |  | 		fn.wait(ctx) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// return the error if it wasn't ignored | 
					
						
							|  |  |  | 		if fn.err != nil { | 
					
						
							| 
									
										
										
										
											2018-05-03 20:49:30 +02:00
										 |  |  | 			debug.Log("err for %v: %v", fn.snPath, fn.err) | 
					
						
							| 
									
										
										
										
											2022-05-21 00:31:26 +02:00
										 |  |  | 			fn.err = s.errFn(fn.target, fn.err) | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 			if fn.err == nil { | 
					
						
							|  |  |  | 				// ignore error | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return nil, stats, fn.err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// when the error is ignored, the node could not be saved, so ignore it | 
					
						
							|  |  |  | 		if fn.node == nil { | 
					
						
							|  |  |  | 			debug.Log("%v excluded: %v", fn.snPath, fn.target) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		debug.Log("insert %v", fn.node.Name) | 
					
						
							|  |  |  | 		err := tree.Insert(fn.node) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, stats, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	id, treeStats, err := s.saveTree(ctx, tree) | 
					
						
							|  |  |  | 	stats.Add(treeStats) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, stats, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	node.Subtree = &id | 
					
						
							|  |  |  | 	return node, stats, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-08 22:28:37 +02:00
										 |  |  | func (s *TreeSaver) worker(ctx context.Context, jobs <-chan saveTreeJob) error { | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 	for { | 
					
						
							|  |  |  | 		var job saveTreeJob | 
					
						
							| 
									
										
										
										
											2022-05-27 19:08:50 +02:00
										 |  |  | 		var ok bool | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 		select { | 
					
						
							|  |  |  | 		case <-ctx.Done(): | 
					
						
							| 
									
										
										
										
											2018-05-08 22:28:37 +02:00
										 |  |  | 			return nil | 
					
						
							| 
									
										
										
										
											2022-05-27 19:08:50 +02:00
										 |  |  | 		case job, ok = <-jobs: | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		node, stats, err := s.save(ctx, job.snPath, job.node, job.nodes) | 
					
						
							| 
									
										
										
										
											2018-05-08 22:28:37 +02:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			debug.Log("error saving tree blob: %v", err) | 
					
						
							| 
									
										
										
										
											2018-05-12 21:40:31 +02:00
										 |  |  | 			close(job.ch) | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2018-05-08 22:28:37 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-22 22:23:02 +02:00
										 |  |  | 		if job.complete != nil { | 
					
						
							|  |  |  | 			job.complete(node, stats) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 		job.ch <- saveTreeResponse{ | 
					
						
							|  |  |  | 			node:  node, | 
					
						
							|  |  |  | 			stats: stats, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		close(job.ch) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |