Обзор систем кеширования в onPHP

    Добрый день!
    В этой статье мы расскажем о том, как мы работаем с кешем в plus1.wapstart.ru, какие проблемы у нас возникали и как мы решали некоторые частные случаи.

    Для начала о терминологии.


    Под «кешем» в этой статье я буду понимать какое-то быстрое хранилище, которое может использоваться, в том числе, и для кеширования. При этом хранилище должно обладать стандартизированным интерфейсом.
    Сервер/хранилище — это какое-нибудь приложение, которое может хранить данные и давать к ним доступ по интерфейсу, который описан ниже. Например, этим приложением может быть memcached.

    Мы используем фреймворк onPHP. В нем есть абстрактный класс CachePeer, от которого должны наследоваться все реализации кешей. Интерфейс любой реализации сводится к следующим методам.

    		abstract public function get($key);
    		abstract public function delete($key);
    		
    		abstract public function increment($key, $value);
    		abstract public function decrement($key, $value);
    		
    		abstract protected function store(
    			$action, $key, $value, $expires = Cache::EXPIRES_MEDIUM
    		);
    		
    		abstract public function append($key, $data);
    

    В нашем мире существуют следующие реализации CachePeer (кликабельно)



    На этой диаграмме представлены как реализации хранилищ (см. мост), так и разнообразные декораторы, решающие частные задачи.

    В onphp есть поддержка работы c Redis; Memcached — целых две реализации: на сокетах и с использованием Memcache (http://php.net/Memcache); мы можем работать с SharedMemory. Если ничего из этого в инсталляции нет, то мы будем работать с памятью приложения.
    Не смотря на все многообразие поддерживаемых технологий, мне не известно ни одного проекта на onphp, который бы использовал что-то отличное от Memcached.
    Memcached правит этим миром. :)

    У нас есть две реализации Memcache по следующим причинам:
    • когда писался Memcached (на сокетах) extension Memcache еще не был написан.
      да, мы знаем о конфликте имен с http://php.net/Memcached, в master это уже поправлено.
    • Реализация подключения на сокетах доступна вам даже тогда, когда вы не имеете доступа к настройкам php и не можете поставить необходимые расширения.

    Мы практически повсеместно используем PeclMemcached, который подключается к серверу через расширение Memcache. При прочих равных он работает быстрее и к тому же поддерживает pconnect.
    С альтернативной библиотекой (MemcacheD) у нас как-то не сложилось. Я пробовал писать под нее реализацию, но на тот момент (около двух лет назад) она была не очень стабильна.

    О декораторах:


    Когда соотношение проектов на одну кеширующую систему становится больше одного, то следует использовать WatermarkedPeer. Его смысл сводится к методу getActualWatermark(). Например, реализация get становится такой
    		public function get($key)
    		{
    			return $this->peer->get($this->getActualWatermark().$key);
    		}
    

    Это позволяет избежать конфликта ключей. Данные из разных проектов/классов/ и т.д. будут записаны под разными ключами. В остальном — этот кеш является стандартным декоратором к какой-нибудь реализации хранилища.

    Если вам необходимо разнести данные по множеству кеширующих систем, то можно либо воспользоваться кластером мемкеша из поставки php, либо взять один из наших аггрегатных кешей. У нас их несколько:
    • AggregateCache определяет сервер, который будет работать с текущим ключем исходя из значения mt_rand от него с заранее переопределенным mt_srand. Звучит диковато, но на самом деле все очень просто.
    • SimpleAggregateCache — вообще простой как кирпич. Нужный сервер определяется исходя из остатка от деления числового представления ключа на число серверов.
    • кружочекCyclicAggregateCache — реализацию мы подсмотрели у last.fm. Она весьма изящна. Берем окружность, на нее наносим «точки монтирования» серверов.
      Причем число точек для каждого сервера будет пропорционально его весу. При получении запроса ключ, так же маппим в какую-то точку на окружности. Его будет обрабатывать тот сервер, чья точка имеет меньшее расстояние к точке ключа.
      Плюс этого подхода в том, что при добавлении сервера в пул, девалидируется лишь часть значений, а не все. Так же при выводе сервера из пула, пропадают только те значения, которые на нем хранились, при этом остальные сервера более-менее равномерно забирают его нагрузку. Об идее алгоритма подробнее можно почитать здесь или здесь.

    На этом более-менее стандартная часть заканчивается. Большинству приложений должно хватить этого набора реализаций для создания нормальной системы кеширования.

    Дальше начинаются частности.


    • DebugCachePeer — название у него до такой степени самодокументируемо, что описывать его я смысла не вижу.
    • ReadOnlyPeer — бывают такие кеши, из которых можно только читать, но нельзя в них писать. Например они могут записываться из какого-то другого места, или вовсе быть реализованы по-другому, например как наша рыба. Для этих хранилищь целесообразно использовать ReadOnlyPeer, т.к. он на стороне приложения будет гарантировать то, что данные будут только читаться, но не записываться/обновляться.
    • CascadeCache — у вас есть локальный быстрый ненагруженный кеш, например на сокете. И есть какой-то удаленный кеш, который при этом еще и преднаполняется. Если для вашего приложения допустимо использование слегка устаревших данных, то можно использовать CascadeCache. Он будет делать читающие операции из локального кеша, при этом, если данные в локальном кеше отсутствуют, они будут запрошены в удаленном кеше.
      Для «негативных» результатов (null) можно использовать одну из двух стратегий — они будут либо сохранены в локальном кеше, либо проигнорированы.
    • MultiCachePeer — у вас есть преднаполнятор кешей. При этом, желательно чтобы он мог наполнять десяток «локальных» кешей на десяти серверах.
      Другими словами, мы хотим писать данные в одном место, а читать их, в общем случае, из другого. При этом, на каждом сервере с приложениями должна быть одна и таже конфигурация — для удобства деплоя. Для этого можно использовать MultiCachePeer примерно с таким конфигом:

      		MultiCachePeer::create(
      			PeclMemcached::create('localhost', 11211),
      			array(
      				PeclMemcached::create('meinherzbrennt', 11211),
      				PeclMemcached::create('links234', 11211),
      				PeclMemcached::create('sonne', 11211),
      				PeclMemcached::create('ichwill', 11211),
      				PeclMemcached::create('feuerfrei', 11211),
      				PeclMemcached::create('mutter', 11211),
      				PeclMemcached::create('spieluhr', 11211)
      			)
      		);
      


    • SequentialCache — представьте, что у вас есть хранилище, которое иногда падает или просто недоступно. Например, оно может иногда выводиться на обслуживание, перезапускаться и т.д. При этом, данные приложение хочет получать всегда. Для покрытия этой ситуации можно использовать SequentialCache, примерно с таким конфигом:
      	$cache = 
      		new SequentialCache(
      			PeclMemcached::create('master', 11211, 0.1), //третий параметр конструктора - это таймаут.
      			array(
      				PeclMemcached::create('backup', 11211, 0.1),		
      			)
      		)
      


    Т.к. практически все реализации используют паттерн декоратор, то их можно вполне успешно комбинировать.
    Например, допустима такая конструкция:

    	$swordfish =
    		ReadOnlyPeer::create(
    			new SequentialCache(
    				PeclMemcached::create('localhost', 9898, 0.1),
    				array(
    					PeclMemcached::create('backup', 9898, 0.1),
    				)
    			)
    		);
    


    Или даже такая:

    	$swordfish =
    		CascadeCache::create(
    			PeclMemcached::create('unix:///var/run/memcached_sock/memcached.sock', 0),
    			ReadOnlyPeer::create(
    				new SequentialCache(
    					PeclMemcached::create('localhost', 9898, 0.1),
    					array(
    						PeclMemcached::create('backup', 9898, 0.1),
    					)
    				)
    			),
    			CascadeCache::NEGATIVE_CACHE_OFF
    		);
    


    В этом случае данные будут сначала искаться в локальном мемкеше, доступном по unix-socket, в случае если их там нет, то будет запрошен «мемкеш» localhost:9898. А в случае, если он недоступен, то backup:9898. При этом, приложение знает, что из кешей на портах 9898 можно только читать, но не писать.

    На этом возможности кешей из onphp не заканчиваются. Можно делать совершенно разные конфигурации, которые будут покрывать ваши задачи. CachePeer из onphp — это круто.
    ps. Когда-то давно здесь говорили о цикле статей про onphp. Начало положено этим постом. В будущем мы затронем другие темы, связанные с фреймворком и его применением в plus1.wapstart.ru.

    pps. Пользуясь случаем, сообщаю, что мы ищем людей:
    hantim.ru/jobs/11163-veduschiy-qa-menedzher-rukovoditel-otdela-testirovaniya
    hantim.ru/jobs/11111-veduschiy-php-razrabotchik-team-leader
    WapStart
    Компания
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 56

      +7
      меГафрейворк!!! в первом попавшемся class`е Timestamp который extends Date
      protected static function getFormat() { return 'Y-m-d H:i:s'; }
      Убиться, простите, веником! Зато у нас ООП!
        +1
        Ну, ребята не зря значит ведущего разраба ищут :)
          0
          Извините, что отвечаю не на тот коммент, но внизу мое сообщение не заметят.
          mc_dir, в чем претензия к данному коду? Да static, да protected, и да — описывает формат. Если внимательно почитать код, то можно понять почему сделано именно так. Мы не принуждает использовать наш фреймворк. Кстати, мы его не продаем. ;)
          Если он вам не по душе, просто не обращайте на него внимания. Если хотите улучшить — код открыт, патчи приветствуются. )

          vaevictus Мы ищем ведущего разработчика не для того, чтобы переписать класс Timestamp, у нас и без этого хватает работы ;)

          OnPHP — это фреймворк, который используется для вполне утилитарных задач. Вряд ли кто-то будет писать туда код «чтобы было». В последнее время код пишется под конкретную задачу. То же самое касается правок — если старый код никто не исправляет, значит он работает.
            +1
            Претензий никаких. Скорее недоумение. Почему например не константа класса, свойство класса или параметр — почему именно метод который ни че не делает, зато " Да static, да protected, и да — описывает формат." — возвращает строчку?
            Раскройте уже сакральный секрет, почему именно так? Остальных «фичь» фрейворка и без меня уже тут описали.
              +2
              не константа класса

              Т.к. переопределяется в наследнике.
              свойство класса

              тогда его тоже пришлось бы делать статичным, т.к. этот метод используется вне объекта.
              параметр

              А это противоречит логике.

              Данный метод нужен для создания даты — github.com/onPHP/onphp-framework/blob/master/core/Base/Date.class.php#L75
              Для ее импорта из примитивов или базы — github.com/onPHP/onphp-framework/blob/master/core/Base/Date.class.php#L302
              И для toString() по-умолчанию — github.com/onPHP/onphp-framework/blob/master/core/Base/Date.class.php#L256

              Не смотря на то, что этот код писал не я, мне понятно почему реализовано именно так. Да, возможно, реализацию можно сделать лучше, но текущий вариант вполне работоспособен.

              фичь

              Это от слова «дичь»? )
                +1
                Т.е. Данный метод нужен для создания даты — без него никак?
                И И И… Мот таки не создавать protected static function getFormat()? «Да, возможно, реализацию можно сделать лучше» — так как можно ее сделать лучше? Предложите свой вариант, если мои не устроили.

                А то ж без глубокого понимания LSB мозг можно поломать как это все работает (
                Слово «фичь» взята в скобки — ели не понятно его значение, считайте его дичью — так проще ж. Проще сказать, что работает, чем сделать качественно, производительно, понятно и красиво.

                «Мало у кого хватает мудрости предпочесть обманчивой похвале благотворную критику.» Ларошфуко
                  +1
                  «меГафрейворк!!! в первом попавшемся class`е Timestamp который extends Date
                  protected static function getFormat() { return 'Y-m-d H:i:s'; }
                  Убиться, простите, веником! Зато у нас ООП!»
                  О да. Настоящая благотворная критика, как это никто ее не распознал. Восклицательных знаков больше чем предложений. И не важно критику что статья о другом, главное заявить о себе погромче (!).
                    0
                    Предложите свой вариант, если мои не устроили.

                    А что вы предложили? Перенести строковую константу из метода в свойство? Шило на мыло, не находите?
                      0
                      Предложите красивое решение с вашей точки зрения.
                        0
                        Для чего?
                        У нас нет открытого issue по поводу «некрасивости» Timestamp. Как только его заведет кто-нибудь из пользователей фреймворка, мы обязательно предложим решение.
                          +3
                          Ребят, Вы просто говорите на разных языках. С точки зрения кододрочерства, да, нужно придумывать красивые решения. С точки зрения практичности — в профайлере кусок кода не выбивается, свою работу делает — да и фиг с ним.

                          dovg, научитесь отвечать в стиле «Да, Вы правы, но в данный момент у нас есть более приоритетные задачи. Но мы будем очень рады, если Вы окажите нам помощь, и предложите лучшее решение.»
                    +1
                    Если бы Ваш метод выглядел как
                    protected static function getFormat() { return _DATE_FORMAT; }
                    Вопросов не было бы. Сейчас же, чтобы поменять формат даты, мне прийдётся либо менять код ядра (хоть и не значительно), либо плодить наследника из-за одной строчки.

                    Я уж молчу про ситуацию, когда у меня для разных регионов разные форматы дат нужны.
                      0
                      Вам _не_нужно_ менять этот метод. Он используется для внутренних целей класса и наружу не торчит.

                      Нужный формат при выводе можно получить используя toFormatString($format); — github.com/onPHP/onphp-framework/blob/master/core/Base/Date.class.php#L261
                        0
                        И всё равно, делать строковую константу в коде не хорошо :).

                        О, придумал пример, мне в наследнике, для внутренних нужд класса надо использовать два формата дат. Если мы используем константу, то код будет выглядеть как то в стиле

                        if ($firstFormat) return _DATE_FORMAT; else return _SECOND_DATE_FORMAT;
                        (название констант, конечно же, можно сделать куда более значимыми)
                        Сейчас же, такой код будет выглядеть в лучшем случае, как то так:
                        if ($firstFormat) return parent::getFormat(); else return "another format";
                        Что, согласитесь, не совсем хорошо.

                        Ваша главная ошибка, на мой взгляд, это то, что Вы защищаете не слишком хороший код, вместо того, чтоб ответить что-то в стиле «Да, спасибо что указали на недостаток, внесём в список будущих изменений. Посмотрите и другие файлы в исходниках, возможно код, присутствующий в них Вам понравится больше», ведь все понимают, что идеального кода, особенно в больших проектах, не существует.

                        А Вы, зачем то, становитесь в позу. Ну зачем всё это? Ведь любая ремарка к Вашему фреймворку — это плюс, это возможность сделать его лучше!
                          0
                          это возможность сделать его лучше!

                          Спасибо. )

                          По теме — ваша идея понятна. Обсудим в рассылке.
                  +2
                  Фреймворк не должен писаться под конкретную задачу. Фреймворк — это набор общих правил и инструментов. А под конкретную задачу вы добавляете свои изменения/модули.

                  В противном случае, Вы можете внести изменения в фреймворк, которые сделают его не применимым для некого рода других «конкретных» задач.

                  И да, код
                  for ($i=1, $b = $a; $i<10; $a+=$b, $i++);
                  тоже вполне рабочий. Хотите сказать он имеет право на существование?
                    0
                    Фреймворк не должен писаться под конкретную задачу.

                    Вы неправильно меня поняли. Фраза начиналась со слов «в последнее время». Это важно.

                    В противном случае, Вы можете внести изменения в фреймворк, которые сделают его не применимым для некого рода других «конкретных» задач.

                    Поэтому у нашей компании есть свой форк, который слегка отличается от master. github.com/WapStart/onphp-framework/
                    Код общего назначения мы сливаем в него. Иногда он попадает в мастер, а иногда сообщество его не принимает. По похожим причинам, кстати :)
                    0
                    Не переживайте по поводу отдельных комментариев — с отдельными товарищами и их подходу к проектированию я уже успел столкнуться в комментариях к другой статье.

                    А в общем идея Ваша мне понравилась — что-то полезное для себя извлек.

                    Вот только меня смущает этот момент:
                    SequentialCache — представьте, что у вас есть хранилище, которое иногда падает или просто недоступно. Например, оно может иногда выводиться на обслуживание, перезапускаться и т.д. При этом, данные приложение хочет получать всегда. Для покрытия этой ситуации можно использовать SequentialCache, примерно с таким конфигом:

                    Вы представляете сколько будет ожидать приложение подключения к упавшему серверу кеширования прежде чем отвалится по timeout-у? Это только увеличит время получения данных а не уменьшит его.
                      0
                      Спасибо.

                      В представляете сколько будет ожидать приложение подключения к упавшему серверу кеширования
                      прежде чем отвалится по timeout-у? Это только увеличит время получения данных а не уменьшит его.

                      У нас мемкеши — это не только кеширующая подсистема, но и система хранения.
                      Т.е. если данных нет в кеше, то приложение считает, что их нет вообще.

                      Приложение будет ждать столько времени, сколько мы указали в конструкторе PeclMemcache. Я вон тут писал, что сейчас можно использовать float в качестве timeout
                        0
                        Если я не ошибаюсь в спецификации протокола TCP минимальное значение UTO равно одной секунде. Так что тут еще под вопросом как это было реализовано в PeclMemcache.
                          0
                          Не обязательно дожидаться ошибки нижележащего протокола для генерации ошибки по тайм-ауту.
                            –1
                            Обязательно. Пока TCP сессия не закроется — а она идентифицируется четверкой <client_ip, client_port, server_ip, server_port> — соединение не будет установленно. У нас же не одно соединение устанавливается.
                  +1
                  Вы его недооцениваете =)
                    0
                    Между прочим, единственный формат даты, позволяющий корректно сравнивать даты в строковом виде (ну, единственный с точностью до украшательств).

                    Не знаю, зачем это могло понадобиться, но, вполне возможно, такая архитектура взята не с потолка.
                      –2
                      как думаете, что будет быстрее сравниваться
                      1352731759 > 1352731760
                      или
                      '2012-11-12 18:50:21' < '2012-11-12 18:50:60'
                      ?
                      Придумать можно много чего — в языке декораторов ( не следует путать с одноимённым шаблоном проектирования) нет, че ж их тоже можно придумать.
                        0
                        Не всегда есть «быстрее» означает «лучше».
                        Например, в некоторых хранилищах целочисленных ключей может вообще не быть.
                        Например, их нет в файловой системе (понимаю, для кэша это очень странный выбор, но разве Timestamp используется только при кэшировании?)
                          +3
                          При сравнении мы используем целочисленное значение даты и времени с кодом можно ознкомится ниже по ссылке
                          github.com/onPHP/onphp-framework/blob/master/core/Base/Date.class.php#L109
                        +1
                        А что вас смущает в данном коде?
                          0
                          mc_dir озвучьте свою позицию, пожалуйста
                            0
                            Меня вот лично смущает несколько моментов:

                            1. Хардкод. Зачем нужен метод всегда возвращающий одну строку?
                            2. В чем разница между вот такими вызовами: date('DEFAULT_FORMAT') vs Timestamp::now()?
                            3. Cтатические вызовы увеличивают связанность кода и невозможность его повторного использования.
                              +2
                              date('DEFAULT_FORMAT') выглядит ужасно.

                              Timestamp::now() выглядит понятно и самодокументируемо.
                                –1
                                facepalm)

                                Вы хоть смотрели что отдает Timestamp::now()?
                                Как вообще надо думать, чтобы ожидать значение этого вызова как дату в определенном формате?

                                –3
                                Ваш подход скорее функциональный, а мы же придерживаемся ООП со всеми плющками (переопределение, наследование ...), кстати onPHP один из не многих фреймворков, где с самого начала идет жесткая привязка к ООП, т.е вы не встретите часть кода функционального а часть ООП, мы старались с ссамого начала избежать такой путаницы.
                              –2
                              покажите свой код, который вы прикрепляете к резюме, или ссылку на ваши репозитории на github
                                +3
                                Это вы еще не добрались до реализации Singleton при помощи eval в Singleton.class.php
                                ...
                                // can't call protected constructor through reflection
                                eval(
                                   '$object = new '.$class
                                   .'($args['.implode('],$args[', array_keys($args)).']);'
                                );
                                ...
                                


                                  –5
                                  Вы год копирайта посмотрите

                                  ps. Если его до сих пор не трогали — значит он работает видимо без нареканий, когда кто-то с багом столкнется или ему захочется актуализировать его — он перепишется, возможно даже с late static binding.

                                  Вам та же просьба что и к товарищу выше: покажите свой код, который вы прикрепляете к резюме, или ссылку на ваши репозитории на github
                                    0
                                      +4
                                      если прокомментировать код в вашем стиле то: открываю файл DesignerConnections.php {{{
                                      <?php
                                      return array(
                                      );
                                      }}}

                                      wtf?

                                      А если посмотреть www/system/app/Application.php то:

                                      1. какого этот файл находится в docRoot как я понимаю?

                                      2. Многочисленные нарушения coding style, trailing tabs и т.п.

                                      3. {{{

                                      public function __construct()
                                      {
                                      $this->_init();
                                      }
                                      }}}

                                      wtf?

                                      4. Комментарии в стиле капитан очевидность

                                      5. У вас что-то есть в архитектуре что не является static? От "::" рябит в глазах

                                      6. Это не ООП, у вас god функции, отсюда и необходимость писать «комментарии»

                                      7. метод run вообще за гранью {{{
                                      public function run()
                                      {
                                      header('Content-Type: text/html; charset=utf-8');

                                      $page = Request::getInstance()->getPart(0);

                                      if($page === self::$_mainConfig['adminPath'])
                                      $this->_runBackend();
                                      else
                                      $this->_runFrontend();
                                      }
                                      }}}

                                      getPart(0) — любой поймет конечно что это

                                      header — конечно же вы знаете что ваше Application всегда отдает text/html а если ему надо будет отдать text/xml — вы что будете делать? подозреваю что костылить

                                      вот так примерно выглядит ваш пост по Singleton.
                                        0
                                        С удовольствием обсужу с вами все ваши замечания. Но не в этом треде.
                                        PS: DesignerConnections.php заполняется автоматически системой, как и многие другие файлы конфигурации.
                                          0
                                          Тут нечего обсуждать, на вкус и цвет как известно фломастеры разные.
                                            0
                                            Не вижу ничего плохого, если кто-то покажет на недоработки и огрехи. Эту информацию можно собрать проанализировать, исправить проблемы. Всего учесть невозможно, тут знающие люди могут подсказать варианты решения.
                                              –1
                                              Нет, тут проблема была в том, что у вас тоже не совсем «идеальный» код, но при этом вы «тычете» другим на это, не стоит так делать. У вас тоже по коду есть много притензий, которые были отмечены в вашем посте давнишнем на хабре, но приэтом там умолчали о половине «плохо пахнущих» моментов, т.к. это никому не нужно просто.
                                                +2
                                                Я не оправдываюсь тем, что раз так, значит работает. Планомерно исправляю претензии и открыт конструктивной критике, не претендую на гениальность. И для меня свои фломастеры пахнут ( к разговору о статиках и например вашем сессионном хранилище, да и вообще по коду)

                                                Использование eval, не мне вам объяснять, что так не стоит делать.

                                                Еще одно отличие я не заявляю что у меня " Аутичный/ Аутентичный" фреймворк, то что вы смотрели это приложение, которое выполняет четко определенную функцию. Остальное на вкус разработчиков, какие фреймворки брать и как их использовать. Про непонятные моменты есть typeHint (в library не капитанские комменты, хотя соглашусь их нехватает). Про ООП, оно используется там где необходимо, например в реализации сложной структуры кодогенератора js. (Приоритеты у проектов совершенно разные в нашем проекте не приветствуется излишняя абстракция )

                                                Не хотите слышать упреки советы по поводу своего кода не публикуйтесь.
                                                Как вы общаетесь со своими коллегами с такими болезненными реакциями?

                                                PS: Мне показалось интересным то, что вы реализовали множество адаптеров для различных бэкендов кэширования и сами подходы… Жаль, что рассуждаем мы сейчас не об этом…
                                                  +2
                                                  Интересная ссылка у меня есть: Ad hominem.
                                                    0
                                                    У вас критика неконструктивная.

                                                    Можно сказать: у тебя сын дебил
                                                    В ответ получить в табло

                                                    А можно: твоему бы сыну таблицу умножения подтянуть
                                                    И в ответ получить информацию что сыну два года и он только начал ее учить

                                                    Вот вы занимаетесь первым
                                                      +1
                                                      Зашибись. Coh сначала пишет коммент не «об этом», а потом чуть позже пишет в P.S. «жаль что рассуждаем мы сейчас не об этом...». А заставлял то кто писать не «об этом» отчего теперь надо сожалеть?
                                                        –2
                                                        Читайте внимательнее тред начался с «Обратите внимание на не лучшую реализацию Singletone» в onPHP и к чему мы пришли в итоге?
                                                          0
                                                          Тред начался с «Это вы еще не добрались до реализации Singleton при помощи eval в Singleton.class.php». А это несколько более недружелюбный тон нежели «Обратите внимание на не лучшую реализацию Singletone».
                                        +1
                                        Тут я придерживаюсь мнения: «Не нравится — исправь». Open source же. Предложите решение лучше.
                                      0
                                      В общих чертах если можно — как происходит работа с записью во все эти кэши и какую роль выполняют кэш-сервера, кроме локального и бэкапного/запасного? Пишем разово в мастер-кэш и потом кастим изменения для остальных, не удерживая скрипт, либо пишем все сразу? Короче с синхронизацией всего этого чуда как, примерная схема?
                                        0
                                        Мы решаем частную задачу: нам нужно максимально быстро показывать хорошие баннеры. Списки баннеров, которые предположительно подходят для показа, строятся в одном месте и раскидываются по всем сервера. Запрашиваются же они всегда с localhost (только localhost для всех разный :). Мгновенная синхронизация нам не нужна. Не будет ничего страшного если на какое-то короткое время значения на серверах будут немного разные.

                                        Последовательный кеш мы писали для другой не менее частной задачи:
                                        У нас есть приложение, которое весьма похоже на мемкеш — habrahabr.ru/company/wapstart/blog/146957/ Оно умеет отдавать характеристики запроса по переданным заголовкам. Грубо говоря может «собрать» географию, оператора по переданному Ip адресу и т.д.
                                        Само собой характеристики запроса меняются крайне редко, поэтому мы сделали двухуровневое кеширование: локальный кеш -> удаленный кеш -> очередь -> СУБД.
                                          +1
                                          Благодарю за развернутый ответ, похоже что решение именно такое, как и должно быть.

                                          Если позволите — еще короткий вопрос. Если, как я понял, существует задача максимально быстрого сбора инфы с нескольких источников в одну точку (верно?) с функциями резервирования — то как Вы смотрите на такой вариант: есть между скриптом/бизнесс-логикой и кэш-серверами некая прокси, к примеру, написанная как консольная/сетевая софтинка, выполняющая при запросе к ней по ключу запросы ко ВСЕМ кеш-серверам СРАЗУ, асинхронно, и отдающая первый полученный результат. После отдачи она реализует минимально необходимые функции по сбору статистики (наподобие пинга до каждого, количества запросов, задержки) для оптимальной раздачи и максимальной скорости.

                                          Если по статистике из десятка доступных серверов быстрее отдают, к примеру, первый и третий (не суть), то в суточный пик запросов можно попробовать увеличить количество обращений именно к ним, тем самым сохранив/увеличив скорость доступа. То есть отдать все возможное ради критерия скорости. Да, у такого решения в принципе весьма приличный оверхед по запросам, но он в основном на уровнях сеть и оперативная память.

                                          Рассматривали ли Вы подобные варианты и если да — почему подобное не оказалось приемлемым. Спасибо.
                                            0
                                            Может быть мы когда-нибудь и придем к этому, но пока такой задачи не стоит. При этом я не говорю, что это плохо или нереализуемо :) Любой дополнительный компонент увеличивает сложность системы и понижает надёжность, пусть даже и опосредованно.
                                            Сейчас сбор характеристик запроса и подходящих баннеров у нас не является узким местом.
                                            Когда-то были проблемы с производительностью этого источника, мы его переписали, производительность нас удовлетворила, и мы успокоились.

                                            Другими словами, у нас задача не в том, чтобы отдать контент максимально быстро, а в том, чтобы отдать контент не медленнее, чем за N миллисекунд. И мы не занимаемся оптимизацией пока укладываемся в эти N.
                                        +1
                                        Еще интересный момент — как обрабатывается ситуация, когда мемкеш лежит — первый сервер, второй и т.д. Ждем по секунде на каждом запросе в кэш или у Вас есть более интересное решение конкретно с мемкэшем?
                                          0
                                          В текущей версии pecl memcache (3.0.5) Можно задавать таймаут в float, хотя документация утверждает о int.
                                          Пруф — svn.php.net/viewvc/pecl/memcache/tags/RELEASE_3_0_5/memcache.c?revision=303968&view=markup (строка 1104 и далее). Соответственно можно задать таймаут меньше одной секунды, чем мы и пользуемся. :)

                                          А так Вы правы, в случае, если лежит всё, мы будем ждать по timeout секунд на каждый сервер. В нашем случае — 0.1. На самом деле сервера memcached падают крайне редко.

                                          ps. К слову, в trunk у параметра уже стоит Int.
                                            0
                                            Значит int все-таки выпилили, отлично, надо будет пощупать как это реализовано на самом деле.

                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                        Самое читаемое