github.com/ledgerwatch/erigon-lib@v1.0.0/downloader/mdbx_piece_completion.go (about)

     1  /*
     2     Copyright 2021 Erigon contributors
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package downloader
    18  
    19  import (
    20  	"context"
    21  	"encoding/binary"
    22  
    23  	"github.com/anacrolix/torrent/metainfo"
    24  	"github.com/anacrolix/torrent/storage"
    25  	"github.com/anacrolix/torrent/types/infohash"
    26  	"github.com/ledgerwatch/erigon-lib/kv"
    27  )
    28  
    29  const (
    30  	complete   = "c"
    31  	incomplete = "i"
    32  )
    33  
    34  type mdbxPieceCompletion struct {
    35  	db kv.RwDB
    36  }
    37  
    38  var _ storage.PieceCompletion = (*mdbxPieceCompletion)(nil)
    39  
    40  func NewMdbxPieceCompletion(db kv.RwDB) (ret storage.PieceCompletion, err error) {
    41  	ret = &mdbxPieceCompletion{db: db}
    42  	return
    43  }
    44  
    45  func (m mdbxPieceCompletion) Get(pk metainfo.PieceKey) (cn storage.Completion, err error) {
    46  	err = m.db.View(context.Background(), func(tx kv.Tx) error {
    47  		var key [infohash.Size + 4]byte
    48  		copy(key[:], pk.InfoHash[:])
    49  		binary.BigEndian.PutUint32(key[infohash.Size:], uint32(pk.Index))
    50  		cn.Ok = true
    51  		v, err := tx.GetOne(kv.BittorrentCompletion, key[:])
    52  		if err != nil {
    53  			return err
    54  		}
    55  		switch string(v) {
    56  		case complete:
    57  			cn.Complete = true
    58  		case incomplete:
    59  			cn.Complete = false
    60  		default:
    61  			cn.Ok = false
    62  		}
    63  		return nil
    64  	})
    65  	return
    66  }
    67  
    68  func (m mdbxPieceCompletion) Set(pk metainfo.PieceKey, b bool) error {
    69  	if c, err := m.Get(pk); err == nil && c.Ok && c.Complete == b {
    70  		return nil
    71  	}
    72  
    73  	var tx kv.RwTx
    74  	var err error
    75  	// On power-off recent "no-sync" txs may be lost.
    76  	// It will cause 2 cases of in-consistency between files on disk and db metadata:
    77  	//  - Good piece on disk and recent "complete"   db marker lost. Self-Heal by re-download.
    78  	//  - Bad  piece on disk and recent "incomplete" db marker lost. No Self-Heal. Means: can't afford loosing recent "incomplete" markers.
    79  	// FYI: Fsync of torrent pieces happenng before storing db markers: https://github.com/anacrolix/torrent/blob/master/torrent.go#L2026
    80  	//
    81  	// Mainnet stats:
    82  	//  call amount 2 minutes complete=100K vs incomple=1K
    83  	//  1K fsyncs/2minutes it's quite expensive, but even on cloud (high latency) drive it allow download 100mb/s
    84  	//  and Erigon doesn't do anything when downloading snapshots
    85  	if b {
    86  		tx, err = m.db.BeginRwNosync(context.Background())
    87  		if err != nil {
    88  			return err
    89  		}
    90  	} else {
    91  		tx, err = m.db.BeginRw(context.Background())
    92  		if err != nil {
    93  			return err
    94  		}
    95  	}
    96  	defer tx.Rollback()
    97  
    98  	var key [infohash.Size + 4]byte
    99  	copy(key[:], pk.InfoHash[:])
   100  	binary.BigEndian.PutUint32(key[infohash.Size:], uint32(pk.Index))
   101  
   102  	v := []byte(incomplete)
   103  	if b {
   104  		v = []byte(complete)
   105  	}
   106  	err = tx.Put(kv.BittorrentCompletion, key[:], v)
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	return tx.Commit()
   112  }
   113  
   114  func (m *mdbxPieceCompletion) Close() error {
   115  	m.db.Close()
   116  	return nil
   117  }