68 results found with an empty search
- Key Practices for Effective Full-stack Web Development
Developer experience The Full Stack Development team spends a significant time writing code. A good developer experience implies a grossly improved developer productivity. Some ways to improve the DX, thereby improving the quality of life and hence the productivity include: Setting up eslint/tslint/prettier so that the IDE can take care of the mundane tasks like enforcing code formatting, highlighting possible code quality issues, enabling early bug detection. Integrating eslint/tslint/prettier within CI/CD Using tools like Storybook with Chromatic enabling UI/UX reviews early on Basic documentation which will enable an easy onboarding process - viz. Instructions on setting up projects for development on local environment, database structure, API documentation, mock data/API sandboxes. Depending on the project, if appropriate, adopt a test driven development. Dependencies & Code maintainability Choosing dependencies is crucial. Using libraries that are no longer maintained might limit upgrading core frameworks in the near future and might require forking repos to update those libraries and then update the core frameworks. Another issue with obsolete libraries is the security risks. From a productivity perspective, some libraries do not actively host documentation, and new developers find it difficult to use those libraries by referring to code snippets without the documentation. Furthermore, having libraries without type safety make it extremely difficult for new developers to use those libraries without documentation. As such, as far as possible, either long term stable libraries should be used. If only small fractions of libraries are required, possibly create in-house libraries. Long term maintainability with a large number of external dependencies can turn into a maintenance nightmare. Team culture Having a positive culture that facilitates open communication, respects individual differences, appreciates contributions, makes people feel valued, has a large impact on how products are built. Code reviews should be what developers look up to, in terms of learning new aspects, instead of dreadful processes. Design system For frontends, having a design system, or components that could easily be abstracted to a library can have a large impact on costs in the long term. When a product pivots, or when companies launch new products, the component libraries can be easily be reused across products. As such, having a design system that facilitates themes can greatly reduce the repetitive efforts of styling and maintaining component libraries. Shipping often Shipping often and early enables feedback earlyon. In this agile-world, regular shipping enables stakeholder participation, identifies issues early, and makes sure that the developers are aligned with the product goals. If releasing to production isn’t an option, having multiple environments can enable internal stakeholder feedback.
- Revamping Full-stack Projects: Strategies for Long-term Success
In the agile world, with dynamic requirements, large projects might need a revamp for numerous reasons. The need for a revamp might arise to improve performance and thereby user experience, to upgrade some libraries that have security vulnerabilities, or to remove certain libraries which are no longer supported and port to new/better libraries. Rebranding might require revamping as well. As products scale, cost optimization might need revamping. Revamps are also frequently driven by a need to merge multiple applications into a single app, usually internal-facing apps. With multiple such reasons overlapping, my team and I had to revamp some projects in the last few years. Here are some of our learnings with the challenges we faced. Moving to a design system Business temptations for shipping optics and associated operational hazards Content architecture vs design changes vs simultaneous content and design changes Dependencies that broke us Developer experience Moving to a design system One of our projects involved an admin panel for a product suite of 5 products. The product suite is a SaaS offering. The admin panel for end users had StyledComponents. Moving to a design system - standardising all components meant great time savings for the long term. However, that implied porting an existing code base of 25+ man-years. We found a sweet spot where we migrated component by component to the design system. Very small PRs where every PR involved changes from only 1 page for only 1 type of component made code review, testing and UX review easy. The new design system had a theme that styled the new components similar to the old implementation. The idea is to switch the theme after all components are migrated. That way, the new design release is a large change visually, but a small code diff on the day theme is flipped. If such small code is not shipped often, the branches become stale, and in a large team that ships new features often, rebasing becomes a large task in itself. Hence the need to ship as often as possible. Business temptations for shipping optics and associated operational hazards Business teams demand large releases that are good optics for marketing. However, those come at a risk. The users using the app face challenges when there are large changes. Large shipping could cause an increase in support tickets if there’s any confusion during app usage, specially from the internal stakeholders. When there are content architecture changes, during release, a different set of fields is required for the old vs new versions. The new fields could be derived from the old ones, hence the need to maintain the old fields for a while. Such old fields become a tech debt and have to be removed later. This brings to another aspect - whether the changes are graphic design changes or content architecture changes or both. For CMS-driven large static websites we developed, for changes involving such old redundant fields, and a large number of new fields, having Storybook updated enabled the content managers to check the old and new components on Storybook. We’ve been using Chromatic for the past few years, and it has helped us enable content managers to quickly experiment with components. Content architecture vs design changes vs simultaneous content and design changes Often, designers creatively add more fields to the UI while redesigning. This mixes two tasks up - graphic design change and content architecture. Separating graphic design changes and content architecture changes helps. Content architecture changes need to be shipped extremely small. Whereas large graphic design changes could be made in large releases. Dependencies that broke us NextJS shipped a stable app directory/app routing. We used a CSS-in-JS library ChakraUI. This library does not support app directory and only works with client-side rendering if used in app directory. If we would have used TailwindCSS, this would not have happened. However, using TailwindCSS, and building components with some other library which would facilitate accessibility would have taken more time (Radix is one way to use primitives and use TailwindCSS). The trade-off of using ChakraUI paid off in the first few years of the project, but now is a blocker for upgrading NextJS and using new features NextJS offers. Another roadblock is that the ChakraUI plugin for Storybook does not support newer Storybook versions. Essentially, we’ll need a complete re-write to exit ChakraUI at some point. In one of our projects, we used a navigation library - navi. This library stopped supporting ReactJS v17 onwards. We had to fork the library and upgrade it since it is no longer supported. While the dev team might want to take some risks and use libraries, when such libraries aren’t supported in newer versions, replacing such core features require large refactoring. Developer experience When setting up a project, prettier and eslint need to be the priority. Missing these out, and enforcing rules later is expensive. Having these set during the project setup makes it easier to onboard new developers. Instead of maintaining documentation or addressing formatting rules during code reviews or memorising rules without documentation, setting up prettier and eslint enforce rules on the codebase, using IDE/IDE plugin features. This saves time and reduces developer fatigue as well as irritation. While some teams consider writing types/interfaces in TypeScript an unnecessary overhead, when building large projects, knowing types really helps in knowing what might break during development instead of runtime. The learning curve associated with learning TypeScript isn’t much, and GitHub Copilot has made things easier as well.
- Server Performance Prediction using ML Models - Part 1
This blog is the first part of the series in "Server Performance Prediction using Machine Learning Models" OVERVIEW: In the semiconductor industry, a Silicon Cycle takes nearly a year or more from conception to the silicon being available. As soon as the concept comes in, there is immense pressure on the marketing and sales teams to come up with performance numbers for this new generation of the silicon i.e. processor. There is a need for a way to find out what the score could be on this new generation. Since the new processor is not physically available, companies run several benchmarks on simulators/emulators for getting the performance scores. This process has two major drawbacks: Running a benchmark on a simulator takes hours more than running it on the physical processor Such simulators are very expensive and a limited number of them are available There should be some way of projecting new scores based on the scores of the older generation of processors. In this project, we aim to predict performance for a benchmark on newer generation processors by training Machine Learning Models on older generation data. METHODOLOGY: Performance Parameters: We identified two performance parameters, namely IPC (Instructions per cycle) and Performance Runtime. The Runtime is actually derived from the Instructions per cycle, assuming a particular clock speed. In the current iteration of the research, we will limit our work to IPC and Runtime prediction. Data Gathering and pre-processing: We gathered our data on two generations of the Graviton processors, namely Graviton 2 and Graviton 3. We captured data for benchmark suites named SPECInt and SPECFp designed by the Standard Performance Evaluation Corporation. For each benchmark in these SPEC benchmark suites, we have captured the following counters at every 100 millisecond intervals: Sr. No: Counter Name 1. L1I_TLB_REFILL 2. L1D_TLB_REFILL 3. L3D_CACHE_ALLOCATE 4. L3D_CACHE_REFILL 5. L3D_CACHE 6. l1i_cache 7. l1i_cache_refill 8. l1d_cache 9. l1d_cache_refill 10. l2d_cache 11. l2d_cache_refill 12. br_mis_pred 13. br_pred 14. mem_access 15. stall_backend 16. stall_frontend 17. ASE_SPEC 18. VFP_SPEC 19. L1I_CACHE_REFILL 20. L2D_CACHE_REFILL 21. INST_SPEC 22. BR_RETIRED 23. BR_MIS_PRED_RETIRED 24. branch-loads 25. MEM_ACCESS_RD Why SPEC? The reason that we captured data on the SPEC benchmark suite is because these benchmarks are designed to capture most of the areas of the CPU. For example, a benchmark like gcc stresses the memory, whereas, another benchmark like x264 stresses on vectorization capabilities of the CPU. Let us call a set of counters collectively as “The Snapshot” of the System. On the other hand, we also capture instructions and cycles for every 100 milliseconds. During post processing, we calculate the Instructions-Per-Cycle (IPC) for every snapshot. For our machine learning model discussed further, we need a set of Input variables and an output variable. The Snapshot of the System serves as the input set of variables (X). The output variable is the Ratio of the IPCs of the newer generation of CPUs to the older generation. Let us have a look at how this training data is generated. Post Processing the counters: For a benchmark, the captured data is available in 7 CSV files. Each file has a few counters (for eg. L1D_TLB_REFILL) captured at every 100 milliseconds intervals over the duration of the benchmark. There are 7 files because only a limited number of counters can be captured during a given benchmark run. Hence, we run the benchmark several times in order to capture all the necessary counters.Inside a counter file, there are many columns, out of which only 3 are important to us for post processing. These are time, count, and type. We filter the dataframe that is read from the csv file for only these 3 columns. For example, a row in the filtered dataframe would look like - 0.300514373,1778078,L1D_TLB_REFILL. The row entry means that the counter L1D_TLB_REFILL is captured at 300 milliseconds from the start of the test, for which the count is 1778078. Once we have the counter files for a benchmark, we apply post processing to the data, where we calculate the Per-Kilo-Instruction (PKI) metric for each counter. For example, we calculate L1D_TLB_REFILL_PKI for the L1D_TLB_REFILL counter for every capture of the L1D_TLB_REFILL counter. So for example, if the corresponding instructions capture for the above example is like this - 0.300514373,618104386,instructions, it means that the instructions count was 618104386 at 300 milliseconds. To calculate the PKI count for each counter, we use the following formula: PKI_Count = (Counter/instructions)*1000 The formula gives us the Per-Kilo-Instructions value of a particular counter. For example, for the 300 milliseconds capture example above, the PKI Count would be calculated as follows: L1D_TLB_REFILL_PKI = L1D_TLB_REFILL/instructions*1000 = 1778078/618104386*1000 = 2.876662972 Hence the L1D_TLB_REFILL_PKI for this particular capture at 300 milliseconds is equal to 2.876662972. Additionally, an Instructions Per Cycle (IPC) count is calculated for each capture of the counters. The formula for IPC simply divides the number of Instructions by the number of cycles. IPC = instructions/cycles It tells you how many instructions were executed for a given clock cycle of the CPU. We consider it a measure of the performance of the CPU. Likewise, the PKI count calculation (same as shown above) is done for all the 25 counters captured. As a result, we have a post processed dataframe which has the following columns: time, IPC, and 25 PKI counters. This is the 2-dimensional dataframe for a benchmark. The post processing is repeated for all the benchmarks, thus creating 2-dimensional dataframes for each benchmark. These dataframes are concatenated one after another and are indexed by the benchmark name, which is our 3rd dimension. Hence, we stack all the 2D dataframes into a 3D dataframe indexed by the benchmark name. Plotting a comparative IPC plot When a benchmark runs on a newer generation processor, it is generally expected that it would take less time to run than on the older generation processor. For example, consider a benchmark named ‘500.perlbench_r_checkspam_0’ . It takes less time to run on Graviton 3 as compared to Graviton 2. Below is a plot of the calculated IPC for the entire duration of the ‘500.perlbench_r_checkspam_0’ benchmark. As seen above, the benchmark on G3 runs in lesser time compared to the same benchmark on G2. The IPC is higher as well. Dynamic Time Warping, Generating Training Data Let us consider an example while taking Graviton 2 (G2) as the older generation and Graviton 3 (G3) as the newer generation used while training the machine learning model. Now for each benchmark, we calculate a new column called the “IPC Ratio”. This is the ratio of the IPC values of each row of G3 dataframe mapped to a row of G2 dataframe for that benchmark. We perform this mapping using Dynamic Time Warping. Why IPC Ratio? For a given snapshot of the system (X), the IPC ratio of G3 to G2 is the same irrespective of the benchmark being run. This is because the architecture of the systems are designed in such a way that if the “state” of the older system is the same at a point during any two benchmark runs, then the IPC Ratios of the new generation to the older generation are equal for both the benchmarks. This is true irrespective of the benchmark being run. Note that only the ratio is the same and not the raw IPC numbers. Hence, we choose IPC Ratio as the output (Y) parameter Dynamic Time Warping Since we need to find the ratio of the IPCs at each capture of the snapshot, there should be an intelligent way to map the benchmark run instances. If we observe the comparative IPC plot above, we notice that the benchmark runs in lesser time on G3 than on G2. This means that more instructions are executed in the same amount of time on G3 than on G2. We calculated the cumulative sum of the number of instructions executed for each row, and store it in a column named “instructions_cumulative_sum”. The graph of the cumulative instructions for G3 and G2 can be seen below (The numbers on the Y-Axis are in the order of 10^12). The example belongs to the benchmark named '502.gcc_r_gcc-pp_3' We map the captured instances by matching the instructions_cumulative_sum column. This is because taking the IPC ratio makes sense only when we are taking the ratio of rows at nearly the same time during the benchmark run. Dynamic time warping is a method that calculates an optimal match between two given sequences with certain rules. For example, First value of sequence 1 is mapped to first value of sequence 2 Last value of sequence 1 mapped to last value of sequence 2 Intermediate mappings have monotonically increasing indices Below is an example of the mapping path generated using the Dynamic Time Warping algorithm We have time on the X-axis, and instructions_cumulative_sum on the Y-axis. The line above represents G2 and the line below represents G3. The orange part in the graph is a lot of lines that represent mapping from one row of G2 to a row of G3 based on the cumulative instructions count. Below are a few initial actual mappings for the above example using the DTW algorithm ((row_g2, row_g3), cumulative_sum_g2, cumulative_sum_g3) (0, 0), 460190954.0, 672156965.0),((1, 0), 1106484590.0, 672156965.0),((2, 1), 1724588976.0, 1657376501.0),((3, 2), 2249696930.0, 2344648605.0),((4, 3), 2780771235.0, 3021707178.0),((5, 3), 3313773511.0, 3021707178.0),((6, 4), 3797070083.0, 3687200150.0),((7, 5), 4311980235.0, 4247959294.0),((8, 6), 4851216305.0, 4889782076.0),((9, 7), 5393885229.0, 5538932327.0),((10, 8), 5943214302.0, 6192166460.0),((11, 8), 6520858849.0, 6192166460.0),((12, 9), 7152711138.0, 7087454578.0)... As seen in the example, the 0th row of G2 is mapped to 0th row of G3, 1st row of G2 with 0th row of G3, 2nd row of G2 with 1st row of G3 and so on. This is done by matching the total number of instructions that are executed as explained above. Calculating IPC Ratio Now since the mapping is done, calculating the IPC ratio is relatively straightforward. We calculate the IPC Ratio as follows: For row i belonging to G2 dataframe, and row j belonging to the G3 dataframe: IPC_Ratio = IPC_j/IPC_i This is done for each (i, j) mapping calculated above. A few insights about IPC_Ratio for '502.gcc_r_gcc-pp_3' : The IPC_Ratio when calculated for the '502.gcc_r_gcc-pp_3' benchmark shows the following distribution which looks pretty much accurate. This is because the improvement is approximately 30% which means the ratio should be 1.3. The comparative IPC plot for G3 vs G2 for this benchmark is shown below The IPC_Ratio calculated for each row after Dynamic time warping (DTW) is shown below Final training data The final training data has the columns ‘time’, ‘instructions’, ‘instructions_cumulative_sum’, ‘IPC’ removed. Hence, the training data has the 25 counters (X variables), and the IPC_Ratio (Y). We use the K Neighbors Regression model for training and inference of IPC Ratio for a given snapshot X. X: Set of variables that define the state of the system at a particular time during the benchmark.Y: IPC Ratio - The Ratio of the Instructions per cycle (IPC) of G3 to G2 at the time when the same number of instructions (approximately) were executed for the benchmark on both the CPUs. Part 1 - Summary In this part, we have covered the captured counters and its post processing, and also the generation of the Final training data. In the next part, we will cover the machine learning algorithm for predicting the IPC Ratio given a snapshot of the system.
- All about Python Kubernetes Client
Kubernetes, also known as K8s, is an open-source system for automating deployment, scaling, and management of containerized applications. Kubernetes is an open source container orchestration engine for automating deployment, scaling, and management of containerized applications. The open source project is hosted by the Cloud Native Computing Foundation (CNCF).So if you are a developer and not familiar with kubernetes CLI, then the kubernetes client will help you to interact with kubernetes.There are several language specific kubernetes clients are available e.g. Python, Java, C#, Javascript etc. Let’s deep dive into the Python Kubernetes Client and understand it using the following steps and examples. Step 1: Install kubernetes Installation guide pip install kubernetes Step 2: Import client and config from kubernetes in your python code from kubernetes import client, config Step 3: Load Kubernetes cluster Configuration try: config.load_incluster_config() except config.ConfigException: try: config.load_kube_config() except config.ConfigException: raise Exception("Could not configure kubernetes python client") Step 4: Interact with Kubernetes resources In step 3 we have loaded kubernetes config so we are ready to perform different operations, or to get different resources of kubernetes using this kubernetes python client api’s. 1. Get Nodes kubectl is the CLI for kubernetes, for getting nodes of clusters you have run commands. A node may be a virtual or physical machine, depending on the cluster. Command: kubectl get nodes Above command will return present nodes in kubernetes cluster To Get the same data using a python client we have to use the class CoreV1Api which we can get from a client that we imported from kubernetes as follows. Using Client: v1 = client.CoreV1Api() v1.list_node() 2. Get Namespaces Namespace in kubernetes is something like group in general term, so if we want to bind our pod, deployment, PV, PVC etc under one label or group, will have create a namespace and while creating each kubernetes resources that we mentioned earlier add –namespace your_namespace_name flag at the end of command. Command: kubectl get namespaces Using Client: v1 = client.CoreV1Api() v1.list_namespace() 3. Get Pods in all Namespaces Pods are the smallest deployable units of computing that you can create and manage in Kubernetes. Pod is a set of containers with shared namespaces and shared file system volumes. Command: kubectl get pods Using Client: v1 = client.CoreV1Api() v1.list_pod_for_all_namespaces() 4. Get Pods in Specific Namespace For finding pods deployed under specific namespace we use below command and similarly we can do using python kubernetes client. Command: kubectl get pods –n your_namespace_here Using Client: v1 = client.CoreV1Api() pod_list = v1.list_namespaced_pod(pod_namespace) pods = [pod.metadata.name + " " + pod.status.phase for pod in pod_list.items] Note: Kubernetes resources (such as pod, deployment etc.) created without namespace are included under default namespace. 5. Create Pod with namespace Command: kubectl apply -f your_pod_yaml_file.yaml Using Client: with open(podYamlFilePath) as f: dep = yaml.safe_load(f) k8s_apps_v1 = client.CoreV1Api() resp = k8s_apps_v1.create_namespaced_pod(body=dep, namespace=pod_namespace) print("Deployment created. status='%s'" % resp.metadata.name) 6.Get pod Status A Pod's status field is a PodStatus object, which has a phase field. The phase of a Pod is a simple, high-level summary of where the Pod is in its lifecycle. Pod status has 4 possible values Pending, Running, Succeeded, Failed and Unknown. Note: After deleting a pod it shows terminating status by some kubectl commands, but this is not a pod phase. Command: kubectl describe pod pod_name --namespace your_pod_namespace Using Client: v1 = client.CoreV1Api() pod = v1.read_namespaced_pod(name=pod_name, namespace=pod_namespace) print(pod.status.phase)#it will print pod status 7. Delete pod with namespace Command: kubectl delete pod your_pod_name —namespace your_pod_namespace Using Client: v1 = client.CoreV1Api() api_response = v1.delete_namespaced_pod(pod_name, pod_namespace) print(api_response) For more information you can see official documentation of Python-Kubernets-Client and for more examples Visit official kubernetes example folder . Conclusion: Kubernetes client helps to interact with kubernetes cluster to perform different tasks and operations programmatically, without running CLI commands as we saw in our article, we can perform much more advanced things using kubernetes client and one of the great things is it is available in different programming languages. References: Kubernetes Docs: https://kubernetes.io/docs/home/ Kubernetes Client: https://github.com/kubernetes-client/
- Server Performance Prediction using ML Models - Part 2
In the first part of the blog, we described the problem that we intend to solve, the data gathering, post processing, and generating the final training data. In the 2nd part, we will take a look at the Machine Learning model we used for training and for inference with new data. Correlation between various counters We have captured various counters for various benchmarks. Here is a graph that shows the correlation between each counter with every other counter. K Neighbors Regression Given a snapshot of the system as a test data row, the K Neighbors algorithm first finds the K nearest among all neighbors using a distance metric such as Euclidean distance (default), Manhattan Distance, Minkowski distance, etc. It then averages the Y values of the K nearest neighbors for the given test row, and assigns the result as the predicted Y value of the test row. Standard Normalization of Counters: In order for the K Neighbors Regression algorithm to calculate these distances in an unbiased manner, we bring all the counters to a comparable scale by using standard normalization. Which means that all the columns will have values that have a standard normal distribution with mean equal to 0 and standard deviation equal to 1. Why did we use K=1? Since we know that given two snapshots whose X values are exactly the same, the ratios would also be the same, we chose K=1 to find the closest neighbor whose input variables match the test data very closely to get a nearly accurate prediction of the IPC ratio. Shown below is a sample of the prediction made using K Neighbors Regression for the IPC Ratio. The above IPC ratio prediction is for the ‘502.gcc_r_gcc-pp_3’ benchmark. The “Actual” line in the graph is present since we have already calculated the IPC Ratio for ‘502.gcc_r_gcc-pp_3’. This dataframe was excluded from the training data for the K Neighbors Regression and was used as a test dataframe. The Runtime can be calculated using the predicted IPC by assuming a particular clock speed of the CPU. We calculate the total number of cycles first, followed by the runtime calculation. The following formula can be used: total_cycles = total_instructions/predicted_ipc predicted_runtime = total_cycles/(2.5*10^9) The above formula for predicted runtime assumes that the clock speed of the processor is 2.5 GHz. The predicted IPC and the runtime for the same benchmark can be seen in the following graph: It shows around 30% improvement, which is close to the predicted value.
- Responsive Next.js Image Using Aspect Ratio
One of our customers at Whileone wants to build cards for their website which contains an image and some other content. Image will cover its container and should adjust its dimensions accordingly without cropping the image. While Using Next.js Image and making it responsive we always faced one challenge that we need to keep the aspect ratio of image so that image will be neat and clean in given space. We can do it by mentioning height and width of image at different breakpoints. Which is actually a time consuming and trial and error method, So we came up with a solution for this problem is css property called aspect-ratio. Below is example Card, Let’s see it in two scenario, With Aspect Ratio and Without Aspect Ratio I’m using Tailwind CSS for styling. Fig. 1 1. Without Aspect Ratio: Without aspect ratio we’ll have the same card on the mobile screen, If we compare the below image (Fig. 2) with the first Image (Fig.1) the bottom corner part of the image get disappeared/cropped on the mobile screen. Fig. 2 2. With Aspect Ratio: With aspect ratio we’ll have the same card on the mobile screen, If we compare the below image (Fig. 3) with the first Image (Fig.1) both are rendered properly. That’s the advantage of Aspect Ratio Property. Let’s see the code what exactly changed, We are using two next image properties Which means Image will take width and height from its parent, and by default Next Image has position absolute so we need its parent to be relative in position. And we need to add the aspect ratio property to its parent, so the question arises, how can we calculate the aspect ratio of an image? So, 1.5 is the aspect ratio for this particular image. Fig. 3 Conclusion: This blog intends to help you understand how aspect ratio works with Next Image, and how It helps us to build responsive images. Here is CodeSandBox link of given example for better understanding, where you can see the code, make changes see the difference: https://codesandbox.io/p/sandbox/next-image-with-aspect-ratio-l6fplz?file=%2Fapp%2Fpage.tsx%3A1%2C1
- AWS Lambda to generate SSH Keys
For the past few months, my team and I at WhileOne Techsoft Pvt. Ltd. have been helping our customer setup a system wherein access to a remote server in the cloud for testing can be granted to users. One of our client’s requirements is to generate SSH keys from the JIRA board. In JIRA use a custom script to generate SSH keys which will help our client for project automation. SSH key pairs are two cryptographically secure keys that can be used to authenticate a client to an SSH server. The private key is retained by the client and should be kept absolutely secret. Why use the AWS Lambda function? AWS Lambda is a serverless compute service that runs code in response to events and automatically manages the underlying compute resource. AWS Lambda automatically runs code in response to multiple events, such as HTTP requests via Amazon API Gateway, modifications to objects in Amazon Simple Storage Service (Amazon S3) buckets, table updates in Amazon DynamoDB, and state transitions in AWS Step Functions. With AWS Lambda, there are no new languages, tools, or frameworks to learn. You can use any third- party library, even native ones. You can also package any code (frameworks, SDKs, libraries, and more) as a Lambda Layer, and manage and share them easily across multiple functions. Lambda natively supports Java, Go, PowerShell, Node.js, C#, Python, and Ruby code, and provides a Runtime API allowing you to use any additional programming languages to author your functions. Steps to generate SSH keys: 1. As a team we have decided to use Ruby as a language in AWS Lambda function. - AWS Lambda functions using Ruby 2.7 which supports architectures such as x86_64 and arm64. - I tried using the OpenSSL- Cipher algorithm which requires an “openssl” gem in Ruby. This algorithm will generate random keys. cipher = OpenSSL:: Cipher.new (‘AES-128-CBC’) Above line will generate the keys with a public and private key pair. But this key pair was not working as expected. - OpenSSL puts a hard limit of 256 bits on key sizes, causing less efficiency. - To overcome above problem, I tried to generate keys using “sshkey” gem which is supported by Ruby with inbuilt function as “sshkey. generate”. This method is quite easy and gives the accurate result for public and private key pair. 2. Once the keys get generated, the next task is to zip these 2 files. As per client requirement, keys should be zipped and sent to the customer as an attachment in an email. - AWS Lambda supports the built function “tar.gz”. But as per requirement I need “.zip” format. - To zip these keys, I have used the “zip” gem in Ruby which is quite easy to use. 3. Next step is to create an Email template and send keys in email format. - First, I tried with SES (Amazon Simple Email Service). I was able to send the emails but it always goes to Junk mail. So, I need to search for some other way to send the emails. - Ruby supports SMTP via “net/smtp”. This method is quite straightforward as add your credentials, make a template and send the email. But this method supports only one attachment in email, which again is a drawback for me since I need to send some PDF documents as an attachment with keys. - To overcome this problem, I have used the “mail” gem which is supported by Ruby. This gem is supported by SMTP. It also supports HTML templates to send the email. 4. Since this AWS Lambda function is going to call from the JIRA board. I need some data from the JIRA board such as clients name, email id etc. In ruby, “jira-ruby” gem is used to fetch the information from the JIRA board. To get the information needed to create API token in Jira board which will act as a password. Use JIRA credentials in the lambda function and get the information. In JIRA, each issue is created with an issue id which is unique. Get all the information from issue id which will be the parameter passed by JIRA board. Steps to call Lambda function in Jira Board: 1. First Create API and add lambda function in POST method. Add any parameters if needed. I need an ID from the JIRA board. Add issue id in URL Query String Parameter. 2. Now to call this API, JIRA supports JIRA webhook. Create a JIRA webhook and add API URL into this. Also add a JQL query when this API should get called. Limitations: AWS Lambda function supports only 3MB space for each function. Since I am using a lot of gems to create this functionality, I need more space for my function. - To resolve this issue, I need to split the function into 2 separate functions each of 3MB. - First function will work to get the information from the JIRA board, create a temporary file and save it into S3 bucket. And also create SSH keys and save those keys in S3 bucket. - Second function will generate an Email template, fetch keys from S3 bucket and send email to the customer. - Now the main task is how these 2 functions will communicate with each other. Since I am going to create an API for this where only 1 function can get called. AWS ruby supports “invoke” functions where you can call other functions. This was a very good way for me to get hands on with AWS Lambda and do a soft landing to understand AWS Lambda. The cost of the lambda functions is in the below table This means no IT department in the company is going to raise eyebrows for cost overheads. These costs are equivalent to negligible or none at all. The same can also be done on other cloud providers namely on GCP which can use Google Cloud functions, or Azure which can use Azure Automation or OCI which uses Oracle functions.
- Android on RiscV Part - I
The Problem statement: Our customer expressed their desire to know if Android (AOSP) was already ported by community to the RiscV platform and if we could provide a detailed summary of the current status of AOSP compilation/build and Qemu emulation progress for RiscV Introduction to AOSP: Android is an open source operating system for mobile devices and an open source project led by Google. Android Open Source Project (AOSP) repository offers the information and source code needed to create custom variants of the Android OS, port devices and accessories to the Android platform, and ensure that the devices meet the compatibility requirements that keep the Android ecosystem a healthy and stable environment for millions of users. AOSP build requirements: 1. Follow instructions to download AOSP source code from this link https://source.android.com/docs/setup/download/downloading 2. Follow Readme.md to configure and build AOSP from this link https://github.com/google/android-riscv64 3. Follow the instructions to setup cuttlefish for riscv64 at this link https://source.android.com/docs/setup/create/cuttlefish-use To build the target AOSP project, follow the steps below: # Start a Baremetal instance on AWS # Install basic dependencies on the instance $> sudo apt-get update && sudo apt-get install build-essential repo # Clone the source code to Baremetal instance. $> mkdir ~/aosp $> cd aosp $> sudo ln -s /usr/bin/python3 /usr/bin/python $> git config --global user.name $> git config --global user.email $> repo init -u https://android.googlesource.com/platform/manifest $> repo sync Note: The above command “repo sync” takes some time to download the sources to the folder. # Once the sources are downloaded successfully, next run the configure command as below. $> source build/envsetup.sh $> lunch aosp_cf_riscv64_phone-trunk_staging-userdebug Figure 2: Run the configuration command “lunch” # Next, run the command to compile and build the AOSP sources $> make –j Figure 3: Run the build command “make” # Build Error: make fails with permission error for /dev/loop* Figure 4: Error as a result of missing loop devices and permissions # Solution: In case, loop devices are not available, then we need to create them. Run command to grant user “ubuntu” permissions to modify loop devices. $> sudo chown ubuntu /dev/loop* # After granting permissions and some minutes later, the AOSP build completes successfully. Figure 5: Error as a result of missing loop devices and root permissions # Verify the images in the directory “vscoc_riscv64” as shown below. Figure 6: Resulting Binaries of AOSP build This completes Part-I of our blog. In the next part, i.e; Part-II, we shall launch the Cuttlefish Emulator and boot Android !!
- Android on RiscV Part - II
The Problem statement: Our customer expressed their desire to know if Android (AOSP) was already ported by community to the RiscV platform and if we could provide a detailed summary of the current status of AOSP compilation/build and Qemu emulation progress for RiscV What have we accomplished in Part - I: We launched a Bare Metal instance on AWS Downloaded the AOSP source code Configured and built the binaries for AOSP RiscV What we plan to accomplish in Part - II: Install Cuttlefish Emulator Launch the emulator Setup reverse tunnel from remote terminal to Bare Metal instance Connect RealVNC and view the Android display *Before trying to launch the emulator, install the cuttlefish package as below. After installing, reboot the Bare metal machine. Follow below steps to install cuttlefish: # Install cuttlefish dependencies: $> sudo apt-get install -y libgtest-dev libssl-dev libxml2-dev libgoogle-glog-dev libcurl4-openssl-dev libcurl4 libjsoncpp-dev libgflags-dev cmake libfmt-dev libprotobuf-dev protobuf-compiler meson bison build-essential $> sudo apt install -y git devscripts config-package-dev debhelper-compat golang curl $> git clone https://github.com/google/android-cuttlefish $> cd android-cuttlefish $> for dir in base frontend; do \ cd $dir \ debuild -i -us -uc -b -d \ cd .. \ done \ $> sudo dpkg -i ./cuttlefish-base_*_*64.deb || sudo apt-get install -f $> sudo dpkg -i ./cuttlefish-user_*_*64.deb || sudo apt-get install -f $> sudo usermod -aG kvm,cvdnetwork,render $USER $> sudo reboot # Run command to launch the Cuttlefish emulator $> launch_cvd -cpus=8 -memory_mb=8192 *The emulator launch takes considerable time to complete. Please wait until you can see the message “VIRTUAL DEVICE BOOT COMPLETED” before proceeding ahead to create a remote connection to the Baremetal instance. Please check the snapshot below. Figure 1: Successful launch of Android RiscV on Cuttlefish Emulator *To view the Android Display, connect to the bare metal machine from the laptop (remote terminal). Type the below command on console window of the laptop (remote terminal). The below has been done inside WSL on Windows. # Set up a reverse tunnel from remote terminal (Laptop) to Bare Metal instance: $> ssh -i ~/.ssh/mykey.pem -p 22 -L5901:localhost:6444 ubuntu@3.93.48.57 -Nf Note : You can replace the IP Address above “ 3.93.48.57 ” with your Bare Metal IP address! # Open RealVNC Viewer ⦁ Open the RealVNC viewer on the remote terminal (Local machine / Laptop). ⦁ Click to create a new connection ⦁ Give a name to the connection “AndroidRiscV” or as per your need. ⦁ Set the IP Address as “localhost:5901” ⦁ Click OK and exit the “New Connection” dialog ⦁ Double click on the connection icon to open the VNC display Figure 2: Android Display Snapshots taken from Remote terminal (Laptop) Note : Wait for the Display to update. It takes considerable time. This brings us to the end of Part - II. Hope you enjoyed it and got some insights into the Android AOSP project.
- Introducing CloudNudge: Your Partner in Cloud Cost Optimization
Hey there, Cloud Enthusiasts! We get it—managing cloud costs can sometimes feel like trying to catch a cloud itself: elusive, elusive, ever-changing, and sometimes, downright overwhelming. That’s why we created CloudNudge—a tool designed to take the guesswork out of cloud cost optimization and make your life just a little bit easier. Why We Built CloudNudge The cloud is amazing. It’s flexible, scalable, and powers much of what we do today. But with great power comes great responsibility—and sometimes, great expenses. When we started talking to teams and organizations, one thing became clear: cloud costs can spiral out of control faster than you can say “provisioned instance.” That’s where CloudNudge comes in. We designed this tool to help you not only manage your cloud spending but also gain deeper insights and optimize it effectively. We wanted something that didn’t just throw numbers at you but offered actionable insights—like a friendly nudge in the right direction. What CloudNudge Can Do for You So, what makes CloudNudge different? Here’s a sneak peek: Real-Time Monitoring : Keep an eye on your cloud costs as they happen. No more end-of-month surprises! Cost Forecasting : Predict future costs based on your current usage, so you can budget with confidence. Budget Alerts : Set your spending limits, and we’ll make sure you never exceed them. Think of it as your financial safety net. Savings Recommendations : We analyze your cloud usage and offer suggestions to save you money without compromising performance. Multi-Cloud Support : Whether you’re on AWS, Azure, Google Cloud, or a mix, we’ve got you covered. The Human Side of CloudNudge But CloudNudge is more than just a tool—it’s a team of passionate individuals who understand the challenges you face. We’re constantly listening, learning, and improving so that CloudNudge evolves with your needs. We believe in transparency, simplicity, and above all, putting you in control of your cloud journey. Our mission is to empower you with the insights you need to make informed decisions, not just for today but for the future. Join Us on This Journey We’re just getting started, and we’re excited to have you along for the ride. Whether you’re a seasoned cloud expert or just beginning to navigate the complexities of cloud management, CloudNudge is here to support you every step of the way. So, here’s our nudge to you: give CloudNudge a try. We’d love to hear your thoughts, your feedback, and ideas. After all, we’re building this together. Stay tuned for more updates, and let’s make cloud management a breeze!
- Debugging Tool for workloads using Java
GCeasy - We have used the Debugging tool , GCeasy in one of our projects to generate reports of performance in a particular way. GCeasy helped us to visualize the performance numbers in a better way. Purpose to use GCeasy - GC easy Log analyzer portal, which we will be using for report generation. Why we use GCeasy - We can use Garbage collection log analysis for the following purposes: It is a wrapper Python script that generates graphs for visual inspections. It captures the GC calls for the young generation, and old generation and metadata and gives a visual representation of each call, time spent, etc. It also allows us to understand the memory-related performance throughout the test. GC easy tool provides a rich set of Garbage collection, memory-related performance. There are below Key Performance Indicators when it comes to Garbage Collection analysis: 1. Throughput 2. Latency (i.e. GC Pause time) I. Throughput - a. Throughput is the amount of productive work done by your application in a given period. b. Let’s say your application runs for 60 minutes. In this 60 minutes let’s say 2 minutes is spent on GC activities. It means the application has spent 3.33% on GC activities (i.e. 2 / 60 * 100). It means application throughput is 96.67% (i.e. 100 – 3.33). II.Latency – This is the amount of time taken by one single Garbage collection event to run. There is a paid version of this tool that can give you more detailed use of it. How to Enable GC Logs java –version <= 8 §-XX:+PrintGCDetails –Xloggc: java –version >= 9 §-Xlog:gc*:file= Steps to GC Logs Zip the log files zip -r gcLogFiles.zip gcLogFiles Sign in to https://gceasy.io/ Upload the zip file to the GCeasy tool A report will be generated, allowing you to explore graphs related to garbage collection (GC) Below is an example to show what GCeasy dashboard steps look like. When to measure Measure at different phases during the test. GC Behavior is a function of traffic, so measure at various points in tests for full disclosure. When Performance Degrades - A sudden drop in performance may be due to inefficient GC, memory leaks, or excessive pause times. You can look for Increased GC pause times, higher memory usage, and full GC frequency. If you’ve made JVM tuning changes (e.g., heap size, GC algorithm), you’ll want to verify their impact on garbage collection. To detect issues proactively and ensure the JVM is functioning optimally. Over time, memory consumption patterns can reveal problems like memory leaks.
- Top 10 Libraries you should consider along with your React Project
Here’s an introduction to the top 10 most popular and essential libraries that work well with React: Vite : Vite helps you to create a React app, it's an alternative to the CRA method to create a React project. Vite uses native ES modules during development and doesn't bundle the entire app upfront. This leads to faster HMR and quicker updates in the browser compared to CRA, which relies on Webpack, leading to slower development builds. React Router DOM : React is Library itself so it does not have any routing functionality by default, here React Router Dom comes into the picture which helps in routing in SPA (single page application) efficiently. Emotion : Emotion provides a powerful and flexible way to write CSS in JavaScript with optimized performance. It offers styled and CSS APIs and can be used with both object and template literal syntax. Framer motion : Framer Motion is a powerful library that helps build simple animations, complex gesture-based interactions, drag-and-drop, and more. It makes it easy to control animation using Javascript which is more flexible and easy to manage rather than writing and handling everything using only CSS. React Hook Form : React Hook Form is a lightweight, performant library for handling form inputs and validation. It’s way more efficient than handing form using states. Axios : Axios is a promise-based HTTP client that simplifies making HTTP requests to interact with REST APIs. It has built-in features like automatic JSON transformation, easier error handling, and support for interceptors, making life easier. React Query (TanStack Query) : React Query simplifies data fetching, caching, synchronizing, and updating server state. It reduces the need for Redux in a server-related state. React Data Table (TanStack Table) : TanStack Table makes life easier when dealing with tables in React. It has some amazing features like Client Side Pagination, filters, Global search, column search, sorting, and much more. Recharts : Recharts is a simple, declarative charting library that is built specifically for React. It supports a variety of chart types like bar, line, area, and pie charts with customization. StoryBook : Storybook is a frontend workshop for building, and testing UI components and pages in isolation outside your app. This makes it easier to focus on building well-structured, reusable components without the distractions of routing, state, or the rest of your app. It's great for building design systems or reusable UI libraries. or building design systems or reusable UI libraries.











