Cara memulai proyek kesayangan dan tidak mendapat manfaat

Cara memulai proyek kesayangan dan tidak mendapat manfaat


TL; DR

Artikel tersebut menjelaskan penggunaan proyek hewan peliharaan sebagai cara untuk mempertahankan dan meningkatkan keterampilan. Penulis membuat perpustakaan PHP untuk menginstal FIAS dari file XML.


Tujuan


Karena itu, saya jarang berganti pekerjaan, mengingat keinginan alami setiap organisasi untuk proses yang tetap, tugas apa pun berubah menjadi rutinitas. Di satu sisi, itu bermanfaat bagi bisnis untuk mempertahankan keadaan seperti itu, di sisi lain, bagi saya itu berarti kerugian total atau usang keterampilan. PHP berkembang pesat, dan, akibatnya, potensi lag juga berkembang pesat. Akhirnya, kita semua tahu bahwa hari ini sulit bagi seorang programmer untuk menemukan pekerjaan yang baik tanpa sepengetahuan Elasticsearch, RabbitMQ, Kafka dan teknologi lainnya yang tidak sering muncul dalam pekerjaan sehari-hari saya.


Setelah memulai situs khas berikutnya, saya memutuskan bahwa sudah waktunya untuk mengubah sesuatu. Saya tidak ingin mengubah pekerjaan saya, tetapi saya ingat bagaimana di salah satu konferensi yang direkomendasikan oleh pembicara menggunakan proyek pilihannya sendiri, yang disebut proyek hewan peliharaan, untuk pelatihan. Metode itu sepertinya tepat dan saya memutuskan untuk mencobanya.


Pemilihan tugas


Pilihan tugas ternyata menjadi bagian paling sulit dari usaha tersebut. Tidak ada yang istimewa terlintas dalam pikiran: beberapa layanan, seperti parser pekerjaan yang dapat dengan mudah diimplementasikan pada tumpukan yang sudah dikenal. Saya meninggalkan ide proyek selama beberapa bulan, sampai saya tidak sengaja melihat berita tentang hackathon dari Kementerian Keuangan. Disarankan menggunakan salah satu daftar sumber data terbuka untuk membuat layanan. Antara lain, Sistem Alamat Informasi Federal (FIAS) juga diindikasikan. Sayangnya, hackathon sudah berakhir saat itu.


Saya belajar tentang FIAS untuk pertama kalinya, tetapi tugas itu tampak menarik. Nilailah sendiri: sekitar 30 GB file XML, sekitar 60 juta baris dalam database dan, lebih lanjut, pustaka nanti terbukti berguna dalam pekerjaan. Ada beberapa solusi siap pakai di Github, tetapi itu tidak menghentikan saya. Sebaliknya, berdasarkan analisis mereka, saya membuat persyaratan tambahan yang akan menyoroti implementasi saya.


Ke depan, saya perhatikan bahwa saya menemui kesulitan jauh lebih sedikit daripada yang saya harapkan.


Pernyataan tugas


90% kesuksesan adalah pernyataan masalah yang benar. Setelah beberapa tahun bekerja dengan template, cukup sulit untuk membuat diri Anda merumuskan masalah dengan jelas. Saya hanya ingin mulai bekerja, tetapi sudah dalam proses semuanya akan beres dengan sendirinya. Setelah satu jam berjuang dengan penundaan, saya akhirnya menulis: membuat perpustakaan di PHP untuk mengimpor data FIAS.


Kemudian, setelah mencicipi, saya menambahkan beberapa persyaratan tambahan:


  • implementasi dalam PHP tanpa menggunakan utilitas pihak ketiga, secara eksklusif kode dalam PHP dan ekstensi dari PECL,
  • impor semua data dari set FIAS,
  • instalasi penuh dan siklus pembaruan: mencari versi yang diperlukan, menerima arsip, membongkar, menulis ke database,
  • fleksibilitas maksimum: kemampuan untuk mengubah lokasi penyimpanan, memodifikasi data sebelum merekam, memfilter yang diperlukan, dll.,
  • Perpustakaan harus dengan mudah diintegrasikan ke dalam proyek yang ada.

FIAS


FIAS memiliki situs web resmi yang memberi kami definisi dan tujuan membuat sistem


Sistem Informasi Alamat Federal (FIAS) adalah sistem informasi negara bagian federal yang menyediakan untuk pembentukan, pemeliharaan, dan penggunaan register alamat negara bagian.

Tujuan menciptakan FIAS adalah pembentukan sumber daya federal tunggal yang berisi informasi alamat terstruktur yang dapat diandalkan, seragam, tersedia untuk umum. Berkat implementasi FIAS, informasi ini dapat diperoleh secara gratis melalui Internet di portal FIAS yang terdaftar secara resmi.

Materi dengan deskripsi cukup memadai baik di situs web FIAS dan di HabrΓ© , oleh karena itu saya tidak akan fokus pada ini.


Singkatnya. FIAS hadir dalam dua format: FIAS dan KLADR. Yang kedua sudah usang dan tidak lagi digunakan. Informasi disimpan baik dalam DBF atau dalam XML. Setiap perubahan dalam komposisi FIAS ditandai dengan versi baru. Anda dapat meminta paket dengan data lengkap yang terkini saat ini, atau hanya berisi perubahan di antara kedua versi. Tautan menyediakan layanan SOAP. Paket ini adalah arsip RAR yang berisi file dengan nama yang dibentuk khusus. Mereka terdiri dari awalan, nama dataset, dan tanggal pembuatan. Ada dua jenis awalan: AS_ untuk file yang datanya harus ditambahkan ke database, dan AS DEL untuk file yang datanya harus dihapus dari database.


FIAS berisi data berikut:


  • daftar elemen pembentuk alamat (ini adalah grafik alamat: wilayah, kota dan jalan),
  • elemen alamat yang mengidentifikasi objek yang dapat dialamatkan (nomor rumah dan data rumah),
  • informasi tentang bidang tanah
  • informasi tentang tempat (apartemen, kantor, kamar, dll),
  • informasi tentang dokumen normatif, yang merupakan dasar untuk penugasan nama ke elemen alamat.

Serta beberapa kamus
  • daftar kemungkinan nilai interval rumah (reguler, genap, ganjil),
  • daftar status relevansi entri elemen alamat oleh classifier KLADR4.0,
  • daftar status relevansi entri elemen alamat sesuai dengan FIAS,
  • daftar lengkap, nama singkatan dari jenis elemen alamat dan tingkat klasifikasi mereka,
  • daftar jenis bangunan,
  • daftar kemungkinan jenis kepemilikan
  • daftar kode operasi dengan objek yang dapat dialamatkan,
  • daftar kemungkinan kondisi real estat,
  • daftar jenis tempat atau kantor,
  • daftar jenis kamar,
  • daftar kemungkinan status (pusat) objek alamat unit administratif,
  • jenis dokumen peraturan.

Struktur data dijelaskan dalam dokumen, yang dapat ditemukan di bagian pembaruan .


Pada akhirnya, kami memiliki algoritma instalasi FIAS yang cukup sederhana dan linier:


  • dapatkan dari layanan SOAP tautan ke arsip dan nomor versi saat ini,
  • unduh arsip,
  • membongkar
  • menulis ke database semua data dari file dengan awalan AS_,
  • hapus dari database semua data dari file dengan awalan AS DEL (ya, itu benar, selama instalasi Anda juga harus menghapus beberapa data),
  • tuliskan nomor versi yang diinstal.

Dan algoritma pembaruan tidak kalah sederhana:


  • dapatkan dari layanan SOAP daftar dengan nomor versi dan tautan ke file dengan perubahan,
  • jika versi saat ini di database lokal adalah yang terbaru, maka hentikan eksekusi,
  • dapatkan tautan ke arsip dengan perubahan ke versi berikutnya,
  • unduh arsip,
  • membongkar
  • menulis ke database semua data dari file dengan awalan AS_,
  • hapus dari database semua data dari file dengan awalan AS DEL ,
  • tuliskan nomor versi yang diperbarui,
  • kembali ke langkah pertama.

FIAS meninggalkan kesan yang bertentangan. Di satu sisi: otomatisasi penuh dari seluruh proses, format terbuka, dokumentasi yang baik. Di sisi lain: keputusan aneh untuk menggunakan RAR berpemilik untuk data terbuka; perbedaan antara dokumentasi dan kenyataan (terutama terkait dengan atribut wajib), yang menyebabkan banyak masalah kecil tetapi tidak menyenangkan; kadang-kadang arsip datang yang tidak dapat dibongkar di Linux; beberapa delta antar versi menempati 4-5 GB.


Arsitektur


Setiap perpustakaan harus didasarkan pada ide dasar, inti di mana fungsi lainnya akan tumbuh. Pola "rantai tugas" bagi saya merupakan pilihan terbaik untuk peran gagasan semacam itu. Pertama, ini ideal: beberapa operasi berurutan yang akan dilakukan seseorang jika dia ingin menginstal FIAS secara manual jelas bagi pengembang dan cocok dengan kelas kecil yang ditulis dalam gaya SOLID. Kedua, rantai seperti itu sangat mudah diperluas dengan operasi baru di hampir semua tahap, yang memberikan fleksibilitas yang baik. Ketiga, saya sudah lama ingin menulis implementasi saya sendiri.


Selain operasi, saya telah membuat beberapa layanan yang dapat ditransfer menggunakan DI. Mereka memungkinkan Anda untuk menggunakan kembali kode, dengan mudah mengganti implementasi untuk tugas-tugas sistem tingkat rendah (mengunduh file, membongkar arsip, menulis ke database dan lain-lain) dan memberikan cakupan yang baik dengan tes berkat ejekan.


Akibatnya, perpustakaan berisi empat jenis objek utama, yang masing-masing bidang tanggung jawabnya didefinisikan dengan jelas:


  • layanan - menyediakan alat untuk melakukan tugas sistem tingkat rendah,
  • objek negara - menyimpan informasi untuk transmisi antar operasi,
  • operasi - menggunakan layanan dan menyatakan mereka menerapkan bagian atom dari logika bisnis,
  • rantai operasi - melakukan operasi dan mentransfer keadaan di antara mereka.

Menggunakan tautan operasi dan layanan yang disediakan oleh perpustakaan, Anda dapat dengan mudah mendapatkan rantai baru atau menambah yang sudah ada hanya menggunakan file konfigurasi.


Kerangka kerja


Dengan istirahat panjang dan refactoring yang konstan, saya bekerja di perpustakaan selama satu setengah tahun.


Versi relatif stabil pertama sudah siap dalam dua bulan kerja di malam hari. Bahkan, itu bisa ada secara terpisah dari kerangka kerja dan berisi semua yang diperlukan: skrip input untuk dijalankan di konsol, wadah DI, add-on lebih dari PDO, migrasi logger dan struktur database sendiri - yang saya sangat bangga.


Tentu saja, rekan-rekannya menolaknya tanpa ampun.


Argumen utama yang menentang hal ini adalah kurangnya dukungan untuk kerangka kerja populer. Tidak ada yang mau menulis bungkus terpisah untuk perpustakaan. Karena itu, saya membuat kesalahan paling mahal dalam waktu: Saya mulai mendukung versi mandiri dan pembungkus individu untuk setiap kerangka kerja. File FIAS nyata berbeda dari apa yang tertulis dalam dokumentasi. Setiap kali itu perlu untuk menghapus atau menambahkan, misalnya, bukan nol dalam deskripsi kolom, saya harus membuat perubahan pada tiga repositori. Karena kebosanan proses, pekerjaan terhenti selama enam bulan.


Perasaan ketidaklengkapan menyiksa saya selama ini dan setelah pertempuran berdarah dengan kemalasan memaksa saya untuk kembali merancang versi baru. Untuk mulai dengan, saya memutuskan bahwa tidak ada yang membutuhkan perpustakaan mandiri, yang berarti Anda harus meninggalkan semua layanan yang menyediakan kerangka kerja dari paket, menggantinya dengan antarmuka. Jadi kami pergi di bawah pisau: skrip input untuk dijalankan di konsol, wadah DI, add-on lebih dari PDO, logger kami sendiri dan migrasi struktur database. Selanjutnya, saya memutuskan untuk membuat paket terpisah untuk setiap kerangka kerja, yang akan menghubungkan semua bagian dari skrip utama ke skrip yang berfungsi dan memberikan implementasi layanan yang spesifik.


Poin kuncinya adalah model. Terus-menerus memperbarui set objek heterogen di beberapa repositori tidak mau. Pada saat yang sama, di pekerjaan utama, saya mendapat proyek tentang Symfony. Setelah mengenalnya dengan cepat, saya memutuskan bahwa fitur SF yang paling berguna adalah pembuatan kode dan itu akan menyelesaikan semua masalah saya. Saya membuat file yaml di paket utama, yang berisi deskripsi deklaratif dari data FIAS. Kemudian saya menambahkan generator kode yang membuat kelas spesifik untuk model berdasarkan pada deskripsi ini: Entitas doktrin untuk objek Symfony dan Eloquent untuk Laravel. Selama pengembangan generator, saya menyadari bahwa tempelan ranting tidak cocok untuk ini, dan memilih solusi khusus - Nette PHP Generator .


Sebagai bukti konsep, saya membuat bundel untuk Laravel dan Symfony . Karena saya bekerja lebih lama dengan yang kedua, saya akan menjelaskan semuanya dalam konteksnya.


Infrastruktur


Sebagian besar proyek tempur saya ditulis pada teknologi yang sudah ketinggalan zaman, jadi saya tidak bisa menggunakan penganalisa kode modern pada salah satu dari mereka. Menyingkirkan penindasan sebelumnya, saya menginstal dan mengkonfigurasi semua alat kontrol kualitas kode yang saya dapat:



Validasi terintegrasi di Github menggunakan Travis . Sebagai sentuhan terakhir, ia menambahkan file Docker untuk membuat lingkungan pengembang lokal lengkap dengan file make yang berisi perintah dasar untuk wadah (meluncurkan pemeriksaan, tes, membuat model, dan lainnya).


Hasil belajar


PHP 7


Sebelum mulai bekerja di perpustakaan, saya tidak pernah benar-benar menggunakan fitur-fitur baru dari PHP 7 . Mereka cantik: dari tipe ketat hingga peningkatan signifikan dalam produktivitas. Terima kasih khusus kepada pengembang untuk operator penggabungan nol. Saya belum melihat penurunan serius pada basis kode setelah memperkenalkan satu operator.


Rar


Anehnya, di PECL ada paket untuk bekerja dengan RAR . Biasanya, ekstensi semacam itu tidak tepercaya dan saya mencoba menghindarinya. Itu ternyata sangat stabil: itu dipasang di 7.2 tanpa masalah, itu mampu membongkar arsip besar relatif cepat dan dengan konsumsi RAM rendah (6 GB dibongkar dalam 10-20 menit tergantung pada sumber daya sistem yang tersedia). Saya masih takut bahwa ini adalah manifestasi dari hukum Murphy.


Xmlreader


Membaca file xml raksasa bukanlah tugas yang sepele. Dan lagi ekstensi PECL datang untuk menyelamatkan - XmlReader . Saya tidak segera menyadari semua kekuatannya, tetapi dalam beberapa pendekatan saya mengadaptasinya bersamaan dengan serializer Symfony untuk secara cepat dan ekonomis memperoleh data dari file FIAS. Di sisi pustaka, objek pembaca mengimplementasikan antarmuka iterator, yang secara berurutan mengembalikan string xml yang terkait dengan satu catatan dalam file. Menggunakan serializer Symfony, string ini dikonversi menjadi objek. File 20 GB dapat dibaca dalam 3-4 menit saat menggunakan tidak lebih dari 50 MB RAM.


Menulis ke basis data


Tentu saja, saya mulai dengan array asosiatif dengan data dan deskripsi tabel besar. Kode dengan cepat berubah menjadi hash konfigurasi dan kelas konverter.


Keajaiban entitas Ajaran menunjukkan bagaimana objek dapat menggambarkan dirinya sendiri. Saya memutuskan untuk menggunakan pendekatan yang sama, tetapi pada saat yang sama menyingkirkan implementasi saya sendiri menulis data ke database menggunakan PDO. Sebagai gantinya, saya membuat antarmuka penyimpanan yang menjelaskan metode untuk memproses objek. Berdasarkan kelas entitas, implementasi Storage tertentu memutuskan dengan tepat bagaimana dan di mana harus menulis data. Pendekatan ini membuatnya mudah untuk menghubungkan berbagai penyimpanan: dari MySql ke file csv.


Optimalisasi Penyisipan Data


Saya menghentikan impor pertama setelah melebihi dalam 48 jam. Menjadi jelas bahwa Anda perlu mengoptimalkan proses memasukkan data.


Pertama, saya beralih ke kolom tipe ugid untuk kunci primer yang dibangun ke dalam PostgreSql . Menulis ke kolom uuid dengan indeks jauh lebih cepat daripada menulis ke string.


Setelah ini, saya meninggalkan semua indeks dan kunci asing yang tidak kritis, karena kepedulian terhadap integritas data sepenuhnya berada di sisi tim FIAS.


Lalu saya redid antarmuka Storage sehingga skrip eksternal dapat secara eksplisit menginformasikannya tentang penyelesaian impor. Ini memungkinkan penggunaan insert massal, yang mempercepat rekaman pada waktu-waktu tertentu. Mencari informasi, saya juga menemukan perintah copy bersama dengan query_to_xml . Itu memiliki dua kelemahan utama: pertama, pengguna PostgreSql harus membaca izin untuk file, yang saya tidak bisa menjamin, dan kedua, kemampuan untuk memodifikasi data di dalam skrip sebelum penulisan hilang.


Meskipun ada perubahan ini, waktu impor melebihi 30 jam. Diperlukan perubahan pendekatan yang radikal.


Proses paralel


Internet penuh dengan artikel tentang asinkron dalam PHP. Pilihan saya jatuh pada Amp . Itu hanya tidak berhasil secara tidak sinkron. Pertama, kode itu dengan cepat berubah menjadi lembar panggilan balik yang mengerikan dan panggilan yang tidak jelas (ini mungkin salah saya, bukan pendekatan yang tidak sinkron). Kedua, saya harus meninggalkan penggunaan ORM standar karena panggilan non-blocking ke database melalui kerangka kerja khusus diperlukan. Ketiga, meskipun ada kondisi di mana PostgreSql dapat menyisipkan baris secara paralel, mereka sangat sulit dipenuhi. Akibatnya, setelah 5 jam beroperasi, saya menyaksikan bagaimana semua permintaan asinkron saya secara paksa "disinkronkan" di sisi basis data.


Tetapi impor dibagi dengan baik ke dalam proses paralel: beberapa tugas yang sepenuhnya independen yang tidak memiliki sumber daya bersama untuk bersaing, dan data yang dapat mereka tukar. Selain itu, dalam kerangka satu utas, saya menerima kode yang indah dan linier.


Yang pertama saya memutuskan untuk mencoba ekstensi Paralel . Ini memiliki satu kesalahan fatal - penerjemah harus dibangun dengan dukungan untuk ZTS (Zend Thread Safety). Karena ZTS tidak berfungsi dalam skrip web biasa, orang harus memiliki dua versi juru bahasa yang berbeda. Satu, tanpa ZTS, untuk web, yang kedua, dengan ZTS, untuk menginstal FIAS. Peningkatan kinerja potensial melebihi ketidaknyamanan ini, terutama mengingat betapa mudahnya untuk merakit wadah Docker baru dan menggunakannya bersamaan dengan yang lama. Sayangnya, memulai Symfony di dalam utas baru menyebabkan tumpukan PHP meluap, dan saya tidak siap untuk menolak wadah DI dan konfigurasi yang mudah.


Akhirnya, saya menemukan proses symfony . Bahkan, ia memulai proses baru untuk perintah konsol yang ditentukan dan memonitor penyelesaiannya. Saya harus menambahkan dua rantai tambahan. Yang pertama mengunduh arsip, membongkar dan memulai proses paralel untuk pemrosesan data. Yang kedua mengambil daftar file dari argumen baris perintah dan menulis kontennya ke database.


Karena kurangnya pengalaman dengan proses paralel, saya tampaknya telah membuat semua kesalahan pemula.


Sebagai contoh, proses inisiasi saya memeriksa penyelesaian anak-anak menggunakan loop tak terbatas dan, tentu saja, menghabiskan banyak sumber daya prosesor untuk hal ini. Panggilan tidur antara iterasi membantu.


Dalam implementasi pertama, file didistribusikan secara tidak merata di antara proses. Dua terbesar jatuh ke dalam satu aliran, yang diproses selama lebih dari 20 jam. Dalam implementasi kedua, saya menambahkan manajer khusus yang mendistribusikan file berdasarkan waktu yang diperlukan untuk mengimpornya. Sekarang proses dimuat secara merata.


Setelah pengeditan ini, saya dapat mengimpor versi lengkap FIAS dalam 16-20 jam, tergantung pada sumber daya server. Tidak sebagus yang kita inginkan, tetapi saya terus bekerja pada optimasi. Langkah selanjutnya adalah penolakan lengkap PostgreSql yang mendukung Elasticsearch.


Kesimpulan


Apakah itu sepadan? Dua tahun bekerja di perpustakaan yang tidak pernah masuk ke proyek pertempuran apa pun?


Ya, sepenuhnya.


Saya mengubah pekerjaan saya. Selama tur selusin wawancara, saya menjawab banyak pertanyaan rumit hanya berkat proyek kesayangan saya.


Kepanikan bahwa PHP sedang sekarat semakin kuat. Saya tidak akan menyembunyikan fakta bahwa saya sendiri berpikir tentang migrasi ke bahasa lain.


Setelah saya melihat pekerjaan besar yang dimasukkan tim PHP ke versi 7; dia diyakinkan oleh teladan pribadi betapa matangnya bahasa itu dan betapa kaya ekosistem itu telah tumbuh; Saya bisa mengatakan bahwa rumor tentang kematian PHP sangat dibesar-besarkan. Dan ini baru permulaan: di masa depan kita sedang menunggu JIT, tidak sinkron di luar kotak dan banyak lagi.

Source: https://habr.com/ru/post/id479122/


All Articles