MarimerLLC / cslaforum

Discussion forum for CSLA .NET
https://cslanet.com
Other
31 stars 6 forks source link

WcfPortal.svc requests are stuck in AuthenticationRequest stage #582

Open minhletn opened 6 years ago

minhletn commented 6 years ago

Question We've encountered an issue when many requests to the portal are stuck in AuthenticteRequest stage for very long (10-20 minutes) and CPU is high

There are many requests stuck in IIS, all with AuthenticateRequest stage `

`

The binding is set up like this in the web.config file `

  </service>
  <service name="Csla.Server.Hosts.WcfSecurePortal" behaviorConfiguration="WcfPortalBehavior">
    <endpoint contract="Csla.Server.Hosts.IWcfPortal" binding="basicHttpBinding" />

`

I'm pretty new to CSLA and just wonder if any has seen this before and know the way to fix?

Version and Platform CSLA version: 4.3.14 OS: Windows Platform: Silverlight

rockfordlhotka commented 6 years ago

When you say "AuthenticationRequest" you mean they get stuck in ASP.NET, or after the data portal itself has been invoked?

minhletn commented 6 years ago

Hi rockfordlhotka,

AuthenticateRequest is the state showed in IIS > WorkerProcesses > [AppPoolName] when the requests are stuck, after the data portal is invoked, e.g calling fetch

When this problem happens, there are many requests there in that state for a very long time

rockfordlhotka commented 6 years ago

This sounds like it is during the ASP.NET pipeline, and before the data portal has been invoked. The data portal isn't invoked until after ASP.NET/WCF have authenticated the request.

Do you have code in the server that handles the ASP.NET authentication event (global.asax), or a custom handler, or anything like that?

minhletn commented 6 years ago

Hi rockfordlhotka,

Thanks very much for getting back to me

There's no logic in global.asax to handle AuthenticateRequest, but there's a class which we use that extends Csla.Server.Hosts.Silverlight.WcfPortal to compress and decompress request and response data

If the logic to compress/decompress has some sort of memory issue (e.g memory stream is not closed properly), do you think it cause the problem?

public class CompressedHost : Csla.Server.Hosts.Silverlight.WcfPortal
  {
    protected override Csla.Server.Hosts.Silverlight.CriteriaRequest ConvertRequest(Csla.Server.Hosts.Silverlight.CriteriaRequest request)
    {
      Csla.Server.Hosts.Silverlight.CriteriaRequest returnValue = new Csla.Server.Hosts.Silverlight.CriteriaRequest();
      returnValue.ClientContext = CompressionUtility.Decompress(request.ClientContext);
      returnValue.GlobalContext = CompressionUtility.Decompress(request.GlobalContext);
      if (request.CriteriaData != null)
        returnValue.CriteriaData = CompressionUtility.Decompress(request.CriteriaData);
      returnValue.Principal = CompressionUtility.Decompress(request.Principal);
      returnValue.TypeName = request.TypeName;
      return returnValue;
    }

    protected override Csla.Server.Hosts.Silverlight.UpdateRequest ConvertRequest(Csla.Server.Hosts.Silverlight.UpdateRequest request)
    {

      Csla.Server.Hosts.Silverlight.UpdateRequest returnValue = new Csla.Server.Hosts.Silverlight.UpdateRequest();  
      returnValue.ClientContext = CompressionUtility.Decompress(request.ClientContext);
      returnValue.GlobalContext = CompressionUtility.Decompress(request.GlobalContext);
      returnValue.ObjectData = CompressionUtility.Decompress(request.ObjectData);
      returnValue.Principal = CompressionUtility.Decompress(request.Principal);
      return returnValue;
    }

    protected override Csla.Server.Hosts.Silverlight.WcfResponse ConvertResponse(Csla.Server.Hosts.Silverlight.WcfResponse response)
    {
      Csla.Server.Hosts.Silverlight.WcfResponse returnValue = new Csla.Server.Hosts.Silverlight.WcfResponse();
      returnValue.GlobalContext = CompressionUtility.Compress(response.GlobalContext);
      returnValue.ObjectData = CompressionUtility.Compress(response.ObjectData);
      returnValue.ErrorData = response.ErrorData;
      return returnValue;
    }
  }

The compress, decompress code are as below

public static int _compressionMethod = ICSharpCode.SharpZipLib.Zip.Compression.Deflater.BEST_SPEED;

    public static byte[] Compress(byte[] byteData)
        {            
            byte[] compressedData = null;
            if (byteData != null)
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    ICSharpCode.SharpZipLib.Zip.Compression.Deflater defl = new ICSharpCode.SharpZipLib.Zip.Compression.Deflater(_compressionMethod, false);
                    using (Stream s = new ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream(ms, defl))
                        s.Write(byteData, 0, byteData.Length);
                    compressedData = ms.ToArray();
                }

            }

            return compressedData;
        }

        public static byte[] Decompress(byte[] byteInput)
        {
            byte[] bytResult = null;
            if (byteInput != null)
            {
                using (MemoryStream ms = new MemoryStream(byteInput, 0, byteInput.Length))
                {
                    string strResult = String.Empty;
                    byte[] writeData = new byte[4096];

                    using (Stream s2 = new ICSharpCode.SharpZipLib.Zip.Compression.Streams.InflaterInputStream(ms))
                        bytResult = ReadFullStream(s2);
                }
            }
            return bytResult;
        }

        private static byte[] ReadFullStream(Stream stream)
        {
            byte[] buffer = new byte[32768];
            using (MemoryStream ms = new MemoryStream())
            {
                while (true)
                {
                    int read = stream.Read(buffer, 0, buffer.Length);
                    if (read <= 0)
                        return ms.ToArray();
                    ms.Write(buffer, 0, read);
                }
            }
        }
rockfordlhotka commented 6 years ago

I don't think that is likely, though not disposing a stream could cause a memory leak, so that's not good on a server.

The Silverlight version of WcfPortal doesn't do a whole lot. The Fetch method, for example, does this:

  1. Deserialize the inbound message (which is where your compression is invoked)
  2. Extract the criteria from the message
  3. Invoke the Fetch method on a MobileRequestProcessor

The MobileRequestProcessor then does this:

  1. Creates an instance of a concrete data portal (either a real data portal, or a data portal pass-through - but I'm betting you are using a real data portal - you aren't configured to pass the request through the web server to an app server?)
  2. Calls your custom initializer (if you provide one via config)
  3. Calls your custom authorizer (if you provide one via config)
  4. Invoke the Fetch method on the concrete data portal instance

The concrete data portal (Csla.Server.DataPortal) is the thing that does all the real work behind any data portal host (WCF, Http, etc.).

So the data portal does authorize the request, but doesn't authenticate it in any way - the assumption is that any authentication occurred before the data portal was invoked at all.

rockfordlhotka commented 6 years ago

As a troubleshooting thing, you could maybe put some logging code into your compression class - just log that you got that far, so you'd know if the data portal was actually invoked, or if the failure occurs somewhere in WCF itself.