Skip to content

Conversation

ianbotsf
Copy link
Contributor

Issue #

Addresses aws/aws-sdk-kotlin#1413

Description of changes

This change overhauls endpoint discovery to address several missed features and a violation of the specification. In summary:

  • Refactor generated <Service>EndpointDiscoverer classes into interfaces so that custom implementations can be provided
    • Default implementations are now codegenned as Default<Service>EndpointDiscoverer
  • Refactor the ReadThroughCache class into an ExpiringKeyedCache interface so that custom implementations can be provided
    • The current implementation of ReadThroughCache is now provided in PeriodicSweepCache, which implements the new interface
  • Favor endpointUrl over endpoint discovery when both are provided/enabled. This allows callers to bypass endpoint discovery when using a custom endpoint.
  • In the codegen layer, provide CodegenContext as a context value in KotlinWriter, which cleans up several duplicated context keys and multiple places where context was unnecessarily handled/duplicated
  • Fix a bug that prevented DiscoveredEndpointErrorInterceptor from being added to relevant operations

⚠️ Breaking change: This is a breaking change because of the type/name changes for endpoint discoverers and caches.

ℹ️ Companion PR: (link coming soon)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

…nd to make endpoint discoverers interfaces so custom implementations can be provided
@ianbotsf ianbotsf added the acknowledge-api-break Acknowledge that a change is API breaking and may be backwards-incompatible. Review carefully! label Jan 15, 2025
@github-actions

This comment has been minimized.

1 similar comment
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@ianbotsf ianbotsf marked this pull request as ready for review January 16, 2025 21:37
@ianbotsf ianbotsf requested a review from a team as a code owner January 16, 2025 21:37
delegator.applyFileWriter(symbol) {
dokka(
"""
A class which looks up specific endpoints for ${ctx.settings.sdkId} calls via the `$operationName`
Copy link
Contributor

Choose a reason for hiding this comment

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

Should ${ctx.settings.sdkId} be the sanitized ${clientName(ctx.settings.sdkId)}?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, good point.

RuntimeTypes.Core.Net.Host,
RuntimeTypes.Core.Collections.PeriodicSweepCache,
KotlinTypes.Time.minutes,
RuntimeTypes.Core.Clock,
Copy link
Contributor

Choose a reason for hiding this comment

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

Clock.System seems to be the default value here, so do we have to set it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, we don't.

val endpointDiscoveryOptional = ctx
companion object {
const val CLIENT_CONFIG_NAME = "endpointDiscoverer"
const val ORDER: Byte = 0 // doesn't depend on any other integrations
Copy link
Contributor

Choose a reason for hiding this comment

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

The default value is zero, so can this be removed?

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 Yes, I wanted to clearly document the relationship between the two integrations.

ctx: ProtocolGenerator.GenerationContext,
resolved: List<ProtocolMiddleware>,
): List<ProtocolMiddleware> = super.customizeMiddleware(ctx, resolved) + listOf(DiscoveredEndpointMiddleware)
): List<ProtocolMiddleware> = super.customizeMiddleware(ctx, resolved) + listOf(DiscoveredEndpointErrorMiddleware)
Copy link
Contributor

Choose a reason for hiding this comment

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

simplification: All other customizeMiddleware implementations just do resolved + listOf(<new middleware>), is the super call necessary? It looks to resolve to the same thing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Simplified!


override fun enabledForService(model: Model, settings: KotlinSettings): Boolean =
model.expectShape<ServiceShape>(settings.service).hasTrait<ClientEndpointDiscoveryTrait>()
override fun enabledForService(model: Model, settings: KotlinSettings): Boolean = isEnabledFor(model, settings)
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of repeating the integration's isEnabled check, can this just be true?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you mean by "repeating the integration's isEnabled check"? This method is the primary way the integration declares whether it's relevant for a particular service. If this were set to true then every service would get an endpointDiscoverer config parameter, endpoint error middleware, etc.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry I got this mixed up thinking it was the middleware's isEnabledFor function, you can ignore this

import software.amazon.smithy.kotlin.codegen.test.toSmithyModel

fun model() =
// language=smithy
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this give you proper syntax highlighting in the IDE? Doesn't seem to be working for me

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this just for our reference?

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It does (mostly) work for me. The IDE can't seem to find the Smithy prelude so it doesn't know what any of the traits mean but keywords and language constructs appear nicely formatted:

image

Note that Smithy syntax support is provided by the Smithy plugin for IntelliJ. If you don't have that installed in your IDE, it'll probably look like plain text.

* [invalidate] methods are `suspend` functions to allow for cross-context synchronization and potentially-expensive
* value lookup.
*
* Values in the cache _may_ expire and are retrieved as [ExpiringValue]. When a value absent/expired in the cache,
Copy link
Contributor

Choose a reason for hiding this comment

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

nit/typo: "When a value is absent/expired"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch!

* exceptions, fall back to other caches, etc.
* @param key The key for which to look up a value
* @param valueLookup A possibly-suspending function which returns the read-through value associated with a given
* key. This function is invoked when the cache, for a given key, does not contain a value or the value is expired.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: "for a given key" -> "for the given key"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right you are!

class DefaultEndpointDiscovererGeneratorTest {
@Test
fun testClass() {
val actual = render()
Copy link
Contributor

Choose a reason for hiding this comment

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

Question: Can the render function be called once at the class level instead of for each test? Same question applies to the EndpointDiscovererInterfaceGeneratorTest

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it can. Refactored.

import software.amazon.smithy.kotlin.codegen.test.toSmithyModel

fun model() =
// language=smithy
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this just for our reference?

@github-actions
Copy link

Affected Artifacts

Changed in size
Artifact Pull Request (bytes) Latest Release (bytes) Delta (bytes) Delta (percentage)
http-client-jvm.jar 318,966 317,120 1,846 0.58%
runtime-core-jvm.jar 819,667 818,677 990 0.12%
aws-signing-common-jvm.jar 71,184 71,597 -413 -0.58%

@ianbotsf ianbotsf merged commit 2e44922 into v1.5-main Jan 17, 2025
16 checks passed
@ianbotsf ianbotsf deleted the fix-overhaul-ep-discovery branch January 17, 2025 19:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

acknowledge-api-break Acknowledge that a change is API breaking and may be backwards-incompatible. Review carefully!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants