-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51

Habrahabr/New








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/new/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-new-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

EBU R128/BS.1770-3: Пакетная нормализация громкости аудио/видео файлов, ч2

Среда, 19 Июля 2017 г. 17:04 + в цитатник
В предыдущем посте обрисовал идею с пакетной нормализацией громкости аудио/видео файлов.

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

Для использования необходимо запастись знаниями матчасти звука и видео, мануалами по SoX — Sound eXchange, FFmpeg, а так же моим любимым пакетом AutoIt.

Реализация состоит в следующем:
Довольно не сложный скрипт (autoit) слушает ini файл, секциями которого являются папки, которые нужно слушать.
На каждую секцию открывается свой воркер (экземпляр скрипта), который считывает конфиг перед каждым прохдом по медиа файлу.
При удалении секции, воркер закрывается.
При закрытии главного воркера, все открытые воркеры закрываются.

Конфиг воркера имеет вид

[\\host\path\]
destination=\\host\path\
bak=\\host\path\
stmp=tmp\
tmp1=tmp\
tmp2=tmp\
otmp=tmp\
destinationExtension=.avi
threads=16
prepare_ffmpeg_cmd=-flags +ilme+ildct -deinterlace -c:v copy -c:a copy
ffmpeg_cmd=-flags +ilme+ildct -deinterlace -c:v copy -c:a copy
sox_cmd=compand 0.1,0.3 -90,-90,-70,-55,-50,-35,-31,-31,-21,-21,0,-20 0 0 0.1


Мануал:


Читать мануал

1) destination


Куда складировать итоговые файлы.

2) bak


Куда складировать исходные файлы.
У меня исходные файлы лежат в сетевой корзине, файлы бекапятся в ту же корзину, в отдельную папочку.

3) временные каталоги


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

stmp


$sFile = $stmp & $tempFile & $sExtension

Файл с сетевой корзины прилетает на хард0

tmp1


$audioInputSox = $tmp1 & $tempFile & "_sox.wav"
$audioOutput = $tmp1 & $tempFile & "_norm.wav"

Если в конфиге присутствует значение sox_cmd, то аудиоданные считываются с хард2 на и результат обработки записывается хард1
При нормализации громкости аудиоданные считываются с хард2 или хард1 (sox_cmd?) и записываются на хард1

tmp2


$audioInput = $tmp2 & $tempFile & ".wav"

Исходный аудио поток видеофайла читается с хард0 на хард2

otmp


$outFile = $otmp & $tempFile & "_out" & $destinationExtension

При сборке готового видеофайла видеопоток считывается с хард0 (stmp), аудиопоток считывется с хард1(tmp1), а результат записывается на хард2

4) destinationExtension


Контейнер результирующего видеофайла

5) threads


Записывает в командную строку сборки результирующего видеофайла threads=(threads) из конфига.

6) prepare_ffmpeg_cmd


Если не пусто, то выполняется
ffmpeg -y -ss 0:0:0.0 -r 25 -i [bak] [prepare_ffmpeg_cmd] [stmp]
Иначе, исходный файл копируется из bak в stmp

7) ffmpeg_cmd


Считывается имя файла.
Если в конце имени файла присутствует конструкция вида {HH MM SS mm ss}, то начиная с кадра HH MM SS хвост файла будет приведен к хронометражу mm ss методом ускорения/замедления без искажения аудиодорожки.
Выполняется сборка результирующего файла командой
ffmpeg -i [stmp(video)] -i [tmp1(normalized -23LUFS audio)] [ffmpeg_cmd] -map 0:v -map 1:a -threads [threads] [otmp] -y

sox_cmd


Перед нормализацией звука будет применен аудиофильтр
sox [tmp2] [tmp1] [sox_cmd]

Вот, собственно, и весь нехитрый скрипт.
Очень удобно.
Папочки создает сам, файлы отслеживает, обрабатывает, складирует как надо, глючит очень редко, почти никогда.
Память не жрет, ведет себя достойно )))
Пользуйтесь на здоровье!
Читать код скрипта
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Icon=C:\Program Files (x86)\AutoIt3\Icons\MyAutoIt3_Blue.ico
#AutoIt3Wrapper_Compile_Both=y
#AutoIt3Wrapper_UseX64=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include 
#include 

Func randomString($digits)
   Local $pwd = ""
   Local $aSpace[3]
   For $i = 1 To $digits
	  $aSpace[0] = Chr(Random(65, 90, 1)) ;A-Z
	  $aSpace[1] = Chr(Random(97, 122, 1)) ;a-z
	  $aSpace[2] = Chr(Random(48, 57, 1)) ;0-9
	  $pwd &= $aSpace[Random(0, 2, 1)]
   Next
   Return $pwd
EndFunc

$iniFile = "watch.ini"

Dim $run[0][2]
Dim $newRun[0]

Func TerminateChilds()
	For $i = 0 to UBound($run) - 1
		ProcessClose($run[$i][0])
	Next
EndFunc

Local $source
If $CmdLine[0] == 0 Then

	Local $i, $j, $exists, $pid
	OnAutoItExitRegister ( "TerminateChilds" )

	While 1
		$source = IniReadSectionNames($iniFile)
		For $i = 0 To UBound($run) - 1
			$exists = False
			For $j = 1 To $source[0]
				If $source[$j] == $run[$i][1] Then $exists = True
			Next
			If Not $exists Then
				ProcessClose($run[$i][0])
				_ArrayDelete($run, $i)
				ContinueLoop
			EndIf
		Next
		For $i = 1 To $source[0]
			$exists = False
			For $j = 0 To UBound($run) - 1
				If $source[$i] == $run[$j][1] Then $exists = True
			Next
			If Not $exists Then
				$pid = Run(@ScriptName & " """ & $source[$i] & """")
				Dim $temp[1][2] = [[$pid, $source[$i]]]
				_ArrayAdd($run, $temp)
				ContinueLoop
			EndIf
		Next
		For $i = 0 To UBound($run) - 1
			If ProcessExists($run[$i][0]) == 0 Then
				$pid = Run(@ScriptName & " """ & $run[$i][1] & """")
				$run[$i][0] = $pid
				ContinueLoop
			EndIf
		Next

		Sleep(1000)
	WEnd

EndIf

MsgBox($MB_SYSTEMMODAL, $CmdLine[1], "I am started " & @CRLF & $CmdLine[1], 10)
Func Terminated()
	MsgBox($MB_SYSTEMMODAL, $CmdLine[1], "I am terminated " & @CRLF & $CmdLine[1], 10)
EndFunc
OnAutoItExitRegister ( "Terminated" )
TraySetToolTip($CmdLine[1])

$tools = "bs1770gain-tools\"

Local $source = $CmdLine[1]
Local $destination = IniRead($iniFile, $source, "destination", Null)
Local $bak = IniRead($iniFile, $source, "bak", Null)
Local $stmp = IniRead($iniFile, $source, "stmp", Null)
Local $tmp1 = IniRead($iniFile, $source, "tmp1", Null)
Local $tmp2 = IniRead($iniFile, $source, "tmp2", Null)
Local $otmp = IniRead($iniFile, $source, "otmp", Null)
Local $ffmpeg_cmd = IniRead($iniFile, $source, "ffmpeg_cmd", Null)
Local $destinationExtension = IniRead($iniFile, $source, "destinationExtension", Null)
Local $threads = IniRead($iniFile, $source, "threads", Null)
Local $sox_cmd = IniRead($iniFile, $source, "sox_cmd", Null)

If Not FileExists($source) Then DirCreate($source)
If Not FileExists($bak) Then DirCreate($bak)
If Not FileExists($destination) Then DirCreate($destination)
If Not FileExists($stmp) Then DirCreate($stmp)
If Not FileExists($tmp1) Then DirCreate($tmp1)
If Not FileExists($tmp2) Then DirCreate($tmp2)
If Not FileExists($otmp) Then DirCreate($otmp)

Local $tempFile
Local $sFile
Local $descriptionFile
Local $audioInput
Local $audioOutput
Local $outFile
Local $sTitr
Local $eTitr

While 1
	Local $files = _FileListToArray($source, "*", $FLTA_FILES, False)

	Local $i = 1
	For $i = 1 To Ubound($files) - 1

		Local $f = $files[$i]
		Local $sDrive = "", $sDir = "", $sFileName = "", $sExtension = ""
		Local $aPathSplit = _PathSplit($f, $sDrive, $sDir, $sFileName, $sExtension)

		Local $h = FileOpen($source & $sFileName & $sExtension, $FO_APPEND)
		If $h == -1 Then ContinueLoop
		FileClose($h)
		Sleep(50)
		Local $h = FileOpen($source & $sFileName & $sExtension, $FO_APPEND)
		If $h == -1 Then ContinueLoop
		FileClose($h)
		Sleep(50)
		Local $h = FileOpen($source & $sFileName & $sExtension, $FO_APPEND)
		If $h == -1 Then ContinueLoop
		FileClose($h)
		Sleep(50)
		Local $h = FileOpen($source & $sFileName & $sExtension, $FO_APPEND)
		If $h == -1 Then ContinueLoop
		FileClose($h)

		$bak = IniRead($iniFile, $source, "bak", Null)
		$destination = IniRead($iniFile, $source, "destination", Null)
		$stmp = IniRead($iniFile, $source, "stmp", Null)
		$tmp1 = IniRead($iniFile, $source, "tmp1", Null)
		$tmp2 = IniRead($iniFile, $source, "tmp2", Null)
		$otmp = IniRead($iniFile, $source, "otmp", Null)
		$ffmpeg_cmd = IniRead($iniFile, $source, "ffmpeg_cmd", Null)
		$destinationExtension = IniRead($iniFile, $source, "destinationExtension", Null)
		$threads = IniRead($iniFile, $source, "threads", Null)
		$sox_cmd = IniRead($iniFile, $source, "sox_cmd", Null)
		$pre_cmd = IniRead($iniFile, $source, "prepare_ffmpeg_cmd", Null)

		$tempFile = randomString(8)
		$bak &= $sFileName & $sExtension
		$sFile = $stmp & $tempFile & $sExtension
		$descriptionFile = $tmp1 & $tempFile & $sExtension & ".ini"
		$audioInput = $tmp2 & $tempFile & ".wav"
		$audioInputSox = $tmp1 & $tempFile & "_sox.wav"
		$audioOutput = $tmp1 & $tempFile & "_norm.wav"
		$outFile = $otmp & $tempFile & "_out" & $destinationExtension

		If FileMove($source & $sFileName & $sExtension, $bak, $FC_OVERWRITE) == 0 Then ContinueLoop
		If Not $pre_cmd Then
			If FileCopy($bak, $sFile, $FC_OVERWRITE) == 0 Then ContinueLoop
		Else
			$cmd_pre  = $tools & "ffmpeg -y -ss 0:0:0.0 -r 25 -i """ & $bak & """ " & $pre_cmd & " " & $sFile
			RunWait($cmd_pre)
		EndIf
		Sleep(100)

		;$log = 	FileOpen($tempFile & ".bat", $FO_OVERWRITE + $FO_UTF8 + $FO_CREATEPATH)

		$cmd_info = "cmd /c """ & $tools & "ffprobe -v quiet -print_format ini -show_format -show_streams " & $sFile & " > """ & $descriptionFile & """"
		;FileWriteLine($log, $cmd_info)
		RunWait($cmd_info)

		$dur = Number(IniRead($descriptionFile, "streams.stream.0", "duration", Null))

		$cmd_AudioInput = $tools & "ffmpeg -ss 0:0:0 -i " & $sFile & " -t " & $dur & " -vn -c:a pcm_s16le -af ""pan=stereo| FL < FL + 0.5*FC + 0.6*BL + 0.6*SL | FR < FR + 0.5*FC + 0.6*BR + 0.6*SR"" -ac 2 " & $audioInput & " -y -threads " & $threads
		;FileWriteLine($log, $cmd_AudioInput)
		RunWait($cmd_AudioInput)
		Sleep(100)

		$audioOutput = "tmp\" & $tempFile & ".flac"

		If IsString($sox_cmd) And $sox_cmd <> "" Then

			$audioOutput = "tmp\" & $tempFile & "_sox.flac"

			$cmd_Sox = $tools & "sox  " & $audioInput & " " & $audioInputSox & " " & $sox_cmd
			;FileWriteLine($log, $cmd_Sox)
			RunWait($cmd_Sox)
			$audioInput = $audioInputSox
		EndIf

		$cmd_BS1770gain = "bs1770gain --ebu """ & $audioInput & """ -ao ""tmp"""
		;FileWriteLine($log, $cmd_BS1770gain)
		RunWait($cmd_BS1770gain)
		Sleep(100)

		$a = StringRegExp($sFileName, "^.+{(\d{2}) (\d{2}) (\d{2}) (\d{2}) (\d{2})}$", $STR_REGEXPARRAYGLOBALMATCH)

		If @error Then
			$cmd_Output = $tools & "ffmpeg -i " & $sFile & " -i " & $audioOutput & " " & $ffmpeg_cmd & " -map 0:v -map 1:a -threads " & $threads & " " & $outFile & " -y"
			;FileWriteLine($log, $cmd_Output)
			RunWait($cmd_Output)
		Else

			$titr_h = Number($a[0])
			$titr_m = Number($a[1])
			$titr_s = Number($a[2])

			$dur_m = Number($a[3])
			$dur_s = Number($a[4])

			$dur = $dur - ($titr_h*60*60 + $titr_m*60 + $titr_s)
			$dstDur = $dur_m*60 + $dur_s
			$outDur = $titr_h*60*60 + $titr_m*60 + $titr_s + $dur_m*60 + $dur_s
			$speed = $dstDur / $dur

			$codec = IniRead($descriptionFile, "streams.stream.0", "codec_name", Null)

			$sTitr = $tmp1 & $tempFile & "_stitr" & $sExtension
			$eTitr = $tmp1 & $tempFile & "_etitr" & $sExtension

			$cmd_ETirt  = $tools & "ffmpeg -y -ss " & $titr_h & ":" & $titr_m & ":" & $titr_s & " -i " & $sFile & " -filter:v ""setpts=" & $speed & "*PTS"" -t 00:" & $dur_m & ":" & $dur_s & " -c:v " & $codec & " -qscale:v 0 -flags +ilme+ildct -deinterlace -an " & $eTitr
			$cmd_STitr  = $tools & "ffmpeg -y -ss 0:0:0 -i " & $sFile & " -t " & $titr_h & ":" & $titr_m & ":" & $titr_s & " -c:v copy -an " & $sTitr
			$cmd_Output = $tools & "ffmpeg -y -i concat:""" & $sTitr & "|" & $eTitr & """ -i " & $audioOutput & " -t " & $outDur & " " & $ffmpeg_cmd & " -map 0:v -map 1:a -threads " & $threads & " " & $outFile
			;FileWriteLine($log, $cmd_ETirt)
			;FileWriteLine($log, $cmd_STitr)
			;FileWriteLine($log, $cmd_Output)
			RunWait($cmd_ETirt)
			RunWait($cmd_STitr)
			RunWait($cmd_Output)

		EndIf

		;FileClose($log)

		FileMove($outFile, $destination & $sFileName & $destinationExtension, $FC_OVERWRITE)

		Sleep(100)

		FileDelete($sFile)
		FileDelete($descriptionFile)
		FileDelete($sTitr)
		FileDelete($eTitr)
		FileDelete($tmp2 & $tempFile & ".wav")
		FileDelete($tmp1 & $tempFile & "_sox.wav")
		FileDelete($tmp1 & $tempFile & "_norm.wav")
		FileDelete($audioOutput)

		;Exit

	Next

	Sleep(1000)
WEnd

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333686/


Метки:  

Программисты-коммунисты всех стран, соединяйтесь

Среда, 19 Июля 2017 г. 16:17 + в цитатник


Здравствуйте, товарищи.

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

Нам нужны и вы.

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

Что такое Техноком?

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

Задачи и ресурсы

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

Работает это так. Человек заходит на главную страницу «Поехали!» умной социальной сети (первая картинка этой статьи — это её скриншот), читает призыв присоединится или выбирает свой язык; если он определился неправильно (уже несколько языков есть), нажимает на кнопку «Да, я с вами» и … и сейчас нормально не доделано, сейчас человек просто заполняет информацию о себе и всё, дальше попадает в социальную сеть. Ну ещё текст на странице «Поехали!» меняется, там появляются ссылки на разные сообщества, ссылка на вступление в команду Технокома. Потом мы вручную его добавляем в разные чаты и системы, и только после этого человек видит, как много тут всего интересного. А будет по-другому, автоматизировано.

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

Дальше люди присоединяются к разным типам сообществ, описывают свои ресурсы, задачи и т.д. На странице «Поехали!» видят, что они могут сделать здесь и сейчас, чтобы достичь различных важных целей. Ресурсы (в том числе компетенции) людей и организаций автоматически концентрируются в ближайших Советах и так далее. Человеку выдаётся информация на основе умных методологий, что и как он может сделать. Например: “В вашей местности проживает ещё 681 человек, которые хотят создать народное предприятие, где все работники являются собственниками, у вас указаны необходимые компетенции, вы хотите узнать больше и присоединиться?” В общем, не буду долго расписывать, суть вы поняли, будем делать так, что только человек нажал на главной странице на кнопку “Да, я с вами” и через 30 минут он объединяется с кучей людей по разным направлениям, добавлен в нужные образовательные курсы, вступил в профсоюз и создаёт народное предприятие или уже подал заявку на работу в существующее народное предприятие. Тут и автоматическая генерация всех документов и так далее.

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



Сообщества

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



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



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

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

Я думаю, многие читали и думали, а где они собираются деньги для этого брать? И тут увидели написанное про организации и подумали, что будут какие-то платные услуги, проценты или что-то такое. Нет, всё бесплатно, мы строим технокоммунизм. А ресурсы мы берём и будем брать из множества источников. Сейчас просто делаем то, что нам нравится, во что верим. Часто в свободное от основной работы время, кто-то может себе позволить заниматься этим большую часть времени. А дальше будут созданы различные Организации, где все мы сможем принимать участие в работе, постепенно будет внедряться безденежное общество и так далее. Об этом подробнее будет ниже.

На чём делается

Изначально, поскольку кругом было нытьё о том, что это сделать “неееевозможно”, задача была показать, что можно делать на чём угодно, чтобы пройти первые шаги, своим примером помочь многим другим людям поверить в свои силы, поэтому был выбран самый показательный путь в виде WordPress и куча плагинов для него, в том числе сейчас пишутся свои. Постепенно собирается конфигурация из разных движков, мониторим, чтобы это не умерло, а наоборот развивалось и на следующий этап развития для создания уже максимально производительного решения накапливаем компетенции. Исходя из текущего развития событий, того, какими навыками обладают вновь присоединившиеся члены команды Технокома, получается, что мы будем делать новый более производительный движок в основном на Python. Но мы ожидаем что развитие текущей конфигурации, центральное звено которой на PHP ещё долго послужит нам. В общем, нам нужны PHP-шники и любые другие программисты, нам нужны админы, нам нужны все. Ничто в техническом плане не догма, если придёт толпа супер-спецов по Ruby, то и им найдётся место. Всё по всей работе Технокома делается и будет делаться в рамках открытых лицензий, это касается и ПО, и железа. Знания должны быть свободны и доступны всем людям.

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



Наши в городе

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

Постепенно продвигаемся в медиа-сфере, в Сербии по государственному телевизору Воеводины показали про Техноком (вот запись), на YouTube каналах инфа выходит. Сейчас готовимся к новому рывку. Общаюсь, налаживаю контакты с некоторыми медиа-известными людьми. Весело получилось с Константином Сёминым. Мы обозначили круг идеологически близких нам медиа-лиц, с которыми хотим познакомится, в том числе с Константином. Узнал его контактные данные через общих знакомых, списались, договорились, что как он разгребётся, можем записать с ним видео. Прошло полтора месяца, он всё по командировкам ездил, я как-то на очередном нашем общем разговоре озвучил, что можно ему напомнить про нас, в шутку говорю, что можно к нему кого-то послать, сказать что-то типа: «У Технокома длинные руки». И только я это сказал, через несколько дней, Константин Сёмин приехал в Крым, в гостинице шёл в свой номер, тут его видит один из наших сторонников, который там случайно оказался, ну и подошёл, рассказал про длинные руки Технокома напомнил ему про Техноком. В общем надеюсь, что он не испугался, что мы за ним следим, ждём пока освободится. Если говорить про телевизионные медиа-лица, уже поддержавшие нас так или иначе, телеведущий РБК Юрий Таманцев видео для нас записал (ссылка на видео). Получилось даже весело, по видео видно как он сопротивляется новой непривычной информации. А теперь мне звонит регулярно поговорить, думает, как ещё помочь, потому что кругом него желчь, бабло, кидалово. Он звонит мне, я ему про строительство светлого будущего 5 минут наговорю, человеку сразу легче становится.

Ещё интересный случай получился. У Технокома в Мариуполе даже первый партизан нашёлся. Я однажды случайно заметил, что один человек активно поддерживает нашу инфу во ВКонтакте, написал ему, предложил присоединятся к Технокому, а он рассказал, что он уже давно для Технокома разрабатывает автоматизированную систему аналитики и поиска новых единомышленников через социальные сети, решил, что пока не сделает не будет в команду Технокома вступать, в общем, партизанил отдельно пока альфу-версию не сделал, сейчас вступил в команду, будем это доделывать и расширять наши мощности по привлечению людей в стройные ряды Технокома.



Эффективность

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

Сейчас с эффективностью действий намного лучше, хоть и всё равно ещё хуже, чем хочется. Но постоянно ищем способы делать больше, эффективнее. Вот пример с документами, точнее с различными правилами, лицензионными соглашениями и так далее. Раз мы делаем ресурс, куда будут со всего мира мощным потоком засасываться люди, чтобы помочь им действовать сообща на основе умных методологий, нужно соблюдать различные юридические нормы, нужно себя защищать через различные юридические механизмы. Когда перед запуском активной работы по созданию Технокома я об этом рассказывал в разных местах, был один из пунктов, который мне озвучивали люди про то, что без бесконечного количества миллионов долларов одной агрессивной северо-американской страны, ничего не получится. В перечислении говорили и: "… а ещё вы хоть понимаете, что вот их (ВКонтакте, Фейсбук и т.д.) всякие лицензионные соглашения, правила, уведомления, это всё прорабатывалось кучей дорогих юристов, на разных языках, одно это стоит много миллионов? Вы понимаете, что вы не можете просто взять их документы, они вас засудят?". Ну вот когда дошло дело до этого вопроса, я просто написал во ВКонтакте и просил: “Можно мы возьмём у вас все ваши документы и для себя адаптируем?”. Они ответили, что можно. Конечно, такие большие документы и адаптировать под себя не совсем просто, но опыт есть и юристы к команде Технокома уже тоже присоединяются. Получается, что мне дольше рассказывали о том, что это невозможно, чем у меня заняло время написать спросить и получить ответ, что можно. Это не означает, что всё у нас получается так легко, это означает что не нужно себе придумывать лишние преграды. И это, кстати, снова ответ на вопрос о том, где мы собираемся брать ресурсы для такого большого дела. Поскольку у нас не стоит задача освоить государственный бюджет или деньги инвесторов себе в карман, у нас всё получается очень экономно и даже бесплатно.

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



Новый Ленинград

Выше я говорил в общем про Общественные проекты Технокома. Теперь опишу несколько, по которым сейчас постоянно повышается активность, начну с Нового Ленинграда — это высокотехнологичный город будущего, который проектируется уже сейчас. Повсеместное внедрение автоматизации, высочайшие экологические стандарты. Определено перспективное место для строительства на берегу Чудского озера: Псковская область, Гдовский район, Юшкинская волость. Центральный ориентир устье реки Куна. Для этой местности и началась постепенная разработка проекта. Основная специализация производственных и научных мощностей Нового Ленинграда будет робототехника, в том числе промышленная и медицинская. По большинству показателей, в том числе производству растительных продуктов питания город будет автономным. Важная часть проработки проекта — это его возможность реализовать здесь и сейчас, в рамках текущих экономических и политических реалий. Сейчас, когда готовится очередное переизбрание Владимира Путина, удачный момент для популяризации проекта. По этой ссылке краткая версия футуристичного агитационного видео по Новому Ленинграду выполненного в форме обращения к Путину, там есть ссылка на полную версию.

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



Общий вид первой очереди Нового Ленинграда на реальной местности приблизительно вот такой.



Платформа Робот-1

Задача — с нуля создать всемирную сеть роботизированных производств. У нас накопилось уже несколько чатов людей, которые прорабатывают различные вопросы автоматизации (в том числе программистов, которые не хотят быть программистами), предлагают свои схемы, паяют разные устройства. Теперь мы объединим всё это в один проект.

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

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




Справедливая криптовалюта

Долго нам со всех сторон говорили, что нам нужно запустить свою криптовалюту, подкрепленную нашими возможностями, в том числе производственными, которые мы будем запускать, чтобы люди вместо того чтобы покупать какие-то абсолютно виртуальные токены, поддерживали наше развитие. Постепенно набрался целый чат по этой теме в 22 человека. Долго не было понятной концепции, достаточного количества компетенций. Этот вопрос назревал, в конечном итоге когда прояснилась возможная концепция создания справедливой криптовалюты Инмо (Inmo — Internacia mono — Международные деньги, на международном языке эсперанто), нашлись программисты с нужными скилами и даже присоединился к Технокому директор коммерческого центра в Луганске и рассказал, как они там мучаются, организовывая платежи между людьми в рамках их зависшего в воздухе правового статуса, в нескольких валютах, с поставками товаров из разных противоборствующих государств и т.д., тогда пришло время запускать предпроектную работу по этому вопросу. В записи видео-конференции, которая ниже, более подробно рассказывается, кратко лишь могу сказать:

1. Техноком пока не запускает криптовалюту, мы в первую очередь сосредоточены на создании безденежного общества, в частности для этого важно развитие платформы Робот-1, а нужен ли на пути к этому такой шаг как создание своей криптовалюты, этот вопрос всесторонне рассматривается. На данный момент мы склоняемся к тому что это можно сделать, когда будут решены все организационные и технические вопросы, для того чтобы качественно поддерживать это решение, нужно больше технических специалистов — программистов, которые глубоко разбираются в этом вопросе, “криптоэкономистов” конечно уже дофига, но лучше, если их будет ещё больше;

2. Поскольку скилы были по Dash, сейчас клонируется он и на нём будут обкатываться различные модели. Также хотим посмотреть другие движки, возможно тот же Ethereum, когда по ним будут скилы;

3. Не будет дикого майнинга для зашибания бабла. Есть много предложений, в частности я предложил, что даже если будет какой-то сторонний майнинг, для начала 99% полученного майнерами будут идти в различные фонды и 1% майнеру. При разных обстоятельствах это пропорция может двигаться, но концепция останется неизменной, майнеру будет доставаться очень мало. Мотивация майнеров будет в том, что значительная часть отчислений будет идти в Фонд стабилизации и развития, задача которого будет в том чтобы сначала сформировать ликвидность с другими криптовалютами, как минимум, а потом на создание производств, чтобы криптовалюта обеспечивалась реальной товарной ликвидностью. Также будут реализовываться социальные программы, даже выплаты пенсий и пособий, чтобы и в этом вопросе дать ответ фиатным государственным деньгам. Вообще много других вещей запланировано (чат бурный, идеями искрит, только успевай записывать), чтобы если будем делать Инмо, чтобы это был не ещё один клон, а совершенно новое, интересное для разных стран и сообществ и самое главное полезное для общества.





Модульные передвижные дома

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

Сейчас запланировано:

— Объединить множество отдельных проектов модульных передвижных домов, в первую очередь домов-трансформеров и разработать новые, создать единую платформу;
— Создать в разных местах производственные мощности;
— Создать базу существующих площадок и возможностей для размещения там передвижного дома и создать новые площадки.

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

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

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




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




Вариант сильно проще





Резюмирую

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

Присоединяйтесь к команде Технокома и вы получите возможность делать невероятное под вдохновляющий крик толпы о том, что у нас ничего не получится и мы придурки. На вас будут показывать пальцем, возможно даже Word, когда вы будете писать «технокоммунизм» будет над вами издеваться и спрашивать: «Возможно вы имели ввиду технолиберализм?». Но вы справитесь, станете сильнее, когда вас будут посылать, научитесь не обижаться, а спрашивать точные спутниковые координаты этого места. У нас есть ответы на многие вопросы, в том числе и на вопрос: «С чего вы взяли, что вам дадут это сделать?». Ответы на вопросы это тема другого материала. Сейчас я только озвучу ответ на вопрос: «Когда это всё получится?». У нас ответ на этот вопрос вызвал бурную дискуссию, которая ещё продолжается, я отвечаю, что, во-первых, многое уже получается, поскольку большая цель состоит из множества небольших задач и многие из этих задач мы уже решили, а во-вторых, мы поставили себе дату 01 октября 2026 года (10-летие запуска Технокома), к которой мы должны достичь максимального результата, например, более половины людей планеты перевести на единую систему планирования, работы, взаимодействий и теперь напрягаем мозг и другие части тела, чтобы придумать, как этого достичь. Это может получится, но даже если мы достигнем несколько процентов от этой цели, это будет уже значительное достижение.

В общем, обещать можем только то, что мы не остановимся в нашем пути к цели, и что у нас интересно.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333450/


[Из песочницы] Создание Angular 2+ компонентов с возможностью переключения темы

Среда, 19 Июля 2017 г. 15:38 + в цитатник
Всем привет.

Итак, допустим мы пишем сайт, на котором нужно реализовать возможность динамического переключения настроек внешнего вида, или, проще говоря, темы. Темой (theme) будем называть набор свойств, определяющих внешний вид компонентов (да и вообще всего сайта).
Допустим, у нас есть одностраничное приложение на Angular, и пусть в нем будет ооочень много компонентов, и один из них — ButtonComponent (к компоненту подключим стили из button.component.css), на примере которого и рассмотрим весь механизм. И нужно реализовать возможность переключения между двумя темами: «dark» и «light», которые у нас будут отличаться только цветами (а в общем случае можно выносить в тему что угодно, размеры там, шрифты, картинки бэкграунда и т.п. — все, чем можно управлять из css).

Исходные стили «light» выглядели так, например:

.my-button {
  padding: 10px;
  font-size: 14px;
  color: midnightblue;
  border: 1px solid mdnightblue;
  background: white;
  cursor: pointer;
}

my-button:disabled {
  pointer-events: none;
  cursor: default;
  color: grey;
  border: 1px solid grey;
}

А для «dark» вот так:

.my-button {
  padding: 10px;
  font-size: 14px;
  color: lightblue;
  border: 1px solid lightblue;
  background: midnightblue;
  cursor: pointer;
}

.my-button:disabled {
  pointer-events: none;
  cursor: default;
  background: grey;
  color: lightgrey;
  border: 1px solid lightgrey;
}

Выделим общую часть и сохраним ее в отдельный файл. Пусть он лежит там же, в папке с нашим компонентом, и называется button-core.component.css. Так же создадим по одному файлу для каждой темы, назовем их соответственно button-light.component.css и button-dark.component.css. Уже понятно, что в них будет, но для полноты все же приведу код (кто не хочет его видеть — смело скролльте):

button-core.css:

.my-button {
  padding: 10px;
  font-size: 14px;
  cursor: pointer;
}

.my-button:disabled {
  pointer-events: none;
  cursor: default;
}

button-light.css:

.my-button {
  color: midnightblue;
  border: 1px solid mdnightblue;
  background: white;
}

my-button:disabled {
  color: grey;
  border: 1px solid grey;
}

button-dark.css:

.my-button {
  color: lightblue;
  border: 1px solid lightblue;
  background: midnightblue;
}

.my-button:disabled {
  background: grey;
  color: lightgrey;
  border: 1px solid lightgrey;
}

И в конце концов, сам button.component.css будет выглядеть вот так:

@import 'button-core.css'
@import 'button-light.css'
@import 'button-dark.css'

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

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

Для этого нам понадобится селектор :host-context(). Что он делает? Этот селектор ищет определенный css — класс, пробегая по всем родителям элемента, до самого корня. И если он этот класс встречает, то применяет описанные изменения. А лучше немного отвлечемся от нашей кнопки и рассмотрим его работу на примере. Вот пусть у нас есть два вложенных компонента: parent-component и child-component. Стили их изолированы друг от друга, спасибо Angular, и это хорошо, но мы хотим иногда изменять вид child-component в зависимости, скажем, от того, наведен ли курсор на parent-component (событие hover). Можно было бы, конечно, убрать инкапсуляцию при помощи ViewEncapsulation: none, или завести в child-component для этих целей Input — переменную, но намного проще и правильней поступить так:

:host-context(.parent-component:hover) .child-component {
  background: lightcoral;
}

Вернемся к нашей кномке. Можно в качестве маркера для хост-контекст поставить какой-нибудь стиль (:host-context(.some-style)), что нам и нужно сделать. Теперь файлы light-темы будет выглядеть так:

button-light.css:

:host-context(.light-theme) .my-button {
  color: midnightblue;
  border: 1px solid mdnightblue;
  background: white;
}

:host-context(.light-theme) my-button:disabled {
  color: grey;
  border: 1px solid grey;
}

Для dark-theme аналогично.

Что мы получили? Теперь, стоит нам только у одного из родительских элементов нашей кнопки установить класс light-theme или dark-theme, как она изменит свой вид, подключив соответствующие классы из наших файлов. Чаще всего, тема применяется сразу ко всему приложению, а это значит, что переключать тему стоит на корневом элементе, например на внешнем div из app-component (ну или того компонента, который вы прописываете в bootstrap вашего приложения). Можно завести в нем флажок theme и на внешний элемент навесить

[ngClass]="{'light-theme': theme === 'light', 'dark-theme': theme === 'dark'}".

Ну да что уже дальше рассказывать, вроде и так все ясно.

Вот такой простенький способ создания тем без использования css — препроцессоров. Способ несовершенен, т.к. предполагает много копипасты. С препроцессорами типа sass возможно более лаконичное создание тем. Однако, этот способ тоже имеет право на жизнь.

Всем спасибо за внимание, надеюсь, кому-нибудь это окажется полезным. За основу взята статья, все идеи, в общем то, оттуда.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333716/


Метки:  

Делаем приложения с поиском на Go

Среда, 19 Июля 2017 г. 15:28 + в цитатник

Однажды в рассылке Golang Weekly мне попался проект Bleve. Это полнотекстовый поиск, который написан на Go. Проект интересный, и появилось бешеное желание получить с ним опыт работы.


Bleve может хранить данные в разных embedded БД:


  • BoltDB (использует по умолчанию)
  • LevelDB
  • RocksDB
  • Goleveldb
  • forestdb
  • Gtreap

Работать с Bleve просто:


import "github.com/blevesearch/bleve"
func main() {
    // Откроем новый индекс
    mapping := bleve.NewIndexMapping()
    index, err := bleve.New("example.bleve", mapping)
    // Положим не много данных
    err = index.Index(identifier, your_data)
    // Найдем что-нибудь
    query := bleve.NewMatchQuery("text")
    search := bleve.NewSearchRequest(query)
    searchResults, err := index.Search(search)
}

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


Архитектура бота


Сервис для работы со slack;
Сервис индекс. Для хранения и поиска сообщений.


План


  • Берем https://api.slack.com/methods/channels.history для работы со слаком;
  • Берем Bleve для поиска и хранения истории;
  • Если к нам пришло сообщение не по меншну бота — кладем в индекс;
  • Если пришло сообщение с меншном — чистим и ищем по текущему каналу;

Slack


Со слаком все просто и пример по сути будет чуть сложнее, чем пример из репо


Единственное, что нам потребуется — два метода, чтобы проверить, адресовано ли боту сообщение и очистить его от имени бота


import "strings"
func isToMe(message string) bool {
    return strings.Contains(message, fmt.Sprintf("<@%s>", ss.me))
}
func cleanMessage(message string) string {
    return strings.Replace(message, fmt.Sprintf("<@%s> ", ss.me), "", -1)
}

Bleve


Учитывая то, что я люблю использовать goleveldb как встраиваемую БД для своих проектов. В этом проекте решил использовать ее же.
Хранить в Bleve будем данные посложнее в виде:


type IndexData struct {
    ID        string `json:"id"`
    Username  string `json:"username"`
    Message   string `json:"message"`
    Channel   string `json:"channel"`
    Timestamp string `json:"timestamp"`
}

Создадим индекс с Goleveldb в качестве БД:


import (
  "github.com/blevesearch/bleve"
  "github.com/blevesearch/bleve/index/store/goleveldb"
)
func createIndex() (bleve.Index, error) {
    indexName := "history.bleve"
    index, err := bleve.Open(indexName)
    if err == bleve.ErrorIndexPathDoesNotExist {
        mapping := buildMapping()
        kvStore := goleveldb.Name
        kvConfig := map[string]interface{}{
            "create_if_missing": true,
        }
        index, err = bleve.NewUsing(indexName, mapping, "upside_down", kvStore, kvConfig)
    }
    if err != nil {
        return err
    }
}

и метод buildMapping, который создаст нам mapping для хранения:


func (ss *SearchService) buildMapping() *bleve.IndexMapping {
    ruFieldMapping := bleve.NewTextFieldMapping()
    ruFieldMapping.Analyzer = ru.AnalyzerName
    eventMapping := bleve.NewDocumentMapping()
    eventMapping.AddFieldMappingsAt("message", ruFieldMapping)
    mapping := bleve.NewIndexMapping()
    mapping.DefaultMapping = eventMapping
    mapping.DefaultAnalyzer = ru.AnalyzerName
    return mapping
}

С поиском все чуть сложнее:


func (ss *SearchService) Search(query, channel string) (*bleve.SearchResult, error) {
    stringQuery := fmt.Sprintf("/.*%s.*/", query)
  // NewTermQuery создает Query для нахождения значений в индексе, которые строго совпадают с запросом
    ch := bleve.NewTermQuery(channel)
  // Создаем Query для совпадений фраз в индексе. Анализатор выбирается по полю. Ввод анализируется этим анализатором. Токенезированные выражения от анализа используются для посторения поисковой фразы. Результирующие документы должны совпадать с этой фразой.
    mq := bleve.NewMatchPhraseQuery(query)
  // Создаем Query для поиска значений в индексе по регулярному выражению
    rq := bleve.NewRegexpQuery(query)
  // Создаем Query для поиска документов, результаты которого удовлетворят поисковой строке.
    qsq := bleve.NewQueryStringQuery(stringQuery)
  // Создаем составную Query Результат должен удовлетворять хотя бы одной Query.
    q := bleve.NewDisjunctionQuery([]bleve.Query{ch, mq, rq, qsq})
    search := bleve.NewSearchRequest(q)
    search.Fields = []string{"username", "message", "channel", "timestamp"}
    return ss.index.Search(search)
}

Соединив все вместе, мы получим бота, который сохраняет историю и может искать по ней без тяжеловесной жавы на примерах ElasticSearch, Solr.


Полный код проекта доступен на Github

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333714/


Метки:  

А ну-ка, девушки! Аде Лавлейс посвящается

Среда, 19 Июля 2017 г. 15:11 + в цитатник
В этот день 174 года назад Ада Лавлейс сообщила Чарльзу Беббиджу, автору проекта аналитической машины, о том, что она самостоятельно «составила список операций для вычисления каждого коэффициента для каждой переменной», то есть написала программу для вычисления чисел Бернулли.


Источник

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

В знак уважения к Аде Лавлейс в этом посте мы хотим рассказать о тех в ЛАНИТ, кто выбрал для себя ее путь, несмотря на то, что в России у ИТ до сих пор мужское лицо.

Бонусом – для всех, кто упрекнет наш блог в отсутствии бородатых инженеров, – представим старшего разработчика Антона. Пусть сегодня он окажется в «малиннике» и поделится взглядом на проектные команды, в которых трудятся женщины.

Очаровательные разработчики ЛАНИТ




Марина:

«В ЛАНИТ я занимаюсь проектированием системы электронного документооборота. Чаще всего пишу на T-SQL.

Программист – творческая профессия. Постоянно узнаешь что-то новое, общаешься с умными и интересными людьми. Любознательность привела меня в профессию.

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

За время работы в ЛАНИТ я успела поучаствовать в разных проектах, связанных с организацией хранения информации и электронным документооборотом. Приятно осознавать свой вклад и видеть результаты работы».



Алина:

«Сейчас я работаю с платформой Pega, в которой используется язык программирования Java. Также имею опыт на С++.

Программистом решила стать потому, что понравилось название факультета — факультет кибернетики. Шутка :) Если серьезно, то информатика и математика нравились еще со школы, да и давались легко. Поэтому, выбирая, кем быть, совершенно не колебалась. Дополнительный плюс — востребованность профессии.

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



Софья:

«Мои языки — C++ и Java. В ЛАНИТ специализируюсь на решениях для крупнейших банков России: разрабатываю различный функционал для кредитных процессов, занимаюсь интеграцией решений со сторонними системами финансовых учреждений.

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





Альфия:

«В ЛАНИТ я участвую в проектах, связанных с базами данных. Прекрасно «говорю» на языке PL/SQL, создавая объекты баз данных, формы запросов и функции. Я сама долго выбирала профессию. Привычные врачи, адвокаты, бухгалтеры и т.д. казались скучными. В выборе помогла книга «SQL для чайников», которая случайно попалась на глаза. После нее я поняла, чем хотела бы заниматься. Мне кажется, что женщины гармонично себя чувствуют в ИТ-сфере: они меньше раздражаются на рутинную работу и более внимательны».    



Мадина:

«Специализируюсь на Java и C++. В ЛАНИТ работаю над решениями для автоматизации процессов в юридических службах банков и судебных учреждениях, а в нерабочее время мы с коллегой из любопытства можем засесть за создание продукционных систем с троичной логикой или сборщик мусора для языка C++.

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



Галина:

«В ЛАНИТ я работаю с Pega – платформой для разработки корпоративных приложений. До этого писала на C++, который изучала самостоятельно еще со школы, и C#, с которым познакомилась в студенческие годы. На нем позже написала программу для тестирования знаний студентов в области объектно-ориентированного программирования. Проекты, в которых я участвую, связаны с упрощением и автоматизацией бизнес-процессов банковских систем.  

Меня часто спрашивают, с какими стереотипами я сталкиваюсь в работе. Не припомню, чтобы сталкивалась с ними, исключение разве что сам вопрос».



Наталья:

«С базами данных столкнулась сразу после окончания института. Несколько лет я работала в телекоммуникационной компании. Наш отдел поддерживал биллинговые системы. Работа была динамичной, поскольку клиентам требовалось решение для быстрой и качественной обработки потребленных ими услуг. Мы постоянно дорабатывали системы, которые использовали операторы.
В дальнейших проектах базы данных меня не покидали. Последнее время работаю с Oracle и PostgreSQL, а начинала с MS SQL и VBA. Занимаюсь поддержкой и развитием систем анализа и принятия решений.

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

Обещанный бонус




Антон, старший разработчик:

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

В то же время квалифицированные женщины-программисты пока редкость. Мне кажется, в командах разработчиков их не больше 5 %. На некоторых должностях я женщин вообще не встречал. Например, сервисных инженеров.

Особенно приятно, что среди разработчиков в ЛАНИТ есть девушки».


Кстати, сейчас у нас открыта вакансия разработчика JAVA. Мы ждем ваши резюме!

Какое приблизительно (или если знаете – точно) распределение М/Ж  в ИТ-подразделениях вашей компании?

Проголосовало 32 человека. Воздержалось 10 человек.

Женщина-программист… Быть или не быть?

Проголосовало 34 человека. Воздержалось 12 человек.

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

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333706/


Метки:  

[Перевод] Что нового в Swift 4.0

Среда, 19 Июля 2017 г. 15:07 + в цитатник
Практические примеры, которые помогут вам узнать о том, что нового нас ждет в Swift 4.

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

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


ПРЕДУПРЕЖДЕНИЕ: Swift 4 на момент написания и подготовки перевода статьи находится в активной разработке и автором оригинала были выбраны только некоторые из наиболее интересных и полезных новых функций для обсуждения. Пожалуйста, имейте в виду, что больше возможностей будет доступно ближе к релизу.

Swift'овое кодирование и декодирование


Мы знаем, что типы значений велики, но еще мы знаем, что они ужасно взаимодействуют с Objective-C API, такими как NSCoding — вам нужно писать некую прослойку или использовать классы, при этом оба варианта неприятны. Хуже того, даже если вы переключаетесь на классы вам нужно писать методы кодирования и декодирования вручную, написание которых является болезненным и подверженным ошибкам.

Swift 4 вводит новый протокол Codable, который позволяет вам сериализовать и десериализовать собственные типы данных без написания дополнительного кода и не беспокоиться о потере ваших типов данных. Более того, вы можете выбрать как вы хотите сериализовать данные: использовать классический формат property list или JSON.

Да, вы все прочитали правильно: Swift 4 позволяет вам сериализовать ваши собственные типы данных в JSON без написания специального кода.

Давайте посмотрим насколько это прекрасно. Во-первых вот пользовательский тип данных и некоторые его экземпляры:
struct Language: Codable {
    var name: String
    var version: Int
}

let swift = Language(name: "Swift", version: 4)
let php = Language(name: "PHP", version: 7)
let perl = Language(name: "Perl", version: 6)


Как вы можете видеть, к структуре Language я подключил протокол Codable. С этим крошечным дополнением мы можем конвертировать это в JSON, представленный как Data следующим способом:
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(swift) {
    // сохраняем `encoded` где-нибудь
}


Swift автоматически кодирует все свойства внутри вашего типа данных, вам не нужно делать дополнительных действий.

Теперь вы, если также как и я долгое время использовали NSCoding, вероятно несколько сомневаетесь: действительно ли это то, что нам нужно и как мы можем быть уверены, что это работает? Давайте добавим немного кода чтобы попробовать преобразовать объект Data так чтобы мы могли отобразить это в консоли, тогда нам нужно декодировать это назад в новый экземпляр структуры Language:
if let encoded = try? encoder.encode(swift) {
    if let json = String(data: encoded, encoding: .utf8) {
        print(json)
    }

    let decoder = JSONDecoder()
    if let decoded = try? decoder.decode(Language.self, from: encoded) {
        print(decoded.name)
    }
}

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

Многострочные строковые литералы


Написание многострочных строк в Swift всегда означало добавление \n внутри ваших строк для добавления переноса строки, там где вы этого хотите. В коде это выглядит не очень хорошо, но по крайней мере корректно отображает информацию для пользователей. К счастью, Swift 4 добавляет новый синтаксис для многострочных строковых литералов, который позваляет добавлять переносы строк и использовать кавычки без экранирования, в то же время доступна такая полезная функциональность, как строковая интерполяция.

Чтобы начать строковый литерал, вам нужно: написать три двойных кавычки """ и нажать [Enter]. Тогда вы можете продолжить писать строку сколько хотите включая переменные и разрывы строк, до завершения вашей строки нажатием [Enter] и написанием трех двойных кавычек """.

Я хотел бы уточнить о нажатии переноса строки потому что строковый литералы имеют два важных правила:
1) когда вы открываете строку используя """ содержимое вашей строки должно начинаться на новой строке;
2) когда вы закрываете строку используя """ это обозначение также должно быть в начале новой строки.

Здесь это в действии:
let longString = """
When you write a string that spans multiple
lines make sure you start its content on a
line all of its own, and end it with three
quotes also on a line of their own.
Multi-line strings also let you write "quote marks"
freely inside your strings, which is great!
"""


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

Улучшенные пути (keypaths) в KVC (Key-Value Coding)


Одной из самых любимых особенностей Objective-C является способность ссылаться на свойство динамически, а не напрямую — то есть, иметь возможность сказать «вот объект X, здесь его свойство, которое я хотел бы прочитать», не читая его вообще. Эти ссылки, названные keypaths, отличаются от прямых доступов к свойствам, потому что они фактически не читают или не записывают значение, они просто скрывают их чтобы использовать позже.

Если вы ранее не использовали keypaths позвольте мне показать вам аналогию того как они работают с использованием обычных методов Swift. Мы собираемся определить структуры Starship и Crew, затем создать по одному экземпляру каждой:
// структура для примера
struct Crew {
    var name: String
    var rank: String
}

// другая структура, в этот раз с методом
struct Starship {
    var name: String
    var maxWarp: Double
    var captain: Crew

    func goToMaximumWarp() {
        print("\(name) is now travelling at warp \(maxWarp)")
    }
}

// создаем экземпляры наших структур
let janeway = Crew(name: "Kathryn Janeway", rank: "Captain")
let voyager = Starship(name: "Voyager", maxWarp: 9.975, captain: janeway)

// захватываем ссылку на `goToMaximumWarp()` метод
let enterWarp = voyager.goToMaximumWarp

// вызываем ссылку
enterWarp()


Поскольку функции в Swift являются типами первого класса последние две строки могут создать ссылку на метод goToMaximumWarp() и вызвать позже когда нам это понадобится. Проблема в том, что мы не можем сделать то же самое для свойств — мы неможем сказать «создай ссылку на свойство имени капитана, которое я смогу проверить когда произойдет неизбежный бунт», потому что Swift просто прочитает свойство напрямую и вы просто получите исходное значение.

Это исправлено с помощью keypaths: они являются ссылками на свойства как наш код enterWarp(). Если вы вызываете ссылку сейчас вы получаете текущее значение, но если вы вызываете ссылку позже, вы получите самое последнее значение. Вы можете добраться через любое количество свойств и Swift использует свой тип вывода, чтобы гарантировать, что вы вернете правильный тип.

Сообщество развивающее Swift потратило много времени на обсуждение правильного синтаксиса для keypaths потому что это должно быть что-то визуально отличное от другого Swift кода и в конце концов этот синтаксис использует обратные косые черты: \Starship.name, \Starship.maxWarp, и \Starship.captain.name. Вы можете присвоить эти значения переменным и использовать тогда когда вы этого хотите любому экземпляру структуры Starship. Например:
let nameKeyPath = \Starship.name
let maxWarpKeyPath = \Starship.maxWarp
let captainName = \Starship.captain.name

let starshipName = voyager[keyPath: nameKeyPath]
let starshipMaxWarp = voyager[keyPath: maxWarpKeyPath]
let starshipCaptain = voyager[keyPath: captainName]


Это сделает starshipName как String, а starshipMaxWarpDouble потому что Swift способен правильно выводить типы данных. Третий пример берет свойство свойства и Swift также корректно определяет его.

Для дополнительной информации смотрите предложения по развитию этой новой функциональности.

Улучшенная функциональность словарей


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

Давайте начнем с простого примера: фильтрация словарей в Swift 3 не возвращает новый словарь. Вместо этого в результате фильтрации мы получаем массив кортежей (tuples) с метками «ключ-значение». Например:
let cities = ["Shanghai": 24_256_800, "Karachi": 23_500_000, "Beijing": 21_516_000, "Seoul": 9_995_000];
let massiveCities = cities.filter { $0.value > 10_000_000 }


После того как это код выполнится вы не можете использовать для получения нужных данных такую запись:
massiveCities["Shanghai"]

потому что это уже не словарь. Вместо этого вам нужно использовать такой код:
massiveCities[0].value

и это не очень-то здорово.

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

Точно также метод map() со словарями не работал так, как надеялись многие люди: вы получали переданный кортеж вида «ключ-значение» кроме того это могло быть одно значение добавленное в массив. Например:
let populations = cities.map { $0.value * 2 }


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

Например, этот код будет преобразовывать числовое значение и преобразовывать в строку данные о населении городов и складывать назад в новый словарь с такими же ключами: Shanghai, Karachi, и Seoul:
 1_000_000) million people" }


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

Мое любимое дополнение для словаря это инициализатор группировки, который преобразует последовательность в словарь последовательностей, которые сгруппированы по вашему желанию. Продолжая наш пример с городами мы могли бы использовать cities.keys, чтобы вернуть массив имен городов, а затем сгруппировать их по первой букве:
let groupedCities = Dictionary(grouping: cities.keys) { $0.characters.first! }
print(groupedCities)


Это выведет в консоль следующее:
["B": ["Beijing"], "S": ["Shanghai", "Seoul"], "K": ["Karachi"]]


Или мы могли бы сделать группировку городов на основе длин их имен:
let groupedCities = Dictionary(grouping: cities.keys) { $0.count }
print(groupedCities)


Это выведет в консоль следующее:
[5: ["Seoul"], 7: ["Karachi", "Beijing"], 8: ["Shanghai"]]


Наконец, сейчас возможно получение доступа к ключу словаря и предоставить значение по умолчанию, если указанный ключ отсутствует:
let person = ["name": "Taylor", "city": "Nashville"]
let name = person["name", default: "Anonymous"]


Сейчас любой опытный разработчик возможно будет утверждать что аналогичного результата можно добиться проще и я с этим согласен. Мы могли бы написать это таким образом в текущей версии Swift:
let name = person["name"] ?? "Anonymous"


Однако это не работает, если вы изменяете значение словаря, а не просто получаете его. Вы не можете сразу изменить значение словаря, потому что доступ по этому ключу возвращает опциональный тип — ключ может не существовать. С помощью значений словаря по умолчанию в Swift 4 вы можете написать более сжатый код, например:
var favoriteTVShows = ["Red Dwarf", "Blackadder", "Fawlty Towers", "Red Dwarf"]
var favoriteCounts = [String: Int]()

for show in favoriteTVShows {
    favoriteCounts[show, default: 0] += 1
}


Этот цикл перебирает каждую строку в favoriteTVShows и использует словарь favoriteCounts чтобы отслеживать как часто появляется каждый элемент. Мы можем менять словарь в одну строку кода, потому что мы знаем что он всегда будет иметь значение: или 0 как значение по умолчанию или некоторое большее число основанное на предыдущем подсчете.

Для дополнительной информации смотрите предложения по развитию этой новой функциональности.

Строки снова коллекции!


Это маленькое изменение, но гарантировано сделает многих людей счастливыми: строки снова являются коллекциями. Это означает, что теперь вы можете их обратить, перебрать по символам, использовать map() и flatMap() и т.д. Например:
let quote = "It is a truth universally acknowledged that new Swift versions bring new features."
let reversed = quote.reversed()

for letter in quote {
    print(letter)
}


Это изменение было внесено как часть набора поправок, называнный «Манифест строк».

Односторонние диапазоны


Последнее, но не менее важное — в Swift 4 появляется Python-подобная односторонняя нарезка коллекций, где недостающая сторона диапазона автоматически определяется как начало или конец коллекции. Это не влияет на существующий код, потому что это новый подход к существующему оператору, таким образом вы можете не беспокоиться о потенциальной поломке в коде. Примеры:
let characters = ["Dr Horrible", "Captain Hammer", "Penny", "Bad Horse", "Moist"]
let bigParts = characters[..<3]
let smallParts = characters[3...]
print(bigParts)
print(smallParts)


Этот код выводит в консоль сначала это:
["Dr Horrible", "Captain Hammer", "Penny"]

а затем это:
["Bad Horse", "Moist"]


Для дополнительной информации смотрите предложения по развитию этой новой функциональности.

Еще больше впереди...


На момент перевода статьи разработчикам уже доступен Xcode 9 Beta 3 (с 10.07.2017) вместе с iOS 11, tvOS 11, watchOS 4, и новой версией macOS. То что мы видели уже многообещающе, потому что понятно, что команда усердно трудится чтобы сделать Swift 4 как можно лучше. Речь идет прежде всего о добавлении новых функций, а не об изменении существующих и это должно сделать легче переход на новую стабильную версию языка.

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

Одной из функций, которая была отложена, является совместимость с ABI, которая позволила бы разработчикам распространять скомпилированные библиотеки — одну из немногих ключевых недостающих функций, которые остаются в Swift сегодня. Надеюсь, мы доберемся до этого в Swift 5…
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333712/


Метки:  

[Перевод] K-sort: новый алгоритм, превосходящий пирамидальную при n <= 7 000 000

Среда, 19 Июля 2017 г. 15:04 + в цитатник

Метки:  

[Из песочницы] 3D Блокчейн. Доказательство на лицо (PoF)

Среда, 19 Июля 2017 г. 15:01 + в цитатник
Моя склонность — всё подвергать сомнению и блокчейн[1] не оказался исключением. Давайте взглянем на Цикл зрелости технологии (Gartner Hype Cycles). Где по Вашему находится Блокчейн? Естественно каждый для себя определит своё нахождение на том или ином цикле, которое свойственно проекту на каком-то из стадий разработки. Кто-то во всю развивает бизнес приложение и видит будущие горизонты, а кто-то только начинает знакомство. Но если взглянуть шире? Блокчейн неоспоримо засел в мысли и показал большой потенциал по трансформации многих сегментов взаимоотношения людей. Но у меня остаётся доля сомнения в отношении некоторых текущих принципов его работы. В итоге, я вижу эту стадию где-то на уровне пика завышенных ожиданий (Peak of inflated expectations) — общественный ажиотаж с чрезмерным энтузиазмом и нереалистичными ожиданиями. Что означает, впереди ещё ждать такие циклы как: Избавление от иллюзий, Преодоление недостатков, Плато продуктивности.

Поэтому, если Вам интересна критика и предположение иного подхода в определении консенсуса, то добро пожаловать под кат.

Обратимся к Биткоину[2]. Это одноранговая денежная система, в которой электронные транзакции совершаются участниками напрямую, минуя финансовые институты (доверенного лица/посредника). Где защита от двойной траты достигается путём консенсуса в пиринговой сети, основанном на доказательстве проделанной работы (Proof of Work[3]). В качестве стимула для поддержания системы эмиссируется криптовалюта и начисляется совместно с комиссией майнерам, которые данную работу выполняют. Возникает закономерный вопрос, а это не посредники? Да, с них убрали принцип доверия, в следствии чего уходит и ответственность (не кого будет винить), но ведь они и есть третья сторона, которым участники транзакций оплачивают за предоставленную услугу (обеспечение надёжности). Более того, огромная часть вознаграждения сейчас происходит за счёт эмиссии, а это означает, что те кто сейчас приобретает криптовалюту, сами того не подозревая (или подозреваете?), оплачивают за уже совершенные транзакции предшественников. Например, данный вопрос поднимался в 9 пункте от Gartner. В дальнейшем, когда эмиссия прекратится, то получаемая из этого ранее сумма распределится в качестве комиссии, что существенно взвинтит стоимость транзакции. С технической стороны этот вопрос может решиться увеличением размера блока или иными решениями типа SegWit[4] (только, на как долго решится?). Но экономическая сторона всё равно страдает. Надёжность обеспечивается тогда, когда кол-во майнеров огромно, ведь подразумевалось «один процессор — один голос». А в итоге они попадут в конкурентную среду, где единственным средством в перетягивании одеяла будет размер комиссии, в общем кто-то вырастет и останется на плаву, а кто-то уйдёт, либо будет поглощён. И от сюда возникает следствие — себестоимость транзакции. Чем система более децентрализована, тем стоимость транзакций будет выше. Чем она более централизована — тем ниже. Это обычные экономические реалии, подобная мысль уже была, только в отношении энергоэффективности, например здесь.

Следующий момент заключается в том, что хоть это и денежная система, но она замкнута сама в себе, иными словами блокчейн взаимодействует с миром через среду основанной на доверии. Также любой блокчейн зависит от собственной криптовалюты, обеспечивающая стимулирование надежности данной сети и осуществление транзакций. Соответственно, с одной стороны — это единая расчётная единица, что хорошо, с другой стороны — это прямая зависимость для функционирования приложений (уже не связанных с деньгами, но задействовавшие данный блокчейн) от судьбы её криптовалюты и стоимости транзакций. Дополнительно эффект замкнутости порождает вопрос, по какому правилу осуществлять эмиссию? Ведь тут уже, кто на что горазд: кто-то ограничивает, кто-то нет, а кто-то и вовсе применил принцип сжигания, как например в Proof of Burn (доказательство сжигания).

Обратимся к Ethereum[5]. Примечательно, что необходимость в собственной криптовалюте в Ethereum более оправдана, т.к. необходима для осуществления смарт-контрактов и работы приложений. Эмиссия здесь не ограничена, но со временем стремится к нулю. В итоге экономическая сторона в отношении майнеров здесь такая же. Но на сколько данный способ эмиссии обладает гибкостью в отношении экономических процессов? Ведь уровень денежной массы является одним из основных элементов функционирования денежной системы и экономики в целом. С другой стороны, если предположить, что весь мир перешёл на блокчейн, тогда например бесконечная эмиссия, которая будет привязана допустим к интенсивности транзакций, то своим ускорением покажет рост экономики, а при спаде кол-ва транзакций наоборот, спадает соответственно интенсивность эмиссии, то такая модель кажется жизнеспособной.

Вернёмся к блокчейн — это цепочка блоков с записями о транзакциях, формирующихся непрерывно, защищенность обеспечивается криптографией. Каждый блок содержит информацию о предыдущем блоке. Блокчейн представляет собой распределённый реестр данных, полная копия которой или её части одновременно хранится на множестве компьютеров. Формирование новых блоков осуществляется по определённому алгоритму консенсуса, как описывал выше, например, основанной на доказательстве проделанной работы (Proof of Work) или иных. При этом, частым явлением является ветвление цепи, это когда одновременно формируется несколько новых блоков, каждый из которых считают предыдущим один и тот же блок. Ветвление прекратится, как только будет найден новый блок, который продолжит любую из ветвей. Все узлы переключатся на ту цепь, которая имеет самую длинную версию и продолжат работать над её удлинением. Т.е. блокчейн представляется линейным. Теперь представьте, на сколько такой блокчейн перегружен лишней работой? Полная актуальная версия цепи хранится на специальных узлах сети, называемые полными нодами, остальные узлы (лёгкие кошельки, используемые пользователями) обращаются к полным в случае необходимости проверить что-либо. Полную версию блокчейна может скачать любой желающий. Но с учётом того, что блокчейн развивается уже не только для транзакций, но и в качестве реестра различных событий, и также даёт возможность работы различных приложений. А в графическом виде блокчейн представляется линейным, то на сколько увеличится размер такой цепи в будущем? Ведь огромный размер приведёт к риску централизации. Лишь единицы смогут держать полную ноду исчисляемую Тб, а значит единицы смогут вступить в сговор и навязывать правила.

Рассмотрим Proof of Stake[3]. Доказательство доли — это второй наиболее известный алгоритм консенсуса, который является альтернативой Proof of Work. Аргументом в пользу PoS является отсутствие в необходимости огромных вычислений и предоставляет малый входной порог. В изначальном PoS идея заключается в том, что вместо вычислительной мощности как в PoW (требующая больших затрат), вероятность создать новый блок пропорциональна доле владения (криптовалютой) пользователя в системе. Соответственно, чтобы нарушить данную систему, потребуются значительные средства и вместе с этим нарушив устойчивость блокчейна сам же и пострадаешь. Но такой подход приводит к тем же результатам централизации, что и в PoW, только быстрее. Поскольку даёт ту же мотивацию накопления средств в одних руках. В результате появились вариации PoS, это: Proof of Activity (доказательство активности — гибридная схема PoW и PoS); Delegated Proof of Stake (делегированное подтверждение доли) и Leased Proof of Stake (арендованное подтверждение доли). DPoS основан на том, что владелец доли, посредством голосования пропорционально своей доли, делегирует обязанность определённому числу делегатов, которые по очереди в случайном порядке генерируют блоки. LPoS основан на том, что пользователи могут сдавать в аренду (вместо голоса как на DPoS) свой баланс узлам (нодам), тем самым получая часть прибыли. Кол-во узлов ограничено для улучшения характеристик скорости работы блокчейна. В итоге, по сути в целом PoS напоминает банковскую систему, только трансформировавшуюся. С тем же успехом, можно было создать блокчейн среди банков, где каждый, кто имеет лицензию, должен быть полной нодой и своим балансом участвовать в генерации блоков (зависящий от вкладчиков) и делать это между собой для оптимизации бизнес процессов, а в дальнейшем и предоставление различных IT решений для работы приложений.

3D Блокчейн. Доказательство на лицо (PoF)


В общем можно и дальше рассуждать о правильности или неправильности того или иного принципа. Однозначно проблемы существуют и бесспорно пути решения будут. Но отталкиваясь от старых парадигм, произойдёт лишь наложение друг на друга различных решений и в результате увидим только усложнение системы, а извечное противостояние останется. Иначе говоря, до тех пор пока блокчейн будет основан на стимулировании средствами для обеспечения надёжности, будет одновременное стимулирование системы к централизации.
Взгляните на рисунок, это задача. Необходимо соединить все 9 точек четырьмя линиями, идущими непрерывно друг за другом. Данная задача демонстрирует принципы выхода за рамки мышления. Быть может и к блокчейну это применимо и не отталкиваться от старых парадигм в виду его выхода за рамки денежных транзакций? Поскольку в блокчейн не нужно доказывать всем о его необходимости, потенциале и он уже давно дистанцировался от обычных транзакций, и предоставил такие возможности как распределённый реестр данных, осуществление смарт-контрактов, децентрализованных приложений, а также проведение ICO. Тогда возможен ли блокчейн без майнеров обеспечивающих надежность? И без основной криптовалюты, которая необходима прежде всего для стимулирования обеспечения этой надежности? Сама то криптовалюта конечно необходима для осуществления денежных транзакций, просто сам блокчейн будет от неё независим, и иные действия не связанные с денежными транзакциями будут протекать обособленно. В таком случае, кто будет обеспечивать надёжность системы? У кого уже есть стимул делать это безвозмездно, но с той же степенью защиты?

Обратимся опять к статье от Сатоши Накомото[2]. Там говорилось, что если один голос это IP-адрес, то такую схему можно скомпрометировать. В итоге добавили «сложность», чтобы схема была иной «один процессор — один голос». Т.е. привязка идёт к физическому миру. Тогда, быть может в физическом мире найти замену процессора. Или быть может, чтоб процессор действовал от чьего-то лица?

Назовём криптовалюту как сервис и обратимся к участникам блокчейна (помимо майнеров):

  • Есть криптовалюта как сервис, для осуществления денежных транзакций и смарт-контрактов, в условиях которых также прописано осуществление данных транзакций.

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

  • Есть клиенты данных сервисов. Они могут быть как физические лица, так и юридические лица (группа лиц). Которые осуществляют транзакции в данных сервисах.

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

Взгляните на схему формирования блока.



Условно представим, что Х, А, Б, В, С, Г, Д, Е это пользователи. Под словом транзакция также подразумевается сделка. Разберём пример с пользователем Б.

  1. Пользователь Б отправляет транзакцию пользователю С. Данная транзакция называется Тб и находится в статусе ожидания.

  2. Её валидность и запись в открытый блок осуществляет чужая подтверждённая транзакция, в данном случае Та.

  3. В открытый блок, уже принадлежащей транзакции Тб, также записывается информация его валидатора — транзакция Та.

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

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

    • Присутствие другого открытого блока, где иная транзакция пользователя Б в данном сервисе. В этом случае проверяется достаточность средств (если это денежная транзакция) при исполнении сделок, также учитывается время и принимается решение об аннулировании того или иного открытого блока, либо наоборот оставить.

    • Присутствие иных отправленных транзакций, на случай если одна из них ушла в офлайн.

  5. Если всё нормально, транзакция Тх, подтвердив валидность транзакции Тб, записывает свою информацию в блок транзакции Тб.

  6. Т.к. в отношении Тб всё нормально, Транзакция Тб проверяет валидность своего первого валидатора — транзакции Та. Если всё нормально, информация транзакции Тб записывается в блок принадлежащей транзакции Та.

  7. Транзакция Та закрывает блок валидатора … (подробнее по аналогии в 10 пункте на примере закрытия блока Тх).

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

  9. Повторяется процесс в отношении транзакции Тв (с 2 по 6 пункт).

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

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

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

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

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

Теперь представим данный блокчейн графически. У нас есть различные сервисы (ось X) на блокчейне (разные криптовалюты — государственные или нет, платформы смарт-контрактов, приложения и т.п.) предоставляющие тем самым возможности (услуги и продукты) клиентам (ось Y), которыми являются как физ. лица, так и юр. лица. А также транзакции клиентов (ось Z). В итоге получается, что у одного лица в разных сервисах свой блокчейн, но при этом связан воедино с остальными. Иными словами Если пользователь Б зарегистрировался в сервисе 1 (например, криптовалюта ЦБ), то все транзакции порождённые пользователем Б в данном сервисе будут записываться последовательно цепочкой в его (пользователя) блокчейне, относящийся к данному сервису. При этом сервис 1 также будет вести последовательность цепочки, но чередуя транзакции от всех пользователей сервиса (т.е. своеобразно будет нодой, последовательно записывающая блоки порождённые пользователями данного сервиса), но при этом не является основным хранителем цепочки, а лишь представляется резервом. Это обусловлено тем, что исходя из схемы формирования блока, каждая транзакция порождённая пользователем Б также записывается в блоки иных шестерых участников (в их блокчейн), это принимающий транзакцию (пользователь С) и пять валидаторов (Та, Тх, Тв, Тг, Тд каждая из которых могла быть осуществлена в ином сервисе). Удивительно, это совпало с теорией шести рукопожатий. Если верить которой, то совершая все участники транзакции будут условно соединены друг с другом как одно целое. Это значит, если кото-то захочет изменить информацию в своём блоке, то данному лицу потребуется подговорить изменить её ещё у шестерых участников, те в свою очередь у своих и далее до конца.

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

  • Два генезис блока открывают блок чужой транзакции, тем самым становятся его валидаторами.

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

Далее всё по схеме формирования блоков.

Заключение


Бесспорно, многих может оттолкнуть подобный подход доказательства на лицо (Proof of Face), т.к. для его осуществления требуется верификация пользователя, включая биометрию, но которая послужит своеобразным закрытым ключом. (заметьте, биометрия в PoF не должна храниться в электронном виде где либо, она лишь является закрытым ключом для генерации публичного). Но с другой стороны, все страны (в том числе различные организации) сейчас ведут разработки по верификации посредством биометрии, и рано или поздно произойдёт переход с бумажного паспорта на цифровой. А его информация будет либо централизована (что уже мало вероятно), либо заключена в блокчейн, который вероятнее всего будет работать на Proof of Stake с криптовалютой от ЦБ, где полными нодами будут банки. Соответственно через них пойдут уже не только денежные транзакции, но и любые сделки. В общем и в целом всё это сугубо последствиями описанными выше, это зависимость от криптовалюты блокчейна, зависимость от стоимости транзакций, а также вероятность осуществления контроля (операторами — полными нодами) ввиду склонности к централизации в угоду экономических/политических интересов.

Тогда подход основанный на Proof of Face, который позволит пользователям быть собственниками своего блокчейна в каждом сервисе отдельно, выглядит более приемлемо. Окончательная невозможность централизации позволит любому оставаться в сети, т.к. сценарий, когда миллионы людей захотят одновременно не принимать транзакции от конкретного человека исключены. Бесплатность простых транзакций так, как это происходит в реальном мире при передаче наличных денег из рук в руки. Отсутствие платы для работы приложений, что действительно сократит издержки осуществляющим сервис. Независимость блокчейна от судьбы той или иной криптовалюты. Малый размер цепочки блоков, т.к. каждая цепочка формируется у каждого пользователя личная и отдельно по каждому сервису, что положительно скажется на скорости валидации. Соответственно и скорость транзакций теоритически будет выше, чем в ныне существующих блокчейнах. Сохранность данных цепочек обеспечивается в устройствах самих пользователей, где каждый блок пользователя синхронизирован с ещё 6 участниками. А также в нодах, записывающих блоки своих пользователей. Примените теперь принципы критографии к схеме формирования блоков и последовательности формирования цепочек, что в совокупе обеспечит помимо защищенности и сохранности информации ещё и конфиденциальность. Ведь не обязательно в каждом блоке сохранять всю информацию по каждому валидатору, а можно лишь её часть, применив например дерево Меркла.

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

  1. На государственном уровне, при переходе из бумажного паспорта на цифровой.
  2. Как Open Source проект отдельного блокчейна.
  3. В качестве Сайдчейна.

В первом случае, это быстро и легально (когда процесс трансформации происходит сообща во всех областях и уровнях). Не будет встречать сопротивления. Но в начале пути будет развиваться внутри одного государства. Присутствует риск чрезмерного контроля.
Во втором случае, регистрация пользователей будет происходить со стороны сервиса (коммерческих организаций). Таким образом будет реализовываться в качестве альтернативы существующим базам данных.
Третий способ подразумевает, что ныне существующие блокчейны — это сервисы в рамках данного подхода, предоставляющие различные IT решения для конкретных задач. А консенсус PoF позволит объединить их и породить полную взаимосвязь.

Есть истина истенней чем другая истина, но есть истина лучше, потому истинно то, что полезно.

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

Список литературы
[1] Блокчейн
[2] Перевод статьи Сатоши Накомото. Биткойн: система цифровой пиринговой наличности
[3] Что такое Proof-of-Work и Proof-of-Stake
[4] Что такое Segregated Witness
[5] White Paper Ethereum перевод
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333708/


От репозитория до CI/CD-инфраструктуры в продакшене за неделю

Среда, 19 Июля 2017 г. 14:47 + в цитатник
Обычно в термин «поддержка» вкладывают только один смысл — это реагирование на беды с хостингом, замена битых дисков, настройка веб-серверов и СУБД, общее повседневное администрирование. Но, на самом деле, это только первый уровень контроля стабильности работы любого интернет-проекта.

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

image

Вот такое:

image

И именно об этом пост — о безопасности и стабильности разработки и о том, какими системами и принципами их можно обеспечить. Или, если воспользоваться модными словами — о DevOps и CI/CD.



Зачем?


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

image

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

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

Именно для этого и были придуманы методология DevOps и CI/CD. CI/CD подразумевает наличие отдельной площадки под разработку, отдельного стейджинга и отдельного прода.

image

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

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

Обычно о таких вещах говорят больше в теоретическом ключе. Вот, мол, есть такая штука, как Docker. Тут у нас можно недорого делать контейнеры. Создавать, обновлять, откатывать, всё можно делать безболезненно, не задевая жизненно важные части родительской системы, при этом не загружая ненужными прослойками и сервисами. А потом в другой статье — а вот есть Kubernetes. Он может вставлять и вытаскивать docker-кирпичи в вашем проекте самостоятельно, без ручного вмешательства, просто опишите ему конфигу, как он будет работать. Вот вам три строчки из официальной документации. И в таком вот стиле почти все материалы. Я же хотел бы рассказать о том, как всё это происходит в реальной жизни, отталкиваясь от реальной задачи обеспечения механик безопасной и стабильной разработки.

О ком?


Рассказывать буду на примере сервиса Edwin, помогающего учить английский с помощью бота в Facebook Messenger. Бот @EnglishWithEdwin определяет ваш уровень английского с помощью теста и исходя из этого проводит для вас уроки прямо в мессенджере.

Они пришли к нам с практически готовым к публикации проектом. Уже имелись некоторые представления о том, как должна выглядеть общая архитектура проекта. На площадке, где велась разработка, уже было собрано несколько docker-контейнеров с сервисами и это всё даже ± работало.

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

Структура проекта


Как Edwin устроен изнутри? Ключевые слова — Python, PostgreSQL, Neo4j, RabbitMQ. Условно архитектуру можно разделить на три больших блока: транспортный, логический и аналитический.

image

В первом блоке — докеризированное приложение-рассыльщик, написанное на питоне, которое через Facebook Gateway получает сообщения из социальной сети, сохраняет их в базу данных и добавляет событие в очередь RabbitMQ.

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

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

Но всё это только предполагаемая структура, т.к. Edwin пришёл к нам, ещё не имея релизной версии проекта, в продакшене он ни разу не светился. Именно настройкой stage-/prod- окружения мы и должны были заняться.

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

Настройка CI/CD


Первое, о чём нужно помнить — всё не так сложно, как кажется издалека. Главное — понимать, что вы делаете и зачем. Выбирать инструменты только под те нужды, которые действительно важны, адекватно оценивать свои реальные потребности и возможности.

Основа всей нашей системы — это docker-контейнеры с сервисами и бизнес-логикой. Контейнеры выбраны для большей универсальности и мобильности в управлении инфраструктурой. Самым популярным инструментом для жонглирования docker-контейнерами на данный момент является Kubernetes от Google (по крайней мере, он больше всех на слуху). Нам же он показался излишне замороченным и структурно переусложнённым, и потому требующим слишком много телодвижений для поддержания работоспособности (и слишком много времени на изначальное понимание). А т.к. сроки введения в строй всей системы CI/CD у нас были сжаты до минимума, мы решили поискать более простые и легковесные решения.

Для хранения шаблонов контейнеров было решено взять (на самом деле, даже не взять, а просто оставить уже выбранный до нас разработчиками) Amazon EC2 Container Registry. В целом, он мало чем отличается от собственноручно поднятого на локалхосте registry. В основном тем, что его не нужно собственноручно поднимать на локалхосте.

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

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

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

Когда серверы под контейнеры настроены, все нужные сервисы докеризированы, остаётся только понять, как организовать, собственно, «непрерывную интеграцию», не нажимая по 500 кнопок каждые полчаса. По итогу остановились на такой системе, как GoCD и небольшой скриптовой обвязке. Почему GoCD, спросите вы меня, а не Jenkins? Ранее, в других проектах, нам приходилось сталкиваться и с тем, и с другим, и по совокупности взаимодействий GoCD показался более простым в настройке, чем Jenkins. Есть возможность создавать шаблоны задач и пайплайнов, на основе которых потом можно генерировать разные производственные цепочки для разных окружений.

Процесс разработки


Понедельник, 6 февраля


Первый день выдался, в общем-то, спокойным. Мы, наконец, получили ТЗ на то, что должны были сделать.

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

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

На основе полученной информации пишем Ansible-рецепты и начинаем установку серверов для продакшена, заводим в Амазоне отдельный VPC, который бы не пересекался с уже имеющимся dev-окружением во избежание неосторожных действий при разработке. Разворачиваем контейнеры со всеми нужными сервисами — PostgreSQL, Neo4j, кодом проекта.

Кстати сказать, при разработке коллеги пользовались PostgreSQL в виде Amazon RDS, но для продакшен-окружения, по совместной договорённости, решили перейти на обычные инстансы с PostgreSQL — это позволило сделать ситуацию более прозрачной для админов, которые могли бы максимально контролировать поведение всех элементов системы во время последующего роста нагрузок.

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

Вторник, 7 февраля


На отдельном сервере устанавливаем в базовом варианте деплой-панель GoCD и сервис-дискавери Consul.

Как я уже раньше упоминал, Edwin пришли к нам уже с готовой в какой-то степени инфраструктурой под разработку. Для жонглирования контейнерами они использовали Amazon ECS, он же EC2 Container Service. Что-то вроде Kubernetes-as-a-Service. После обсуждения с разработчиками пожеланий на тему будущей структуры проекта и работы с выкладками, было решено от ECS отказаться. Как и многие облачные штуки, он оказался недостаточно прозрачным для контроля происходящих внутри вещей, не хватало очень полезных в имеющейся ситуации вещей, типа задаваемых параметров окружения для одного и того же контейнера. Т.е. нет возможностей один и тот же контейнер использовать в разных пайплайнах, просто переопределив окружение. В амазоновой парадигме приходится создавать по отдельному контейнеру под каждое окружение (на примере сборщика логов можно показать).

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

Остаток дня занимаемся созданием и настройкой пайплайнов для продового окружения в GoCD — сборка кода, пересоздание контейнеров, выкатка новых контейнеров в прод, регистрация обновлённых сервисов в консуле.

Среда, 8 февраля


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

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

Удостоверившись, что всё работает как надо, настраиваем резервные репликации для баз данных в соседних Зонах Доступности, вместе с разработчиками заливаем рабочие данные в продовые базы данных.

Четверг, 9 февраля


Как я уже сказал, на этот момент, в общем-то, минимально необходимые элементы системы CI/CD у нас все установлены и настроены. Мы передаём разработчикам все данные по структуре получившейся системы, они со своей стороны приступают к тестированию всех механизмов сборки и выкладки проекта.

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

Логи решено собирать в Kibana — очень удобный интерфейс для разного рода аналитики. Мы неоднократно сталкивались с ней как на сторонних проектах, так и использовали у себя на внутренних проектах.

За несколько часов поднимаем Kibana, настраиваем сбор логов с сервисов, настраиваем мониторинги всех критичных сервисов на проде.

Пятница, 10 февраля


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

Результаты


image

Что мы получили в итоге? Модульную систему разработки проекта, состоящую из трёх этапов и окружений — dev, stage и prod. Весь проект разделён на отдельные микросервисы, для каждого из которых заведены свои пайплайны.

Когда разработчики выпускают новую версию, они идут в панель GoCD и просто запускают соответствующий пайплайн. Например «раскатать новый логгер на stage», тестируют там, проверяют, что всё работает как надо, потом раскатывают на прод. Если вдруг что-то пошло не так, то опять же, самостоятельно через панель откатывают нужный сервис до нужной версии.

image

Все три окружения — и dev, и stage, и prod — изолированы в разных VPC. В Consul заведены под каждое окружение отдельные дата-центры, поэтому сервисы даже случайно не могут ходить между разными окружениями. И даже при большом желании в такой ситуации довольно сложно какими-либо действиями на деве например сломать прод.

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

Проблемы, с которыми столкнулись


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

Проблем подкинул Amazon Web Services, на котором хостится этот проект.

При настройке Consul’а столкнулись с интересным затыком. Агенты и доступы к ним прописываются в контейнерах через системные резолверы. Но AMI Linux, используемый Амазоном по умолчанию, несколько отличается от дефолтной CentOS, и работа с резолверами там вынесена в настройки сети Virtual Private Cloud (VPC). При перезапуске сервера Amazon принудительно прописывает свои резолверы. Принудить его не ломать настройки резолва консула у нас пока так и не получилось, к сожалению. Но chattr +i всех спас, так теперь и живём.

Вообще структура VPC и его сетей довольно интересна, и там можно наткнуться на ограничения в очень неожиданных местах. Например, application balancer можно создать только мультизоне доступности, то есть бэкенды должны находиться сразу в нескольких разных availability zones. Для этого у VPC network должна быть разбивка, чтобы там присутствовали адреса в private/public и одной зоны, и другой. А менять разбивку VPC network можно только при создании. И менять VPC у инстансов тоже можно только при создании.

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

Выводы и планы


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

Хотя, конечно, многие могут сказать: «Ага-ага, они десять лет поддерживают проекты типа Хабрахабра и Нашего Радио, а нам тут разглагольствуют про простоту!». Да, действительно, на нашем веку мы видели много разного. И, возможно, как раз это и помогает нам отличать простое от сложного и не наступать на слишком большое количество граблей. Но опять же, если в самом начале разумно подойти к поставленной задаче и правильно подобрать инструменты, не гонясь за «крутостью», то можно значительно облегчить себе жизнь.

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

Какие у нас дальнейшие планы по проекту?

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

Также, похоже, приближается момент, когда нужно будет настраивать автоскейлинг на основных рабочих нодах, т.к. их общее количество постоянно растёт, и экономия на количестве активных серверов в часы минимальной нагрузки поможет сократить затраты.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333650/


Метки:  

Корпоративное управление: как юридически строится управление в компании

Среда, 19 Июля 2017 г. 14:28 + в цитатник

Метки:  

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

Среда, 19 Июля 2017 г. 13:38 + в цитатник


Ранним утром 20 мая мы, команда организаторов первого в истории CUSTIS хакатона, радовались разгоравшемуся теплому дню (что для весны 2017 — эксклюзив). Каждый думал о своем: админы изучали графики нагруженности Wi-Fi-диапазона, девушки из PR- и HR-отделов сверлили взглядом списки участников, кураторы из департамента разработки почему-то вспоминали Макаренко и лучшие навыки управления распределенными командами. Антикафе во «Флаконе», где мы должны проводить хакатон, еще закрыто, узкие проезды между лофтами еще не наполнились густым туманом от вейпов и гулом гироскутеров. Мы были уверены в том, что все готово, но легкое волнение нас не покидало.


Это было утро перед Битвой. Спустя 15 часов мы, усталые, но довольные, прощались на том же месте, вспоминали яркие моменты, благодарили участников и друг друга и точно знали, как нужно проводить хакатоны. Спокойствие и чувство выполненного долга.


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


С чего все началось


Лучшие друзья HR-брендинга


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


Что мы практиковали раньше:


  1. Ярмарки вакансий и карьерные форумы. Отличная возможность познакомиться с молодыми айтишниками, рассказать о компании, заинтересовать и пригласить следить за нашими предложениями. Работает только на далеких временных горизонтах и отличается низкой конверсией, если не звать ребят присоединиться к другим активностям компании.
  2. Семинары, митапы, воркшопы и мастер-классы для студентов и молодых специалистов, сопутствующие активности в соцсетях (задачки, конкурсы, образовательный контент). Крутой способ познакомить новичков с опытными специалистами компании, в игровом формате показать, как устроена работа в крупных проектах, и помочь ребятам понять, выбирать ли этот путь профессионального развития. Здесь конверсия выше: сила предметных и практических коммуникаций творит чудеса. Несколько постоянных слушателей наших семинаров до сих пор работают у нас.
  3. Стажировки и летние школы. Cамый эффективный с точки зрения коммуникаций формат. Стажировка позволяет «в бою» оценить не только скилы, но и командные, организаторские и профессиональные компетенции каждого участника. Благодаря стажировке, которую мы делали пять лет назад, мы познакомились с очень талантливыми ребятами и сразу же пригласили их в команду. Недостаток: при таком формате возрастает нагрузка на кураторов стажировки и требуется колоссальное количество трудозатрат ключевых сотрудников из производства.

Почему мы выбрали формат хакатона


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


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


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


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


Выходим в свет


Рождение идеи


Нет ничего проще, чем объявить хакатон на тему, актуальную для отрасли или конкретной компании. Как показывает скроллинг выдачи в Гугле, хакатон можно устроить практически по любому поводу, была бы в основе идея, которая будоражит отрасль или отдельных ее игроков: алгоритмы machine learning, нейросети и компьютерное зрение, банковские микросервисы, социальные и благотворительные проекты и даже целые технологические стартапы с TTM в 48 часов.


Что нам нравилось в формате:


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

Что мы хотели привнести от себя:


  • «битву» техстеков Java и С# (команды формируются по стекам);
  • игровую механику (команды пишут программы, которые соревнуются на общем сервере);
  • фан и развлечение (мы не собирались переиспользовать произведения цифрового искусства участников и требовать от них невозможного).

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


Проверяем на профессионалах


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


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


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


Утверждаем формат


Мы проектировали формат около месяца, после чего настало время зафиксировать основные детали мероприятия. Финальный вердикт выглядел так:


  • проводим турнир «Битва технологий: Java vs. C#»;
  • берем на себя всю организацию;
  • заимствуем игровую механику из морского боя и добавляем игровые бонусы и квесты;
  • набираем не более восьми команд (четыре на каждый техстек) по три-четыре человека;
  • проводим турнир в субботу, в течение целого дня, на внешней площадке;
  • делаем предложение о работе победителям Битвы, с остальными участниками в дальнейшем поддерживаем контакт.

Запускаем в производство


Концепция


Когда мы определились с игровой платформой, стало ясно, что для успешного продвижения нашей идеи в массы нужно что-то более оригинальное, чем предложение сыграть в морской бой. И достойный замысел родился там же, в недрах нашего департамента разработки: «Айтишники всех поколений искренне любят вселенную „Звездных войн“. Так почему бы не назвать наш турнир галактической битвой, а весь антураж — от интерфейсов серверной части приложения до оформления площадки — адаптировать под эту концепцию?». Морской бой не в тренде — да здравствует космический бой!


Технологическая часть


Чтобы обеспечить всех участников главным орудием пролетариата 21 века — настроенной средой разработки и удобными компьютерами, наши админы тщательно выбрали и арендовали 26 ноутбуков. На них были развернуты Visual Studio и IDEA, а на площадку мы привезли и подключили к локальной сети резервный сервер (его роль блистательно сыграл мощный десктоп) на случай, если удаленный будет недоступен.


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


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


Сервер приложения устроен очень просто и не требует какого-либо контейнера (обычное standalone java-приложение). Он подключается ко всем очередям в RabbitMQ и слушает их. В этом плане технологически сервер похож на клиентские приложения. Можно сказать, что все боты и сервер образуют микросервисы, перевязанные каналами RabbitMQ. Сложность здесь только в том, что сервер хранит множество информации: текущие и прошедшие игры, статистику и т. д. Чтобы это не вызвало проблем, для всех таких данных есть лимиты по количеству, то есть старые сведения забываются и запоминаются новые.


Написан сервер на Kotlin, но никакой особой котлинской магии не используется — в основном функциональщина и extension-методы.


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


Площадка


Параллельно мы стартовали трек поиска площадки, подходящей по техническим и эстетическим параметрам.


Мы хотели:


  • быстрый интернет;
  • мощный проектор для демонстрации турнира;
  • удобное рабочее пространство для команд;
  • вкусную еду в нужное время, чай с печеньками — всегда.

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


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


Наш шорт-лист выглядел так:


  1. Кафе-клуб «Папа Вейдер».
    • Плюсы. Метро «Китай-город» и «Лубянка» в шаговой доступности, своя кухня (нам было очень важно накормить наших участников полноценным обедом и ужином), удобные «кабинки» с диванчиками и столами на четверых, общая вместимость площадки — 60–80 человек, дружелюбный персонал и тематический декор.
    • Минусы. Достаточно темное подвальное помещение, соседство с «ароматным» продуктовым магазином оранжевого цвета, высокая стоимость аренды.
  2. Антикафе Geek Square.
    • Плюсы. Метро «Дмитровская» (относительно недалеко от центра), уютная атмосфера, стильный интерьер, лавка комиксов и гик-сувениров, просторный зал с удобными рабочими зонами. Свежий ремонт (ребята не очень давно открылись) и отзывчивость организаторов к нашим пожеланиям, демократичная стоимость аренды. В самом антикафе еды нет, но прямо под Geek Square есть ресторан. С ними мы договорились на кейтеринг для наших участников.
    • Минусы. Некоторые сложности в навигации по «Флакону», необходимость отдельно договариваться с разными контрагентами (был нужен более широкий канал для подключения и кейтеринг). Разместить с комфортом можно человек 20 (вместе с организаторами), больше было бы душно и тесно.

Мы остановили свой выбор на антикафе. Это была любовь с первого взгляда, которой мы легко простили все недостатки.


Маркетинг и работа с заявками


Как только внутри нашей команды воцарилось согласие о том, что backend мероприятия полностью готов к бою, мы принялись за активную работу над «клиентской частью» турнира.


Мы представляли себе портрет потенциального участника (и сотрудника) так:


  • 22–26 лет;
  • 3–4 года опыта разработки на Java или С#;
  • живет и работает в Москве;
  • не испытывает природного скепсиса по поводу формата хакатонов;
  • запросто проведет субботу за интенсивным кодингом и жаркими айтишными баталиями.

Нам предстояло:


  • протрубить о «Битве технологий» на широкую публику;
  • выйти на связь с теми, кто изъявил желание, найти среди них «избранных» и позвать на турнир;
  • закупить призы и сувениры в тематике «Звездных войн», разработать визуальную концепцию турнира — от дизайна интерфейса игры и бейджей участников до подбора музыки.

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


Чтобы анонсировать регистрацию на Битву, в ход пошли все доступные инструменты диджитал-маркетинга, которые мы своими силами и с помощью партнеров смогли в короткое время мобилизовать. Мы даже почти устроили тотализатор на тему «Сколько нужно времени, сил и охваченных аудиторий, чтобы 24 разработчика пришли на хакатон?».


Первыми в бой были брошены e-mail’ы. Текст приглашения участников был оформлен в стилистике мероприятия. Мы сделали две рассылки по партнерским базам и две — по своим. Рассылки проводились в несколько этапов. Охват: 10 000 человек. Результат: 29 регистраций (конверсия = 0,29).


Затем мы подключили SMM-артиллерию и порекламировались в группах «Типичный программист» и «Программирование ITmozg» Вконтакте. О нас узнали 42 000 и 12 000 человек соответственно. Результат: 10 регистраций (конверсия = 0,02) и 5 регистраций (конверсия = 0,04) соответственно.


Еще несколько тысяч человек удалось охватить с помощью корпоративного профиля на Фейсбуке и репостов коллег, партизанского маркетинга в сообществах ВК и тематических каналах в Телеграме. Наше сообщество CUSTIS Young ВК не принесло нам новых регистраций, но стало хорошей точкой оперативного взаимодействия с ребятами, у которых были оргвопросы.


На выходе форма регистрации показала занимательную статистику:




Приятно было наблюдать, что IT все возрасты покорны и молодыми разработчиками ощущают себя ребята 16 лет и старше. Сильно старше.




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




А вот эта диаграмма хорошо отражает рыночные тренды.




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


Графу анкеты «Расскажи о себе» мы придумали, чтобы ребята могли поделиться коротким резюме о своем опыте и задать вопросы организаторам. Помимо обстоятельных рассказов о достижениях и ожиданиях участников (а таких было большинство) мы получили нетривиальное собрание занимательных фактов:


  • «У меня на ноуте Linux Ubuntu!»
  • «Отучился 5 лет на программиста, отслужил в армии и устроился на работу».
  • «Джедай в пятом поколении».
  • «Android там вся хурма».
  • «Я милый».
  • «Студент третьего курса, которому надоело выполнять кучу лабораторных и который стремится познать новое».
  • «Люблю спорт (сейчас велосипед и скейт), хороший чай, кофе и мороженое. А что любите вы? Еще я профессионально владею слоупок-программированием».

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


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


  • 46 человек заполнили анкету;
  • 31 человек подтвердил участие.

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



Готовность №1


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


CUSTIS TechBattle: Java vs. C#


Утро


Ранним утром 20 мая мы, команда организаторов… Стоп, мы уже об этом рассказывали. Если отвлечься от субъективных переживаний, мы предпринимали вот что:


  • Развернули стойку регистрации перед входом на веранду кафе.
  • Подготовили на веранде печеньки, кофе и сэндвичи, чтобы участники как следует подкрепились перед битвой.

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



Что такое реальная конверсия, мы поняли за полчаса до старта турнира. Несмотря на активный утренний follow up, уговоры, торг с аргументами про печеньки, к началу битвы пришли только 14 из 31 заявившихся участников. Нужно было заранее опираться на отраслевую статистику и понимать, что до большинства бесплатных мероприятий доходит около 50% «подтвердившихся» участников.


В 11:05 было принято волевое решение начинать битву. Те, кто в это утро смог победить сон, трудности навигации по «Флакону» и плотный кофе-брейк, уже были достойны приза организаторских симпатий. И, руководствуясь принципом «четырнадцать смелых шестнадцать спящих не ждут», мы объявили начало турнира. Мы представили себя и компанию, рассказали о наших кураторах. После этого разбили участников на пять команд по технологиям (слепой жребий и цветные бейджи помогли нам) и комфортно разместили на площадке (нет худа без добра!).



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


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


День


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


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



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


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



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


Вечер


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


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



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



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


После Битвы


Обратная связь


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


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


Итоги


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


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

Чем в итоге оказался крут хакатон (справедливо для участников и организаторов):


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

С какими вызовами нам пришлось столкнуться:


  • конверсия зарегистрировавшихся;
  • модерация работы команд, отличающихся по уровню подготовки;
  • быстрое разворачивание инфраструктуры;
  • быстрое заворачивание бургеров в ланчбоксы.

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


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


Материалы


  • Ознакомиться с исходным кодом ботов можно в репозитории битвы на GitHub.
  • Посмотреть фото с хакатона можно на наших страницах в Фейсбуке и ВКонтакте.
  • Почитать материалы и задания для турнира можно в Google Drive.
  • Поделиться опытом или задать вопросы о хакатонах, HR-брендинге, говорящих Чубакках или тематических капкейках можно, написав на почту hr@custis.ru.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333634/


Метки:  

Как EA усложнили нам жизнь, или как мы чинили баг 12-летней давности

Среда, 19 Июля 2017 г. 13:21 + в цитатник
Иногда в программы закрадываются баги. Причем закрадываются так, что обнаружить их получается лишь через много-много лет после выпуска, когда чинить их уже нерентабельно. Иногда такие баги оказываются слишком критическими, чтобы их игнорировать. Поэтому под катом я расскажу, как мы устраняли один такой критический баг в одной старенькой гонялке. А заодно наглядно продемонстрирую, чем плох float, какие могут быть последствия и как с этим бороться.

image

Лирика.


Речь пойдет об игре Need for Speed: Most Wanted. Эта игра очень популярна и крайне любима многими геймерами-энтузиастами, и многими считается чуть ли не лучшей в серии. Даже если вы не азартный игрок, то наверняка слышали об этой игре в частности или о серии в целом. Но обо всем по порядку.

Я – спидранер. Прохожу игры на скорость. Довелось мне быть одним из первопроходцев в деле скоростного прохождения гоночных игр, поэтому я заодно являюсь одним из «глобальных модераторов» спидран-коммьюнити серии NFS. Участь со мной разделил чех под ником Ewil.

Почему участь? Потому что в один прекрасный момент к нам в дискорд-сервер пришел человек и заявил, что все наши рекорды трасс неправильны, и что мы – нубы. Скрепя сердце, подавляя багет от, как казалось, необоснованного обвинения и борясь с языковым барьером (английским этот человек владеет на очень плохом уровне), мы начали разбираться, что же не так с нашими рекордами. Из обрывков речи мы поняли, что в игре есть некий «timebug», который делает IGT неправильным. Ewil пересмотрел некоторые записи и руками пересчитал время. Оказалось, нам не врали. На записях IGT резко отличалось от RTA. Это были не пара миллисекунд, которые тоже могу решить исход рекорда, а местами разница доходила даже до нескольких секунд(!).

Мы начали искать причину и последствия этого явления. Еще задолго до этого я в личных интересах пытался «вытащить» из игры IGT. Моя попытка не увенчалась успехом, и я как-то забил на эту идею. Информации в интернете мы найти не смогли, за исключением какой-то английской странички с очень коротким описанием, без какой-либо доказательной базы. Поиск по ютубу также не принес результатов, но были найдены записи, которые гласили «No TimeBug».

Чуть позже я познакомился со SpeedyHeart, и она мне подсказала, что в игре время считается как float. Тут все начало проясняться, и мы медленно переходим от унылого вступления к лютому экшону!

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


Вооружившись Cheat Engine, OllyDbg, DxWND и NFS: MostWanted версии 1.3, я полез рыться в памяти. Выкопал я примерно вот что (картинка кликабельна):


Нас интересуют последние три адреса. Они хранят в себе IGT для разных ситуаций. Почему они float – одному Блэк Боксу известно… Но так не делают! Float, у него же точность, как у дробовика, а может и того хуже.
Занудство
Кстати, в предыдущей игре серии время считается как int – общее количество тиков процессора (а когда оно перевалит за те самые 2 миллиарда, игра запаникует и предпочтет упасть). Соответственно, этот модуль их движка был переписан, но лучше не стало. В предыдущей части, кстати, IGT тоже отличается от RTA, но там это, скорее всего, вызвано подлагиванием движка.

Собственно, немного о самих таймерах. Таймеры хранят время в секундах, т. е. целая часть – количество полных секунд. Два из этих таймеров, а именно Global IGT и Race IGT, периодически обнуляются. Для Global IGT это происходит в момент выхода в главное меню, а Race IGT обнуляется при рестарте гонки. Подсчет IGT производится через Global IGT, и в какой-то момент времени ему уже не хватает точности. Из-за этого время считается неправильно.

На этой стадии меня заинтересовали несколько вопросов:
  1. Раз уж есть разница во времени, то отличается ли геймплей с багом и без? Логично предположить, что если IGT ускоряется, то и в целом игра должна становиться «быстрее»
  2. Какие рамки у этого бага? Как он будет себя вести при разных значениях таймера, и как на это будет реагировать игра.

Ответ на вопрос номер 1 был найден крайне быстро. Я просто взял и изменил показания Global IGT на 300000.0 и получил то, что получил. Время ускорилось почти в два раза(!), однако на физике и поведении игры это никак не отразилось. Прикола ради я тыркал и другие таймеры, но они, почему-то, ни на что не влияют. Собственно, если бы с ускорением времени ускорялся и геймплэй, то в нашем мире спидранерства это считается вполне законным. Все таки мы любим баги. Но такой расклад никого не устроил.

Я пошел немного глубже и нашел ответ на вопрос 2. Как только Global IGT достигает отметки в 524288 время в игре полностью останавливается. Это немного смущает игру, и она начинает плохо себя вести делать интересные вещи. Например, не дает начать гонку после рестарта, намертво блокируя игру (выйти из нее можно только через диспетчер задач или Alt+F4). Отрыв/отставание от соперников перестает работать. А если проиграть гонку, то игра отправляет вас в свободное плавание по миру.

Float — ад перфекциониста.


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

Перед непосредственно кодом, распишу немного про цели. Известно, что игра «блокирует» цикл обновления физики на 120 раз в секунду (опять же, спасибо SpeedyHeart за информацию). Однако vsync обрубает обновление физики еще сильнее, до 60 раз в секунду. Соответственно, мы просто возьмем float-переменную и будем циклически туда добавлять 1/60 секунды. Потом мы посчитаем, за сколько шагов мы добились результата, и за сколько шагов мы должны были добиться этого результата. Также будем делать все циклически для разных случайных величин и считать среднюю погрешность в рассчетах. Любые отклонения в 2 и менее шагов (33мс) мы будем считать незначительными, потому что игра показывает время до сотых секунды.

#include 
#include 
#include 
#include 
#include 

#define REPEATS 1000       // Количество проверок
#define ESCAPE 27          // Код клавиши ESCAPE
#define TEST_TIME 2        // Время, проведенное на трассе
#define START_TIME 0       // Изначальное значение внутриигрового таймиера

void main( )
{
	time_t t;
	srand( time( &t ) );
	while ( true )
	{
		float diffs[ REPEATS ];
		int   frame_diffs[ REPEATS ];   // Сторэйдж разниц
		for ( int i = 0; i < REPEATS; i++ )
		{
			int limit = rand( ) % TEST_TIME + 1;// Генерируем случайное кол-во времени,
							 // +1 не даст нам сгенерировать 0
			limit *= 60;			// Минуты в секунды
			limit += (START_TIME * 60);	   // Выравниваем конечное время
			float t = 0.0f + (START_TIME*60); // Выравниваем начальное время
			float step = 1.0f / 60.0f;      	   // И лочим все на 60 фпс
			int steps = 0;
			while ( t < limit )
			{
				steps++;
				t += step;
			}

			// Считаем ожидания и выводим их на экран
			double expectation = (double)(limit - START_TIME*60)/ ( 1.0 / 60.0 );
			printf("%f\n", t );
			printf("Difference = %f; steps = %d\n", t - limit, steps );
			printf( "Expected steps = %f; frames dropped = %d\n", 
					expectation, (int)expectation - (int)steps );

			diffs[i] =  fabs( t - limit );
			frame_diffs[ i ] = (int)expectation - (int)steps;
		}

		// Считаем среднее и статистику
		float sum = 0;
		int frame_sum = 0;
		for ( int j = 0; j < REPEATS; j++ )
		{
			sum += diffs[ j ];
			frame_sum += frame_diffs[ j ];
		}

		printf( "Avg. time difference = %f, avg. frame difference = %d\n", 
			     sum / REPEATS, frame_sum / REPEATS  );
		// В случае "any key" продолжаем, в случае "ESCAPE" выходим
		printf( "Press any key to continue, press esc to quit\n" );
		if ( getch() == ESCAPE )
			break;
	}
}


Меняя значения START_TIME и TEST_TIME мы можем получить необходимую нам статистику. В целом, пока START_TIME не превышает 15 минут, то обычный 2-х минутный заезд окажется «свободным» от бага. Разница остается не критичной в рамках игры, 1-2 кадра:
image

16 Минут же оказались «критической точкой», когда время беспощадно плывет:
image

Интересен так же тот момент, что в зависимости от START_TIME будет меняться «сторона» ошибки. Например, после полутора часового непрерывного геймплея время начнет течь медленнее, чем должно:
image

Поигравшись со значениями еще нмного я оценил, что в получившейся программе «время» течет примерно так же, как в игре. Это было подтверждено практически – любые рекорды, записанные «из главного меню» в течение первых 15 минут геймплея были чистыми. При START_TIME близкому к 300000 секунд количество шагов было почти в два раза меньше, чем ожидалось. При START_TIME, большем магической константы 524288 программа переставала работать. Все это подтверждало, что процесс подсчета времени был скопирован верно.
Еще нудности
Результаты вычислений будут отличаться, если игра отрабатывает больше чем 60 кадров в секунду. При 120 кадрах в секунду значение «свободной от бага зоны» составляет 7 минут. Да и время останавливается значительно раньше. В двух словах, чем быстрее работает игра, тем сильнее становится ошибка.


Устраняем нежелательное поведение.


Теперь, когда известна проблема и ее поведение, можно ее устранить. Нужно лишь перезаписывать Global IGT всякий раз, когда игрок начинает заезд заново. Это можно узнать довольно просто – в этот момент обнуляется Race IGT. Но тут есть проблема.

Есть два издания игры: NFS: Most Wanted и NFS: Most Wanted Black Edition. Второе издание включает в себя две дополнительные машины и две трассы, да 69-ое испытание. Но, технически, это две совершенно разные игры! Их запускаемые файлы отличаются. Помимо этого, есть патч 1.3… Который отличается для каждого издания. В итоге у нас есть 4 разных версии игры, которые надо поддерживать. Этот факт делает «правильный» путь чрезмерно сложным и неоправданным. По-хорошему, нужно слегка подправить запускаемый файл и обнулять счетчик там, но… Править 4 разных экзешника, которые еще и запакованы, да защищены от отладки… Лучше просто напишем простую программку, которая будет в реалтайме отслеживать состояние таймеров и обнулять их при необходимости. Писать будем на C#.

image

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

Как же нам определить версию? Просто – по размеру основного модуля. Я специально реализовал проперти ImageSize. А чтобы не захламлять код магическими константами, запилим enum:
enum ProcessSizes
{
    MW13 = 0x00678e4e
}


Остальные версии добавим по мере их попадания ко мне в руки.

isUnknown отвечает за тот факт, удалось ли нам определить версию или нет. Из всего класса нам интересен только метод Refresh, вот он:
        public void Refresh()
        {
            if(!process.IsOpen)
            {
                // In cases when the process is not open, but the game exists
                // The process had either crashed, either was exited on purpose
                if(game != null)
                    Console.WriteLine("Process lost (quit?)");
                game = null;
                return;
            }
            if(isUnknown) // If we couldn't determine game version, do nothing
            {
                return;
            }
            // If process is opened, but the game doesn't exist, we need to create it
            if(process.IsOpen && game == null)
            {
                Console.WriteLine("Opened process, size = 0x{0:X}", process.ImageSize);
                switch((ProcessSizes)process.ImageSize)              // Guessing version
                {
                    case ProcessSizes.MW13:
                        game = new MW.MW13(process);
                        break;
                    default:
                        Console.WriteLine("Unknown game type");
                        isUnknown = false;
                        break;
                }
            }
            // At last, update game
            game.Update();
        }


Логика фикса вышла совсем простенькой:
    public abstract class Game
    {
        private float lastTime;
        private GameProcess game;

        /// 
        /// Synch-timer's address
        /// 
        protected int raceIgtAddress;               
        
        /// 
        /// Timer-to-sync address
        /// 
        protected int globalIgtAddress;

        private void ResetTime()
        {
            byte[] data = { 0, 0, 0, 0 };
            game.WriteMemory(globalIgtAddress, data);
        }

        public void Update()
        {
            float tmp = game.ReadFloat(raceIgtAddress);
            if (tmp < lastTime)
            {
                ResetTime();
                Console.WriteLine("Timer reset");
            }
            lastTime = tmp;
        }

        public Game(GameProcess proc)
        {
            game = proc;
            lastTime = -2; // Why not anyway
        }
    }


Дело осталось за малым: реализовать версию, выставив в конструкторе необходиме значения соответствующим protected-переменным. В мэйне же просто кидаем цикл обновления в отдельный трэд и забываем про него. Ах да, из-за особенностей карточек Nvidia и особенностей реализации установщика игр NFS мы будет принимать на вход имя процесса, чтобы была возможность кастомизации.
    class Program
    {
        static void Run(object proc)
        {
            GameHolder holder = new GameHolder((string)proc);
            while (true)
            {
                Thread.Sleep(100);
                holder.Refresh();
            }
        }
        static void Main(string[] args)
        {
            Thread t = new Thread(new ParameterizedThreadStart(Run));
            t.Start(args[0]);
            Console.WriteLine("Press any key at any time to close");
            Console.ReadKey();
            t.Abort();
        }
    }


На этом фикс заканчивается. Компилируем, запускаем и забываем о таймбаге, yay! ^_^ Картинка кликабельна.


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

Summary.


Вот так один маленький баг нехило подпортил нам жизнь. А ведь его можно было избежать, если бы Black Box использовали в свое время double, но нет. Кстати, это яркий пример того, как «написанное однажды» выливается в кучу неулавливаемых/перекатывающихся багов. Timebug присутствовал в каждой игре от Black Box ever since. В Carbon, ProStreet и даже Undercover. В последнем они поменяли логику подсчета IGT, но эти три таймера там все так же присутствуют, и ошибки округления приводят к странным последствиям. SpeedyHeart обещала сделать видео-обзор всей найденой в процессе информации, так что ждем-с.

Чему меня научила эта ситуация? Не знаю. Я итак понимал, что использовать float для серьезных вычислений – идея смонительная. Но теперь я лучше представляю, как именно все это будет работать на практике. Однако забавно получилось, что такая серьезная компания могла допустить такую серьезную ошибку, да еще и несколько лет подряд не замечать ее.

Мне кажется, что для данной задачи (подсчет IGT) нужно использовать такой путь: ставить timestamp в начале заезда, а потом вычитать из текущего времени. Причем арифметических операций стоит избегать, даже над целыми числами. 1/60 секунды это 16,(6) миллисекунд, поэтому в случае целого числа мы будем наивно откидывать 0,(6) при каждом сложении, что приведет к неточностям в подсчете.

В некоем обозримом будущем я постараюсь написать фикс и на другие версии. На этом у меня все, спасибо за внимание.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333676/


Метки:  

[Из песочницы] Дженерики и конвертеры в Nim

Среда, 19 Июля 2017 г. 13:10 + в цитатник
Логотип языка Nim

Всем привет! В данной статье я постараюсь рассказать, что такое generic процедуры и converter’ы в Nim (и показать примеры их использования)

Что такое Nim? Nim – компилируемый (в C, C++, Objective C и JS) высокоуровневый язык программирования со сборщиком мусора, имеющий три основных цели (в порядке приоритета): производительность, выразительность, элегантность. Официальный сайт языка, репозиторий на GitHub.
Также в Nim достаточно развито метапрограммирование (дженерики, шаблоны, макросы).

Для начала я покажу, как выглядит процедура, которая имеет один аргумент типа int, и возвращает тоже int:

proc plusOne(arg: int): int = 
  return arg + 1
echo plusOne(5)  # Выведет 6

Как я думаю, тут всё предельно ясно (прибавляем число 1 к аргументу arg типа int и возвращаем результат)

Дженерики в Nim — это процедуры, которые могут принимать несколько типов (компилятор сделает отдельную версию процедуры для каждого типа, который использовался с данной процедурой).

1 пример — обычный дженерик


Этот пример выводит длину объекта, если для данного объекта имеется процедура len (почти что полный аналог __len__ в Python):

proc tryLen[T](something: T) = 
  when compiles(something.len):  # something.len это тоже самое, что len(something)
    echo something.len
  else:
    echo "У этого типа не объявлена процедура `len`"

# Объявим тип нового объекта, у которого не будет процедуры `len` (так как мы её не объявляли)
type MyObject = object
# Создадим сам объект
let myObj = MyObject()
tryLen([1, 2, 3])  # Выведет 3
tryLen("Hello world!")  # Выведет 12
tryLen(myObj)  # Выведет "У этого типа не объявлена процедура `len`"

Этот пример намного сложнее предыдущего, я постараюсь кратко объяснить, что тут происходит:

Для начала мы объявляем саму процедуру tryLen, которая принимает аргумент something типа T (T — это чаще всего используемое название для неопределённого типа, вместо T можно написать abcd, и всё будет работать точно так же).
Затем мы используем специальное условие when compiles (это аналог if, но условие должно быть известно во время компиляции, и сам when в скомпилированный код не попадает). Если это условие выполняется для данного типа — выводим результат процедуры len для данного аргумента, если не выполняется — выводим сообщение.
Затем мы создаём объект типа MyObject, и применяем tryLen к массиву [1, 2, 3], строке «Hello world!», и нашему объекту.

2 пример — конвертер


Он служит для неявного конвертирования одного типа в другой (но он не особо приветствуется самими разработчиками языка, так как в этом случае всё-таки «явное лучше неявного»).
В данном примере мы сделаем конвертер, который конвертирует наш тип объекта MyObject в число (в данном случае — просто 1):

type MyObject = object
let myObj = MyObject()

# Если процедура или конвертер маленькие, то можно сразу написать возвращаемое значение без return
# Имя конвертера не играет какой-либо особенной роли
converter toInt(b: MyObject): int = 1
# Конвертер можно вызвать явно (однако в этом случае лучше сделать обычную процедуру)
echo myObj.toInt + 1  # Выведет 2
# Или он сам может вызываться неявно:
echo myObj + 1  # Тоже выведет 2, так как toInt неявно конвертировал myObj в число 1

Использование конвертеров — спорная тема (в стандартной библиотеке языка они почти не используются), но я отношусь к ним нейтрально.

Последний пример — дженерик конвертер


Да, звучит страшно, но на самом деле всё не так сложно. С помощью него мы сможем сделать Nim чуть более похожим на Python (а именно — неявно преобразовывать некоторые типы к bool)

converter toBool[T](arg: T): bool = 
  # Если для данного типа аргумента имеется процедура len
  when compiles(arg.len):
    arg.len > 0
  # Если аргумент можно сложить с числом того же типа T
  # T(0) используется для того, чтобы условие одновременно совпадало
  # со всеми числовыми типами (т.е мы конвертируем 0 в данный тип)
  elif compiles(arg + T(0)):
    arg > 0


if [1, 2, 3]:  # Длина массива
  echo "True!"
if @[1, 2, 3]:  # Длина последовательности
  echo "True too!"
if "":  # Пустая строка
  echo "No :("
if 5:  # Integer
  echo "Nice number!"
if 0.0001:  # Float
  echo "Floats are nice too!"

Спасибо за чтение! Я надеюсь, что вы смогли почерпнуть для себя что-то новое.

Источники
Мануал
Википедия
А также пользователи Nim, которые мне подсказывали решение различных проблем.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333702/


Метки:  

Черты великого продакт-менеджера

Среда, 19 Июля 2017 г. 12:44 + в цитатник


Почему у одних людей ничего не получается, а другие доводят любое дело до конца? В чем разница между хорошими и выдающимися профессионалами? Где грань между деспотизмом и верой в результат? Ну и что объединяет прекрасных продакт-менеджеров в клуб настоящих профессионалов? Под катом прекрасный рассказ Лоуренса Рипшера.

В своей книге “The Hard Thing about Hard Things” Бен Горовиц ссылается на свой документ, в котором описаны критерии хорошего продакт-менеджера. Хотя документ написан несколько лет назад, он всё ещё очень актуален и наполнен прекрасными советами.



По мере его чтения я заметил, что мысленно сверял, насколько написанное соответствует знакомым мне продакт-менеджерам. За годы работы мне довелось управлять парой сотен менеджеров продуктов/программ, и просто работать с несметным количеством. Я считаю, что многие из них являются лучшими в индустрии, и некоторые стали моим ближайшими друзьями. Так что я составил собственный список черт и поступков, которые наблюдал у лучших продакт-менеджеров. Именно эти черты и подходы я ценю в таких специалистах, и стараюсь развить в других, сравнивая со своим собственным ростом. Но я не утверждаю, что это истина в последней инстанции.

Я отбросил очевидные вещи вроде «является достойным человеком», «уважительно общается с людьми», «доверяет другим» и так далее. Старался писать так, словно описываю идеального продакт-менеджера своему другу или коллеге. В реальности один человек не может обладать всем этим набором черт, и если вы читаете это для себя, то постарайтесь понять, что именно созвучно с вашими убеждениями и подходит к вашей ситуации. Как только разберётесь, начинайте развивать в себе эти черты (см. совет №9). Хотя здесь я использую устоявшийся термин «продакт-менеджер», но на самом деле подразумеваю «создателей продукта (product people)». Великие создатели продукта могут именоваться как угодно — продакт-менеджерами, менеджерами программ, дизайнерами, разработчиками, маркетологами или продажниками. Иными словами, навыки важнее званий.

Я намеренно составил этот список без видимого порядка, поскольку ценность каждой черты меняется ситуативно и зависит от конкретного человека, команды и контекста. Так что можете читать в любой последовательности. Однако мой друг напомнил, что я должен следовать собственному совету (№10 — Приоритезирует). Так что если бы меня попросили выбрать три качества создателя продукта, я бы выбрал:

№1 — Начинает с «зачем».
№13 — Любопытен / ценит любопытство.
№18 — Имеет твёрдую точку зрения/слабо её придерживается.
Эти три я считаю фундаментальными качествами, а остальному можно научиться.

Список


1) Начинает с «зачем». Всегда начинает работу с клиентов и имеет ясное понимание, зачем кто-то захочет использовать наш продукт, какие проблемы он решит в жизни клиентов. Симон Синек (Simon Sinek) прекрасно обобщает свой подход. Она пишет обзоры, которые нам хотелось бы получать от наших пользователей. Обосновав миссию продукта, продакт-менеджер сплачивает вокруг неё людей и упрямо настаивает на этом видении, при этом оставаясь гибким в достижении этой миссии.

«Будь настойчив в видении, гибким в деталях» (с) Джефф Безос.

2) Создаёт продукты, решающие его собственные проблемы. Продакт-менеджер знает, что великие продукты зачастую создаются для себя. Этот совет часто дают всяким СЕО, но он относится к каждому, кто участвует в создании. Работая над продуктом, который он сам бы использовал, продакт-менеджер чувствует клиентов, интуитивно понимает, что им нужно. Он еждневно использует продукт и является одним из лучших тестировщиков в команде, выявляя больше недостатков, чем кто-либо другой.

«Если ищете истоки успешных стартапов, то некоторые из них начинались в подражание каким-то другим стартапам. Откуда они взяли свои идеи? Обычно, их основатели находят какие-то специфические нерешённые проблемы» (с) Пол Грэм.

3) Ясно устанавливает цели, измеряет прогресс и взаимодействует с командой. У продакт-менеджера есть ясное определение успеха для любой команды и продукта, ад которым он работает. Эти цели вдохновляющие (чтобы люди мечтали), реалистичные (чтобы люди оставались сосредоточены на достижении) и измеримые (чтобы помогать направлять людей). Также это должны быть общие цели — что-то, во что команда верит и чего хочет достичь. Продакт-менеджер неустанно отслеживает прогресс на пути к целям, но знает, что метрики лишь свидетельство успеха, а не сам успех.

4) Знает рынок. Он хорошо знает рынок и как в него вписывается продукт. Он понимает конкурентов и постоянно пользуется их продуктами. Он регулярно делится с коллегами информацией о ситуации на рынке посредством ссылок, презентаций и разборов продуктов. Эти данные он используется для направления и информирования (не не для диктата) в рамках своих продуктовых направлений.

5) Ищет наставников. Наставляет других. Продакт-менеджер знает, что один из лучших способов учиться — это перенимать опыт тех, кто уже сделал то, что он хочет сделать. Он строит отношения с наставниками, берёт на себя планирование и внятно объясняет, в чём он хочет вырасти. Он передаёт полученный опыт дальше, наставляя других продакт-менеджеров, и в процессе обнаруживает, что его точка зрения постоянно усиливается и улучшается.

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

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



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

Он не использует в качестве костылей такие вещи, как эксперименты ли машинное обучение (например, «мы просто обучим алгоритм пользовательским предпочтениям»). Он наверняка уже нарисовал карикатуру на подобие приведённой ниже. Будучи прагматиком, он не сосредоточен чрезмерно на реализации (см. №1 — Начинает с «зачем») и остаётся открытым для технических прорывов.



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

«Враг искусства — это отсутствие ограничений» (с) Пабло Пикассо.

9) Развивает свои сильные стороны. Он не мастер на все руки, но у него есть сильные стороны, и он последовательно развивает их. Он заинтересован в том, чтобы его сильные стороны становились как можно сильнее; знает о своих недостатках и не позволяет им поглотить себя. Но самое главное, что он способен делать, чтобы его достоинства дополняли друг друга, превращаяся в уникальные навыки. Он уважает другие дисциплины (разработку, дизайн и так далее), но не идентифицирует избыточно себя с ними, понимая, что конкретные навыки ценнее названий должностей.

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

«Если делать всё, то потерпишь неудачу в самом важном» (с) Бен Горовиц.

Он также понимает, что при всей важности отбрасывания лишнего, самое трудное — понять, что именно нужно отбросить. И интуитивно мыслит в терминах сценариев и цельного опыта, а не фич продукта. Этот навык очевиден благодаря его подходу к созданию MVP (minimum viable product, продукт с минимальной функциональностью).



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

12) Извлекает пользу из перемен и неопределённости. Некоторые любят перемены и неопределённость, а продакт-менеджер воспринимает их как естественное следствие погони за инновациями, и действует крайне эффективно в такие периоды. В это время он обязательно помогает другим, помогает обрести ясность понимания и уверенность, когда это возможно.

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

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



15) Упрощает сложное. Он всегда старается разложить проблему на наиболее важные вопросы и решения. Делясь информацией, продакт-менеджер оценивает аудиторию по принципу «что им нужно знать?», а не «сколько я могу показать?». Он предвосхищает будущие проблемы, но остаётся сосредоточенным на вероятных рисках, а не на конкретном наборе проблем, которые могут произойти. Любит подводить итог (выводить главную мысль).

«Я написал бы письмо покороче, но у меня не было времени» (с) Марк Твен

16) Ценит попытки, а не разговоры; результат, а не деятельность; создание, а не критику. Продакт-менеджер первым начинает действовать, избегает длинных дебатов по потенциальному решению, на создание прототипа которого требуется всего один день, или которое может быть выбрано просто на основании ответа клиента. Продакт-менеджер выбирает хорошую идею с отличной реализацией, а не блестящую идею, но плохо реализованную. Он не боится «испачкать руки» и регулярно смотрит, кому команде нужна его помощь.



17) Умеет находить правильную точку зрения на ситуацию. Продакт-менеджер умеет посмотреть на конкретную ситуацию в правильном «масштабе», и одинаково комфортно действовать на разных уровнях «подробности». Он не «предлагает сделать шаг назад», когда необходим прогресс, и не углубляется в мелкие детали, когда полезнее более широкий взгляд. Это называется «системное мышление».

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

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

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

На этом всё! Спасибо всем, кто работал над замечательными продуктами — вы практически написали этот список, просто делая то, что делали.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333698/


Метки:  

npm link на стероидах

Среда, 19 Июля 2017 г. 12:32 + в цитатник

npm link + steroids = npmy


Думаю многие из вас уже сталкивались с локальной разработкой npm-пакетов. Обычно никаких трудностей это не вызывает: создаём папку, запускаем npm init, пишем тесты, дальше используем npm link (либо просто симлинк) и «шлифуем» api до полной готовности.


Звучит просто… только если вы не используете Babel, Rollup, Webpack и т.п. Иными словами, всё хорошо, пока проект не нужно собрать перед публикацией, да ещё с модификацией исходного кода. Кроме того, одновременно разрабатываемых пакетов может быть больше чем один, что в разы усложняет «жизнь». Чтобы исправить эту ситуацию, пришлось сделать маленькую утилиту npmy, под катом небольшая статья с описанием тех. процесса работы и пример использования.


Итак, как я уже говорил, основная проблема локальной разработки — это использование scripts/хуков (prepublish, prepublishOnly и т.д.), именно по этой причине npm link не подходит, ведь по сути — это банальный симлинк, да ещё по завершению разработки нужно не забывать про npm unlink.


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


  1. Имеет простую настройку.
  2. Эмулирует полный цикл публикации.
  3. Создает симлинк на псевдо-опубликованную версию (далее ПОВ).
  4. Следит за изменениями и обновляет ПОВ.


Настройка проекта


Первой мыслью было добавить правила прямо в package.json, но это неправильно, ведь это именно локальная разработка, поэтому правила было решено размещать в .npmyrc, который без труда можно добавить в .gitignore.


Сам файл — ни что иное, как простой JSON-объект, у которого:


  • key — название зависимости из package.json;
  • value — локальный путь до разрабатываемого пакета (относительный или абсолютный).

Всё, на этом конфигурация закончена.



Запуск


Заходим в папку с .npmyrc и запускаем npmy, который:


  1. Читает .npmrc.
  2. Фильтруем список зависимостей на два списка для:
    • установки из NPM;
    • локальной установки.
  3. Установка зависимостей из NPM.
  4. Псевдо-публикация пакетов из .npmrc.
  5. Создание симлинка на ПОВ.
  6. Запуск отслеживания изменений (watch).


Процесс псевдо-публикации


Это самое интересное, ради чего всё и затевалось. Для начала вспомним, как это работает в оригинальном npm.


npm install && npm publish


Как видите, тут нас ждет сюрприз, prepublish и prepare выполняются как на npm publish, так и на npm install (без аргументов). Поэтому если вам нужна сборка проекта перед публикацией, используйте prepublishOnly, но только начиная с 5 версии. Хоть этот хук и добавили в 4, работает он неправильно, и вместо собранного пакета уедет не пойми что, увы.


В моём процессе перед запуском всех хуков есть ещё одно звено, а именно создание копии проекта (вместе с node_modules):


  1. Копия создается через rsync в темповую папку.
  2. Модифицируется package.json, из которого убирается npm test, чтобы не тормозить процесс псевдо-публикации.
  3. Затем для копии запускаются все хуки соответствующие процессу публикации.
  4. И в финальный штрих: удаление всего, что не соответствует секции files.
  5. Profit.

Вуаля, теперь мы имеем версию пакета, которую бы вы получили при установки из npm. Также при каждом изменении исходников, ПОВ будет обновлена автоматом.


Кроме этого, npmy не забывает про секцию bin в package.json и корректно создаёт симлинки на объявленные там скрипты.



Итого


  • npm install -g npmy
  • Создаем .npmrc в проекте
  • Запускаем npmy

Спасибо за внимание, надеюсь утилита будет полезна не только мне. :]



P.S. Инструмент новый, так что не стесняйтесь писать о проблемах.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333580/


Метки:  

Система управления складом с использованием CQRS и Event Sourcing. Проектирование

Среда, 19 Июля 2017 г. 12:21 + в цитатник
image

Итак, после постановки требований описанной в части 1 можно перейти к проектированию системы.

Основная наша задача в проектировании, как это понятно из названия статьи, добиться разделения интерфейсов на Query и Command, чтобы впоследствии разделить бизнес сценарии на те, которые будут читать данные (Query интерфейсы) и на те, которые будут изменять данные (Command интерфейсы). А также обеспечить минимальное время ожидание (latency) на обновление данных, доступных через Query, после того как мы изменили данные через Command.

Почему CQRS?


В последнее время разделение методов интерфейса (CQS), а впоследствии и самих интерфейсов на Query/Command стало популярным веянием в разработке архитектуры приложений. Но в Magento CQRS применяют не из-за популярности, а из-за того, что для нас иногда это единственный способ построить гибкое расширяемое (адаптирование к специфическим потребностям) решение с возможностью независимо масштабировать операции Чтения и Записи.

Фактически мы пришли к CQRS в результате осмысления и повсеместного применения SOLID в написании кода. CQRS это реализация принципа единой ответственности (The Single Responsibility Principle) и принципа разделения интерфейсов (The Interface Segregation Principle) и уход от классического CRUD.

Элементы CQRS у нас появились достаточно давно, когда мы задавались вопросом как масштабировать модель данных EAV (entity-attribute-value) для операций чтения и вводили индексные агрегационные таблицы для этого.
Более подробно про CQRS, и что он для нас значит можно послушать в презентации, которую я делал на MageCONF 2016 (видео, слайды).

Описание доменных сущностей


В предметной области Inventory у нас есть шесть основных доменных моделей:
  • Source — сущность ответственная за представление любого физического склада где может находиться товар (магазин, склад).
  • Source Item — сущность-связка, представляет собой количество определенного продукта (SKU) на конкретном физическом хранилище. А также хранит статус доступен ли продукт для продажи в данный момент.
  • Stock — виртуальная сущность ответственная за представление запасов на нескольких физических складах (Source) одновременно. Представляет собой аргрегацию по физическим складам. Связь между складами и Stock (какие именно склады входят в агрегацию) задается администратором в админ панеле либо через вызов API.
  • Stock Item — индексная сущность, строится на основании связей между Source и Stock, и представляет собой количество определенного продукта (SKU) доступное на конкретном Stock, т.е. виртуальной агрегации.
  • Sales Channel — канал продаж через который осуществляется продажа конечному покупателю. В случае Magento это может быть (Website, Store, Store View), но канал продаж может определяться продавцом самостоятельно, поэтому для некоторых продавцов это может быть Страна (Country), или большой Оптовый клиент (Wholesale) может выступать самостоятельным каналом продаж. Фактически канал продаж это контекст (scope) в рамках которого происходит продажа, который помогает нам четко определить Stock который должен быть использован во время выполнения бизнес операции.
  • Reservation — объект резервации, создается для того, чтобы иметь актуальный уровень товаров, которыми мы располагаем для продажи между событиями создания заказа и уменьшением количества товаров на конкретных физических складах. Фактически объект резервации помогает нам избавиться от надобности выполнения проверок, блокировок и списывания товаров из инвентаря физических складов (Source Item) во время операции размещения заказа, при этом значение товаров доступных для продажи в рамках определенного Stock меняется сразу же.

Данная диаграмма представляет собой взаимодействие описанных выше сущностей:


Таким образом мы получаем разделение интерфейсов на Query и Command


Theory of operation


Одна из наших основных задача это максимально разгрузить процесс размещения заказа. И идеально сделать эту операцию такой, которая не будет изменять состояние системы (State Modification). Это избавит от лишних блокировок и улучшит масштабирование чекаута.

По диаграммам выше видно, что такие операции как рендеринг страницы категории будет выполнен используя только Query API (StockItem), где нам нужно отобразить количество товаров, которые мы можем продать в определенном контексте (SalesChannel).
Операция синхронизации с внешней ERP или PIM системой будет использовать Command API (SourceItem) и обновлять количество товаров на конкретных складах, после чего реиндексация, которая пересоберет StockItem позволит этим обновлениям быть видимыми на фронте.

В случае операции размещения заказа все становится интересней.

Размещение Заказа по Шагам


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

Собственно, расмотрим эти операции на примере подробней.
Пусть мы имеем 3 физических склада: Source A, Source B, Source C
На которых хранится товар SKU-1 в таком количестве:
  • SourceItem A — 20
  • SourceItem B — 25
  • SourceItem C — 10
Мы имеет всего один канал продаж (Sales Channel), роль которого выполняет Website.
Для этого канала продаж у нас есть созданная виртуальная агрегация Stock A, с которой связаны все текущие физические склады (Source A, Source B, Source C).
Получаем StockItem A для SKU-1 имеет количество 20+25+10 = 55

Соответственно, когда покупатель заходит на Website, система точно определяет Stock, который должен быть применен для определения количества товаров, и использует Stock Item-ы в рамках этого стока для всех продуктов (SKU) в категории, в нашем случае SKU-1.

Размещение заказа


Пусть покупатель решил купить 30 едениц товара SKU-1.
  1. Мы принимаем решение о том можем ли мы выполнить продажу (хватает ли у нас товара для продажи), количество StockItem A для SKU-1 = 55 минус количество всех резерваций для продукта SKU-1 на Stock A, в нашем случае 0 (так как пока ни одна резервация не создана), 55 — 0 > 30, то мы принимаем решение, что продать 30 единиц товара SKU-1 можем.
  2. Во время размещения заказа мы агностичны к тому с каких именно физических складов произойдет в итоге списание, поэтому мы не работаем с Source Item сущностями. *Данная тема будет рассмотрена в отдельной статье, которая будет полностью посвящена алгоритму выбора складов для доставки в рамках выполнения заказа.
  3. При этом мы не можем уменьшить кол-во SKU-1 на StockItem, так как это Read проекция, созданная только для чтения. Поэтому мы создаем резервацию (Reservation) для SKU-1 на Stock A в количестве 30 единиц. Создание резервации это Append Only операция, которая добавляется без каких либо проверок.
  4. Сама команда заказа может при этом класться в очередь на последующую обработку.

Состояние системы, которое мы имеем на текущий момент:
Количество товара SKU-1 на складах
  • SourceItem A — 20
  • SourceItem B — 25
  • SourceItem C — 10
Количество товара SKU-1 для StockItem A — 55 (не изменилось).
Reservation для SKU-1 на Stock A в количестве 30.

Пока мы не успели обработать данный заказ, например из-за большой latency, к нам на сайт приходит другой покупатель, который также хочет приобрести товар SKU-1 в количестве 10 единиц.

Система начинает выполнять те же шаги, что описаны выше.
Проверяем можем ли мы продать 10 единиц SKU-1. Проверка осуществляется таким образом: количество StockItem A для SKU-1 = 55 минус количество всех резерваций для продукта SKU-1 на Stock A, в нашем случае 30, 55 — 30 = 25 > 10 принимаем решение, что продать 10 единиц товара SKU-1 можем.

По факту состояние в Event Sourcing определяется как проекция агрегированных данных (в нашем случае StockItem), с добавлением всех событий, которые были получены за дельту времени с момента формирования данной проекции (в нашем случае это Reservation).

Обработка заказа


На данном этапе мы должны определить какие именно физические склады будут принимать участие в доставке, и для этих складов уменьшить значение количества отгруженных товаров.
За эту часть ответсвенный алгоритм, который приймет решение о выборе складов (будет описан в следующей статье). Например, алгоритм принял решение, что делевле всего для продавца будет отправить 30 товаров со складов Source С и Source B
Соответсвенно количество товара SKU-1 на складах должно измениться:
  • SourceItem A: 20
  • SourceItem B: 25 — 20 = 5
  • SourceItem C: 10 — 10 = 0

А открытая резервация для SKU-1 на Stock A удалена (либо изменить статус этой резервации на Complete), чтобы она больше не участвовала в вычислениях.
После этого должна создасться команда на обновление индекса Stock Item, которая также может быть асинхронной.

Magento MSI (Multi Source Inventory)


Данная статья является второй статьей в цикле «Система управления складом с использованием CQRS и Event Sourcing» в рамках которого будет рассмотрен сбор требований, проектирование и разработка системы управления складом на примере Magento 2.

Открытый проект, где ведется разработка, и куда привлекаются инженеры из сообщества, а также где можно ознакомиться с текущим состоянием проекта и документацией доступен по ссылке — github.com/magento-engcom/magento2/projects
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333678/


Продолжаем изучать Tizen: C# компоненты оказались высокого качества

Среда, 19 Июля 2017 г. 12:19 + в цитатник

Picture 1

Сегодня я вновь возвращаюсь к проекту Tizen. В своей недавней заметке «Эксперимент по поиску ошибок в коде C# компонентов Tizen» в нашем блоге я провел поверхностный анализ и пришел к выводу, что имеет смысл проверить весь код C# компонентов этого проекта на наличие ошибок при помощи анализатора PVS-Studio и написать про это статью. Не откладывая в долгий ящик, я проделал эту работу и хочу поделиться с вами её результатами. Сразу скажу, что на C# коде анализатор PVS-Studio показал себя слабо. Однако обо всём по порядку: давайте посмотрим, что смог найти анализатор, а затем займёмся статистикой и подведём итоги.

Введение


Мой коллега Андрей Карпов не так давно публиковал две эпические статьи, посвященные анализу кода проекта Tizen, написанного на языках Си и Си++:

  1. 27000 ошибок в операционной системе Tizen
  2. Поговорим о микрооптимизациях на примере кода Tizen

Когда я заметил, что проект Tizen включает в себя код и на языке C#, мне захотелось сделать аналогичную статью о проверке компонентов, написанных на этом языке. К сожалению, в этот раз анализатор не смог продемонстрировать яркие успехи, но давайте не будем торопиться и изучим вопрос детально.

Что я проверял


Исходный код Tizen доступен для загрузки по ссылке. Репозиторий содержит около 1 000 проектов, каждый из которых состоит из архива с исходным кодом, а также вспомогательных файлов. По именам архивов или описанию не всегда возможно понять, что же находится внутри. Поэтому потребовалась загрузка, распаковка и изучение архивов для всего репозитория.

В предыдущей статье я приводил общее число файлов исходного кода C# (4 929, исключая *.Designer.cs) и строк кода в них (около 691 000), содержащееся в проекте Tizen. Сейчас нам потребуется более детальный анализ. Для начала попробуем найти файлы с расширением .sln или .csproj. Наличие таких файлов позволит проводить анализ в IDE Visual Studio, что значительно упростит работу.

Итак, в ходе поиска было найдено 227 решений (*.sln) и 166 проектов C# (*.csproj). Из файлов решений я выбрал те, которые содержали в своем составе проекты C#. Таких решений оказалось всего 3:

  • Tizen.Xamarin.Forms.Extension.sln
  • Xamarin.Forms.Tizen.sln
  • Xamarin.Forms.sln

Первые два решения являются надстройкой Tizen над сторонним компонентом Xamarin.Forms, а третье содержит сам компонент. Чуть более года назад мы писали статью про проверку Xamarin.Forms. В своей работе я учту эти результаты и попробую найти новые ошибки.

Далее, исключив файлы проектов (*.csproj), входящие в состав этих решений, я получил 107 проектов C#, которые не были связаны ни с какими решениями. Практически все они находятся в папках верхнего уровня с именами вида «csapi-*». Исключив из этого числа 11 тестовых проектов, а также 9 проектов, которые имели неподдерживаемый Visual Studio формат, я получил в остатке 87 проектов. Каждый из них я проверял отдельно.

Для чистоты исследования я решил отделить результаты, полученные для внутренних C# компонентов Tizen (те самые 87 проектов), от результатов проверки компонентов на базе Xamarin.Forms. Сначала я не хотел учитывать Xamarin.Forms, но, поразмыслив, пришел к выводу, что раз уж Tizen использует этот компонент для своих целей, то и ошибки Xamarin.Forms могут оказывать влияние на Tizen.

Также я не буду описывать ошибки, которые уже приводил в предыдущей статье.

Picture 6



Результаты проверки


Внутренние C# компоненты Tizen


В ходе проверки этой части проекта Tizen анализатор PVS-Studio выдал 356 предупреждений, из них 18 — уровня достоверности High, 157 — уровня достоверности Medium и 181 — уровня достоверности Low. Было проанализировано около 325 000 строк кода.

Предупреждения уровня достоверности Low я не рассматривал, так как там обычно велик процент ложных срабатываний. Впрочем, к сожалению, много ложных срабатываний в этот раз находятся не только на уровне Low. Среди 175 предупреждений уровней High и Medium мне удалось обнаружить всего 12 ошибок. Давайте взглянем на наиболее интересные.

Предупреждение PVS-Studio: V3008 The '_scanData' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 138, 137. Tizen.Network.Bluetooth BluetoothLeAdapter.cs 138
CWE-563. Assignment to Variable without Use ('Unused Variable')

internal BluetoothLeDevice(BluetoothLeScanData scanData)
{
  _scanData = new BluetoothLeScanData ();
  _scanData = scanData;
  ....
}

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

internal class BluetoothLeScanData
{
  internal string RemoteAddress { get; set; }
  internal BluetoothLeDeviceAddressType AddressType { get; set; }
  internal int Rssi { get; set; }
  internal int AdvDataLength { get; set; }
  internal byte[] AdvData { get; set; }
  internal int ScanDataLength { get; set; }
  internal byte[] ScanData { get; set; }
}

Как видим, класс не содержит переопределённого конструктора по умолчанию, поэтому, по всей видимости, двойное присвоение значения полю _scanData является ошибкой.

Предупреждение PVS-Studio: V3009 It's odd that this method always returns one and the same value of '0'. Tizen.Applications.WidgetApplication WidgetType.cs 47
CWE-393. Return of Wrong Status Code

private int OnCreate(....)
{
  WidgetBase b = Activator.CreateInstance(ClassType) as WidgetBase;
  ....  
  if (b == null)
    return 0;
  ....  
  return 0;
}

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

private int OnCreate(....)
{
  WidgetBase b = Activator.CreateInstance(ClassType) as WidgetBase;
  ....  
  if (b == null)
    return 0;
  ....  
  return 1;
}

Предупреждения PVS-Studio:
  • V3022 Expression '!LeftBoundIsForward' is always false. clipper_library clipper.cs 838
  • V3022 Expression '!LeftBoundIsForward' is always true. clipper_library clipper.cs 863
CWE-570/CWE-571 Expression is Always False/True

private TEdge ProcessBound(TEdge E, bool LeftBoundIsForward)
{
  ....
  if (LeftBoundIsForward)
  {
    ....
    if (!LeftBoundIsForward) Result = Horz.Prev;
    ....
  }
  else
  {
    ....
    if (!LeftBoundIsForward) Result = Horz.Next;
    ....
  }
  ....
}

Данный фрагмент кода содержит сразу 2 однотипные ошибочные проверки. При этом в первом случае переменная Result никогда не получит значения Horz.Prev, а во втором случае та же переменная Result всегда получит значение Horz.Next. Автору необходимо внимательно изучить данный код и самостоятельно исправить ошибку.

Предупреждение PVS-Studio: V3022 Expression 'e.OutIdx >= 0' is always true. clipper_library clipper.cs 3144
CWE-571 Expression is Always True

private void DoMaxima(TEdge e)
{
  ....
  if(....)
  {
    ....
  } else if( e.OutIdx >= 0 && eMaxPair.OutIdx >= 0 )
  {
    if (e.OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e.Top);
    ....
  }
  ....
}

Ещё один фрагмент кода с ошибочной проверкой. Возможно, если бы в условии e.OutIdx >= 0 && eMaxPair.OutIdx >= 0 был использован оператор "||", то проверка e.OutIdx >= 0 во вложенном блоке if имела бы смысл. Сейчас это выглядит подозрительно.

Предупреждение PVS-Studio: V3110 Possible infinite recursion inside 'InsertBefore' method. ElmSharp Toolbar.cs 288
CWE-674 Uncontrolled Recursion

public ToolbarItem InsertBefore(ToolbarItem before, string label)
{
  return InsertBefore(before, label);
}

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

public ToolbarItem InsertBefore(ToolbarItem before, string label,
  string icon)
{
  ....
}

Пожалуй, это все интересные ошибки данного раздела. Есть ещё несколько подозрительных участков кода, но я не буду на них останавливаться. Код от Samsung Electronics, написанный на C#, демонстрирует хорошее качество. Почему я так уверен, что проверенный код имеет авторство Samsung? Да потому, что каждый из проверенных файлов содержал в своём заголовке комментарий вида «Copyright © 2016 Samsung Electronics Co., Ltd All Rights Reserved».

Компоненты Tizen на базе Xamarin.Forms


Используемая в Tizen надстройка над Xamarin.Forms содержит около 242 000 строк кода. В ходе её проверки анализатор PVS-Studio выдал 163 предупреждения, из них 10 — уровня достоверности High, 46 — уровня достоверности Medium и 107 — уровня достоверности Low (не рассматривались).

Как и обещал, я попытаюсь найти ошибки, которые не были описаны в предыдущей статье про проверку Xamarin.Forms. Кстати, некоторые из приведенных там ошибок я не обнаружил при новой проверке. По всей видимости, они были исправлены после знакомства авторов со статьей.

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

Предупреждение PVS-Studio: V3095 The 'context' object was used before it was verified against null. Check lines: 16, 18. Xamarin.Forms.Xaml XamlServiceProvider.cs 16
CWE-476 NULL Pointer Dereference

internal XamlServiceProvider(INode node, HydratationContext context)
{
  ....
  if (node != null && node.Parent != null
    && context.Values.TryGetValue(node.Parent,  // <=
    out targetObject))
    IProvideValueTarget = new XamlValueTargetProvider(....);
  if (context != null)  // <=
    IRootObjectProvider =
    new XamlRootObjectProvider(context.RootElement);
  ....
}

Переменную context сначала используют, а только затем проверяют на равенство null.

Предупреждение PVS-Studio: V3095 The 'type' object was used before it was verified against null. Check lines: 147, 149. Xamarin.Forms.Xaml ExpandMarkupsVisitor.cs 147
CWE-476 NULL Pointer Dereference

public INode Parse(....)
{
  ....
  var xmltype = new XmlType(namespaceuri, type.Name, null);  // <=
  
  if (type == null)
    throw new NotSupportedException();
  ....
}

Еще один пример возможного выброса исключения NullReferenceException. Переменную type используют для создания экземпляра класса XmlType, а уже затем проверяют на равенство null.

Другие аналогичные ошибки:

  • V3095 The 'e.NewElement' object was used before it was verified against null. Check lines: 32, 46. Xamarin.Forms.Platform.Tizen MasterDetailPageRenderer.cs 32
  • V3095 The 'e.NewItems' object was used before it was verified against null. Check lines: 557, 567. Xamarin.Forms.Core Element.cs 557
  • V3095 The 'e.OldItems' object was used before it was verified against null. Check lines: 561, 574. Xamarin.Forms.Core Element.cs 561
  • V3095 The 'part' object was used before it was verified against null. Check lines: 135, 156. Xamarin.Forms.Core BindingExpression.cs 135

Предупреждение PVS-Studio: V3125 The 'e.NewItems' object was used after it was verified against null. Check lines: 999, 986. Xamarin.Forms.Core TemplatedItemsList.cs 999
CWE-476 NULL Pointer Dereference

void OnProxyCollectionChanged(....)
{
  ....
  if (e.NewStartingIndex >= 0 && e.NewItems != null)  // <=
    maxindex = Math.Max(maxindex, e.NewStartingIndex +
      e.NewItems.Count);
  ....
  for (int i = e.NewStartingIndex; i < _templatedObjects.Count; i++)
    SetIndex(_templatedObjects[i], i + e.NewItems.Count);  // <=
  ....
}

А здесь — обратная ситуация. Переменную e.NewItems проверяют на равенство null перед первым использованием. Однако при повторном использовании это сделать забывают.

Статистика


Как писал мой коллега Андрей Карпов в одной из предыдущих статей, анализатор PVS-Studio выявляет около 0.4 ошибки на 1000 строк C/C++ кода проекта Tizen. Давайте подсчитаем, что у нас получается для C# кода.

Всего было проверено около 567 000 строк кода на языке C#.

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

Получается, что анализатор обнаруживает 0.02 ошибки на 1000 строк кода. Или, другими словами, он находит 1 ошибку на 50 000 строк кода. Маловато.

Picture 11



Приходится признать, что данном случае анализатор не смог продемонстрировать свою полезность. Почему так получилось — сказать сложно. При проверке других открытых проектов анализатор часто демонстрировал хорошие результаты. Например, при проверке открытых компонентов Unity3D плотность обнаруживаемых ошибок составляла 0.5 ошибки на 1000 строк кода. Т.е. показатель был в 25 раз лучше.

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

Выводы


Итак, результат проверки оказался не таким, как я ожидал для такого объема кода. Скажу честно: я предполагал найти как минимум несколько сотен ошибок, но нашлось около пятнадцати.

Однако важно то, что анализатор PVS-Studio справился с задачей по анализу C# кода проекта Tizen. А значит, может быть полезен если не сейчас, то в дальнейшем, когда в Tizen будут появляться новые компоненты, написанные с использованием языка C#. Подтверждением потенциальной пользы является огромное количество ошибок, которые анализатор уже обнаружил в других открытых проектах (см. список статей).

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

Picture 17


Загрузить и попробовать PVS-Studio

Дополнительные ссылки


  1. Эксперимент по поиску ошибок в коде C# компонентов Tizen
  2. Microsoft открыла исходники Xamarin.Forms. Мы не могли упустить шанс проверить их с помощью PVS-Studio
  3. Команда PVS-Studio готова поработать над проектом Tizen (открытое письмо)
  4. Предоставляем анализатор PVS-Studio экспертам безопасности
  5. Как PVS-Studio может помочь в поиске уязвимостей?
  6. 27000 ошибок в операционной системе Tizen
  7. Поговорим о микрооптимизациях на примере кода Tizen




Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Sergey Khrenov. We Continue Exploring Tizen: C# Components Proved to be of High Quality

Прочитали статью и есть вопрос?
Часто к нашим статьям задают одни и те же вопросы. Ответы на них мы собрали здесь: Ответы на вопросы читателей статей про PVS-Studio, версия 2015. Пожалуйста, ознакомьтесь со списком.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333694/


Метки:  

МЕГА ищет идеи: новый сезон

Среда, 19 Июля 2017 г. 11:12 + в цитатник

Официально представляем dapp — DevOps-утилиту для сопровождения CI/CD

Среда, 19 Июля 2017 г. 11:01 + в цитатник
Читатели этого блога, а также посетители последних HighLoad++ и РИТ++ с большой вероятностью уже слышали про нашу утилиту для DevOps-инженеров dapp, но теперь мы решили официально и окончательно представить её «большому миру». Формальное право на то нам даёт тот факт, что мы работаем с dapp для решения задач в production уже больше года, поэтому считаем, что технология созрела для более массового использования.



Итак, dapp — написанный на Ruby инструмент, созданный в компании «Флант» как Open Source-проект для реализации и сопровождения процессов CI/CD. Что он позволяет?

Сборка образов


Когда мы рассказывали о dapp впервые (пост, видео), речь шла об использовании этой утилиты для сборки образов Docker-контейнеров. Не буду пересказывать весь доклад, но перечислю основные возможности dapp, которые (с помощью конфигураций образов, описанных в Dappfile) позволяют выполнять сборку образов быстро и эффективно:
  1. четыре стадии сборки образа (before_install, install, before_setup, setup), результаты выполнения которых кэшируются (даёт значительный рост в скорости повторных сборок образа);
  2. «внешний контекст» для монтирования каталогов, используемых во время сборок, но исключаемых из конечного образа;
  3. продуманная работа с Git, добавляющая в образ только дельты (git patch apply) при новых сборках и кэширующая содержимое этих патчей;
  4. «артефакты» — возможность использования сторонних инструментов на этапе сборки проекта, но не включать их в финальный образ; для артефактов тоже поддерживается кэш (с недавним выпуском Docker 17.06 эта фича перестала быть столь значимой из-за появления multi-stage builds);
  5. поддержка Chef, позволяющая настраивать системы в Docker-образах по рецептам (cookbooks).

Последнее мы планировали дополнить поддержкой Ansible, и актуальность этого вопроса для нас только возрастает, но фактическая реализация пока отстаёт от намерений.

Деплой в Kubernetes


Зато мы продвинулись в другом важном направлении — расширении возможностей dapp за рамками сборки образов, в области других составляющих процессов непрерывной интеграции и доставки приложений. Начальная поддержка деплоя в dapp ознаменовала возможность интеграции с системой Kubernetes, используемой нами для оркестровки контейнеров (подробнее мы недавно рассказывали: «Наш опыт с Kubernetes в небольших проектах (обзор и видео доклада)»). Суть этой фичи заключается в следующем:
  1. Для Kubernetes готовятся YAML-конфигурации, которые могут быть разными для каждого нужного контура (production, staging, testing…) и которые помещаются в специальный каталог в Git-репозиторий с кодом приложения и инфраструктуры (например: файлы backend.yaml, frontend.yaml, cron.yaml в специальном каталоге .kube/).
  2. Созданные конфигурации отдаются утилите kubectl, которая разворачивает описанную в них инфраструктуру в нужном кластере Kubernetes (опять же, кластеры могут быть разными для каждого контура).
  3. Созданные Docker-образы разворачиваются в инфраструктуре Kubernetes.

Технически это реализовано с помощью команды dapp kube deploy (см. документацию), которая по своей сути является обёрткой к менеджеру пакетов Kubernetes — Helm, позволяя:
  1. дополнять и расширять возможности передачи параметров в Helm (добавляет секретные значения и файлы, имеет helper для чтения этих файлов в шаблонах);
  2. интегрировать образы, собираемые по Dappfile, в Helm (имеет helper для составления имени образа).

Примечание: Сам деплой мы делаем через GitLab CI, о чём писали в недавней статье «GitLab CI для непрерывной интеграции и доставки в production»: «Часть 1: наш пайплайн», «Часть 2: преодолевая трудности».

Дополнительная информация


Прямо сейчас наш коллега пишет статью-знакомство с dapp, в которой будет приведён пример сборки приложения в dapp и сравнение этого процесса с Docker Compose. Мы опубликуем её в блоге на следующей неделе.

А пока предлагаю ссылки на уже имеющуюся информацию:
  • упомянутый доклад «Собираем Docker-образы для CI/CD быстро и удобно вместе с dapp (обзор и видео)»: пост и видео с выступления технического директора АО «Флант» Дмитрия Столярова, где и состоялась первая и более «камерная» презентация dapp;
  • официальная техническая документация на русском языке — flant.github.io/dapp;
  • отдельного внимания в этой документации заслуживает инструкция «Первое приложение на dapp»;
  • официальная группа поддержки в Telegramdapp_ru.

Да, это Open Source!


Исходный код dapp написан на языке Ruby и опубликован на GitHub под свободной лицензией Apache License 2.0 (она же используется в таких близких нам проектах, как Moby/Docker, Kubernetes, Helm, etcd и других).

Мы приглашаем DevOps-инженеров и Open Source-энтузиастов, заинтересованных в применении dapp, к участию в проекте. Исследуйте его, задавайте вопросы (можно прямо здесь в комментариях), указывайте на проблемы, предлагайте улучшения. Спасибо за внимание!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333682/


Вы ни черта не понимаете в цветах

Среда, 19 Июля 2017 г. 10:52 + в цитатник

Вы ничего не понимаете в оттенках черничного белья, это же очевидный #101318ff

Я думаю многие сталкивались с ситуацией, девушки пытаются убедить нас, что совершенно очевидно, что цвет ее белья вовсе не черный, а очевидно ониксовый или обсидианово-дымчатый. При том, что там явно #000000ff или максимум #080808d5, если белье слегка прозрачное. Или вдруг та же девушка внезапно решает перекрасить стены и начинает мечтательно описывать всю красоту оттенков, которые она хотела бы получить. И вы погружаетесь вместе с ней в чудесный мир лавандовых стен и пытаетесь определиться со шторами. То ли цвет пенящейся морской волны, то ли кораллово-бирюзовый. Где-то на этом этапе вы начинаете тоскливо смотреть на стену, размышляя о том, какой оттенок придаст ей ваш мозг после хорошего разбега.


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


Как мы с коллегами поспорили


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

Проблема кроется в цветовых эталонах. Это те оттенки, которые отпечатались у нас в памяти с детства при фразе «Смотри, бабушка с лиловыми волосами. Нет, она нормальная, просто ей цвет нравится». И все. Импритинг на цвет готов. Даже, если волосы были не лиловые, а фиолетовые или розовые. Особенная беда с цветами, которые привязаны к реальным объектам, растениям или явлениям природы. Приведу примеры:


Сиреневый это же как сирень, правильно?


Цвет морской волны — это так очевидно.

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

Как описать цвет


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

Зато цветовое пространство HSL (Hue, Saturation, Lightness) Выглядит наиболее привлекательным. Тот же «сиреневый» описывается как оттенок фиолетового (Hue) средней насыщенности (Saturation) и достаточно темный по светлоте (Lightness). Можно даже уточнить, что Lightness в районе одной трети. Для этого достаточно, чтобы человек мог хотя бы раз увидеть эти ползунки в графическом редакторе. Если же люди все-таки настаивают чтобы я описал цвет краски на стене своей кухни как-нибудь «попроще», то я обычно отвечаю что-то вроде «цвет гнилого лосося». Человек впадает в глубокую задумчивость, пытаясь это представить, и вопросы как-то отпадают сами собой.

Эксперимент


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

Проголосовало 3 человека. Воздержавшихся нет.

Какой вариант описания сложного цвета кажется более удобным?

Проголосовало 3 человека. Воздержавшихся нет.

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

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333552/


Метки:  

Поиск сообщений в rss_rss_hh_new
Страницы: 1437 ... 1056 1055 [1054] 1053 1052 ..
.. 1 Календарь