Skip to content

Commit 1a881b8

Browse files
authored
Merge pull request #1476 from 0chain/feat/storage-v2
Storage V2
2 parents 3dc0b63 + ea3eeb4 commit 1a881b8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2519
-381
lines changed

.github/workflows/tests.yml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ jobs:
1414
name: Lints
1515
runs-on: [self-hosted,blobber-runner]
1616
steps:
17-
- name: Setup go
18-
uses: actions/setup-go@v3
19-
with:
20-
go-version: ^1.20 # The Go version to download (if necessary) and use.
17+
# - name: Setup go
18+
# uses: actions/setup-go@v3
19+
# with:
20+
# go-version: ^1.22 # The Go version to download (if necessary) and use.
2121

2222
- name: Clone blobber
2323
uses: actions/checkout@v2
@@ -33,7 +33,9 @@ jobs:
3333
run: make local
3434

3535
- name: Build Base
36-
run: ./docker.local/bin/build.base.sh
36+
run: |
37+
./docker.local/bin/build.base.sh
38+
golangci-lint cache clean
3739
3840
- name: Golangci-lint
3941
uses: golangci/golangci-lint-action@v3
@@ -52,7 +54,7 @@ jobs:
5254
- name: Setup go
5355
uses: actions/setup-go@v3
5456
with:
55-
go-version: ^1.20 # The Go version to download (if necessary) and use.
57+
go-version: ^1.22 # The Go version to download (if necessary) and use.
5658

5759
- name: Clone blobber
5860
uses: actions/checkout@v2

code/go/0chain.net/blobber/datastore.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ func setupDatabase() error {
3737
if err := migrateDatabase(pgDB); err != nil {
3838
return fmt.Errorf("error while migrating schema: %v", err)
3939
}
40+
if err := datastore.OpenBlockStore(); err != nil {
41+
return fmt.Errorf("error while opening block store: %v", err)
42+
}
4043

4144
return nil
4245
}

code/go/0chain.net/blobber/http.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func startHttpServer() {
3838

3939
logging.Logger.Info("Ready to listen to the requests with development mode: " + mode)
4040
fmt.Print("> start http server [OK]\n")
41-
41+
handler.HandleShutdown(common.GetRootContext())
4242
wg.Wait()
4343
}
4444

@@ -92,16 +92,21 @@ func startServer(wg *sync.WaitGroup, r *mux.Router, mode string, port int, isTls
9292
Handler: r,
9393
}
9494
}
95+
go func() {
96+
if isTls {
97+
err := server.ListenAndServeTLS(httpsCertFile, httpsKeyFile)
98+
if err != nil && err != http.ErrServerClosed {
99+
logging.Logger.Fatal("blobber failed", zap.Error(err))
100+
}
101+
} else {
102+
err := server.ListenAndServe()
103+
if err != nil && err != http.ErrServerClosed {
104+
logging.Logger.Fatal("blobber failed", zap.Error(err))
105+
}
106+
}
107+
}()
108+
// this is blocking call, will wait for the interrupt or quit signal
95109
common.HandleShutdown(server)
96-
handler.HandleShutdown(common.GetRootContext())
97-
98-
if isTls {
99-
err := server.ListenAndServeTLS(httpsCertFile, httpsKeyFile)
100-
logging.Logger.Fatal("validator failed", zap.Error(err))
101-
} else {
102-
err := server.ListenAndServe()
103-
logging.Logger.Fatal("validator failed", zap.Error(err))
104-
}
105110
}
106111

107112
func initHandlers(r *mux.Router, devMode bool) {

code/go/0chain.net/blobbercore/allocation/allocationchange.go

Lines changed: 200 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8+
"strconv"
89
"sync"
10+
"sync/atomic"
911
"time"
1012

1113
"github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore"
1214
"github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore"
1315
"github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference"
1416
"github.com/0chain/blobber/code/go/0chain.net/core/common"
1517
"github.com/0chain/blobber/code/go/0chain.net/core/logging"
18+
"github.com/0chain/common/core/util/wmpt"
1619
"github.com/0chain/gosdk/constants"
1720
"golang.org/x/sync/errgroup"
1821

@@ -34,6 +37,7 @@ type AllocationChangeProcessor interface {
3437
DeleteTempFile() error
3538
ApplyChange(ctx context.Context, rootRef *reference.Ref, change *AllocationChange, allocationRoot string,
3639
ts common.Timestamp, fileIDMeta map[string]string) (*reference.Ref, error)
40+
ApplyChangeV2(ctx context.Context, allocationRoot, clientPubKey string, numFiles *atomic.Int32, ts common.Timestamp, trie *wmpt.WeightedMerkleTrie, collector reference.QueryCollector) (int64, error)
3741
GetPath() []string
3842
Marshal() (string, error)
3943
Unmarshal(string) error
@@ -74,6 +78,7 @@ type AllocationChange struct {
7478
Input string `gorm:"column:input"`
7579
FilePath string `gorm:"-"`
7680
LookupHash string `gorm:"column:lookup_hash;size:64"`
81+
AllocationID string `gorm:"-" json:"-"`
7782
datastore.ModelWithTS
7883
}
7984

@@ -275,10 +280,47 @@ func (cc *AllocationChangeCollector) ApplyChanges(ctx context.Context, allocatio
275280
if err != nil {
276281
return rootRef, err
277282
}
278-
err = collector.Finalize(ctx)
283+
//Ignore ref cache for version 1 of storage
284+
err = collector.Finalize(ctx, "", "")
279285
return rootRef, err
280286
}
281287

288+
func (cc *AllocationChangeCollector) ApplyChangesV2(ctx context.Context, allocationRoot, clientPubKey string, numFiles *atomic.Int32, maxFileChange int32, ts common.Timestamp, trie *wmpt.WeightedMerkleTrie) error {
289+
now := time.Now()
290+
collector := reference.NewCollector(len(cc.Changes))
291+
eg, _ := errgroup.WithContext(context.TODO())
292+
eg.SetLimit(10)
293+
cc.Size = 0
294+
for idx, change := range cc.Changes {
295+
change.AllocationID = cc.AllocationID
296+
changeIndex := idx
297+
eg.Go(func() error {
298+
changeProcessor := cc.AllocationChanges[changeIndex]
299+
sizeChange, err := changeProcessor.ApplyChangeV2(ctx, allocationRoot, clientPubKey, numFiles, ts, trie, collector)
300+
if err != nil {
301+
logging.Logger.Error("ApplyChangesV2Error", zap.Error(err))
302+
return err
303+
}
304+
atomic.AddInt64(&cc.Size, sizeChange)
305+
return nil
306+
})
307+
308+
}
309+
err := eg.Wait()
310+
if err != nil {
311+
logging.Logger.Error("ApplyChangesV2", zap.Error(err))
312+
return err
313+
}
314+
if numFiles.Load() > int32(maxFileChange) {
315+
return common.NewError("max_file_change", "Max file change exceeded "+strconv.Itoa(int(maxFileChange)))
316+
}
317+
elapsedApplyChanges := time.Since(now)
318+
err = collector.Finalize(ctx, cc.AllocationID, allocationRoot)
319+
elapsedFinalize := time.Since(now) - elapsedApplyChanges
320+
logging.Logger.Info("ApplyChangesV2", zap.String("allocation_id", cc.AllocationID), zap.Duration("elapsed_apply_changes", elapsedApplyChanges), zap.Duration("elapsed_finalize", elapsedFinalize), zap.Int("changes", len(cc.Changes)))
321+
return err
322+
}
323+
282324
func (a *AllocationChangeCollector) CommitToFileStore(ctx context.Context) error {
283325
// Limit can be configured at runtime, this number will depend on the number of active allocations
284326
eg, _ := errgroup.WithContext(ctx)
@@ -313,10 +355,30 @@ type Result struct {
313355
}
314356

315357
// TODO: Need to speed up this function
316-
func (a *AllocationChangeCollector) MoveToFilestore(ctx context.Context) error {
358+
func (a *AllocationChangeCollector) MoveToFilestore(ctx context.Context, allocationObj *Allocation) error {
317359

318360
logging.Logger.Info("Move to filestore", zap.String("allocation_id", a.AllocationID))
319-
err := deleteFromFileStore(ctx, a.AllocationID)
361+
if allocationObj.IsRedeemRequired {
362+
err := datastore.GetStore().WithNewTransaction(func(ctx context.Context) error {
363+
allocationObj.IsRedeemRequired = false
364+
365+
updateMap := map[string]interface{}{
366+
"is_redeem_required": false,
367+
}
368+
updateOption := func(a *Allocation) {
369+
a.IsRedeemRequired = false
370+
}
371+
372+
if err := Repo.UpdateAllocation(ctx, allocationObj, updateMap, updateOption); err != nil {
373+
return common.NewError("allocation_write_error", "Error persisting the allocation object")
374+
}
375+
return nil
376+
})
377+
if err != nil {
378+
return err
379+
}
380+
}
381+
err := deleteFromFileStore(a.AllocationID)
320382
if err != nil {
321383
return err
322384
}
@@ -393,7 +455,7 @@ func (a *AllocationChangeCollector) MoveToFilestore(ctx context.Context) error {
393455
return err
394456
}
395457

396-
func deleteFromFileStore(ctx context.Context, allocationID string) error {
458+
func deleteFromFileStore(allocationID string) error {
397459
limitCh := make(chan struct{}, 10)
398460
wg := &sync.WaitGroup{}
399461
var results []Result
@@ -460,6 +522,140 @@ func deleteFromFileStore(ctx context.Context, allocationID string) error {
460522
})
461523
}
462524

525+
func (a *AllocationChangeCollector) MoveToFilestoreV2(ctx context.Context, allocationObj *Allocation, allocationRoot string) error {
526+
logging.Logger.Info("Move to filestore v2", zap.String("allocation_id", a.AllocationID))
527+
now := time.Now()
528+
if allocationObj.IsRedeemRequired {
529+
err := datastore.GetStore().WithNewTransaction(func(ctx context.Context) error {
530+
allocationObj.IsRedeemRequired = false
531+
532+
updateMap := map[string]interface{}{
533+
"is_redeem_required": false,
534+
}
535+
updateOption := func(a *Allocation) {
536+
a.IsRedeemRequired = false
537+
}
538+
tx := datastore.GetStore().GetTransaction(ctx)
539+
if err := tx.Exec("SET LOCAL synchronous_commit TO OFF").Error; err != nil {
540+
return err
541+
}
542+
543+
if err := Repo.UpdateAllocation(ctx, allocationObj, updateMap, updateOption); err != nil {
544+
return common.NewError("allocation_write_error", "Error persisting the allocation object")
545+
}
546+
return nil
547+
})
548+
if err != nil {
549+
return err
550+
}
551+
}
552+
elapsedUpdateAllocation := time.Since(now)
553+
554+
var (
555+
refs []*reference.Ref
556+
useRefCache bool
557+
deletedRefs []*reference.Ref
558+
)
559+
refCache := reference.GetRefCache(a.AllocationID)
560+
defer reference.DeleteRefCache(a.AllocationID)
561+
if refCache != nil && refCache.AllocationRoot == allocationRoot {
562+
useRefCache = true
563+
refs = refCache.CreatedRefs
564+
deletedRefs = refCache.DeletedRefs
565+
} else if refCache != nil && refCache.AllocationRoot != allocationRoot {
566+
logging.Logger.Error("Ref cache is not valid", zap.String("allocation_id", a.AllocationID), zap.String("ref_cache_root", refCache.AllocationRoot), zap.String("allocation_root", allocationRoot))
567+
} else {
568+
logging.Logger.Error("Ref cache is nil", zap.String("allocation_id", a.AllocationID))
569+
}
570+
err := deleteFromFileStoreV2(a.AllocationID, deletedRefs, useRefCache)
571+
if err != nil {
572+
return err
573+
}
574+
elapsedDeleteFromFilestore := time.Since(now) - elapsedUpdateAllocation
575+
576+
limitCh := make(chan struct{}, 12)
577+
wg := &sync.WaitGroup{}
578+
if !useRefCache {
579+
tx := datastore.GetStore().GetTransaction(ctx)
580+
err = tx.Model(&reference.Ref{}).Select("lookup_hash").Where("allocation_id=? AND allocation_root=? AND type=?", a.AllocationID, allocationRoot, reference.FILE).Find(&refs).Error
581+
if err != nil {
582+
logging.Logger.Error("Error while moving files to filestore", zap.Error(err))
583+
return err
584+
}
585+
}
586+
587+
for _, ref := range refs {
588+
589+
limitCh <- struct{}{}
590+
wg.Add(1)
591+
refLookupHash := ref.LookupHash
592+
go func() {
593+
defer func() {
594+
<-limitCh
595+
wg.Done()
596+
}()
597+
err := filestore.GetFileStore().MoveToFilestore(a.AllocationID, refLookupHash, filestore.VERSION)
598+
if err != nil {
599+
logging.Logger.Error(fmt.Sprintf("Error while moving file: %s", err.Error()))
600+
}
601+
602+
}()
603+
}
604+
605+
wg.Wait()
606+
elapsedMove := time.Since(now) - elapsedUpdateAllocation - elapsedDeleteFromFilestore
607+
logging.Logger.Info("moveToFilestoreV2", zap.Duration("elapsedAllocation", elapsedUpdateAllocation), zap.Duration("elapsedDelete", elapsedDeleteFromFilestore), zap.Duration("elapsedMove", elapsedMove), zap.Duration("elapsedTotal", time.Since(now)), zap.Bool("useRefCache", useRefCache), zap.Int("createRefs", len(refs)), zap.Int("deleteRefs", len(deletedRefs)))
608+
return nil
609+
}
610+
611+
func deleteFromFileStoreV2(allocationID string, deletedRefs []*reference.Ref, useRefCache bool) error {
612+
limitCh := make(chan struct{}, 12)
613+
wg := &sync.WaitGroup{}
614+
var results []*reference.Ref
615+
if useRefCache {
616+
results = deletedRefs
617+
}
618+
619+
return datastore.GetStore().WithNewTransaction(func(ctx context.Context) error {
620+
db := datastore.GetStore().GetTransaction(ctx)
621+
if !useRefCache {
622+
err := db.Model(&reference.Ref{}).Unscoped().Select("lookup_hash").
623+
Where("allocation_id=? AND type=? AND deleted_at is not NULL", allocationID, reference.FILE).
624+
Find(&results).Error
625+
if err != nil && err != gorm.ErrRecordNotFound {
626+
logging.Logger.Error("DeleteFromFileStore", zap.Error(err))
627+
return err
628+
}
629+
}
630+
631+
for _, res := range results {
632+
limitCh <- struct{}{}
633+
wg.Add(1)
634+
resLookupHash := res.LookupHash
635+
go func() {
636+
defer func() {
637+
<-limitCh
638+
wg.Done()
639+
}()
640+
641+
err := filestore.GetFileStore().DeleteFromFilestore(allocationID, resLookupHash,
642+
filestore.VERSION)
643+
if err != nil {
644+
logging.Logger.Error(fmt.Sprintf("Error while deleting file: %s", err.Error()),
645+
zap.String("validation_root", resLookupHash))
646+
}
647+
}()
648+
649+
}
650+
wg.Wait()
651+
652+
return db.Model(&reference.Ref{}).Unscoped().
653+
Delete(&reference.Ref{},
654+
"allocation_id = ? AND deleted_at IS NOT NULL",
655+
allocationID).Error
656+
})
657+
}
658+
463659
// Note: We are also fetching refPath for srcPath in copy operation
464660
func (a *AllocationChangeCollector) GetRootRef(ctx context.Context) (*reference.Ref, error) {
465661
paths := make([]string, 0)

code/go/0chain.net/blobbercore/allocation/common_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ func (mfs *MockFileStore) DeleteFromFilestore(allocID, hash string, version int)
4848
return nil
4949
}
5050

51+
func (mfs *MockFileStore) CopyFile(allocationID, oldFileLookupHash, newFileLookupHash string) error {
52+
return nil
53+
}
54+
5155
func (mfs *MockFileStore) DeleteTempFile(allocID, connID string, fileData *filestore.FileInputData) error {
5256
return nil
5357
}

0 commit comments

Comments
 (0)