Skip to content

Add IPFS usage metrics / extend logging / extend supported content path formats #6058

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

isum
Copy link
Member

@isum isum commented Jun 12, 2025

This PR introduces the following improvements to IPFS:

  • Adds 4 new metrics grouped by deployment hashes:
    • ipfs_request_count - shows the total number of IPFS requests
    • ipfs_error_count - shows the total number of failed IPFS requests
    • ipfs_not_found_count - shows the total number of IPFS requests that timed out
    • ipfs_request_duration - shows the duration of successful IPFS requests
  • Fixes a minor bug that could cause the IPFS gateway client to fail during initialization
  • The IPFS content path parser now supports the following formats:
    • <CID>[/<path>]
    • /ipfs/<CID>[/<path>]
    • ipfs://<CID>[/<path>]
    • http[s]://.../ipfs/<CID>[/<path>]
    • http[s]://.../api/v0/cat?arg=<CID>[/<path>]
    • http[s]://.../<CID>[/<path>]
  • IPFS error logs now include more context, such as deployment hash and the requested IPFS path

Closes #6036

@fordN fordN requested a review from lutter June 12, 2025 17:24
Copy link
Collaborator

@lutter lutter left a comment

Choose a reason for hiding this comment

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

Nice improvements! It would be good to address the comments about the Default implementation and ownership of IpfsContext before merging, but feel free to merge once that's done.

@@ -433,7 +444,10 @@ mod test {
let ds: UnresolvedDataSource = serde_yaml::from_str(TEMPLATE_DATA_SOURCE).unwrap();
let link_resolver: Arc<dyn LinkResolver> = Arc::new(NoopLinkResolver {});
let logger = Logger::root(Discard, o!());
let ds: DataSource = ds.resolve(&link_resolver, &logger, 0).await.unwrap();
let ds: DataSource = ds
.resolve(&Default::default(), &link_resolver, &logger, 0)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I kinda dislike using Default::default because from just reading the code, I have no idea what gets passed there. Could you write that as LinkResolverContext::default() ?

@@ -470,7 +484,10 @@ mod test {
serde_yaml::from_str(TEMPLATE_DATA_SOURCE_WITH_PARAMS).unwrap();
let link_resolver: Arc<dyn LinkResolver> = Arc::new(NoopLinkResolver {});
let logger = Logger::root(Discard, o!());
let ds: DataSource = ds.resolve(&link_resolver, &logger, 0).await.unwrap();
let ds: DataSource = ds
.resolve(&Default::default(), &link_resolver, &logger, 0)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same comment about Default::default() here

assert_eq!(path, make_path(CID_V0, None));

let path = ContentPath::new(format!("https://ipfs.com/{CID_V0}/readme.md")).unwrap();
assert_eq!(path, make_path(CID_V0, Some("readme.md")));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Everything in this file looks great, but since we are handling user input here, are there ways to abuse this in security-relevant ways? (I think the answer is 'no', but wanted to double-check)

deployment_hash: "test".into(),
logger: crate::log::discard(),
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems petty, but rather than implement Default, it might be clearer to make this a method LinkResolverContext::dummy() (only in debug builds) It's not really a default implementation, since there's not really a sensible default for this struct.

pub type IpfsService = Buffer<ContentPath, BoxFuture<'static, Result<Option<Bytes>, Error>>>;
pub type IpfsService = Buffer<IpfsRequest, BoxFuture<'static, Result<Option<Bytes>, Error>>>;

#[derive(Clone, Debug)]
Copy link
Collaborator

Choose a reason for hiding this comment

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

I got a little hung up on why this needs to be 'Clone'; I think it ultimately boils down to the fact that when we make the request, we want it returned back and we do that in ReturnRequest::call; it seems we could avoid cloning if we changed what IpfsServiceInner::call_inner returns and have it always include the request in its return value, basically moving ownership of the request through call_inner. In this case, I am also not sure how important it is to save on cloning, though we clone on every request.

In any event, this would definitely be something for a different PR.

@@ -32,21 +32,31 @@ pub trait IpfsClient: Send + Sync + 'static {
/// The timeout is not propagated to the resulting stream.
async fn cat_stream(
self: Arc<Self>,
ctx: IpfsContext,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this need to take ownership? It seems that just a reference would be enough

@@ -61,27 +71,33 @@ pub trait IpfsClient: Send + Sync + 'static {
/// does not return a response within the specified amount of time.
async fn cat(
self: Arc<Self>,
ctx: IpfsContext,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same question about ownership

@@ -95,26 +111,32 @@ pub trait IpfsClient: Send + Sync + 'static {
/// does not return a response within the specified amount of time.
async fn get_block(
self: Arc<Self>,
ctx: IpfsContext,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same question about ownership

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

IPFS logging and metrics to aid with debugging
2 participants