Skip to content
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

fix(proxy): refresh connection between retries #3566

Merged
merged 10 commits into from
Feb 26, 2025

Conversation

bodinsamuel
Copy link
Collaborator

@bodinsamuel bodinsamuel commented Feb 24, 2025

Changes

Fixes https://linear.app/nango/issue/NAN-2745/retry-strategy-should-call-getconnection-before-each-try

credentials are expired between calls

  • ProxyRequest now accepts a getConnection param called on every iteration
    The main idea behind this PR is to re-inject the connection after TTL expires.
    I had to rewrite part of the code to be able to dynamically build axios config (url + headers).

  • Type: Split ProxyConfig and Connection
    Passing a connection to this type was not useful and was kind of weird with the new behavior. Now we pass a base proxyconfig that tells what we want to query and we recompile the axios config based on that.

Tests

  • Call the proxy with the retry header
curl --request GET \
  --url http://localhost:3003/proxy/500 \
  --header 'Authorization: REDACTED' \
  --header 'Base-Url-Override: https://httpstatuses.maor.io' \
  --header 'Connection-Id: 025d9a38-f31c-4a64-b034-57a425761a90' \
  --header 'Provider-Config-Key: unauthenticated' \
  --header 'Retries: 10'
  • Same for local sync
import type { NangoSync } from "../../models";

export default async function fetchData(nango: NangoSync): Promise<void> {
  await nango.log("started");
  await nango.proxy({
    baseUrlOverride: "https://httpstatuses.maor.io",
    endpoint: "/500",
    method: "GET",
    retries: 10
  });
}

Very long retry with a periodic refresh
Screenshot 2025-02-25 at 12 13 08

@bodinsamuel bodinsamuel self-assigned this Feb 24, 2025
if (!this.activityLogId) throw new Error('Parameter activityLogId is required');
if (!this.environmentId) throw new Error('Parameter environmentId is required');
if (!this.nangoConnectionId) throw new Error('Parameter nangoConnectionId is required');
if (!this.syncConfig) throw new Error('Parameter syncConfig is required');
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

left over from rewrite

proxyConfig: config
});

const responseStream = (await proxy.request({ axiosConfig })).unwrap();
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

All the removed logic has been abstracted in getAxiosConfiguration

/**
* Called on error, gives the ability to control the retry and wait time
*/
onError?: Props['onError'];
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

not used yet

@bodinsamuel bodinsamuel marked this pull request as draft February 24, 2025 16:50
onError?: (args: { err: unknown; max: number; attempt: number }) => { retry: boolean; reason: string; wait?: number };
getConnection: () => MaybePromise<ConnectionForProxy>;
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Rewrote this file to accept a getConnection param that's called on every iteration

@@ -6,36 +6,32 @@ import { Readable, Transform, PassThrough } from 'stream';
import type { UrlWithParsedQuery } from 'url';
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Removed a lot of 'functional method' that did nothing

@bodinsamuel bodinsamuel marked this pull request as ready for review February 25, 2025 13:50
@bodinsamuel bodinsamuel requested a review from a team February 25, 2025 13:50
Copy link

linear bot commented Feb 25, 2025

const redactedURL = redactURL({ url: axiosConfig.url!, valuesToFilter });
/**
* For testing purpose only
* @private
Copy link
Collaborator

Choose a reason for hiding this comment

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

How does this @Private work? The method is still declared as public below

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it's only for documentation purposes, I need it to be public so I can mock it

@@ -26,23 +33,51 @@ export class ProxyError extends Error {
}
}

const methodDataAllowed = ['POST', 'PUT', 'PATCH', 'DELETE'];
Copy link
Collaborator

Choose a reason for hiding this comment

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

used only once. Not sure it requires its own global const

Copy link
Collaborator Author

@bodinsamuel bodinsamuel Feb 25, 2025

Choose a reason for hiding this comment

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

true, but allocating an array at each call seems like a waste to me

@bodinsamuel bodinsamuel merged commit 85c998b into master Feb 26, 2025
16 checks passed
@bodinsamuel bodinsamuel deleted the sam/25_02_24/fix/proxy-refresh-connection branch February 26, 2025 10:12
bodinsamuel added a commit that referenced this pull request Feb 26, 2025
@bodinsamuel bodinsamuel restored the sam/25_02_24/fix/proxy-refresh-connection branch February 26, 2025 14:33
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.

2 participants