Skip to content

Conversation

@marcrocny
Copy link

@marcrocny marcrocny commented Aug 15, 2017

Remove() allows for reconfiguration of an existing tenant; fixes #5.

Changes the locking to use a reader-writer lock.

…nt()` for management/reconfiguration of tenants.
@marcrocny marcrocny changed the title Add tenant (re)configuration methods. Add tenant reconfiguration; update locking. Aug 15, 2017
@tillig
Copy link
Member

tillig commented Aug 16, 2017

I really like this! Thanks!

Thinking about reconfiguring a tenant and multi-threaded apps. I imagine reconfiguring a tenant on the fly may be like this:

mtc.RemoveTenant("tenant1");
mtc.ConfigureTenant("tenant1", b => b.RegisterType<T>());

Hypothetically, between RemoveTenant and ConfigureTenant something on another thread could resolve something from the tenant1 tenant, which would cause the tenant scope to be re-created, which would cause ConfigureTenant to throw.

I can think of a couple of options to get around this:

  1. Expose the lock (or a lock access mechanism) for consumers. This would allow a large set of atomic operations to be executed by a consumer. Of course, that's not a great API and could create some even more challenging locking issues within the MTC class.
  2. Change ConfigureTenant so it doesn't throw on a second call; instead have it drop any existing tenant scope and create a new one as needed. Not a bad idea, but could be confusing to people trying to "append to a tenant" by calling ConfigureTenant a bunch of times for the same tenant and not realizing that you can't do that.
  3. Add a ReconfigureTenant override that handles the remove/add as an atomic operation internally. RemoveTenant is still interesting, but ReconfigureTenant would be a remove/add all at once and handle the locking to ensure atomicity.

What do you think? Is there a fourth option? Or maybe I'm off the deep end, which isn't a far stretch since my current work project has driven me batty. 😆

@marcrocny
Copy link
Author

Thanks very much.

Admittedly, I should have given the atomic-reconfiguration case more thought. Was focused more on the concern of supporting an "admin" side of an application where clients are added or removed as a single operation, not often reconfigured during their lifetime.

(For that requirement I have used a less "drastic" mechanism: load all the known implementations of an interface and use ResolveKeyed() or ResolveNamed(). Although, dynamically-loaded extensions or plug-ins would be another matter. Haven't had to go there yet.)

Maybe it's just me, but the indeterminate nature of ConfigureTenant() in option 2 has a "smell" to me. Overloading the current functionality doesn't seem necessary--if you're embarking on reconfiguring tenants, a new API seems appropriate.

Option 3, ReconfigureTenant(), I like. Working on that right now...

Option 1, exposing the lock, only makes sense if there are a multitude of other unforeseeable interactions with the tenant-dictionary, or if we really just want this to be as open as possible. We already have a "start," an "end," and--with the addition of ReconfigureTenant()--a "knife-edge cutover". Those three seem to cover all cases, I'm not sure there's a need for the complication. Plus, locking is hard. Even with good docs, exposing the "slim" lock will require the user to reference the code to avoid lock-recursion.

I can't think of any other options either. 🦇, maybe, but pretty cogent 😄

Marc Lewandowski added 2 commits August 16, 2017 15:19
…ve-configure fail-case demo; additional caveats in XML docs.
@marcrocny
Copy link
Author

Because it proceeds destructively, should it be ReconfigureTenant() or RebuildTenant()?

@tillig
Copy link
Member

tillig commented Aug 16, 2017

Good question. I could see either way, though I lean toward Reconfigure since that both implies you're changing it and it has the benefit of consistent grammar (configure/reconfigure).

@marcrocny
Copy link
Author

I'll leave as is, with the appropriate caveats in the XML-docs.

/// Removes the tenant configuration and disposes the associated lifetime scope.
/// </summary>
/// <param name="tenantId">The id of the tenant to dispose.</param>
/// <remarks>Like </remarks>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's, like, a floating Like here.

/// Tenant lifetime scopes are immutable, so once they are retrieved,
/// The ability to remove (<see cref="RemoveTenant(object)"/>) or reconfigure
/// (<see cref="ReconfigureTenant(object, Action{ContainerBuilder})"/> an
/// active tenant has been added. However, it must still be noted that
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"...an active tenant exists." - long term it won't have been added, it just exists.

/// early, in application startup.
/// </para>
/// <para>
/// Even when using ReconfigureTenant, the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReconfigureTenant should be a <see> link to indicate you're referencing a method.

/// <para>
/// Even when using ReconfigureTenant, the
/// existing tenant scope isn't modified, but is disposed and rebuilt.
/// Any dependencies that were resolved from a Removed scope will also
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"removed" should have a lower-case R.

/// <summary>
/// Returns whether the given tenantId has been configured.
/// </summary>
/// <param name="tenantId">The tenantId to test.</param>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tenant ID

/// <summary>
/// Removes the tenant configuration and disposes the associated lifetime scope.
/// </summary>
/// <param name="tenantId">The id of the tenant to dispose.</param>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"ID of the tenant" (cap ID)

@tillig
Copy link
Member

tillig commented Aug 16, 2017

Aight, super minor crap doc updates because I'm a grammar policeman, otherwise ready to go. This is really good stuff.

@tillig
Copy link
Member

tillig commented Aug 16, 2017

And, yeah, the grammar police just started a sentence with "aight" which isn't a word. I see the irony there.

@marcrocny
Copy link
Author

Briefly considered appending "Aight, " to the start of some random paragraphs and doing a global replace of "\btenant\b" with "tenet".

@tillig
Copy link
Member

tillig commented Aug 16, 2017

YES. THAT. 😆 MOAR MULTI TENETS.

@tillig tillig merged commit 0611383 into autofac:develop Aug 16, 2017
@tillig
Copy link
Member

tillig commented Aug 16, 2017

Thank you so much! Very appreciated. I'll set about cutting a .1 release for this in a couple of minutes.

@marcrocny marcrocny deleted the tenant-reconfiguration branch October 30, 2018 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reconfiguration of tenants in MultitenantContainer

2 participants