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

[Campaign] Add phpredis support #232

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4df146b
Configure using one time banners, configure persistent redis connection
pulzarraider Oct 5, 2023
855f27f
Fetch all running campaigns at once with `mget`
pulzarraider Oct 6, 2023
3115ed9
Optimize loading maps with `mget`
pulzarraider Oct 6, 2023
8e0ea94
Parse the user agent just once
pulzarraider Oct 6, 2023
596999a
Check segment cache only once
pulzarraider Oct 6, 2023
0e8f43e
Check user or browser in same segment just once
pulzarraider Oct 6, 2023
8844388
Store result of device detection in redis
pulzarraider Oct 7, 2023
cbe89f4
Fix error `Too few arguments to function PlainPhpShowtimeResponse::su…
pulzarraider Oct 9, 2023
986f2ac
Add phpredis support
pulzarraider Oct 9, 2023
3772b1e
Fix phpredis compatibility
pulzarraider Oct 9, 2023
3184363
Refresh redis instance after deserialization to avoid duplicated conn…
pulzarraider Oct 9, 2023
27d3514
Refresh redis instance in `Segment` after deserialization to avoid du…
pulzarraider Oct 10, 2023
defad10
Merge main optimization branch
pulzarraider Oct 10, 2023
66187a6
Code cleanup
pulzarraider Oct 10, 2023
1fcd2c5
Store device detection to local cache
pulzarraider Oct 10, 2023
b9b918e
Store device detection to local cache
pulzarraider Oct 10, 2023
bf12523
Merge branch 'configure_one_time_banner' into phpredis
pulzarraider Oct 10, 2023
0ec4b93
Fix tests
pulzarraider Oct 10, 2023
04161ec
Merge branch 'configure_one_time_banner' into phpredis
pulzarraider Oct 10, 2023
4cdb7f3
Merge master
pulzarraider Oct 27, 2023
8aba3c9
Rollback default value in ShowtimeResponse::success
pulzarraider Oct 27, 2023
c843942
Merge branch 'configure_one_time_banner' into phpredis
pulzarraider Oct 27, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Campaign/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ DB_PORT=3306

# Redis client to be used. Available values are:
# - predis: default value, used if nothing is set
# - phpredis: use if you have installed the php-redis extension for better performance
# - mock: fake implementation for tests
# It's recommended to set this variable only for testing scenarios.
# REDIS_CLIENT=
Expand All @@ -85,6 +86,9 @@ REDIS_HOST=redis
# Redis connection port. 6379 is the default port used by Redis installation.
REDIS_PORT=6379

# Specifies if the underlying redis connection resource should be left open when a script ends its lifecycle. Default: false
REDIS_PERSISTENT=false

# Alternative Redis connection configuration.
# The accepted format of a single connection is
#
Expand Down Expand Up @@ -249,3 +253,10 @@ MAXMIND_DATABASE=resources/assets/maxmind/GeoLite2-Country.mmdb
# In the case of same amount of variants the banner from more recent campaign is prioritized.

#PRIORITIZE_BANNERS_ON_SAME_POSITION=

# Flag whether the one time banners should be enabled or not.
#
# If this option is set to `true`, the user and browser one time banners are used in Showtime.
# If you are not using the one time banners, set this option to `false` for better performance.
# The default value is `true`.
ONE_TIME_BANNER_ENABLED=true
15 changes: 12 additions & 3 deletions Campaign/app/Contracts/Crm/Segment.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use App\Contracts\SegmentAggregator;
use App\Contracts\SegmentContract;
use App\Contracts\SegmentException;
use App\Jobs\CacheSegmentJob;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use Illuminate\Support\Collection;
Expand All @@ -27,13 +26,20 @@ class Segment implements SegmentContract

private $redis;

public function __construct(Client $client, \Predis\Client $redis)
public function __construct(Client $client, \Predis\Client|\Redis $redis)
{
$this->client = $client;
$this->providerData = new \stdClass;
$this->redis = $redis;
}

public function setRedisClient(\Predis\Client|\Redis $redis): self
{
$this->redis = $redis;

return $this;
}

public function provider(): string
{
return static::PROVIDER_ALIAS;
Expand Down Expand Up @@ -128,7 +134,10 @@ public function getProviderData()

public function addUserToCache(CampaignSegment $campaignSegment, string $userId): bool
{
return $this->redis->sadd(SegmentAggregator::cacheKey($campaignSegment), [$userId]) ?: false;
return $this->redis->sadd(
SegmentAggregator::cacheKey($campaignSegment),
$this->redis instanceof \Redis ? $userId : [$userId]
) ?: false;
}

public function removeUserFromCache(CampaignSegment $campaignSegment, string $userId): bool
Expand Down
32 changes: 30 additions & 2 deletions Campaign/app/Contracts/SegmentAggregator.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,20 @@ public function list(): Collection

public function checkUser(CampaignSegment $campaignSegment, string $userId): bool
{
if (!isset($this->contracts[$campaignSegment->provider])) {
return false;
}

return $this->contracts[$campaignSegment->provider]
->checkUser($campaignSegment, $userId);
}

public function checkBrowser(CampaignSegment $campaignSegment, string $browserId): bool
{
if (!isset($this->contracts[$campaignSegment->provider])) {
return false;
}

return $this->contracts[$campaignSegment->provider]
->checkBrowser($campaignSegment, $browserId);
}
Expand All @@ -66,6 +74,10 @@ public function users(CampaignSegment $campaignSegment): Collection

public function cacheEnabled(CampaignSegment $campaignSegment): bool
{
if (!isset($this->contracts[$campaignSegment->provider])) {
return false;
}

return $this->contracts[$campaignSegment->provider]
->cacheEnabled($campaignSegment);
}
Expand Down Expand Up @@ -136,6 +148,15 @@ public function getErrors()
return $this->errors;
}

public function refreshRedisClient(Client|\Redis $redisClient): void
{
foreach ($this->contracts as $contract) {
if (method_exists($contract, 'setRedisClient')) {
$contract->setRedisClient($redisClient);
}
}
}

/**
* SegmentAggregator contains Guzzle clients which have properties defined as closures.
* It's not possible to serialize closures in plain PHP, but Laravel provides a workaround.
Expand All @@ -158,9 +179,16 @@ public function serializeToRedis()
Redis::set(\App\Models\Alignment\Map::ALIGNMENTS_MAP_REDIS_KEY, $alignmentsMap->alignments()->toJson());
}

public static function unserializeFromRedis(Client $redisClient): ?SegmentAggregator
public static function unserializeFromRedis(Client|\Redis $redisClient): ?SegmentAggregator
{
$serializedClosure = $redisClient->get(self::SEGMENT_AGGREGATOR_REDIS_KEY);
return $serializedClosure ? unserialize($serializedClosure)() : null;

/* @var ?SegmentAggregator $segmentAggregator */
$segmentAggregator = $serializedClosure ? unserialize($serializedClosure)() : null;

// set the redis to avoid duplicated connection
$segmentAggregator?->refreshRedisClient($redisClient);

return $segmentAggregator;
}
}
12 changes: 5 additions & 7 deletions Campaign/app/Http/Showtime/LazyDeviceDetector.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@
namespace App\Http\Showtime;

use DeviceDetector\Cache\PSR6Bridge;
use Predis\ClientInterface;
use Psr\Cache\CacheItemPoolInterface;

class LazyDeviceDetector
{
private $detector;

private $redis;

public function __construct(ClientInterface $redis)
{
$this->redis = $redis;
public function __construct(
private readonly CacheItemPoolInterface $cachePool
) {
}

public function get($userAgent)
Expand All @@ -22,7 +20,7 @@ public function get($userAgent)
$this->detector = new \DeviceDetector\DeviceDetector();
$this->detector->setCache(
new PSR6Bridge(
new \Cache\Adapter\Predis\PredisCachePool($this->redis)
$this->cachePool
)
);
}
Expand Down
Loading