From: Marc Alff Date: April 1 2010 2:26pm Subject: bzr commit into mysql-next-mr-bugfixing branch (marc.alff:3144) Bug#52502 List-Archive: http://lists.mysql.com/commits/104912 X-Bug: 52502 Message-Id: <20100401142658.5AF7E336EF1@MarcBook.local> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="Boundary_(ID_WUay1BPU+8sH57QQYWs4XQ)" --Boundary_(ID_WUay1BPU+8sH57QQYWs4XQ) MIME-version: 1.0 Content-type: text/plain; CHARSET=US-ASCII Content-transfer-encoding: 7BIT Content-disposition: inline #At file:///Users/malff/BZR_TREE/mysql-next-mr-bugfixing-52502/ based on revid:alik@stripped 3144 Marc Alff 2010-04-01 Bug#52502 Performance schema does not start with large mutex_instance buffers This is a performance issue affecting scalability. Prior to this fix, the performance schema code did not scale well when a very large number of instruments are created in the code. The root cause was create_mutex() spinning "forever", trying to find an empty slot in a full mutex_array. With this fix: - functions like create_mutex() abort sooner, when used with a very large array which is full or almost full. - randomize_index() has been revised to improve the statistical spread of values in the mutex_array. See the comments in the code for details. Other allocation functions similar to create_mutex() have been also fixed. No MTR test case provided (performance scalability issue). Fix tested manually. modified: storage/perfschema/pfs_global.h storage/perfschema/pfs_instr.cc storage/perfschema/pfs_instr.h === modified file 'storage/perfschema/pfs_global.h' --- a/storage/perfschema/pfs_global.h 2010-01-12 01:47:27 +0000 +++ b/storage/perfschema/pfs_global.h 2010-04-01 14:26:53 +0000 @@ -1,4 +1,4 @@ -/* Copyright (C) 2008-2009 Sun Microsystems, Inc +/* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,27 +30,49 @@ void pfs_free(void *ptr); inline uint randomized_index(const void *ptr, uint max_size) { + static uint seed1= 0; + static uint seed2= 0; + uint result; + register intptr value; + if (unlikely(max_size == 0)) return 0; /* - ptr is typically an aligned structure, - so the last bits are not really random, but this has no effect. - Apply a factor A*x to spread - close values of ptr further apart (which helps with arrays), - and to spread values way beyond a typical max_size. - Then, apply a modulo to end within [0, max_size - 1]. - A is big prime numbers, to avoid resonating with max_size, - to have a uniform distribution in [0, max_size - 1]. - The value of A is chosen so that index(ptr) and index(ptr + N) (for arrays) - are likely to be not similar for typical values of max_size - (50, 100, 1000, etc). - In other words, (sizeof(T)*A % max_size) should not be a small number, - to avoid that with 'T array[max_size]', index(array[i]) - and index(array[i + 1]) end up pointing in the same area in [0, max_size - 1]. + ptr is typically an aligned structure, and can be in an array. + - The last bits are not random because of alignment, + so we divide by 8. + - The high bits are mostly constant, especially with 64 bits architectures, + but we keep most of them anyway, by doing computation in intptr. + The high bits are significant depending on where the data is + stored (the data segment, the stack, the heap, ...). + - To spread consecutive cells in an array further, we multiply by + a factor A. This factor should not be too high, which would cause + an overflow and cause loss of randomness (droping the top high bits). + The factor is a prime number, to help spread the distribution. + - To add more noise, and to be more robust if the calling code is + passing a constant value instead of a random identity, + we add the previous results, for hysteresys, with a degree 2 polynom, + X^2 + X + 1. + - Last, a modulo is applied to be within the [0, max_size - 1] range. + Note that seed1 and seed2 are static, and are *not* thread safe, + which is even better. + Effect with arrays: T array[N] + - ptr(i) = & array[i] = & array[0] + i * sizeof(T) + - ptr(i+1) = ptr(i) + sizeof(T). + What we want here, is to have index(i) and index(i+1) fall into + very different areas in [0, max_size - 1], to avoid locality. */ - return static_cast - (((reinterpret_cast (ptr)) * 2166179) % max_size); + value= (reinterpret_cast (ptr)) >> 3; + value*= 1789; + value+= seed2 + seed1 + 1; + value%= max_size; + + result= static_cast (value); + seed2= seed1*seed1; + seed1= result; + + return result; } void pfs_print_error(const char *format, ...); === modified file 'storage/perfschema/pfs_instr.cc' --- a/storage/perfschema/pfs_instr.cc 2010-03-22 12:48:18 +0000 +++ b/storage/perfschema/pfs_instr.cc 2010-04-01 14:26:53 +0000 @@ -445,6 +445,75 @@ void cleanup_file_hash(void) } } +void PFS_scan::init(uint random, uint max_size) +{ + m_pass= 0; + + if (max_size == 0) + { + /* Degenerated case, no buffer */ + m_pass_max= 0; + return; + } + + DBUG_ASSERT(random < max_size); + + if (PFS_MAX_ALLOC_RETRY < max_size) + { + /* + The buffer is big compared to PFS_MAX_ALLOC_RETRY, + scan it only partially. + */ + if (random + PFS_MAX_ALLOC_RETRY < max_size) + { + /* + Pass 1: [random, random + PFS_MAX_ALLOC_RETRY - 1] + Pass 2: not used. + */ + m_pass_max= 1; + m_first[0]= random; + m_last[0]= random + PFS_MAX_ALLOC_RETRY; + m_first[1]= 0; + m_last[1]= 0; + } + else + { + /* + Pass 1: [random, max_size - 1] + Pass 2: [0, ...] + The combined length of pass 1 and 2 is PFS_MAX_ALLOC_RETRY. + */ + m_pass_max= 2; + m_first[0]= random; + m_last[0]= max_size; + m_first[1]= 0; + m_last[1]= PFS_MAX_ALLOC_RETRY - (max_size - random); + } + } + else + { + /* + The buffer is small compared to PFS_MAX_ALLOC_RETRY, + scan it in full in two passes. + Pass 1: [random, max_size - 1] + Pass 2: [0, random - 1] + */ + m_pass_max= 2; + m_first[0]= random; + m_last[0]= max_size; + m_first[1]= 0; + m_last[1]= random; + } + + DBUG_ASSERT(m_first[0] < max_size); + DBUG_ASSERT(m_first[1] < max_size); + DBUG_ASSERT(m_last[1] <= max_size); + DBUG_ASSERT(m_last[1] <= max_size); + /* The combined length of all passes should not exceed PFS_MAX_ALLOC_RETRY. */ + DBUG_ASSERT((m_last[0] - m_first[0]) + + (m_last[1] - m_first[1]) <= PFS_MAX_ALLOC_RETRY); +} + /** Create instrumentation for a mutex instance. @param klass the mutex class @@ -453,17 +522,15 @@ void cleanup_file_hash(void) */ PFS_mutex* create_mutex(PFS_mutex_class *klass, const void *identity) { - int pass; - uint i= randomized_index(identity, mutex_max); + PFS_scan scan; + uint random= randomized_index(identity, mutex_max); - /* - Pass 1: [random, mutex_max - 1] - Pass 2: [0, mutex_max - 1] - */ - for (pass= 1; pass <= 2; i=0, pass++) + for (scan.init(random, mutex_max); + scan.has_pass(); + scan.next_pass()) { - PFS_mutex *pfs= mutex_array + i; - PFS_mutex *pfs_last= mutex_array + mutex_max; + PFS_mutex *pfs= mutex_array + scan.first(); + PFS_mutex *pfs_last= mutex_array + scan.last(); for ( ; pfs < pfs_last; pfs++) { if (pfs->m_lock.is_free()) @@ -511,17 +578,15 @@ void destroy_mutex(PFS_mutex *pfs) */ PFS_rwlock* create_rwlock(PFS_rwlock_class *klass, const void *identity) { - int pass; - uint i= randomized_index(identity, rwlock_max); + PFS_scan scan; + uint random= randomized_index(identity, rwlock_max); - /* - Pass 1: [random, rwlock_max - 1] - Pass 2: [0, rwlock_max - 1] - */ - for (pass= 1; pass <= 2; i=0, pass++) + for (scan.init(random, rwlock_max); + scan.has_pass(); + scan.next_pass()) { - PFS_rwlock *pfs= rwlock_array + i; - PFS_rwlock *pfs_last= rwlock_array + rwlock_max; + PFS_rwlock *pfs= rwlock_array + scan.first(); + PFS_rwlock *pfs_last= rwlock_array + scan.last(); for ( ; pfs < pfs_last; pfs++) { if (pfs->m_lock.is_free()) @@ -575,17 +640,15 @@ void destroy_rwlock(PFS_rwlock *pfs) */ PFS_cond* create_cond(PFS_cond_class *klass, const void *identity) { - int pass; - uint i= randomized_index(identity, cond_max); + PFS_scan scan; + uint random= randomized_index(identity, cond_max); - /* - Pass 1: [random, cond_max - 1] - Pass 2: [0, cond_max - 1] - */ - for (pass= 1; pass <= 2; i=0, pass++) + for (scan.init(random, cond_max); + scan.has_pass(); + scan.next_pass()) { - PFS_cond *pfs= cond_array + i; - PFS_cond *pfs_last= cond_array + cond_max; + PFS_cond *pfs= cond_array + scan.first(); + PFS_cond *pfs_last= cond_array + scan.last(); for ( ; pfs < pfs_last; pfs++) { if (pfs->m_lock.is_free()) @@ -633,17 +696,15 @@ void destroy_cond(PFS_cond *pfs) PFS_thread* create_thread(PFS_thread_class *klass, const void *identity, ulong thread_id) { - int pass; - uint i= randomized_index(identity, thread_max); + PFS_scan scan; + uint random= randomized_index(identity, thread_max); - /* - Pass 1: [random, thread_max - 1] - Pass 2: [0, thread_max - 1] - */ - for (pass= 1; pass <= 2; i=0, pass++) + for (scan.init(random, thread_max); + scan.has_pass(); + scan.next_pass()) { - PFS_thread *pfs= thread_array + i; - PFS_thread *pfs_last= thread_array + thread_max; + PFS_thread *pfs= thread_array + scan.first(); + PFS_thread *pfs_last= thread_array + scan.last(); for ( ; pfs < pfs_last; pfs++) { if (pfs->m_lock.is_free()) @@ -727,7 +788,7 @@ find_or_create_file(PFS_thread *thread, const char *filename, uint len) { PFS_file *pfs; - int pass; + PFS_scan scan; if (! filename_hash_inited) { @@ -839,17 +900,14 @@ search: } /* filename is not constant, just using it for noise on create */ - uint i= randomized_index(filename, file_max); + uint random= randomized_index(filename, file_max); - /* - Pass 1: [random, file_max - 1] - Pass 2: [0, file_max - 1] - */ - for (pass= 1; pass <= 2; i=0, pass++) + for (scan.init(random, file_max); + scan.has_pass(); + scan.next_pass()) { - pfs= file_array + i; - PFS_file *pfs_last= file_array + file_max; - + pfs= file_array + scan.first(); + PFS_file *pfs_last= file_array + scan.last(); for ( ; pfs < pfs_last; pfs++) { if (pfs->m_lock.is_free()) @@ -934,17 +992,15 @@ void destroy_file(PFS_thread *thread, PF */ PFS_table* create_table(PFS_table_share *share, const void *identity) { - int pass; - uint i= randomized_index(identity, table_max); + PFS_scan scan; + uint random= randomized_index(identity, table_max); - /* - Pass 1: [random, table_max - 1] - Pass 2: [0, table_max - 1] - */ - for (pass= 1; pass <= 2; i=0, pass++) + for (scan.init(random, table_max); + scan.has_pass(); + scan.next_pass()) { - PFS_table *pfs= table_array + i; - PFS_table *pfs_last= table_array + table_max; + PFS_table *pfs= table_array + scan.first(); + PFS_table *pfs_last= table_array + scan.last(); for ( ; pfs < pfs_last; pfs++) { if (pfs->m_lock.is_free()) === modified file 'storage/perfschema/pfs_instr.h' --- a/storage/perfschema/pfs_instr.h 2010-01-12 01:47:27 +0000 +++ b/storage/perfschema/pfs_instr.h 2010-04-01 14:26:53 +0000 @@ -136,6 +136,48 @@ struct PFS_table : public PFS_instr */ #define LOCKER_STACK_SIZE 3 +/** + @def PFS_MAX_ALLOC_RETRY + Maximum number of times the code attempts to allocate an item + from internal buffers, before giving up. +*/ +#define PFS_MAX_ALLOC_RETRY 1000 + +#define PFS_MAX_SCAN_PASS 2 + +/** + Helper to scan circular buffers. + Given a buffer of size [0, max_size - 1], + and a random starting point in the buffer, + this helper returns up to two [first, last -1] intervals that: + - fit into the [0, max_size - 1] range, + - have a maximum combined length of at most PFS_MAX_ALLOC_RETRY. +*/ +struct PFS_scan +{ +public: + void init(uint random, uint max_size); + + bool has_pass() const + { return (m_pass < m_pass_max); } + + void next_pass() + { m_pass++; } + + uint first() const + { return m_first[m_pass]; } + + uint last() const + { return m_last[m_pass]; } + +private: + uint m_pass; + uint m_pass_max; + uint m_first[PFS_MAX_SCAN_PASS]; + uint m_last[PFS_MAX_SCAN_PASS]; +}; + + /** Instrumented thread implementation. @see PSI_thread. */ struct PFS_thread { --Boundary_(ID_WUay1BPU+8sH57QQYWs4XQ) MIME-version: 1.0 Content-type: text/bzr-bundle; CHARSET=US-ASCII; name="bzr/marc.alff@stripped" Content-transfer-encoding: 7BIT Content-disposition: inline; filename="bzr/marc.alff@stripped" # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: marc.alff@stripped # target_branch: file:///Users/malff/BZR_TREE/mysql-next-mr-bugfixing-\ # 52502/ # testament_sha1: afd53690e41a0f11212e1fad59f921e19213c34b # timestamp: 2010-04-01 08:26:57 -0600 # base_revision_id: alik@stripped # # Begin bundle IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWfFP0V4AB6rfgFwQW3///3/H 3uu////6YA+feuXV64NBu9ddOgdCI9z7mLTQj77jfb5PvYNUqE+zrqXWo1uhkIhoFJ4aaZGgp5T1 PCnlAAGgAeoDQA00mgJiBMppkUP1Q9R6R6jIAHpAAAABpogqe0ob1J5IBoAAA9QAAAAAAk1EUwin oVP0ZUD9SenlT2ppDynknqPUaNAMjIGmgElNIJMmmTTRk0HqNAyDRiaA0wJoAMQCRImQEZCekEwm TTKPIk/URoDQfqRpoYgxLAW5iVGAA10n5pgyeuvIH3vqH23F3BzcNKLFw0/VuabV+A5cHb5WDt/6 Wc+AqqtcO6wZPqll0+t8y4vHh0kFfytioGJwkm5Aqlq6++yyyO7kODeO2iDjOPDgE/Rw1rFyjuOs 3vYZGpQDi5obyI/lIi+scNRQ1ze7mmhDyjHv7O/EtKAAQduphSjwUUX2eLcxYTifJlbrLTW77svv 1OQBJG7AgEkNIARz4jmLdOCXi/nasnUC532anQ/+/lmCMytg+VOs5du4ArTwsuIYcpUjX2E/mD7z 3jGxtJGvMwNQ7++ng22J2XYEfSgqGfhNhlmijNFECsNAA+ExIyYrK3QMApM97VVlTg9A+DMHEHQV 9xdIxNbKPcxLXJSlKClchzATPVKJEYr0VjNmGFCgqTNqzauLgUQXcly7LtE6LEOtp0Pg6YRtl9q1 hcibTT1FqUXv6K/PEZ4whwpIRaIbgHdpE49mlR/tWc/rsJEDbfCB+H8Tpo/SBicGpWC905EZzkP3 I8u2zqnShQrYyO5GJDUYdgmJ7pPC1MyjWu6BDLjfCUoRdSock4jCXKQdgEjDw9kNa0qUbCls32Yy k4f02XbwuIv1m4zW5cxq9wPVPo+NZ6S051hY8QdzHaiMzPL2pmlBasOoJsWrkWNARgwOISSkw+T8 JQ8VKJC+qScygnH0nbljoNTBbF/N1dX1S3F++lqdSuOnUNVN3hlDAEIqhKcR1aNnFm4ouj9EmRnc UFxq7O2nWAmAR4VpGyyc7S+Ujj6dNmnzvnyo/ruHZHJRqPz9zcJC9thq5QwpNxU4bxlzba4iIVEx VlG+DMzDS4teNgkgUTuDqP1D0obcQowGEMC5pdTybwENIoB7wCb+jhnfq5dvjumFalz3mjqZhXkJ CjapvISbfAIOGlPLh7R8SCBQCYp9QyeQ2xRQ+q+CQnaKAEfQS+J0aexv4/k7art24Wy0Dn7OPVvO ngqqqyboFApI9JfSHyDW7fB+yT/aRqXQCzYkZaHxsNs7ivrv24Dw8OXIy5SGFBV7KyucIAPVi5yk +h1QZrWUPtv3INUPkagcbEQy/zVSQy4Ag3adfiIy7O8ETcptxG+/hxoyC6le12KY1jugz1udRCJG f6tgsCZ6lxhLFbNo2mIYspMZAC9wHSdx1RjAwszuX0cHtTTPI+HL7Lw30IOZoDyanSEq2h0O2f4U aImsTPvrgaMhGQlSHEhWWQ3NMw4ZqQ/4QxcqrYgYUvAwEVO76fLYc5nlneGDQpvJmDzpBOPpXmSm U0gaQ2EMR6zvsmhF8ki7OtMLtfnlF9Gyg/ILxE0i8rxTiL7i72zi/s0KiMKF5VUebZkEBxC6CGMX wQZmw8O9eQqgvZr/zLOU89bKnRmoGgVd6RFXx8yLKVKNBJWlsyQ1eI6CYzQiw5+rmgwV1BQbcC4x K3QYFxmb9MEpTw27ZqxbFwQYnDKispgBEYCPEbCPKMg6vNwtV9xxHRTSd1+ZnLwhBPFiIUBSsCMK uRGNh1jwnVBYTNdYMpuwwK3xwwKHEOQ5i4uKmh+OU8aaRmX02Fc5zMSsHrzbSWl5BeIZvzNqqbty pYNG83DLjUwKmfIItv0pddPIyKXE0FBq1KZyxcIuLKDEg+NhzQXDx4+VAL6JRMipzVzYhkaziDAb iAbyYouogIiE34oGFwwoMh3BBlCV8DZVriXlULaiuIgkgeDKeEByZGqFXHSuYUVERdADeF8bLRtC EMjUnEp3YCNLqDHaVNEVntLRmRjgaky0uOK40x7ezYVT3hHjsNmnTXYSDgi6oWCr/PZ8xTOAbDFh NirHpRTns/RShQoAW0iWnz7sETpz7r7LrRvypGKE2EmVQoxA5yFYT4Z0Wyk5WOR23CRKjscHOpaH UTfOd+irV8mfU0JZXt/DXllQpVTl4+89fGi+Fmmu3ZjbhpXiSe+V9kJ9E+uiJ/kAwan31ER+kXtN 8llQowI3iyIVyYkBBmFEYELKhNOAHb3/KxVuqhWSqoR9bAqUSgBh6/cm5V/tahbhQhZyTFigZ+CQ FTQWRrchYlmMQ3i2z94ud3jkAbwlk8/cEFKqip3/7Z/L5Cln72UzrO8qFkf8En1kyAazvhMT0+Hs 9FXd0WWdHzbT1hOc/FMzSGpNcksiTgEvbJku7LMk1KnDTCJihItj/FBHCivBDUg4WsfGJsTZJVA3 ncYRi6GIMJ/OJYpdtrSFaVFIhHamiPGIRRUaVE11CXUItPh6lk+TKk2OZ/mX5tgYIxpNMbHywkIl cGyxAwEctn2UKU3Ph9j87BKYGoKa+YJYqhrh4771JxM+4jCTaB+bBRA476SjvLN16zfDYFjLv7iJ BepXpINLhLNpfH6zrD7DnIPqNx+CmYgYp7h8xJQiBezllLqgDhd7OM16B87AXCZy/qj0SkMmJe3Q 9x9p4nMXLwWe2UoYkiIO8mQXPwWCOc5fOzyL1R8cUaYVxOargGhRJGSAQOYknk0OJpLvZmMK1rQV 41iysVF6KyOZnNZotI+SBU7WHmExC4PWakK1XJKpeRofQWdOiD9n1GlkcusDwO8vzg4QSQO7cVjc lVR4k30kG4LHts3yffJFIdTKyA96hQ5JUkLOY644PnKGMZM2Dge+DAssUVSbZOm042bttkhIQp5L 0t2RG2d6SumzuPlNCe4pgcxU8DsJExnJnonxhbgzK01hYgUsFAWLem0tRowPA19l5XfxtynyPNNz Oup4cYtEbhU9PTCgjadOyrJf0lZOOqvIfj593CYmt5ryotldqmfIENIMhYMTUIPslnUYEScStY6K KOCvcING8SwpW4PRgFZ6Xc1rVpYY4bAqv4iBLwkTO2oXWWwhUCxsBpStAhK3bgt+STcNHyTDsPb3 EhVJ50iGIxKcFAhK1AkIkltPIBhKeF1+uh7kzWPNWFNA5JjUh3QLBKBPCTb35IAJVuEHhvIRCgP1 tfRVaNLe/isVapHADGKYK0U79iiSf/qrzXie3wDYukhD1JMIPEB5EhWfk5dDg9RoYwVjFnbcrSHI ob9BcC5YVwY57fdyPUcJKRIxASckHOQ/rgjPfBGWh72Uqgt0TF9dvADXcCdBpmjLIkfF1Rf4ZBkW 8j9zD4aICp3VyVuPONLBot9BbiQ0SL1zbbtnDTI18tc+0XtzpnDE+/BpBHe0ePQ90tNqaBnHbHTq CKAcqbGsqJEHZXklV8QLY7kd7ERzo6ei8TNsfxxy9dgbgFcky5oNyO1MHlzC0p2PkYo/3EK5L0Xg SOnDAXYLEQTnZl51NKCVPa4Vgq6z+PACXj4bhnnks+F3WO2IUohD1VanQKNNbVZbfIi5ebsZIDb4 T2ezrlPt7sF2RYEPDo9P6y7uyi7sqJVls/GJeG6o3doWf98ftPRDUFi6RBQWI0LYti46o3c9ndZj 8sKVoi4ORl8jYFNxL0pM2ohLr+27dQubk4IVOzdDUwLiiWSsHRDBJKZM0A41D9mSUKEFWkxQDQUk JBBJrSJ7cru4VQVqorNevIG11qflSf379T49TpQ1RA48qWQGlpjEiMQYFbS57yF7ef508fz0HUnn Tk/e4VwZnE5sDxhXZ6+mQ6j2hfxc+dXJq7OWQoASDGRFnXNkhRPTrjBgnP4Ojcth8/lJetFeDt/A FAp7iIAvzkt9gabXJo1lrzGOe1201jr5nwuMrmNb/d7kSPcITPHdULssinvfgsw7fkYS2sbG4Fmn qww696BdovTd49+u4OjY8lTNQwib44eZNh0eE3JPUlKb1Utqqq6QQ2akY/TggfBz5ooJ6hWUSGnX CG4B2JqqpjzPcdoqGPAOFDdmmng8ESuBaVQMaQZbbFNqFowBAphntih0pMTRDVzr4VtoHimnGEGe UIb3u4G6N4mpsFYRk+jW46UDU20XQ2J9krZ1ik8JTspKyRP+FRKbna+rXGH3TfOh1EzqNlyRJykx zsZ0PgUSifkLmFAoFKALVsxbL3pGZpKZVdzS789o4DEJhOoiQEmkmeqCZoiRlpHvvgN8ZgatBKGe Y8h1S8ua6nsxIkVgIxioiiKTN8EVEKxmY5hKwCN2SdkJoPIgkrk6Gp6Rq4FS0tcbWTOLxiA4o52V 0Y0zoSaAaaWrt8vxQLuC7nUTZIrACrCqhTOU0ClGI2OzB1aljRVdtI7zGxqEScdIco3IdzA8enBC 50DDZO8MYJp6TMYMZZCnWKgmwopJSF1pQJuGtcSTT4WSvmYNGG+y8gWBKlsARKgitnZYY3jYveB/ V3AbxoPds3LtRheHi2bFlJFhccVLlAGn46ARGI0qAi3q8ZFBrSYU8BBy5IX98Rt5ovMAXLS92bO2 R29sI9FbBk0vzQPMXEjI2MFsQo/pAahciEGvXPtYLxJOywjibU3tQ4bqqQURVRG9bmLVAhHuy2ZY 8WiETqAQBiaam6QgXzhFRQKSlr/4u5IpwoSHin6K8A== --Boundary_(ID_WUay1BPU+8sH57QQYWs4XQ)--