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?
Related
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.
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.
Normally I use C# for everything, I can create a desktop application with C# and also place a lot of the same c# code in a dll on my shared hosted web server. This saves me a lot of coding time.
Is there any way to this with go?
e.g. place some kind of go dll on my hosted web server to generate HTML.
I am aware that go doesn't do dll's, I am also aware that creating a web server with go to listen to port 80 is straight forward. But this is not a solution for a shared web server.
This seems like a brilliant use for go and it surprises me that this might not be possible.
I should mention, it would be nice if the go code didn't have to restart with every http request.
This is how I do it with C#:
On the web server I add an aspx page like this:
<%# Page Language="C#" %>
<%# Register TagPrefix="MyDLL" Namespace="MyDLL" Assembly="MyDLL"%>
<MyDLL:Web Runat="Server"/>
It simply loads the C# dll which generates HTML based on the http request.
First you have to setup IIS to use fastcgi (instructions) then you can simply build a go web app like you normally would but instead of using http.ListenAndServe you use fcgi.Serve
Example:
func hello(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello, world of FCGI!")
}
func main() {
http.HandleFunc("/hello/", hello)
if err := fcgi.Serve(nil, nil); err != nil {
log.Fatal("fcgi.Serve: ", err)
}
}
But keep in mind that there can be multiple instances of the programming running / destroyed all the time, so any temp data (in-memory sessions, etc) should be stored on memcache or temp files on disk or a database.
I am having a problem with running multiple Facebook applications under a single Web application.
Similar to this problem:
Multiple facebook apps under one web application
I do like the solution suggested here, however the CanvasAuthorizeAttribute uses the FacebookApplication.Current internally and this is a static instance of the FB application within the Web application.
I can't call the .SetApplication method within the controller as I will have concurrency issues.
It would be nice if the FacebookApplication.Current called the Func that is passed into the .SetApplication instead of just calling inside of the .SetApplication.
That way I could pass a function that does my logic for retrieving the correct FB application.
Am I missing something that is already in the SDK?
You can always have your own logic at http://facebook.stackoverflow.com/questions/4931240/multiple-facebook-apps-under-one-web-application/4936703#4936703
private IFacebookApplication Current
{
get
{
var url = HttpContext.Current.Request.Url;
var app = GetSettingsForUrl(url);
// put your custom logic here and return the appropriate IFacebookApplication
return app;
}
}
But you should do this during Application_Start where it is still running on a single thread and not in Controller.
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);