Setting up Neo4J with Golang - go

I am setting up Go with Neo4j on a live project for one of the microservices
I went through the docs around setting up the same but it does not show the best practice to do the same (specifically globally and pass around the session instance throughout the application)
This is what I am doing to setup the same, was wondering if this is the right approach:
// app.go
import ""github.com/neo4j/neo4j-go-driver/neo4j""
type App struct {
Router *mux.Router
DB *sqlx.DB
Neo4j neo4j.Session // setting neo4j session globally for injection
}
// =============================
// Neo4j initialization
// =============================
driver, err2 := neo4j.NewDriver(
neo4jConfig.connstring,
neo4j.BasicAuth(neo4jConfig.username, neo4jConfig.password, ""),
func(c *neo4j.Config){
c.Encrypted = false
},
)
checkForErrors(err2, "Cannot connect to NEO4J")
defer driver.Close()
session, err3 := driver.NewSession(neo4j.SessionConfig{})
a.Neo4j = session // 👈 assigning the session instance
Now this will be injected as a dependency in the repo package where the queries are being executed

The example in the readme says the following:
// Sessions are short-lived, cheap to create and NOT thread safe. Typically create one or more sessions
// per request in your web application. Make sure to call Close on the session when done.
// For multi-database support, set sessionConfig.DatabaseName to requested database
// Session config will default to write mode, if only reads are to be used configure session for
// read mode.
session := driver.NewSession(neo4j.SessionConfig{})
So having a global driver instance is not an issue, but you should not be using a global session instance since it is not thread safe.

Related

Is it possible to use MassTransit Transactional Outbox in a Multi-Tenant per DB architecture?

The main objective is to guarantee the reliability of the message delivery of our integration events in case of failures with the Application or DB, and in this scenario the new Transactional Outbox seems to be the most indicated.
With the MT Transactional Outbox, the writing (publishing) of our integration events happens atomically in the Commit of our Command Handlers.
Our solution is built as microservices that use one DB per tenant. However, the applications are shared and the DB (connection string) selection is built into the request scope.
The Outbox MT tables (OutboxMessage, In/OutboxState) are created in each tenant DB and with that we have the atomic writing of the operation along the triggering of integration events.
The problem is in the delivery to the Bus. How to make the MT background services (DeliveryService and InboxCleanupService) reach the outbox in the tenant DB, since there is no request to identify the tenant in advance?
It wouldn't be a problem if the application also runs in an environment for each tenant with their respective configuration / connection string, but that's not the case.
An alternative would be to create a third application dedicated only to running delivery/cleanup services for all tenants in separate tasks.
But to use MT delivery, we would have to dynamically create a DbContext instance (with only the Mappings for MT) for each tenant at startup. I don't know if this is the best way and it also doesn't seem possible to use the MassTransit configuration API this way.
Does anyone have an advice, approach or guide that could help us?
We were having exactly the same issue as you are describing, and we've ended up with the solution you are essentially describing yourself, a separate service that does nothing but outbox delivery and inbox cleanup.
For every MassTransit endpoint, we disable both outbox delivery and inbox cleanup
mt.AddEntityFrameworkOutbox<ContextWithOutboxTables>(outbox =>
{
var outboxConfig = outbox.UseSqlServer();
outboxConfig.DisableInboxCleanupService();
outboxConfig.UseBusOutbox(busOutbox =>
{
busOutbox.DisableDeliveryService();
});
}
and the separate service is configured similar to the below (with some code replaced/removed)
builder.ConfigureServices(s =>
{
foreach (var connectionString in distinctTenantConnectionStrings)
{
IServiceCollection outboxServices = new ServiceCollection();
outboxServices.AddDbContext<ContextWithOutboxTables>(options =>
{
options.UseSqlServer(connectionString);
});
var outboxContainer = outboxServices.BuildServiceProvider();
s.AddSingleton<IHostedService>(container =>
{
return new BusOutboxDeliveryService<ContextWithOutboxTables>(
container.GetRequiredService<IBusControl>(),
new OptionsWrapper<OutboxDeliveryServiceOptions>(new OutboxDeliveryServiceOptions()
{
// tweak your options here
}),
new OptionsWrapper<EntityFrameworkOutboxOptions>(new EntityFrameworkOutboxOptions()
{
LockStatementProvider = new SqlServerLockStatementProvider(),
// tweak your options here
}),
logger,
outboxContainer);
});
s.AddSingleton<IHostedService>(container =>
new InboxCleanupService<ContextWithOutboxTables>(new OptionsWrapper<InboxCleanupServiceOptions>(
new InboxCleanupServiceOptions()
{
// tweak your options here
}),
logger,
outboxContainer)
);
}
});
It is necessary to register the hosted service using the AddSingleton<IHostedService> override instead of AddHostedService because MS dependency injection eliminates duplicate service implementations under the covers, which is not the behavior you want, you ARE actually registering the same hosted service multiple times, with different configuration.
Creating a separate container via
IServiceCollection outboxServices = new ServiceCollection();
is necessary due to MT resolving the data context from the container, and you need it to provide a differently configured data context (connection string) for each instance of the hosted service.

Firebase Go wants host "firebaseio.com"

I'm new to firebase and I'm trying to setup a small test with a simple database in Go.
I struggle a lot with the database connection. Here is my code:
tx := context.Background()
conf := &firebase.Config{
DatabaseURL: "https://mydb.europe-west1.firebasedatabase.app",
}
// Fetch the service account key JSON file contents
opt := option.WithCredentialsFile("./fireBasePrivateKey.json")
// Initialize the app with a service account, granting admin privileges
app, err := firebase.NewApp(ctx, conf, opt)
if err != nil {
log.Fatalln("Error initializing app:", err)
}
client, err := app.Database(ctx)
if err != nil {
log.Fatalln("Error initializing database client:", err)
}
With that code (which comes from the official documentation), I've got an error on the Database client initialization:
invalid database url: wants host: .firebaseio.com
I then tried with the requested url: mydb.firebaseio.com -> I've got another error telling me my db is not in that region and gives me the previous db address.
I also tried other things like mydb.europe-west1.firebaseio.com but here it says me the certificate is not valid for this url...
I'm a bit lost. I understand the problem has to do with the localization of the DB I choose when I created it, but I don't understand how to handle it with the go implementation.
The <projectname>.firebaseio.com format used to be the only format for Firebase Database URLs until early last year. Nowadays, databases in the US still use that format, but databases in other regions use the <dbname><region>.firebasedatabase.app format that you have.
Support for the newer URL format was added in PR #423 and released in version 4.6 of the Go Admin SDK, which was released in June. Upgrade to this version (or later), to ensure you don't get the error message anymore.

go-iris Re-registering a service in an mvc application

I am working on a heartbeat failure detector where on the front end I am using Iris and the front end is checking if the backend is alive.
The front end is an Iris mvc application done the following way:
app := iris.New()
app.StaticWeb("/", "./public")
// Configure the websocket server
ws := websocket.New(websocket.Config{})
jobAppsRouter := app.Party("/jobApplications")
jobAppsRouter.Any("/iris-ws.js", websocket.ClientHandler())
// Create the MVC object
jobApplicationsApp := mvc.New(jobAppsRouter)
Then I create my connection and I do the following
/ Create the service that interacts with the repo
jobAppService := services.NewJobApplicationService(conn)
// Dependencies
jobApplicationsApp.Register(
jobAppService,
ws.Upgrade,
)
// Controllers registration
jobApplicationsApp.Handle(new(controllers.JobAppController))
// Start the web server at http://localhost:8080
app.Run(iris.Addr(portStr))
This works and starts the app properly. The problem is that later on, I create a new connection to the same back end and I want to register a new service with it. Simply doing this again
jobAppService := services.NewJobApplicationService(conn)
// Dependencies
jobApplicationsApp.Register(
jobAppService,
ws.Upgrade,
)
doesn't do what I need. What could I do?

Is there a way to reset all ColdFusion connections from the pool?

We use Oracle and ColdFusion 9.
Whenever an Oracle package gets invalidated it causes every ColdFusion connection to fail with an invalid package warning on the next time the connection accesses the particular package.
Is there a programmatic way to invalidate all ColdFusion connections in the pool?
A quick and dirty option for a development server is to modify the datasource settings via the admin api. I believe disabling/enabling connection pooling (or just modifying the dsn) automatically closes all connections.
Here is an MS SQL example. The settings for Oracle may differ slightly.
<cfscript>
// get datasource api
adminAPI = createObject("component", "cfide.adminapi.administrator");
adminAPI.login( "cf_admin_password" );
dsnService = createObject("component","cfide.adminapi.datasource");
// disable pooling
// NOTE: change setMSSQL() to setOracle()
props = {name="MyDatasourceName", pooling=false, host="127.0.0.1", database="MyDBName"};
dsnService.setMSSQL(argumentCollection=props);
sleep(2000);
// re-enable pooling
props.pooling = true;
dsnService.setMSSQL(argumentCollection=props);
// sanity check
finalSettings = dsnService.getDatasources()[props.name];
if (finalSettings.pooling) {
WriteDump("SUCCESS");
} else {
WriteDump("ERROR: Pooling was not re-enabled");
}
</cfscript>

how to pass a target database on H2 database console

I'm using H2 database console as a servlet in my own web application that provides a front end of many databases.
How to skip or help a login step at H2 database console by passing some parameters in my own code?
(I have many databases, so I won't use "saved settings" first.)
imaginary: http://myapp/h2console/login.do?user=scott&password=tiger&url=jdbc:thin:......
Because of the somewhat special session handling of the console, this is not possible just using an fixed URL. (The session handling allows to open multiple connections within multiple tabs from one browser, which is not possible when using cookies.)
However, what you can do is create a URL in the same way as Server.startWebServer(Connection conn) does:
// the server is already running in your case,
// so most likely you don't need the following lines:
WebServer webServer = new WebServer();
Server web = new Server(webServer, new String[] { "-webPort", "0" });
web.start();
Server server = new Server();
server.web = web;
webServer.setShutdownHandler(server);
// this will create a new session and return the URL for it:
String url = webServer.addSession(conn);

Resources