Skip to content
This repository has been archived by the owner on Aug 2, 2021. It is now read-only.

LocalStore #1032

Closed
wants to merge 81 commits into from
Closed

LocalStore #1032

wants to merge 81 commits into from

Conversation

janos
Copy link
Member

@janos janos commented Nov 29, 2018

Implementation of localstore using shed package, based on https://hackmd.io/ffBjSu8RTyKikvRO7bYrzA document.

swarm/storage/localstore/accessor.go Outdated Show resolved Hide resolved
swarm/storage/localstore/accessor.go Outdated Show resolved Hide resolved
swarm/storage/localstore/accessor_test.go Outdated Show resolved Hide resolved
if err != nil {
return nil, err
}
db.retrievalIndex, err = db.shed.NewIndex("Hash->StoredTimestamp|AccessTimestamp|Data", shed.IndexFuncs{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think disk IO will suffer from this if we write 4k chunks every time we update access time.

Shall we just have a separate hash->AccessTimestamp index until we have a smarter way?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can measure both cases, and also the third case that you thought of, with more complex index structure where two or more key/value pairs are created with prefixes to optimize seeks and writes. I still think that it is not hard to create such index type.

swarm/storage/localstore/localstore.go Outdated Show resolved Hide resolved
swarm/storage/localstore/mode.go Outdated Show resolved Hide resolved
swarm/storage/localstore/localstore.go Outdated Show resolved Hide resolved
swarm/storage/localstore/mode.go Outdated Show resolved Hide resolved
swarm/storage/localstore/mode.go Outdated Show resolved Hide resolved
swarm/storage/localstore/mode.go Outdated Show resolved Hide resolved
Copy link
Member

@zelig zelig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very promising 👍

swarm/storage/localstore/localstore.go Outdated Show resolved Hide resolved
swarm/storage/localstore/localstore.go Outdated Show resolved Hide resolved
swarm/storage/localstore/localstore.go Outdated Show resolved Hide resolved
@janos
Copy link
Member Author

janos commented Dec 7, 2018

Thanks @zelig, comments are updated.

// is provided to the function.
ErrInvalidMode = errors.New("invalid mode")
// ErrDBClosed is returned when database is closed.
ErrDBClosed = errors.New("db closed")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not used.

t.Run("gc uncounted hashes index count", newItemsCountTest(db.gcUncountedHashesIndex, 0))
}

func testStoredGCSize(t *testing.T, db *DB, want uint64) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This helper appears to not be used.

@nonsense
Copy link
Contributor

@janos I haven't reviewed this fully, but I think it would be good to have some instrumentation in this code, so that we can monitor it's behaviour when we deploy. Right now it will be very difficult to understand if this is working as expected if we merge.

@janos
Copy link
Member Author

janos commented Jan 22, 2019

@nonsense I am not sure if you were on a meeting when I did a code walkthrough, I would be glad to do it again, to explain details.

This code is not integrated into swarm, current localstore is still used, so I tried to create tests that would provide some insight into the expected behaviour. If you have any suggestions to make the code more readable, I would be glad to hear. This code is based on this document https://hackmd.io/ffBjSu8RTyKikvRO7bYrzA.

@nonsense
Copy link
Contributor

@janos yes, all clear ;) I see you are not updating any existing lines of code, so I figured this is not integrated. Still I think my comment applies - tests and benchmarks are great, but we also want to have visibility when we run with production work loads, so we should add some logging and metrics.

@janos
Copy link
Member Author

janos commented Jan 22, 2019

@nonsense Yes, logging and metrics at runtime is missing. I just did not think of them at this stage of development. They certainly must be added before the code is used in production. We can see what are the most critical parts add logging and metrics there.

Copy link
Contributor

@holisticode holisticode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I asked this at first (high level) walk through, don't remember answer. This is not integrated with the stream package yet. Will that be part of the same PR? Then it will become a very big one. Nevertheless, I would oppose NOT migrating the stream package right away along these changes, because otherwise we don't have a clear idea of how and how well it works together (smoke and other tests)

  2. How is localstore/db how to insert items in GC index #1031 related? I can't see a clear decision has been taken on that ticket; which Suggestion has been implemented here? Or will it be implemented in the future? How will it change this PR?

  3. How does this implementation of GC work with insured chunks in the future? Will it have to change again? Will it just not be added to the GC index?

  4. How could different GC algorithms plugged in resp. adaptations be made to GC in the future? What if we need very flexible solutions due to crypto-economic requirements?


Getters, Putters and Setters accept different get, put and set modes
to perform different actions. For example, ModeGet has two different
variables ModeGetRequest and ModeGetSync and dwo different Getters
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/dwo/two

// in range (0,1]. For example, with 0.9 value,
// garbage collection will leave 90% of defined capacity
// in database after its run. This prevents frequent
// garbage collection runt.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/runt/runs

swarm/storage/localstore/gc.go Outdated Show resolved Hide resolved
swarm/storage/localstore/gc.go Outdated Show resolved Hide resolved
}

gcSize := db.getGCSize()
if gcSize-collectedCount <= target {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there not the danger of the following here:

  • We reach GC target
  • GC kicks in
  • Global Lock On
  • Run GC
  • In The meanwhile new syncing happens, new chunks arrive and wait
  • GC terminates
  • New chunks from above now have to be written again, then GC runs again - If the amount of new chunks exceeds the target, GC runs again....

"Loop" repeats

What happens if we reach target while new chunks wait for write, is the write interrupted so that GC runs? What if DB is at 90%, then new chunks arrive with exceeding capacity, (say 105%), will write be interrupted?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that you are referring only to a global lock.

You can see that every chunk is saved in its own batch, so this can not happen, as gc should kick in. There should be one difference with global lock. The gc size counting should be much simpler and included in the chunk write batch. But this is only for the global lock, which is here only for BenchmarkPutUpload, until we decide whether we want to use it, or stick with address lock.


// schema name of loaded data
schemaName shed.StringField
// filed that stores number of intems in gc index
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

file that stores number of items?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ups, that is a bad typo, should be field.

return storage.NewChunk(out.Address, out.Data), nil
}

// get returns Item with from the retrieval index
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get returns Item with...from?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'With' is the word that is not needed.

return err
}
if item.AccessTimestamp == 0 {
// chunk is not yes synced
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not yet

const (
// ModePutRequest: when a chunk is received as a result of retrieve request and delivery, it is put only in
ModePutRequest ModePut = iota
// ModePutSync: when a chunk is received via syncing in it is put in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"in it is put in" sounds strange

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this comments should be corrected.

}
if item.AccessTimestamp != 0 {
// delete current entry from the gc index
db.gcIndex.DeleteInBatch(batch, item)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like GC logic is dispersed over different places...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any ideas how to organize it better? My current one is to use global lock, which would simplify gc counting. But, this part of the code that you are referring, just needs to remove the old item in gc index and put the new one with new access timestamp. Any ideas how to simplify it?

@janos
Copy link
Member Author

janos commented Jan 29, 2019

Thanks @holisticode for a very detailed code review. It opens some nice questions regarding the complexity and many parts of the code that are still too convoluted and not clear. I ask for a discussion about the global lock based on BenchmarkPutUpload results and impact on performance and code clarity.

  1. I asked this at first (high level) walk through, don't remember answer. This is not integrated with the stream package yet. Will that be part of the same PR? Then it will become a very big one. Nevertheless, I would oppose NOT migrating the stream package right away along these changes, because otherwise we don't have a clear idea of how and how well it works together (smoke and other tests)

This PR should contain only additions in the localstore package. Is under a review because it is getting large and integration to stream would make it even larger.

  1. How is localstore/db how to insert items in GC index #1031 related? I can't see a clear decision has been taken on that ticket; which Suggestion has been implemented here? Or will it be implemented in the future? How will it change this PR?

Issue #1031 is related but still under discussion, while the gc implementation in new localstore is functional with algorithm similar to the one in current ldbstore. It may be better to have a separate PR for new gc implementation only. This PR should not contain work based on #1031. Would you suggest a different approach?

  1. How does this implementation of GC work with insured chunks in the future? Will it have to change again? Will it just not be added to the GC index?

This are great questions but I think that they are better asked in #1031. Could you raise your concerns there?

  1. How could different GC algorithms plugged in resp. adaptations be made to GC in the future? What if we need very flexible solutions due to crypto-economic requirements?

We should implement that. Do you have ideas how we can have better storage foundations for that? I tried to make localstore with indexes as abstractions for this reason. I would like to hear ideas that it can be improved with different generalizations and what would be the tradeoffs in terms of performance.

@janos
Copy link
Member Author

janos commented Feb 5, 2019

@holisticode I tried to elaborate the reasoning behind garbage collection index size counting in this comment 40432d9. Is this addresses some complexities that you raised in comments? Would you write it differently, or maybe change the implementation?

@janos
Copy link
Member Author

janos commented Feb 7, 2019

Submitted usptream ethereum/go-ethereum#19015.

@janos janos closed this Feb 7, 2019
@acud acud deleted the localstore branch June 3, 2019 14:43
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants