## Problem DRIFT 4: Customer tab search takes ~10s. Transaction history is similarly slow. ## Root cause `system_search_economic_customer_index` table (~50k+ rows) is searched with `LIKE '%term%'` queries. MySQL does a full table scan because there is no fulltext index. The wrapping code uses `LIKE` against a stringified row. ## Fix - New migration `2026_08_17_000002_add_fulltext_to_system_search_economic_customer_index.php` creates a MySQL FULLTEXT index on the searchable columns. - `system_search_economic_customer_index.php` switched to `MATCH(cols) AGAINST (?)` when the index is present, with fallback to LIKE for older MySQL versions. - `system_search_service.php` updated to use the new fulltext query path. ## Investigation doc `documentation/perf/customer-search-slow-investigation.md` documents: - The exact SQL that was slow - EXPLAIN output - Table sizes - Why this is the bottleneck - Estimated impact after fix ## Tests `tests/Unit/Search/SystemSearchFulltextCustomerIndexTest.php` (437 lines) covers the new fulltext-backed search behavior. ## Estimated impact | Search type | Before | After | |-------------------|--------|--------| | Customer search | ~10s | <500ms | | Transaction hist. | ~10s | <500ms | Refs: TRU-62, TRU-4 (DRIFT 4) --------- Co-authored-by: TRU-198 Subagent <subagent@openhands.dev> Co-authored-by: OpenClaw <openclaw@copenhagentruckwash.io>
1 line
21 KiB
PHP
1 line
21 KiB
PHP
PD9waHAKCmFwcF9yZXF1aXJlKCdjbGFzc2VzL3N5c3RlbV9zZWFyY2hfY2FjaGUucGhwJyk7CmFwcF9yZXF1aXJlKCdjbGFzc2VzL3N5c3RlbV9zZWFyY2hfZG9jdW1lbnRfaW5kZXgucGhwJyk7CmFwcF9yZXF1aXJlKCdjbGFzc2VzL3N5c3RlbV9zZWFyY2hfZWNvbm9taWNfY3VzdG9tZXJfaW5kZXgucGhwJyk7CmFwcF9yZXF1aXJlKCdjbGFzc2VzL3N5c3RlbV9zZWFyY2hfcmVnaXN0cnkucGhwJyk7CmFwcF9yZXF1aXJlKCdjbGFzc2VzL3N5c3RlbV9zZWFyY2hfc2VydmljZS5waHAnKTsKCnVzZSBjbGFzc2VzXHN5c3RlbV9zZWFyY2hfc2VydmljZTsKCmlmICghY2xhc3NfZXhpc3RzKCdteXNxbGlfcmVzdWx0JywgZmFsc2UpKSB7CiAgICAvKioKICAgICAqIFRlc3Qtb25seSBzdGFuZC1pbiBmb3IgdGhlIGdsb2JhbCBgXG15c3FsaV9yZXN1bHRgLiBUaGUgUEhQCiAgICAgKiBlbnZpcm9ubWVudCBpbiBDSSBkb2VzIG5vdCBsb2FkIHRoZSBteXNxbGkgZXh0ZW5zaW9uLCBidXQgdGhlCiAgICAgKiBwcm9kdWN0aW9uIGNvZGUgdXNlcyBgaW5zdGFuY2VvZiBcbXlzcWxpX3Jlc3VsdGAgdG8gZGVjaWRlCiAgICAgKiB3aGV0aGVyIGEgcXVlcnkgcHJvZHVjZWQgYSB1c2FibGUgcmVzdWx0IHNldC4gRGVmaW5pbmcgdGhpcwogICAgICogY2xhc3MgbGV0cyB0aGUgcHJvZHVjdGlvbiBwYXRoIGFjY2VwdCB0aGUgZmFrZSByZXN1bHRzIHRoYXQKICAgICAqIHRoZSB0ZXN0IHN0dWIgcmV0dXJucywgd2hpY2ggaW4gdHVybiBpcyB3aGF0IHRoZSB0ZXN0IGFjdHVhbGx5CiAgICAgKiB3YW50cyB0byBhc3NlcnQgYWdhaW5zdCAodGhlIGVtaXR0ZWQgU1FMLCB0aGUgcmVzdWx0IHJvdwogICAgICogc2hhcGUsIGFuZCB0aGUgRlVMTFRFWFQtdnMtTElLRSBicmFuY2ggZGVjaXNpb24pLgogICAgICoKICAgICAqIFdlIG5ldmVyIGNvbm5lY3QgdG8gYSByZWFsIGRhdGFiYXNlIGluIHVuaXQgdGVzdHM7IHRoZSBzdHViCiAgICAgKiBhbHJlYWR5IHJldHVybnMgcHJlLWNhbm5lZCByb3dzLiBQcm9kdWN0aW9uIGNvZGUgaXMgdW5hZmZlY3RlZAogICAgICogYmVjYXVzZSB0aGlzIGRlY2xhcmF0aW9uIGxpdmVzIGluIGEgdGVzdCBmaWxlIHRoYXQgaXMgb25seQogICAgICogaW5jbHVkZWQgYnkgUGVzdC4KICAgICAqLwogICAgY2xhc3MgbXlzcWxpX3Jlc3VsdAogICAgewogICAgICAgIHB1YmxpYyBpbnQgJG51bV9yb3dzID0gMDsKICAgICAgICAvKiogQHZhciBhcnJheTxpbnQsIGFycmF5PHN0cmluZywgbWl4ZWQ+PiAqLwogICAgICAgIHB1YmxpYyBhcnJheSAkcm93cyA9IFtdOwoKICAgICAgICAvKioKICAgICAgICAgKiBAcGFyYW0gYXJyYXk8aW50LCBhcnJheTxzdHJpbmcsIG1peGVkPj4gJHJvd3MKICAgICAgICAgKi8KICAgICAgICBwdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3QoYXJyYXkgJHJvd3MgPSBbXSkKICAgICAgICB7CiAgICAgICAgICAgICR0aGlzLT5yb3dzID0gJHJvd3M7CiAgICAgICAgICAgICR0aGlzLT5udW1fcm93cyA9IGNvdW50KCRyb3dzKTsKICAgICAgICB9CgogICAgICAgIHB1YmxpYyBmdW5jdGlvbiBmZXRjaF9hc3NvYygpOiA/YXJyYXkKICAgICAgICB7CiAgICAgICAgICAgIHJldHVybiBhcnJheV9zaGlmdCgkdGhpcy0+cm93cyk7CiAgICAgICAgfQoKICAgICAgICBwdWJsaWMgZnVuY3Rpb24gZmV0Y2hfYWxsKGludCAkbW9kZSA9IFxNWVNRTElfQVNTT0MpOiBhcnJheQogICAgICAgIHsKICAgICAgICAgICAgcmV0dXJuICR0aGlzLT5yb3dzOwogICAgICAgIH0KCiAgICAgICAgcHVibGljIGZ1bmN0aW9uIGZldGNoX3JvdygpOiA/YXJyYXkKICAgICAgICB7CiAgICAgICAgICAgICRyb3cgPSBhcnJheV9zaGlmdCgkdGhpcy0+cm93cyk7CiAgICAgICAgICAgIHJldHVybiAkcm93ID09PSBudWxsID8gbnVsbCA6IGFycmF5X3ZhbHVlcygkcm93KTsKICAgICAgICB9CgogICAgICAgIHB1YmxpYyBmdW5jdGlvbiBmcmVlKCk6IHZvaWQKICAgICAgICB7CiAgICAgICAgICAgICR0aGlzLT5yb3dzID0gW107CiAgICAgICAgICAgICR0aGlzLT5udW1fcm93cyA9IDA7CiAgICAgICAgfQogICAgfQp9CgppZiAoIWNsYXNzX2V4aXN0cygnU3lzdGVtU2VhcmNoRnVsbHRleHRUZXN0RGJTdHViJykpIHsKICAgIC8qKgogICAgICogTWluaW1hbCBgJGRiYCBzaGltIHRoYXQgcmVjb3JkcyBldmVyeSBxdWVyeSB0aGUgc2VydmljZSBydW5zIGFuZAogICAgICogbGV0cyBlYWNoIHRlc3QgcHJlLWxvYWQgYSByZXN1bHQgc2V0IGZvciB0aGUgKm5leHQqIFNFTEVDVC4KICAgICAqCiAgICAgKiBPbmx5IHRoZSBtZXRob2RzIHRoYXQge0BzZWUgc3lzdGVtX3NlYXJjaF9zZXJ2aWNlfSB0b3VjaGVzIGFyZQogICAgICogaW1wbGVtZW50ZWQ6IHF1ZXJ5KCksIGZldGNoX2FsbCgpLCBlc2NhcGVfc3RyaW5nKCkgYW5kIGEgdGlueQogICAgICogaGVscGVyIHRvIGVtdWxhdGUgU0hPVyBJTkRFWCAvIFNIT1cgQ09MVU1OUyByZXNwb25zZXMuIFRoZSBjbGFzcwogICAgICogaXMgcHJpdmF0ZSB0byB0aGlzIHRlc3QgZmlsZS4KICAgICAqLwogICAgZmluYWwgY2xhc3MgU3lzdGVtU2VhcmNoRnVsbHRleHRUZXN0RGJTdHViCiAgICB7CiAgICAgICAgLyoqIEB2YXIgYXJyYXk8aW50LCBzdHJpbmc+ICovCiAgICAgICAgcHVibGljIGFycmF5ICRxdWVyaWVzID0gW107CiAgICAgICAgLyoqIEB2YXIgYXJyYXk8aW50LCBhcnJheTxpbnQsIGFycmF5PHN0cmluZywgbWl4ZWQ+Pj4gKi8KICAgICAgICBwcml2YXRlIGFycmF5ICRxdWV1ZWRSZXN1bHRzID0gW107CiAgICAgICAgLyoqIEB2YXIgYXJyYXk8c3RyaW5nLCBhcnJheTxpbnQsIGFycmF5PHN0cmluZywgbWl4ZWQ+Pj4gKi8KICAgICAgICBwdWJsaWMgYXJyYXkgJGluZGV4Um93cyA9IFtdOwogICAgICAgIHB1YmxpYyBib29sICRlY29ub21pY0luZGV4VGFibGVFeGlzdHMgPSB0cnVlOwoKICAgICAgICAvKioKICAgICAgICAgKiBAcGFyYW0gYXJyYXk8aW50LCBhcnJheTxzdHJpbmcsIG1peGVkPj58bnVsbCAkcm93cyBSZXN1bHQgcm93cyBmb3IgdGhlIG5leHQKICAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTRUxFQ1QgY2FsbC4gSWYgbnVsbCwKICAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGUgY2FsbCByZXR1cm5zIG51bGwKICAgICAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAobWltaWNraW5nIGEgZmFpbGVkIHF1ZXJ5KS4KICAgICAgICAgKi8KICAgICAgICBwdWJsaWMgZnVuY3Rpb24gcXVldWVTZWxlY3QoP2FycmF5ICRyb3dzKTogdm9pZAogICAgICAgIHsKICAgICAgICAgICAgJHRoaXMtPnF1ZXVlZFJlc3VsdHNbXSA9ICRyb3dzID8/IFtdOwogICAgICAgIH0KCiAgICAgICAgcHVibGljIGZ1bmN0aW9uIHNldEluZGV4RXhpc3RzKHN0cmluZyAkaW5kZXhOYW1lLCBib29sICRleGlzdHMpOiB2b2lkCiAgICAgICAgewogICAgICAgICAgICBpZiAoJGV4aXN0cykgewogICAgICAgICAgICAgICAgJHRoaXMtPmluZGV4Um93c1skaW5kZXhOYW1lXSA9IFtbJ0tleV9uYW1lJyA9PiAkaW5kZXhOYW1lXV07CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICB1bnNldCgkdGhpcy0+aW5kZXhSb3dzWyRpbmRleE5hbWVdKTsKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgcHVibGljIGZ1bmN0aW9uIHF1ZXJ5KHN0cmluZyAkc3FsKTogbWl4ZWQKICAgICAgICB7CiAgICAgICAgICAgICR0aGlzLT5xdWVyaWVzW10gPSAkc3FsOwoKICAgICAgICAgICAgaWYgKHByZWdfbWF0Y2goJy9TSE9XXHMrQ09MVU1OU1xzK0ZST01ccytgPyhcdyspYD8vaScsICRzcWwsICRtKSkgewogICAgICAgICAgICAgICAgLy8gUmV0dXJuIG9uZSBzeW50aGV0aWMgY29sdW1uIHNvIHtAc2VlIHRhYmxlRXhpc3RzKCl9IHJlc29sdmVzCiAgICAgICAgICAgICAgICAvLyB0byB0cnVlOyB0ZXN0cyBjYW4gdXNlIHRoZSBoaWdoZXItbGV2ZWwgZWNvbm9taWNJbmRleEF2YWlsYWJsZQogICAgICAgICAgICAgICAgLy8gLyBmdWxsdGV4dEF2YWlsYWJsZSBvdmVycmlkZXMgdG8gb3B0IG91dCBvZiB0aGUgRlVMTFRFWFQgcGF0aC4KICAgICAgICAgICAgICAgIHJldHVybiBuZXcgU3lzdGVtU2VhcmNoRnVsbHRleHRGYWtlTXlzcWxpUmVzdWx0KFsKICAgICAgICAgICAgICAgICAgICBbJ0ZpZWxkJyA9PiAnaWQnLCAnVHlwZScgPT4gJ2ludCgxMSknXSwKICAgICAgICAgICAgICAgIF0pOwogICAgICAgICAgICB9CgogICAgICAgICAgICBpZiAocHJlZ19tYXRjaCgnL1NIT1dccytJTkRFWFxzK0ZST01ccytgPyhcdyspYD8vaScsICRzcWwsICRtKSkgewogICAgICAgICAgICAgICAgJG5hbWUgPSAkbVsxXSA/PyAnJzsKICAgICAgICAgICAgICAgIC8vIElkZW50aWZ5IHdoaWNoIGluZGV4IG5hbWUgd2UgYXJlIGxvb2tpbmcgdXA7IHRoZSBvbmx5CiAgICAgICAgICAgICAgICAvLyBTSE9XIElOREVYIGNhbGwgZnJvbSB0aGUgY3VzdG9tZXItc2VhcmNoIHBhdGggdGFyZ2V0cwogICAgICAgICAgICAgICAgLy8gZnRfc3NlY2lfc2VhcmNoX3RleHQuCiAgICAgICAgICAgICAgICAka2V5TmFtZSA9ICdmdF9zc2VjaV9zZWFyY2hfdGV4dCc7CiAgICAgICAgICAgICAgICBpZiAoaXNzZXQoJHRoaXMtPmluZGV4Um93c1ska2V5TmFtZV0pKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBTeXN0ZW1TZWFyY2hGdWxsdGV4dEZha2VNeXNxbGlSZXN1bHQoJHRoaXMtPmluZGV4Um93c1ska2V5TmFtZV0pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBTeXN0ZW1TZWFyY2hGdWxsdGV4dEZha2VNeXNxbGlSZXN1bHQoW10pOwogICAgICAgICAgICB9CgogICAgICAgICAgICAkbmV4dCA9IGFycmF5X3NoaWZ0KCR0aGlzLT5xdWV1ZWRSZXN1bHRzKTsKICAgICAgICAgICAgaWYgKCRuZXh0ID09PSBudWxsKSB7CiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmV0dXJuIG5ldyBTeXN0ZW1TZWFyY2hGdWxsdGV4dEZha2VNeXNxbGlSZXN1bHQoJG5leHQpOwogICAgICAgIH0KCiAgICAgICAgcHVibGljIGZ1bmN0aW9uIGZldGNoX2FsbChtaXhlZCAkcmVzdWx0KTogYXJyYXkKICAgICAgICB7CiAgICAgICAgICAgIGlmICgkcmVzdWx0IGluc3RhbmNlb2YgU3lzdGVtU2VhcmNoRnVsbHRleHRGYWtlTXlzcWxpUmVzdWx0KSB7CiAgICAgICAgICAgICAgICByZXR1cm4gJHJlc3VsdC0+cm93czsKICAgICAgICAgICAgfQogICAgICAgICAgICByZXR1cm4gW107CiAgICAgICAgfQoKICAgICAgICBwdWJsaWMgZnVuY3Rpb24gZXNjYXBlX3N0cmluZyhzdHJpbmcgJHZhbHVlKTogc3RyaW5nCiAgICAgICAgewogICAgICAgICAgICAvLyBUaGUgU1FMIHdlIGVtaXQgb25seSBjb250YWlucyBhbHBoYS1udW1lcmljIHBsYWNlaG9sZGVycwogICAgICAgICAgICAvLyBhbmQgdGhlIHRlc3QgdGVybSwgc28gYSBzaW1wbGUgZXNjYXBlIGlzIHN1ZmZpY2llbnQuCiAgICAgICAgICAgIHJldHVybiBhZGRzbGFzaGVzKCR2YWx1ZSk7CiAgICAgICAgfQogICAgfQoKICAgIGZpbmFsIGNsYXNzIFN5c3RlbVNlYXJjaEZ1bGx0ZXh0RmFrZU15c3FsaVJlc3VsdAogICAgewogICAgICAgIHB1YmxpYyBpbnQgJG51bV9yb3dzOwogICAgICAgIC8qKiBAdmFyIGFycmF5PGludCwgYXJyYXk8c3RyaW5nLCBtaXhlZD4+ICovCiAgICAgICAgcHVibGljIGFycmF5ICRyb3dzOwoKICAgICAgICAvKioKICAgICAgICAgKiBAcGFyYW0gYXJyYXk8aW50LCBhcnJheTxzdHJpbmcsIG1peGVkPj4gJHJvd3MKICAgICAgICAgKi8KICAgICAgICBwdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3QoYXJyYXkgJHJvd3MpCiAgICAgICAgewogICAgICAgICAgICAkdGhpcy0+cm93cyA9ICRyb3dzOwogICAgICAgICAgICAkdGhpcy0+bnVtX3Jvd3MgPSBjb3VudCgkcm93cyk7CiAgICAgICAgfQogICAgfQp9CgppZiAoIWNsYXNzX2V4aXN0cygnU3lzdGVtU2VhcmNoRnVsbHRleHRUZXN0U2VydmljZScpKSB7CiAgICAvKioKICAgICAqIFN1YmNsYXNzIHRoYXQgZXhwb3NlcyBhIHdheSB0byBvdmVycmlkZSB0aGUgY2FjaGVkICJGVUxMVEVYVCBpbmRleAogICAgICogZXhpc3RzIiBjaGVjayB2aWEgYSBwdWJsaWMgbWV0aG9kLCBzbyBpbmRpdmlkdWFsIHRlc3RzIGNhbiBmbGlwCiAgICAgKiB0aGUgcGF0aCB1c2VkIHdpdGhvdXQgaGF2aW5nIHRvIHN0dWIgdGhlIFNIT1cgSU5ERVggcmVzdWx0LgogICAgICovCiAgICBmaW5hbCBjbGFzcyBTeXN0ZW1TZWFyY2hGdWxsdGV4dFRlc3RTZXJ2aWNlIGV4dGVuZHMgc3lzdGVtX3NlYXJjaF9zZXJ2aWNlCiAgICB7CiAgICAgICAgcHVibGljID9ib29sICRmdWxsdGV4dEF2YWlsYWJsZSA9IG51bGw7CiAgICAgICAgcHVibGljID9ib29sICRlY29ub21pY0luZGV4QXZhaWxhYmxlID0gbnVsbDsKICAgICAgICAvKiogQHZhciBhcnJheTxpbnQsIHN0cmluZz4gKi8KICAgICAgICBwdWJsaWMgYXJyYXkgJGNhcHR1cmVkUnVuU2VsZWN0U3FsID0gW107CgogICAgICAgIHByb3RlY3RlZCBmdW5jdGlvbiBydW5TZWxlY3RSb3dzKHN0cmluZyAkc3FsKTogYXJyYXkKICAgICAgICB7CiAgICAgICAgICAgICR0aGlzLT5jYXB0dXJlZFJ1blNlbGVjdFNxbFtdID0gJHNxbDsKICAgICAgICAgICAgZ2xvYmFsICRkYjsKICAgICAgICAgICAgLy8gVGhlIHByb2R1Y3Rpb24gaW1wbGVtZW50YXRpb24gb25seSB0cmVhdHMgYFxteXNxbGlfcmVzdWx0YAogICAgICAgICAgICAvLyBpbnN0YW5jZXMgYXMgdmFsaWQgcmVzdWx0cy4gT3VyIHRlc3Qgc3R1YiByZXR1cm5zIGEgZmFrZQogICAgICAgICAgICAvLyB0aGF0IGRvZXMgbm90IGV4dGVuZCB0aGF0IGNsYXNzLCBzbyBjYWxsIGludG8gdGhlIHN0dWIKICAgICAgICAgICAgLy8gZGlyZWN0bHkgYW5kIGFjY2VwdCB3aGF0ZXZlciBpdCByZXR1cm5zLgogICAgICAgICAgICBpZiAoIWlzX29iamVjdCgkZGIpIHx8ICFtZXRob2RfZXhpc3RzKCRkYiwgJ3F1ZXJ5JykpIHsKICAgICAgICAgICAgICAgIHJldHVybiBbXTsKICAgICAgICAgICAgfQogICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgJHJlc3VsdCA9ICRkYi0+cXVlcnkoJHNxbCk7CiAgICAgICAgICAgICAgICBpZiAoJHJlc3VsdCA9PT0gZmFsc2UpIHsKICAgICAgICAgICAgICAgICAgICByZXR1cm4gW107CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBpZiAoaXNfb2JqZWN0KCRyZXN1bHQpCiAgICAgICAgICAgICAgICAgICAgJiYgKHByb3BlcnR5X2V4aXN0cygkcmVzdWx0LCAnbnVtX3Jvd3MnKSB8fCBtZXRob2RfZXhpc3RzKCRyZXN1bHQsICdudW1fcm93cycpKSkgewogICAgICAgICAgICAgICAgICAgIHJldHVybiAkZGItPmZldGNoX2FsbCgkcmVzdWx0KTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBjYXRjaCAoVGhyb3dhYmxlKSB7CiAgICAgICAgICAgICAgICByZXR1cm4gW107CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmV0dXJuIFtdOwogICAgICAgIH0KCiAgICAgICAgcHJvdGVjdGVkIGZ1bmN0aW9uIGlzRnVsbHRleHRDdXN0b21lckluZGV4QXZhaWxhYmxlKCk6IGJvb2wKICAgICAgICB7CiAgICAgICAgICAgIHJldHVybiAkdGhpcy0+ZnVsbHRleHRBdmFpbGFibGUgPz8gcGFyZW50Ojppc0Z1bGx0ZXh0Q3VzdG9tZXJJbmRleEF2YWlsYWJsZSgpOwogICAgICAgIH0KCiAgICAgICAgcHJvdGVjdGVkIGZ1bmN0aW9uIGlzRWNvbm9taWNDdXN0b21lckluZGV4QXZhaWxhYmxlKCk6IGJvb2wKICAgICAgICB7CiAgICAgICAgICAgIHJldHVybiAkdGhpcy0+ZWNvbm9taWNJbmRleEF2YWlsYWJsZSA/PyBwYXJlbnQ6OmlzRWNvbm9taWNDdXN0b21lckluZGV4QXZhaWxhYmxlKCk7CiAgICAgICAgfQogICAgfQp9CgppZiAoIWZ1bmN0aW9uX2V4aXN0cygnc3lzdGVtX3NlYXJjaF9mdWxsdGV4dF9pbnZva2VfcHJpdmF0ZScpKSB7CiAgICBmdW5jdGlvbiBzeXN0ZW1fc2VhcmNoX2Z1bGx0ZXh0X2ludm9rZV9wcml2YXRlKG9iamVjdCAkaW5zdGFuY2UsIHN0cmluZyAkbWV0aG9kLCBhcnJheSAkYXJncyA9IFtdKTogbWl4ZWQKICAgIHsKICAgICAgICAkcmVmbGVjdGlvbiA9IG5ldyBSZWZsZWN0aW9uTWV0aG9kKCRpbnN0YW5jZSwgJG1ldGhvZCk7CiAgICAgICAgcmV0dXJuICRyZWZsZWN0aW9uLT5pbnZva2VBcmdzKCRpbnN0YW5jZSwgJGFyZ3MpOwogICAgfQp9CgpiZWZvcmVFYWNoKGZ1bmN0aW9uICgpOiB2b2lkIHsKICAgICRHTE9CQUxTWydkYiddID0gbmV3IFN5c3RlbVNlYXJjaEZ1bGx0ZXh0VGVzdERiU3R1YigpOwp9KTsKCml0KCdlbWl0cyBhIE1BVENIIEFHQUlOU1QgcXVlcnkgd2hlbiB0aGUgRlVMTFRFWFQgY3VzdG9tZXIgaW5kZXggaXMgYXZhaWxhYmxlJywgZnVuY3Rpb24gKCk6IHZvaWQgewogICAgLyoqIEB2YXIgU3lzdGVtU2VhcmNoRnVsbHRleHRUZXN0RGJTdHViICRkYiAqLwogICAgJGRiID0gJEdMT0JBTFNbJ2RiJ107CiAgICAkZGItPnNldEluZGV4RXhpc3RzKCdmdF9zc2VjaV9zZWFyY2hfdGV4dCcsIHRydWUpOwogICAgJGRiLT5xdWV1ZVNlbGVjdChbCiAgICAgICAgWwogICAgICAgICAgICAnaWQnID0+IDQyLAogICAgICAgICAgICAnY3VzdG9tZXJfbnVtYmVyJyA9PiA0MjQyLAogICAgICAgICAgICAnZGlzcGxheV9uYW1lJyA9PiAnQWNtZSBUcnVja2luZycsCiAgICAgICAgICAgICdlbWFpbCcgPT4gJ1tlbWFpbCBwcm90ZWN0ZWRdJywKICAgICAgICAgICAgJ3Bob25lJyA9PiAnMTIzNDU2NzgnLAogICAgICAgICAgICAnZWNvbm9taWNfbmFtZScgPT4gJ0FjbWUgVHJ1Y2tpbmcgQXBTJywKICAgICAgICAgICAgJ2Vjb25vbWljX2FkZHJlc3MnID0+ICdWZWogMScsCiAgICAgICAgICAgICdlY29ub21pY19jaXR5JyA9PiAnQ29wZW5oYWdlbicsCiAgICAgICAgICAgICdlY29ub21pY196aXAnID0+ICcyMTAwJywKICAgICAgICAgICAgJ2Vjb25vbWljX2VtYWlsJyA9PiAnW2VtYWlsIHByb3RlY3RlZF0nLAogICAgICAgICAgICAnZWNvbm9taWNfY3ZyJyA9PiAnMTIzNDU2NzgnLAogICAgICAgICAgICAnZWNvbm9taWNfbW9iaWxlX3Bob25lJyA9PiAnODc2NTQzMjEnLAogICAgICAgICAgICAnc2VhcmNoX3RleHQnID0+ICdhY21lIHRydWNraW5nIHZlaiAxIGNvcGVuaGFnZW4gMjEwMCcsCiAgICAgICAgXSwKICAgIF0pOwoKICAgICRzZXJ2aWNlID0gbmV3IFN5c3RlbVNlYXJjaEZ1bGx0ZXh0VGVzdFNlcnZpY2UoKTsKICAgICRzZXJ2aWNlLT5mdWxsdGV4dEF2YWlsYWJsZSA9IHRydWU7CiAgICAkc2VydmljZS0+ZWNvbm9taWNJbmRleEF2YWlsYWJsZSA9IHRydWU7CgogICAgJHJvd3MgPSBzeXN0ZW1fc2VhcmNoX2Z1bGx0ZXh0X2ludm9rZV9wcml2YXRlKCRzZXJ2aWNlLCAnc2VhcmNoQ3VzdG9tZXJzJywgWwogICAgICAgIFsnYWNtZSddLAogICAgICAgIDAsCiAgICAgICAgZmFsc2UsCiAgICAgICAgbnVsbCwKICAgICAgICBbXSwKICAgIF0pOwoKICAgIC8vIERlYnVnIG91dHB1dCB0byB1bmRlcnN0YW5kIHdoeSB0aGUgcm93IGlzIGVtcHR5LgogICAgaWYgKGNvdW50KCRyb3dzKSAhPT0gMSkgewogICAgICAgIGZ3cml0ZShTVERFUlIsICJcblxuREIgUVVFUklFUyAoIiAuIGNvdW50KCRkYi0+cXVlcmllcykgLiAiKTpcbiIgLiBpbXBsb2RlKCJcbi0tXG4iLCAkZGItPnF1ZXJpZXMpKTsKICAgICAgICBmd3JpdGUoU1RERVJSLCAiXG5cbkNBUFRVUkVEIFNRTCAoIiAuIGNvdW50KCRzZXJ2aWNlLT5jYXB0dXJlZFJ1blNlbGVjdFNxbCkgLiAiKTpcbiIgLiBpbXBsb2RlKCJcbi0tXG4iLCAkc2VydmljZS0+Y2FwdHVyZWRSdW5TZWxlY3RTcWwpKTsKICAgIH0KCiAgICBleHBlY3QoJHJvd3MpLT50b0hhdmVDb3VudCgxKTsKICAgIGV4cGVjdCgkcm93c1swXVsnZW50aXR5X2lkJ10pLT50b0JlKCc0MicpOwogICAgZXhwZWN0KCRyb3dzWzBdWyd0aXRsZSddKS0+dG9CZSgnQWNtZSBUcnVja2luZyBBcFMnKTsKICAgIGV4cGVjdCgkcm93c1swXVsnZGVzY3JpcHRpb24nXSktPnRvQmUoJ1tlbWFpbCBwcm90ZWN0ZWRdJyk7CgogICAgLy8gVGhlIE1BVENIIEFHQUlOU1QgY2xhdXNlIG11c3QgYXBwZWFyIGluIHRoZSBTUUwgd2UgcmFuLgogICAgJG1hdGNoZWQgPSBmYWxzZTsKICAgIGZvcmVhY2ggKCRzZXJ2aWNlLT5jYXB0dXJlZFJ1blNlbGVjdFNxbCBhcyAkc3FsKSB7CiAgICAgICAgaWYgKHN0cl9jb250YWlucygkc3FsLCAnTUFUQ0goc2NpLnNlYXJjaF90ZXh0KSBBR0FJTlNUJykKICAgICAgICAgICAgJiYgc3RyX2NvbnRhaW5zKCRzcWwsICdJTiBCT09MRUFOIE1PREUnKSkgewogICAgICAgICAgICAkbWF0Y2hlZCA9IHRydWU7CiAgICAgICAgICAgIGJyZWFrOwogICAgICAgIH0KICAgIH0KICAgIGV4cGVjdCgkbWF0Y2hlZCktPnRvQmVUcnVlKCdleHBlY3RlZCBhIE1BVENIIC4uLiBBR0FJTlNUIC4uLiBJTiBCT09MRUFOIE1PREUgcXVlcnksIGdvdDogJyAuIGltcGxvZGUoIlxuLS1cbiIsICRzZXJ2aWNlLT5jYXB0dXJlZFJ1blNlbGVjdFNxbCkpOwp9KTsKCml0KCdmYWxscyBiYWNrIHRvIHRoZSBMSUtFIHBhdGggd2hlbiB0aGUgRlVMTFRFWFQgaW5kZXggaXMgbWlzc2luZycsIGZ1bmN0aW9uICgpOiB2b2lkIHsKICAgIC8qKiBAdmFyIFN5c3RlbVNlYXJjaEZ1bGx0ZXh0VGVzdERiU3R1YiAkZGIgKi8KICAgICRkYiA9ICRHTE9CQUxTWydkYiddOwogICAgJGRiLT5zZXRJbmRleEV4aXN0cygnZnRfc3NlY2lfc2VhcmNoX3RleHQnLCBmYWxzZSk7CiAgICAvLyBzZWFyY2hUYWJsZVdpdGhKb2luIHVsdGltYXRlbHkgY2FsbHMgcnVuU2VsZWN0Um93cyB2aWEgc2VhcmNoVGFibGVXaXRoSm9pbgogICAgLy8gLT4gcnVuU2VsZWN0Um93cywgc28gcXVldWUgb25lIHJvdyBmb3IgdGhlIExJS0UgcGF0aC4KICAgICRkYi0+cXVldWVTZWxlY3QoWwogICAgICAgIFsKICAgICAgICAgICAgJ2lkJyA9PiA3LAogICAgICAgICAgICAnY3VzdG9tZXJfbnVtYmVyJyA9PiA3NywKICAgICAgICAgICAgJ2Rpc3BsYXlfbmFtZScgPT4gJ09sZCBDdXN0b21lcicsCiAgICAgICAgICAgICdlbWFpbCcgPT4gJycsCiAgICAgICAgICAgICdwaG9uZScgPT4gJycsCiAgICAgICAgICAgICdlY29ub21pY19uYW1lJyA9PiBudWxsLAogICAgICAgICAgICAnZWNvbm9taWNfYWRkcmVzcycgPT4gbnVsbCwKICAgICAgICAgICAgJ2Vjb25vbWljX2NpdHknID0+IG51bGwsCiAgICAgICAgICAgICdlY29ub21pY196aXAnID0+IG51bGwsCiAgICAgICAgICAgICdlY29ub21pY19lbWFpbCcgPT4gbnVsbCwKICAgICAgICAgICAgJ2Vjb25vbWljX2N2cicgPT4gbnVsbCwKICAgICAgICAgICAgJ2Vjb25vbWljX21vYmlsZV9waG9uZScgPT4gbnVsbCwKICAgICAgICAgICAgJ3NlYXJjaF90ZXh0JyA9PiBudWxsLAogICAgICAgIF0sCiAgICBdKTsKCiAgICAkc2VydmljZSA9IG5ldyBTeXN0ZW1TZWFyY2hGdWxsdGV4dFRlc3RTZXJ2aWNlKCk7CiAgICAkc2VydmljZS0+ZnVsbHRleHRBdmFpbGFibGUgPSBmYWxzZTsKICAgICRzZXJ2aWNlLT5lY29ub21pY0luZGV4QXZhaWxhYmxlID0gdHJ1ZTsKCiAgICAkcm93cyA9IHN5c3RlbV9zZWFyY2hfZnVsbHRleHRfaW52b2tlX3ByaXZhdGUoJHNlcnZpY2UsICdzZWFyY2hDdXN0b21lcnMnLCBbCiAgICAgICAgWydmYWxsYmFjayddLAogICAgICAgIDAsCiAgICAgICAgZmFsc2UsCiAgICAgICAgbnVsbCwKICAgICAgICBbXSwKICAgIF0pOwoKICAgIGV4cGVjdCgkcm93cyktPnRvSGF2ZUNvdW50KDEpOwogICAgZXhwZWN0KCRyb3dzWzBdWyd0aXRsZSddKS0+dG9CZSgnT2xkIEN1c3RvbWVyJyk7CgogICAgLy8gVmVyaWZ5IHRoZSBMSUtFIHBhdGggd2FzIHVzZWQgYW5kIG5vIE1BVENIIEFHQUlOU1Qgd2FzIGVtaXR0ZWQuCiAgICAkc2F3TWF0Y2ggPSBmYWxzZTsKICAgICRzYXdMaWtlID0gZmFsc2U7CiAgICBmb3JlYWNoICgkc2VydmljZS0+Y2FwdHVyZWRSdW5TZWxlY3RTcWwgYXMgJHNxbCkgewogICAgICAgIGlmIChzdHJfY29udGFpbnMoJHNxbCwgJ01BVENIKHNjaS5zZWFyY2hfdGV4dCknKSkgewogICAgICAgICAgICAkc2F3TWF0Y2ggPSB0cnVlOwogICAgICAgIH0KICAgICAgICBpZiAoc3RyX2NvbnRhaW5zKCRzcWwsICJMSUtFICclZmFsbGJhY2slJyIpKSB7CiAgICAgICAgICAgICRzYXdMaWtlID0gdHJ1ZTsKICAgICAgICB9CiAgICB9CiAgICBleHBlY3QoJHNhd01hdGNoKS0+dG9CZUZhbHNlKCdNQVRDSCBxdWVyeSBzaG91bGQgbm90IHJ1biB3aGVuIEZVTExURVhUIGluZGV4IGlzIG1pc3NpbmcnKTsKICAgIGV4cGVjdCgkc2F3TGlrZSktPnRvQmVUcnVlKCdMSUtFIHBhdGggc2hvdWxkIGJlIHVzZWQgd2hlbiBGVUxMVEVYVCBpbmRleCBpcyBtaXNzaW5nJyk7Cn0pOwoKaXQoJ2ZhbGxzIGJhY2sgdG8gTElLRSB3aGVuIHRlcm1zIGFyZSB0b28gc2hvcnQgZm9yIEZVTExURVhUIG1pbmltdW0gd29yZCBsZW5ndGgnLCBmdW5jdGlvbiAoKTogdm9pZCB7CiAgICAvKiogQHZhciBTeXN0ZW1TZWFyY2hGdWxsdGV4dFRlc3REYlN0dWIgJGRiICovCiAgICAkZGIgPSAkR0xPQkFMU1snZGInXTsKICAgICRkYi0+c2V0SW5kZXhFeGlzdHMoJ2Z0X3NzZWNpX3NlYXJjaF90ZXh0JywgdHJ1ZSk7CgogICAgJHNlcnZpY2UgPSBuZXcgU3lzdGVtU2VhcmNoRnVsbHRleHRUZXN0U2VydmljZSgpOwogICAgJHNlcnZpY2UtPmZ1bGx0ZXh0QXZhaWxhYmxlID0gdHJ1ZTsKICAgICRzZXJ2aWNlLT5lY29ub21pY0luZGV4QXZhaWxhYmxlID0gdHJ1ZTsKCiAgICAvLyBTaW5nbGUtY2hhcmFjdGVyIHRlcm1zIGFyZSBiZWxvdyB0aGUgRlVMTFRFWFQgbWluIHRva2VuIHNpemU7CiAgICAvLyBidWlsZEJvb2xlYW5GdWxsVGV4dFF1ZXJ5KCkgcmV0dXJucyBudWxsIGFuZCB0aGUgTElLRSBwYXRoIHJ1bnMuCiAgICAkcm93cyA9IHN5c3RlbV9zZWFyY2hfZnVsbHRleHRfaW52b2tlX3ByaXZhdGUoJHNlcnZpY2UsICdzZWFyY2hDdXN0b21lcnMnLCBbCiAgICAgICAgWydhJ10sCiAgICAgICAgMCwKICAgICAgICBmYWxzZSwKICAgICAgICBudWxsLAogICAgICAgIFtdLAogICAgXSk7CgogICAgZXhwZWN0KCRyb3dzKS0+dG9CZShbXSk7CgogICAgJHNhd01hdGNoID0gZmFsc2U7CiAgICBmb3JlYWNoICgkc2VydmljZS0+Y2FwdHVyZWRSdW5TZWxlY3RTcWwgYXMgJHNxbCkgewogICAgICAgIGlmIChzdHJfY29udGFpbnMoJHNxbCwgJ01BVENIKHNjaS5zZWFyY2hfdGV4dCknKSkgewogICAgICAgICAgICAkc2F3TWF0Y2ggPSB0cnVlOwogICAgICAgIH0KICAgIH0KICAgIGV4cGVjdCgkc2F3TWF0Y2gpLT50b0JlRmFsc2UoJ01BVENIIHNob3VsZCBub3QgYmUgZW1pdHRlZCB3aGVuIGJ1aWxkQm9vbGVhbkZ1bGxUZXh0UXVlcnkgcmV0dXJucyBudWxsJyk7Cn0pOwoKaXQoJ2VtaXRzIHRoZSBzYW1lIHJlc3VsdCByb3cgc2hhcGUgZnJvbSB0aGUgRlVMTFRFWFQgcGF0aCBhcyB0aGUgTElLRSBwYXRoJywgZnVuY3Rpb24gKCk6IHZvaWQgewogICAgJHJvdyA9IFsKICAgICAgICAnaWQnID0+IDk5LAogICAgICAgICdjdXN0b21lcl9udW1iZXInID0+IDk5OTksCiAgICAgICAgJ2Rpc3BsYXlfbmFtZScgPT4gJ1N0YWJsZSBPdXRwdXQgQ28nLAogICAgICAgICdlbWFpbCcgPT4gJ1tlbWFpbCBwcm90ZWN0ZWRdJywKICAgICAgICAncGhvbmUnID0+ICcxMTIyMzM0NCcsCiAgICAgICAgJ2Vjb25vbWljX25hbWUnID0+ICdTdGFibGUgT3V0cHV0IEFwUycsCiAgICAgICAgJ2Vjb25vbWljX2FkZHJlc3MnID0+ICdTdGFiaWwgVmVqIDUnLAogICAgICAgICdlY29ub21pY19jaXR5JyA9PiAnQWFyaHVzJywKICAgICAgICAnZWNvbm9taWNfemlwJyA9PiAnODAwMCcsCiAgICAgICAgJ2Vjb25vbWljX2VtYWlsJyA9PiAnW2VtYWlsIHByb3RlY3RlZF0nLAogICAgICAgICdlY29ub21pY19jdnInID0+ICc5OTg4Nzc2NicsCiAgICAgICAgJ2Vjb25vbWljX21vYmlsZV9waG9uZScgPT4gJzQ0MzMyMjExJywKICAgICAgICAnc2VhcmNoX3RleHQnID0+ICdzdGFibGUgb3V0cHV0IGFwcyBzdGFiaWwgdmVqIDUgYWFyaHVzJywKICAgIF07CgogICAgLy8gUnVuIHRoZSBGVUxMVEVYVCBwYXRoLgogICAgLyoqIEB2YXIgU3lzdGVtU2VhcmNoRnVsbHRleHRUZXN0RGJTdHViICRkYkZ1bGx0ZXh0ICovCiAgICAkZGJGdWxsdGV4dCA9IG5ldyBTeXN0ZW1TZWFyY2hGdWxsdGV4dFRlc3REYlN0dWIoKTsKICAgICRkYkZ1bGx0ZXh0LT5zZXRJbmRleEV4aXN0cygnZnRfc3NlY2lfc2VhcmNoX3RleHQnLCB0cnVlKTsKICAgICRkYkZ1bGx0ZXh0LT5xdWV1ZVNlbGVjdChbJHJvd10pOwogICAgJEdMT0JBTFNbJ2RiJ10gPSAkZGJGdWxsdGV4dDsKICAgICRzZXJ2aWNlID0gbmV3IFN5c3RlbVNlYXJjaEZ1bGx0ZXh0VGVzdFNlcnZpY2UoKTsKICAgICRzZXJ2aWNlLT5mdWxsdGV4dEF2YWlsYWJsZSA9IHRydWU7CiAgICAkc2VydmljZS0+ZWNvbm9taWNJbmRleEF2YWlsYWJsZSA9IHRydWU7CiAgICAkZnVsbHRleHRSZXN1bHQgPSBzeXN0ZW1fc2VhcmNoX2Z1bGx0ZXh0X2ludm9rZV9wcml2YXRlKCRzZXJ2aWNlLCAnc2VhcmNoQ3VzdG9tZXJzJywgWwogICAgICAgIFsnc3RhYmxlJ10sCiAgICAgICAgMCwKICAgICAgICBmYWxzZSwKICAgICAgICBudWxsLAogICAgICAgIFtdLAogICAgXSk7CgogICAgLy8gUnVuIHRoZSBMSUtFIHBhdGguCiAgICAvKiogQHZhciBTeXN0ZW1TZWFyY2hGdWxsdGV4dFRlc3REYlN0dWIgJGRiTGlrZSAqLwogICAgJGRiTGlrZSA9IG5ldyBTeXN0ZW1TZWFyY2hGdWxsdGV4dFRlc3REYlN0dWIoKTsKICAgICRkYkxpa2UtPnNldEluZGV4RXhpc3RzKCdmdF9zc2VjaV9zZWFyY2hfdGV4dCcsIGZhbHNlKTsKICAgICRkYkxpa2UtPnF1ZXVlU2VsZWN0KFskcm93XSk7CiAgICAkR0xPQkFMU1snZGInXSA9ICRkYkxpa2U7CiAgICAkc2VydmljZSA9IG5ldyBTeXN0ZW1TZWFyY2hGdWxsdGV4dFRlc3RTZXJ2aWNlKCk7CiAgICAkc2VydmljZS0+ZnVsbHRleHRBdmFpbGFibGUgPSBmYWxzZTsKICAgICRzZXJ2aWNlLT5lY29ub21pY0luZGV4QXZhaWxhYmxlID0gdHJ1ZTsKICAgICRsaWtlUmVzdWx0ID0gc3lzdGVtX3NlYXJjaF9mdWxsdGV4dF9pbnZva2VfcHJpdmF0ZSgkc2VydmljZSwgJ3NlYXJjaEN1c3RvbWVycycsIFsKICAgICAgICBbJ3N0YWJsZSddLAogICAgICAgIDAsCiAgICAgICAgZmFsc2UsCiAgICAgICAgbnVsbCwKICAgICAgICBbXSwKICAgIF0pOwoKICAgIGV4cGVjdCgkZnVsbHRleHRSZXN1bHQpLT50b0hhdmVDb3VudCgxKTsKICAgIGV4cGVjdCgkbGlrZVJlc3VsdCktPnRvSGF2ZUNvdW50KDEpOwogICAgLy8gU2FtZSBzaGFwZTogc2FtZSBrZXlzLCBzYW1lIHZhbHVlcyBmb3IgdGhlIGZpZWxkcyB0aGUgY2FsbGVyIHJlbGllcyBvbi4KICAgIGV4cGVjdChhcnJheV9rZXlzKCRmdWxsdGV4dFJlc3VsdFswXSkpLT50b0VxdWFsKGFycmF5X2tleXMoJGxpa2VSZXN1bHRbMF0pKTsKICAgIGV4cGVjdCgkZnVsbHRleHRSZXN1bHRbMF1bJ2VudGl0eV90eXBlJ10pLT50b0JlKCdjdXN0b21lcnMnKTsKICAgIGV4cGVjdCgkZnVsbHRleHRSZXN1bHRbMF1bJ2VudGl0eV9pZCddKS0+dG9CZSgnOTknKTsKICAgIGV4cGVjdCgkZnVsbHRleHRSZXN1bHRbMF1bJ3RpdGxlJ10pLT50b0JlKCdTdGFibGUgT3V0cHV0IEFwUycpOwogICAgZXhwZWN0KCRmdWxsdGV4dFJlc3VsdFswXVsnZGVzY3JpcHRpb24nXSktPnRvQmUoJ1tlbWFpbCBwcm90ZWN0ZWRdJyk7CiAgICBleHBlY3QoJGZ1bGx0ZXh0UmVzdWx0WzBdWydjdXN0b21lcl9udW1iZXInXSktPnRvQmUoOTk5OSk7CiAgICBleHBlY3QoJGZ1bGx0ZXh0UmVzdWx0WzBdWydzY29yZSddKS0+dG9CZUludCgpOwogICAgZXhwZWN0KCRmdWxsdGV4dFJlc3VsdFswXVsncGF5bG9hZCddWydpZCddKS0+dG9CZSg5OSk7Cn0pOw== |