Skip to content

Commit 21e0267

Browse files
committed
centralise state-key construction
1 parent b4c3fbb commit 21e0267

4 files changed

Lines changed: 179 additions & 131 deletions

File tree

src/Cache/CacheStateResolver.php

Lines changed: 93 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,16 @@ public function resolve(
4343
QueryPlan::CANONICAL, QueryPlan::DIRECT_PK => $this->keys->generation($plan->root),
4444
default => null,
4545
} : null;
46-
$tagKey = str_starts_with($namespace, 'g')
47-
? $this->keys->tagVersion(substr($namespace, 1))
48-
: null;
49-
$epochKey = $this->runtime->knownEpoch() === null ? $this->keys->epoch() : null;
46+
$tagKey = $this->tagKey($namespace);
47+
$epochKey = $this->unknownEpochKey();
5048
$values = $this->store->mget(array_values(array_unique(array_filter([
5149
$epochKey,
5250
...array_values($versionKeys),
5351
$generationKey,
5452
$tagKey,
5553
...$alsoFetch,
5654
]))));
57-
58-
if ($epochKey !== null) {
59-
$this->runtime->rememberEpoch($values[$epochKey] ?? '0');
60-
}
55+
$this->rememberEpochFrom($epochKey, $values);
6156

6257
if (
6358
$knownVersion !== null
@@ -109,7 +104,7 @@ public function resolve(
109104
return [
110105
new CacheState(
111106
key: $key,
112-
epoch: $this->epoch(),
107+
epoch: $this->runtime->epoch(),
113108
version: $rootVersion,
114109
generation: $generation,
115110
versions: $versions,
@@ -120,112 +115,127 @@ public function resolve(
120115
];
121116
}
122117

118+
/**
119+
* @param list<string> $rowKeys
120+
* @return array{0: CacheState, 1: array<string, ?string>}
121+
*/
122+
public function resolveCanonical(
123+
QueryPlan $plan,
124+
string $namespace,
125+
string $queryHash,
126+
array $rowKeys,
127+
): array {
128+
$keys = $this->stateKeys($plan, $this->tagKey($namespace), $this->unknownEpochKey());
129+
$values = $this->store->mget(array_values(array_unique([
130+
...$rowKeys,
131+
...$keys['all'],
132+
])));
133+
$this->rememberEpochFrom($keys['epoch'], $values);
134+
135+
$versions = [];
136+
137+
foreach ($keys['dependencies'] as $hash => $key) {
138+
$versions[$hash] = $values[$key] ?? '0';
139+
}
140+
141+
ksort($versions, SORT_STRING);
142+
$version = $values[$keys['version']] ?? '0';
143+
144+
return [
145+
new CacheState(
146+
key: $this->keys->membership($plan->root, $version, $namespace, $queryHash),
147+
epoch: $this->runtime->epoch(),
148+
version: $version,
149+
generation: $values[$keys['generation']] ?? '0',
150+
versions: $versions,
151+
tag: $keys['tag'] !== null ? ($values[$keys['tag']] ?? '0') : null,
152+
tagKey: $keys['tag'],
153+
),
154+
$values,
155+
];
156+
}
157+
158+
/** @phpstan-impure re-read on each call: callers check either side of a publish */
159+
public function isCurrent(QueryPlan $plan, CacheState $expected): bool
160+
{
161+
$keys = $this->stateKeys($plan, $expected->tagKey, $this->keys->epoch());
162+
$values = $this->store->mget($keys['all']);
163+
$current = static fn(string $key): string => $values[$key] ?? '0';
164+
165+
if (
166+
$current((string) $keys['epoch']) !== $expected->epoch
167+
|| $current($keys['version']) !== $expected->version
168+
|| $current($keys['generation']) !== $expected->generation
169+
) {
170+
return false;
171+
}
172+
173+
foreach ($keys['dependencies'] as $hash => $key) {
174+
if ($current($key) !== ($expected->versions[$hash] ?? null)) {
175+
return false;
176+
}
177+
}
178+
179+
return $expected->tag === null
180+
|| $keys['tag'] !== null && $current($keys['tag']) === $expected->tag;
181+
}
182+
123183
/**
124184
* @return array{
125185
* epoch: ?string,
126186
* version: string,
127187
* generation: string,
128188
* dependencies: array<string, string>,
129189
* tag: ?string,
130-
* final: list<string>
190+
* all: list<string>
131191
* }
132192
*/
133-
public function canonicalKeys(QueryPlan $plan, string $namespace): array
193+
private function stateKeys(QueryPlan $plan, ?string $tagKey, ?string $epochKey): array
134194
{
135-
$dependencyKeys = [];
195+
$dependencies = [];
136196

137197
foreach ($plan->dependencies as $dependency) {
138198
if ($dependency->hash !== $plan->root->hash) {
139-
$dependencyKeys[$dependency->hash] = $this->keys->version($dependency);
199+
$dependencies[$dependency->hash] = $this->keys->version($dependency);
140200
}
141201
}
142202

143-
$epochKey = $this->runtime->knownEpoch() === null ? $this->keys->epoch() : null;
144203
$versionKey = $this->keys->version($plan->root);
145204
$generationKey = $this->keys->generation($plan->root);
146-
$tagKey = str_starts_with($namespace, 'g')
147-
? $this->keys->tagVersion(substr($namespace, 1))
148-
: null;
149-
$externalKeys = array_values(array_filter([
150-
$epochKey,
151-
...array_values($dependencyKeys),
152-
$tagKey,
153-
]));
154205

155206
return [
156207
'epoch' => $epochKey,
157208
'version' => $versionKey,
158209
'generation' => $generationKey,
159-
'dependencies' => $dependencyKeys,
210+
'dependencies' => $dependencies,
160211
'tag' => $tagKey,
161-
'final' => [$versionKey, $generationKey, ...$externalKeys],
212+
'all' => array_values(array_filter([
213+
$versionKey,
214+
$generationKey,
215+
$epochKey,
216+
...array_values($dependencies),
217+
$tagKey,
218+
])),
162219
];
163220
}
164221

165-
/**
166-
* @param array{
167-
* epoch: ?string,
168-
* version: string,
169-
* generation: string,
170-
* dependencies: array<string, string>,
171-
* tag: ?string,
172-
* final: list<string>
173-
* } $keys
174-
* @param array<string, ?string> $values
175-
*/
176-
public function canonicalFromFetched(
177-
QueryPlan $plan,
178-
string $namespace,
179-
string $queryHash,
180-
array $keys,
181-
array $values,
182-
): CacheState {
183-
if ($keys['epoch'] !== null) {
184-
$this->runtime->rememberEpoch($values[$keys['epoch']] ?? '0');
185-
}
186-
187-
$versions = [];
188-
189-
foreach ($keys['dependencies'] as $hash => $key) {
190-
$versions[$hash] = $values[$key] ?? '0';
191-
}
192-
193-
ksort($versions, SORT_STRING);
194-
$version = $values[$keys['version']] ?? '0';
195-
$generation = $values[$keys['generation']] ?? '0';
196-
$tag = $keys['tag'] !== null ? ($values[$keys['tag']] ?? '0') : null;
197-
198-
return new CacheState(
199-
key: $this->keys->membership(
200-
$plan->root,
201-
$version,
202-
$namespace,
203-
$queryHash,
204-
),
205-
epoch: $this->epoch(),
206-
version: $version,
207-
generation: $generation,
208-
versions: $versions,
209-
tag: $tag,
210-
tagKey: $keys['tag'],
211-
);
222+
private function tagKey(string $namespace): ?string
223+
{
224+
return str_starts_with($namespace, 'g')
225+
? $this->keys->tagVersion(substr($namespace, 1))
226+
: null;
212227
}
213228

214-
/**
215-
* @param list<string> $keys
216-
* @param list<string> $alsoFetch
217-
* @return array<string, ?string>
218-
*/
219-
public function fetch(array $keys, array $alsoFetch = []): array
229+
private function unknownEpochKey(): ?string
220230
{
221-
return $this->store->mget(array_values(array_unique([
222-
...$keys,
223-
...$alsoFetch,
224-
])));
231+
return $this->runtime->knownEpoch() === null ? $this->keys->epoch() : null;
225232
}
226233

227-
private function epoch(): string
234+
/** @param array<string, ?string> $values */
235+
private function rememberEpochFrom(?string $epochKey, array $values): void
228236
{
229-
return $this->runtime->epoch();
237+
if ($epochKey !== null) {
238+
$this->runtime->rememberEpoch($values[$epochKey] ?? '0');
239+
}
230240
}
231241
}

src/Cache/CanonicalRepository.php

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,11 @@ public function read(
6767
$unique[$rowPrefix . $token] = true;
6868
}
6969

70-
$stateKeys = $this->states->canonicalKeys($plan, $namespace);
71-
$fetched = $this->states->fetch(array_keys($unique), $stateKeys['final']);
72-
$state = $this->states->canonicalFromFetched(
70+
[$state, $fetched] = $this->states->resolveCanonical(
7371
$plan,
7472
$namespace,
7573
$queryHash,
76-
$stateKeys,
77-
$fetched,
74+
array_keys($unique),
7875
);
7976

8077
if (

src/Cache/Engine.php

Lines changed: 4 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ private function repairRows(
783783

784784
$rows = $this->readRepairedRows($plan, $state, $tokens);
785785

786-
if ($rows === null || !$this->stateStillCurrent($plan, $state)) {
786+
if ($rows === null || !$this->states->isCurrent($plan, $state)) {
787787
return null;
788788
}
789789

@@ -881,7 +881,7 @@ private function buildRepairedRows(
881881
return null;
882882
}
883883

884-
if (!$this->stateStillCurrent($plan, $state)) {
884+
if (!$this->states->isCurrent($plan, $state)) {
885885
return null;
886886
}
887887

@@ -916,7 +916,7 @@ private function buildRepairedRows(
916916
return [];
917917
}
918918

919-
return $this->stateStillCurrent($plan, $state) ? $rowsByToken : [];
919+
return $this->states->isCurrent($plan, $state) ? $rowsByToken : [];
920920
}
921921

922922
/**
@@ -934,7 +934,7 @@ private function readRepairedRows(
934934
$rowKeys[$token] = $this->keys->row($plan->root, $state->generation, $token);
935935
}
936936

937-
$raw = $this->states->fetch(array_values($rowKeys));
937+
$raw = $this->store->mget(array_values($rowKeys));
938938
$rows = [];
939939

940940
foreach ($rowKeys as $token => $rowKey) {
@@ -959,45 +959,6 @@ private function readRepairedRows(
959959
}
960960

961961
/** @phpstan-impure */
962-
private function stateStillCurrent(QueryPlan $plan, CacheState $state): bool
963-
{
964-
$epochKey = $this->keys->epoch();
965-
$versionKey = $this->keys->version($plan->root);
966-
$generationKey = $this->keys->generation($plan->root);
967-
$dependencyKeys = [];
968-
969-
foreach ($plan->dependencies as $dependency) {
970-
if ($dependency->hash !== $plan->root->hash) {
971-
$dependencyKeys[$dependency->hash] = $this->keys->version($dependency);
972-
}
973-
}
974-
975-
$values = $this->store->mget(array_values(array_filter([
976-
$epochKey,
977-
$versionKey,
978-
$generationKey,
979-
$state->tagKey,
980-
...array_values($dependencyKeys),
981-
])));
982-
$current = static fn(string $key): string => $values[$key] ?? '0';
983-
984-
if (
985-
$current($epochKey) !== $state->epoch
986-
|| $current($versionKey) !== $state->version
987-
|| $current($generationKey) !== $state->generation
988-
) {
989-
return false;
990-
}
991-
992-
foreach ($dependencyKeys as $hash => $key) {
993-
if ($current($key) !== ($state->versions[$hash] ?? null)) {
994-
return false;
995-
}
996-
}
997-
998-
return $state->tag === null
999-
|| $state->tagKey !== null && $current($state->tagKey) === $state->tag;
1000-
}
1001962

1002963
/** @param array<int, mixed> $rows */
1003964
private function publish(

0 commit comments

Comments
 (0)