# Topic 32-34 ###### tags: `The Pragmatic Programmer` ## Topic 32 Configuration - **Code relies on values that may change** after the application has gone live, keep those values **external** to the app. - Application will run in different environments, and potentially for different customers - **Keep the environment- and customer-specific values outside the app** - **Parameterize** your application: the code adapts to the places it runs. ### Tip 55: Parameterize Your App Using External Configuration Look for anything that you know will have to change that you can express outside your main body of code :::spoiler Common things put in configuration data - Credentials for external services (database, third party APIs, and so on) - Logging levels and destinations - Port, IP address, machine, and cluster names the app uses - Environment-specific validation parameters - Externally set parameters, such as tax rates - Site-specific formatting details - License keys ::: #### Static Configuration > Flat files or database tables Format: - Popular flat files type: **YAML**, **JSON** (example from FR Studio) - Better to store in database when: - Information is structured - Likely to be changed by the customer (ex: sales tax rates) The configuration: - Is read into your application as a data structure - Normally when the application starts - Commonly is made global, easier for any code to get to the values. :::warning The author suggest us NOT to do the above. Instead, **wrap the configuration information behind a (thin) API**. This decouples our code from the details of the representation of configuration. ::: #### Configuration-As-A-Service > Rather than flat file or database, the authors suggest configuration to be stored behind a service API. (example from FR Studio) Benifits: - Multiple applications can **share configuration information**, with authentication and access control limiting what each can see - Configuration **changes can be made globally** - The configuration data can be **maintained via a specialized UI** - <u>The configuration data becomes **dynamic** !!!</u> :::danger :no_entry: <u>Static Configuation</u> - We have to stop and restart an application to change a single parameter. ::: :::success :heavy_check_mark: <u>Configuration service</u> - Components of the application could **register for notifications of updates** to parameters they use. - Service could send them messages containing new values when they are changed. - No need to rebuild, when configuration values change. ::: **Reminders:** - Don’t Write Dodo-Code: Without external configuration, your code is not as adaptable or flexible as it could be. - Don’t Overdo It: Don’t push decisions to configuration out of laziness. ## Chapter 6 Concurrency: Introduction - **Definition** - <u>Concurrency</u> - When the execution of two or more pieces of code act as if they run at the same time. - To have concurrency, you need to run code in an environment that can switch execution between different parts of your code when it is running. => JavaScript Event Loop ![](https://i.imgur.com/8uurTn0.jpg) - <u>Parallelism</u> - When they do run at the same time. - To have parallelism, you need hardware that can do two things at once. (ex: multiple cores in a CPU, multiple CPUs in a computer, or multiple computers connected together) ![](https://i.imgur.com/ckShgXg.png) ### Everything Is Concurrent > Concurrency is a requirement if you want your application to be able to deal with the real world, where things are asynchronous: users are interacting, data is being fetched, external services are being called, all at the same time. ## Topic 33: Breaking Temporal Coupling Two aspects of time that are important to us: - **Concurrency**: things happening at the sametime - **Ordering**: the relative positions of things in time Temporal coupling: coupling in time, ex: - `Method A` must always be called before `method B` - Only one report can be run at a time - Must wait for the screen to redraw before the button click is received. :arrow_right: We need to allow for concurrency and to think about decoupling any time or order dependencies. ### Looking for Concurrency Find out: - **What can happen at the same time** - **What must happen in a strict order** :star: **Activity Diagram**: one way to capture the workflow > Maximize parallelism by identifying activities that could be performed in parallel, but aren’t. #### *Tip 56: Analyze Workflow to Improve Concurrency* Example: steps of robotic piña colada maker 1. Open blender 2. Open piña colada mix 3. Put mix in blender 4. Measure 1/2 cup white rum 5. Pour in rum 6. Add 2 cups of ice 7. Close blender 8. Liquefy for 1 minute 9. Open blender 10. Get glasses 11. Get pink umbrellas 12. Serve :arrow_down: **Applying activity diagram** ![](https://i.imgur.com/SY2uNDB.png) What is found: - The top-level tasks (1, 2, 4, 10, and 11) can all happen concurrently, up front. - Tasks 3, 5, and 6 can happen in parallel later. ### Opportunities for Concurrency Activity diagrams show the potential areas of concurrency, but **have nothing to say about whether these areas are worth exploiting**. :arrow_right: **This is where the design part comes in** *Robotic piña colada maker example: Number 8, liquify, will take a minute. During that time, our bartender can get the glasses and umbrellas (activities 10 and 11) and probably still have time to serve another customer.* :::info Find activities that take time, but not time in our code. ex: - Querying a database - accessing an external service - waiting for user input These are opportunities to do something more productive. ::: ### Opportunities for Parallelism > Parallelism is a hardware concern - Split into pieces of work that are relatively independent—where each can proceed without waiting for anything from the others. - Split it into independent chunks, process each in parallel, then combine the results. :::warning :heavy_exclamation_mark: Identifying Opportunities Is the Easy Part. The tricky part: how can we implement it safely. ::: *<u>Challenges</u> How many tasks do you perform in parallel when you get ready for work in the morning? Could you express this in a UML activity diagram? Can you find some way to get ready more quickly by increasing concurrency?* ## Topic 34: Shared State Is Incorrect State *The apple pie example* ![](https://i.imgur.com/rw8UNTd.png) - The problem is the shared state. Each server in the restaurant looked into the display case without regard for the other. - The problem here is not that two processes can write to the same memory. The problem is that **neither process can guarantee that its view of that memory is consistent**. *If the value in the display case changes, their memory (which they are using to make decisions) is now out of date.* *This is all because the fetching and then updating the pie count is not an atomic operation: the underlying value can change in the middle.* ### Semaphores and Other Forms of Mutual Exclusion Semaphore: a thing that only one person can own at a time *Apple pie example with Semaphore* ``` case_semaphore.lock() if display_case.pie_count > 0 promise_pie_to_customer() display_case.take_pie() give_pie_to_customer() end case_semaphore.unlock() ``` :::warning Problems with this approach: - This is a convention that everyone need to agree and remember, or chaos. - Delegates responsibility for protecting access to the pie case to the people who use it. ::: #### Make the Resource Transactional > centralize the control of Semaphore Misconception example: ``` slice = display_case.get_pie_if_available() if slice give_pie_to_customer() end def get_pie_if_available() #### if @slices.size > 0 # update_sales_data(:pie) # return @slices.shift # else # incorrect code! false # end # end #### ``` Correct way: ``` def get_pie_if_available() @case_semaphore.lock() try { if @slices.size > 0 update_sales_data(:pie) return @slices.shift else false end } ensure { @case_semaphore.unlock() } end ``` #### Multiple Resource Transactions *Apple pie with ice cream as a single order* ``` slice = display_case.get_pie_if_available() if slice try { scoop = freezer.get_ice_cream_if_available() if scoop try { give_order_to_customer() } rescue { freezer.give_back(scoop) } end } rescue { display_case.give_back(slice) } end ``` :::warning Problems with this approach: - The code is now really ugly - What about more resource? ::: :::success The pragmatic approach would be to say that “apple pie à la mode” is its own resource. We’d move this code into a new module, and then the client could just say “get me apple pie with ice cream” and it either succeeds or fails. - Composite dishes - Generic `get_menu_item` ::: <br> To be completed...