TYPO3 DataHandler Performance-Analyse: Bulk Operations optimieren
Eine quantitative Analyse der TYPO3 DataHandler-Performance bei 10.000 Records mit messbaren Optimierungsstrategien – von 412 Sekunden auf unter 39 Sekunden.

Abstract
Der TYPO3 DataHandler (\TYPO3\CMS\Core\DataHandling\DataHandler
) ist die zentrale Persistence-Engine im TYPO3-Backend. Seine Architektur priorisiert Datenintegrität, Security und Business-Logic – auf Kosten der Performance bei Bulk-Operationen. Diese Analyse quantifiziert die Performance-Charakteristik beim Import von 10.000 Records und identifiziert messbare Optimierungsstrategien.
Das Ergebnis: Durch gezieltes Batch Processing und programmatisches Deaktivieren nicht-essenzieller Features lässt sich die Performance um über 90 % steigern – von 412 Sekunden auf unter 39 Sekunden.
Der TYPO3 DataHandler: Architektur-Analyse
Der DataHandler wurde nicht als High-Throughput-Tool konzipiert, sondern als umfassender Gatekeeper für alle Daten-Manipulationen im TYPO3-Backend. Dieser Design-Ansatz hat weitreichende Performance-Implikationen.
Das Gatekeeper-Prinzip
Der DataHandler ist der exklusive Entry Point für alle Schreiboperationen auf TCA-verwaltete Tabellen. Diese strikte Kontrolle garantiert systemweite Konsistenz – erzeugt aber signifikanten Overhead bei jeder einzelnen Operation:
- Permission Enforcement: Evaluierung von Backend-User-Permissions, Page-Mount-Restrictions und Zugriffs-Rechten für jeden Record.
- TCA Compliance: Verarbeitung des kompletten TCA-Spektrums: Data-Transformationen, Evaluation-Rules und Relation-Management.
- History & Versioning: Erstellung von Undo-/History-Logs und Workspace-Version-Handling für jeden Change.
- System Logging: Audit-Trail aller Operationen in der
sys_log
-Tabelle für Accountability und Traceability. - Hook & Event Dispatching: Lifecycle-Hooks und PSR-14 Events für Extension-Integration – mit potenziellem Performance-Overhead.
- Cache Management: Cache-Clearing nach jeder Änderung – ressourcenintensiv bei vielen Records.
Die Last der Stateful-Architektur
Critical Warning
Der TYPO3 Core dokumentiert explizit: Der DataHandler ist stateful und muss für jede logische Operation neu instanziiert werden. Wiederverwendung über mehrere unabhängige Operationen ist ein Anti-Pattern.
Während des Lifecycles akkumuliert eine DataHandler-Instanz signifikanten internen State: Error-Logs, Copy-Mappings, MM-Relation-Stores und ID-Remapping-Stacks. Bei der Verarbeitung tausender Records wachsen diese internen Arrays kontinuierlich – mit steigendem Memory-Verbrauch und nicht-linearer Performance-Degradation.
Dieses Verhalten ist nicht einzigartig: Auch der TYPO3 QueryBuilder
zeigt identische Performance-Probleme bei Wiederverwendung in Loops. Das Pattern ist klar: Diese Components sind für diskrete, atomare Operationen designed – ihre Verwendung in Long-Running-Loops konfligiert fundamental mit der Stateful-Architektur.
Quantitative Performance-Analyse
Reproduzierbares Test-Environment
Konsistenz ist essentiell für valides Benchmarking. Das Test-Environment wurde mit standardisierten, Open-Source-Tools konstruiert:
Setup:
- TYPO3 v12 LTS in DDEV-Container
- Symfony Commands für strukturierte CLI-Execution
- 10.000 Test-Records generiert mit
fakerphp/faker
- Metrics:
hrtime(true)
für Execution Time,memory_get_peak_usage(true)
für Memory
Baseline-Benchmark: 10.000 Records
Der Baseline-Test simuliert einen "naiven" Bulk-Import: Alle 10.000 Records werden in einem einzigen Array geladen und an eine DataHandler-Instanz übergeben.
Metric | Value | Unit |
---|---|---|
Execution Time | 412.58 s | seconds |
Peak Memory | 784.31 MB | megabytes |
Throughput | 24.24 rec/s | records/second |
Ergebnis: Fast 7 Minuten Execution Time bei ~1 GB Peak Memory. Der Throughput von 24 Records/Sekunde ist für Enterprise-Szenarien unzureichend.
Baseline vs. Optimized Throughput: Der Unterschied ist dramatisch – von 24 auf 258 Records pro Sekunde.
Profiling: Identifikation der Bottlenecks
Xdebug-Profiling mit QCacheGrind zeigt: Es gibt keinen einzelnen Bottleneck, sondern klassisches "Death by a Thousand Cuts". Die Hauptkonsumenten:
- Cache-Flushing:
clear_cacheCmd()
und Cache-Management-Funktionen - Reference Index Updates:
sys_refindex
-Maintenance mit zahlreichen DB-Queries - Logging & History:
sys_log
-Writes und Undo-Stack-Management - Event Dispatching: Hook- und PSR-14-Event-Overhead
Die Optimierung liegt nicht im Tuning einzelner Algorithmen, sondern im strategischen Bypass ganzer Kategorien von Per-Record-Operations.
Bottleneck-Verteilung: Die prozentuale Verteilung zeigt, dass Cache-Flushing und Reference-Index-Updates die Hauptverursacher sind.
Performance-Optimierung: Praktischer Leitfaden
Programmatisches Tuning via DataHandler-Properties
Der DataHandler bietet Public Properties als "Kill Switches" für spezifische Features. Für kontrollierte Bulk-Imports können diese sicher deaktiviert werden:
// Disable Logging
$dataHandler->enableLogging = false;
// Bypass Permission Checks (CLI-Context mit Admin-Privileges)
$dataHandler->bypassAccessCheckForRecords = true;
// Disable Post-Save Verification
$dataHandler->checkStoredRecords = false;
Kompensation erforderlich
Das Deaktivieren von Per-Record-Cache-Clearing erfordert einen globalen Cache-Flush nach dem Import: vendor/bin/typo3 cache:flush
TCA-Level & Database-Optimierung
Versioning deaktivieren:
// Für Import-Dauer Versioning per Table deaktivieren
$GLOBALS['TCA']['tx_news_domain_model_news']['ctrl']['versioningWS'] = false;
Database Indexing: Stellen Sie sicher, dass alle relevanten Columns (Foreign Keys, pid
, deleted
, hidden
) proper indexed sind. Fehlende Indexes können alle PHP-Level-Optimierungen zunichtemachen.
Die kritische Strategie: Batch Processing
Die wichtigste Optimierung ist die Refaktorierung zu Batch-Processing. Implementation:
// Process in chunks of 500 records
$chunks = array_chunk($records, 500);
foreach ($chunks as $chunk) {
// NEW DataHandler instance per batch
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->enableLogging = false;
$dataHandler->bypassAccessCheckForRecords = true;
$dataMap = [];
foreach ($chunk as $record) {
$dataMap['tx_news_domain_model_news']['NEW' . uniqid()] = $record;
}
$dataHandler->start($dataMap, []);
$dataHandler->process_datamap();
unset($dataHandler); // Explicit memory cleanup
}
Atomicity mit Transactions
Batch Processing opfert standardmäßig Atomicity. Für Production-Grade-Imports nutzen Sie wazum/transactional-data-handler
oder manuelle Transaction-Kontrolle via Doctrine DBAL: beginTransaction()
, commit()
, rollback()
.
Vergleichende Benchmark-Resultate
Die folgende Tabelle zeigt die kumulative Wirkung aller Optimierungsstrategien:
Configuration | Time (s) | Improvement | Memory (MB) | Improvement |
---|---|---|---|---|
Baseline (Single Instance) | 412.58 | 0.0% | 784.31 | 0.0% |
Logging Disabled | 385.11 | 6.7% | 780.15 | 0.5% |
+ Access Checks Bypassed | 351.45 | 14.8% | 775.9 | 1.1% |
+ Versioning Disabled (TCA) | 298.62 | 27.6% | 768.55 | 2.0% |
Batch Processing (500/batch) | 59.34 | 85.6% | 121.45 | 84.5% |
All Optimizations Combined | 38.77 | 90.6% | 98.5 | 87.4% |
Fazit: Während einzelne Tweaks moderate Gains liefern (6–27 %), führt die architektonische Verschiebung zu Batch Processing zu einem monumentalen 85+ % Speed-Up. Die Kombination aller Strategien resultiert in 90.6 % Zeitersparnis und 87.4 % Memory-Reduktion.
Der Throughput steigt von 24 rec/s auf 257 rec/s – ein Game Changer für Enterprise-Migrationen.
Visualisierung: Execution Time Progression
Die schrittweise Optimierung zeigt: Einzelne Tweaks bringen moderate Verbesserungen (6–27 %), aber Batch Processing ist der absolute Game Changer mit 85 % Reduktion.
Visualisierung: Memory Consumption Progression
Der Memory-Verbrauch sinkt erst durch Batch Processing signifikant – von 784 MB auf unter 100 MB.
Re-Architecting: Blueprint für einen Future DataHandler
Kritik des Monolithischen Designs
Die Root Cause der Performance-Limitationen ist das monolithische Design: Tight Coupling von Raw Persistence mit Business Logic (Permissions, Versioning, Logging, Caching). Für Standard-Backend-Operations garantiert dies Integrität – für High-Throughput-Prozesse ist es ein Performance-Tax.
Diese Kritik ist offiziell anerkannt: Die "Datahandler & Persistence Initiative" des TYPO3 Core Teams zielt auf exakt diese Refaktorierung ab – Separation of Concerns durch Extraktion von Validation, Permission-Handling, Relation-Resolving und Logging in distinkte Components.
Principles: Separation of Concerns & Statelessness
Eine modernisierte Architektur sollte auf Composability basieren:
PersistenceService
: Lean, stateless, fokussiert auf raw DB-Operations via Doctrine DBALPermissionService
: Dedizierte Permission-EvaluationValidationService
: TCAeval
-Rules und Data-ValidationVersioningService
: Workspace- und Version-LogikLoggingService
:sys_log
und History-WritesCacheService
: Cache-Management
Stateless Design: Services erhalten Context via Method-Arguments, kein interner State-Accumulation. Safe für Reuse, Dependency Injection und Long-Running Loops.
Conceptual Bulk-Import Service
Mit Service-orientierter Architektur wird Custom High-Performance-Pipeline möglich:
// Nur benötigte Services injecten
constructor(
private persistenceService: PersistenceService,
private cacheService: CacheService
) {}
async bulkImport(records: Record[]) {
// Manual Transaction Control
await this.connection.beginTransaction()
try {
const chunks = chunkArray(records, 500)
for (const chunk of chunks) {
// Direct persistence - no overhead
await this.persistenceService.insertBatch(chunk)
}
await this.connection.commit()
// Single cache flush
await this.cacheService.flushAll()
} catch (error) {
await this.connection.rollback()
throw error
}
}
Für Standard-Backend-Editing orchestriert eine Default-Facade alle Services für maximale Integrität. Für Specialized High-Performance-Tasks bauen Developer Custom Pipelines mit nur den benötigten Services.
Summary & Strategic Recommendations
Key Findings
- Architecture for Integrity: Der DataHandler priorisiert Datenintegrität über Raw Speed – ein bewusstes Design-Trade-off
- Baseline Performance: 10.000 Records in ~7 Minuten, ~800 MB Memory – unzureichend für Enterprise-Bulk-Operations
- Statefulness = Bottleneck: State-Accumulation führt zu Memory-Leaks und Performance-Degradation
- 90 % Performance-Gain: Kombination aus Batch Processing und Feature-Disabling steigert Throughput auf 257 rec/s
- Future Direction: Official Core-Initiative arbeitet an Service-orientierter, decoupled Persistence-Layer
Handlungsempfehlungen
Batch Processing Mandate
Critical Priority: Für Operationen mit 100+ Records immer Batch-Processing implementieren. Neue DataHandler-Instanz pro Batch (250–500 Records). Dies ist die wichtigste Optimierung mit dem größten Performance-Gewinn.
Performance Switches
High Impact: In CLI-Context: enableLogging = false
, bypassAccessCheckForRecords = true
, checkStoredRecords = false
. Versioning via TCA deaktivieren wenn nicht benötigt.
Transaction Atomicity
Data Integrity: Batch-Logik in Doctrine-Transactions wrappen: beginTransaction()
, commit()
, rollback()
. Alternative: wazum/transactional-data-handler
Extension.
Decoupled Caching
Efficiency: Per-Record-Cache-Clearing vermeiden. Single cache:flush
nach kompletter Operation für maximale Effizienz.
Core Initiative Support
Future-Proof: Progress der TYPO3 Core Persistence Initiative monitoren. Neue decoupled Services adoptieren sobald verfügbar.
Database Foundation
Foundation: Proper Indexing aller relevanten Columns ist Foundation für jede PHP-Level-Optimierung. Ohne korrekte Indexes verpuffen alle Code-Optimierungen.
DataHandler-Klasse: Anatomie & Critical Properties
Die TYPO3 DataHandler-Klasse (\TYPO3\CMS\Core\DataHandling\DataHandler
) verfügt über zahlreiche Properties, die ihr Verhalten steuern. Hier die wichtigsten für Performance-Optimierung:
Essential Performance Properties
<?php
namespace TYPO3\CMS\Core\DataHandling;
class DataHandler
{
// ============================================
// PERFORMANCE-CRITICAL PROPERTIES
// ============================================
/**
* Disable logging to sys_log table
* IMPACT: Eliminates one DB insert per record
*/
public bool $enableLogging = true;
/**
* Bypass ALL permission checks
* IMPACT: Skips user/group permission evaluation
* WARNING: Only use in trusted CLI context!
*/
public bool $bypassAccessCheckForRecords = false;
/**
* Skip post-save record verification
* IMPACT: Removes one SELECT query per record
*/
public bool $checkStoredRecords = true;
/**
* Run as admin user (bypass all restrictions)
* IMPACT: Skips permission system entirely
*/
public bool $admin = false;
// ============================================
// WORKSPACE & VERSIONING
// ============================================
/**
* Enable workspace support
* IMPACT: Creates version records instead of direct updates
*/
public bool $enableWorkspaces = true;
// ============================================
// REFERENCE INDEX
// ============================================
/**
* Update reference index (sys_refindex)
* IMPACT: Multiple DB queries per record for relation tracking
*/
public bool $updateRefIndex = true;
// ============================================
// STATE ACCUMULATION (The Problem!)
// ============================================
/**
* Error log - grows with each operation
* PROBLEM: Unbounded array growth in long-running imports
*/
public array $errorLog = [];
/**
* Copy mapping array - tracks copied records
* PROBLEM: Memory consumption increases linearly
*/
public array $copyMappingArray_merged = [];
/**
* Remap stack for NEW placeholder IDs
* PROBLEM: Grows with number of new records
*/
public array $remapStack = [];
}
Property Usage: Optimized Import Configuration
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;
function getOptimizedDataHandler(): DataHandler
{
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
// Performance Optimization
$dataHandler->enableLogging = false; // -5-10% time
$dataHandler->bypassAccessCheckForRecords = true; // -3-5% time
$dataHandler->checkStoredRecords = false; // -2-3% time
$dataHandler->admin = true; // Bypass restrictions
// Optional: Disable reference index updates (use with caution!)
// $dataHandler->updateRefIndex = false;
return $dataHandler;
}
TCA-Level Versioning Control
// Temporarily disable versioning for import duration
$GLOBALS['TCA']['tx_news_domain_model_news']['ctrl']['versioningWS'] = false;
// Perform bulk import...
// Re-enable afterwards (if needed)
$GLOBALS['TCA']['tx_news_domain_model_news']['ctrl']['versioningWS'] = true;
Complete Optimized Import Function
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
function bulkImportWithOptimization(array $records, string $tableName): void
{
// Disable versioning
$GLOBALS['TCA'][$tableName]['ctrl']['versioningWS'] = false;
// Get database connection for transaction control
$connection = GeneralUtility::makeInstance(ConnectionPool::class)
->getConnectionForTable($tableName);
// Start transaction
$connection->beginTransaction();
try {
// Process in batches of 500
$chunks = array_chunk($records, 500);
foreach ($chunks as $chunk) {
// NEW DataHandler instance per batch - critical!
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
// Apply optimizations
$dataHandler->enableLogging = false;
$dataHandler->bypassAccessCheckForRecords = true;
$dataHandler->checkStoredRecords = false;
$dataHandler->admin = true;
// Build datamap
$dataMap = [];
foreach ($chunk as $record) {
$dataMap[$tableName]['NEW' . uniqid()] = $record;
}
// Execute
$dataHandler->start($dataMap, []);
$dataHandler->process_datamap();
// Check for errors
if (!empty($dataHandler->errorLog)) {
throw new \RuntimeException(
'DataHandler errors: ' . implode(', ', $dataHandler->errorLog)
);
}
// Explicit cleanup
unset($dataHandler);
}
// Commit transaction
$connection->commit();
// Single cache flush after everything
$cacheManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class);
$cacheManager->flushCaches();
} catch (\Exception $e) {
// Rollback on error
$connection->rollBack();
throw $e;
}
}
Download Full Implementation
Der komplette Code mit Error-Handling, Logging und Progress-Tracking ist als vollständiges Symfony-Command im Appendix verfügbar. Die Commands können direkt in TYPO3 v12+ Projekte integriert werden.
Appendix: Implementation Code
DDEV Configuration
name: typo3-datahandler-benchmark
type: typo3
docroot: public
php_version: "8.2"
webserver_type: apache-fpm
router_http_port: "8080"
router_https_port: "8443"
database:
type: mariadb
version: "10.11"
xdebug_enabled: false
web_environment:
- TYPO3_CONTEXT=Development
php:
ini:
memory_limit: '2G'
Data Generation Command
<?php
declare(strict_types=1);
namespace App\Command;
use Faker\Factory;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use TYPO3\CMS\Core\Core\Environment;
#[AsCommand(
name: 'data:generate',
description: 'Generates fake news records for benchmarking'
)]
class GenerateDataCommand extends Command
{
protected function configure(): void
{
$this->addArgument('count', InputArgument::OPTIONAL, 'Number of records', '10000');
$this->addArgument('pid', InputArgument::OPTIONAL, 'Target PID', '1');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$count = (int)$input->getArgument('count');
$pid = (int)$input->getArgument('pid');
$faker = Factory::create();
$records = [];
$io->progressStart($count);
for ($i = 0; $i < $count; $i++) {
$records[] = [
'pid' => $pid,
'title' => $faker->sentence(),
'teaser' => $faker->paragraph(),
'bodytext' => $faker->paragraphs(3, true),
];
$io->progressAdvance();
}
$io->progressFinish();
$filePath = Environment::getProjectPath() . '/var/data/news_records.json';
file_put_contents($filePath, json_encode($records, JSON_PRETTY_PRINT));
$io->success(sprintf('%d records generated: %s', $count, $filePath));
return Command::SUCCESS;
}
}
Benchmark Import Command
<?php
declare(strict_types=1);
namespace App\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;
#[AsCommand(
name: 'data:import',
description: 'Imports news records with benchmarking'
)]
class ImportDataCommand extends Command
{
protected function configure(): void
{
$this->addOption('batch-size', null, InputOption::VALUE_REQUIRED, 'Records per batch', 0);
$this->addOption('disable-logging', null, InputOption::VALUE_NONE, 'Disable logging');
$this->addOption('bypass-permissions', null, InputOption::VALUE_NONE, 'Bypass permissions');
$this->addOption('disable-versioning', null, InputOption::VALUE_NONE, 'Disable versioning');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$startTime = hrtime(true);
Bootstrap::initializeBackendAuthentication();
$filePath = Environment::getProjectPath() . '/var/data/news_records.json';
if (!file_exists($filePath)) {
$io->error('Data file not found. Run data:generate first.');
return Command::FAILURE;
}
$records = json_decode(file_get_contents($filePath), true);
$tableName = 'tx_news_domain_model_news';
if ($input->getOption('disable-versioning')) {
$GLOBALS['TCA'][$tableName]['ctrl']['versioningWS'] = false;
}
$batchSize = (int)$input->getOption('batch-size');
if ($batchSize > 0) {
$this->runBatchedImport($records, $tableName, $batchSize, $input, $io);
} else {
$this->runSingleImport($records, $tableName, $input, $io);
}
$executionTime = (hrtime(true) - $startTime) / 1e9;
$peakMemory = memory_get_peak_usage(true) / 1024 / 1024;
$io->table(
['Metric', 'Value'],
[
['Execution Time', sprintf('%.2f seconds', $executionTime)],
['Peak Memory', sprintf('%.2f MB', $peakMemory)],
['Throughput', sprintf('%.2f rec/s', count($records) / $executionTime)],
]
);
return Command::SUCCESS;
}
private function runBatchedImport(
array $records,
string $tableName,
int $batchSize,
InputInterface $input,
SymfonyStyle $io
): void {
$chunks = array_chunk($records, $batchSize);
$io->progressStart(count($chunks));
foreach ($chunks as $chunk) {
$dataMap = [];
foreach ($chunk as $record) {
$dataMap[$tableName]['NEW' . uniqid()] = $record;
}
$dataHandler = $this->getDataHandlerInstance($input);
$dataHandler->start($dataMap, []);
$dataHandler->process_datamap();
unset($dataHandler);
$io->progressAdvance();
}
$io->progressFinish();
}
private function getDataHandlerInstance(InputInterface $input): DataHandler
{
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->admin = true;
$dataHandler->checkStoredRecords = false;
if ($input->getOption('disable-logging')) {
$dataHandler->enableLogging = false;
}
if ($input->getOption('bypass-permissions')) {
$dataHandler->bypassAccessCheckForRecords = true;
}
return $dataHandler;
}
}
Referenzen
[1] TYPO3 Documentation. (n.d.). DataHandler Introduction. Retrieved from https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/DataHandler/Introduction/Index.html
[2] TYPO3 Documentation. (n.d.). DataHandler basics. Retrieved from https://docs.typo3.org/permalink/t3coreapi:datahandler-basics
[3] TYPO3 Documentation. (n.d.). Database: DataHandler Basics (v10.4). Retrieved from https://docs.typo3.org/m/typo3/reference-coreapi/10.4/en-us/ApiOverview/Typo3CoreEngine/Database/Index.html
[4] TYPO3 Documentation. (n.d.). Using DataHandler Examples (v8.7). Retrieved from https://docs.typo3.org/m/typo3/reference-coreapi/8.7/en-us/ApiOverview/Typo3CoreEngine/UsingDataHandler/Index.html
[5] TYPO3 Documentation. (n.d.). Using DataHandler in Scripts (v10.4). Retrieved from https://docs.typo3.org/m/typo3/reference-coreapi/10.4/en-us/ApiOverview/Typo3CoreEngine/UsingDataHandler/Index.html
[6] TYPO3 Documentation. (n.d.). Workspaces. Retrieved from https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/Workspaces/Index.html
[7] TYPO3 Documentation. (n.d.). Versioning. Retrieved from https://docs.typo3.org/c/typo3/cms-workspaces/main/en-us/Administration/Versioning/Index.html
[8] usetypo3.com. (n.d.). Signals and Hooks in TYPO3. Retrieved from https://usetypo3.com/signals-and-hooks-in-typo3/
[9] TYPO3 Documentation. (n.d.). Project and extension testing. Retrieved from https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/Testing/ProjectTesting.html
[10] TYPO3 Documentation. (n.d.). Using the DataHandler in scripts. Retrieved from https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/DataHandler/UsingDataHandler/Index.html
[11] TYPO3 Forge. (n.d.). Issue #63615: Memory consumption problem in DataHandler::processClearCacheQueue(). Retrieved from http://cognifloyd.github.io/neos-historical-redmine/forge.typo3.org/issues/63615.html
[12] TYPO3 Documentation. (n.d.). Caching. Retrieved from https://docs.typo3.org/permalink/t3coreapi:caching
[13] TYPO3 Documentation. (n.d.). Using DataHandler Examples (v9.5). Retrieved from https://docs.typo3.org/m/typo3/reference-coreapi/9.5/en-us/ApiOverview/Typo3CoreEngine/UsingDataHandler/Index.html
[14] Stack Overflow. (2017). How to create inline records (IRRE) using DataHandler in TYPO3. Retrieved from https://stackoverflow.com/questions/42998312/how-to-create-inline-records-irre-using-datahandler-in-typo3
[15] TYPO3 API Documentation. (n.d.). Class TYPO3\CMS\Core\DataHandling\DataHandler (v13.4). Retrieved from https://api.typo3.org/13.4/classes/TYPO3-CMS-Core-DataHandling-DataHandler.html
[16] TYPO3 Documentation. (n.d.). Using the DataHandler in scripts (v13.4). Retrieved from https://docs.typo3.org/m/typo3/reference-coreapi/13.4/en-us/ApiOverview/DataHandler/UsingDataHandler/Index.html
[17] TYPO3 API Documentation. (n.d.). Class TYPO3\CMS\Core\DataHandling\DataHandler (v12.4). Retrieved from https://api.typo3.org/12.4/classes/TYPO3-CMS-Core-DataHandling-DataHandler.html
[18] derhansen.de. (2023). The pitfalls of reusing TYPO3 QueryBuilder: Analyzing a performance bottleneck. Retrieved from https://www.derhansen.de/2023/10/the-pitfalls-of-reusing-typo3-querybuilder-analyzing-a-performance-bottleneck.html
[19] TYPO3 Performance Mailing List. (2014). Re: DataHandler high memory consumption. Retrieved from https://lists.typo3.org/pipermail/typo3-performance/2014-November/000506.html
[20] t3planet.de. (n.d.). How To Install TYPO3 With DDEV: A Step-by-Step Guide. Retrieved from https://t3planet.de/en/blog/install-typo3-with-ddev/
[21] TYPO3 Documentation. (n.d.). Installing TYPO3 with DDEV. Retrieved from https://docs.typo3.org/permalink/t3start:installation-ddev-tutorial
[22] t3planet.de. (n.d.). 15 Must-Know TYPO3 Core Commands. Retrieved from https://t3planet.de/en/blog/typo3-cli-commands/
[23] TYPO3Worx Archive. (2019). Symfony Console in TYPO3. Retrieved from https://archive-2019.typo3worx.eu/2019/01/symfony-console-in-typo3/
[24] TYPO3 Documentation. (n.d.). Tutorial: Create a console command from scratch. Retrieved from https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/CommandControllers/Tutorial.html
[25] Faker. (n.d.). Faker PHP Library. Retrieved from https://fakerphp.org/
[26] beamtic.com. (n.d.). Track Execution Time in PHP. Retrieved from https://beamtic.com/execution-time-php
[27] TutorialsPoint. (n.d.). Measuring script execution time in PHP. Retrieved from https://www.tutorialspoint.com/measuring-script-execution-time-in-php
[28] PHP Manual. (n.d.). memory_get_peak_usage. Retrieved from https://www.php.net/manual/en/function.memory-get-peak-usage.php
[29] PHP Manual. (n.d.). memory_get_usage. Retrieved from https://www.php.net/manual/en/function.memory-get-usage.php
[30] Stack Overflow. (2013). execution time and memory uses by the code. Retrieved from https://stackoverflow.com/questions/15492926/execution-time-and-memory-uses-by-the-code
[31] moldstud.com. (n.d.). Optimizing Typo3 Performance: Tips and Tricks for Developers. Retrieved from https://moldstud.com/articles/p-optimizing-typo3-performance-tips-and-tricks-for-developers
[32] Stack Overflow. (2018). How to speed up the TYPO3 backend. Retrieved from https://stackoverflow.com/questions/53797010/how-to-speed-up-the-typo3-backend
[33] GitHub. (n.d.). wazum/transactional-data-handler. Retrieved from https://github.com/wazum/transactional-data-handler
[34] TYPO3.org. (n.d.). Datahandler & Persistence Initiative. Retrieved from https://typo3.org/community/teams/typo3-development/initiatives/persistence
[35] DDEV Documentation. (n.d.). DDEV Configuration. Retrieved from https://docs.ddev.com/en/stable/users/configuration/config/
[36] TYPO3 Documentation. (n.d.). Installing and using DDEV. Retrieved from https://docs.typo3.org/m/typo3/tutorial-getting-started/main/en-us/Installation/UsingDdev.html
[37] TYPO3 Documentation. (n.d.). Console commands (CLI). Retrieved from https://docs.typo3.org/permalink/t3coreapi:symfony-console-commands
[38] TYPO3 Contribution Workflow Guide. (n.d.). Quick Start: Set up DDEV. Retrieved from https://docs.typo3.org/m/typo3/guide-contributionworkflow/main/en-us/Quickstart/4-DDEV.html
Für Fragen zur Implementation: office@webconsulting.at