Çekirdek Yükseltmesini Atlatma

1.6k görüntülenme Markdown

Yükseltme koşusunun adım adım ne yaptığı, neyi geri alabildiği ve öncesinde ile sonrasında yapılacak kontroller.

Genel Bakış

Bir yükseltme, zaman bütçesi olan bir adımlar dizisi ve üzerine yazdığı her şeyin defteri. Son yazma ile sürüm damgası arasında bir sağlık kapısı durur.

Ön Koşullar

  • Bozabileceğiniz ikinci bir sistem. Müşterilere hizmet veren sistem ya da onunla aynı veritabanını kullanan bir kopya olmasın.
  • Koşudan önce alınmış ve doğrulanmış gerçek bir yedek.
  • Başlamadan önce not edilmiş, coremio/VERSION dosyasındaki güncel sürüm.
  • Çekirdeğe Dokunmadan Çalışma: aşağıdaki her kontrol, kodunuzun bir genişleme noktasının arkasında olduğunu varsayar.

Yapı

Koşu

Bir sürüm, sabit sırayla beş adım. Zincirli bir yükseltme her sürümü tam ve sırayla, bir öncekinin koduyla uygular.

AdımNe yaparGeri alınabilir mi?
backupİsteğe bağlı. Hiçbir şeye dokunulmadan önce veritabanı, dosyalar ve yüklemelerGeçerli değil, yalnız okur
downloadPaketi çalışma dizinine indirirEvet, çalışma dizininin dışında hiçbir şey değişmedi
extractDilim başına bir grup girdi olacak şekilde paketi açarEvet, aynı sebeple
databaseSürümün şema ifadelerini bir kez koşturur. Zaten uygulanmış ifadeler atlanır, sırası gelmemişler dosyanın sonunda tekrarlanırHayır. İleri yönlü olarak kaydedilir ve operatöre adıyla söylenir
configurationSürümün yapılandırma betiğini koşturur, sonra dil dosyalarını ve bildirim şablonlarını birleştirirBirleştirmeler evet, betik hayır: keyfi kodun kopyası alınamaz
applyPaketin dosyalarını sistemin üzerine kopyalar, sonra silme bildirimi, sağlık kapısı ve sürüm damgasıEvet, defterden, bir kez
finishÇalışma dizinini süpürürGeçerli değil

Her adım zaman bütçesi dolana kadar çalışır, nereye geldiğini bir imlece yazar ve döner. Bir web isteği, yapılandırılmış çalışma süresi sınırının 5 ile 45 saniye arasına kısılmış yüzde altmışını alır. Dakikalık cron'un kullandığı komut satırı dilimi 300 saniye alır. Cron işleyicisi kendi 1800 saniyelik tavanının 1440. saniyesinden sonra yeni dilim başlatmaz.

Koşu Öncesi Kopya Defteri

coremio/storage/updates-preimage altında koşu başına bir dizin. Çalışma dizininin dışında durur, çünkü koşudan uzun yaşar. Her üzerine yazma ve bildirimle yapılan her silme önce buraya kopyalanır. Saklanan kopya koşu öncesi hâli taşır.

Girdiİçinde ne varOnu ne okur
meta.jsonGeldiği sürüm, uygulanan sürümler, yeniden uygulama olup olmadığı, öncesindeki durumSunulup sunulmayacağına karar veren geri alma butonu
files/Üzerine yazılan ya da silinen her dosyanın koşu öncesi kopyasıHepsini geri kopyalayan geri yükleme
added.listKoşunun oluşturduğu, daha önce var olmayan yollarOnları silen geri yükleme (bir eklemeyi geri almak kaldırmaktır)
changed.listİçeriği gerçekten değişen dosyalar, sağlama toplamına göreTam olarak bu listeyi ve başka hiçbir şeyi lint'leyen sağlık kapısı
forward.listGeri alınamayanlar: şema migration'ı, sürümün yapılandırma betiğiOnları operatöre adıyla söyleyen geri alma özeti
baseline.json, gate.json, restored.jsonÖncesindeki ve sonrasındaki sağlık ölçümü, bir geri yüklemenin kanıtıKapının ne gördüğünü bilmeniz gerektiğinde siz

Sağlık Kapısı

Sağlık iki kez ölçülür: uygulama adımının ilk baytı yazılmadan önce bir taban ölçüm, sonra kapı. Yargı göreli olduğu için taban ölçüm zaten bozuksa hiçbir şey geri alınmaz.

Ucuzdan pahalıya üç soru sorulur. Değişen PHP dosyaları hâlâ ayrıştırılıyor mu? Taze bir süreç açılıp veritabanına ulaşıyor mu? Sistem kendi anasayfasını cevaplıyor mu?

AlanAnlamıKapıya etkisi
errorsBir kontrol koştu ve düştüGeri almayı tetikler ve koşu sağlık kontrolü hatasıyla düşer
skippedBir kontrol hiç koşamadıYargı yok. Kanıt dosyasına yazılır, dışında dikkate alınmaz
httpBoolean değil bir durum: ok, ulaşılamadı ya da hataTaban ölçümle karşılaştırılır, inanılmadan önce ikinci kez sorulur

Adım Adım

Öncesinde

  1. Bağımlılık listenizi hafızaya değil bir dosyaya yazın. Dinlediğiniz her kanca adı. Kendi dizininizin dışından çağırdığınız her sınıf ve metot. Okuduğunuz her çekirdek tablosu, kolonu ve yapılandırma anahtarı.
  2. Kendi ağacınızda çekirdek yollarını arayın ve hiçbirini düzenlemediğinizi doğrulayın. Defter sizin sürümünüzü değil bir öncekini geri getirir.
  3. Modülünüz güncellenebilir olacaksa bir manifest.json taşıdığını kontrol edin. Manifest yoksa güncelleyici o dizine hiç dokunmaz.
  4. Yükseltmenin tamamını önce kopyada, hem cron'dan hem panelden çalıştırın.

Sonrasında

  1. Tek bir sayfa açmadan önce uyumluluk probunuzu çalıştırın. Çağırdığınız her sınıf ve metot hâlâ var mı?
  2. Bağımlı olduğunuz her kancayı çalıştırın ve kanaryanızın bir isabet kaydettiğini doğrulayın. Kanca adlarının çalışma zamanı kaydı yoktur.
  3. Hata günlüğünü okuyun. Bir dinleyicinin içindeki istisna yakalanır, loglanır ve yutulur. Bozuk bir dinleyici hiç ateşlenmemiş bir kancaya benzer.
  4. Kodunuzun okuduğu yapılandırma anahtarlarını kontrol edin. Uygulama adımı var olan bir yapılandırma dosyasını atlar, modül güncelleyici de config.php dosyanızı korur. Yeni anahtarlar sizin için oluşturulmaz.
  5. Modülünüz tablo sahibiyse ya da kolon eklediyse kendi şema kapınızı yeniden çalıştırın. Çekirdek yükseltmesi onlardan haberdar değil.
  6. Operatöre geri alma penceresinin bir koşu ve bir kez olduğunu söyleyin.

Bir Genişleme Noktası Yer Değiştirdiğinde

BelirtiKontrolGenellikle şu demektir
Dinleyiciniz artık çalışmıyor ve hiçbir şey loglanmıyorKanca kataloğunda önce adı, sonra yakın adları arayınKanca yeniden adlandırıldı ya da tetiklendiği nokta kaldırıldı. Kaydınız hâlâ geçerli ve hiçbir şeye bağlı
Dinleyiciniz çalışıyor ama değişiklik dikkate alınmıyorNoktanın değerle mi referansla mı ateşlendiğiKanca referanslı olmuş. Dönüş değeriniz atılıyor, çağıran argümanın yerinde değiştirilmesini bekliyor
Ölümcül hata: tanımsız metot çağrısıLoga değil kaynağa bakınSınıf adını korumuş, metot korumamış. Çağrıyı kapıya alın, hata fırlatmak yerine yeteneği kapatın
Çok az ya da çok fazla argümanKancanın sayfasındaki parametre tablosuTetiklendiği noktanın argüman listesi değişti. Yalnız kullandığınız parametreleri tanımlayın, kalanına varsayılan verin
Admin sayfanız bulunamadı dönüyorRotalardan önce modül tipiSessizce reddedilen bir admin alanı kaydı. Ya da bir çekirdek rotasıyla çakışan bir slug
Cron'dan çalışıyor, panelden ölüyorHer iki bağlamda function_exists('exec')Yükseltme regresyonu değildir. Paylaşımlı hosting, süreç fonksiyonlarını web havuzunda kapatırken komut satırı ikilisinde açık tutar

Kendi Güncellemenizi Yayınlama

  1. Modül ya da tema dizinine bir manifest.json koyun.
  2. Sürümü orada tutun, başka hiçbir yerde. config.php içindeki sürüm ilk kurulumda donar, çünkü bir güncelleme o dosyayı hiç yazmaz.
  3. Yayımladıktan sonra bir sürüm numarasını asla yeniden kullanmayın ya da düzenlemeyin. Her sistemin karşılaştırdığı kimlik odur.
  4. Müşterinin diskinde, göndermediğiniz iki şey yaşamaya devam eder. Zaten var olan her yerde atlanan config.php dosyanız. Bir de paketten çıkardığınız her dosya: bir güncelleme üzerine yazar ama silmez.
  5. Kendi araçlarınızda da manifest'i en son yazın. Yarım kalmış bir uygulama, diskte tam olmayan bir sürümü asla iddia etmemeli.

Referans

Yükseltme API'si

coremio/helpers/UpdateRunner.php
const VERSION_STEPS  = ['download', 'extract', 'database', 'configuration', 'apply'];
const PREIMAGE_DIR   = 'updates-preimage';   // coremio/storage/ altında
const PREIMAGE_KEEP  = 3;                    // saklanan koşu; yeni koşu açılınca budanır
const LINT_SECONDS   = 60.0;                 // kapı lint bütçesi, dosya sayısı değil süre
const LINT_MAX_FILES = 2000;                 // süre bütçesinin arkasındaki emniyet freni
const HTTP_SETTLE_SECONDS = 5;               // sormadan önce ve geri yükledikten sonra beklenir

public static function open(array $opts, string $workerId): int;
public static function tick(int $runId, string $workerId): array;
public static function health_probe(): array;
public static function restorable(): array;
public static function restore_last(): array;
public static function parked(array $run): bool;
coremio/helpers/updates.php (seçilmiş)
public static function check_state(): array;
public static function check_new_version(): array;
public static function next_versions(string|int $ver = 0): array;
public static function run_active(string $kind = 'core'): array;
public static function last_completed_run(string $kind = 'core'): array;
public static function run_get(int $id): array;
public static function sc_state(): array;

// coremio/helpers/license.php - sistemin şu an kendini hangi sürüm saydığı.
public static function version($realtime = false): string;
UpdateRunner::health_probe() Aynı üç sorunun bir yükseltmenin dışında sorulmuş hâli; değişen dosya listesi boş olduğu için lint geçişi kendini atlar. ['ok' => bool, 'errors' => string[], 'skipped' => string[], 'http' => string] döner. Kendi probunuzun kopyalayacağı şekil budur.
UpdateRunner::restorable() Geri alınabilecek koşuyu ya da boş bir dizi döner. Aktif koşu varken, tamamlanmış koşu yokken, defterde meta veri yokken ya da bir geri yükleme zaten yapılmışken boştur. Geri alma koşu başına bir kez sunulur.
Updates::run_active() Bir yükseltme sürerken boş olmayan bir dizi. Kendi zamanlanmış işinizin başında kontrol etmeye değer: süren bir koşu, dosya ağacının karışık olduğu anlamına gelir.
License::version() Koşucunun en son yazdığı damgayı okur, yani yalnız tamamlanmış bir sürümü bildirir. version_compare() ile karşılaştırın: metin olarak iki basamaklı bir ara sürüm, tek basamaklının altında sıralanır.

Güncelleme Manifesti

Modül sınıfının yanında, temada ise tema tanımının yerine değil yanına konur.

manifest.json
{"type":"marketplace","name":"AcmeBilling","version":"1.2.0","last_updated":"2026-07-28"}
AlanZorunluNe yapar
typeHer zamanBu dizinin sorumlusu hangi yayıncı. Bilinmeyen bir değer manifest'i yarım okutmak yerine tümüyle geçersiz kılar
nameMağaza ürünlerindeÜrün anahtarı. Dizin adı kullanılmaz; onu siz seçersiniz
idMarketplace ilanlarındaİlan kimliği, aynı sebeple: iki geliştirici aynı dizin adını seçebilir
versionHer zamanKurulu sürüm. Karşılaştırmayı yalnız ve yalnız bu yapar
last_updatedİsteğe bağlıOperatöre gösterilir. Hiçbir kararda yer almaz
Tespit otomatiktir, kurulum değil

Günlük görev yeni sürüm arar ve operatöre bildirim gönderir. Modül güncellemesi kuran zamanlanmış bir görev yok.

Örnek

Modülünüzün gönderebileceği bir uyumluluk probu.

coremio/modules/Addons/Acme/src/Compat.php
namespace WISECP\Modules\Addons\Acme\Src;

class Compat
{
    // Bağımlılık yüzeyi, veri olarak. Bu modülün kendi dizini dışında dokunduğu her şey
    // burada listelenir; böylece prob, yeniden yazım değil listenin kendisi olur.
    private const NEEDS_METHOD = [
        'Checkout::payment_methods',
        'Services::get',
        'Invoices::create',
    ];

    private const NEEDS_HOOK = [
        'filter:order.cart_totals',
        'action:order.checkout_completed',
    ];

    private const CORE_MIN = '5.0.0';
    private const CORE_MAX = '6.0.0';   // hariç: ana sürüm sıçraması tercihe bağlıdır, varsayılmaz

    /** @return array{ok:bool,errors:string[],skipped:string[],version:string} */
    public static function check(): array
    {
        $errors = $skipped = [];
        $core   = \License::version();

        if (version_compare($core, self::CORE_MIN, '<'))
            $errors[] = 'core ' . $core . ' is below the minimum ' . self::CORE_MIN;

        if (version_compare($core, self::CORE_MAX, '>='))
            $skipped[] = 'core ' . $core . ' is newer than this module was tested against';

        foreach (self::NEEDS_METHOD as $ref) {
            [$class, $method] = explode('::', $ref, 2);
            if (!class_exists($class))            $errors[] = 'missing class: ' . $class;
            elseif (!method_exists($class, $method)) $errors[] = 'missing method: ' . $ref;
        }

        // Sonuç değil, yetenek sorusu. FPM altında bunlar genelde kapalıdır ama aynı
        // sunucunun CLI'ı onları açık tutar; "soramadım" asla "düştü" diye okunmamalı.
        if (!function_exists('exec')) $skipped[] = 'subprocess probes (exec unavailable)';

        foreach (self::NEEDS_HOOK as $hook)
            if (!self::hook_seen($hook)) $skipped[] = 'hook not observed yet: ' . $hook;

        return ['ok' => !$errors, 'errors' => $errors, 'skipped' => $skipped, 'version' => $core];
    }

    /*
     * Kanca ADLARININ çalışma zamanı kaydı yoktur: kancalar beyan edilmez, ateşlenir ve
     * deposu özeldir. Dolayısıyla tek dürüst cevap gözleme dayanır: aşağıdaki kanarya,
     * nokta gerçekten ateşlendiğinde bir zaman damgası kaydeder ve "görülmedi" atlanandır,
     * asla hata değil, çünkü o noktaya henüz hiç ulaşılmamış olabilir.
     */
    public static function canary(string $hook): void
    {
        $seen = (array) \Utility::jdecode((string) \Config::getd('acme_hook_seen'), true);
        $seen[$hook] = time();

        \Config::setd('acme_hook_seen', \Utility::jencode($seen));
    }

    private static function hook_seen(string $hook): bool
    {
        $seen = (array) \Utility::jdecode((string) \Config::getd('acme_hook_seen'), true);

        return (int) ($seen[$hook] ?? 0) > 0;
    }
}

Kanarya, izlediği dinleyicinin yanına ve onu öne alan bir öncelikle kaydedilir.

coremio/modules/Addons/Acme/hooks.php
use WISECP\Modules\Addons\Acme\Src\Compat;

include_once __DIR__ . DS . 'src' . DS . 'Compat.php';

// Öncelik 1: asıl dinleyicinin önünde, böylece gözlem ondan bağımsız olur.
Hook::add('filter:order.cart_totals', 1, function (&$summary, $items, $subtotal) {
    Compat::canary('filter:order.cart_totals');
});

// Modülün kendisinin arkasında durduğu kapı. Uyumsuz bir çekirdek, birinin ödeme
// akışının içinde hata fırlatmak yerine özelliği devre dışı bırakır.
$acme_compat = Compat::check();

if ($acme_compat['ok']) {
    Hook::add('filter:order.cart_totals', 20, function (&$summary, $items, $subtotal) {
        // ... asıl iş
    });
}
else {
    Hook::add('ui:admin.body.end', 90, function () use ($acme_compat) {
        return '<!-- acme disabled: ' . htmlspecialchars(implode('; ', $acme_compat['errors'])) . ' -->';
    });
}

Koşu biter bitmez kopya üzerinde elle çalıştırmaya değer iki komut.

yükseltme sonrası kontroller
# Kapının gerçekte ne gördüğü, ölçemedikleri dahil.
cat coremio/storage/updates-preimage/*/gate.json

# Koşudan bu yana çıkan hatalar; yutulmuş bir dinleyici istisnasının tek izi burasıdır.
php coremio/errlog.php list --limit=20

Tuzaklar

Cron'dan geçer, panelden ölümcül hata verir

Paylaşımlı hosting havuzları süreç fonksiyonlarını web yapılandırmasında kapatır, aynı sunucudaki komut satırı ikilisi ise açık tutar. Kapatılmış bir fonksiyon uyarı değil hata üretir, susturma operatörü de işe yaramaz. Alt süreç açmadan önce yeteneği kontrol edin. Eksik yetenek düştü değil atlandı sayılır.

Dosyalar geri döner, şema dönmez

Defteri geri yüklemek üzerine yazılan her dosyayı geri koyar ve koşunun oluşturduğu her şeyi siler. Ama migration şemayı çoktan taşımış, yapılandırma betiği çoktan çalışmıştır. Modülünüz bir çekirdek kolonunu okuyorsa, sürüm numarasını değil kolonun varlığını kontrol edin.

Yarı uygulanmış çekirdek gerçek bir durum

Dosya ağacı dakikalarca iki sürümün karışımı olabilir. O pencerede başlayan zamanlanmış bir iş, bir sınıfı eski sürümden diğerini yenisinden yükleyebilir.

Geri alma bir koşu, bir kez ve sonuncusu için

Buton yalnız en son tamamlanmış koşu için ve hiçbir koşu sürmezken görünür. Bir kez kullanıldığında kaybolur. Daha eskisi tam yedeğin işi.

Faydalı oldu mu?

Geri bildiriminiz için teşekkürler!

Hâlâ Yardıma mı İhtiyacınız Var?

Yukarıda bulamadığınız her şey için destek ekibimiz her zaman yanınızda.