...

суббота, 16 февраля 2019 г.

Технология, которая приблизит запуск квантовых сетей

Физики из Университетов Торонто, Осаки и Тоямы представили концепцию квантового повторителя, который не использует ячейки квантовой памяти и способен работать при комнатной температуре. В перспективе он позволит реализовать масштабные квантовые сети.

Рассказываем, в чем заключается инновация.


/ PxHere / PD

В чем отличие нового квантового повторителя


Информация в квантовых сетях кодируется в фотонах. Однако переслать их на большие расстояния по оптоволокну довольно сложно. Более 90% частиц теряется в кабеле, длина которого превышает 50 км. Чтобы увеличить расстояние эффективной передачи, ученые и инженеры работают над созданием квантовых повторителей. Они помогают предотвратить потери при передаче фотонов по оптоволоконному кабелю. Однако существующие устройства используют ячейки квантовой памяти, например ионные ловушки, которые стабильны только при сильном охлаждении.

Такие решения сперва декодируют передаваемую информацию, а затем кодируют её снова для передачи дальше по цепочке. Такой подход создает потенциальную уязвимость для злоумышленников, которые могут перехватывать информацию на скомпрометированном узле.

Команде физиков и инженеров под руководством профессора Хой-Квонга Ло (Hoi-Kwong Lo) удалось решить эту проблему. Они продемонстрировали возможность реализации фотонного повторителя, который не требует «промежуточных» преобразований. Отметим, что концепцию устройства исследователи предложили еще в 2015 году, а в начале 2019 им удалось доказать её экспериментально.

В этом случае квантовая память заменяется граф-состоянием (photonic graph state). Два компьютера, расположенные на разных концах оптоволоконного соединения, генерируют квантовую запутанность между своими фотонами. После они отправляют сразу множество фотонов повторителю. В повторителе эти частицы представляются в виде графа, в котором каждый кубит — это вершина. Затем над фотонами выполняется измерение состояния Белла (Bell state measurement). Результатом измерения становится проектирование частиц в перепутанное состояние.

Преимущества и сложности


Оптические повторители способны обеспечить передачу фотонов на значительно большие расстояния — отправитель и получатель смогут находиться в 800 километрах друг от друга. При этом повторители сохраняют работоспособность при комнатной температуре.

Для достижения таких результатов в сети требуется использовать высокочувствительные оптические детекторы, квантовая эффективность которых будет превышать 60%. Из существующих устройств немногие способны выдавать такие показатели, а те, что способны, дорого стоят.

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

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


/ Flickr / Nick Harris / CC BY-ND

Что ещё делают для реализации квантового интернета


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

Также ведется работа над алгоритмами коррекции ошибок. Они кодируют квантовые состояния фотонов таким образом, чтобы при потере одной или нескольких частиц при передаче сигнала, информацию из них можно было восстановить. Для коррекции ошибок предлагают использовать несколько методов, например алгоритм Шора, кодирование Стейна, принцип квантовой чётности и другие.

Все из представленных технологий пока находятся на ранних этапах разработки. Поэтому говорить о том, что какие-то из них определенно станут использоваться в квантовом интернете пока рано. Однако тестовые сети уже начинают появляться: в ближайшие пять лет станции с квантовыми повторителями планируют построить в Великобритании. Вероятно, инициативу подхватят и другие страны.



О чем мы пишем в корпоративном блоге:
Свежие посты из блога на Хабре:

Let's block ads! (Why?)

[Из песочницы] Учимся стандарту проектирования — Entity Relationship

[Из песочницы] Распределённый чат на Node.JS и Redis

Результат пошуку зображень за запитом "голубиная почта"

Небольшой вопрос/ответ:

Для кого это? Людям, которые мало или вообще не сталкивались с распределенными системами, и которым интересно увидеть как они могут строится, какие существуют паттерны и решения.

Зачем это? Самому стало интересно что и как. Черпал информацию с разных источников, решил выложить в концентрированном виде, ибо в свое время сам хотел бы увидеть подобную работу. По сути это текстовое изложение моих личных метаний и раздумий. Также, наверняка будет много исправлений в комментариях от знающих людей, отчасти это и есть целью написания всего этого именно в виде статьи.


Постановка задачи

Как сделать чат? Это должно быть тривиальной задачей, наверное каждый второй бекендер пилил свой собственный, так же как игровые разработчики делают свои тетрисы/змейки и т. п. Я взялся за такой, но чтоб было интереснее он должен быть готов к захвату мира, чтоб мог выдерживать сотниллиарды активных пользователей и вообще был неимоверно крут. Из этого исходит ясная потребность в распределенной архитектуре, потому что вместить все воображаемое количество клиентов на одной машине — пока нереально с нынешними мощностями. Заместо того чтоб просто сидеть и ждать на появление квантовых компьютеров я решительно взялся за изучение темы распределенных систем.

Стоит отметить что быстрый отклик очень важен, пресловутый realtime, ведь это же чат! а не доставка почты голубями.

%рандомная шутка про почту россии%

Использовать будем Node.JS, он идеален для прототипирования. Для сокетов возьмем Socket.IO. Писать на TypeScript.

И так, что вообще мы хотим:


  1. Чтоб пользователи могли слать друг-другу сообщения
  2. Знать кто онлайн/оффлайн

Как мы это хотим:


Сингл сервер

Тут нечего говорить особо, сразу к коду. Объявим интерфейс сообщения:

interface Message{
    roomId: string,//В какую комнату пишем
    message: string,//Что мы туда пишем
}

На сервере:

io.on('connection', sock=>{

    //Присоеденяемся в указанную комнату
    sock.on('join', (roomId:number)=> 
            sock.join(roomId))

    //Пишем в указанную комнату
    //Все кто к ней присоеденился ранее получат это сообщение
    sock.on('message', (data:Message)=> 
            io.to(data.roomId).emit('message', data))
})

На клиенте что-то типа:

sock.on('connect', ()=> {
    const roomId = 'some room'

    //Подписываемся на сообщения из любых комнат
    sock.on('message', (data:Message)=> 
            console.log(`Message ${data.message} from ${data.roomId}`))

    //Присоеденяемся к одной
    sock.emit('join', roomId)

    //И пишем в нее
    sock.emit('message', <Message>{roomId: roomId, message: 'Halo!'})
})

С онлайн статусом можно работать так:

io.on('connection', sock=>{

    //При авторизации присоеденяем сокет в комнату с идентификатором пользователя
    //В будущем, если нужно будет послать сообщение конкретному пользователю - 
    //можно его скинуть прямо в нее
    sock.on('auth', (uid:string)=> 
            sock.join(uid))

    //Теперь, чтоб узнать онлайн ли пользователь,
    //просто смотрим есть ли кто в комнате с его айдишником
    //и отправляем результат
    sock.on('isOnline', (uid:string, resp)=> 
            resp(io.sockets.clients(uid).length > 0))
})

И на клиенте:

sock.on('connect', ()=> {
    const uid = 'im uid, rly'

    //Типо авторизуемся
    sock.emit('auth', uid)

    //Смотрим в онлайне ли мы
    sock.emit('isOnline', uid, (isOnline:boolean)=>
             console.log(`User online status is ${isOnline}`))
})

Примечание: код не запускал, пишу по памяти просто для примера

Просто как дрова, докручиваем сюды реальную авторизацию, менеджмент комнат (история сообщений, добавление/удаление участников) и профит.

НО! Мы же собрались захватывать мир во всем мире, а значит не время останавливаться, стремительно движемся дальше:


Node.JS кластер

Примеры использования Socket.IO на множестве нод есть прямо на официальном сайте. В том числе там есть и про родной Node.JS кластер, который мне показался неприменимым к моей задаче: он позволяет расширить наше приложение по всей машине, НО не за ее рамки, поэтому решительно пропускаем мимо. Нам нужно наконец-то выйти за границы одной железки!


Распределяй и велосипедь

Как это сделать? Очевидно, нужно как-то связать наши инстансы, запущенные не только дома в подвале, но и в соседском подвале тоже. Что первое приходит в голову: делаем некое промежуточное звено, которое послужит шиной между всеми нашими нодами:

1549140775997

Когда нода хочет послать сообщение другой, она делает запрос к Bus, и уже он в свою очередь пересылает его куда надо, все просто. Наша сеть готова!

FIN.

… но ведь не все так просто?)

При таком подходе мы упираемся в производительность этого промежуточного звена, да и вообще хотелось бы напрямую обращаться к нужным нодам, ведь что может быть быстрее общения на прямую? Так давайте двинемся именно в эту сторону!

Что нужно в первую очередь? Собственно, законнектить один инстанс к другому. Но как первому узнать об существовании второго? Мы же хотим иметь их бесконечное количество, произвольно поднимать/убирать! Нужен мастер-сервер, адрес которого заведомо известен, к нему все коннектятся, за счет чего он знает все существующие ноды в сети и этой информацией добросердечно делиться с всеми желающими.

1549048945334

Т е нода поднимается, говорит мастеру о своем пробуждении, он дает список других активных нод, мы к ним подключаемся и все, сеть готова. В качестве мастера может выступать Consul или что-то подобное, но поскольку мы велосипедим то и мастер должен быть самопальный.

Отлично, теперь мы располагаем собственным скайнетом! Но текущая реализация чата в нем уже не пригодна. Давайте, собственно, придумаем требования:


  1. Когда пользователь шлет сообщение, мы должны знать КОМУ он его шлет, т е иметь доступ к участникам комнаты.
  2. Когда мы получили участников мы должны доставить им сообщения.
  3. Мы должны знать какой пользователь в онлайне сейчас
  4. Для удобства — дать пользователям возможность подписываться на онлайн статус других пользователей, чтоб в реальном времени узнавать об его изменении

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

1549237952673

Два пользователя подсоединены к разным нодам. Мастер знает об этом, ноды знают что мастер знает. Когда UserB авторизуется, Node2 уведомляет об этом Master, который "запоминает" что UserB присоединен к Node2. Когда UserA захочет послать сообщение UserB то получится следующая картина:

1549140491881

В принципе все работает, но хотелось бы избежать лишнего раунд трипа в виде опрашивания мастера, экономнее было бы сразу обращаться напрямую к нужной ноде, ведь именно ради этого все и затеивалось. Это можно сделать, если они будут сообщать всем окружающим какие пользователи к ним подключены, каждая из них становится самодостаточным аналогом мастера, и сам мастер становится не нужным, т к список соотношения "User=>Node" дублируется у всех. При старте ноде достаточно подключиться к любой уже запущенной, стянуть ее список себе и вуаля, она также готова к бою.

1549139768940

1549139882747

Но качестве trade off мы получаем дублирование списка, который хоть и являет собой соотношение "user id -> [host connections]", но все же при достаточном количестве пользователей получится довольно большим по памяти. Да и вообще пилить такое самому — это явно попахивает велосипедостроительной промышленностью. Чем больше кода — тем больше потенциальных ошибок. Пожалуй, заморозим этот вариант и глянем что уже есть из готового:


Брокеры сообщений

Сущность реализующая тот самый "Bus", "промежуточное звено" упомянутое выше. Его задача — получение и доставка сообщений. Мы, как пользователи — можем подписаться на них и отсылать свои. Все просто.

Есть зарекомендовавшие себя RabbitMQ и Kafka: они только и делают что доставляют сообщения — таково их предназначение, напичканы всем необходимым функционалом по горло. В их мире сообщение должно быть доставлено, несмотря ни на что.

В то же время есть Redis и его pub/sub — тоже что и вышеупомянутые ребята но более дубово: он просто тупо принимает сообщение и доставляет подписчику, без всяких очередей и остальных оверхедов. Ему абсолютно плевать на сами сообщения, пропадут они, подвис ли подписчик — он выбросит его и возьмется за новое, будто ему в руки кидают раскаленную кочергу от которой хочется избавиться по быстрее. Также, если он вдруг упадет — все сообщения также канут вместе с ним. Иными словами — ни о какой гарантии доставки речи не идет.

… и это то что нужно!

Ну действительно же, мы делаем просто чат. Не какой-то критический сервис по работе с деньгами или центр управления космическими полетами, а… просто чат. Риск что условному Пете раз в год не придет одно сообщение из тысячи — им можно пренебречь, если в замен получаем рост производительности а в месте с ним количества пользователей за те же деньки, trade off во всей красе. Тем более что в то же время можно вести историю сообщений в каком нибудь персистентном хранилище, а значит Петя таки увидит то самое пропущенное сообщение перезагрузив страницу/приложение. Именно по этому остановимся на Redis pub/sub, а точнее: посмотрим на существующий адаптер для SocketIO, который упоминается в статье на оф. сайте.

Так что это?


Redis adapter

https://github.com/socketio/socket.io-redis

С его помощью обычное приложение посредством нескольких строчек и минимальным количеством телодвижений превращается в настоящий распределенный чат! Но как? Если посмотреть внутрь — оказывается там всего один файл на пол сотни строк.

В случай, когда мы эмитим сообщение

io.emit("everyone", "hello")

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

1549232309776

Сообщение разойдется по всех нодах даже если мы эмитим конкретному пользователю. Т е каждая нода принимает все сообщения и уже сама разбирается нужно ли оно ей.

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

Что с этим можно сделать? Например, использовать идентификатор пользователя в качестве имени комнаты (user id == room id). При авторизации джоинить сокет к ней, а когда мы захотим послать пользователю сообщение — просто шлем в нее. Еще, мы можем узнать онлайн ли юзер, элементарно глянув есть ли сокеты в указанной комнате.

В принципе на этом можно остановиться, но нам как всегда мало:


  1. Бутылочное горлышко в виде одного инстанса редиса
  2. Избыточность, хотелось бы чтоб ноды получали только нужные им сообщения

На счет пункта первого, глянем на такую штуку как:


Redis cluster

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

1549233023980

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

1549231953897

Я новичек, многого не знаю, иногда приходится возвращаться к вилесопедостроению, чем мы и займемся. Нет, редис оставим дабы совсем не скатываться, но нужно что-то придумать с архитектурой ибо текущая никуда не годится.


Поворот не туда

Что нам нужно? Повысить общую пропускную способность. Например попробуем тупо заспавнить еще один инстанс. Представим что socket.io-redis умеет подключаться к нескольким, при пуше сообщения он выбирает рандомный, а подписывается на все. Получится вот так:

1549239818663

Вуаля! В общем и целом, проблема решена, редис теперь не узкое место, можно спавнить сколь угодно экземпляров! Зато им стали ноды. Да-да, инстансы нашего чатика по прежнему переваривают ВСЕ сообщения, кому бы они не предназначались.

Можно наоборот: подписываться на один рандомный, что уменьшит нагрузку на ноды, а пушить в все:

1549239361416

Видим что стало наоборот: ноды чувствуют себя спокойнее, но на инстанс редиса нагрузка выросла. Это тоже никуда не годится. Нужно чутка повелосипедить.

Дабы прокачать нашу систему оставим пакет socket.io-redis в покое, он хоть и классный но нам нужно больше свободы. И так, подключаем редис:

//Отдельные каналы для:
const pub = new RedisClient({host: 'localhost', port: 6379})//Пуша сообщений
const sub = new RedisClient({host: 'localhost', port: 6379})//Подписок на них

//Также вспоминаем этот интерфейс
interface Message{
    roomId: string,//В какую комнату пишем
    message: string,//Что мы туда пишем
}

Настраиваем нашу систему сообщений:

//Отлавливаем все приходящие сообщения тут
sub.on('message', (channel:string, dataRaw:string)=> {
    const data = <Message>JSON.parse(dataRaw)
    io.to(data.roomId).emit('message', data))
})

//Подписываемся на канал
sub.subscribe("messagesChannel")

//Присоеденяемся в указанную комнату
sock.on('join', (roomId:number)=> 
        sock.join(roomId))

//Пишем в комнату
sock.on('message', (data:Message)=> {

    //Публикуем в канал
    pub.publish("messagesChannel", JSON.stringify(data))
})

На данный момент получается как в socket.io-redis: мы слушаем все сообщения. Сейчас мы это исправим.

Организуем подписки следующим образом: вспоминаем концепцию с "user id == room id", и при появлении пользователя подписываемся на одноименный канал в редисе. Таким образом наши ноды будут получать только предназначенные им сообщения, а не слушать "весь эфир".

//Отлавливаем все приходящие сообщения тут
sub.on('message', (channel:string, message:string)=> {
    io.to(channel).emit('message', message))
})

let UID:string|null = null;
sock.on('auth', (uid:string)=> {
    UID = uid

    //Когда пользователь авторизируется - подписываемся на 
    //одноименный нашему UID канал
    sub.subscribe(UID)

    //И соответствующую комнату
    sock.join(UID)
})
sock.on('writeYourself', (message:string)=> {

    //Пишем сами себе, т е публикуем сообщение в канал одноименный UID
    if (UID) pub.publish(UID, message)
})

Офигенно, теперь мы уверены что ноды получают только предназначенные им сообщения, ничего лишнего! Следует заметить, однако, что самих подписок теперь намного, намного больше, а значит будет кушать память ой йой йой, + больше операций подписки/отписки, которые сравнительно дорогие. Но в любом случае это придает нам некоторой гибкости, даже можно на этом моменте остановиться и пересмотреть заново все предыдущие варианты, уже с учетом нашего нового свойства нод в виде более выборочного, целомудренного получения сообщений. Например, ноды могут подписываться на один из нескольких инстансов редиса, а при пуше — отсылать сообщение в все инстансы:

1550174595491

… но, как ни крути, они все равно не дают бесконечной расширяемости при разумных накладных расходах, нужно рожать иные варианты. В один момент в голову пришла следующая схема: а что если инстансы редисов поделить на группы, скажем А и В по два инстанса в каждой. Ноды при подписке подписываются по одному инстансу от каждой группы, а при пуше отсылают сообщение в все инстансы какой-то одной рандомной группы.

1550174092066

1550174943313

Таким образом мы получаем действующую структуру с бесконечным потенциалом расширяемости в реальном времени, нагрузка на отдельный узел в любой точке не зависит от размера системы, ибо:


  1. Общая пропускная способность делится между группами, т. е. при увеличении пользователей/активности просто сравним дополнительные группы.
  2. Менеджмент пользователями (подписками) делится внутри самих групп, т е при увеличении пользователей/подписок просто наращиваем количество инстансов внутри групп.

… и как всегда есть одно "НО": чем больше оно все становится, тем больше ресурсов нужно для следующего прироста, это мне кажется непомерным trade off.

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

1550233610561


Что-то похожее рассказывает этот чувак:


Чтобы получить местонахождение пользователя мы проделываем дополнительный раундтрип, что в принципе О'К, но не в нашем случае. Кажется мы копаем в не совсем правильную сторону, нужно что-то иное...


Сила хэша

Есть такая штука как хэш. У него есть некоторый конечный диапазон значений. Получить его можно из любых данных. А что если разделить этот диапазон между инстансами редиса? Ну вот берем мы идентификатор пользователя, производим хэш, и в зависимости от диапазона в котором он оказался подписываемся/пушим в один определенный инстанс. Т е мы заранее не знаем где какой юзерь существует, но получив его айди можем с уверенностью сказать что он именно в n инстансе, инфа 100. Теперь то же самое, но с кодом:

function hash(val:string):number{/**/}//Наша хэш-функция, возвращающая число
const clients:RedisClient[] = []//Массив клиентов редиса
const uid = "some uid"//Идентификатор пользователя

//Теперь, такой не хитрой манипуляцией мы получаем всегда один и тот же
//клиент из множества для данного пользователя
const selectedClient = clients[hash(uid) % clients.length]

Вуаля! Теперь мы не зависим от количества инстансов от слова вообще, можем скалироваться сколь угодно без оверхедов! Ну серьезно, это же гениальный вариант, единственный минус которого: нужда в полном перезапуске системы при обновлении количества редис-инстансов. Есть такая штука как Стандартное кольцо и Партиционное кольцо, позволяющие побороть это, но они не применимы в условиях системы обмена сообщениями. Ну т е сделать логику миграции подписок между инстансами можно, но это стоит еще дополнительный кусок кода непонятного размера, а как мы знаем — чем больше кода, тем больше багов, нам этого не надо, спасибо. Да и в нашем случае downtime вполне допустимый tradeoff.

Еще можно посмотреть на RabbitMQ с его плагином, позволяющий вытворять то же что и мы, и + обеспечивает миграцию подписок (как я говорил выше — он обвязан функционалом с ног до головы). В принципе можно взять его и спать спокойно но, если кто шарит в его тюнинге дабы вывести в realtime режим оставив только лишь фичу с хэш рингом.

Залил репозиторий на гитхаб.

В нем реализован тот финальный вариант к которому мы пришли. Помимо всего, там есть дополнительная логика по работе с комнатами (диалогами).

В общем, я доволен и можно закругляться.


Итого

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

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

Let's block ads! (Why?)

[Перевод] SMAA: улучшенное субпиксельное морфологическое сглаживание

[Перевод] Оверинжинирг 80 уровня или редьсюеры: путь от switch-case до классов

image


О чем пойдет речь?

Посмотрим на метаморфозы редьюсеров в моих Redux/NGRX приложениях за последние пару лет. Начиная с дубового switch-case, продолжая выбором из объекта по ключу и заканчивая классами с декораторами, блекджеком и TypeScript. Постараемся обозреть не только историю этого пути, но и найти какую-нибудь причинно-следственную связь.


Если вы так же как и я задаетесь вопросами избавления от бойлерплейта в Redux/NGRX, то вам может быть интересна эта статья.

Если вы уже используете подход к выбору редьюсера из объекта по ключу и сыты им по горло, то можете сразу листать до "Редьюсеры на основе классов".


Шоколадный switch-case


Обычно switch-case ванильный, но мне показалось, что это серьезно дискриминирует все остальные виды switch-case.

Итак, взглянем на типичную проблему асинхронного создания какой-то сущности, например, джедая.

const actionTypeJediCreateInit = 'jedi-app/jedi-create-init'
const actionTypeJediCreateSuccess = 'jedi-app/jedi-create-success'
const actionTypeJediCreateError = 'jedi-app/jedi-create-error'

const reducerJediInitialState = {
  loading: false,
  // Список джедаев
  data: [],
  error: undefined,
}
const reducerJedi = (state = reducerJediInitialState, action) => {
  switch (action.type) {
    case actionTypeJediCreateInit:
      return {
        ...state,
        loading: true,
      }
    case actionTypeJediCreateSuccess:
      return {
        loading: false,
        data: [...state.data, action.payload],
        error: undefined,
      }
    case actionTypeJediCreateError:
      return {
        ...state,
        loading: false,
        error: action.payload,
      }
    default:
      return state
  }
}

Я буду предельно откровенен и признаюсь, что никогда в своей практике не использовал switch-case. Хотелось бы верить, что у меня для этого даже есть некий список причин:


  • switch-case слишком легко поломать: можно забыть вставить break, можно забыть о default.
  • switch-case слишком многословен.
  • switch-case почти что O(n). Это не то, чтобы сильно важно само по себе, т.к. Redux не хвастается умопомрачительной производительностью сам по себе, но сей факт крайне бесит моего внутреннего ценителя прекрасного.

Логичный способ все это причесать предлагает официальная документация Redux — выбирать редьюсер из объекта по ключу.


Выбор редьюсера из объекта по ключу

Мысль проста — каждое изменения стейта можно описать функцией от стейта и экшна, и каждая такая функция имеет некий ключ (поле type в экшне), которая ей соответствует. Т.к. type — строка, нам ничто не мешает сообразить на все такие функции объект, где ключ — это type, а значение — это чистая функция преобразования стейта (редьюсер). В таком случае мы можем выбирать необходимый редьюсер по ключу (O(1)), когда в корневой редьюсер прилетает новый экшн.

const actionTypeJediCreateInit = 'jedi-app/jedi-create-init'
const actionTypeJediCreateSuccess = 'jedi-app/jedi-create-success'
const actionTypeJediCreateError = 'jedi-app/jedi-create-error'

const reducerJediInitialState = {
  loading: false,
  data: [],
  error: undefined,
}
const reducerJediMap = {
  [actionTypeJediCreateInit]: (state) => ({
    ...state,
    loading: true,
  }),
  [actionTypeJediCreateSuccess]: (state, action) => ({
    loading: false,
    data: [...state.data, action.payload],
    error: undefined,
  }),
  [actionTypeJediCreateError]: (state, action) => ({
    ...state,
    loading: false,
    error: action.payload,
  }),
}

const reducerJedi = (state = reducerJediInitialState, action) => {
  // Выбираем редьюсер по `type` экшна
  const reducer = reducerJediMap[action.type]
  if (!reducer) {
    // Возвращаем исходный стейт, если наш объект не содержит подходящего редьюсера
    return state
  }
  // Выполняем найденный редьюсер и возвращаем новый стейт
  return reducer(state, action)
}

Самое вкусное тут то, что логика внутри reducerJedi остается той же самой для любого редьюсера, и мы можем ее переиспользовать. Для этого даже есть нанобиблиотека redux-create-reducer.

import { createReducer } from 'redux-create-reducer'

const actionTypeJediCreateInit = 'jedi-app/jedi-create-init'
const actionTypeJediCreateSuccess = 'jedi-app/jedi-create-success'
const actionTypeJediCreateError = 'jedi-app/jedi-create-error'

const reducerJediInitialState = {
  loading: false,
  data: [],
  error: undefined,
}
const reducerJedi = createReducer(reducerJediInitialState, {
  [actionTypeJediCreateInit]: (state) => ({
    ...state,
    loading: true,
  }),
  [actionTypeJediCreateSuccess]: (state, action) => ({
    loading: false,
    data: [...state.data, action.payload],
    error: undefined,
  }),
  [actionTypeJediCreateError]: (state, action) => ({
    ...state,
    loading: false,
    error: action.payload,
  }),
})

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


  • Для сложный редьюсеров нам приходится оставлять комментарии, т.к. данный метод не предоставляет из коробки способа предоставить некую поясняющую мета-информацию.
  • Объекты с кучей редьюсеров и ключей не очень хорошо читаются.
  • Каждому редьюсеру соответствует только один ключ. А что если хочется запускать один и тот же редьюсер для нескольких экшнов?

Я чуть не расплакался от счастья, когда переехал на редьюсеры на основе классов, и ниже я расскажу почему.


Редьюсеры на основе классов

Плюшки:


  • Методы классов — это наши редьюсеры, а у методов есть имена. Как раз та самая мета-информация, которая расскажет, чем же этот редьюсер занимается.
  • Методы классов могут быть декорированы, что есть простой декларативный способ связать редьюсеры и соответствующие им экшны (именно экшны, а не один экшн!)
  • Под капотом можно использовать все те же объекты, чтобы получить O(1).

В итоге, хотелось бы получить что-то такое.

const actionTypeJediCreateInit = 'jedi-app/jedi-create-init'
const actionTypeJediCreateSuccess = 'jedi-app/jedi-create-success'
const actionTypeJediCreateError = 'jedi-app/jedi-create-error'

class ReducerJedi {
  // Смотрим на предложение о "Class field delcaratrions", которое нынче в Stage 3.
  // https://github.com/tc39/proposal-class-fields
  initialState = {
    loading: false,
    data: [],
    error: undefined,
  }

  @Action(actionTypeJediCreateInit)
  startLoading(state) {
    return {
      ...state,
      loading: true,
    }
  }

  @Action(actionTypeJediCreateSuccess)
  addNewJedi(state, action) {
    return {
      loading: false,
      data: [...state.data, action.payload],
      error: undefined,
    }
  }

  @Action(actionTypeJediCreateError)
  error(state, action) {
    return {
      ...state,
      loading: false,
      error: action.payload,
    }
  }
}

Вижу цель, не вижу препятствий.


Шаг 1. Декоратор @Action.

Нам надо, чтобы в этот декоратор мы могли запхать любое количество экшнов, и чтобы эти жкшны сохранились как некая мета-информация, к которой позже можно получить доступ. Для этого можем воспользоваться замечательным полифиллом reflect-metadata, который патчит Reflect.

const METADATA_KEY_ACTION = 'reducer-class-action-metadata'

export const Action = (...actionTypes) => (target, propertyKey, descriptor) => {
  Reflect.defineMetadata(METADATA_KEY_ACTION, actionTypes, target, propertyKey)
}

Шаг 2. Превращаем класс в, собственно, редьюсер.


Нарисовали кружок, нарисовали второй, а теперь немного магии и получаем сову!

Как мы знаем каждый редьюсер — это чистая функция, которая принимает текущий стейт и экшн и возвращает новый стейт. Класс — это, конечно, функция, но не совсем та, которая нам нужна, да и ES6 классы не могут быть вызваны без new. В общем надо его как-то преобразовать.

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

Начнем со сбора мета-информации.

const getReducerClassMethodsWthActionTypes = (instance) => {
  // Получаем названия методов из прототипа класса
  const proto = Object.getPrototypeOf(instance)
  const methodNames = Object.getOwnPropertyNames(proto).filter(
    (name) => name !== 'constructor',
  )

  // На выходе мы хотим получить коллекцию с типами экшнов и соответствующими редьюсерами
  const res = []
  methodNames.forEach((methodName) => {
    const actionTypes = Reflect.getMetadata(
      METADATA_KEY_ACTION,
      instance,
      methodName,
    )
    // Мы хотим привязать конекст `this` для каждого метода
    const method = instance[methodName].bind(instance)
    // Необходимо учесть, что каждому редьюсеру могут соответствовать несколько экшн типов
    actionTypes.forEach((actionType) =>
      res.push({
        actionType,
        method,
      }),
    )
  })
  return res
}

Теперь мы можем преобразовать полученную коллекцию в объект

const getReducerMap = (methodsWithActionTypes) =>
  methodsWithActionTypes.reduce((reducerMap, { method, actionType }) => {
    reducerMap[actionType] = method
    return reducerMap
  }, {})

Таким образом конечная функция может выглядеть так:

import { createReducer } from 'redux-create-reducer'

const createClassReducer = (ReducerClass) => {
  const reducerClass = new ReducerClass()
  const methodsWithActionTypes = getReducerClassMethodsWthActionTypes(
    reducerClass,
  )
  const reducerMap = getReducerMap(methodsWithActionTypes)
  const initialState = reducerClass.initialState
  const reducer = createReducer(initialState, reducerMap)
  return reducer
}

Далее мы можем применить ее к нашему классу ReducerJedi.

const reducerJedi = createClassReducer(ReducerJedi)

Шаг 3. Смотрим, что получилось в итоге.

// Переместим общий код в отдельный модуль
import { Action, createClassReducer } from 'utils/reducer-class'

const actionTypeJediCreateInit = 'jedi-app/jedi-create-init'
const actionTypeJediCreateSuccess = 'jedi-app/jedi-create-success'
const actionTypeJediCreateError = 'jedi-app/jedi-create-error'

class ReducerJedi {
  // Смотрим на предложение о "Class field delcaratrions", которое нынче в Stage 3.
  // https://github.com/tc39/proposal-class-fields
  initialState = {
    loading: false,
    data: [],
    error: undefined,
  }

  @Action(actionTypeJediCreateInit)
  startLoading(state) {
    return {
      ...state,
      loading: true,
    }
  }

  @Action(actionTypeJediCreateSuccess)
  addNewJedi(state, action) {
    return {
      loading: false,
      data: [...state.data, action.payload],
      error: undefined,
    }
  }

  @Action(actionTypeJediCreateError)
  error(state, action) {
    return {
      ...state,
      loading: false,
      error: action.payload,
    }
  }
}

export const reducerJedi = createClassReducer(ReducerJedi)

Как жить дальше?

Кое-что мы оставили за кадром:


  • Что если один и тот же экшн тип соответствует нескольким редьюсерам?
  • Было бы здорово добавить immer из коробки.
  • Что если мы хотим использовать классы для создания наших экшнов? Или функции (action creators)? Хотелось бы, чтобы декоратор мог принимать не только типы экшнов, то и actions creators.

Весь этот функционал с дополнительными примерами есть у небольшой библиотеки reducer-class.

Стоит заметить, что идея об использовании классов для редьюсеров не нова. @amcdnl некогда создал великолепную библиотеку ngrx-actions, но, кажется, сейчас он на нее забил и переключился на NGXS. К тому же мне хотелось более строгой типизации и сбросить балласт в виде специфичного для Angular функционала. Здесь можно ознакомиться со списком ключевых отличий между reducer-class и ngrx-actions.


Если вам понравилась идея с классами для редьюсеров, то вам также может понравиться использовать классы для ваших экшнов. Взгляните на flux-action-class.

Надеюсь, вы не потратили время зря, и статья была вам хоть чуточку полезна. Просьба пинать и критиковать. Будем учиться кодить лучше вместе.

Let's block ads! (Why?)

[Перевод] Как Project Infer от Facebook помогает искать баги в мобильных приложениях перед деплоем

Несколько дней назад команда инженеров Facebook отличилась — ее удостоили награды Most Influential POPL Paper Award. В среде специалистов по машинному обучению это весьма почетно. Награду вручили за работу Compositional Shape Analysis by Means of Bi-abduction, которая раскрывает нюансы Project Infer. Сам проект предназначен для обнаружения и ликвидации багов в коде мобильного приложения перед его деплоем.

Баги в программном обеспечении для мобильных устройств обходятся очень дорого как разработчикам, так и пользователям. Что касается первых, то обнаружение проблемы в уже размещенном в каталогах приложении — ночной кошмар для любого специалиста. Конечно, софт тестируется, работу программ проверяют по определенным шаблонам. Но чаще разработчики не могут предусмотреть всего, и баги в приложении обнаруживаются уже после деплоя.

Напоминаем: для всех читателей «Хабра» — скидка 10 000 рублей при записи на любой курс Skillbox по промокоду «Хабр».

Skillbox рекомендует: Онлайн-курс «Аналитик данных на Python».

Project Infer сканирует код мобильных приложений и позволяет найти разного рода проблемы, паттерны которых хранятся в базе (а она все время обновляется). Сам проект был представлен три года назад. Почти сразу после анонса Facebook открыл код, после чего его стали использовать в таких компаниях, как Amazon Web Services, Spotify и Uber.

Как это работает?


Project Infer использует специализированный набор алгоритмов для анализа работы кода. В исходном коде любого крупного приложения могут быть миллионы комбинаций, которые приводят к появлению ошибок. Традиционные процедуры анализа кода не могут помочь обнаружить все. Infer от Facebook, самообучаясь, расширяет свою базу, так проект позволяет обнаружить массу проблем в коде.

В общем смысле процесс работы Facebook Infer можно разделить на два этапа: сбор данных и их анализ. Жизненный цикл (lifecycle) также разделяется на две части: глобальную и дифференциальную.

На этапе сбора данных Infer переводит исходный код в собственный язык. Этап анализа посвящается изучению мельчайших деталей структуры кода, которые могут потенциально привести к появлению ошибки. Если Infer встречает уже знакомое сочетание факторов, идентифицируемое как паттерн ошибки, анализ останавливается для конкретного метода или функции, но остальные методы и функции продолжают анализироваться. Вот пример работы Infer.

C точки зрения исполнения Infer может работать в двух модальностях — Global и Differential, как и говорилось выше. В первом случае Infer анализирует все файлы. Для проекта, который компилируется с использованием Gradle, запуск Infer производится командой

infer run -- gradle build

Дифференциальный процесс используется в инкрементных системах сборки, характерных для мобильных приложений. В этом случае нужно сначала запустить сбор данных Infer, чтобы получить все команды компиляции, а затем провести анализ только изменений. Для этого используется такой набор команд:
gradle clean
infer capture -- gradle build
edit some/File.java
# make some changes to some/File.java
infer run --reactive -- gradle build

Отчеты Infer можно просмотреть при помощи команды InferTraceBugs.
infer run -- gradle build
inferTraceBugs

Основа Project Infer


Infer от Facebook основан на двух новых математических методах: логике разделения и Bi-abduction.

Ключевая особенность логики разделения — возможность локальных рассуждений (local reasoning). Она появилась благодаря наличию в утверждениях пространственных связок (англ. spatial connectives) между частями кучи. В этом случае нет необходимости учитывать весь объем памяти на каждом из этапов.

Основной элемент логики разделения — оператор * (и отдельно), который называется разделяющим соединением. Формула X↦Y ∗ Y↦X может быть прочитана как «X указывает на Y, а отдельно Y указывает на X», что очень похоже на то, как работают указатели памяти.

В контексте Infer Bi-abduction можно рассматривать как метод логического вывода, который дает платформе возможность обнаруживать свойства, касающиеся поведения независимых частей кода приложения. Bi-abduction совместно выводит антифреймы (отсутствующие части состояния) и фреймы (те части, которые не затронуты операцией). Математически проблема Bi-abduction выражается с использованием следующего синтаксиса: A ∗? Antiframe⊢B ∗? Frame.

В Infer от Facebook метод дает возможность вывести спецификации pre/post из чистого кода при условии, что мы знаем спецификации для примитивов на базовом уровне кода.

Создание FI стало возможно благодаря анализу работы специалистов по машинному обучению, который проводился много лет. В ходе работы над Infer были опубликованы такие ключевые для всей сферы статьи:

  • Compositional Shape Analysis by means of Bi-abduction. Как раз за эту работу была получена премия, о которой говорится выше. Работа знакомит читателя с композиционным анализом формы. Это дополнение к традиционному анализу формы (shape analysis), дающее возможность применить метод для анализа исходного кода приложений.
  • A Local Shape Analysis Based on Separation Logic: эта статья описывает логику разделения в качестве механизма анализа исходного кода приложений. Авторы показывают возможность изучения отдельных ячеек в куче памяти, без изучения всей кучи целиком. Таким образом, определенные ячейки составляют связанный список и без полного анализа.
  • Smallfoot: Modular Automatic Assertion Checking with Separation Logic: в этой работе описывается предшественник Facebook Infer, который называется Smallfoot.
  • AL: A new declarative language for detecting bugs with Infer: AL позволяет любому разработчику проектировать новые чекеры без полного понимания внутренней кухни Infer. AL — это декларативный язык.
  • Moving Fast with Software Verification: Наконец, статья, которая раскрывает, как Facebook использует Project Infer для собственных нужд. В документе рассказывается о том, как разработчики Facebook интегрировали Infer в свой процесс разработки для обеспечения статического анализа для мобильных приложений, таких как Instagram, Facebook Messenger и приложения Facebook для Android и iOS.

Пока что Infer может использоваться лишь для мобильных приложений. Но некоторые его принципы применимы к приложениям общего назначения. Возможно, в будущем возможности Infer станут шире, и с его помощью разработчики смогут анализировать десктопные или серверные приложения.
Skillbox рекомендует:

Let's block ads! (Why?)

Капитализация Reddit достигла $3 млрд после привлечения $300 млн инвестиций

Соцсеть Reddit в ходе очередного инвестиционного раунда привлекла $300 млн. Оценка капитализации компании достигла $3 млрд. Среди инвесторов компании венчурные фонды Sequoia, Fidelity, Tacit, а также рэп-исполнитель Снуп Дог – они участвовали в новом раунде совместно с компанией Tencent, вложившей в соцсеть $150 млн.

Немного цифр


До объявления о новой сделке соцсеть Reddit провела серию инвестиционных раундов, в ходе которых привлекла $250 млн. В ходе последнего из них оценка компании составила примерно $1,8 млрд.

Всего у Reddit более двадцати инвесторов, среди которых такие компании как Advance Publications, владеющая Conde Nast, которой ранее принадлежала соцсеть.

По последним публичным данным от ноября 2017 года, аудитория Reddit составляла 330 млн активных пользователей в месяц, которые генерировали 14 млрд просмотров.

Новостной фон


Сообщения о сделке появились на фоне не самого позитивного для Reddit новостного фона. Не так давно компания столкнулась с утечкой данных, в ходе которой хакер смог проникнуть в ее системы и получить доступ к пользовательским данным – включая текущие адреса электронной почты и бэкап базы данных за 2007 год. Этот бэкап содержал старые зашифрованные пароли.

Однако серьезных последствий удалось избежать – по заявлению представителей Reddit, взломщику удалось получить только read-only доступ.

При этом инвестиции от китайской Tencent также спровоцировали волну обсуждений, связанных с возможными ограничениями свободы слова. Reddit называет себя «главной страницей интернета», и его пользователи ценят свободу слова.

При этом в самом Китае соцсеть заблокирована наряду с некоторыми другими крупными международными онлайн-сервисами.

Перспективы


По итогам предыдущего инвестиционного раунда Reddit расширила свой штат и представительства в Сан-Франциско, Лос-Анджелесе, Чикаго и Нью-Йорке.

Впервые за несколько лет компания начала показывать существенную выручку, которая по разным оценкам составляет от $85 млн до $100 млн в год. При этом Reddit остается крупнейшей социальной сетью, которая еще не вышла на биржу.

По словам сооснователя и гендиректора компании Стива Хафмана, у Reddit нет планов по активному найму сотрудников или расширению внутри США. Однако соцсеть хочет заняться международным развитием и продвижением рекламной платформы. Это позволит Reddit активнее конкурировать с гигантами онлайн-рекламы Google и Facebook.

Другие материалы по теме финансов и фондового рынка от ITI Capital:


Let's block ads! (Why?)

[Из песочницы] Пишем учебное приложение на Go и Javascript для оценки реальной доходности акций. Часть 1 — backend

Давайте попробуем написать небольшую тренировочную, но вполне себе законченную информационную систему, состоящую из серверной части на Go и клиентского веб-приложения на Javascript + Vue JS.

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

  • Деньги съедает инфляция (инфляционный риск)
  • Рубль может обесцениться (курсовой риск)

Было принято решение изучить вопрос и выбрать подходящий инструмент для инвестирования. Основными критериями были надёжность и защита сбережений от указанных выше рисков.
Вопрос я изучил и в результате пришёл к выводу, что единственным адекватным инвестиционным инструментом для жителя России являются акции биржевых фондов (ETF), причём именно те, что торгуются на Московской Бирже.

Таким образом, предлагаю написать учебное приложение, которое бы показывало доходность всех ETF, которые представлены на Московской Бирже.
Вы можете сказать, что эту доходность можно посмотреть на самом сайте биржи, а приложение, хотя бы и учебное, должно быть сколько-нибудь полезным. Согласен, поэтому попытаемся отобразить некоторую условную реальную доходность акций. Под этой условной реальной доходностью, я буду понимать доходность, скорректированную на инфляцию в России.
В первой части статьи мы разберём серверную часть приложения. Наш бэкенд написан на Go и в ходе разработки мы попытаемся применить такие возможности языка, как параллельное исполнение кода, интерфейсы, тестирование и прочее.

Требования ТЗ:

  1. Серверная часть приложения должна предоставлять по запросу данные о котировках всех ETF Московской Биржи и данные об инфляции за все месяцы торгов по каждой бумаге
  2. Серверная часть приложения должна поддерживать несколько поставщиков хранилища данных, переключение между поставщиками не должно требовать изменения кода
  3. Серверная часть приложения должна предоставлять API по протоколу http для получения данных из хранилища

Итак, давайте спроектируем программную архитектуру серверной части нашей системы.

Во-первых, придумаем структуру пакетов приложения. Согласно ТЗ, приложение будет состоять из веб-сервера, который будет предоставлять REST API и отдавать фалы нашего веб-приложения (впоследствии мы напишем SPA на Vue). Кроме того, по ТЗ мы должны сделать несколько пакетов для поставщиков хранилища данных.

На этом моменте следует остановиться поподробнее. Каким образом можно предоставить возможность переключения между поставщиками некоторой функциональности в Go? Ответ: с помощью интерфейсов. Таким образом мы должны будем разработать интерфейс (контракт) для пакетов, каждый из которых будет выполнять контракт для своего типа хранилища. В статье рассмотрим хранение данных в оперативной памяти, но по аналогии можно легко добавить любую СУБД. Итоговая структура пакетов будет такая:

Во-вторых, давайте определимся с типами данных, в которых мы будем хранить полученную информацию, и контрактом для поставщиков хранилища.

Нам потребуются типы данных для котировок акций и инфляции. Котировки и инфляцию мы будем брать по месяцам, этот масштаб вполне подходит для такого неспекуляционного инструмента, как ETF.

Контракт будет требовать наличия методов заполнения хранилища данными с сервера Мосбиржи (инициализация) и предоставления данных котировок по запросу. Всё предельно просто.

В итоге в модуль storage мы помещаем типы для хранения котировок и интерфейс:

// Package storage описывает общие требования к поставщику хранилища и используемые типы данных
package storage

// Security - ценная бумага
type Security struct {
        ID        string  // ticker
        Name      string  // полное имя бумаги
        IssueDate int64   // дата выпуска в обращение
        Quotes    []Quote // котировки
}

// Quote - котировка ценной бумаги (цена 'close')
type Quote struct {
        SecurityID string  // ticker
        Num        int     // номер измерения (номер месяца)
        TimeStamp  int64   // отметка времени в формате Unix Time
        Price      float64 // цена закрытия
}

// Interface - контракт для драйвера хранилища котировок
type Interface interface {
        InitData() error                 // инициализирует хранилище данными с сервера Мосбиржи
        Securities() ([]Security, error) // получить список бумаг с котировками
}


Данные по инфляции для простоты закодируем в модуле сервера:
var inflation = []struct {
        Year   int
        Values [12]float64
}{
        {
                Year:   2013,
                Values: [12]float64{0.97, 0.56, 0.34, 0.51, 0.66, 0.42, 0.82, 0.14, 0.21, 0.57, 0.56, 0.51},
        },
        {
                Year:   2014,
                Values: [12]float64{0.59, 0.70, 1.02, 0.90, 0.90, 0.62, 0.49, 0.24, 0.65, 0.82, 1.28, 2.62},
        },
        {
                Year:   2015,
                Values: [12]float64{3.85, 2.22, 1.21, 0.46, 0.35, 0.19, 0.80, 0.35, 0.57, 0.74, 0.75, 0.77},
        },
        {
                Year:   2016,
                Values: [12]float64{0.96, 0.63, 0.46, 0.44, 0.41, 0.36, 0.54, 0.01, 0.17, 0.43, 0.44, 0.40},
        },
        {
                Year:   2017,
                Values: [12]float64{0.62, 0.22, 0.13, 0.33, 0.37, 0.61, 0.07, -0.54, -0.15, 0.20, 0.22, 0.42},
        },
        {
                Year:   2018,
                Values: [12]float64{0.31, 0.21, 0.29, 0.38, 0.38, 0.49, 0.27, 0.01, 0.16, 0.35, 0.50, 0.84},
        },
}

В-третьих, давайте опишем конечные точки нашего API. Их будет всего две: для котировок и инфляции. Только метод HTTP GET.
        // API нашего сервера
        http.HandleFunc("/api/v1/securities", securitiesHandler) // список бумаг с котировками
        http.HandleFunc("/api/v1/inflation", inflationHandler)   // инфляция по месяцам

Собственно получение и обработка данных с сайта Мосбиржи осуществляется в методе инициализации. Данные берём согласно справке по API биржи.
На что стоит обратить внимание: мы вынуждены использовать отдельный запрос по каждой ценной бумаге (а их уже пара десятков). Исполнение инициализации данных последовательно, в один поток, заняло бы много времени. Поэтому мы будем использовать гордость Go — горутины. Обратите внимание на следующий кусок кода:
// InitData инициализирует хранилище данными с сервера Мосбиржи
func (s *Storage) InitData() (err error) {

        securities, err := getSecurities()
        if err != nil {
                return err
        }

        // объект синхронизации горутин
        var wg sync.WaitGroup

        // увеличиваем счётчик горутин по количеству ценных бумаг
        wg.Add(len(securities))

        for _, security := range securities {

                go func(item storage.Security) {
                        // уменьшаем счётчик перед завершением функции
                        defer wg.Done()

                        var quotes []storage.Quote
                        quotes, err = getSecurityQuotes(item)
                        if err != nil {
                                fmt.Println(item, err)
                                return
                        }

                        item.Quotes = quotes

                        err = s.Add(item)
                        if err != nil {
                                return
                        }

                }(security)

        }
        // ожидаем выполнения всех горутин
        wg.Wait()

        return err

}


В функции инициализации данных мы распараллелили запросы к серверу. На практике такой парсинг сайтов имеет ряд проблем:
  • Может привести к автоматической блокировке запросов из-за подозрения на DoS
  • Нужно использовать модуль context или управляющий канал для принудительного завершения горутин
  • Нужно использовать канал для возврата ошибки из горутины

Для простоты все эти моменты опускаются.

Для целей учебной программы нам хватит встроенного маршрутизатора HTTP-запросов. В более сложных системах, вы, вероятно, захотите использовать какой-нибудь другой. Лично я пользуюсь маршрутизатором из проекта Gorilla, но вообще их полно.

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

Итак давайте напишем наш сервер:

// Package main реализует веб-сервер проетка moex-etf
package main

import (
        "encoding/json"
        "fmt"
        "log"
        "moex_etf/server/storage"
        "moex_etf/server/storage/inmemory"
        "net/http"
)

var db storage.Interface

func main() {

        // здесь мы можем, например, добавить проверку флагов запуска или переменной окружения
        // для выбора поставщика хранилища. выбрали память
        db = inmemory.New()

        fmt.Println("Inititalizing data")
        // инициализация данных хранилища
        err := db.InitData()
        if err != nil {
                log.Fatal(err)
        }

        // API нашего сервера
        http.HandleFunc("/api/v1/securities", securitiesHandler) // список бумаг с котировками
        http.HandleFunc("/api/v1/inflation", inflationHandler)   // инфляция по месяцам

        // запускаем веб сервер на порту 8080
        const addr = ":8080"
        fmt.Println("Starting web server at", addr)
        log.Fatal(http.ListenAndServe(addr, nil))

}

// обработчик запроса котировок
func securitiesHandler(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "GET")
        w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")

        if r.Method != http.MethodGet {
                return
        }

        securities, err := db.Securities()
        if err != nil {
                w.WriteHeader(http.StatusInternalServerError)
                w.Write([]byte(err.Error()))
        }

        err = json.NewEncoder(w).Encode(securities)
        if err != nil {
                w.WriteHeader(http.StatusInternalServerError)
                w.Write([]byte(err.Error()))
        }

}

// обработчик запроса инфляции
func inflationHandler(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "GET")
        w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")

        if r.Method != http.MethodGet {
                return
        }

        err := json.NewEncoder(w).Encode(inflation)
        if err != nil {
                w.WriteHeader(http.StatusInternalServerError)
                w.Write([]byte(err.Error()))
        }

}

// инфляция в России по месяцам
var inflation = []struct {
        Year   int
        Values [12]float64
}{
        {
                Year:   2013,
                Values: [12]float64{0.97, 0.56, 0.34, 0.51, 0.66, 0.42, 0.82, 0.14, 0.21, 0.57, 0.56, 0.51},
        },
        {
                Year:   2014,
                Values: [12]float64{0.59, 0.70, 1.02, 0.90, 0.90, 0.62, 0.49, 0.24, 0.65, 0.82, 1.28, 2.62},
        },
        {
                Year:   2015,
                Values: [12]float64{3.85, 2.22, 1.21, 0.46, 0.35, 0.19, 0.80, 0.35, 0.57, 0.74, 0.75, 0.77},
        },
        {
                Year:   2016,
                Values: [12]float64{0.96, 0.63, 0.46, 0.44, 0.41, 0.36, 0.54, 0.01, 0.17, 0.43, 0.44, 0.40},
        },
        {
                Year:   2017,
                Values: [12]float64{0.62, 0.22, 0.13, 0.33, 0.37, 0.61, 0.07, -0.54, -0.15, 0.20, 0.22, 0.42},
        },
        {
                Year:   2018,
                Values: [12]float64{0.31, 0.21, 0.29, 0.38, 0.38, 0.49, 0.27, 0.01, 0.16, 0.35, 0.50, 0.84},
        },
}


Приводить здесь код реализации хранилища в памяти не буду, всё доступно на Гитхабе.

Для проверки наш API:

инфляция
котировки

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

Let's block ads! (Why?)

[Из песочницы] Кто заселит землю киборгов? О будущем статусе острова Русский рассказал директор по развитию компании «Моторика»

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

Базовый облик будущего законопроекта создадут учёные и непосредственные участники рынка, которые 1-4 марта соберутся в стенах Дальневосточного федерального университета (ДВФУ) на специальной сессии правового дизайна и инжиниринга, созванной по предложению Агентства стратегических инициатив и Компании «Моторика».

Главными темами для обсуждения станут этические и юридические вопросы киборгизации людей, упрощенные режимы внедрения новых технологий в медицину, а также способы привлечения иностранных компаний и применения их разработок в России.

Какие перспективы ждут разработчиков медицинских изделий? Какие проблемы поможет решить спецстатус острова Русский? Каковы будут основные положения закона? И что в результате ждёт рынок кибермедицины?

На эти и другие вопросы отвечает один из инициаторов законопроекта – директор по развитию компании «Моторика» Василий Хлебников.
– Василий, здравствуйте! Если не секрет, расскажите почему вы решили заняться вопросом регистрации медицинских изделий?

– Здравствуйте! Сегодня ситуация такова, что начинающим молодым разработчикам, чтобы получить возможность провести клинические испытания, необходимо достаточно долгое время заниматься доклиническими исследованиями, проходить различные комиссии. В большинстве случаев требования оказываются для них невыполнимыми.

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

Мы пришли к выводу, что производителям и разработчикам необходима зона опережающего развития, в рамках которой разрешительные процедуры будут проходить гораздо быстрее. Ответственность за них ляжет на плечи конкретных людей. По предварительным планам, за разработки в зоне опережающего развития будет отвечать буквально несколько комиссий и привлеченные институты развития. Они же смогут решать часть вопросов, возникающих со стороны проектов: технических, юридических, медицинских и финансовых.

– А что конкретно будет входить в обязанности этих комиссий?

– Их базовая цель – быстро анализировать запросы команд и давать разрешение на испытания и исследования. Сегодня, если молодые разработчики какие-то моменты не понимают и подают неправильно оформленные документы – им никто не объясняет – в чем ошибка и что нужно исправить. Им просто отказывают.

А мы хотим создать такую структуру, в рамках которой все участники смогут достаточно быстро доходить до результатов в своих исследованиях, быстро проверять их эффективность и получать экономический эффект, минуя годы разрешительной волокиты.

Наша текущая цель – выстроить работу с Технопарком «Русский», медицинским центром Дальневосточного федерального университета и создать среду, в которой информация будет добываться не только через коммерческий консалтинг.

– Что включает в себя соглашение между компанией «Моторика» и Дальневосточным федеральным университетом?

– Совместно с ДВФУ мы создаем «Проектный центр», это отдельная организация для работы, в которой сейчас собирается некоторый перечень экспертов. Мы определились с базовой организационной структурой «Проектного центра» и сейчас начинаем развивать сервисы для разработчиков. Теперь нам нужно сформировать дорожную карту для будущих резидентов: как разработчикам дойти до клинических испытаний, как зарегистрировать свои исследования и т.д.

Совместно с ДВФУ мы готовим поправки к закону «О свободном порте Владивосток», где как раз описывается процедура медицинского обслуживания. Мы хотим дополнить этот закон, чтобы появилась возможность проводить клинические испытания технических средств реабилитации. Сейчас в первую очередь будем работать над упрощением системы регистрации и апробации медицинских изделий, доведения до клинических испытаний инвазивных устройств.

– Когда новый законопроект будет принят и вступит в силу, а ваша система заработает – какие этапы должны будут пройти разработчики, чтобы выйти на рынок?

– Сначала, разумеется, проведение НИОКР, доклинические испытания. Для некоторых проектов, в частности, для всех инвазивных устройств, проводятся дополнительные исследования с лабораторными животными. Если всё проходит успешно, то разработчики получают экспертное заключение, подтверждающее безопасность процедуры, её положительный эффект и уже могут подавать запрос на проведение клинических испытаний.

Мы планируем, что первичная апробация с конкретными людьми, которые нуждаются в этих технологиях, будут проводиться на базе медицинского центра ДВФУ, а потом на базе медицинских учреждений в других регионах. А после неё можно говорить о повсеместном внедрении и выводе средств реабилитации на Федеральный рынок.

– А почему для создания специальной зоны был выбран именно Дальний Восток?

– Мы выбрали остров Русский, потому что нам интересен Владивосток и в целом Азиатско-Тихоокеанский регион. Оттуда всего несколько часов полёта до Сеула, Пекина или Токио. На территории Владивостока уже действует зона «Свободного порта», пользующаяся правом беспошлинного ввоза и вывоза товаров (морской аналог таможенной зоны в аэропорту – «duty free»). А иностранные граждане могут восемь дней находиться там без визы.

Таким образом мы видим развитие Владивостока именно как центр компетенций в области медицинских технологий. «Остров киборгов» станет местом, где будут развиваться технологии киборгизации для всех тех, кому это нужно по медицинским показаниям… И для тех, кто сам захочет изменить свойства своего тела.

Сегодня множество людей ищут возможности стать носителями немедицинских инвазивных устройств. Как правило, речь идёт о чипах от банковских карт или системах мониторинга здоровья. Для таких людей все формальные разрешительные процедуры должны быть максимально упрощены.

– Расскажите, какие еще инструменты поддержки вы предлагаете разработчикам?

– На базе Медицинского центра ДВФУ разработчики получат ресурсы для прохождения клинических исследований. А в рамках Технопарка «Русский» будет осуществлена техническая поддержка проектов. Мы можем говорить о производственных мощностях, технических специалистах и акселерации проектов.

Кроме того, разработчики получат юридическое сопровождение. Далее мы будем развивать и финансовые инструменты поддержки, привлекать венчурные фонды, государственные и частные инвестиции в проекты.

Разумеется, мы рассматриваем и уже существующие инструменты поддержки, например, гранты Фонда «Сколково» или Фонда содействия инновациям. Агентство стратегических инициатив также проявляет интерес к проекту по развитию территории. В целом наша задача – создать точку притяжения для людей, которые занимаются технологиями и медициной.

– Кроме начинающих разработчиков, кого вы еще готовы пригласить?

– Мы нацелены в том числе на привлечение любых крупных игроков, которые хотят выйти на рынок России и быстро довести какую-то технологическую инициативу до апробации. В перспективе это могут быть и крупные фармацевтические компании.

Сейчас мы заинтересованы в том, чтобы набрать команду людей, которым это интересно – разработчиков и медицинских работников, являющихся в некой степени заказчиками услуг «Проектного центра». Кроме того, важным аспектом для нас является юридическая работа – законотворческая деятельность. Поэтому мы также будем рады юристам.

– На каких условиях разработчики и предприятия смогут к вам присоединиться?

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

Нам нужно, чтобы в регион пришли крупные игроки и передовые технологии, чтобы приезжали профессионалы.

– К чему должны быть готовы предприятия и команды разработчиков, желающие начать с вами сотрудничество?

– Получение резидентства и открытие юридического лица на новой территории не должно занимать много времени. Но вполне вероятно, что наиболее активные группы разработчиков захотят переехать сюда и работать во Владивостоке.

С точки зрения развития территории, для нас особенно важно – собрать разработчиков в одном месте. Мы готовы и будем рассматривать различные варианты дистанционного взаимодействия. Большую часть процедур постараемся оцифровать, чтобы непосредственное присутствие разработчиков было необязательным. Но все медицинские вмешательства, операции по вживлению придётся проводить только на территории, юридически обладающей спецстатусом.

– С кем можно связаться по вопросу сотрудничества?

– В компании «Моторика» можно связаться со мной (наш офис расположен в Москве), если вы находитесь во Владивостоке, то стоит обратиться в напрямую в Технопарк ДВФУ. Наш партнер в Медицинском центре ДВФУ – Артур Биктимиров, нейрохирург и один из основных идеологов проекта. Будем рады видеть разработчиков реабилитационных устройств и приглашаем всех к сотрудничеству. Связаться с нами можно через почту: info@motorica.org.

– Спасибо за интересную беседу, Василий. Желаем успехов вам и вашей инициативе!

Слайды для материала предоставлены компанией «Моторика».

Сессия правового дизайна и инжиниринга по разработке законопроекта, посвященного созданию специального медицинского статуса на острове Русский состоится 1-4 марта в Дальневосточном федеральном университете (Владивосток).

Подать заявку на регистрацию можно здесь.

Let's block ads! (Why?)

[Перевод] InterNyet — как в Советском Союзе изобрели интернет и почему он не заработал

Утром 1 октября 1970 года ученый-компьютерщик Виктор Глушков вошел в Кремль, чтобы встретиться с Политбюро. Он был настороженным человеком с пронзительными глазами в черных очках, с таким типом ума, который, решая одну проблему, мог найти параллельно метод решения всех аналогичных проблем. В тот момент у Советского Союза возникла серьезная проблема. Годом раньше Соединенные Штаты запустили ARPANET, первую распределенную компьютерную сеть с коммутацией пакетов, которая со временем породит интернет, каким мы его знаем сегодня. Распределенная сеть изначально была разработана с целью опередить СССР, позволяя компьютерам ученых и правительственных лидеров США обмениваться информацией даже в случае ядерной атаки. Это была высшая точка технологической гонки, и Советы должны были чем-то ответить.

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

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

В период с 1959 по 1989 год ведущие советские ученые и государственные деятели неоднократно пробовали создать национальную компьютерную сеть с просоциальными целями. С глубокими и еще не зажившими ранами Второй мировой войны, Советский Союз продолжал развертывание масштабных проектов модернизации, которые за пару поколений превратили разбросанную царскую нацию неграмотных крестьян в глобальную ядерную державу.

После того как Хрущев осудил культ личности Сталина в 1956 году, страну охватило чувство открывающихся возможностей. На эту сцену взошло множество проектов по связыванию национальной экономику, среди которых было и первое в мире предложение создания национальной гражданской компьютерной сети. Идея была детищем военного ученого Анатолия Ивановича Китова.

Молодой человек с невысоким ростом и увлеченный математикой, во время Великой отечественной войны Китов был произведен в ряды Красной Армии. Затем, в 1952 году, в секретной военной библиотеке он познакомился с шедевром Норберта Винера «Кибернетика» (1948). Название книги — неологизм, взятый из греческого языка, чтобы озаглавить послевоенную науку о самоуправляемых информационных системах. При поддержке двух других ведущих ученых, Китов преобразовал кибернетику в целый русскоязычный подход к разработке систем управления и связи на основе компьютеров. Гибкий системный словарь кибернетики был нацелен на оснащение советского государства высокотехнологичным инструментарием для рационального марксистского управления, противоядием против насилия и культа личности, характеризующим государство Сталина. И действительно, кибернетика, возможно, смогла бы даже гарантировать, что больше никогда не появится еще одного властного диктатора, а может это было лишь технократической мечтой.

В 1959 году, будучи директором секретного военного компьютерного исследовательского центра, Китов сосредоточил свое внимание на том, чтобы направить «неограниченное количество вычислительных мощностей» на решение задач планирования национальной экономики, чему перманентно мешала проблема координации информации, стоящая на пути у всего «советского социалистического проекта». К примеру, в 1962 году было обнаружено, что погрешность ручного подсчета при переписи населения 1959 года, вылилась в ошибку прогноза численности населения на 4 млн. человек. Китов изложил свои мысли в докладной записке, которую отправил Хрущеву. Он предложил разрешить гражданским организациям использовать действующие военные компьютерные комплексы в целях экономического планирования в ночные часы, когда большинство военных спят. В записке было сказано, что специалисты по экономическому планированию могут использовать вычислительные излишки военных, чтобы корректировать проблемы переписи в режиме реального времени, при необходимости также корректируя экономический план. Свою военно-гражданскую национальную компьютерную сеть он назвал автоматизированной системой управления экономикой.

Как это порой случалось, военные руководители Китова перехватили письмо, прежде чем оно было доставлено Хрущеву. Они были взбешены его предложением, гласящим что Красная Армия должна делиться ресурсами с гражданскими экономическими планировщиками — ресурсами, которые Китов также осмелился назвать отставшими от времени. Был организован секретный военный трибунал для рассмотрения его преступлений, за которые Китов был быстро лишен членства в Коммунистической партии на год и уволен с работы на постоянной основе. Так закончилась история первой из когда-либо предложенных общенациональных компьютерных сетей.

Идея, тем не менее, выжила. В начале 1960-х годов уже другой ученый взялся за предложение Китова, человек, с которым жизненные пути оказались настолько близки, что через несколько десятилетий их дети даже вступили в брак — Виктор Михайлович Глушков.

Полное название плана Глушкова — «Общегосударственная автоматизированная система сбора и обработки информации для учета, планирования и управления народным хозяйством СССР» — говорит сама за себя и показывает всю грандиозность амбиций. Впервые предложенная в 1962 году, автоматизированная система «ОГАС» должна была стать национальной компьютерной сетью с удаленным доступом в реальном времени, построенной на существующих и новых телефонных линиях. В своей самой амбициозной версии она охватила бы большую часть евразийского континента, представляя собой нервную систему, которая интегрирована в каждый завод и предприятие плановой экономики. Сеть была смоделирована по образу иерархической трехуровневой пирамидальной структуры государства и экономики: один главный компьютерный центр в Москве будет соединен с 200 компьютерными узлами среднего уровня в крупных городах, которые, в свою очередь, будут связаны с 20 000 терминалами, распределенными по ключевым производственным площадкам народного хозяйства.


Виктор Михайлович Глушков в 1979 году

В соответствии с большими опытом Глушкова в сфере жизнедеятельности страны, архитектура сети неслучайно содержала в себе принципы децентрализованного дизайна. Это означало, что хотя в Москве и можно будет указать, кому какие разрешения предоставить, любой авторизованный пользователь мог связаться с любым другим пользователем через пирамидальную сеть — без прямого разрешения от материнского узла. При проектировании Глушков хорошо понимал преимущества использования локальных знаний, потратив большую часть своей карьеры, работая над созвучными математическими проблемами, постоянно перемещаясь между родным городом и столицей (он в шутку назвал поезд Киев-Москва своим «вторым домом»).

Проект ОГАС показался многим чиновникам и специалистам по экономическому планированию, особенно в конце 1960-х годов, следующим шагом в решении старой головоломки: Советы были согласны с тем, что коммунизм — это путь будущего, но никто со времен Маркса и Энгельса не знал, как именно туда добраться. По мнению Глушкова сетевые вычисления могли приблизить страну к эпохе, которую автор Фрэнсис Спаффорд назвал «красным изобилием». Это был способ, с помощью которого медленная основа централизованной экономики — квоты, планы и сборники отраслевых стандартов — превратится в нейронную сеть нации, движимую невероятной скоростью электричества. Проект претендовал не меньше, чем на установление «электронного социализма».

Такие амбиции требуют блестящих, целеустремленных людей, готовых отказаться от старого мышления. В 1960-х годах таких людей можно было найти в Киеве — в нескольких кварталах от того места, где братья Стругацкие по ночам писали свою научную фантастику, а днем ​​работали физиками. Там, на окраине Киева, Глушков, начиная с 1962 года, руководил Институтом кибернетики в течение 20 лет. Он набирал в свой институт амбициозных юношей и девушек — средний возраст исследователей был около 25 лет. Глушков и его молодые сотрудники посвятили себя разработке ОГАС и других кибернетических проектов, стоявших на службе Советского государства — таких как система электронных чеков, которая должна была заменить твердую валюту на виртуальную, и превратить в систему электронных счетов — и это в начале 1960-х. Глушков, который, как известно, мог заставить замолчать даже идеологов Коммунистической партии, цитируя фразы Маркса по памяти, описал свое новшество как верное исполнение марксистского пророчества о безденежном социалистическом будущем. К сожалению для Глушкова, идея советской электронной валюты вызвала лишь надуманные опасения и не получила одобрения комиссии в 1962 году. Но вот его грандиозный проект экономической сети дожил до следующего этапа.

Группа кибернетиков представляла, своего рода, умную нейронную сеть, нервную систему для советской экономики. Эта кибернетическая параллель между компьютерной сетью и мозгом наложила свой отпечаток и на другие инновации в вычислительной теории в Киеве. Например, вместо так называемого «бутылочного горлышка» архитектуры фон Неймана (которое ограничивает объем передаваемых данных в компьютере), команда Глушкова предложила модель, подобную одновременному срабатыванию большого количества синапсов в человеческом мозге. Помимо бесчисленных компьютерных проектов для мэйнфреймов, в число теоретических работ входили также теория автоматов, безбумажный документооборот и парадигма программирования на естественном языке, которая позволяла бы людям общаться с компьютерами на семантическом, а не на синтаксическом языке, как это делают программисты сегодня. Наиболее масштабно Глушков и его ученики теоретизировали «цифровое бессмертие», концепцию, которую мы могли бы назвать сегодня «загрузкой разума», держа книги Айзека Азимова или Артура Кларка в руках. На смертном одре, десятилетия спустя, Глушков утешал свою скорбящую жену глубоким размышлением: «Не волнуйся», сказал он. «Однажды свет от нашей Земли достигнет далеких созвездий, и в каждом из них мы снова окажемся молодыми. Так мы будем вечно вместе!».

После рабочего дня кибернетики предавались увеселениям, полным легкомыслия и ярких шуток, граничащих с откровенным бунтарством. Их клуб для «работы в нерабочее время», представляющий собой место выпуска пара, считался также виртуальной страной, независящей от московского правления. На новогодней вечеринке в 1960 году они окрестили свою группу «Кибертонией» и стали организовывать регулярные общественные мероприятия в Киеве и Львове — танцы, симпозиумы, конференции, иногда даже издавая насмешливые статьи, вроде «О желании остаться невидимым — по крайней мере для власти». Вместо приглашений на мероприятия группа выпускала каламбурные паспорта, свадебные сертификаты, информационные бюллетени, валюту с перфокартами и даже конституцию Кибертонии. В качестве пародии на советскую структуру управления, Кибертонией управлял совет роботов, и во главе этого совета стоял их талисман и верховный лидер, робот, играющий на саксофоне — дань культурному значению джаза в США.

Глушков лично тоже повеселился: он назвал свои мемуары «Вопреки власти», несмотря на свою официальную должность вице-президента Украинской Академии наук. Контркультура, как способность противостоять другим силам, по мнению Фреда Тернера, долгое время была близким спутником киберкультуры.

Все это, однако, требовало денег — больших денег, особенно для проекта ОГАС. Это означало необходимость убедить в этом Политбюро. Отсюда и получилось, что Глушков оказался в Кремле 1 октября 1970 года в надежде продолжить работу в Кибертонии и подарить интернет советскому государству.

На пути у Глушкова стоял один человек — министр финансов Василий Гарбузов. Гарбузов не горел желанием, чтобы какие-то «оптимизированные в реальном времени компьютерные сети» управляли экономикой целого государства. Вместо этого он призывал к созданию простых компьютеров, которые будут зажигать освещение и воспроизводить музыку на птицефермах, чтобы увеличить производство яиц, что он лично наблюдал в ходе недавнего визита в Минск. Конечно, его мотивы не были порождением прагматизма. Он хотел получить финансирование для собственного министерства.

Ходят даже слухи, что он лично встречался с настроенным на реформы председателем Совета министров Алексеем Косыгиным, угрожая, что если Центральное статистическое управление сохранит контроль над проектом ОГАС, то Гарбузов и его министерство финансов потопит любые проекты реформирования, которые он выдвинет, так же, как он сделал это в части либеральных реформ Косыгина пять лет назад.

Чтобы выступить против Гарбузова и поддержать советский интернет, Глушкову нужны были союзники. Но на собрании их не было. В тот день было два пустых кресла — одно председателя Совета министров, а второе — генерального секретаря и известного технократа Леонида Брежнева. Они были двумя самыми влиятельными персонами в советском государстве, и, вероятно, сторонниками ОГАС. Но, судя по всему, они решили не противостоять мятежу министров.

Гарбузов успешно убедил Политбюро в том, что проект ОГАС с его амбициозными планами по моделированию и управлению информационными потоками в плановой экономике слишком поспешен. Комиссия, едва не принявшая позицию другой стороны, сочла, что безопаснее поддержать Гарбузова. Так, по-прежнему сверхсекретный проект ОГАС остался в подвешенном состоянии еще на одно десятилетие.

Факторы, которые противостояли ОГАС, напоминают силы, которые в конечном итоге разрушили Советский Союз — удивительно неофициальные формы недобросовестного поведения. Мятежные министры, чиновники статуса-кво, взвинченные управляющие заводами, сбитые с толку рабочие и даже другие экономические реформаторы выступали против проекта ОГАС, потому что это было в их локальных интересах. Без государственного финансирования и общей координации, проект национальной сети раскололся в 1970-х и 80-х годах на лоскутное одеяло из десятков, а затем и сотен изолированных, не взаимодействующих между собой заводских локальных систем управления. Советское государство не сумело объединить свою нацию не потому, что было слишком жестким или директивным по своей структуре, а потому, что оно было слишком непостоянным и губительным на практике.

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

В судьбе советского интернета можно увидеть серьезное предупреждение о будущем. Сегодня интернет, понимаемый как единая глобальная сеть сетей, стоящая на страже информационной свободы, демократии и торговли — находится в серьезном упадке. Если высказывания Принса и гайд Associated Press звучат для вас не убедительно, подумайте, как часто компании и государства сегодня стремятся распространить свое влияние в интернете: массовые приложения — это скорее «огороженный сад» для арендаторов, чем общественный ресурс пользователей. Замкнутые системы с «повышенной гравитацией» (такие как Facebook или Китайский файрвол) все больше блокируют сайты, которые ссылаются наружу. Главы Франции, Индии, России и других стран стремятся вмешаться в деятельность ICANN и ужесточить местные законы для своих граждан. Фактически, сотни не-интернет сетей функционируют в корпорациях и странах на протяжении десятилетий. Будущее компьютерных сетей — это не один интернет, а множество различных онлайн-экосистем.

Другими словами, будущее, несомненно, напоминает прошлое. 20-й век показал пример множества национальных компьютерных сетей, претендующих на глобальный статус. Драма Холодной войны, то что мы могли бы с подмигиванием назвать «Soviet nyetworking» или, как именовала статья историка Славы Геровича, «Soviet InterNyet», помогает завершить сравнительное исследование компьютерных сетей первой волны. Витающее в воздухе ощущение от ушедших в историю и потенциальных будущих сетей, что существует только одна глобальная сеть сетей, кажется скорее исключением из правила. Учитывая, что ирония времен Холодной войны в основе этой истории — то, что кооперирующиеся капиталисты перехитрили конкурирующих социалистов — печальным образом сыграла свою роль для Советского Союза, возможно, нам не стоит быть слишком уверенными, что интернет завтрашнего дня будет намного лучше.

Антрополог и философ Бруно Латур однажды сказал, что технология — это общество, сделанное долговечным, подразумевая под этим, что в самой основе технологий лежат, в первую очередь, технологии социальные. Например, алгоритм Google PageRank считается «демократическим», поскольку, среди многих других факторов, он считает ссылки (и ссылки на сайты, делающие ссылки) в качестве голосов. Как и политики с голосами избирателей, страницы с наибольшим количеством ссылок имеют самый высокий рейтинг. Сегодня интернет кажется средством свободы, демократии и торговли, отчасти потому, что он застыл таковым в нашем сознании, так же, как западные ценности, казалось, одержали победу после Холодной войны. В разрезе советской истории интернета можно перевернуть афоризм Латура — общественные технологии стали недолговечными.

Иными словами, по мере изменения наших общественных ценностей, меняется и то, что кажется очевидным в технологиях. Ценности Советского Союза тех лет — кибернетический коллективизм, государственная иерархия и плановая экономика — кажутся нам чуждыми сегодня. То же самое можно сказать в отношении ценностей, которыми современные читатели наделяют интернет, но через призму будущих поколений. Сетевые технологии будут существовать и развиваться, даже если наши самые смелые социальные представления о них окажутся на свалке истории.

История Глушкова также является масштабным напоминанием инвесторам и другим агентам технологических изменений о том, что неординарности гения, дальновидности и политической хватки недостаточно, чтобы изменить мир. Общественные институты зачастую имеют решающее значение. Это наглядный урок советского опыта, современной медиасреды, непрерывно накапливающей данные, и другие случаи нарушения приватности — общественные институты, которые лежат в основе создания компьютерных сетей и их культур, являются жизненно важными и далекими от единства.

В то время как новые сетевые проекты и их евангелисты будут обещать светлое будущее, отдельные институциональные силы, если их не проверять, продолжат извлекать выгоду из контроллируемых сетей, направленных на вмешательство в наши жизни. Возможно, именно это и формирует в действительности ландшафт конфиденциальности — обширное влияние институциональных сил, которые могут проникнуть в нашу жизнь, а не только индивидуальные права для защиты от этой проникновения. Советское исследование напоминает нам о том, что программа шпионажа АНБ США и облачные сервера Microsoft участвуют в более давней традиции генеральных секретариатов 20-го века, приверженных присвоению личной и публичной информации для их личной выгоды.

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

Let's block ads! (Why?)