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

Provide a way to pin a mirror to a zone #1062

Merged
merged 10 commits into from
Dec 5, 2024
Merged

Conversation

ikhoon
Copy link
Contributor

@ikhoon ikhoon commented Nov 18, 2024

Motivation:

I propose running the mirror in a zone close to the git server or only in zones that are allowed access.

Modifications:

  • Add zone as an optional property to mirror configurations.
  • Add GET /api/v1/mirror/config API to provide zone-related configurations.
    • The API is used to select a pinned zone on the mirror form.
  • Add zonePinned flag to MirroringServicePluginConfig to enable zone-pinned mirroring.
    • The option is disabled by default.
    • DefaultMirroringServicePlugin.target() returns PluginTarget.ZONE_LEADER_ONLY if zonePinned == true
    • Fixed MirrorSchedulingService to only run a pinned zone if zonePinned == true
  • Add ZoneConfig to replace zone: string configuration.
    • allZones is newly added to specify the list of zone names.
  • Breaking) Make Plugin.target() take CentralDogmaConfig as an argument.

Result:

You can now specify a zone where you want to perform mirroring.

Motivation:

We propse to run mirror in a zone close to the git server or only in
zones that are allowed access.

Modifications:

- Add `zone` as an optional property to mirror configurations.
- Add `GET /api/v1/mirror/config` API to provide zone related configurations.
  - The API is used to select a pinned zone on the mirror form.
- Add `zonePinned` flag to `MirroringServicePluginConfig` to enable
  zone-pinned mirroring.
  - The option is disabled by default.
  - `DefaultMirroringServicePlugin.target()` returns
    `PluginTarget.ZONE_LEADER_ONLY` if `zonePinned == true`
  - Fixed `MirrorSchedulingService` to only run a pinned zone if
    `zonePinned == true`
- Add `ZoneConfig` to replace `zone: string` configuration.
  - `allZones` is newly added to specify the list of zone names.
- Breaking) Make `Plugin.target()` take `CentralDogmaConfig` as an
  argument.

Result:

You can now specify a zone where you want to perform mirroring.
@ikhoon ikhoon marked this pull request as draft November 18, 2024 10:20
@ikhoon ikhoon added this to the 0.71.0 milestone Nov 18, 2024
@ikhoon ikhoon marked this pull request as ready for review November 19, 2024 02:17
Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

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

Left only nits but looks good to me 👍 👍

site/src/sphinx/setup-configuration.rst Outdated Show resolved Hide resolved
.add("configType", configType())
.add("target", target())
.add("configType", configType().getName())
.add("target", PluginTarget.LEADER_ONLY)
Copy link
Contributor

Choose a reason for hiding this comment

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

Question) doesn't this plugin target all replicas?

Suggested change
.add("target", PluginTarget.LEADER_ONLY)
.add("target", target())

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops... 😱

.add("target", target())
.omitNullValues()
.add("configType", configType().getName())
.add("target", pluginTarget)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit; target is null unless #target(CentralDogmaConfig) is called. Not sure if this is intentional or not.

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 was intentional and I thought it was safe because target(CentralDogmaConfig) is called right after the plugin is initialized. pluginTarget may be not null for most of the object's lifecycle.

if (zoneConfig != null) {
String pinnedZone = m.zone();
if (pinnedZone == null) {
// Use the first zone if the mirror does not specify a zone.
Copy link
Contributor

Choose a reason for hiding this comment

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

Noted that the order of allZones matters

pinnedZone = zoneConfig.allZones().get(0);
}
if (!pinnedZone.equals(currentZone)) {
// Skip the mirror if the zone does not match.
Copy link
Contributor

Choose a reason for hiding this comment

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

Question) Would it be worth checking if the pinnedZone is contained in ZoneConfig#allZones to detect ill-configured mirrors?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Most users may choose the pinnedZone from the UI's select box, but it also makes sense to validate on the server side.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, let me rephrase my concern.

For instance, if servers are deployed in JP1, JP2, JP3 and we decide to migrate (JP3 -> JP4), is there no need for us to be aware of failing mirror jobs? I think moving servers across zones is not such a rare occasion.

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 makes more sense. Let me report a failure to MirrorListener.

Copy link
Contributor

@minwoox minwoox Nov 29, 2024

Choose a reason for hiding this comment

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

Note that I also want to generalize the mirror listener so that it also reports when a k8s aggregator fails.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are you planning to design a general listener to receive arbitrary events?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. 😄

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 sounds nice. I'm looking forward to the Central Dogma event bus. 🚌

@Nullable
static PluginGroup loadPlugins(ClassLoader classLoader, PluginTarget target, CentralDogmaConfig config,
List<Plugin> plugins) {
static Map<PluginTarget, PluginGroup> loadPlugins(ClassLoader classLoader, CentralDogmaConfig config,
Copy link
Contributor

Choose a reason for hiding this comment

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

Note: Understood that with this change, we don't allow the same plugin to be added with different PluginTargets (i.e. PluginA+LEADER_ONLY, PluginA+ZONE_LEADER_ONLY)

Copy link
Contributor

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

Left some nits. Looks great. 👍

pluginGroups.forEach((target, group) -> {
logger.debug("Loaded plugins for target {}: {}", target,
group.plugins().stream().map(plugin -> plugin.getClass().getName())
.collect(Collectors.toList()));
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: toImmutableList()

* Creates a new instance.
*/
@JsonCreator
public ZoneConfig(@JsonProperty("currentZone") String currentZone,
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably better to remove Zone suffix?

{
  ...
  "zone": {
    "current": "foo",
    "all": ["foo", "bar"]
  }
}

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's possible, but I don't think all conveys the exact meaning, so I'd like to keep the current expression.

if (mirroringServicePluginConfig.zonePinned()) {
zoneConfig = cfg.zone();
if (zoneConfig == null) {
throw new IllegalStateException(
Copy link
Contributor

Choose a reason for hiding this comment

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

How about throwing this exception a little bit earlier? e.g. in target method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

zoneConfig won't be null because it was checked in

if (pluginsForZoneLeaderOnly != null) {
checkState(cfg.zone() != null,
"zone must be specified when zone leader plugins are enabled.");
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just added this logic for double checking.

Copy link
Contributor

Choose a reason for hiding this comment

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

Then, how about using assert? We use assert for this purpose. 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

}

/**
* Returns the number of mirroring threads.
*/
@JsonProperty
@JsonProperty("numMirroringThreads")
Copy link
Contributor

Choose a reason for hiding this comment

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

revert?

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 looks like a boilerplate but I prefer this pattern because it is more deterministic.
We also use a standard getter for boolean values for which is is dropped. I wasn't sure if that was intended or not.

@JsonProperty
public boolean isWebAppEnabled() {
return webAppEnabled;
}

Anyway, it is unrelated to this PR so I will revert it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, we're currently using it in a mixed manner, and it might be a good time to define a standard. 🤔

site/src/sphinx/mirroring.rst Outdated Show resolved Hide resolved
Copy link
Contributor

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

👍 👍 👍

Comment on lines 48 to 50
if (!allZones.contains(currentZone)) {
throw new IllegalArgumentException("The current zone must be one of the all zones.");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit:

Suggested change
if (!allZones.contains(currentZone)) {
throw new IllegalArgumentException("The current zone must be one of the all zones.");
}
checkArgument(allZones.contains(currentZone), "The current zone: %s, (expected: one of %s)",
currentZone, allZones);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was lazy. I used the code generated by GitHub Copilot as is. 😅

Copy link
Contributor

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

👍 👍 👍

@minwoox minwoox modified the milestones: 0.71.0, 0.72.0 Dec 4, 2024
@ikhoon ikhoon merged commit 3a261ba into line:main Dec 5, 2024
10 checks passed
@ikhoon ikhoon deleted the zone-aware-mirroring branch December 5, 2024 08:05
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.

3 participants