When using DocumentUltimate on Azure (or on other Cloud service or in multi-server environments), you need to make sure:
- Sessions are shared between instances.
- Document cache folder is shared between instances.
When you have multiple App Services or VMs in Azure, you need to make sure your sessions are shared between multiple instances. This is because Azure has a default internal load balancer which automatically distributes HTTP requests randomly to your instances. This is not a load balancer you own or create, it's internal load balancer of Azure Cloud Services. If your sessions are not shared between instances, you will get this error:
Session has expired, the page will be automatically refreshed in 10 seconds.
This is what happens:
- You view your site page which hosts DocumentViewer and this hits Instance A.
- DocumentViewer starts loading the document and sends request to Azure.
- Azure's default internal load balancer randomly decides this request should be sent to Instance B.
- Instance B receives the request but it does not know this session because it was initiated on Instance A.
- So you receive an error that says “Session has expired”
The problem is if you have more than 1 instance on Azure, each HTTP request can arrive to a different instance. Azure's default internal load balancer decides to send a HTTP request to one of you instances.
The best way to share sessions between instances on Azure is using RedisSessionStateProvider in your project. This is easy to do:
- In your project that hosts DocumentUltimate, open Web.config and insert these settings (<sessionState> tag):
<sessionState mode="Custom" customProvider="RedisSessionStateProvider">
Microsoft.Web.RedisSessionStateProvider.Internal, Version=220.127.116.11, Culture=neutral, PublicKeyToken=a6f3cafa178e6038"
As of DocumentUltimate v3.2.5, Microsoft.Web.RedisSessionStateProvider is already bundled (inside GleamTech.Core 1.9.5 DLL) so you don't need to install any additionaly Nuget packages. Note the green higlighted part which instructs to load type Microsoft.Web.Redis.RedisSessionStateProvider
from internal bundled DLL. If you need to use official Nuget package for some reason, see RedisSessionStateProvider
page for instructions.
- Put your Azure Redis connection string in yellow highlighted part.
To find out your connection string;
- Go to Azure Portal and search for redis. Create a new one with default settings (you only need to give it a name like xxx) or click existing redis cache.
- In Settings section click Access Keys and it will show you the connection string. Copy the one showed in Primary connection string (StackExchange.Redis) and paste it in your Web.config at yellow highlighted place.
- Now DocumentUltimate will be able to make use of Azure Redis Cache for session.
If you don't want to use RedisSessionStateProvider, you can also:
- Change LoadBalancerDistribution to sourceIP mode for Azure's default internal load balancer. It seems this is possible as described here:
This way session will not be shared, however you will guarantee that all HTTP requests of same session arrives to the same instance because distribution will be made according to sourceIP instead of default more random combination.
- If you have your own load balancer in Azure, you can already go to Load balancing rules -> Session persistance and select Client IP for same effect.
Sharing document cache:
The other problem you will have on Azure is sharing document cache between instances. If you don't use a common document cache folder, you will get this error:
Document cache info not found
It's easy to share a document cache folder between Azure VMs:
- Create a network share for App_Data\DocumentCache folder on VM1 (via Share command on context menu). See detailed instructions on how to create network shares on Azure VMs:
- Set CachePath to this network share.
- In code:
DocumentUltimateWebConfiguration.Current.CacheLocation = @” \\VM1\DocumentCache”;
- Or in Web.config AppSettings:
<add key="DocumentUltimateWeb:CacheLocation" value="\\VM1\DocumentCache" />
If both instances on VM1 and VM2 have this path set then they can share the cache folder.
- Make sure you set correct permissions for the network share:
When you use common network folder for document share e.g. which is hosted on VM1, VM2 app pool needs to have read and write permission on this share.
For example by default IIS AppPool\DefaultAppPool user on VM2 will not be able to access the network share on VM1.
- Go to IIS of VM2 and edit your application pool to use a common user eg. (user: Test, password: Test) as the pool identity. If there is a common user with same password on both VM1 and VM2 , then VM1 will allow VM2 to connect to the network share.
- You can also try giving permissions to Network Service account if you don't want create a common user on both VMs.
- You can also use a domain account (if you have a domain) as the pool identity.
Once the VM2 application pool can access the network share, DocumentViewer will work as expected.
Sharing document cache folder between Azure App Services:
In future versions, we plan to add Azure Blobs support for DocumentCache class so that it will not be dependent only on physical file systems.
UPDATE v3.7.0: Temp file handling on Azure, from now on d:\home will be used as the base (%HOME% environment variable) so that the files are persisted and shared between multiple instances of a site. So if you use d:\home\DocumentCache for CacheLocation, multiple instances would be able to share the cache.
Document cache can now be stored on any file system (e.g. Amazon S3, Azure Blob). See CacheLocation
docs for more info.