Instructions for developing a tailored database solution State Storage Adapter for the Microsoft Bot Framework

As I work on creating a custom adapter for integrating a specific database with the Microsoft Bot framework, my approach is to develop something similar to the cosmosDBPartitionedStorage class within the bot framework.

From my analysis, it seems that there are three main operations - read, write, and delete - that need to be inherited or implemented from the botbuilder storage. However, are there any aspects from the database standpoint that I should consider during the creation of this adapter which may not be evident from simply reading through the source code layers? For instance, the initialization() method that is cosmos-specific - how should I adapt this for the solution I am aiming for?

My plan involves utilizing two databases, one of which is Redis. I intend to test this setup in an Azure Redis instance during my local development, as I believe it serves as a good starting point. Therefore, initially, the focus will be on creating a Redis adapter.


Update: I eventually opted for a Redis-only cluster solution, which has proven to be stable. While I wasn't able to achieve concurrency checking due to the need for a server-side script (which I am employing for my CRUD operations), this might be addressed in a future update.

The assistance provided by @mrichardson, mentioned in the reply below, was crucial in developing my own data store. I managed to successfully conduct most essential base tests in unit testing for my TypeScript implementation, except for the concurrency test.

Through the utilization of Redis, I crafted an adapter that supports JSON using the RedisJson module. This Redis module needs to be installed via your command line or configuration file settings.

I chose the library IORedis from Luin, and while working with it was challenging due to the integration complexities with Redis and especially when used in a cluster alongside the RedisJson module, it turned out to be a rewarding experience!

Due to opting for the RedisJson module, I had to resort to using LUA scripts like load and EVALSHA for every CRUD operation, falling back to EVAL if necessary and re-establishing the script upon failure.

Although I am uncertain about the significant performance improvement gained from using EVALSHA LUA scripting solely for read and write operations, the Redis documentation does suggest its benefits.

A major benefit of scripting lies in its ability to read and write data swiftly, minimizing latency and making actions like read, compute, write extremely fast. Pipelining cannot facilitate such scenarios efficiently since the client requires the response from the read command before proceeding with the write command.

Yet, my decision to employ scripting primarily stemmed from the limitations of the IORedis client, which lacks native support for RedisJson commands. To circumvent this issue, I either needed to create a custom script (impeding pipelining but offering evalhas fallback) in IORedis or establish my own fallback system from EVALSHA to EVAL.

So far, the results have been impressive!

The codebase caters to a RedisCluster, and once I finalize a few adjustments, I aim to publish it as a TypeScript npm package on GitHub and npm.

Additionally, the inputs also come with a TTL setting, providing a valuable security and performance abstraction ideal for applications like the Microsoft Bot framework.

Answer №1

It seems that there are three important operations - read, write, and delete - inherited/implemented from the botbuilder storage.

That's correct. As long as you can successfully perform those operations, your adapter should work fine.

Is there anything database-related that needs to be considered when creating this adapter which may not be immediately obvious from reading the source code? For instance, the initialization() function that is specific to Cosmos - how should I handle that for my solution?

You're right. The initialization() function is specific to Cosmos and it:

  1. Creates a database if it doesn't already exist
  2. Saves the existing/created database as a property of the class to avoid making unnecessary HTTP requests in the future
  3. Ensures thread safety by locking the class during these operations

If you want to check if the database exists before performing any read/write operations, having something like the initialization() function is advisable. It's a good practice for future-proofing your bot, but not mandatory.

This would initially be for a Redis adapter.

Unfortunately, we don't have Redis storage adapters available at the moment. However, here are some other storage adapters you might find helpful while building yours:

When developing your own adapter, make sure to test it thoroughly with our set of Storage Base Tests to ensure its functionality.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The browser is throwing errors because TypeScript is attempting to convert imports to requires during compilation

A dilemma I encountered: <script src="./Snake.js" type="text/javascript"></script> was added to my HTML file. I have a file named Snake.ts which I am compiling to JS using the below configuration: {target: "es6", module: "commonjs"} Howeve ...

Is it possible to create a combined header/declaration file in Golang within a single file?

My goal is to automatically generate Golang declaration files based on .json data. While with TypeScript I can consolidate types/declarations in one file using namespaces, it seems more complex to achieve the same with Golang packages and namespacing. In ...

Error: Code layer not located while utilizing "sam invoke local" in AWS

Currently, I am engaged in an AWS project where I am developing two lambda functions. Both of these functions rely on a common codebase stored in the node_modules directory, which is placed in a separate layer named AWS::Lambda::LayerVersion, not to be con ...

Incorporate form information into an array in Angular Version 2 or higher

This form is where I craft my questions https://i.sstatic.net/93781.png When composing a question, the ability to include multiple alternatives is available. There will also be an option to edit these alternatives. The desired format for my array is as ...

Exploring TypeScript's generic features and conditional types in function parameters

Imagine having a function that retrieves the return value of a provided getIdentifier function. This function is necessary, except in cases where the item contains an id property, in which case we can simply read that value. type FuncArg<T> = T exten ...

Is my implementation of this [^{}]+(?=}) regex pattern in TypeScript accurate?

Hey there! I'm currently working on extracting values that are inside curly braces "{value}". Do you think the regular expression [^{}]+(?=}) I am using is correct? let url = "/{id}/{name}/{age}"; let params = url.match('[^{\}]+(? ...

A step-by-step guide on leveraging swagger-autogen in TypeScript applications

Is it possible to integrate the swagger-autogen module into a Typescript project? I have attempted multiple methods, but have been unsuccessful. The error message "Failed" keeps appearing when using a swagger.js file: const swaggerAutogen = require("swagge ...

Do Not Activate the Android App with Deeplink in Ionic3

I'm currently using the ionic-plugin-deeplinks to enable deep linking within my app. Here are the steps I followed: $ ionic cordova plugin add ionic-plugin-deeplinks --variable URL_SCHEME=myapp --variable DEEPLINK_SCHEME=https --variable DEEPLINK_HOS ...

Setting up Identity Server 4 integration with Ionic 2

Currently, I am in the process of setting up Identity Server to function with Ionic 2. I am a little confused about how to set up the Redirect URLs specifically for testing purposes in the browser. Furthermore, I am updating and integrating an OIDC Cordov ...

"Unexpected Type Inference Issue: A variable initially defined as a string inexplicably transforms into 'undefined'

Currently, I am incorporating the await-to-js library for handling errors (specifically utilizing the to method from the library). In an intriguing scenario, the variable type shifts to string | undefined within a for..of loop, whereas outside of the loop ...

Angular 13 does not currently have support for the experimental syntax 'importMeta' activated

Since upgrading to angular 13, I've encountered an issue while attempting to create a worker in the following manner: new Worker(new URL('../path/to/worker', import.meta.url), {type: 'module'}) This code works as expected with "ng ...

In TypeScript, what specific type or class does a dynamically imported module belong to?

Can someone assist me in determining the type of an imported module in TypeScript? Here is my query: I have a module called module.ts export class RSL1 {}; Next, I import it into my index.ts using the following code: const script = await import('mod ...

Strategies for efficiently storing and querying a detailed hierarchical JSON structure in Redis

I'm seeking guidance on how to efficiently store and index all the elements from the JSON data below in REDIS. My objective is to save all the AWS DESCRIBE_INSTANCE CLI Data in REDIS for every EC2 instance, and then query the indexed information to re ...

Angular 9: The Ultimate Interceptor

Hey there! I'm currently working on implementing an interceptor in Angular 9. The goal is to capture when the idtoken is incorrect and generate new tokens, but unfortunately the request is not being sent again. Here's the code for the interceptor ...

Modify a particular attribute in an array of objects

I am currently working on an Angular project and dealing with the following array object: { "DATA": [ { "CUSTOM1": [ { "value": "Item1", ...

Mongoose and TypeScript - the _id being returned seems to be in an unfamiliar format

Experiencing unusual results when querying MongoDB (via Mongoose) from TypeScript. Defined the following two interfaces: import { Document, Types } from "mongoose"; export interface IModule extends Document { _id: Types.ObjectId; name: stri ...

What is the correct way to interact with an Object's functions in TypeScript?

I'm facing a challenge with one of the libraries I'm using as it is throwing a lot of TypeScript errors related to Object functions. The list of TS errors that I encountered include: error TS2339: Property 'random' does not exist on t ...

Patience is key as you await the completion of an API call in Angular 14

Within my Angular 14 application, I am faced with a scenario where I need to make two API calls and then combine the results using "combineLatest" from rxjs. The combined data is then assigned to a variable that I must use in a separate function. How can I ...

What is the best way to iterate through all class properties that are specified using class-validator?

I have a class defined using the class-validator package. class Shape { @IsString() value?: string @IsString() id?: string } I am trying to find a way to retrieve the properties and types specified in this class. Is there a method to do s ...

Leveraging functionality from an imported module - NestJS

Currently, I am utilizing a service from a services module within the main scaffolded app controller in NestJS. Although it is functioning as expected - with helloWorldsService.message displaying the appropriate greeting in the @Get method - I can't ...