-
Notifications
You must be signed in to change notification settings - Fork 121
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
Conversation
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.
There was a problem hiding this 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 👍 👍
.add("configType", configType()) | ||
.add("target", target()) | ||
.add("configType", configType().getName()) | ||
.add("target", PluginTarget.LEADER_ONLY) |
There was a problem hiding this comment.
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?
.add("target", PluginTarget.LEADER_ONLY) | |
.add("target", target()) |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/MirrorRunner.java
Outdated
Show resolved
Hide resolved
if (zoneConfig != null) { | ||
String pinnedZone = m.zone(); | ||
if (pinnedZone == null) { | ||
// Use the first zone if the mirror does not specify a zone. |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. 😄
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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 PluginTarget
s (i.e. PluginA+LEADER_ONLY
, PluginA+ZONE_LEADER_ONLY
)
Co-authored-by: jrhee17 <guins_j@guins.org>
… into zone-aware-mirroring
There was a problem hiding this 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())); |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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"]
}
}
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
centraldogma/server/src/main/java/com/linecorp/centraldogma/server/CentralDogma.java
Lines 267 to 270 in 5f0a9a1
if (pluginsForZoneLeaderOnly != null) { | |
checkState(cfg.zone() != null, | |
"zone must be specified when zone leader plugins are enabled."); | |
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. 😉
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revert?
There was a problem hiding this comment.
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.
centraldogma/server/src/main/java/com/linecorp/centraldogma/server/CentralDogmaConfig.java
Lines 500 to 503 in 5f0a9a1
@JsonProperty | |
public boolean isWebAppEnabled() { | |
return webAppEnabled; | |
} |
Anyway, it is unrelated to this PR so I will revert it.
There was a problem hiding this comment.
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. 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 👍 👍
if (!allZones.contains(currentZone)) { | ||
throw new IllegalArgumentException("The current zone must be one of the all zones."); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
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); |
There was a problem hiding this comment.
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. 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 👍 👍
Motivation:
I propose running the mirror in a zone close to the git server or only in zones that are allowed access.
Modifications:
zone
as an optional property to mirror configurations.GET /api/v1/mirror/config
API to provide zone-related configurations.zonePinned
flag toMirroringServicePluginConfig
to enable zone-pinned mirroring.DefaultMirroringServicePlugin.target()
returnsPluginTarget.ZONE_LEADER_ONLY
ifzonePinned == true
MirrorSchedulingService
to only run a pinned zone ifzonePinned == true
ZoneConfig
to replacezone: string
configuration.allZones
is newly added to specify the list of zone names.Plugin.target()
takeCentralDogmaConfig
as an argument.Result:
You can now specify a zone where you want to perform mirroring.