Ниже — задачка по работе с двухуровневым кешем (БД + redis) на php, которую мне нужно было решить за ~20 минут отведенного времени.

Дано:

три хранилища:

  • база данных (mysql),

  • кэш (redis)

  • API (http)

Необходимо:

  • по id пользователя вернуть данные из хранилища

Условия:

  • Если данные есть в кэше — вернуть из кэша.

  • Если данных в кэше нет, то взять из базы, затем закэшировать и вернуть.

  • Если данных нет ни в кэше, ни в базе, то взять из API, сохранить в базе, закэшировать и вернуть.

  • В API данные есть всегда.

(звезда) Реализовывать подключения к хранилищам и непосредственно запросы к ним не нужно, достаточно пустых методов в классах.

Решение:

(звезда) Сгенерировано нейронкой ChatGpt 3.5 (с моими доработками ниже)

Комментарий ИИ:

В этом примере класс DataRetriever отвечает за получение данных из трех хранилищ. Если данные есть в кэше, они возвращаются сразу. Если данных нет в кэше, они извлекаются из базы данных, сохраняются в кэше и затем возвращаются. Если данных нет ни в кэше, ни в базе данных, они запрашиваются из API, сохраняются в базе данных и кэше, и затем возвращаются.

Обратите внимание, что в этом примере я использовал пустые методы для подключений к хранилищам и запросов к ним. Вам нужно будет реализовать эти методы с учетом вашей конкретной среды выполнения и используемых инструментов (например, использовать соответствующие библиотеки для работы с MySQL, Redis и HTTP).

<?php

class Database {
    public function getDataFromDatabase($userId) {
        // Подключение к базе данных и выполнение запроса
        // Возвращает данные пользователя из базы данных
    }
}

class Cache {
    public function getDataFromCache($userId) {
        // Подключение к кэшу и получение данных по ключу $userId
        // Возвращает данные пользователя из кэша
    }

    public function cacheData($userId, $data) {
        // Подключение к кэшу и сохранение данных по ключу $userId
    }
}

class API {
    public function getDataFromAPI($userId) {
        // Подключение к API и получение данных пользователя по id
        // Возвращает данные пользователя из API
    }
}

class DataRetriever {
    private $database;
    private $cache;
    private $api;

    public function __construct(Database $database, Cache $cache, API $api) {
        $this->database = $database;
        $this->cache = $cache;
        $this->api = $api;
    }

    public function getData($userId) {
        $data = $this->cache->getDataFromCache($userId);

        if (!$data) {
            $data = $this->database->getDataFromDatabase($userId);
            
            if (!$data) {
                $data = $this->api->getDataFromAPI($userId);
                $this->database->saveDataToDatabase($userId, $data);
            }

            $this->cache->cacheData($userId, $data);
        }

        return $data;
    }
}

// Пример использования

$database = new Database();
$cache = new Cache();
$api = new API();

$dataRetriever = new DataRetriever($database, $cache, $api);

$userId = 123;
$data = $dataRetriever->getData($userId);

// Используем данные пользователя
echo "Данные пользователя: " . $data;

Дорабатываем до рабочей (демо) версии:

  • добавляем интерфейс для источников данных (т.к. по сути операции у них одинаковые)

  • добавляем вероятность получения данных из источников через трейт

  • добавляем примеры демо- данных (для проверки)

<?php

/**
 * Генератор случайных данных
 */
trait RandomGen
{
    public function getRandomExist()
    {
        return (rand(0, 2) == 2);
    }
}

/**
 * Единый интерфейс под разные источники данных
 */
interface DataSource {
    public function getUserData(int $userId): ?array;
    public function saveUserData(int $userId, array $data): bool;
}

/**
 * Источник данных - База данных
 */
class DatabaseSource implements DataSource
{
    use RandomGen;

    public function getUserData(int $userId): ?array
    {
        // Подключение к базе данных и выполнение запроса
        // Возвращает данные пользователя из базы данных

        if($this->getRandomExist()) {
            return [$userId, 'nameFromDB'];
        }

        return null;
    }

    public function saveUserData(int $userId, array $data): bool
    {
        // сохранение даных в БД
        return true;
    }
}

/**
 * Источник данных - Кеш
 */
class CacheSource implements DataSource
{
    use RandomGen;

    public function getUserData($userId): ?array
    {
        // Подключение к кэшу и получение данных по ключу $userId
        // Возвращает данные пользователя из кэша

        if($this->getRandomExist()) {
            return [$userId, 'nameFromCache'];
        }

        return null;
    }

    public function saveUserData($userId, $data): bool
    {
        // Подключение к кэшу и сохранение данных по ключу $userId
        return true;
    }
}

/**
 * Источник данных - API
 */
class ApiSource implements DataSource
{
    use RandomGen;

    public function getUserData($userId): ?array
    {
        // Подключение к API и получение данных пользователя по id
        // Возвращает данные пользователя из API

        if($this->getRandomExist()) {
            return [$userId, 'nameFromAPI'];
        }

        return null;
    }

    public function saveUserData($userId, $data): bool
    {
        // Сохранение данных в АПИ
        return true;
    }
}

class DataRetriever
{
    private $database;
    private $cache;
    private $api;

    public function __construct(DatabaseSource $database, CacheSource $cache, ApiSource $api)
    {
        $this->database = $database;
        $this->cache = $cache;
        $this->api = $api;
    }

    /**
     * Get user data
     * @param int $userId
     * @return array|null
     */
    public function getUserData(int $userId)
    {
        $data = $this->cache->getUserData($userId);

        if (!$data) {
            $data = $this->database->getUserData($userId);

            if (!$data) {
                $data = $this->api->getUserData($userId);
                if($data) {
                    $this->database->saveUserData($userId, $data);
                }
            }

            $this->cache->saveUserData($userId, $data);
        }

        return $data;
    }
}

//////////////////////////////////////////////////////////////////////////////////
// Пример использования

$database = new DatabaseSource();
$cache = new CacheSource();
$api = new ApiSource();

$dataRetriever = new DataRetriever($database, $cache, $api);

for($userId = 1; $userId < 10; $userId++) {
    $data = $dataRetriever->getUserData($userId);
    // Используем данные пользователя
    echo "Данные пользователя: #$userId: " . var_export($data, 1) . "nn";
}

Результат работы:

php index.php

Данные пользователя: #1: array (
  0 => 1,
  1 => 'nameFromAPI',
)

Данные пользователя: #2: array (
  0 => 2,
  1 => 'nameFromDB',
)

Данные пользователя: #3: array (
  0 => 3,
  1 => 'nameFromAPI',
)

Данные пользователя: #4: array (
  0 => 4,
  1 => 'nameFromCache',
)

Данные пользователя: #5: array (
  0 => 5,
  1 => 'nameFromCache',
)

Данные пользователя: #6: array (
  0 => 6,
  1 => 'nameFromDB',
Данные пользователя: #9: array (
  0 => 9,
  1 => 'nameFromAPI',
)
Tags

Нет Ответов

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Рубрики


Подпишись на новости
👋

Есть вопросы?