lolli / dbhealth
Requires
- php: ^7.4 || ^8.0
- typo3/cms-core: ^11.5 || ^12.4
Requires (Dev)
- bnf/phpstan-psr-container: ^1.0
- codeception/codeception: ^4.1 || ^5.0.0
- codeception/module-asserts: ^2.0 || ^3.0.0
- codeception/module-cli: ^1.1 || ^2.0.0
- friendsofphp/php-cs-fixer: ^3.52.0
- friendsoftypo3/phpstan-typo3: ^0.9.0
- phpstan/phpstan: ^1.4.6
- phpstan/phpstan-phpunit: ^1.0
- phpunit/phpunit: ^9
- typo3/cms-impexp: ^11.5 || ^12.4
- typo3/cms-redirects: ^11.5 || ^12.4
- typo3/cms-workspaces: ^11.5 || ^12.4
- typo3/testing-framework: ^7.0.2
README
TYPO3 DB doctor
任务
此扩展的任务是查找在活着的 TYPO3 实例中可能随时间引入的数据库不一致性,并修复它们。
例如,当编辑删除页面树时,有时大多数页面被正确设置为已删除,但有些页面被遗漏,或者一个页面上某个内容元素没有被删除。这导致数据库中出现孤立页面或内容元素。
有许多原因会导致出现上述无效数据库状态:一般而言,TYPO3 对数据库表没有参照完整性约束,不一致性可能由一个即将结束的 PHP 进程、丢失的 DB 连接、核心错误、有缺陷的扩展、损坏的部署等原因触发。经过多次核心版本升级的长期活跃实例往往会出现一些不再完全正确的情况。
这种不一致性可能导致进一步的问题。例如,如果复制了一个具有孤立本地化记录的页面,系统往往会弄乱复制页面的本地化。然后编辑会遇到问题,TYPO3 代理机构需要进行耗时的调试会话来找出问题所在。
此扩展提供了一个 CLI 命令,该命令试图查找各种此类不一致性,并给管理员提供修复选项。
替代方案
我们不知道有其他开源扩展以类似系统的这种方式尝试实现同样的目标。核心 lowlevel
扩展包含了一些尝试清理各种数据库状态的命令,但其代码库相当陈旧且难以维护。
此扩展不是 lowlevel
命令的替代品(目前还不是),它更像是一个孵化器,以查看某种处理不一致性的策略在项目中是否真正有效。它将随着时间的推移而增长。也许它最终会进入核心,或者核心将来会将此扩展作为“维护”扩展。我们将拭目以待。
策略
此命令的策略是逐个检查单个事项,并在进行下一项检查之前修复它们。对不正常记录的更新和删除直接使用低级数据库查询完成,而不使用 DataHandler。
单个检查是精心设计的,并经过功能测试,它们的执行顺序很重要。可能会在链中多次运行单个检查。
单个检查更倾向于避免内存消耗和假设状态,以牺牲更多的查询执行为代价。查询通常作为预处理语句执行,以便在单个检查中多次重用。当单个检查完成后,语句会被适当地关闭,有效地使用 PHP 垃圾回收。总的来说,此命令即使对于大型实例也应该相对快速,但它将对数据库产生大量压力。
对前端渲染的影响
当健康检查发现可疑之处时,dbdoctor 只允许一种硬编码的解决方案来处理它。用户不会被要求选择解决方案,它要么接受提出的更新或删除数据库更改,要么需要中止并手动处理(然后重新启动)。
在dbdoctor中实现每条记录的问答功能是不可行的,因为这会给系统增加一个正交的复杂维度,使其很快变得难以维护:单次检查被设计为相互叠加工作,dbdoctor需要建立一个“正确性链”来完成其工作。
通常有三种针对特定“修复”方法的选择
- 硬删除受影响的记录
- 对于软删除感知表,将记录设置为
deleted=1
- 将记录更新为“更正确”的内容
一般策略是尽量减少TYPO3 前端渲染 视角造成的损害。
例如,当在特定语言中,默认语言的记录有两个本地化版本时,dbdoctor会将其检测为无效,并建议将其中一个设置为 deleted=1
。在两个记录中,它将尝试设置在Frontend中通常 不 渲染的那个已删除的记录。
尽管这种一般策略与上述例子一样简单,但实际上并不总是这样:由于TYPO3前端渲染非常灵活,实际渲染的记录有时取决于dbdoctor无法知晓的具体前端渲染细节。在这些情况下,dbdoctor会尝试造成尽可能少的损害。这不一定总是符合实际情况。处理这种情况的唯一解决方案是逐个查看单个记录更改建议。交互式选项 p
、d
和 s
希望有助于对单个建议的更改进行分类。
限制
尽管这个低级工具试图非常小心,并在提出更改建议之前检查很多细节,但仍有一些限制和假设:例如,软删除感知TCA表的“删除”列被 假设 为整数列,而不是文本或varchar或类似类型。只要在ext_tables.sql
文件中没有明确定义,这个列的正确模式通常由核心创建。然而,如果扩展定义了这样的字段并出错,dbdoctor可能会通过建议删除或更新所有行来造成危险。
还有一些假设:例如,dbdoctor假设核心为标准表提供的某些TCA设置(尤其是pages
、tt_content
和 sys_file_reference
)没有被扩展更改。例如,这些表被假设既支持软删除,又支持工作空间,相关字段将由dbdoctor查询这些表,如果扩展篡改了相应的TCA ctrl
设置,dbdoctor将会失败。
还有dbdoctor无法处理的场景:例如,假设某个扩展通过TCA条目 ['ctrl']['delete'] = 'deleted'
声明了一个软删除感知的表,并且你有某些 deleted=1
的行。后来,该TCA表被设置为不再软删除感知,通过移除['ctrl']['delete']
声明。然后,核心数据库分析器将建议首先将 deleted
列重命名为 zzz_deleted_deleted
,然后允许删除该列。这样做将有效地将之前已删除的所有记录“激活”,当你忘记在之前删除所有受影响的 deleted=1
记录时。当TCA表被更改为不再工作空间感知,但你仍然有工作空间相关的记录在表中,或者当TCA表不再“starttime”/“endtime”感知且表中包含有时间记录时,也会有类似的场景。
dbdoctor始终在当前的TCA状态下工作。它不知道某个TCA表之前是否被定义为“软删除感知”,以及之后是否有所改变。当您通过删除“已删除”列、删除“工作区”扩展、相关工作区列或与时间相关的字段来将记录推送到生产环境时,这可能导致无法修复的状态,dbdoctor将无法修复。相反,它倾向于找到更多损坏的数据库关系,并建议使情况比之前更糟的更改。此外,dbdoctor永远不会查看可能存在的zzz_deleted
列——从dbdoctor的角度来看,这些列不存在,因为它们依赖于某些“之前”的TCA状态,该状态无法重建。由上述情况创建的状态不可修复,需要手动重建。祝你好运。
总的来说,在dbdoctor工作之前,TCA和扩展的ext_tables.sql
应该处于良好状态,并且应该手动检查健康检查建议的更改,然后再将其提交到数据库。另外,永远别忘了备份数据库,以备不时之需。不要盲目接受dbdoctor的建议!
当前状态
已发布第一个版本,但我们还没有足够的信心发布1.0.0版本。此扩展的本质是执行潜在的恶意查询,因此请谨慎使用系统。然而,我们已经在一些客户身上成功使用了此扩展。
安装
Composer
该扩展目前支持TYPO3 v11和TYPO3 v12。该扩展可以作为非开发依赖项安装(不要在composer require
中添加--dev
):只要不通过CLI执行,它就不会对生产实例产生影响(除了依赖注入定义)。
$ composer require lolli/dbdoctor
TYPO3扩展存储库
对于非Composer项目,该扩展可在TER中以扩展键dbdoctor
获取,并可以使用扩展管理器进行安装。
准备
CLI命令的本质是在您的实例上执行破坏性的数据库操作。因此,应考虑以下几点
-
[!!!] 💣 在使用CLI接口之前和之后创建一个全新的数据库备份。确保恢复策略确实有效:扩展和用户都可能出错。毕竟,我们正在处理低级数据库内容,所以事情可能会很快变得不妙。请参阅下面的“进一步提示”部分。
-
[!!!] 确保TYPO3“数据库分析器”满意,不需要新的或更改的列或表。早期检查验证缺失的表和列,但在运行dbdoctor之前再次检查是个好主意。
-
[!!!] 应没有悬而未决的核心升级向导。dbdoctor目前不会预先检查是否已执行所有升级向导。
后处理
- [!!!] 在此命令完成后运行引用索引更新器!它很可能将更新某些内容。随着核心版本越来越新,干净的引用索引变得越来越重要。执行此操作的CLI命令:
bin/typo3 referenceindex:update
。
用法
$ bin/typo3 dbdoctor:health
请注意,dbdoctor是“运行时静态”与TCA:当dbdoctor运行时,TCA在此时不应该改变。当您查看单个更改并决定更改TCA时,请清除所有缓存并终止dbdoctor(在交互模式下按“a”)以重新开始。不这样做可能导致dbdoctor将风险提交到数据库,具体取决于您对TCA做了什么。
界面看起来像这样
请注意,上述图像是臭名昭著的过时,当前版本的界面可能略有不同。我们太懒了,不愿经常更新图像,但应该可以很好地了解界面看起来像什么。
主要命令是一系列单次检查的组合。它们是逐一执行的。受影响的记录细节可以按页面和每条记录显示,以提供快速概览。根据检查类型,界面允许删除或更新受影响的记录。
默认交互模式永远不会自动执行更新,并且始终要求用户进行操作。按's'(模拟/显示)时,会显示将要执行的查询,按'e'(执行)时,实际上会执行这些查询。
交互模式
当dbdoctor在(默认)交互模式下发现需要修复的问题时,执行将停止并等待用户输入。
- e - 执行建议的更改!
- s - 模拟建议的更改,不执行
- a - 立即中止
- r - 重新加载此检查
- p - 按页显示记录
- d - 显示记录详情
- ? - 帮助
退出值
退出值是位掩码:整数3表示:“需要或已执行更改”AND“用户中止”
- 0 - 无需或未执行更改
- 1 - 需要或已执行更改
- 2 - 用户中止
- 4 - 发生错误
选项
CLI命令可以带一些选项执行。默认模式是“交互式”,在每个检查失败后提示用户输入。
-
帮助概览
$ bin/typo3 dbdoctor:health -h
留给读者自行了解这里所做的工作 :-P
-
交互模式:
--mode interactive
或-m interactive
或未指定选项$ bin/typo3 dbdoctor:health -m interactive
默认模式:逐一执行检查,并具有交互式界面以查看受影响的记录详情,显示受影响记录的页面,模拟执行查询,重新加载检查,最终执行查询。
-
检查模式:
--mode check
或-m check
$ bin/typo3 dbdoctor:health -m check
运行所有检查但不执行任何数据库更改。如果所有检查都正常,则返回0(零);如果有任何检查发现问题,则返回非零值。用作cron作业非常有用,以检查在修复所有问题后是否任何检查“变红”。
-
执行模式:
--mode execute
或-m execute
$ bin/typo3 dbdoctor:health -m execute -f /tmp/dbdoctor-my-instance-`date +%Y-%m-%d-%H-%M-%S`.sql
盲目执行,不再提问!这将会执行dbdoctor建议的所有更新和删除查询!如果你信任此命令,这是一种潜在的有害自动操作(你不应该这么做);你创建数据库备份了吗?注意,使用此模式时,必须使用
-f
选项:你必须将执行查询记录到不存在的文件中,以至少为你提供在dbdoctor破坏你的数据库后调试问题的理论选项。因此,-f
选项应包含一些日期或类似的内容,以使其唯一。 -
将执行查询记录到文件:
--file
或-f
$ bin/typo3 dbdoctor:health -f /tmp/foo.sql
$ bin/typo3 dbdoctor:health -f /tmp/dbdoctor-my-instance-`date +%Y-%m-%d-%H-%M-%S`.sql
将所有更改数据的查询记录到文件。参数必须是一个绝对文件名。永远不要将此类文件放入实例的公共网络文件夹。在“交互式”模式下,
-f
选项是可用的,在“执行”模式下是必需的。执行的数据更改查询不仅会显示,还会记录到文件。如果该命令已在使用当前实时数据库镜像的测试系统中执行,这可能很有用:可以再次审查查询,然后使用类似mysql my_database < file.sql
或类似的其他DBMS在实时实例上执行。
当前健康检查
在运行CLI命令时将详细描述单个测试。简要概述
- 页面树完整性检查
- FAL相关的sys_file_reference及其相关检查
- 与语言处理相关的检查
- 与工作空间相关的检查
- 内联父子关系相关的检查
其他提示
我们强烈建议管理员在处理dbdoctor时备份数据库。在执行此操作时,必须记住有关SQL转储的一些基本规则。
-
在执行CLI命令前后导出现有的MySQL / MariaDB数据库时,关闭“扩展插入”选项可能很有帮助:
mysqldump
默认将多个INSERT语句合并为一个调用以提高效率和速度。这既加快了导出和导入速度,又减少了磁盘空间的使用。然而,当寻找单个数据库更改时,关闭此选项并且每行插入一行会更加方便。当搜索最终出错的内容时,工具如
diff
更容易理解。以下是一些示例shell命令:$ mysqldump --skip-extended-insert myDatabase > /tmp/myDatabase-`date +%Y-%m-%d-%H-%M-%S`-dbdoctor-before.sql $ bin/typo3 dbdoctor:health $ mysqldump --skip-extended-insert myDatabase > /tmp/myDatabase-`date +%Y-%m-%d-%H-%M-%S`-dbdoctor-after.sql
尽管如此,如果你知道自己在做什么,总是可以偏离常规。我通常会创建两个备份:一个带有
--skip-extended-insert
,另一个不带。从没有“每行一行”的文件加载灾难恢复要快得多,但为了调试,比较基于跳过扩展插入的备份要容易得多。 -
在导出数据库时,永远不要将此类备份放入公共目录,该目录可通过Web服务器或第三方服务器用户访问。违反此基本规则是野外数据泄露的常见原因!对此没有借口。将SQL文件放在备份轮换的地方也是一个好主意,以便在问题过一段时间后才出现时进行调试。为了遵循GDPR规则,这些文件也应在某个时候被删除!
-
在导出数据库时,通常建议对.sql文件进行gzip压缩:这通常可以将文件大小减少到原来的八分之一。让我们节省一些宝贵的服务器磁盘和备份空间!在导出时也可以直接将数据“管道”到gzip。你可以这样做,或者记得在退出系统之前压缩数据。
常见问题解答(FAQ)
-
功能是否将在后端GUI中提供?
不会。CLI是处理这类事务的唯一合理方式。
-
是否会增加对TYPO3 v10或其他较旧核心版本的支持?
不会。TYPO3 v11进行了很多数据库更改,并且没有计划实现v10向后兼容层。
标签和发布
packagist.org通过casual github钩子启用。当使用tailor标签版本时,通过“publish.yml”github工作流程创建TER发布。标记提交的提交信息用作TER上传注释。
示例
Build/Scripts/runTests.sh -s clean
Build/Scripts/runTests.sh -s composerUpdate
composer req --dev typo3/tailor
.Build/bin/tailor set-version 0.3.2
composer rem --dev typo3/tailor
git commit -am "[RELEASE] 0.3.2 Added some basic inline foreign field related checks"
git tag 0.3.2
git push
git push --tags