1 | <?php |
||
10 | |||
11 | class AerospikeAdapter extends AbstractAdapter implements AdapterInterface |
||
12 | { |
||
13 | const BIN_KEY = 'v'; |
||
14 | const DEFAULT_AEROSPIKE_NAMESPACE = 'test'; |
||
15 | |||
16 | protected Aerospike $db; |
||
|
|||
17 | protected string $aerospikeNamespace; |
||
18 | protected int $ttl = 0; |
||
19 | |||
20 | public function __construct(array $config) |
||
21 | { |
||
22 | $aerospikeConfig = []; |
||
23 | |||
24 | if (!isset($config['hosts'])) { |
||
25 | throw new InvalidConfigurationException('Missing configuration parameter: hosts'); |
||
26 | } |
||
27 | |||
28 | // default options |
||
29 | $persistent = true; |
||
30 | $options = []; |
||
31 | $this->ttl = 0; |
||
32 | |||
33 | if (isset($config['persistent'])) { |
||
34 | $persistent = (bool) $config['persistent']; |
||
35 | } |
||
36 | |||
37 | if (isset($config['options'])) { |
||
38 | $options = $config['options']; |
||
39 | } |
||
40 | |||
41 | if (isset($config['ttl'])) { |
||
42 | $this->ttl = (int) $config['ttl']; |
||
43 | } |
||
44 | |||
45 | if (isset($config['cache_not_found_keys'])) { |
||
46 | $this->cacheNotFoundKeys = (bool) $config['cache_not_found_keys']; |
||
47 | } |
||
48 | |||
49 | if (isset($config['aerospike_namespace'])) { |
||
50 | $this->aerospikeNamespace = $config['aerospike_namespace']; |
||
51 | } else { |
||
52 | $this->aerospikeNamespace = self::DEFAULT_AEROSPIKE_NAMESPACE; |
||
53 | } |
||
54 | |||
55 | $aerospikeConfig['hosts'] = $config['hosts']; |
||
56 | |||
57 | $this->db = new \Aerospike($aerospikeConfig, $persistent, $options); |
||
58 | if (!$this->db->isConnected()) { |
||
59 | throw new \RuntimeException("Failed to connect to Aerospike Server [{$this->db->errorno()}]: {$this->db->error()}\n"); |
||
60 | } |
||
61 | } |
||
62 | |||
63 | /** |
||
64 | * @return mixed |
||
65 | */ |
||
66 | public function get(string $key) |
||
67 | { |
||
68 | $namespacedKey = $this->getNamespacedKey($key); |
||
69 | $status = $this->db->get($namespacedKey, $metadata); |
||
70 | |||
71 | if ($status != \Aerospike::OK) { |
||
72 | throw new KeyNotFoundException(); |
||
73 | } |
||
74 | |||
75 | return $this->removeBin($metadata); |
||
76 | } |
||
77 | |||
78 | public function getMulti(array $keys): array |
||
79 | { |
||
80 | $namespacedKeys = []; |
||
81 | foreach ($keys as $key) { |
||
82 | $namespacedKeys[] = $this->getNamespacedKey($key); |
||
83 | } |
||
84 | |||
85 | $status = $this->db->getMany($namespacedKeys, $result); |
||
86 | if ($status != \Aerospike::OK) { |
||
87 | return []; |
||
88 | } |
||
89 | |||
90 | $values = []; |
||
91 | foreach ($result as $entry) { |
||
92 | $key = $entry['key']['key']; |
||
93 | $value = $this->removeBin($entry); |
||
94 | |||
95 | if ($value === null) { |
||
96 | continue; |
||
97 | } |
||
98 | |||
99 | $values[$key] = $value; |
||
100 | } |
||
101 | |||
102 | return $values; |
||
103 | } |
||
104 | |||
105 | /** |
||
106 | * @param mixed $value |
||
107 | */ |
||
108 | public function set(string $key, $value): bool |
||
109 | { |
||
110 | $status = $this->db->put($this->getNamespacedKey($key), $this->createBin($value), $this->ttl); |
||
111 | |||
112 | return $status == \Aerospike::OK; |
||
113 | } |
||
114 | |||
115 | public function setMulti(array $data): bool |
||
116 | { |
||
117 | $success = true; |
||
118 | foreach ($data as $key => $value) { |
||
119 | $success = $success && $this->set($key, $value); |
||
120 | } |
||
121 | |||
122 | return $success; |
||
123 | } |
||
124 | |||
125 | public function contains(string $key): bool |
||
126 | { |
||
127 | $status = $this->db->exists($this->getNamespacedKey($key), $metadata); |
||
128 | |||
129 | return $status == \Aerospike::OK; |
||
130 | } |
||
131 | |||
132 | public function delete(string $key): bool |
||
133 | { |
||
134 | $namespacedKey = $this->getNamespacedKey($key); |
||
135 | |||
136 | $this->db->remove($namespacedKey); |
||
137 | |||
138 | return true; |
||
139 | } |
||
140 | |||
141 | public function deleteMulti(array $keys): bool |
||
142 | { |
||
143 | foreach ($keys as $key) { |
||
144 | $this->delete($key); |
||
145 | } |
||
146 | |||
147 | return true; |
||
148 | } |
||
149 | |||
150 | public function flush(): bool |
||
151 | { |
||
152 | $this->db->scan( |
||
153 | $this->aerospikeNamespace, |
||
154 | $this->namespace, |
||
155 | function ($record): void { |
||
156 | unset($record['key']['key']); |
||
157 | $this->db->remove($record['key']); |
||
158 | } |
||
159 | ); |
||
160 | |||
161 | return true; |
||
162 | } |
||
163 | |||
164 | protected function getNamespacedKey(string $key): array |
||
165 | { |
||
166 | return $this->db->initKey($this->aerospikeNamespace, str_replace([':', '.'], '_', $this->namespace), $key); |
||
167 | } |
||
168 | |||
169 | /** |
||
170 | * @param mixed $value |
||
171 | */ |
||
172 | protected function createBin($value): array |
||
173 | { |
||
174 | return [self::BIN_KEY => $value]; |
||
175 | } |
||
176 | |||
177 | /** |
||
178 | * @param mixed $metadata |
||
179 | */ |
||
180 | protected function removeBin($metadata): ?string |
||
181 | { |
||
182 | if (!is_array($metadata) || !array_key_exists('bins', $metadata) || !is_array($metadata['bins']) || !array_key_exists(static::BIN_KEY, $metadata['bins'])) { |
||
183 | return null; |
||
184 | } |
||
185 | |||
186 | return $metadata['bins'][static::BIN_KEY]; |
||
187 | } |
||
189 |