İçeriğe geç

Yeniden oluşturma (Rebuild) sistemi

Bazı veriler başka verilerden türetilir: bir kategorideki yazı sayısı, bir bölümün konu sayısı, bir sayfanın kayıtlı HTML çıktısı, bir arama dizini. Türetilmiş veri zamanla bozulabilir: bir içe aktarma, elle yapılan SQL, bir kütüphane yükseltmesi ya da yarıda kalmış bir işlem sayaçları yanlış bırakır. Rebuild sistemi bu veriyi kaynağından yeniden hesaplar.

Kural

İçerik üreten her modül, yeniden oluşturma sistemine kendini dahil etmelidir.

Çekirdek, hiç duymadığı bir tabloyu yeniden sayamaz. Forum modülü rebuilder kaydetmezse Rebuild ekranı forum konularını ve sayaçlarını düzeltemez. Bu yüzden kural bir tercih değil, modülün sözleşmesinin parçasıdır.

Kuralın iki yolu vardır:

  1. Bir rebuilder kaydetmek (aşağıda), ya da
  2. Neden gerekmediğini söylemek: modülün türetilmiş verisi yoksa module.json içine yazın:
"rebuild": {
    "exempt": "Notes keeps no derived counters or indexes; every value is stored as the user wrote it."
}

Bu kural cp:doctor tarafından denetlenir: varlığı (Entity/ klasöründe sınıfı) olan etkin bir modül ne bir rebuilder kaydediyor ne de muafiyet bildiriyorsa rebuild.module_uncovered uyarısı çıkar. Uyarı hangi modülün eksik olduğunu adıyla söyler.

php cp-core/bin/console cp:doctor

Nasıl çalışır

RebuilderInterface uygulayan her sınıf cpalius.rebuilder etiketini otomatik alır; modülün services.yaml'ında yalnızca sınıfın bulunduğu klasörün tanımlı olması yeter (Modules\Foo\Rebuild\: resource: '../../Rebuild/'). RebuilderRegistry hepsini kimliğine göre toplar; aynı kimlik iki kez kayıtlıysa bu bir hatadır, çünkü iki iş aynı rotayı ve ilerleme durumunu ezerdi. Pasif modülün rebuilder'ı konteynerde yoktur, liste kendiliğinden küçülür.

İş parti parti çalışır. Tarayıcıdaki koşucu her istekte rebuild($offset, $limit) çağırır; dönen sayı $limit'ten küçükse iş biter. Bu, ucuz barındırmada PHP zaman aşımına takılmadan milyonlarca satırı işlemenin yoludur.

İki ekran vardır:

Ekran Adres Neyi gösterir
Studio → Yeniden oluştur /admin/tools/rebuild Site masası: içerik sayaçları (isStudioVisible() = true olanlar)
AACP → Bakım → Yeniden oluştur /aacp/maintenance/rebuild Platform masası: Core önbellekleri dahil tüm işler; tek tek ya da "hepsini" sırayla

"Hepsini yeniden oluştur" işleri getPriority() sırasıyla çalıştırır: önce içerik sayaçları, en sonda Core önbellekleri.

Rebuilder yazmak

namespace Modules\Foo\Rebuild;

use App\Core\Rebuild\RebuilderInterface;
use Doctrine\ORM\EntityManagerInterface;

final class FooCountRebuilder implements RebuilderInterface
{
    public function __construct(private readonly EntityManagerInterface $em) {}

    public function getId(): string { return 'foo.counts'; }              // <modül>.<ne>; yayınlandıktan sonra asla değişmez
    public function getName(): string { return 'foo.rebuild.counts'; }    // çeviri anahtarı
    public function getDescription(): string { return 'foo.rebuild.counts_desc'; }
    public function getBatchSize(): int { return 100; }                   // bir istekte işlenecek satır
    public function getPriority(): int { return 50; }                     // küçük önce
    public function isStudioVisible(): bool { return true; }

    public function getTotal(): int
    {
        return (int) $this->em->getRepository(Foo::class)->count([]);
    }

    public function rebuild(int $offset, int $limit): int
    {
        $rows = $this->em->getRepository(Foo::class)->findBy([], ['id' => 'ASC'], $limit, $offset);
        foreach ($rows as $row) {
            // türetilmiş değeri kaynağından yeniden hesapla
        }
        $this->em->flush();
        $this->em->clear();          // ZORUNLU: uzun çalışma tüm tabloyu belleğe doldurmasın

        return \count($rows);        // ziyaret edilen satır, işe yaramayanlar dahil
    }
}

Kurallar:

  • COUNT(*) yazma yolunda değil, burada yapılır. Yazı kaydedilirken sayaç için tabloyu saymayın; sayacı artırın/azaltın ve rebuilder'ı doğrulayıcı olarak kullanın.
  • Idempotent olun. İki kez çalıştırmak aynı sonucu vermeli.
  • Her parti hızlı dönmeli (getBatchSize()'ı küçük tutun).
  • getTotal() ucuz olmalı; yalnızca yüzde çubuğu içindir.
  • Offset'e güvenin, durum tutmayın. Koşucu her istekte sıfırdan çağırır.
  • Çeviri anahtarlarını modülün messages+intl-icu.<dil>.yaml dosyasına ekleyin.

Örnekler

Modül Rebuilder Ne yeniden hesaplanır
Blog blog.categories Her kategorideki yayınlanmış yazı sayısı
Forum forum.topics, forum.sections, forum.user_stats, ACL temizliği Konu/mesaj sayaçları, bölüm sayaçları, üye istatistikleri
Belgeler docs.render Her sayfanın kayıtlı HTML'i ve içindekiler listesi (Markdown kaynağından)
Core core.caches Çekirdek önbellekleri

Sonraki: Hook, cron, kuyruk.

Son güncelleme: 13.10.2026