Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce a version-specific ID column for display purposes #8504

Merged
merged 2 commits into from
Apr 8, 2021

Conversation

joelverhagen
Copy link
Member

@joelverhagen joelverhagen commented Apr 6, 2021

Progress on #3349.

  1. Add a column to the Packages table. This is nullable and will be filled lazily as new packages are pushed or existing packages are reflowed.
  2. Populate this new field when the package ID uploaded or reflowed
  3. Update the casing of the ID on the PackageRegistration when the latest version is calculated.
    • I chose to do this so that places using only the PackageRegistration.Id also have an improvement. Examples are the API keys page and the admin package locking UI.
    • Note: this routine is also called in the orchestrator so we need to migrate the change there as well for best impact.
  4. Use this new property in the most easily visible places. I focused on a smaller changeset to give us the biggest bang for the smallest buck. Fixed places:
    • package details page
    • managed packages page
    • Lucene index rebuilding
    • delete UI

case-of-case

A subsequent PR will update the version of NuGetGallery.Core in orchestrator (i.e. propagate change 3 above).

The migration SQL is:

ALTER TABLE [dbo].[Packages] ADD [Id] [nvarchar](128)
INSERT [dbo].[__MigrationHistory]([MigrationId], [ContextKey], [Model], [ProductVersion])
VALUES (N'202104062157118_AddVersionSpecificId', N'NuGetGallery.Migrations.MigrationsConfiguration',  0x1F8B0800000000000400ED7D5B6F1CB992E6FB02FB1F043DCD0C7A2CCBDDE7EC99863D035996DDC2F80649DDD83721554949399D955927334BB6CF627FD93EEC4FDABFB0C9BC152F413278C94B498201DB95248364F06330182423FEDFFFF9BFAFFFE3FB3A3D78204599E4D99BC3E3172F0F0F48B6CAE324BB7B73B8AD6EFFF56F87FFF1EFFFFDBFBD3E8BD7DF0FFEE8F3FD4CF3D525B3F2CDE17D556D7E3D3A2A57F7641D952FD6C9AAC8CBFCB67AB1CAD747519C1FBD7AF9F2DF8E8E8F8F484DE2B0A67570F0FA629B55C99A343FEA9FA779B6229B6A1BA59FF298A465F7BD4EB96CA81E7C8ED6A4DC442BF2E6F0F3F603A93E44694A8A1F8707276912D56DB824E9EDE1419465791555750B7FFDBD249755916777979BFA43945EFDD8903ADF6D9496A46BF9AFBBECD84EBC7C453B71B42BD8935A6DCB2A5F5B123CFEB9E3CA9158DC89B78703D76ABE9DD5FCAD7ED05E37BC7B73784A8A2AB94D565155F75FACEFD7D3B4A0793BEEBEB824C543B222E58B864C52FF8729FED38122D34F033E6A18D13F3F1D9C6ED36A5B903719D9564594FE74F0757B9326ABFF243FAEF23F49F626DBA629DBEEBAE5751AF7A1FEF4B5C83775FD3F2EC86DD79B3AD3E1C1115FF0482C3994630BB5DD3CCFAA9F5F1DD61D49D3E82625033018965C5679413E908C14759FE3AF515591A21ED7F39834AC95AA172ABBBC8F8EAFEEB7EB9B4D9164555F6F8DC97A621D1E7C8ABE7F24D95D75FFE6F0977A26BD4FBE93B8FFD035E5F72CA9A7615DA62AB60468AABE7A54D5AFFEF2D731EABEDCDEFC1759E92AAEFF8BAAD8C4E2BCA8A6AAECBC2CB7A498A64F13D575F67D93149DBC69AB7A5743FDAA96CB52C9CFD14372D7641568D472B660644379787041D22663799F6C5AC1CC0A8F6BB9C0FB225F5FE4292FA3A47CD797F9B658D52DBBCA1199AFA2E28E547C375E1FED84A256540AD45CC4A540E25964624426C33044BD7A5A7400EC892861CEC1612C84F7A04521BC9F0E3613156C3A4DD0B619CC203516CE05B5D26A1EBA4EBEE71987997167EB28494FE2B8206519404F304CC97A5B91DD26C59AC4D3D65BC3218BE8A2A6ACE8AFBF8CA107B5BD4CD3FC1B89FBCADFE6F56C89326B5AE7E53B9292CA9FD05946337FAA219FBC8F56357C4EB6D53D45CB8AD3045CC97FCEEBC9FFE36BB4FA33BA235FB7E57D801653369EB6D069DAD8CC4ACD68D249E38D9AAF51597ECB8BF88294A49AA9C69D82F6AE59780C4A9AB86615840A85DFAB956DC98F5159BDAF995EF727BF4B32070A4CE9D37CBBDB8478AFC30569445B5D0A5E8787F4EB76F560965E3E495E6D8574DB05F6532DCB6ACCC3ED6A28EE72088B6B9F20E9B87C2AA4D4EA5AF4A5B88BB2E41F4DD2A7E4AE45D205F9FB969495BA95FA5242CB7599E1DE684B84ECA166200CC52CFAA818327D119F5EDA750ED5277C5782F400D9747D9B118DB56F2515BCC50389076323DC5429DBF5976FB502C635589547923ACA8CB6E28792D3B0B64B1658DA7C8559D926D9B2F092ACB645AD787ECD6B053AD13488CBF84312D68A2CE0AE03CAE7B23B329A311C774A207BC35A2C766B97936D7728FDBC6BC2EC9ADC6C0BA281B62E3BF98EE48F28DDEA6A1DC9207C45B228ABCEE3D1B77AEF48B92A920DBB9119ADAE1D5A46AEA8D3E36515DC01E2A7F97AB36D88214CC3A4B455FB771B96AB64F567C9CC108A67F3A6A39E59403FF5E52EC843DEEE5D5B41CBCC4C614B20E6B458D656F54F78516892AE5901BC5B0FC43469F9923204B3EA596C8424C3BA62A3E4B432353D7459949A82CFEB11CA6E3E0C98F7AAD4289F3295659CF775A6B4939541BE636B0B6085F01709E2EC53CA0CF45E8B0EA1A65D5DBAD8A4E6B3A2356D9A971C688C789D25C3451CB0E59FA502462ABCCD639D6E12664A5214C1EAB04164909D4DD0D52E3C95D8B9CA031F26F64C0327290BF4EB5DCEDD740533481317CE652B4BDAAEFB5834C16342C9DEE92452F4864A7B11A3A3B70491F3997C639BE82481201A3EFAC249BC4EB220FB619B039E500BBCB49368C61A3E6B719EED038BA6B14783130E67C2C6F64800D1144709D6BDF2132D9FC8FAA69ED675773CE48944640942C420415482A7ED8B93C8092A6FF8A6F8503A2F9B89E9AB843C5AB135707A8C9326E37476964D46C1C466B86E7B08B55D934D6ABD2E6F300DC757A3092E7C0EDE462561EC6CCD12873E2BE7F93ECE38893A31664CF1A7FD8D70C736DDD46654633D0F8976EB91DF6AB6AFCBD852D6B099173003A4C39CCE1B05FC3492DD3CED10B2DC53884B27FC2EB34F22B28449B87C1B98E9BCF3F8D5DFC6D0C8CECBCBFBA8E086DCF7F6E7D782DC26DFC34DF6FECA4AE8FB2DE23266BC08836D7077A5F482DC256555686E12C935C245759D814A20BA0616F35AB3018A2EE20320F32C403002447B676124E9F12EFF96A57914BB5C9C95E4463D96C96D12E2FEF9C77CF5A73F9D0B42AFE2C7C3495068DBBDDA0C97D291AF531EC8975B50700093E45A28B59319C6CC92B83097703AE983852054992CC5D5B950ADF795E4140986E6D32CCDF1052808819EE80A48CA1EAE94ADA2DAD1C00FCCAE807E68FA7CA8C11932DBDF82FDFB36A99597CBE4CE0A5E52397D6784ECA83E896526BBE01B4883305FFED52A1E3E1AC43BB22948FFC4C7598160A83CEB0F18FDA1E39BF766FFB26ECE76F7548E9983CC90F4996CCFF0BA458800E0B33DD71769D996EF7B43E2B73F5CEE15ECCA7F011EB58F7861F2B49952F3A9353DC375E2DAA8CD28F41141242BF41CD716F34BFAF87A995209306A72D81E8A20D6F58A99BED77239A95FBAECAA25545BC65179D3C28CA9105203B87425C8B84C2156408F65EF79ADB358EBF4CB87F5F1EAE64791DCDD8F7FCB6B8E5BF6B8E70A61FA57CF571295E4735EEDAEF58F56594013C6D9F74614A71DBA7E2FD2D15BFF5B54DE9FA477799154F76B9DFD07E7B9C95CD9F42F72CE577936052BCFCB8FF4759BF75DCF9E4EADE2D685825123EB5A7EBF0ADBB840449BE7309B78729944EB3D8B13B05A43C97A69CCCA6966681293AEBA0BB2C90B6F7C7D8CB2BBAD5E737F1562B637CA00EB14639231ED64E7FBA44667F20FC20864FA2CCB9255F54F7AED7A8A61A6635B2675DF7F4C5B9BE16DE6F1CB4082FF8244F127520F43B3C24918365FFBA26FF4BA7970B2A21E34A3CCFFB0F11D792069BE59D75A5FAD8493AC56FF563F7C895E6ED7EBA818FF41C2557437BE7A739554A956540479F7D96D2D267F24FC392FD6515ACB8938580B4C8F41E88AF991C2CED6DED381FF737B9230F2B0732BCE1412E963BD910A7174F7B52045ABFEFBD27A9FD2B5272331F51A9517E3F37CA8701045C904233DD4DAAD9CB4BAF16BFD9464A769528B5DF3BCFB25C4BC7333B006F142D6F1B5B5588B2FB6B9447B9301D178919CE0B4DFB09D5DDF9038AE69B4B284D334DAE75F5C3A55D9DA3CB60A6D5BFEECFB86FAD8D383E92FA1F499BEF17477E98B909E16D591D66A3EB5C97836F9DF0FB0B5A22BACB29E67FF83FCD535B4CF0334B14D5237AE4BB76D96C94F6A4F9FCB27378FF57CAA6A229BC7B699FC62A2376533194153F690AE6C2997C9A1A98C313D94D55DD354D9348F6D2AA71F691BCBE5BC1E0CF452A3C17CAA230338B3E3ED8DDF6AD52B2F0CF06833FDD07440C8A16ABA98CDB1D14E4769D6B74F300768D25515CB9E740A9706EE7C4619EE6CBA12EE5C266B5F67DB0D451A89DF17F5EF6F79F1A7B6C5432E0D5CA43C2A66CB19AD5DDAFC58DFE4E96EF035AC16B3CACCE67328D92D64B365B8E92C55F274C37E5736CAE918F48F6D4A8D71B5A2D1A33FCA546C94F276EA7D53049C7EB812123690C5421CA6B6FA81C7916A4BE0F96075CA4B44F4EF09AFA6A06EA4985554E52D144185F5BC1D37D8397D2EC775449E613D25AC27D8B577A376B9212B4D5D61CCBE2DDE87053E7C7DBE777BF01B22CDDD1E79D7E4337F85B340E729CCD1799EC553CE629DB77ADBB326FAA89B3908736B10C6CC3FD2ED8CD37CBD26412277596EDD2D36EDBB12865D7B9F51A5802B728F7031D1CFEA806ABEB86D0B20D4FCC5D9B320C3083283761CEAB19FE6718AA5D9CC7E06A20C67D27CF5C17067D8F2C07047E119C3532EC62E278374A92EAB68BDF15EBFA7B95E31D509F6745798A7BC4E3CD505D2092F153EE28B6D135EFD1EED5ADAA3BA4116E8D6F782EEB107BD636A77F939DC95DB809B0DFBB341C50643758418EAD4A4A7AF383C61934D4DF40F17C01CC879E88C34D3B3C2B8FF470BD61762E738C2B03A88565944C1D36A9F19C4D88F9DA7D140E3792E61E6D2A20DF76EB70F14F25E7D4DC109B2DCB500A758312C8167B0CE60B61F534B335CD50EA2703E81279F63DCB5BEC8BF098BF2DB246B3669A779B6DA16053D0DFFD4B4B9E910D74F853D35E43B38AF85DDFADA936A7157DC8E729295DA3B3E2EB2534BF05996A2BC8C762C4CD23AB3B744ED86610ECF8320A4D48D40463DB77D62941465558FC1EA7E787E33D63B3437176EA3DDE713B52DCB6B80B677171BB8EA242097F1FAE4F696ACEAE9D1DF7494C4A12EBB4A366ACB84D807099D75DE0B71749EC522462C7E48AADFB637B51E17DD44A5BFA679123F2465308BB9F595920752B0C15E55D36497D1FD259230D1A699A08AAD1E6A52FB4CD0D6F9A69753634AE0794A62A6A4C2C5A9F7DCBCCA47A15A4459794B8AAFF9669B46ECF4733D43EACE14C6F60DA88DFC07706A4A1FB84A3FABDA52F68103DDBAA928A7EA2498DDD045B88C9728A3D5384575A8CB3D0BAE0097B342DBFDE94116BCF236475C74DCE40828CD5738F2499BE4A5CF523A9764B5A562F06B5E8FBA93362B5379C61F067F41425C1A403C96DD6F7B83B9163452EDA6082C63CC5CE5C4E5912F9D782BB248EB892A9FD71AF2214A5352D4FB87AA6A786498DB5DF6177CB16E32F789A735E69FA7313B03C9F70AF41B170899B69B9F26828247885180CCB33C9FCB572D0D8B4B07C29B5037923520C3D06363949ED6C89DE40EE02811491B7EE8B611EC4CB8DE1590F60F603ED5C601CE1C208015BA27B84D91A908AA7F21767F0280D1DD94CAE9BB286447754F2C13C284D5FAC4F27A394C093C4B6E8CE4EE1C90A14C38284A6F81233B93788BCA20D7D06C35F9E42E8BE8B04F50B5C61F51C7357D54039AE99AC90B4432E0B3A8A31708F91CE5AED690DE55A239EB1772185AAB3D9B438B9793D58AFA4F77172F1C8167F1328B78E9C6C05B8973965621A586A590E8FA0ECE3C0E9BD76281DDFCD3E59366A136B3BDE7359DA403ABE2259D220BAED50A49A7161EDB35233A4E0BD2003E4A2FC843DE8587EAAA3D2FDFA7D15D398CB1BD5C51530F1F65FEF72C2645FAA3462C8B797E08DB38CDDCB1F3E14163797A73F852E65FCB290DF706612E85D61A78D7CE871081D45AD20B60DCE7BCDA850752F14F2CF4A5BAA78A7A97F9589FF923B98BA8DDBACB2D8B2E2EF76951336015A56FB7F4415657E617F7E194C6D019FF1CC1058CDCC94394A46D600CDCA80D5E7C71E356E74AE2A8B54BE2C6EE7DDD2012F7E5A8B2DC95FBD961FC943E71FD4752417A09637A53362E407003FA358D92EC8A7CAFB043FA292AFE8CF36F996A40AD064674C21B6E5C78CA7B372C12978FDDC597E29A51306906D25F00C73FE6DFD0ECAE55D5A2313AE226C16FC9DDBDED6A6427C94ECA325F25CD40F4EAF8AA5632AF771A94D0F5B32C3E68B54231E34E6DDCDD406BF2D49A7CCDF96453F3BA1EB13787FF227544437438AEDE1165DA26503EE61B5B53FE92B54BC9C1C98AF6B1665354AEA258DE2AD4DC89F92FF5AE90146D3DA7B5665D4326C92A790B9964AB6413A586F60BE5B09B4FDAB0A10A31A5F7525619060453F7AE94A215436502DB4C5C7A7DC4400C83BCD6B2AAC787604EF5C61B6F6A65E8B527B03CB9972F5EC8D3D6032C5CE5D3E184E321A6DADD59CF2CE86070DD8C8A6A44857C104AD4024407159130122EF3CB2445C3A7809A623030550F375A6641DBD9BADE2074D1929B4B875ACC81B921E4B1196DB0075730B9C0D236630A3C69198D69405F663E64352DEEBA502A11C5E5829034BAA851B505009D1DAA9D9007F2630AC4819DC7547C952F00675F8ABB284BFED17CFF94DCB5C7E5FD7D1EED78EB4AE2F1A89668E64A00A069BB339AD443F363323C62F886690CBD2BC2905A30580D92525B7409E253DF404FA88794B1284E2E02E856D2F8245E27D9BC0299EDCB756BBA31825B530642359BDD4618EBAA01B0D9E6A0678DE30212D1FB299088E00E6AFFBC08490B74C60A7E0EB81B45AA824DD362753A90CE8DCE3D84E505A965F30389E9AF7213AD3AFB941A9AAA02103CA5BC365B6F6545C8DDB740DB8227BD7B163E9EA3AA9D606E881B836B1984BE6C220FF040BE4030D2C4D376788A99A765094A29E90AF6EE33E69B7E43573A9FD3461488E1398DF01A6919101BA406641F116C5C2CF27C9914853C0730552F06766CB455D34883A1578DF0D34978883274ECC83472345B83A62D5382096032A67A31B4F5ACA0E2224199C61E8E933BB754838355416BED101D6E54F10671694A5842FCD82B41C7C540468C3E1010790198942330CFA9FF416C9A18941243F609948AA03D061C9862684980109E355B2FCFA69041AA0A83EFC7E0285D56AD1F1C6F4EC4ADBE3E35B396776B01D39109A7B97600F769BA8B711F0C4052068190203484D2B206AB3208FDFEC054D1850901AA18A87D84A6F6AE0D90775450CE73D346D3881930B55F77B7FA19D0FD4BC7CCAC6CB39997A36C73AD528BC5F655C3A85A36C49F29B56C8813FB24DBE4F81686B1D704BB90C69F89E9612DE7D4B1325CCE0C1CA6A9E0C6DE342514CEEA679FAA2A5FFBCCE5743E8CC8A8D315E6D2941316E6C73E4D59941E6252405C0CD1B3AA1C73E91A7BA664209DECAB86DAD6E3FE0E0486001B78B4597AEFB786B6FD84D3BA0B374C1A9CEF70A90F82FBFD29570C5C38826003EF231330CC9D5054603887698E1C1765CEE586B1230F3665EA6603B30AE9CA6AD06F65A4B7AB74AEE50BD3A809A18A19184C73C4B2B3A395F3BF677543495D528354DEB3A0CBCD254DB50B38C532336542D09A5985698C78BB49F265BA18F81AAE1AAA8B380216215BA1CAA6BA706888E8606CB9AEB49663B4A013AFB435AA67B76E8C1673B280EBDDA4D20133C09806E9A2BDCC2B1C9A1EC2B13E70603404FE080E7E7DD0105BE8875DD9CC0D9C1EBDDAF1C134471D516831CB1AD6A40A16725CDAC6DD32C32D75B65084D4D96634B56AB96261705D2E92E973ABA4203175156AABA909652752D9C45A47D7DDE4672A40B36C9ED340C527785C0356EBB68EB93353158B45D50D6360167ED4C53049787E9922BB2CDE178DA10353484DC3602DFFE8417A196DFF46DEFE69BC852787253C1D36F6782AA469398369C467F2AD65DA82006781B4C5795F98E795B09A2FB300D10A818B801FED8801764D162CDC4CABAC182D915178F234E0D1675317FD8B793B65E8A7987B09334C6A934223D13CA20B39D3542C9A6CB2A9F8B17CED827DFA87C6A5AE10E881510D839150AA6DE16C60C5F06D0ACC62B88369C7921E80C2A1D60C9B7843DC3569FFC90748B4B619E823B72D7E3787E9C5848630EDE02175ED99BDDC423D713885C047DA1B15D18FF0380EDBBFB960EF79ACB14453B03604A30D1C95F118479D04AA888E7B29DD159D990BED8A11C534070A623BEFF53F2150970163EAA85DC02DA636409FC30D3F45C4AFE96FF5C10D9910772A7E639AC0C7C65B00CAB047C3CAA892488CE1AEE6A982528E79E55C1BDE4FD55E5CACBF5DAB850099F8F9878A13B8F80504D38B29663066D82CA6311B317396B9AC0AED68852678C5088BD839560C434366C3DBC2578CB3266A675DA6AA4B0C41A5CEBA685AF47B13BC4D8ADE550F6617C0AB0F3A2922A46AC27555B285A83C3C381B6285428641096C3C29C93A299393AC7808922A3AC6C2BB602760CF98A03306424D801E884617DEC8509C0DC70051E1C3351888E99DB2CBC4F57ECB0D9549A7C3500DC011329AAC9E9E9190745D05A2075C883190056FEAC8844133898134E7704C49927BE6806BAC861A9644EF6C5449A877D98923C7F9FBD37595F4BEF2707439CF3F1ACA82CB262BE266B25882ADCB0E2D0B069F28388AADFF0C25B5D6F7048ED4E06B40438FF165601293BCE300405CF20FEE0DE476EF2BBB17A73241C3134C6405F4B9A176808447A958E9412F366BE5467BC5DC24E39A837640AC35C7DE88F593BB5B05F713BA0467A0FC214A5352D00FD4640311E57360D9C69A9334CCE30D825881447538AD2C6AB56C03394E9D84C8094ABB408E51F80415838DF279C06463B50D6524504EFB57C6021DFAB2D36A24655643A6DF3D3064382D4BDCB7F27D45F3A135252B5900589A8166F33665978EF3F6618642A7967AF7568CA108F4581B66916BB32AD0226EACB4A44664011CD80F6004220220D7077D0C40A627827AAE618B3EEADF08CCE163D3014CD104AFE35A0E87AF33B5584302E8B4968FAE9DD76F5F140CC14759937B888AB366CF3854643586AC619B373E73D570B3080B66C7071F60E2E2794DC8605D742980B3E860545CD731E1A8AC7889093CC51084EC0363700ECB324B5E856392813B21D8A28CC803B00617BD87EB94317E0FD331C8ECA2E1973162CF082B67A7D10B9E300056C119D59D01F3434CDAED8335AC81A9017CE1ED46E1D8D39B7C348C8142D0C09D1082D0383343881D23D3199A1D8C119C515ACD0C656014B02350681457A640C15058CD9EB3BB07630B6FC353F3451DDB03EC0C18DDC3953360500E9918DF95901C6226A696438A4813AA4EC9B1263C38248788185DB828E218A87984097C00F5CF10FA40E69A684136B3D010EC4059C568EC1C8CA8486EF6F92D7BDA23683C5EF635A859199087A2AF7D35F7B45EF9A15EA9FCF2CBFD614E04CCCC52B9E29F844D2A530C940DDD139519C6873B63DB617AEEF327306A61CFE533CB6736BBB7B0E788A9E1D2F521146B6477DA6AF6185C6F43BD523BDF967BC61E6A99F9A576B73DEA1413BD6C6BC0A4F3C70D2240E191DB15500A47DAAC415DE84B302619041052F298440E8E0D63CB18A40363801B2EAE8FB92E5A3A3F663A2F9FD16A3869E9E078D4F9A7756DAC461CDE23328421944F6480BBC301B519A62827C61603E8CE61ADFB5C3587F15E77A1EEA3FCEE22773ED6F4C75740344E5ED51CC57A8685FA8BF00D2B4F53E1DE9499AB0857B0A3EF562107A4489E1A6DABEA42A37171320BABC139A98E85DA8288EE62DC5E828CED2EE66058AAAD43BD526907CF9BD9B0234C23AB11FE33354CD07BD00CC166BDCFCC09990CF96C44CA02BC9AAEF7F2185A1E4CAB5D699D052239A9F53168ECAFCACB6068BEAAFC0A8E2070911E0551C78160499B133C88C02847856045D3C90495E741C5E508A39342E9E682CE4DA10022F922A7E15684CE33E158B795403F7898AB242E3748025F1C99FCAA03E0C80DC32A2B1E8562CE04571C18C7622A36006EC7E466F38EC7ECBBCDBB1A63A559DBB6301D959F4B29FAACF7FA24375FE9F0C99E134ACF4C02A9C0C7CF5A9F57D02559B48F2CAE8B282F59C843763CE16958D8AD87B0D325B50A8670D204E9447A374DF2322DDCF2372B5D7AC74CE36D6F8D8E7F90AC74DA79A1BD068566F022F6615A373348B66BBDD318D9A0F24F139ADD2A8F34E3C15A7AAAAE33C8EA5EB5C34652C5BB76E8524EF78606637A55BC641F9D4B081B80D68D87A637887DBF3D8B26DAEB6B3D7600ACC27BF8E0FA86F2F1C1F4507C9DA5611BCAABC708F052F98BC0324D3B094DCE2502B22ACC24A4DE4D68E1C1D7C190F6FAE872754FD651F7E1F5519D654536D5364A3FE53149CB3EE153B4D9D05788BB92DD97834B6A44A815B27FBD3C3CF8BE4EB3F2CDE17D556D7E3D3A2A1BD2E58B75B22AF232BFAD5EACF2F55114E747AF5EBEFCB7A3E3E3A3754BE368C5CD67D133C350539517F5CC1252EBAAEB96BE4F8AB27A1755D14D445F0E9FC66B295BF381F1ECC0736F606F5F1BE0BC411EBCFE59625F88FEBF73ACB8FD40AAEE81E60BCDA5D61D27DFD79D5B93AC6AFA4960A5582E5D97BF5C456954F41E35588F1EA779BA5D671A171FEAF297F7D1F1D5FD767DB329122A6D5852621A9EAA8AA21BB5CBEDCD7F9195D8B8FEA34D5FF3A282897129788AE765B9A53A144BABFF66D92E88149780A777F67D93F44A354B8EFD2E537B7D2400549C0647D23C10049338B750330FB1CB44CE3ED1258AFD0C3452186B168ADE85595226CFC36AAA83BB6D969CD207F7AC08B01BF6F392FEFFCBED3F49E3FFCF8EA33EDD50378F3B4FE2B82065294C4F2EC56298B3559EDD26C59AC46AE2CA4C76706A1D2D887882DC2F987990A6F93712433CE8536C0471A74989B278F86CD1BA8CDEE269BC78BD8F563558E82319FA887B05C95453667CBD9FF37A9EFFE8F6155FB7E5BDD81B308325D74F5B14348DBBCAFF2462771479F0B57C8DCAF25B5EC4F400AA026A80D27DA8EF16B577CD93237D5D626E0B115D107A43E9F76A258867E63B9EDAC7A8ACDED7AC26F1C7FC2EC924AA503A9E3A53F2B4DD44B2B4E5D4F04B81CDBACD9E8D38886FED798E9A4758393ED3A2C83A9870DF8F283D9C60B6239AC2632D8EB61A8B72FFD1B86EE2761E8033271D853FA2742B90E83E59B4826451569D0B527CF7154FE91D295745B291971F2EC162B96C46B61218BDFB6A2D16419968BD6721A246D47F74D9FB5C25AB3F217A6CA29DC0AE8118CB62BAFD8AA774411EF25639680D4912DEC10C8B114C9DCF1F6799D47A7AB417478A72A3EDC806E9276FC8F8243CCD9D3B76969CDA49BB9A52288B48A769B75E93796A42D262F0C73BC7718621E72AD41E8DFAE26381F26D1E0B04DA2F169A61E76B496A099760014422190DC93CC6BDAB1CEC17F3793110B6F5FBE3A0479B9C3559EAD56672CA4D2DF9C61292C6074AB7905FF1BADE1C41E3CEA758C87DFDFED86B6BDC314FDEA772098B81297051CF199BB26B637B40226828575E1D043DF0C787B01570AD892EAAA6785E36C815CD58DDC7671C7BE1380880BD903B3F6415789D04AC330100B83AEF8C03D907BA3D1C1034C6D220439835CECBCBFBA8E0DCC0F3E32F25DBD0FE5A90DBE4BB48B2FFBA184C21DF7D205185B8D488C0158ACA58C812CD5C9606AEFC5B96E6510C98AB85241B2CFD418AE436910F8576DF6DA87DCC577FCAB4FAAF36CB6756FF1B0F712AF815944F5B0CDE0D5EB7EC70CE46657086B996C85828EFEA96C8B0DF2DF6BE55546D051B65FFCDC688D3BEF326C0FC97F745A6CCEEF51AEBB2A42FBA0590E883195CE87F91ACEA6C8A85D2DDC4100227B690B49879ADB9686B37A7DD27F2E4B3573B41FCE7C569BEF9512477F795B8E51A3E4F7FBC12FA00E982A4242AC9E7BC12CF6CF894793580B3EF8D00EA1D2AFD5EA4E279909C8EA7FE5B54DE9FA477799154F76B9EB0906447532665A5A7ACF24CEAE8F0D14ADFA1D7F12A49DFE9BEDA53AA57B69B54DA2CF0690E54C9BA56E75E29C8F689AEADD553E7B3581E266E627966730976F4CEE20424D77FB7A0D6BA959440C47EB74074128B01A43878CBC936FDCEEEB6D252BBFB6AB1326C6FD244BE71C57CB65E65DE273536927F48579284440BBA454E0F45A47161BFDB48F04D5E26D4E9A344504872A1295FC010D3AC64E20589EA3DD179D68A6B4940B6C9B6264C7AB5A083DEC98ABE868832D196A1C966B3F63E9034DF5015888DD1C6AFC260169B53B4F53A2A04AAC3478B53B4E84E58CFDB2F1614924A14F2DD270B2B55EBCE4EB053F51F6D6E5416EB28ADA7590C5204926D4E3EA9D8FF48074ED221C5346BD9DBD8CE40E9DBA558536C85AB4AA633A936944B79C5E9BE595AFC8A566904AC7E438AC519771A5515C9483C38B7E70EBAA55407CABC0B7490BCCE4B3AAA8E6EA5E8DCCB8275F0592CCE0092EC344D6A61034E0B3975FA0B7CE06D6E87BBDC1D875AA38A6AAFC7A4CEFD64C3CF8A7AB6BE21714CE26E56CB8B3098C15A969C7DDFD0B70312708064AB55BE6F1DDDAD486B3C9F68CF13AA23AC352C61D397669D51470EB1B3D17461679D2D35AAF2FB616DA57F0B0A4004BF5B9977B40D2140AC4DEDBD3AE9636957D2D88FA1F713ABDD3A78B9212B50256D136C946CFAD49B0907CCEBDB42E2D2E029C4A9F00528BFF977C6A881CC7EC034EC1BA34B7A1B47DAD2EEBEDAEDEB81CD83D3AEE1345FAFA54BA3C3C785823D18CCBD013E1DB4F765B9DCC539F11DA33E7CBCF3182909EC87F809F6FA2A5993B28AD61BD10C347C9ED69C04DA011CB6FF210FF682BFE80A74EC13D6F8BF0F26EBD00799539A969763F6F53FB81CE33875ACA398D007782EC74FF3AEBAAA885C762B6E636E715E6EE1D2FBB1D6E2B5AB508724F3E2858925E60B9ADDDED819391A1263C1674F37FDC69869D867BF2C1D97E7BFFAF2FB31E943DDE60A795B015E1B5D16C565DE87521CFD389DF95CE4DF40A9CB7E5FCCCC95029239CF5D6DB83987B96C496FACB9CD45789388C9A9D65243B43A339FAD69712C82A8F2196CE67F51565FA36A753F9C278318D7E55B1CE6FB387FBEAA0607027775C340662C807F48AADFB637BDB754891A906C6153891F9212DA89730936C6EA0752481E69765F1703B1E17E7816AD0320ACA5E3F3D80B2E3F16A61411DC407F12FE57E9C14066808F870035155156DE92E26BBED9D6A91212A1740BCB6A6B1210DF99309F17836F555C18ECFBD8BAB8CB9358B0D8933DDA008354398F891CC1CAD14BAA89C858E315EA60C2DFE872B9BD51D8EDF9140B2DD4E245F64C68EC70547FA791353C80C8137200A189C06802837CAF348F1880E4C50C1E1857C5576B61A9B9EB2E7A2A239B74467E04D84756021D1E39787C1322DB004E03E5749B93CD9D3B181A66413CE01453ADDBBD2FBE62845031BEF3A4A5E373F30E2E3FD6DCE86E58CBEFA187CFD6B4DECAA6153EC5064B512999DEBA6F164B78729745D5B610EF45ED3E2F068D425819673472741CD06828BF4768EC7AA282249B3C27D29788513EEC90C1C3D275B30A958768374A4301A3B3245528604958C035F4E19B64FEE1302CD1058796327368914763BBA850AE8D55EE91D4ED13E34B596301BA907B3DDCA3B4B90DCC94C2DCFAD55F9404B8AEADD31F27945C0870E89BE989908ECE4C20194EBCFB507B267C00056C8FF8D51C9688FB32B727131005721B3D716ADFB67A3B102774540FCECBCFDB347D73781BA5E2E34D4DD7BDC1A33D3F64824CBA1C6732C5AD8E2D1537138081C455EF3BACFA5A420012D98F79A6502801C505EE35E834BA2208577D48BD465D8B2F64B0460F37C10535787FB51BAE37DD55DE98AAE7B60091CA229172EC0815A1BE3DC28CD8F23D048FACE6031DB5D934C1C58D5B28A098D38E0AAA7E4FF65760D3C3AC551E93C11B6114D5D7ED11AEEE34B0CF021DF24147B1004B77647C47DC61269A5AE4399094C84C0B8B14291D6360D628AD78BD14A6186881E8A8065C1B1421DA97B90D5277DF8C1B29AAB7986530FC755F86DF4354EF2EA23617EABBE10C0DDC7D30084520C4769BE5F0A066C24312D3F0DA973FCA8AAC5FD00C2F2EFF9EB69E6276193E4559724BCA36DAE29BC3572F8F5F1D1E9CA44954B671D8BBE0E1BFAE1A7FA85196E55517A51D114DFCF8671A4D9CC4EB23B1B87D4C724AA52C632EB81D6351EF0FC6C82E1AF08158E9AFE7594CBEBF39FC5F07FF5B88325E0FB408971E4617E4F64009B9D74762C9D710706903DF1C3651B49BF9FE81D09D58452F6656D4451A1BCA8D42936ED206781E69E98B31BFDBAA1EA262751F1587079FA2EF1F497657DDBF39FCE5A535710D61356F69D15F0FCEFFE7351B25FA7A47EAA77AFEFD9E257FDFD6B9AE8A2DA907836DE6ABBFFCD59E097DF8A1B69159D7CA7F5A47DFFF99255615F23D0D99A16C68F10004FB78E0A1DA16901E1B60BC2547EFB755C99ABCA21282AC92B299EAFF434F983DA9D0CE4E315EF79398A1A27329A62AC42412C3AD5FF3E43A5DECA7832F452DCC7F3D7809CD2ECB060FFADDA82D3DB66EA915CC9E06B6F8D0E0BC44D049558C705046200F5CCD108F5C43F7AFBF38B2A68F4CDED2BE492A6B3A4C48727722C6A8E3EEA4C120E31E2D550414D70CCECFAF6CC71C8A293E72056220717EB9B3A5CF3AF8F1A304050EF7A328870B6704501801CB849A7E1262D66345E4166787C5B8757018563476F795F1721CD7D2214E76D8058273B01296F40E1061E90E4F84C599AC00E669BEDE6C9B02B6D2BA0FB8ED2732A438DBFDFA7127080DAC446B636CFB350A0CAB0D8A328F1D491BA5FA490831210CB69D28E30A7B09B4DD7567BB16F4E55495A3F6CF01B7F542BC6D03459FD5968B5DFD24A0DA46C90E30485C946C3BBC3145BD2047761A988B3E1E12B24C6C6D3B5E0C052D663E1ADEDAB8D5A1E16E0E5F6D463F44C38E9F32052F91CAC7CEB66B0A5BD6AB11C63D234626425A00F3EC44AD50F800508E531D1A75C6F0BF305295317ECD18F504684874F21DB19E284359AF460C418EDD25F16300F82290FD3860ED8CE999008D468B1CEDFA49E89C66A3C8F1ABBF390C93145CDBC714DD87D50E3EE8D00DB32731EC067B95CB980BE12FB1D65768BC7781AF7D50D387BC76A72186B9B6DE8CD802910D16FD2470C83A2844AD27DEC7BC7DE06A777C1A63535B6E45F4E47C76E160646BBFD679B6088C856DD722804498167D91AE8384B75C0BF1B54717284F4A8A784E44BBF987DC3C0D3EF70318B2263B5FD19C3D39359CF7511F8060305D030AAD1DA07D8223599DA6F5D28576F053CC210443909B7F7DF06D2FED8D0BB81D80521FFF3A54A30210E4DCF08F3DA9D928DB9E973798201B21268B1C5BDB87A57D546DDD0CB19E748CCFEAB1C7498AF7A03A95C6516342980491E45C0093A0148D773E8E5FBA08CB3EEA3688295C0B952150DC71AA08A5ED7362D68553097162D644550941A88D8114F69AC9E0CF39ECF52020CC36BA02DC91281F6D1B79A743297E1BDB5A905192A36B07215A7ADE1EE5036BBBD391436987386287036887A4CC85B00941598E98AD01F82FD6001FE7BA206E0FEF7D55B96336136FC17D53E1F7F4812FEDC3163B232FEAC61C1498DB9D5140246E4D7BFFE2B4FCF301B9DD010205E076EF39F0E0D2C958B12380078AAD0DA90BA7FD942C49EE03E27D6D2092DF848C73D030E89CCFE33AC1B88697C55C54EFB01AB614E7CB87BC2D340553C0333A274027EE610F6E1F3A04EE765F1D953B116F3BE710C17B06443F0D2C430B98E5DD025BFEF6F1AC9F047F679715F36DF398C8DC9E426A14BB5448F342D883C3D0E779214F8B021F26EC81BD3BF059E828E6E9455A936D4F3EE73EA90D7B92A33D2CB45D44D4C7594157E6C64CF2BCF2CE6B1B409FA08C606760F6904F41390BBA715622D0C35E3892A5900F7BFD24867A7669A0BC0D36C65A803AB80AB3CEEDDDDDA92067466C6CEE964A917F7BE8BFF8DD31414F637DC4EB2731ADE578DA76935B2CDFCFF6F3F81AE069500766434D32C23523E7DF76D0A599E76312107FF85EF976E995A94BBC6F26DC35044D64728F8B26B61A191FE4FB49CC692082386A527BCF482EC8B841D7461AD7FB40E3D895C6FE295CD6EC329E022E74C1BFEDA4BE9AD2B52AEA7750D1AF0E2D3E5D3FEC3D5B02FD00C29733462C1D28ADCDD8BBA0E6616FF4E25F1C530FF64F62A28D7DA90108FAFD24F8EA7114C3B3AB3F9DA159D808E14185147C32384E7B4D3AA9A3BB6D8D4BBE71FA6152445DFA013EF90F391D85F0E74F622A02B1D54737AE7281C99F049717FBD0930B976ED71AA6A85713A0D0EA762D912904738DD5C6630F74DA39B2DF20EE6AEDD398574CAC6A4F6B321F4ADA0E7F6C599F7B227DB0F5203BEE5D2C6B7B7268C8F191CB9F21E742890D8AEE84BB1D012FB1B784198047AD87168208798EC32732969E5C501192CD0C6EB846DBD552A6B1C08B7501564274F872EC70AB438ECBB9D591BF31C33CD4643BB65D41AF01659B6E57FFAEE418C36A1F211A37B02EC19C652ADA90DCA6114734C1ED3C4F49D00B21E35EE808EC19CF55BCA3E38D0614F08BDD282E56C81B22373F0FF523186AFB08CB232B7038BCECA93EB710605A9DA2C85195710850074706D002872D368FF28CF18886365B62AA2D3676FC807236AB00820761FACF841FEEF7B834860D13A145E0C159161F50F6F361BBDAA6D308BF2FD8CF4D88BA4D9AACEA9ADF1C0A4B5F4DEC4BD6EECC0FDAB82735CDA85C45B10C1B1A8959D58E2EE80ED384EE0B5FFBBF4844EB312345DBD4D37AB06B2950F35E1EE0245B259B28957B2D64C5A2817667A02AA6F40FC82BAE73989A84803C729D036981B526367031AA31D869ACDC4AD8B4E13499D16A3FF083F5F2C58B6369BCE61A7259419F7DB0774711B38CF30E6BD7C0EEC572AC47120BD6E269DFD0622989D4AAC4148861A34E0DC1904695117C9C2B86169FB0DF18E0FA82A9900B61350B1268EDD75D9345056B1992E3193950854CB8AFF970A38DEEE58925B528D1C7146308EB3322C4D7B23183E7BEA201E678694B01D33225D32840DC37C9E58D423EC0DD2CF863FBD0857032028FB74CA846BBB4116C40AC3A862C903A0A72342697912024F70CB5E95A84E802A03313661460798428D9237848E6F6D61C535EB761AF954801E2B431A30BA44EB2E0C17761B42D8396E3401094B930050C11F781143523CF8F668565EBDA6191DAD6B2C037997EE58EB77975AAFEE6461F5A2B79205F6E95C01A9C6A30C3397CB332344131E664A25CF22800016F188D8411A05328CD1B0A9C362F583AE772016032921412BC45CBEDE8531E0BA8BAFE606A5C0C8C18E7F2EAF352260F7722C17E77913C1870863AFD54F5735C48E00E40091F1E60564070E12B162F5CE45039C2D235A43E1621C3F469AF040DB3E2EE07AE9E75A23D8116F71AA1BF296F341BC0CEE5E5A116324C0942DD330B534BC5EC63829467D1848845BF43513443F7F0647E001B0C0C68403C835603DA7D82ABFAE5D39C7885DF072D704DF713F5FBBBAEDB8BE7A5ACEC5D1C87FD81D81078426EC890F45860D577681F0135FA1DBEB9F13099CDDD010CCB30B677FFD281D983FD28AD1F6A45FBFDB18894A637FB244F06C7EE2197289C68917CCAB32499C4C7828D5D972C00A278633EA59CE13CE12F59D2082EFBD9272A7CCADE238AEFCF3EC99BC91497094F66A65655F64047D1FB98E86589C9C26970DECF0CB021E724E207E58304DDE87121ABE7D71458B6F7D8A268888B8B96054D019C8D74C655759F603DE51A1C0CC08BD1F4B8380AD727B7B7645591B8E98F51ED136230C8681532CC0F5DF7F5232C5879C6CC8E5C2B99BB0B2933276E9963DFE1909E7A1C9C4AD59CFDA87D6A05D4F69C5D1C94D911C3BA9EB1BBB4CAF91502069B4FDF4B34413D5930B8C48BAD664F5D73010DF71A2400D0C6B5E44A0EF40CAD1BF162FECC50D5BB1254D4BD2F305DF0EB90E50171EA15D809790B597AB3FAA72A2CD33E4B45DA2FB84D6DCAE394804DDF3035EA42932D00906030B071E16854059F3165A8511D246E31AB29F6DC6CB9926DC293949991F738F437EA572129484C23263CC6FDC63146CF3B7E9C0835B8B47E2C90DDBFBDC7ACA09C7BF78143E1BCDB0FD90B02D099C7E2AAC324D371051EB3230F5739BE68BF1E20A27107CC0B5CFEF709D573A905C170BD28154111557691FA0014A859688998BCDF3A00D0A3E5AFFE92D3C950BE2631CEDAE674F037192CDCFCFA7D26DFDA820B82C632FD8FCEE5FB6F0600A1AA5B046C68CB8D7B86B95727FA3FA9FEF6E37E23068EEEA3A86C01EB4F8B16833E2E8D1634528F082DE8019C142D34E3CC68A17F317E9596B92A098D949AA171B0B58FD2C6D607D7BC4287692D1E4D0EAED21E2DB6A676B9E602B125B95E6B6E0E75EAFF350D38E01F8D6A5C2B3EDB60C8EAC5A7EFB7FC82BA84DCA2CD1CF20A42D7A3BA3BB40018CE7CAE698DCA45D92B218076FF26D9DDB3147C04525018CED9B1D68EFE75FB4FFCF6871FBEB02F136865F0A38436E55160A3EB0CA6C681FD4BC103F6B699ED502EEECDBDFD58CD7047AC1B94F9C071B25AE5DBACE28545F771912B12D760AE1142CA7E4B1ABE331692A62BB830448DBCFC3C83420F8A39971F2E24161D991DC1E68D476378BABECAC39EB0A9B1E218756BCF23427A05E50A8E92B326DC7D5DA6AA4B90A23754E531799F1465F52EAAA29B48F418DB95BA24956CD83A3C681355E6C8CBD53D59476F0EE39BBC1E72FAB09D2B5E0278E1EB92CC89527D520EA84EC9C88AA9575199BA06335936D0B6CC39261164DC906EAEA80BFD2ED5D17D87C8374966CA7CBC5FA9023E19AA87CD61AE4E1FB453AA5E9F1D6A8E3EE0A9A979C06D11A94D401EA82152369BDAB5D59AEA335704DC9F95EA03F240D54AD9CCB5836640A97E3017D402E83226B60DDC237F5513B84C9A1670712B900D50D7AAAD0A4DBF0F9CA4AAA54FD7D4D5878FC2B394F49154341C1DF2E8194A860033C8EA05CFDCAA1608D9348DE072DA36C3D8004CD5E84A0747C1AA4A870C9A4ADB3C161C6F1DCBAAAA6C5335F5B56E75917531DE4A5515325934B50EB9106B2FEFD3525E83F974702DE63D7B9A6A343849925A60C80FB5485B043D1C82AF29D59008D934C3C2E6B48060FF4A57BD88B4E9DAE58366412C9DCD0D1D79B56C3E830B24BD8C845250C5EBCBA0BA2A665229AF5C3E0C2B3F44694A0AFA811ADF81DAC50C50CD7C1EF4F8F1071FAA51E47369C692CD68B16EB5B60DF59AD5A66BD72B9AC55CA3604D916A14D2A11AB92C0E0A9F6243A4C88752FC5A77264EEBB4E5A20917C2AEE1E85515E75FD14E1A6B97094C4927F55A31DACA9C4835DB6EC481479AD8161DA39B243C70B59F18B8CD09AE186ADAD8ED5B769744C1D541BD0CF5A9765BF4D2B0272FCD9B70B942C69A255838AE599B09938DB576B07944131B73BB4EB2CCD455F19F2503DDAE34676E690A765FC41316BE2BE86EB63715943D042E3200C65BA675ED87593AB4E368FB740CE894982564C7ACC7D9A18BAC85ABB1B5AB3A0A670CD95DC85AD794E413BCBBDC1C26EC8C7A7257F90C7BDB45BD7D51D16D6DA190ACC0184B1B4AFA8CE3B34A0D117DA9C7C82CEEA0AE35CAEAB8A4CB8EEB6CA9EC9DB4F4723494B6EE860E903A066BB03C998E190A2E04E8BEEC478077540A70C25444DD21A589BFE91790AA618E76F708D383A66B4896758FBB6C5806BD07F39439F331A6DB6F083E950176C019D55D12F6B54D27866F1A56A80F5458125C723826F427149AEE775946EB387FD0C296EE538275973B1E577759F3C045F50C89D5A0D9EFE6EE63D8E6D165FE2448DD672EDF9820178EB8048C0FA92119C09C2D6A19B0CBF7A866B93E76BB9A251631DFA1AE82E7896C67850C66E6212C9F4AFA62F69199AB5E6551E5B0ACD074FB713074985A4846F6F9479BC07EA876678B18A659CD106D40E790AC100ECDD9E24352E8EEAB0C4850B690FAEA0C5DE6A3E76A562E2EDF68A3CD5E5660CBB6DF43755A0EF6AAEEB821306CC8CE4BB72758024C62B0B117EF3DA8475F17FB34080BC05B1CAD259C4F09D679C3241F6D768FA48ADB459A04FAED11AA92EB26EA764CD379434E0D2B6D8E7ECD554D3E086ABDCD2358629069B854C6F6C47591F9D473191FD00F6222788B8AE5A890C195BDEE73C39DA1DA90716A86E223CD859199736C6835B1D1D48CC10654833A09DD69607BC9A7EF01D38CE67373A1C999A6BA96A3A219D65EAC893F65C7BE118CE90B6110BD06AA8C9AA46392B6E00C3863AFC4F294DA94C02C83E3FA1819860807F4D8D80505AD41CE3EFC06293483C635F063E2AA20598489CA3287A03FC60832F96DEC382CB593F998A821E308FFC9986617B50275ECAC2330D3B9BD09EAB8025331DBE68C5F4760BAA9BF5486CB0F4794D62955D690935DFDD865282F2687BFC36675756D941B6BD35FC092FA85EBFD58DD1EEFAA15E3065A2DB2E54CA141CEDED41F4AB51F4376512D28E54C1AE12536166AE8F45D947D3D28BAA9F7BC1B6034555765A434FF4BF53A87C2D00D7BB403E2A0777FA6654AB734C2BE6FD59A2CC2576E18DD157AC4C86A117CFA28ECB0DB81DBF9799D62CFB400168AEE4491ECD37A21DD677C490EC874467B9DAFB250867AF6CD2C6F6E6E5302771C61A1D1BA840CD18909ED315A4786000BF08E0F3DC71F7C33DD141552C661010C7C9493BEBDE9B8A38B39D3030E4767759E6C737C14A2611BF5A749890CFED686B4D747ED23D8EE43FDB3CA8B7A3E7ECA639296CDD7D74717F550256BD2FE7A47CAC696D697A86966A471E7B923DAE739CF6EF3DEC79CD0A23E4B9F3CF891AAA238AAA213AA7E45ABAA4E5E91B26CBC36FC11A55B421FD3DD90F83CFBB2AD36DBAAEE72BDF94AB96D367557A7ABFFF591D4E6D75F360D4F4374A16E665277817CC9DE6E93341EDAFD3E4AC56B9A2A12D40FDE07420FB99BB1A46A08B9FB3150FA9C6748421DFB06F77D5764BD49A96AFD25BB8C1E884BDB6AF87E2477D1EA47FDFD218929965544CC03C1B3FDF5BB24BA2BA275D9D1D895AF7FD6188ED7DFFFFDFF03EED82DDC9C250300 , N'6.4.0-preview3-19553-01')

@@ -594,7 +594,7 @@ public async Task WillSaveThePackageFileAndSetThePackageFileSize()
}

[Fact]
private async Task WillSaveTheCreatedPackageWhenANewPackageRegistrationIsCreated()
public async Task WillSaveTheCreatedPackageWhenANewPackageRegistrationIsCreated()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UTs must be public to run.

@@ -35,7 +35,7 @@ public PackageViewModel Setup(PackageViewModel viewModel, Package package)

viewModel.FullVersion = NuGetVersionFormatter.ToFullString(package.Version);

viewModel.Id = package.PackageRegistration.Id;
viewModel.Id = package.Id;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks to our view model inheritance, this was the model impactful part of the PR and fixed the majority of the cases.

Copy link
Contributor

@drewgillies drewgillies left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only concern I have is in the area of discoverability. Simply calling the column Id seems to indicate to a casual observer that it should have the Id in all rows of the table, and that it's in no way obvious that PackageRegistrations has the true working Id. Could it be something more descriptive like CasedIdForVersion or the like? Not a strong opinion but something to consider. Approving just the same.

@joelverhagen
Copy link
Member Author

Well, unfortunately CasedIdForVersion isn't even true from the consuming, .NET side because the implementation of property getter falls back to PackageRegistration.Id. So it's like of like MaybeCasedIdForVersion 😨. Maybe DisplayId? This property is referenced in 60+ places today so that would lead to a big changeset. I'm not sure if it's worth it but I agree it's not very discoverable (beyond that XML doc).

&& !string.Equals(latestSemVer2Package.Id, packageRegistration.Id, StringComparison.Ordinal))
{
packageRegistration.Id = latestSemVer2Package.Id;
}
Copy link
Contributor

@zhhyu zhhyu Apr 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not very sure whether it's a good question, but how do we evaluate the risk and influence of this "packageRegistration.Id" change for many other components, including validation, V3, search, and etc.? This seems a very big change here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a very good question 😄. Let's think about the components you mentioned and split them into two categories:

  1. Components that call into gallery DB:
    • some validation jobs (orchestrator, process signature, scan-and-sign)
    • Db2Catalog
    • Db2AzureSearch
    • Auxiliary2AzureSearch
  2. Components that do NOT call into gallery DB:
    • the rest of V3
    • search service
    • most other jobs

For components that call into gallery DB, there are further two categories:

  1. Services that read and write the DB
  2. Services that just read the DB

For services that read and write the gallery DB, only orchestrator and gallery modify the Packages table. In general scary DB changes would be ones that are not backwards compatible since you may have readers and writers that are out of sync codewise but running at the same time. This is mitigated in this case because new code reading records updated/written by old code will fall back to the previous behavior of using the PackageRegistration.Id.

As mentioned in the PR description there will be another PR in NuGet.Jobs to bring orchestrator up to date with NuGetGallery.Core so that it starts populating the new Id column. Technically Orchestrator does not add new Packages rows so it won't be possible for a new row to be created without the Id column populated since gallery is the only thing that creates these rows, and it will be updated. But even if there was a job in this category, gallery would handle those rows just fine because the null in the Package.Id column will fall back to PackageRegistration.Id.

For services that just read the DB, if they have old code they will ignore the column. If they have new code they will handle the null column case via the null coalescence in the Id property that I added. Raw SQL will not know about this column and it will therefore be ignored taking the pre-existing behavior.

Db2Catalog does not care about package ID casing since the xslt on the package nuspec is what determines the ID casing for V3, and this has had the proper, true casing for ages. This is why the Azure Search index has the correct package ID casing a lot of the time. Packages pushed since the last index rebuild have their ID casing source from the catalog leaf, which is sourced from the .nupkg itself not gallery DB.

For Db2AzureSearch, there will need to be a follow-up change to use the new column but during sprint planning we decided to punt this for now. This is not a huge deal since there will be no regression from current behavior. In fact it will be a bit better automatically since I have modified the upload flow to also try to keep PackageRegistration.Id casing in sync with the latest version.

For Auxiliary2AzureSearch, the package ID casing is irrelevant since it's only used for building data files that are used as look-up hash maps.

I'm not sure if this answers your question directly, but the tldr is that adding this column is backwards compatible with existing services/code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I were to guess, any problems that would come from this change would be app-code problems in Gallery not data issues or app-code problems in any other service that is not yet referencing the column.

In light of this, we can use the normal recovery plan for Gallery in the case of critical bugs which is to roll back to the previous version of the code.

We should not need to roll back the DB change (hopefully these are not famous last works 😄)

@lyndaidaii
Copy link
Contributor

looking this PR now

@joelverhagen joelverhagen merged commit b3d7dc9 into dev Apr 8, 2021
@joelverhagen joelverhagen deleted the jver-caseofcase branch April 8, 2021 03:19
@ryuyu ryuyu mentioned this pull request Apr 8, 2021
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants