Skip to content

Commit b535767

Browse files
authored
Merge pull request #168 from splitio/task/hashtags
added keyHashTags option
2 parents a392c27 + e06c277 commit b535767

File tree

6 files changed

+172
-57
lines changed

6 files changed

+172
-57
lines changed

.github/workflows/ci.yml

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
name: ci
22
on:
3-
push:
3+
pull_request:
44
branches:
5+
- develop
56
- master
6-
pull_request:
7+
push:
78
branches:
9+
- develop
810
- master
911

1012
jobs:
@@ -18,15 +20,15 @@ jobs:
1820
- 6379:6379
1921
strategy:
2022
matrix:
21-
php-versions: ['7.3', '8.0']
23+
version: ['7.3', '8.0']
2224
steps:
2325
- name: Checkout code
2426
uses: actions/checkout@v2
2527

2628
- name: Setup PHP
2729
uses: shivammathur/setup-php@v2
2830
with:
29-
php-version: ${{ matrix.php-versions }}
31+
php-version: ${{ matrix.version }}
3032
extensions: mbstring, intl
3133
ini-values: post_max_size=256M, max_execution_time=180
3234
coverage: xdebug
@@ -37,7 +39,49 @@ jobs:
3739
composer update
3840
composer dumpautoload
3941
40-
- name: script
42+
- name: Build
4143
run: |
4244
vendor/bin/phpcs --ignore=functions.php --standard=PSR2 src/
4345
vendor/bin/phpunit -c phpunit.xml.dist -v --testsuite integration
46+
47+
sonarqube:
48+
name: Sonarqube
49+
runs-on: ubuntu-latest
50+
steps:
51+
- name: Checkout code
52+
uses: actions/checkout@v2
53+
with:
54+
fetch-depth: 0
55+
56+
- name: SonarQube Scan (Push)
57+
if: github.event_name == 'push'
58+
uses: SonarSource/sonarcloud-github-action@v1.5
59+
env:
60+
SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }}
61+
with:
62+
projectBaseDir: .
63+
args: >
64+
-Dsonar.host.url=${{ secrets.SONARQUBE_HOST }}
65+
-Dsonar.projectName=${{ github.event.repository.name }}
66+
-Dsonar.projectKey=splitsoftware_split-sdk-php
67+
-Dsonar.exclusions="**/tests/**/*.*"
68+
-Dsonar.links.ci="https://github.com/splitio/${{ github.event.repository.name }}/actions"
69+
-Dsonar.links.scm="https://github.com/splitio/${{ github.event.repository.name }}"
70+
71+
- name: SonarQube Scan (Pull Request)
72+
if: github.event_name == 'pull_request'
73+
uses: SonarSource/sonarcloud-github-action@v1.5
74+
env:
75+
SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }}
76+
with:
77+
projectBaseDir: .
78+
args: >
79+
-Dsonar.host.url=${{ secrets.SONARQUBE_HOST }}
80+
-Dsonar.projectName=${{ github.event.repository.name }}
81+
-Dsonar.projectKey=splitsoftware_split-sdk-php
82+
-Dsonar.exclusions="**/tests/**/*.*"
83+
-Dsonar.links.ci="https://github.com/splitio/${{ github.event.repository.name }}/actions"
84+
-Dsonar.links.scm="https://github.com/splitio/${{ github.event.repository.name }}"
85+
-Dsonar.pullrequest.key=${{ github.event.pull_request.number }}
86+
-Dsonar.pullrequest.branch=${{ github.event.pull_request.head.ref }}
87+
-Dsonar.pullrequest.base=${{ github.event.pull_request.base.ref }}

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
7.1.0 (Dec 3, 2021)
2+
- Added a new option to use when running Redis in cluster mode called `keyHashTags` which receives a list of hashtags from which the SDK will randomly pick one to use on the generated instance.
3+
14
7.0.0 (Nov 23, 2021)
25
- BREAKING CHANGE: Removed support from versions older than PHP 7.3.
36
- BREAKING CHANGE: Removed SharedMemory (shmop) component for Segments.

sonar-scanner.sh

Lines changed: 0 additions & 41 deletions
This file was deleted.

src/SplitIO/Component/Cache/Storage/Adapter/PRedis.php

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,19 @@ private function isValidSentinelConfig($sentinels, $options)
6767
return true;
6868
}
6969

70+
private function validateKeyHashTag($keyHashTag)
71+
{
72+
if (!is_string($keyHashTag)) {
73+
return array('valid' => false, 'msg' => 'keyHashTag must be string.');
74+
}
75+
if ((strlen($keyHashTag) < 3) || ($keyHashTag[0] != "{") ||
76+
(substr($keyHashTag, -1) != "}") || (substr_count($keyHashTag, "{") != 1) ||
77+
(substr_count($keyHashTag, "}") != 1)) {
78+
return array('valid' => false, 'msg' => 'keyHashTag is not valid.');
79+
}
80+
return array('valid' => true, 'msg' => '');
81+
}
82+
7083
/**
7184
* @param mixed $options
7285
* @return string
@@ -77,19 +90,42 @@ private function getDefaultKeyHashTag($options)
7790
if (!isset($options['keyHashTag'])) {
7891
return "{SPLITIO}";
7992
}
80-
$keyHashTag = $options['keyHashTag'];
81-
if (!is_string($keyHashTag)) {
82-
throw new AdapterException("keyHashTag must be string.");
83-
} else {
84-
if ((strlen($keyHashTag) < 3) || ($keyHashTag[0] != "{") ||
85-
(substr($keyHashTag, -1) != "}") || (substr_count($keyHashTag, "{") != 1) ||
86-
(substr_count($keyHashTag, "}") != 1)) {
87-
throw new AdapterException("keyHashTag is not valid.");
93+
$validation = $this->validateKeyHashTag($options['keyHashTag']);
94+
if (!($validation['valid'])) {
95+
throw new AdapterException($validation['msg']);
96+
}
97+
return $options['keyHashTag'];
98+
}
99+
100+
101+
/**
102+
* @param mixed $options
103+
* @return string
104+
* @throws AdapterException
105+
*/
106+
private function selectKeyHashTag($options)
107+
{
108+
if (!isset($options['keyHashTags'])) { // check if array keyHashTags is set
109+
return $this->getDefaultKeyHashTag($options); // defaulting to keyHashTag or {SPLITIO}
110+
}
111+
$keyHashTags = $options['keyHashTags'];
112+
$msg = $this->isValidConfigArray($keyHashTags, 'keyHashTags'); // check if is valid array
113+
if (!is_null($msg)) {
114+
throw new AdapterException($msg);
115+
}
116+
$filteredArray = array_filter( // filter to only use string element {X}
117+
$keyHashTags,
118+
function ($value) {
119+
return $this->validateKeyHashTag($value)['valid'];
88120
}
121+
);
122+
if (count($filteredArray) == 0) {
123+
throw new AdapterException('keyHashTags size is zero after filtering valid elements.');
89124
}
90-
return $keyHashTag;
125+
return $selected = $filteredArray[array_rand($filteredArray, 1)];
91126
}
92127

128+
93129
/**
94130
* @param array $clusters
95131
* @return bool
@@ -143,7 +179,7 @@ private function getRedisConfiguration($options)
143179
switch ($_options['distributedStrategy']) {
144180
case 'cluster':
145181
if ($this->isValidClusterConfig($clusters)) {
146-
$keyHashTag = $this->getDefaultKeyHashTag($_options);
182+
$keyHashTag = $this->selectKeyHashTag($_options);
147183
$_options['cluster'] = 'redis';
148184
$redisConfigutation['redis'] = $clusters;
149185
$prefix = isset($_options['prefix']) ? $_options['prefix'] : '';

src/SplitIO/Version.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33

44
class Version
55
{
6-
const CURRENT = '7.0.0';
6+
const CURRENT = '7.1.0';
77
}

tests/Suite/Adapter/RedisAdapterTest.php

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,79 @@ public function testRedisWithoutCustomKeyHashtagClusters()
401401
$predis->getItem('this_is_a_test_key');
402402
}
403403

404+
public function testRedisWithClustersKeyHashTags()
405+
{
406+
$this->expectException(
407+
'SplitIO\Component\Cache\Storage\Exception\AdapterException',
408+
"keyHashTags must be array."
409+
);
410+
$predis = new PRedis(array(
411+
'clusterNodes' => array(
412+
'tcp://MYIP:26379?timeout=3'
413+
),
414+
'options' => array(
415+
'distributedStrategy' => 'cluster',
416+
'keyHashTags' => '{TEST}'
417+
)
418+
));
419+
420+
$predis->getItem('this_is_a_test_key');
421+
}
422+
423+
public function testRedisWithClustersKeyHashTagsInvalid()
424+
{
425+
$this->expectException(
426+
'SplitIO\Component\Cache\Storage\Exception\AdapterException',
427+
"keyHashTags size is zero after filtering valid elements."
428+
);
429+
$predis = new PRedis(array(
430+
'clusterNodes' => array(
431+
'tcp://MYIP:26379?timeout=3'
432+
),
433+
'options' => array(
434+
'distributedStrategy' => 'cluster',
435+
'keyHashTags' => array(1, 2)
436+
)
437+
));
438+
439+
$predis->getItem('this_is_a_test_key');
440+
}
441+
442+
public function testRedisWithClustersKeyHashTagsInvalidHashTags()
443+
{
444+
$this->expectException(
445+
'SplitIO\Component\Cache\Storage\Exception\AdapterException',
446+
"keyHashTags size is zero after filtering valid elements."
447+
);
448+
$predis = new PRedis(array(
449+
'clusterNodes' => array(
450+
'tcp://MYIP:26379?timeout=3'
451+
),
452+
'options' => array(
453+
'distributedStrategy' => 'cluster',
454+
'keyHashTags' => array("one", "two", "three")
455+
)
456+
));
457+
458+
$predis->getItem('this_is_a_test_key');
459+
}
460+
461+
public function testRedisWithClustersKeyHashTagsValid()
462+
{
463+
$this->expectException('\Predis\ClientException');
464+
$predis = new PRedis(array(
465+
'clusterNodes' => array(
466+
'tcp://MYIP:26379?timeout=3'
467+
),
468+
'options' => array(
469+
'distributedStrategy' => 'cluster',
470+
'keyHashTags' => array("{one}", "{two}", "{three}")
471+
)
472+
));
473+
474+
$predis->getItem('this_is_a_test_key');
475+
}
476+
404477
public function testRedisSSLWithClusterFails()
405478
{
406479
$this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException');

0 commit comments

Comments
 (0)