-
Notifications
You must be signed in to change notification settings - Fork 110
Conversation
if err != nil { | ||
return nil, err | ||
} | ||
db.retrievalIndex, err = db.shed.NewIndex("Hash->StoredTimestamp|AccessTimestamp|Data", shed.IndexFuncs{ |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks very promising 👍
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") |
There was a problem hiding this comment.
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.
swarm/storage/localstore/gc_test.go
Outdated
t.Run("gc uncounted hashes index count", newItemsCountTest(db.gcUncountedHashesIndex, 0)) | ||
} | ||
|
||
func testStoredGCSize(t *testing.T, db *DB, want uint64) { |
There was a problem hiding this comment.
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.
@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. |
@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. |
@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. |
@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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
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 thestream
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) -
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?
-
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?
-
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?
swarm/storage/localstore/doc.go
Outdated
|
||
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/dwo/two
swarm/storage/localstore/gc.go
Outdated
// 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/runt/runs
} | ||
|
||
gcSize := db.getGCSize() | ||
if gcSize-collectedCount <= target { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
swarm/storage/localstore/mode_get.go
Outdated
return storage.NewChunk(out.Address, out.Data), nil | ||
} | ||
|
||
// get returns Item with from the retrieval index |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
swarm/storage/localstore/mode_get.go
Outdated
return err | ||
} | ||
if item.AccessTimestamp == 0 { | ||
// chunk is not yes synced |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not yet
swarm/storage/localstore/mode_put.go
Outdated
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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?
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.
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.
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?
This are great questions but I think that they are better asked in #1031. Could you raise your concerns there?
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. |
@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? |
Submitted usptream ethereum/go-ethereum#19015. |
Implementation of localstore using shed package, based on https://hackmd.io/ffBjSu8RTyKikvRO7bYrzA document.