Skip to content

Commit

Permalink
Add Support for Hot Reload of GraphQL Schemas (#2510)
Browse files Browse the repository at this point in the history
## Why make this change?

- Closes #2431 
- This change is necessary to enable dynamic updates to GraphQL schemas
without requiring a full application restart.
- It enhances the developer experience by allowing schema changes to be
reflected immediately during runtime, improving efficiency in
development and deployment processes.

## What is this change?

- Addition of new constants in `DabConfigEvents.cs` for GraphQL schema
events.
- Updates to `HotReloadEventHandler.cs` to include handlers for GraphQL
schema-related events.
- Modifications in `RuntimeConfigLoader.cs` to ensure proper firing
order of events for schema eviction and creation.
- Updates to `GraphQLSchemaCreator.cs` to handle configuration changes
and update runtime entities accordingly.
- Modifications in `Startup.cs` to register hot reload event handlers
and methods for refreshing and evicting the GraphQL schema.

## How was this tested?

- [X] Integration Tests
- [X] Manual Tests

## Sample Request(s)


https://github.com/user-attachments/assets/0a2e6353-d466-453e-b1d3-d2f64b3f1d75
  • Loading branch information
abhishekkumams authored Jan 22, 2025
1 parent d757585 commit 833521d
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 47 deletions.
4 changes: 4 additions & 0 deletions src/Config/DabConfigEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ public static class DabConfigEvents
public const string POSTGRESQL_QUERY_EXECUTOR_ON_CONFIG_CHANGED = "POSTGRESQL_QUERY_EXECUTOR_ON_CONFIG_CHANGED";
public const string DOCUMENTOR_ON_CONFIG_CHANGED = "DOCUMENTOR_ON_CONFIG_CHANGED";
public const string AUTHZ_RESOLVER_ON_CONFIG_CHANGED = "AUTHZ_RESOLVER_ON_CONFIG_CHANGED";
public const string GRAPHQL_SCHEMA_REFRESH_ON_CONFIG_CHANGED = "GRAPHQL_SCHEMA_REFRESH_ON_CONFIG_CHANGED";
public const string GRAPHQL_SCHEMA_EVICTION_ON_CONFIG_CHANGED = "GRAPHQL_SCHEMA_EVICTION_ON_CONFIG_CHANGED";
public const string GRAPHQL_SCHEMA_CREATOR_ON_CONFIG_CHANGED = "GRAPHQL_SCHEMA_CREATOR_ON_CONFIG_CHANGED";

}
5 changes: 4 additions & 1 deletion src/Config/HotReloadEventHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ public HotReloadEventHandler()
{ QUERY_ENGINE_FACTORY_ON_CONFIG_CHANGED, null },
{ MUTATION_ENGINE_FACTORY_ON_CONFIG_CHANGED, null },
{ DOCUMENTOR_ON_CONFIG_CHANGED, null },
{ AUTHZ_RESOLVER_ON_CONFIG_CHANGED, null }
{ AUTHZ_RESOLVER_ON_CONFIG_CHANGED, null },
{ GRAPHQL_SCHEMA_CREATOR_ON_CONFIG_CHANGED, null },
{ GRAPHQL_SCHEMA_REFRESH_ON_CONFIG_CHANGED, null },
{ GRAPHQL_SCHEMA_EVICTION_ON_CONFIG_CHANGED, null }
};
}

Expand Down
5 changes: 5 additions & 0 deletions src/Config/RuntimeConfigLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ protected void SignalConfigChanged(string message = "")
// RuntimeConfig must already be updated and is implied to have been updated by the time
// this function is called.
OnConfigChangedEvent(new HotReloadEventArgs(AUTHZ_RESOLVER_ON_CONFIG_CHANGED, message));

// Order of event firing matters: Eviction must be done before creating a new schema and then updating the schema.
OnConfigChangedEvent(new HotReloadEventArgs(GRAPHQL_SCHEMA_EVICTION_ON_CONFIG_CHANGED, message));
OnConfigChangedEvent(new HotReloadEventArgs(GRAPHQL_SCHEMA_CREATOR_ON_CONFIG_CHANGED, message));
OnConfigChangedEvent(new HotReloadEventArgs(GRAPHQL_SCHEMA_REFRESH_ON_CONFIG_CHANGED, message));
}

/// <summary>
Expand Down
20 changes: 18 additions & 2 deletions src/Core/Services/GraphQLSchemaCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.ObjectModel;
using System.Net;
using Azure.DataApiBuilder.Auth;
using Azure.DataApiBuilder.Config;
using Azure.DataApiBuilder.Config.DatabasePrimitives;
using Azure.DataApiBuilder.Config.ObjectModel;
using Azure.DataApiBuilder.Core.Configurations;
Expand Down Expand Up @@ -39,7 +40,7 @@ public class GraphQLSchemaCreator
private readonly IQueryEngineFactory _queryEngineFactory;
private readonly IMutationEngineFactory _mutationEngineFactory;
private readonly IMetadataProviderFactory _metadataProviderFactory;
private readonly RuntimeEntities _entities;
private RuntimeEntities _entities;
private readonly IAuthorizationResolver _authorizationResolver;
private readonly RuntimeConfigProvider _runtimeConfigProvider;
private bool _isMultipleCreateOperationEnabled;
Expand All @@ -52,13 +53,16 @@ public class GraphQLSchemaCreator
/// <param name="mutationEngineFactory">MutationEngineFactory to retreive mutation engine to be used by resolvers.</param>
/// <param name="metadataProviderFactory">MetadataProviderFactory to get metadata provider used when generating the SQL-based GraphQL schema. Ignored if the runtime is Cosmos.</param>
/// <param name="authorizationResolver">Authorization information for the runtime, to be applied to the GraphQL schema.</param>
/// <param name="handler">Optional hot-reload event handler to subscribe to the config change event.</param>
public GraphQLSchemaCreator(
RuntimeConfigProvider runtimeConfigProvider,
IQueryEngineFactory queryEngineFactory,
IMutationEngineFactory mutationEngineFactory,
IMetadataProviderFactory metadataProviderFactory,
IAuthorizationResolver authorizationResolver)
IAuthorizationResolver authorizationResolver,
HotReloadEventHandler<HotReloadEventArgs>? handler = null)
{
handler?.Subscribe(DabConfigEvents.GRAPHQL_SCHEMA_CREATOR_ON_CONFIG_CHANGED, OnConfigChanged);
RuntimeConfig runtimeConfig = runtimeConfigProvider.GetConfig();

_isMultipleCreateOperationEnabled = runtimeConfig.IsMultipleCreateOperationEnabled();
Expand All @@ -70,6 +74,18 @@ public GraphQLSchemaCreator(
_runtimeConfigProvider = runtimeConfigProvider;
}

/// <summary>
/// Executed when a hot-reload event occurs. Pulls the latest
/// runtimeconfig object from the provider and updates the flag indicating
/// whether multiple create operations are enabled, and the entities based on the new config.
/// </summary>
protected void OnConfigChanged(object? sender, HotReloadEventArgs args)
{
RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig();
_isMultipleCreateOperationEnabled = runtimeConfig.IsMultipleCreateOperationEnabled();
_entities = runtimeConfig.Entities;
}

/// <summary>
/// Take the raw GraphQL objects and generate the full schema from them.
/// At this point, we're somewhat agnostic to whether the runtime is Cosmos or SQL
Expand Down
Loading

0 comments on commit 833521d

Please sign in to comment.