Skip to content

feat(updates): Multi-Tenant Update Scheduling (v1.8.0, Issue #262)#4205

Open
Copilot wants to merge 2 commits intodevelopfrom
copilot/multi-tenant-update-scheduling
Open

feat(updates): Multi-Tenant Update Scheduling (v1.8.0, Issue #262)#4205
Copilot wants to merge 2 commits intodevelopfrom
copilot/multi-tenant-update-scheduling

Conversation

Copy link
Contributor

Copilot AI commented Mar 13, 2026

Implements per-tenant update scheduling for the updates module — maintenance windows, blackout periods, priority tiers, consent management, and per-tenant rollback.

Description

Core Implementation

  • include/updates/tenant_update_scheduler.hTenantUpdateScheduler class with types: MaintenanceWindow, BlackoutPeriod, UpdatePolicy, UpdatePriority (CRITICAL/NORMAL/LOW), TenantUpdateStatus
  • src/updates/tenant_update_scheduler.cpp — Full production implementation with cross-midnight window support, injectable clock for testability, thread-safe via internal mutex

Key scheduling rules

  1. Active blackout → blocks all updates (CRITICAL + critical_auto_update bypasses)
  2. Outside maintenance window → blocks (same bypass applies)
  3. notification_lead_time not elapsed → blocks
  4. auto_update=false and no explicit consent → blocks
TenantUpdateScheduler scheduler;

scheduler.setMaintenanceWindow("tenant-123", {
    .days = {"Saturday", "Sunday"},
    .start_time = "02:00", .end_time = "06:00",
    .timezone = "America/New_York"
});
scheduler.setUpdatePolicy("tenant-123", {
    .auto_update = false,
    .critical_auto_update = true,
    .notification_lead_time = std::chrono::hours(24)
});

if (scheduler.canUpdateNow("tenant-123")) {
    engine->applyHotReload("1.5.0");
} else {
    auto next = scheduler.getNextMaintenanceWindow("tenant-123");
    // "2026-03-14T02:00:00Z"
}

Per-tenant rollback stores the rollback_id from applyUpdate() and calls HotReloadEngine::rollback() via rollbackTenant().

Build / Test / CI

  • cmake/CMakeLists.txt + cmake/ModularBuild.cmake — source registered in themis_core
  • tests/test_multi_tenant_update_scheduling.cpp — 37 focused tests across all 5 ACs
  • tests/CMakeLists.txtMultiTenantUpdateSchedulingFocusedTests target added
  • .github/workflows/multi-tenant-update-scheduling-ci.yml — CI on ubuntu-22.04/24.04

Type of Change

  • Bug fix
  • New feature
  • Refactoring
  • Documentation
  • Other:

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed

📚 Research & Knowledge (wenn applicable)

  • Diese PR basiert auf wissenschaftlichen Paper(s) oder Best Practices?
    • Falls JA: Research-Dateien in /docs/research/ angelegt?
    • Falls JA: Im Modul-README unter "Wissenschaftliche Grundlagen" verlinkt?
    • Falls JA: In /docs/research/implementation_influence/ eingetragen?

Relevante Quellen:

  • Paper:
  • Best Practice:
  • Architecture Decision:

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Documentation updated (if needed)
  • No new warnings introduced
Original prompt

This section details on the original issue you should resolve

<issue_title>Multi-Tenant Update Scheduling</issue_title>
<issue_description>### Context

This issue implements the roadmap item 'Multi-Tenant Update Scheduling' for the updates domain. It is sourced from the consolidated roadmap under 🟢 Low Priority — Future (v1.9.0+) and targets milestone v1.8.0.

Primary detail section: Multi-Tenant Update Scheduling

Goal

Deliver the scoped changes for Multi-Tenant Update Scheduling in src/updates/ and complete the linked detail section in a release-ready state for v1.8.0.

Detailed Scope

Multi-Tenant Update Scheduling

Priority: Low
Target Version: v1.8.0

Per-tenant update schedules and maintenance windows.

Features:

  • Tenant-specific maintenance windows
  • Update blackout periods
  • Priority tiers (critical, normal, low)
  • Tenant consent for updates
  • Rollback per tenant

Configuration:

TenantUpdateScheduler scheduler;

// Configure tenant maintenance windows
scheduler.setMaintenanceWindow("tenant-123", {
    .days = {"Saturday", "Sunday"},
    .time_range = {"02:00", "06:00"},
    .timezone = "America/New_York"
});

scheduler.setMaintenanceWindow("tenant-456", {
    .days = {"Daily"},
    .time_range = {"23:00", "05:00"},
    .timezone = "Europe/London"
});

// Set update policy
scheduler.setUpdatePolicy("tenant-123", {
    .auto_update = false,           // Require manual approval
    .critical_auto_update = true,   // Auto-apply critical updates
    .notification_lead_time = std::chrono::hours(24)
});

// Check if update can be applied now
if (scheduler.canUpdateNow("tenant-123")) {
    engine->applyHotReload("1.5.0");
} else {
    auto next_window = scheduler.getNextMaintenanceWindow("tenant-123");
    LOG_INFO("Next maintenance window: {}", next_window);
}

Acceptance Criteria

  • Tenant-specific maintenance windows
  • Update blackout periods
  • Priority tiers (critical, normal, low)
  • Tenant consent for updates
  • Rollback per tenant

Relationships

References

  • src/ROADMAP.md
  • src/updates/FUTURE_ENHANCEMENTS.md#multi-tenant-update-scheduling
  • Source key: roadmap:262:updates:v1.8.0:multi-tenant-update-scheduling

Generated from the consolidated source roadmap. Keep the roadmap and issue in sync when scope changes.

</issue_description>

Comments on the Issue (you are @copilot in this section)


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

…#262)"

Co-authored-by: makr-code <150588092+makr-code@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement multi-tenant update scheduling feature feat(updates): Multi-Tenant Update Scheduling (v1.8.0, Issue #262) Mar 13, 2026
Copilot AI requested a review from makr-code March 13, 2026 20:19
@makr-code makr-code marked this pull request as ready for review March 14, 2026 06:13
@makr-code makr-code requested a review from Copilot March 14, 2026 06:13
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new updates-domain component (TenantUpdateScheduler) to support per-tenant maintenance windows, blackout periods, priority-based bypass rules, consent/notification lead-time gating, and per-tenant rollback tracking, with focused unit tests and CI wiring.

Changes:

  • Introduces TenantUpdateScheduler public API + implementation for per-tenant scheduling/consent/rollback.
  • Adds 37 focused unit tests and a dedicated focused test target.
  • Registers sources in monolithic/modular builds and adds a dedicated GitHub Actions workflow.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
include/updates/tenant_update_scheduler.h Public API/types and documentation for multi-tenant update scheduling.
src/updates/tenant_update_scheduler.cpp Core scheduling logic, consent management, next-window calculation, apply/rollback bookkeeping.
tests/test_multi_tenant_update_scheduling.cpp Focused unit tests covering AC1–AC5 plus status/lifecycle and construction.
tests/CMakeLists.txt Adds focused test executable + CTest registration for this feature.
cmake/CMakeLists.txt Registers scheduler source in monolithic themis_core build.
cmake/ModularBuild.cmake Registers scheduler source in modular build source lists.
.github/workflows/multi-tenant-update-scheduling-ci.yml Adds targeted CI workflow running the focused test and unified binary filters.
src/updates/ROADMAP.md Marks the roadmap item as completed for v1.8.0.
src/updates/FUTURE_ENHANCEMENTS.md Marks feature as implemented and links implementation files/tests/CI.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +75 to +88
/**
* @brief Priority tier for an update.
*
* Determines scheduling and consent rules:
* - CRITICAL : may override blackout periods when
* `UpdatePolicy::critical_auto_update` is true.
* - NORMAL : follows regular maintenance windows.
* - LOW : deferred to the next available window; never auto-applied.
*/
enum class UpdatePriority {
CRITICAL, ///< Security patches and data-integrity fixes.
NORMAL, ///< Feature releases and minor patches.
LOW, ///< Optional enhancements; applied only when explicitly approved.
};
const std::string& blackout_id);

/**
* @brief Return all active blackout periods for a tenant.
with:
cc: ${{ matrix.cc }}
cxx: ${{ matrix.cxx }}
extra-packages: libssl-dev libcurl4-openssl-dev libboost-all-dev librocksdb-dev libfmt-dev libtbb-dev libspdlog-dev nlohmann-json3-dev libexpected-dev
Comment on lines +355 to +365
// Build the candidate time_point: midnight of (now + offset days)
// plus start_min minutes.
const std::time_t candidate_t =
now_t
- static_cast<std::time_t>(current_min_of_day * 60) // back to midnight
+ static_cast<std::time_t>(offset * 24 * 3600) // advance days
+ static_cast<std::time_t>(start_min * 60); // add start time

const auto candidate_tp =
std::chrono::system_clock::from_time_t(candidate_t);

Comment on lines +327 to +336
auto dayAllowed = [&](int wday) -> bool {
for (const auto& d : win.days) {
std::string dlower = d;
for (auto& c : dlower) c = static_cast<char>(std::tolower(c));
if (dlower == "daily") {
return true;
}
std::string kday = kDayNames[wday];
for (auto& c : kday) c = static_cast<char>(std::tolower(c));
if (dlower == kday) {
Comment on lines +397 to +409
auto& state = tenants_[tenant_id];
if (result.success) {
state.current_version = version;
state.last_rollback_id = result.rollback_id;
state.consent_granted = false; // reset consent after successful apply
state.pending_version.clear();
state.notification_time.reset();
LOG_INFO("TenantUpdateScheduler: applied version '{}' for tenant '{}' "
"(rollback_id='{}')",
version, tenant_id, result.rollback_id);
} else {
LOG_WARN("TenantUpdateScheduler: engine failed to apply '{}' for '{}': {}",
version, tenant_id, result.error_message);
Comment on lines +239 to +240
* After removal, `canUpdateNow()` returns false for the tenant unless
* an override is granted via `grantConsent()`.
Comment on lines +535 to +556
// Day-of-week check (tm_wday: 0=Sunday).
static const char* const kDayNames[] = {
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
};
bool day_ok = false;
for (const auto& d : win.days) {
std::string dlower = d;
for (auto& c : dlower) c = static_cast<char>(std::tolower(c));
if (dlower == "daily") {
day_ok = true;
break;
}
std::string kday = kDayNames[utc.tm_wday];
for (auto& c : kday) c = static_cast<char>(std::tolower(c));
if (dlower == kday) {
day_ok = true;
break;
}
}
if (!day_ok) {
return false;
tenant_id, version);
return r;
}

*
* scheduler.setMaintenanceWindow("tenant-123", {
* .days = {"Saturday", "Sunday"},
* .time_range = {"02:00", "06:00"},
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.

Multi-Tenant Update Scheduling

3 participants