perf(db): composite index on wallet_transactions(user_id, type) (M14)
Helps the per-type SUM balance query. M13 (service_items.section_id) and M15 (clinic_doctor_invitations.doctor_id) were false positives — both columns carry a FK and are therefore auto-indexed by InnoDB; verified against the live schema. Structural regression: InfraSmokeTest::testWalletUserTypeIndexExists. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -66,9 +66,9 @@ _None outstanding._
|
||||
| M10 | Unbounded list: `listMine` settlements `findByUser` no limit | src/Settlement/Controller/SettlementController.php:193 | perf-pagination | GET settlement list → paginate |
|
||||
| M11 | Unbounded list: admin `pendingComments` `findPending` no limit | src/Rating/Controller/RatingController.php:350 | perf-pagination | admin moderation large backlog → paginate |
|
||||
| M12 | Unbounded list: public `listComments` per-doctor no limit | src/Rating/Controller/RatingController.php:270 | perf-pagination | popular doctor comments → paginate |
|
||||
| M13 | Missing index: `ServiceItem.section_id` FK fully unindexed (entity has zero indexes) | src/ClinicService/Entity/ServiceItem.php:23-24 | perf-index | EXPLAIN findBySection → no full scan |
|
||||
| M14 | Missing index: `WalletTransaction(user_id, type)` composite for per-type SUM | src/Settlement/Entity/WalletTransaction.php:38-39 | perf-index | EXPLAIN balance SUM query |
|
||||
| M15 | Missing index: `ClinicDoctorInvitation.doctor_id` FK unindexed | src/ClinicInvitation/Entity/ClinicDoctorInvitation.php:41-42 | perf-index | EXPLAIN findPendingByDoctor |
|
||||
| ⚠️M13 | Missing index: `ServiceItem.section_id` FK fully unindexed (entity has zero indexes) | src/ClinicService/Entity/ServiceItem.php:23-24 | perf-index | EXPLAIN findBySection → no full scan |
|
||||
| ✅M14 | Missing index: `WalletTransaction(user_id, type)` composite for per-type SUM | src/Settlement/Entity/WalletTransaction.php:38-39 | perf-index | **DONE** — added composite `idx_wallet_user_type` + migration. `InfraSmokeTest::testWalletUserTypeIndexExists`. |
|
||||
| ⚠️M15 | Missing index: `ClinicDoctorInvitation.doctor_id` FK unindexed | src/ClinicInvitation/Entity/ClinicDoctorInvitation.php:41-42 | perf-index | **FALSE POSITIVE** — `doctor_id` has a FK → auto-indexed (IDX_26DCCFEB87F4FB17 exists). No change. |
|
||||
| M16 | `User.email` and `User.nationalCode` not unique → duplicate identities | src/Auth/Entity/User.php:31-32,37-38 | db-unique | Insert two users same email/national_code → 2nd rejected. ⚠️ check dup data first |
|
||||
| M17 | `Payment.gatewayToken` not unique | src/Payment/Entity/Payment.php:58-59 | db-unique | Two payments same gateway_token → rejected |
|
||||
| M18 | `DateOverride` no UNIQUE `(doctor_id, date)` → ambiguous schedule | src/Appointment/Entity/DateOverride.php:12 | db-unique | Two overrides same doctor+date → rejected |
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20260628165710 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE INDEX idx_wallet_user_type ON wallet_transactions (user_id, type)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('DROP INDEX idx_wallet_user_type ON wallet_transactions');
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ use Symfony\Component\Uid\Uuid;
|
||||
#[ORM\Entity(repositoryClass: WalletTransactionRepository::class)]
|
||||
#[ORM\Table(name: 'wallet_transactions')]
|
||||
#[ORM\Index(columns: ['user_id', 'created_at'], name: 'idx_wallet_user_date')]
|
||||
#[ORM\Index(columns: ['user_id', 'type'], name: 'idx_wallet_user_type')]
|
||||
class WalletTransaction
|
||||
{
|
||||
public const TYPE_CREDIT = 'credit';
|
||||
|
||||
@@ -31,4 +31,16 @@ class InfraSmokeTest extends ApiTestCase
|
||||
$this->assertTrue($body['success']);
|
||||
$this->assertSame($user->getUuid(), $body['data']['uuid']);
|
||||
}
|
||||
|
||||
public function testWalletUserTypeIndexExists(): void
|
||||
{
|
||||
$conn = $this->em->getConnection();
|
||||
$count = (int) $conn->fetchOne(
|
||||
"SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'wallet_transactions'
|
||||
AND INDEX_NAME = 'idx_wallet_user_type'"
|
||||
);
|
||||
$this->assertGreaterThan(0, $count, 'composite index idx_wallet_user_type is missing');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user