Задача четвърта

  1. Публикувахме четвърта задача за домашно. Условието и тестове може да намерите и в github хранилището ни. Може да ви е полезно да си припомните нещата от този гайд преди да предадете решението.

    Задачата не безбожно трудно но има някои специфики. Хубаво ще е да прочетете условието няколко пъти и да разберете какво трябва да сте прави. Ще трябва и да разберете как се правят range заявки и как работят те. Както и да имплементирате io.Reader, за което също ще е хубаво да прочетете няколко пъти документацията по въпроса.

    Поради празниците задачата е със срок до 3-ти януари чак другата година 2017-та.

    Тук можете да ни питате ако има нещо неясно по условието или да споделяте ваши примерни тестове с останалите.

    Приятни празници и да си напишете задачата ;).

  2. Добавил съм и специално уточнение че DownloadFile се очаква да върне io.Reader независимо от заявките които правите, това да се разбира като да не чака да се почват/свършват и други такива.

  3. Здравейте!

    Сега... следните редове от спецификацията:

    Прекъсната връзка (т.е. невърнати всички поискани байтове) от даден сървър не се счита за грешка и трябва отново да се опитате да се свържете към този сървър. При грешка при самото свързване обаче (timeout, статус 4xx или 5xx) обаче, трябва да считате сървъра за невалиден и да го игнорирате. Байтовете които е трябвало да се свалят от него трябва да се разпределят по равно на останалите.

    Тук не са описани две неща:

    1. Колко пъти опитваме отново?
    2. Как точно се разпределят байтовете между другите сървъри?

    Управлението на тези "грешки" конкурентно е именно трудната част от задачата според мен и за това искам да е конкретно ясно как трябва да стане.

    1. Опитвате се докато не започне да връща грешка самото правене на за заявка или започне да ви връща странни статус 4xx,5xx. Тоест няма опитване n пъти и после спирате. Ако от един сървър трябва да свалите 100 байта и на всеки върнат байт ви прекъсва повтаряте, ще трябва да направите 100 заявки. Дори да не ви върне никакви байтове а да затвори връзката след header-ите пак повтаряте.
    2. Байтовете които е трябвало да се свалят от него трябва да се разпределят по равно на останалите. "По равно" може би е малко не качествено определение в някои много гранични случаи. Приемам за валидно решение ако всеки път като трябва да преразпределите байтове от един url на останалите, ги разпределяте така че този който получи най-малко да не получи с повече от 1 байт повече от този с най-малко.

    По точно условие и в обратната посока: накрая на всичкото сваляне всички не върнали грешки URL-и трябва да се сервирали по равен брой байтове с допустима грешка между този върнал най-много и най-малко 1 байт. Но ще приемам и решения които разпределят по-равно за всеки случай по отделно.

    Следва дълъг пример:

    Имам 5 URL-а len(urls) == 5 за 200 байта файл:

    1. Правите 5 заявки към петте URL-a конкурентно за по 40 байта всяка.
    2. urls[0] ви връща грешка че не може да направи заявката (примерно не може да резолвне ip-то от домейна ...), тези 40 байта трябва да се разпределят на другите 4 url-и - в случая е лесно по 10 на всяка
    3. urls[1] сваля 3 байта и му се затваря връзката - опитвайки отново връща 3 байта и пак прекъсва, после 1 после 2 после 0 после 27 - свалили сме 40-те байта които са дадени за този url в точка 1.*
    4. urls[2], urls[3] и urls[4] завършват своите начални 40 байта без проблеми.
    5. Приемаме че urls[1:5] са свалили своите начални 40 байта и сега трябва да свалят по 10 байта от тези които са били за urls[0]
    6. urls[1] се проваля и неговите 10 байта трябва да се разпределят на останалите 3 - очевидно единия ще получи 4, а другите по 3 за да са по-равно ... нека urls[4] получи четирите
    7. urls[2] сваля 1 байта и му се затваря връзката, като на последваща заявка се връща статус код 500. Тук има 9 байта, като urls[3] е свалил 40 и са му дадени други 13 да сваля а urls[5] e свалил 40 и са му дадени други 14 да сваля. Ако се дадат 4 на urls[3] и 5 на urls[4] това ще по-равно за текущото разпределяне, но не и за глобалното.
    8. от urls[3] и urls[4] се свалят всичките байтове които са им били разпределени.

    * Тук може след първото прекъсване да почнете да сваляте 10 байта, оригинално за url[0], или не - въпрос на имплементация. Също така принципно началните парчета можеха да не са по 40 байта а по 2 и после по 3 и 5 и т.н. - това пак е по ваш избор.

    edit: Весела Нова Година :D

  4. Само на мен ли ми се струва, че тестовете и условието имаха разминаване, и също така, че не беше доизмислено домашното до последния момент.

    Какво имам предвид?

    Например, бихме искали DownloadFile да връща веднага io.Reader обект. Добре. Искаме да сваляме асинхронно, и в момента, в който прочетем от този рийдер, да ни се връщат последователните байтове, които са готови и да спрем дотам. Хубаво обаче, в тестовете се проверява за връщане на обект веднага, но повечето тестове например не оставят никакво време на нашите рутини да свалят каквото и да било, а направо се опитват да четат от върнатия io.Reader. Освен ако ние изкуствено но добавим забавяне в нашия io.Reader, което е концептуално неправилно, няма как да получим желаното действие. io.Reader рутините няма да са свалили още нищо, и Reader ще върне нула свалени байта и грешка "Download in Progress".

  5. Първо се извинявам, но ще се забавя още. Поради натовареност/промени в плановете ми навярно ще публикувам крайните тестове, примерното решение и крайните точки от това домашното не по рано от Неделя.

    Това от части се дължи на това че искам да напиша още няколко теста и да проверя че покрай промени в тестовете или тестове които не са напълно коректни някой от предалите не получава по-малко точки.

    @Никола Юруков:

    Условието с изключение на реда за това че очакваме че ще върнете io.Reader независимо от свалянето, не е било променяно. Това обаче не е промяна във замисъла на задачата, а е следствие на това че задачата беше намалена като сложност в последните дни преди да я постнем и покрай някоя от редакциите сме го пропуснали. Ако това е единственото нещо което не изпълнявате навярно evans няма да ви вземе точка - тъй като има само един тест (за сега поне) в който се ползва и съм склонен да върна точката на хората които са предали преди нова година(теста и промяната са добавени на 31 сутринта).

    Промените по тестовете с изключение на няколко са в повече оправяне на проблеми които евентуално вие(и аз) бихте имали когато не ви минат. Изключенията са:

    • добавянето на теста покрай изпуснатата част от условието
    • поправянето на теста покрай това че имплементирах как се правят и отговарят range заявки по спомен вместо да прочета rfc-то - тук са два къмита в рамките на 10 минути
    • връщането на Content-Length header вместо нищо в случая че се взима празен файл в един от тестовете - което отново е според rfc-то и ако не ви работи теста всъщност имплементирате грешно условието.

    Отново за хората предали преди въпросните промени а и за хората предали след тях, съм склонен да променя тестовете по такъв начин че да минават и при тях. Това е главно защото необходимите промени в решенията, за да минат промените в тестовете, са минимални и приемам че просто не са гледали форума/github репото след като в началото са си взели тестовете.

    Това всъщност е и основната причина да искам да видя решенията по внимателно.

    Примера за разминаване между условието и тестовете не е валиден. Иначе част от тестовете (навярно всичките) могат да бъдат написани по добре.

    Примера ти не е валиден, защото Read може да блокира докато има какво да се прочете. Това както навярно се досещаш е начина по който работи, когато четеш от файловата система или мрежата(и навярно всичко друго). Примерно четейки от http.Response.Body е възможно към момента, в който викнеш Read още нищо освен header-ите на заявката да не са изпратени. При което Read може да блокира до получаването на някакво количество байтове вместо да върне 0, nil, така и прави в реалната имплементация, но не е задължително. Но дори решение да връща 0, nil докато няма нито грешка нито какво да върне, това навярно пак ще мине всички тестове - възможно е поради зацикляне да не успеете да се вместите във времето за теста и принципно не съм тествал, но не виждам причина да не минава.

Трябва да сте влезли в системата, за да може да отговаряте на теми.