---
# System prepended metadata

title: Задача "Фетчинг RSS фидов"

---

# Задача "Фетчинг RSS фидов"

Это нужно для того, чтобы:
- содержимое фида загружалось быстро (для тех фидов, которые очень долго грузятся, например, фид с названием "National")
- пользователь мог видеть более ранние посты в фиде (сейчас мы не храним содержимое фида на сервере)

## Требования

- Получение наиболее актуального состояния фида
- Быстрое получение первой порции элементов фида на клиенте
- "Infinite" scroll фида

## Решение \#1 (на клиенте: Feedly + RSS parser)

Т.к. Feedly API имеет ограничение 500 запросов в сутки с одного ip адреса, появилась идея вынести обращения к их API на клиент.

### Получение первой порции данных

Для быстрого получения первых данных вызываются параллельно: 
- getRssItems(url): запрос по прямой ссылке к RSS фиду (получение RssItems)
- getStreamContents(url, count): запрос содержимого стрима (StreamItems) в Feedly API: `GET https://cloud.feedly.com/v3/streams/contents?streamId=feed/{FEED_URL}&count={COUNT}`

Каждый из двух запросов делается с timeout 3-5s.

### Объединение полученных данных (mergeItems)

В ходе тестирования Feedly API было замечено, что содержимое стримов не является актуальным: иногда нет постов за последние 12-24 часа.

Для решения этой проблемы необходимо объединить полученные данные:
- сортируем RssItems по убыванию времени публикации
- из RssItems берем все элементы, которые опубликованы позже, чем StreamItems[0]
- объединяем полученные элементы и StreamItems

Если StreamItems пустой или не удалось их получить, то возвращаем отсортированный массив RssItems.

### Трекинг успешных открытий и ошибок (sendEvent)

Если не удалось загрузить ни RssItems, ни StreamItems - клиент шлет событие "fail" в Fulldive API. В ином случае клиент запрашивает метаданные по фиду у Feedly (пример, `GET https://cloud.feedly.com/v3/feeds/feed%2Fhttp%3A%2F%2Frss.cnn.com%2Frss%2Fcnn_topstories.rss`) и в событии "success" высылает метаданные и текущую локаль пользователя. 

События "fail" позволят отключить фид по достижению порогового значения в данной локали. А события "success" позволят выявлять наиболее популярные фиды в данной локали (по числу успешных открытий).

Предлагается добавить роут в нашем API для обработки этих событий:

```
POST /v4/sources/:sourceId/requests
```

Fail event body
```jsonld=
{
    "type": "fail",
    "locale": {
        "country": "us",
        "language": "en"
    }
}
```

Success event body
```jsonld=
{
    "type": "success",
    "source": {
        "title": "...",
        "language": "..." // optional
    },
    "locale": {
        "country": "us",
        "language": "en"
    }
}
```

**Контракт роута необходимо обсудить!**

### Infinite scroll

Для бесконечного скролла фида используется Feedly API: запрос `GET https://cloud.feedly.com/v3/streams/contents?streamId=feed/{FEED_URL}&count={COUNT}&continuation={CONTINUATION}` Continuation необходимо брать из ответа на предыдущий подобный запрос (подробнее [тут](https://developer.feedly.com/v3/streams/))

Также надо учесть случай, когда исчерпается квота в 500 запросов и сервер начнет отдавать "429 Too many requests".

На диграмме наглядно представлен вышеописанный процесс:

![Sequence diagram](https://i.imgur.com/dFQTy7j.png)

### Class StreamItem
- originId (item's url)
- published (timestamp in ms)
- title (optional)
- author (optional)
- language (optional)
- enclosure (array of images)

### Class FeedMetadata
- id
- title
- language (optional)
- topics (optional)

Плюсы:
- возможность получать недоступные ранее фиды
- возможность сортировки по количеству открытий фида (по сути популярность фида)
- возможность отключения фида в локали 

Минусы
- необходима реализация на iOS / Android


## Решение \#2 (на сервере: Feedly + RSS parser)

Клиент запрашивает содержимое фидов через Fulldive API, который собирает ответ на запрос из данных, полученных от Feedly API и данных парсинга RSS xml-файла (по тому же самому принципу, что и Решение #1)

Плюсы:
- нетривиальная логика реализована один раз на сервере

Минусы:
- ограничение в использовании Feedly API
- платим за трафик

На данный момент неизвестны доступные квоты на запросы в Enterprise плане Feedly.

Рассмотреть https://www.inoreader.com/developers/ (очень похож на Feedly) - 50,000 requests per day for $50 yearly

## Решение \#3 (на клиенте: RSS parser)

Клиент самостоятельно парсит RSS xml файл и отрисовывает полученние элементы фида.

Данное решение реализовано и используется в настоящий момент.

Плюсы:
- уже готово

Минусы:
- некоторые фиды не открываются в некоторых локалях или открываются очень долго
- достаточно маленькое количество элементов в фиде

# Задачи "Мониторинг доступности RSS фидов"

Это нужно для того, чтобы:
- пользователь видел только доступные ему фиды (которые скорее всего загрузятся у него) -> нет неработающих фидов


## Требования


## Решение \#1 (CronJob на основе статистики ошибок и открытий фидов, собранной с клиентов)

CronJob агрегирует события открытий фидов (fail / success) по (sourceId, locale). Фиды с большим процентом событий типа "fail" отключаются.

## Решение \#2 (CronJob на основе доступности фида с сервера)

CronJob по расписанию пингует с сервера все фиды и отключает недоступные.

Плюсы:
- отсутствие зависимостей 3rd-party сервисов (Feedly)

Минусы:
- много фидов долго грузятся и не успевают даже при timeout=15s
- фиды, доступные в локали пингующего сервера, могут быть недоступны в других локалях

# Задача "Получение топ фидов / фидов по категориям"

## TODO
- Наиболее популярные фиды для данной локали: рассчитывать score / internalScore на основе количества людей использующих фид и сортировать по нему
- Разметить фиды по категориям (topics)
- Отсутствие дубликатов фидов в выдаче


# Идеи по RSS фидам в целом

- Показывать время публикации поста в фиде вместо домена, т.к. для RSS фида у всех постов по идее один и тот же домен?
- Сделать названия топовых фидов покороче, чтобы в интерфейсе было легче переключаться между табами ("CNN.com - RSS Channel - HP Hero" -> "CNN.com")

# Интеграция с внешним провайдером

- Первый месяц мы скачиваем метаданные фидов (Feedly / Inoreader). Дальнейшее использование сервиса направлено в основном на увеличение доступности фидов в тех локалях, где по прямым ссылкам они недоступны.

# Что можно сделать без интеграции с внешним провайдером


(А надо ли это все с 20 RPS? Как это поможет?)

- Выключить фиды, которые недоступны из разных локаций
    - Проверяется с помощью VPN
    - Известны 400 фидов, которые не работают из России и США
- Клиенты: протестировать/пофиксить парсинг фидов
- Трекинг доступности фида
    - Клиент отправляет статус загрузки фида (успех/фейл)
        - Клиент смог или не смог показать фид (проблемы парсинга)
    - Сервер прячет то, что не получается скачать с каким-то порогом
    - А прячет ли то, что не парсит определенная версия клиента?
    - (опционально) Трекинг просмотра (может быть, есть смысл разделить)
        - Прочитать != загрузить
        - Клиент открывает табу — сервер считает популярность фидов
- Сервер тащит/парсит все РСС из базы, делает по аналогии с фидами от ТВБС
    - формирует файлы фидов 
    - по 100 постов (например)
    - как с ТВБС: кладет в бакет, кэширует в CDN
- Сервер медленно (за деньги — быстро) скачивает каталог фидов из провайдера (фидли,иноридер) и наполнять базу категориями и фидами 
