github.com/ethersphere/bee/v2@v2.2.0/pkg/storer/uploadstore.go (about)

     1  // Copyright 2023 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package storer
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"sort"
    12  
    13  	storage "github.com/ethersphere/bee/v2/pkg/storage"
    14  	"github.com/ethersphere/bee/v2/pkg/storer/internal"
    15  	pinstore "github.com/ethersphere/bee/v2/pkg/storer/internal/pinning"
    16  	"github.com/ethersphere/bee/v2/pkg/storer/internal/transaction"
    17  	"github.com/ethersphere/bee/v2/pkg/storer/internal/upload"
    18  	"github.com/ethersphere/bee/v2/pkg/swarm"
    19  )
    20  
    21  const uploadsLock = "pin-upload-store"
    22  
    23  // Report implements the storage.PushReporter by wrapping the internal reporter
    24  // with a transaction.
    25  func (db *DB) Report(ctx context.Context, chunk swarm.Chunk, state storage.ChunkState) error {
    26  
    27  	unlock := db.Lock(uploadsLock)
    28  	defer unlock()
    29  
    30  	err := db.storage.Run(ctx, func(s transaction.Store) error {
    31  		return upload.Report(ctx, s, chunk, state)
    32  	})
    33  	if err != nil {
    34  		return fmt.Errorf("reporter.Report: %w", err)
    35  	}
    36  
    37  	return nil
    38  }
    39  
    40  // Upload is the implementation of UploadStore.Upload method.
    41  func (db *DB) Upload(ctx context.Context, pin bool, tagID uint64) (PutterSession, error) {
    42  	if tagID == 0 {
    43  		return nil, fmt.Errorf("storer: tagID required")
    44  	}
    45  
    46  	var (
    47  		uploadPutter  internal.PutterCloserWithReference
    48  		pinningPutter internal.PutterCloserWithReference
    49  		err           error
    50  	)
    51  
    52  	err = db.storage.Run(ctx, func(s transaction.Store) error {
    53  		uploadPutter, err = upload.NewPutter(s.IndexStore(), tagID)
    54  		if err != nil {
    55  			return fmt.Errorf("upload.NewPutter: %w", err)
    56  		}
    57  
    58  		if pin {
    59  			pinningPutter, err = pinstore.NewCollection(s.IndexStore())
    60  			if err != nil {
    61  				return fmt.Errorf("pinstore.NewCollection: %w", err)
    62  			}
    63  		}
    64  		return nil
    65  	})
    66  
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	return &putterSession{
    72  		Putter: putterWithMetrics{
    73  			storage.PutterFunc(func(ctx context.Context, chunk swarm.Chunk) error {
    74  				unlock := db.Lock(uploadsLock)
    75  				defer unlock()
    76  				return errors.Join(
    77  					db.storage.Run(ctx, func(s transaction.Store) error {
    78  						return uploadPutter.Put(ctx, s, chunk)
    79  					}),
    80  					func() error {
    81  						if pinningPutter != nil {
    82  							return db.storage.Run(ctx, func(s transaction.Store) error {
    83  								return pinningPutter.Put(ctx, s, chunk)
    84  							})
    85  						}
    86  						return nil
    87  					}(),
    88  				)
    89  			}),
    90  			db.metrics,
    91  			"uploadstore",
    92  		},
    93  		done: func(address swarm.Address) error {
    94  			defer db.events.Trigger(subscribePushEventKey)
    95  			unlock := db.Lock(uploadsLock)
    96  			defer unlock()
    97  
    98  			return errors.Join(
    99  				db.storage.Run(ctx, func(s transaction.Store) error {
   100  					return uploadPutter.Close(s.IndexStore(), address)
   101  				}),
   102  				func() error {
   103  					if pinningPutter != nil {
   104  						return db.storage.Run(ctx, func(s transaction.Store) error {
   105  							pinErr := pinningPutter.Close(s.IndexStore(), address)
   106  							if errors.Is(pinErr, pinstore.ErrDuplicatePinCollection) {
   107  								pinErr = pinningPutter.Cleanup(db.storage)
   108  							}
   109  							return pinErr
   110  						})
   111  					}
   112  					return nil
   113  				}(),
   114  			)
   115  		},
   116  		cleanup: func() error {
   117  			defer db.events.Trigger(subscribePushEventKey)
   118  			unlock := db.Lock(uploadsLock)
   119  			defer unlock()
   120  			return errors.Join(
   121  				uploadPutter.Cleanup(db.storage),
   122  				func() error {
   123  					if pinningPutter != nil {
   124  						return pinningPutter.Cleanup(db.storage)
   125  					}
   126  					return nil
   127  				}(),
   128  			)
   129  		},
   130  	}, nil
   131  }
   132  
   133  // NewSession is the implementation of UploadStore.NewSession method.
   134  func (db *DB) NewSession() (SessionInfo, error) {
   135  	unlock := db.Lock(lockKeyNewSession)
   136  	defer unlock()
   137  
   138  	trx, done := db.storage.NewTransaction(context.Background())
   139  	defer done()
   140  
   141  	info, err := upload.NextTag(trx.IndexStore())
   142  	if err != nil {
   143  		return SessionInfo{}, err
   144  	}
   145  	return info, trx.Commit()
   146  }
   147  
   148  // Session is the implementation of the UploadStore.Session method.
   149  func (db *DB) Session(tagID uint64) (SessionInfo, error) {
   150  	return upload.TagInfo(db.storage.IndexStore(), tagID)
   151  }
   152  
   153  // DeleteSession is the implementation of the UploadStore.DeleteSession method.
   154  func (db *DB) DeleteSession(tagID uint64) error {
   155  	return db.storage.Run(context.Background(), func(s transaction.Store) error {
   156  		return upload.DeleteTag(s.IndexStore(), tagID)
   157  	})
   158  }
   159  
   160  // ListSessions is the implementation of the UploadStore.ListSessions method.
   161  func (db *DB) ListSessions(offset, limit int) ([]SessionInfo, error) {
   162  	const maxPageSize = 1000
   163  
   164  	limit = min(limit, maxPageSize)
   165  
   166  	tags, err := upload.ListAllTags(db.storage.IndexStore())
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  
   171  	sort.Slice(tags, func(i, j int) bool {
   172  		return tags[i].TagID < tags[j].TagID
   173  	})
   174  
   175  	return tags[min(offset, len(tags)):min(offset+limit, len(tags))], nil
   176  }