github.com/susy-go/susy-graviton@v0.0.0-20190614130430-36cddae42305/swarm/storage/localstore/index_test.go (about)

     1  // Copyleft 2018 The susy-graviton Authors
     2  // This file is part of the susy-graviton library.
     3  //
     4  // The susy-graviton library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The susy-graviton library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MSRCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the susy-graviton library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package localstore
    18  
    19  import (
    20  	"bytes"
    21  	"math/rand"
    22  	"testing"
    23  
    24  	"github.com/susy-go/susy-graviton/swarm/storage"
    25  )
    26  
    27  // TestDB_pullIndex validates the ordering of keys in pull index.
    28  // Pull index key contains PO prefix which is calculated from
    29  // DB base key and chunk address. This is not an Item field
    30  // which are checked in Mode tests.
    31  // This test uploads chunks, sorts them in expected order and
    32  // validates that pull index iterator will iterate it the same
    33  // order.
    34  func TestDB_pullIndex(t *testing.T) {
    35  	db, cleanupFunc := newTestDB(t, nil)
    36  	defer cleanupFunc()
    37  
    38  	uploader := db.NewPutter(ModePutUpload)
    39  
    40  	chunkCount := 50
    41  
    42  	chunks := make([]testIndexChunk, chunkCount)
    43  
    44  	// upload random chunks
    45  	for i := 0; i < chunkCount; i++ {
    46  		chunk := generateRandomChunk()
    47  
    48  		err := uploader.Put(chunk)
    49  		if err != nil {
    50  			t.Fatal(err)
    51  		}
    52  
    53  		chunks[i] = testIndexChunk{
    54  			Chunk: chunk,
    55  			// this timestamp is not the same as in
    56  			// the index, but given that uploads
    57  			// are sequential and that only ordering
    58  			// of events matter, this information is
    59  			// sufficient
    60  			storeTimestamp: now(),
    61  		}
    62  	}
    63  
    64  	testItemsOrder(t, db.pullIndex, chunks, func(i, j int) (less bool) {
    65  		poi := storage.Proximity(db.baseKey, chunks[i].Address())
    66  		poj := storage.Proximity(db.baseKey, chunks[j].Address())
    67  		if poi < poj {
    68  			return true
    69  		}
    70  		if poi > poj {
    71  			return false
    72  		}
    73  		if chunks[i].storeTimestamp < chunks[j].storeTimestamp {
    74  			return true
    75  		}
    76  		if chunks[i].storeTimestamp > chunks[j].storeTimestamp {
    77  			return false
    78  		}
    79  		return bytes.Compare(chunks[i].Address(), chunks[j].Address()) == -1
    80  	})
    81  }
    82  
    83  // TestDB_gcIndex validates garbage collection index by uploading
    84  // a chunk with and performing operations using synced, access and
    85  // request modes.
    86  func TestDB_gcIndex(t *testing.T) {
    87  	db, cleanupFunc := newTestDB(t, nil)
    88  	defer cleanupFunc()
    89  
    90  	uploader := db.NewPutter(ModePutUpload)
    91  
    92  	chunkCount := 50
    93  
    94  	chunks := make([]testIndexChunk, chunkCount)
    95  
    96  	// upload random chunks
    97  	for i := 0; i < chunkCount; i++ {
    98  		chunk := generateRandomChunk()
    99  
   100  		err := uploader.Put(chunk)
   101  		if err != nil {
   102  			t.Fatal(err)
   103  		}
   104  
   105  		chunks[i] = testIndexChunk{
   106  			Chunk: chunk,
   107  		}
   108  	}
   109  
   110  	// check if all chunks are stored
   111  	newItemsCountTest(db.pullIndex, chunkCount)(t)
   112  
   113  	// check that chunks are not collectable for garbage
   114  	newItemsCountTest(db.gcIndex, 0)(t)
   115  
   116  	// set update gc test hook to signal when
   117  	// update gc goroutine is done by sending to
   118  	// testHookUpdateGCChan channel, which is
   119  	// used to wait for indexes change verifications
   120  	testHookUpdateGCChan := make(chan struct{})
   121  	defer setTestHookUpdateGC(func() {
   122  		testHookUpdateGCChan <- struct{}{}
   123  	})()
   124  
   125  	t.Run("request unsynced", func(t *testing.T) {
   126  		chunk := chunks[1]
   127  
   128  		_, err := db.NewGetter(ModeGetRequest).Get(chunk.Address())
   129  		if err != nil {
   130  			t.Fatal(err)
   131  		}
   132  		// wait for update gc goroutine to be done
   133  		<-testHookUpdateGCChan
   134  
   135  		// the chunk is not synced
   136  		// should not be in the garbace collection index
   137  		newItemsCountTest(db.gcIndex, 0)(t)
   138  
   139  		newIndexGCSizeTest(db)(t)
   140  	})
   141  
   142  	t.Run("sync one chunk", func(t *testing.T) {
   143  		chunk := chunks[0]
   144  
   145  		err := db.NewSetter(ModeSetSync).Set(chunk.Address())
   146  		if err != nil {
   147  			t.Fatal(err)
   148  		}
   149  
   150  		// the chunk is synced and should be in gc index
   151  		newItemsCountTest(db.gcIndex, 1)(t)
   152  
   153  		newIndexGCSizeTest(db)(t)
   154  	})
   155  
   156  	t.Run("sync all chunks", func(t *testing.T) {
   157  		setter := db.NewSetter(ModeSetSync)
   158  
   159  		for i := range chunks {
   160  			err := setter.Set(chunks[i].Address())
   161  			if err != nil {
   162  				t.Fatal(err)
   163  			}
   164  		}
   165  
   166  		testItemsOrder(t, db.gcIndex, chunks, nil)
   167  
   168  		newIndexGCSizeTest(db)(t)
   169  	})
   170  
   171  	t.Run("request one chunk", func(t *testing.T) {
   172  		i := 6
   173  
   174  		_, err := db.NewGetter(ModeGetRequest).Get(chunks[i].Address())
   175  		if err != nil {
   176  			t.Fatal(err)
   177  		}
   178  		// wait for update gc goroutine to be done
   179  		<-testHookUpdateGCChan
   180  
   181  		// move the chunk to the end of the expected gc
   182  		c := chunks[i]
   183  		chunks = append(chunks[:i], chunks[i+1:]...)
   184  		chunks = append(chunks, c)
   185  
   186  		testItemsOrder(t, db.gcIndex, chunks, nil)
   187  
   188  		newIndexGCSizeTest(db)(t)
   189  	})
   190  
   191  	t.Run("random chunk request", func(t *testing.T) {
   192  		requester := db.NewGetter(ModeGetRequest)
   193  
   194  		rand.Shuffle(len(chunks), func(i, j int) {
   195  			chunks[i], chunks[j] = chunks[j], chunks[i]
   196  		})
   197  
   198  		for _, chunk := range chunks {
   199  			_, err := requester.Get(chunk.Address())
   200  			if err != nil {
   201  				t.Fatal(err)
   202  			}
   203  			// wait for update gc goroutine to be done
   204  			<-testHookUpdateGCChan
   205  		}
   206  
   207  		testItemsOrder(t, db.gcIndex, chunks, nil)
   208  
   209  		newIndexGCSizeTest(db)(t)
   210  	})
   211  
   212  	t.Run("remove one chunk", func(t *testing.T) {
   213  		i := 3
   214  
   215  		err := db.NewSetter(modeSetRemove).Set(chunks[i].Address())
   216  		if err != nil {
   217  			t.Fatal(err)
   218  		}
   219  
   220  		// remove the chunk from the expected chunks in gc index
   221  		chunks = append(chunks[:i], chunks[i+1:]...)
   222  
   223  		testItemsOrder(t, db.gcIndex, chunks, nil)
   224  
   225  		newIndexGCSizeTest(db)(t)
   226  	})
   227  }