Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update/create time period entries efficiently #114

Closed
wants to merge 1 commit into from

Conversation

raviks789
Copy link
Contributor

@raviks789 raviks789 commented Sep 11, 2023

Currently a new time period gets created whenever a time period entry with the same group of contacts and/or contact groups for a given schedule. This nullifies the reason for existence of time periods.

Time periods should act as a bridge between the time period entries and the schedules.

Hence, time periods for a group of contacts and/or contact groups should be same across all time period entries for in a given schedule.

A new time period for a new time period entry should be generated only if there is no existing time period for the given schedule and the group of contacts and/contact groups. If another time period entry is created for the same group of contacts and/or contact groups in the same schedule, existing time period or these entries should be used.

@cla-bot cla-bot bot added the cla/signed CLA is signed by all contributors of a PR label Sep 11, 2023
@raviks789 raviks789 force-pushed the improve-timeperiod-storage branch 4 times, most recently from 44ee546 to d43b162 Compare September 13, 2023 13:20
@raviks789 raviks789 changed the title WIP: Make creating and updating time period entries efficient Update/create time period entries efficiently Sep 13, 2023
@raviks789 raviks789 self-assigned this Sep 13, 2023
@raviks789 raviks789 marked this pull request as ready for review October 20, 2023 13:42
Copy link
Member

@nilmerg nilmerg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't look at editEntry yet

@@ -123,6 +124,10 @@ public function loadEntry(int $scheduleId, int $id): self

$recipients = [];
foreach ($members as $member) {
if (! isset($values['membership_hash'])) {
$values['membership_hash'] = (new Binary([]))->toDb($member->membership_hash, null, null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

membership_hash is nullable and this line doesn't account for that and leads to errors for me.
I think registering NULL as value is just fine. Though, remember to switch to array_key_exists above then.
Besides, don't use the behavior here, just use bin2hex and later when processing the hash again, hex2bin. There's no need to do this database vendor aware.

Comment on lines +328 to +337
$members = ScheduleMember::on(Database::get())
->with('timeperiod')
->filter(
Filter::all(
Filter::equal('timeperiod.owned_by_schedule_id', $scheduleId),
Filter::equal('schedule_member.membership_hash', $membershipHash)
)
);

if ($members->count() > 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather select from Timeperiod directly. That's the only information you're really interested in. The count of members for example, you're using it to check whether there are any members at all. That's a common mistake, I mean, to count although the count itself is of no interest. So by fetching the timeperiod directly, you already verified that at least one member's hash matches. (Given that the filter is still the same)

'contactgroup_id' => $id
]);
if ($members->count() === 0) {
$binaryMemberHash = (new Binary([]))->toDb($membershipHash, null, null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$binaryMemberHash = (new Binary([]))->toDb($membershipHash, null, null);
$q = ScheduleMember::on(Database::get());
$binaryMemberHash = $q->getResolver()->getBehaviors($q->getModel())->persistProperty($membershipHash, 'membership_hash');

This way you don't hardcode the binary behavior here and it will still work once are compatible with MySQL.

*
* @return string
*/
public function getMembershipHashFromRecipients(int $scheduleID): string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use $scheduleId (with a lowercase d at the end) instead. addEntry does it already.

{
$recipients = explode(',', $this->getValue('recipient'));
sort($recipients);
return sha1(sprintf('schedule:%d,%s', $scheduleID, implode(',', $recipients)), true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

empty line before return, pls

}
}

$db->commitTransaction();
}

/**
* Get membership hash for the recipients in timeperiod entry for the given schedule
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please be more specific here and less verbose. You don't need to mention timeperiod entries here, that's obvious from the class already. Then, get imposes that the value may be be cached or so. At least, I don't see it fit with the implementation. It's not a getter. So maybe rename the method to calculate... or generate..., or even hash....

Copy link
Member

@nilmerg nilmerg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You didn't adjust removeEntry. So it's currently not possible to remove an entry once it's not the only one in a timeperiod.

['id = ?' => $id]
);
$prevTimeperiodEntriesCount -= 1;
} elseif ($prevTimeperiodEntriesCount === 1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case doesn't update the entry at all

$prevTimeperiodId = $this->getValue('timeperiod_id');
$suppliedHash = $this->getValue('membership_hash');
$calculatedHash = $this->getMembershipHashFromRecipients($scheduleId);
$changedMembers = ScheduleMember::on(Database::get())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you're also actually only interested in the timeperiod.

$db = Database::get();
$db->beginTransaction();
$binaryBehavior = new Binary([]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use the behavior directly here as well.

);

$prevTimeperiodEntriesCount -= 1;
if ($changedMembers->count() === 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that check is redundant. If this case runs, the count is always zero.

application/forms/EntryForm.php Show resolved Hide resolved
@nilmerg
Copy link
Member

nilmerg commented Nov 29, 2023

As discussed. This is obsolete since it only complicates the code to achieve a so small benefit and is only relevant in a very specific edge-case.

@nilmerg nilmerg closed this Nov 29, 2023
@nilmerg nilmerg deleted the improve-timeperiod-storage branch November 29, 2023 09:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla/signed CLA is signed by all contributors of a PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants