| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | package archiver | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2022-10-08 21:29:51 +02:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"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
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TreeSaver concurrently saves incoming trees to the repo. | 
					
						
							|  |  |  | type TreeSaver struct { | 
					
						
							| 
									
										
										
										
											2022-10-07 20:23:38 +02:00
										 |  |  | 	saveBlob func(ctx context.Context, t restic.BlobType, buf *Buffer, cb func(res SaveBlobResponse)) | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 	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-10-07 20:23:38 +02:00
										 |  |  | func NewTreeSaver(ctx context.Context, wg *errgroup.Group, treeWorkers uint, saveBlob func(ctx context.Context, t restic.BlobType, buf *Buffer, cb func(res SaveBlobResponse)), errFn ErrorFunc) *TreeSaver { | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 	ch := make(chan saveTreeJob) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s := &TreeSaver{ | 
					
						
							|  |  |  | 		ch:       ch, | 
					
						
							| 
									
										
										
										
											2022-08-19 23:08:13 +02:00
										 |  |  | 		saveBlob: saveBlob, | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 		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. | 
					
						
							| 
									
										
										
										
											2022-05-29 11:57:10 +02:00
										 |  |  | func (s *TreeSaver) Save(ctx context.Context, snPath string, target string, node *restic.Node, nodes []FutureNode, complete CompleteFunc) FutureNode { | 
					
						
							|  |  |  | 	fn, ch := newFutureNode() | 
					
						
							| 
									
										
										
										
											2018-05-08 22:28:37 +02:00
										 |  |  | 	job := saveTreeJob{ | 
					
						
							| 
									
										
										
										
											2020-04-22 22:23:02 +02:00
										 |  |  | 		snPath:   snPath, | 
					
						
							| 
									
										
										
										
											2022-05-29 11:57:10 +02:00
										 |  |  | 		target:   target, | 
					
						
							| 
									
										
										
										
											2020-04-22 22:23:02 +02:00
										 |  |  | 		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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-29 11:57:10 +02:00
										 |  |  | 	return fn | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type saveTreeJob struct { | 
					
						
							| 
									
										
										
										
											2020-04-22 22:23:02 +02:00
										 |  |  | 	snPath   string | 
					
						
							| 
									
										
										
										
											2022-05-29 11:57:10 +02:00
										 |  |  | 	target   string | 
					
						
							| 
									
										
										
										
											2020-04-22 22:23:02 +02:00
										 |  |  | 	node     *restic.Node | 
					
						
							| 
									
										
										
										
											2022-05-29 11:57:10 +02:00
										 |  |  | 	nodes    []FutureNode | 
					
						
							|  |  |  | 	ch       chan<- futureNodeResult | 
					
						
							| 
									
										
										
										
											2020-04-22 22:23:02 +02:00
										 |  |  | 	complete CompleteFunc | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // save stores the nodes as a tree in the repo. | 
					
						
							| 
									
										
										
										
											2022-05-29 11:57:10 +02:00
										 |  |  | func (s *TreeSaver) save(ctx context.Context, job *saveTreeJob) (*restic.Node, ItemStats, error) { | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 	var stats ItemStats | 
					
						
							| 
									
										
										
										
											2022-05-29 11:57:10 +02:00
										 |  |  | 	node := job.node | 
					
						
							|  |  |  | 	nodes := job.nodes | 
					
						
							|  |  |  | 	// allow GC of nodes array once the loop is finished | 
					
						
							|  |  |  | 	job.nodes = nil | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-21 13:33:08 +02:00
										 |  |  | 	builder := restic.NewTreeJSONBuilder() | 
					
						
							| 
									
										
										
										
											2022-10-08 21:29:51 +02:00
										 |  |  | 	var lastNode *restic.Node | 
					
						
							| 
									
										
										
										
											2021-09-26 17:18:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-29 11:57:10 +02:00
										 |  |  | 	for i, fn := range nodes { | 
					
						
							|  |  |  | 		// fn is a copy, so clear the original value explicitly | 
					
						
							|  |  |  | 		nodes[i] = FutureNode{} | 
					
						
							|  |  |  | 		fnr := fn.take(ctx) | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// return the error if it wasn't ignored | 
					
						
							| 
									
										
										
										
											2022-05-29 11:57:10 +02:00
										 |  |  | 		if fnr.err != nil { | 
					
						
							|  |  |  | 			debug.Log("err for %v: %v", fnr.snPath, fnr.err) | 
					
						
							|  |  |  | 			fnr.err = s.errFn(fnr.target, fnr.err) | 
					
						
							|  |  |  | 			if fnr.err == nil { | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 				// ignore error | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-29 11:57:10 +02:00
										 |  |  | 			return nil, stats, fnr.err | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// when the error is ignored, the node could not be saved, so ignore it | 
					
						
							| 
									
										
										
										
											2022-05-29 11:57:10 +02:00
										 |  |  | 		if fnr.node == nil { | 
					
						
							|  |  |  | 			debug.Log("%v excluded: %v", fnr.snPath, fnr.target) | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-21 13:33:08 +02:00
										 |  |  | 		err := builder.AddNode(fnr.node) | 
					
						
							| 
									
										
										
										
											2022-10-08 21:29:51 +02:00
										 |  |  | 		if err != nil && errors.Is(err, restic.ErrTreeNotOrdered) && lastNode != nil && fnr.node.Equals(*lastNode) { | 
					
						
							| 
									
										
										
										
											2023-04-23 11:38:06 +02:00
										 |  |  | 			debug.Log("insert %v failed: %v", fnr.node.Name, err) | 
					
						
							| 
									
										
										
										
											2022-10-08 21:29:51 +02:00
										 |  |  | 			// ignore error if an _identical_ node already exists, but nevertheless issue a warning | 
					
						
							|  |  |  | 			_ = s.errFn(fnr.target, err) | 
					
						
							|  |  |  | 			err = nil | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2023-04-23 11:38:06 +02:00
										 |  |  | 			debug.Log("insert %v failed: %v", fnr.node.Name, err) | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 			return nil, stats, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-10-08 21:29:51 +02:00
										 |  |  | 		lastNode = fnr.node | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-19 23:08:13 +02:00
										 |  |  | 	buf, err := builder.Finalize() | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, stats, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-19 23:08:13 +02:00
										 |  |  | 	b := &Buffer{Data: buf} | 
					
						
							| 
									
										
										
										
											2022-10-07 20:23:38 +02:00
										 |  |  | 	ch := make(chan SaveBlobResponse, 1) | 
					
						
							|  |  |  | 	s.saveBlob(ctx, restic.TreeBlob, b, func(res SaveBlobResponse) { | 
					
						
							|  |  |  | 		ch <- res | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-08-19 23:08:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-07 20:23:38 +02:00
										 |  |  | 	select { | 
					
						
							|  |  |  | 	case sbr := <-ch: | 
					
						
							|  |  |  | 		if !sbr.known { | 
					
						
							|  |  |  | 			stats.TreeBlobs++ | 
					
						
							|  |  |  | 			stats.TreeSize += uint64(sbr.length) | 
					
						
							|  |  |  | 			stats.TreeSizeInRepo += uint64(sbr.sizeInRepo) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		node.Subtree = &sbr.id | 
					
						
							|  |  |  | 		return node, stats, nil | 
					
						
							|  |  |  | 	case <-ctx.Done(): | 
					
						
							| 
									
										
										
										
											2022-08-19 23:08:13 +02:00
										 |  |  | 		return nil, stats, ctx.Err() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-05-29 11:57:10 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		node, stats, err := s.save(ctx, &job) | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-05-29 11:57:10 +02:00
										 |  |  | 		job.ch <- futureNodeResult{ | 
					
						
							|  |  |  | 			snPath: job.snPath, | 
					
						
							|  |  |  | 			target: job.target, | 
					
						
							|  |  |  | 			node:   node, | 
					
						
							|  |  |  | 			stats:  stats, | 
					
						
							| 
									
										
										
										
											2018-04-30 15:13:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		close(job.ch) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |