Skip to content

Conversation

bajtos
Copy link
Member

@bajtos bajtos commented Jul 4, 2024

  • chore: setup Prettier formatting for Markdown
  • docs: initial design proposal

See also #1 for a PoC fetching & parsing a single advertisement.

Links:

bajtos added 2 commits July 4, 2024 10:25
Signed-off-by: Miroslav Bajtoš <oss@bajtos.net>
Signed-off-by: Miroslav Bajtoš <oss@bajtos.net>

Pieces are immutable. If we receive an advertisement saying that a payload block
CID was found in a piece CID, then this information remains valid forever, even
after the SP advertise that they are no longer storing that block. This means

Choose a reason for hiding this comment

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

But if the SP says that they are no longer storing the piece then we should no longer make retrieval requests to that SP for any payloads in the piece, no?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, we should not make any retrievals for such payload.

I am envisioning the following architecture:

  1. A piece indexer I describe in this document. It will be eventually replaced by a proper IPNI reverse index solution.
  2. A deal tracker - a component that listens for actor events and builds a list of active deals - conceptually a list of pairs (piece_cid, miner_id). We will need to build this.

When Spark build a list of tasks for the current round, it will ask the deal tracker for 1000 active deals. This ensures we test retrieval for active deals only.

When Spark checker tests retrieval, it will first consult the piece indexer to convert deal's piece_cid to a payload CID to retrieve.

It's ok if the piece indexer stores data for expired deals, because Spark is not going to ask for that data.

Of course, storing expired deals unnecessarily increases storage requirements, but since we want to run this service only for 2-6 months, I think it's fine.

Copy link
Member

Choose a reason for hiding this comment

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

Sgtm, could you add this context to the document please? I was also not aware of the distinction between the piece indexer and the deal tracker

docs/design.md Outdated
advertised for the same Payload CID. Different SPs can advertise different lists
(e.g. the entries can be ordered differently) or can even cheat and submit CIDs
that are not part of the Piece. Our indexer must scope the information to each
index provider.

Choose a reason for hiding this comment

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

By index proivder do you mean IPNI instance or Storage provider creating indexes/advertisements?

Copy link
Member Author

Choose a reason for hiding this comment

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

The index provider is the actor submitting data to the index. Typically, Boost and Venus Droplet.

@patrickwoodhead
How can I improve the text to make this easier to understand?

Copy link
Member

Choose a reason for hiding this comment

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

Is "index provider" the term to use? If so, I don't think this needs to be clearer up. Maybe we could add the term definition to a definition section in this document?

Copy link
Member Author

Choose a reason for hiding this comment

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

The term "index provider" is used by IPNI:

However, the spec uses just "provider" 🤷🏻‍♂️

https://github.com/ipni/specs/blob/90648bca4749ef912b2d18f221514bc26b5bef0a/IPNI.md#terminology

Publisher: This is an entity that publishes advertisements and index data to an indexer. It is usually, but not always, the same as the data provider. A publisher is identified by a libp2p peer ID.

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 like the idea of adding a section to explain the terminology 👍🏻

docs/design.md Outdated

The response provides all the metadata we need to download the advertisements:

- `Publisher.Addrs` describes where we can contact SP's index provider

Choose a reason for hiding this comment

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

what does this mean?

Copy link
Member

Choose a reason for hiding this comment

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

I believe it's the HTTP address where the SP can respond to indexer requests

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 think it doesn't have to be an HTTP address; it could be a Graphsync/libp2p address, too.

However, there is a push from IPNI to move to HTTP transport. For our lighweight indexer, we will require SPs to support the HTTP protocol for handling indexer requests.

Unfortunately, this requires SPs to tweak their Boost configuration and provide the public hostname at which Boost can be reached from outside. On the bright side, I think it's most likely that SPs have to configure this option anyways if they want cid.contact to receive their advertisements, in which case our lightweight indexer is not adding any new requirements.

https://boost.filecoin.io/configuration/http-indexer-announcement

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 reworded this item as follows:

Publisher.Addrs describes where we can contact SP's index provider to retrieve content for CIDs, e.g., advertisements.

docs/design.md Outdated
The response provides all the metadata we need to download the advertisements:

- `Publisher.Addrs` describes where we can contact SP's index provider
- `LastAdvertisement` contains the CID of the head advertisement

Choose a reason for hiding this comment

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

Is this the Last advertisement for a specific SP or just in the IPNI instance altogether?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's the last advertisement for a specific SP.

All fields described in this section are per-provider.

How can I improve the text to make this more clear?

Copy link
Member

Choose a reason for hiding this comment

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

What about

The response provides all the metadata we need to download the advertisements:

->

The response provides all the per-SP metadata we need to download the advertisements:

Copy link
Member Author

Choose a reason for hiding this comment

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

Reworded as follows:

The response provides all the metadata we need to download the advertisements.
For each index provider, the response includes:

- `Publisher.Addrs` describes where we can contact SP's index provider to
  retrieve content for CIDs, e.g., advertisements.
- `LastAdvertisement` contains the CID of the head advertisement from the SP

advertisements from `last_head` to the end of the chain were already
processed.

- `next_head` - The CID of the most recent head seen by cid.contact. This is

Choose a reason for hiding this comment

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

how does next_head differ from head? Do we not start each walk from the current head?

Copy link
Member Author

Choose a reason for hiding this comment

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

The initial walk will take a long time to complete. While we are walking the "old" chain, new advertisements (new heads) will be announced to IPNI.

  • next_head is the latest head announced to IPNI
  • head is the advertisement where the current walk-in-progress started

I suppose we don't need to keep track of next_head. When the current walk finishes, we will wait up to one minute until we make another request to cid.contact to find what are the latest heads for each SPs.

In my current proposal, when the current walk finishes, we can immediately continue with walking from the next_head.

Copy link
Member Author

Choose a reason for hiding this comment

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

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 captured my explanation in the design doc.

docs/design.md Outdated
- `tail` - The CID of the next advertisement in the chain that we need to
process in the current walk.

Every minute, fetch the latest providers from cid.contact. For each provider

Choose a reason for hiding this comment

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

Each minute, we are running an algorithm that has a complexity of the order of the number of providers. Do we have any concerns about how much processing we will be doing each minute? Might this process take more than a minute to run?

Copy link
Member

Choose a reason for hiding this comment

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

What about with one minute delay between every run?

Copy link
Member Author

@bajtos bajtos Jul 25, 2024

Choose a reason for hiding this comment

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

I think this should be fine. Every minute, we need to do these three four steps:

  1. Run a SQL query to get the next_head for each SP
  2. Make one HTTP call to cid.contact to find the latest advertisement heads announced by all SPs
  3. Run one SQL query to update next_head for all SPs where there is a new head
  4. Kick-off advertisement walks (up to one walk per provider). These walks are executed in the background and don't block this loop.

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 updated the spec to capture my explanation.

spark-evaluate to verify the authenticity of results reported by checker nodes:

```
GET /sample/{providerId}/{pieceCid}?seed={seed}

Choose a reason for hiding this comment

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

can we add an endpoint that allows a SP to see which records there are in the DB associated to them? As per F8 Ptrk's comment a week or so back

Copy link
Member Author

Choose a reason for hiding this comment

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

See the "Observability" section below. Do you think we need something different?

docs/design.md Outdated
- `tail` - The CID of the next advertisement in the chain that we need to
process in the current walk.

Every minute, fetch the latest providers from cid.contact. For each provider
Copy link
Member

Choose a reason for hiding this comment

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

What about with one minute delay between every run?

where we need to start the next walk from.

- `head` - The CID of the head advertisement we started the current walk from.
We update this value whenever we start a new walk.
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need this state? Should it not be enough to go from next_head to last_head and afterwards update last_head?

Or is this for the case where this takes a long time and we want to be able to resume the chain walk? In this case, what about we simplify the design by saying that we will only ever walk X links per iteration, thereby eliminating the need for the head state?

Copy link
Member

Choose a reason for hiding this comment

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

Then we could also remove tail

Copy link
Member Author

@bajtos bajtos Jul 24, 2024

Choose a reason for hiding this comment

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

The current walk starts from head and walks up to last_head. When the current walk reaches last_head, we need to set last_head ← head so that the next walk knows where to stop.

next_head is updated every minute when we query cid.contact for the latest heads. If the walk takes longer than a minute to finish, then next_head will change and we cannot use it for last_head.

What we can do, is to remove next_head, as I explained in https://github.com/filecoin-station/piece-indexer/pull/2/files#r1689865074

In this case, what about we simplify the design by saying that we will only ever walk X links per iteration, thereby eliminating the need for the head state?

We must always walk the chain all the way to the genesis or to the entry we have already seen & processed. Here is how the state looks like in the middle of a walk:

next_head
  ↓
(entries announced after we started the current walk)
  ↓
head
  ↓
(entries visited in this walk)
  ↓
tail
  ↓
(entries NOT visited yet)
  ↓
last_head
  ↓
(entries visited in the previous walks)
  ↓
(null)

bajtos and others added 10 commits July 24, 2024 15:28
Co-authored-by: Julian Gruber <julian@juliangruber.com>
Co-authored-by: Julian Gruber <julian@juliangruber.com>
Co-authored-by: Julian Gruber <julian@juliangruber.com>
Co-authored-by: Julian Gruber <julian@juliangruber.com>
Co-authored-by: Julian Gruber <julian@juliangruber.com>
Co-authored-by: Julian Gruber <julian@juliangruber.com>
Signed-off-by: Miroslav Bajtoš <oss@bajtos.net>
Signed-off-by: Miroslav Bajtoš <oss@bajtos.net>
Signed-off-by: Miroslav Bajtoš <oss@bajtos.net>
Signed-off-by: Miroslav Bajtoš <oss@bajtos.net>
Signed-off-by: Miroslav Bajtoš <oss@bajtos.net>
@bajtos bajtos merged commit 0fc5efd into main Sep 4, 2024
@bajtos bajtos deleted the design-doc branch September 4, 2024 08:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants